<commit_before>
#include <qbitarray.h>
#include <qbuffer.h>
#include <qdom.h>
#include <qfile.h>
#include <qtextstream.h>

#include <kdebug.h>

#include <eventsyncee.h>

#include "device.h"
#include "datebook.h"

using namespace OpieHelper;

namespace {
// from TT
int week ( const QDate &start ) {
    int stop = start.day();
    int sentinel = start.dayOfWeek();
    int dayOfWeek = QDate( start.year(),  start.month(),  1 ).dayOfWeek();
    int week = 1;
    for ( int i = 1; i < stop; i++ ) {
        if ( dayOfWeek++ == sentinel )
            week++;
        if ( dayOfWeek > 7 )
            dayOfWeek = 0;
    }
    return week;
}

};

DateBook::DateBook( CategoryEdit* edit,
                    KSync::KonnectorUIDHelper* helper,
                    const QString& tz,
                    bool meta, Device *dev )
    : Base( edit,  helper,  tz, meta, dev )
{
}
DateBook::~DateBook(){
}
/**
 * Converts an Opie Event to a KDE one
 */
KCal::Event* DateBook::toEvent( QDomElement e) {
//    kdDebug(522) << "inside event" << endl;
    KCal::Event* event = new KCal::Event();
    QStringList list = QStringList::split(";",  e.attribute("categories") );
    QStringList categories;
    for ( uint i = 0; i < list.count(); i++ ) {
//        kdDebug(5229) << list[i]<< " categories " << m_edit->categoryById( list[i],  "Calendar") << endl;
        categories.append(m_edit->categoryById(list[i], "Calendar") );
    }
    if (!categories.isEmpty() ) {
        event->setCategories( categories );
    }
    //event->setDescription(e.attribute("Description") );
    event->setSummary( e.attribute("description") );
    event->setUid( kdeId( "EventSyncEntry",  e.attribute("uid") ) );
    event->setDescription( e.attribute("note") );
    event->setLocation( e.attribute("location") );
    // time

    QString start = e.attribute("start");
//    kdDebug(5229) << "Start " << fromUTC( (time_t) start.toLong() ).toString() << endl;
    event->setDtStart( fromUTC( (time_t) start.toLong() ) );

    QString end = e.attribute("end");
//    kdDebug(5229) << "End " << fromUTC( (time_t) end.toLong() ).toString() << endl;
    event->setDtEnd( fromUTC( (time_t) end.toLong() ) );

    // type
    if ( e.attribute("type") == "AllDay" ) {
        event->setFloats( true );
    }else{
        event->setFloats( false );
    }

    // alarm
    QString alarm = e.attribute("alarm");

    /* there is an alarm */
    if (!alarm.isEmpty() ) {
        KCal::Alarm *al = new KCal::Alarm( event );
        al->setStartOffset( alarm.toInt() * -60 );
        al->setAudioAlarm( e.attribute("sound") );
        event->addAlarm( al );

        kdDebug(5229) << "Alarm offset " << al->startOffset().asSeconds() << endl;
        kdDebug(5229) << "Alarm " << e.attribute("alarm") << " " << e.attribute("sound") << endl;
    }

    // Recurrence damn I feared to do that
    QString type = e.attribute("rtype");
    int freq = e.attribute("rfreq").toInt();
    bool hasEnd = e.attribute("rhasenddate").toInt();
    //kdDebug(5229) << "HasEndDate: " << hasEnd << endl;

    KCal::Recurrence *rec = event->recurrence();
    start = e.attribute("created");
    rec->setRecurStart( fromUTC( (time_t) start.toLong() ) );

    if ( type == "Daily" ) {
        if ( hasEnd ) {
            start = e.attribute("enddt");
            rec->setDaily(freq,  fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setDaily( freq,  -1 );
        }
        // weekly
    }else if ( type == "Weekly") {
        int days = e.attribute("rweekdays").toInt();
        QBitArray bits( 7 );
        bits.fill( false );
        if ( Monday & days )
            bits.setBit( 0 );
        if ( Tuesday & days )
            bits.setBit( 1 );
        if ( Wednesday & days )
            bits.setBit( 2 );
        if ( Thursday & days )
            bits.setBit( 3 );
        if ( Friday & days )
            bits.setBit( 4 );
        if ( Saturday & days )
            bits.setBit( 5 );
        if ( Sunday & days )
            bits.setBit( 6 );

        if ( hasEnd ) {
            start = e.attribute("enddt");
//            kdDebug(5229) << "has end " << start << endl;
            rec->setWeekly( freq,  bits, fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setWeekly( freq,  bits,  -1 );
        }
    // monthly
    }else if ( type == "MonthlyDay" ) {
        // monthly day the  1st Saturday of the month
        int rposition = e.attribute("rposition").toInt();
        if ( hasEnd ) {
            start = e.attribute("enddt");
            rec->setMonthly( KCal::Recurrence::rMonthlyPos,
                             freq,fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setMonthly( KCal::Recurrence::rMonthlyPos,
                             freq,  -1 );
        }
        QBitArray array( 7);
        array.fill( false );
        QDate date = event->dtStart().date();
        array.setBit( date.dayOfWeek() - 1 );
        rec->addMonthlyPos( rposition, array );

    }else if ( type == "MonthlyDate" ) {
        // int rposition = e.attribute("rposition").toInt();
        if ( hasEnd ) {
            start = e.attribute("enddt");
            rec->setMonthly( KCal::Recurrence::rMonthlyDay,
                             freq,fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setMonthly( KCal::Recurrence::rMonthlyDay,
                             freq,  -1 );
        }
        QDate date = event->dtStart().date();
        rec->addMonthlyDay( date.day() );

    }else if ( type == "Yearly" ) {
        if (hasEnd ) {
            start = e.attribute("enddt");
            rec->setYearly( KCal::Recurrence::rYearlyDay,
                            freq,
                            fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setYearly( KCal::Recurrence::rYearlyDay,
                            freq, -1 );
        }
        rec->addYearlyNum( event->dtStart().date().dayOfYear() );
    }
    return event;
}

KSync::EventSyncee* DateBook::toKDE( const QString& fileName )
{
//    kdDebug(5229) << "To KDE " << endl;
    KSync::EventSyncee* syncee = new KSync::EventSyncee();
    if( device() )
	syncee->setSupports( device()->supports( Device::Calendar ) );

    QFile file( fileName );
    if ( file.open( IO_ReadOnly ) ) {
        //kdDebug(5229) << "file open" << endl;
        QDomDocument doc("mydocument");
        if ( doc.setContent( &file ) ) {
//            kdDebug(5229) << "setContent" << endl;
            QDomElement docElem = doc.documentElement();
//            kdDebug(5229) << "TagName docElem " << docElem.tagName() << endl;
            QDomNode n = docElem.firstChild();
            QString dummy;
            while (!n.isNull() ) {
                QDomElement el = n.toElement();
                if (!el.isNull() ) {
//                    kdDebug(5229) << "e " << el.tagName() << endl;
//                    kdDebug(5229) << "e.isNull not" << endl;

                    if ( el.tagName() == "events") {

                        QDomNode no = el.firstChild();
                        while (!no.isNull() ) {
                            QDomElement e = no.toElement();

                            if (!e.isNull() ) {
                                if (e.tagName() == "event") {
                                    KCal::Event* event = toEvent( e );
                                    if (event != 0 ) {
                                        KSync::EventSyncEntry* entry;
                                        entry = new KSync::EventSyncEntry( event );
                                        syncee->addEntry( entry );
                                    }
                                }
                            }
                            no = no.nextSibling();
                        }
                    }
                    n = n.nextSibling();
                }// n.isNULL
            }
        }
    }
    return syncee;
}

KTempFile* DateBook::fromKDE( KSync::EventSyncee* syncee )
{
    m_kde2opie.clear();
    Kontainer::ValueList newIds = syncee->ids( "EventSyncEntry");
    Kontainer::ValueList::ConstIterator idIt;
    for ( idIt = newIds.begin(); idIt != newIds.end(); ++idIt ) {
        m_helper->addId("EventSyncEntry",  (*idIt).first(),  (*idIt).second() );
    }
    KTempFile* tempFile = file();
    if ( tempFile->textStream() ) {
        QTextStream *stream = tempFile->textStream();
        stream->setEncoding( QTextStream::UnicodeUTF8 );
        *stream <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
        *stream << "<!DOCTYPE DATEBOOK><DATEBOOK>" << endl;
        KSync::EventSyncEntry *entry;
        KCal::Event *event;
        *stream << "<events>" << endl;
        for ( entry = (KSync::EventSyncEntry*) syncee->firstEntry();
              entry != 0;
              entry = (KSync::EventSyncEntry*) syncee->nextEntry() )
        {
            if (entry->state() == KSync::SyncEntry::Removed )
                continue;
            event = entry->incidence();
            *stream << event2string( event ) << endl;
        }
        *stream << "</events>" << endl;
        *stream << "</DATEBOOK>" << endl;

    }
    if (m_helper )
        m_helper->replaceIds( "EventSyncEntry",  m_kde2opie );

    tempFile->close();
    return tempFile;
}
QString DateBook::event2string( KCal::Event *event )
{
    QString str;
    str.append( "<event ");
    str.append( "description=\"" +escape( event->summary() ) + "\" ");
    str.append( "location=\"" + escape( event->location() ) + "\" ");
    str.append( "categories=\"" +  categoriesToNumber( event->categories() ) + "\" ");
    str.append( "uid=\"" +  konnectorId("EventSyncEntry", event->uid() ) + "\" ");
    str.append( "start=\"" + QString::number( toUTC( event->dtStart() ) ) + "\" ");
    str.append( "end=\"" +  QString::number(  toUTC( event->dtEnd() ) ) + "\" ");
    str.append( "note=\"" + escape( event->description() ) + "\" "); //use escapeString copied from TT
    if ( event->doesFloat() )
        str.append( "type=\"AllDay\" ");
    // recurrence
    KCal::Recurrence *rec = event->recurrence();
    if ( rec->doesRecur() ) {
        QString type;
        switch( rec->doesRecur() ) {
        case KCal::Recurrence::rDaily :{
            type = "Daily";
            break;
        }
        case KCal::Recurrence::rWeekly :{
            type = "Weekly";
            char day = 0; // signed
            QBitArray array = rec->days();
            if ( array.testBit(0 ) ) day |= Monday;
            if ( array.testBit(1 ) ) day |= Tuesday;
            if ( array.testBit(2 ) ) day |= Wednesday;
            if ( array.testBit(3 ) ) day |= Thursday;
            if ( array.testBit(4 ) ) day |= Friday;
            if ( array.testBit(5 ) ) day |= Saturday;
            if ( array.testBit(6 ) ) day |= Sunday;
            str.append( "rweekdays=\"" + QString::number(static_cast<int> (day) ) + "\" ");
            break;
        }
        case KCal::Recurrence::rMonthlyPos :{
            str.append( "rposition=\"" + QString::number( week( event->dtStart().date() ) )  + "\" ");
            type = "MonthlyDay";
            break;
        }
        case KCal::Recurrence::rMonthlyDay :{
            type = "MonthlyDate";

            break;
        }
        case KCal::Recurrence::rYearlyDay :{
            type = "Yearly";
            break;
        }
        case KCal::Recurrence::rNone : // fall through
        default :
            break;
        }
        str.append( "rtype=\"" + type + "\" ");
        str.append( "rfreq=\"" + QString::number( rec->frequency() ) + "\" ");
        if ( rec->duration() == -1 || rec->duration() != 0 )
            str.append( "rhasenddate=\"0\" ");
        else if ( rec->duration() == 0 ) {
            str.append( "rhasenddate=\"1\" ");
            str.append( "enddt=\"" + QString::number( toUTC(rec->endDate() ) ) + "\" ");
        }
        str.append( "created=\"" + QString::number( toUTC(rec->recurStart() ) ) + "\" ");

    }
    // alarm
    KCal::Alarm *al = event->alarms().first();
    if ( al != 0 ) {
        int sec = al->startOffset().asSeconds();
        sec = sec > 0 ? sec % 60 : sec % -60; // as minutes now
	if( sec != 0 ){
            str.append( "alarm=\"" +QString::number( sec )  + "\" ");
	    QString sound = al->audioFile();
    	    if ( sound != "loud" && sound != "silent" ) {
        	if ( sound.isEmpty() )
                	sound = QString::fromLatin1("silent");
	        else
    	            sound = QString::fromLatin1("loud");
    	    }
    	    str.append( "sound=\"" +  sound + "\" ");
	}
    }
    str.append( " />" );
    return str;
}
<commit_msg>Fix writing of recurrence in XML files If no recurrence day is set set it to the event start day only save if days > 0<commit_after>
#include <qbitarray.h>
#include <qbuffer.h>
#include <qdom.h>
#include <qfile.h>
#include <qtextstream.h>

#include <kdebug.h>

#include <eventsyncee.h>

#include "device.h"
#include "datebook.h"

using namespace OpieHelper;

namespace {
// from TT
int week ( const QDate &start ) {
    int stop = start.day();
    int sentinel = start.dayOfWeek();
    int dayOfWeek = QDate( start.year(),  start.month(),  1 ).dayOfWeek();
    int week = 1;
    for ( int i = 1; i < stop; i++ ) {
        if ( dayOfWeek++ == sentinel )
            week++;
        if ( dayOfWeek > 7 )
            dayOfWeek = 0;
    }
    return week;
}

};

DateBook::DateBook( CategoryEdit* edit,
                    KSync::KonnectorUIDHelper* helper,
                    const QString& tz,
                    bool meta, Device *dev )
    : Base( edit,  helper,  tz, meta, dev )
{
}
DateBook::~DateBook(){
}
/**
 * Converts an Opie Event to a KDE one
 */
KCal::Event* DateBook::toEvent( QDomElement e) {
//    kdDebug(522) << "inside event" << endl;
    KCal::Event* event = new KCal::Event();
    QStringList list = QStringList::split(";",  e.attribute("categories") );
    QStringList categories;
    for ( uint i = 0; i < list.count(); i++ ) {
//        kdDebug(5229) << list[i]<< " categories " << m_edit->categoryById( list[i],  "Calendar") << endl;
        categories.append(m_edit->categoryById(list[i], "Calendar") );
    }
    if (!categories.isEmpty() ) {
        event->setCategories( categories );
    }
    //event->setDescription(e.attribute("Description") );
    event->setSummary( e.attribute("description") );
    event->setUid( kdeId( "EventSyncEntry",  e.attribute("uid") ) );
    event->setDescription( e.attribute("note") );
    event->setLocation( e.attribute("location") );
    // time

    QString start = e.attribute("start");
//    kdDebug(5229) << "Start " << fromUTC( (time_t) start.toLong() ).toString() << endl;
    event->setDtStart( fromUTC( (time_t) start.toLong() ) );

    QString end = e.attribute("end");
//    kdDebug(5229) << "End " << fromUTC( (time_t) end.toLong() ).toString() << endl;
    event->setDtEnd( fromUTC( (time_t) end.toLong() ) );

    // type
    if ( e.attribute("type") == "AllDay" ) {
        event->setFloats( true );
    }else{
        event->setFloats( false );
    }

    // alarm
    QString alarm = e.attribute("alarm");

    /* there is an alarm */
    if (!alarm.isEmpty() ) {
        KCal::Alarm *al = new KCal::Alarm( event );
        al->setStartOffset( alarm.toInt() * -60 );
        al->setAudioAlarm( e.attribute("sound") );
        event->addAlarm( al );

        kdDebug(5229) << "Alarm offset " << al->startOffset().asSeconds() << endl;
        kdDebug(5229) << "Alarm " << e.attribute("alarm") << " " << e.attribute("sound") << endl;
    }

    // Recurrence damn I feared to do that
    QString type = e.attribute("rtype");
    int freq = e.attribute("rfreq").toInt();
    bool hasEnd = e.attribute("rhasenddate").toInt();
    //kdDebug(5229) << "HasEndDate: " << hasEnd << endl;

    KCal::Recurrence *rec = event->recurrence();
    start = e.attribute("created");
    rec->setRecurStart( fromUTC( (time_t) start.toLong() ) );

    if ( type == "Daily" ) {
        if ( hasEnd ) {
            start = e.attribute("enddt");
            rec->setDaily(freq,  fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setDaily( freq,  -1 );
        }
        // weekly
    }else if ( type == "Weekly") {
        int days = e.attribute("rweekdays").toInt();
        QBitArray bits( 7 );
        bits.fill( false );
        if ( Monday & days )
            bits.setBit( 0 );
        if ( Tuesday & days )
            bits.setBit( 1 );
        if ( Wednesday & days )
            bits.setBit( 2 );
        if ( Thursday & days )
            bits.setBit( 3 );
        if ( Friday & days )
            bits.setBit( 4 );
        if ( Saturday & days )
            bits.setBit( 5 );
        if ( Sunday & days )
            bits.setBit( 6 );

        if ( hasEnd ) {
            start = e.attribute("enddt");
//            kdDebug(5229) << "has end " << start << endl;
            rec->setWeekly( freq,  bits, fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setWeekly( freq,  bits,  -1 );
        }
    // monthly
    }else if ( type == "MonthlyDay" ) {
        // monthly day the  1st Saturday of the month
        int rposition = e.attribute("rposition").toInt();
        if ( hasEnd ) {
            start = e.attribute("enddt");
            rec->setMonthly( KCal::Recurrence::rMonthlyPos,
                             freq,fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setMonthly( KCal::Recurrence::rMonthlyPos,
                             freq,  -1 );
        }
        QBitArray array( 7);
        array.fill( false );
        QDate date = event->dtStart().date();
        array.setBit( date.dayOfWeek() - 1 );
        rec->addMonthlyPos( rposition, array );

    }else if ( type == "MonthlyDate" ) {
        // int rposition = e.attribute("rposition").toInt();
        if ( hasEnd ) {
            start = e.attribute("enddt");
            rec->setMonthly( KCal::Recurrence::rMonthlyDay,
                             freq,fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setMonthly( KCal::Recurrence::rMonthlyDay,
                             freq,  -1 );
        }
        QDate date = event->dtStart().date();
        rec->addMonthlyDay( date.day() );

    }else if ( type == "Yearly" ) {
        if (hasEnd ) {
            start = e.attribute("enddt");
            rec->setYearly( KCal::Recurrence::rYearlyDay,
                            freq,
                            fromUTC( (time_t) start.toLong() ).date() );
        }else{
            rec->setYearly( KCal::Recurrence::rYearlyDay,
                            freq, -1 );
        }
        rec->addYearlyNum( event->dtStart().date().dayOfYear() );
    }
    return event;
}

KSync::EventSyncee* DateBook::toKDE( const QString& fileName )
{
//    kdDebug(5229) << "To KDE " << endl;
    KSync::EventSyncee* syncee = new KSync::EventSyncee();
    if( device() )
	syncee->setSupports( device()->supports( Device::Calendar ) );

    QFile file( fileName );
    if ( file.open( IO_ReadOnly ) ) {
        //kdDebug(5229) << "file open" << endl;
        QDomDocument doc("mydocument");
        if ( doc.setContent( &file ) ) {
//            kdDebug(5229) << "setContent" << endl;
            QDomElement docElem = doc.documentElement();
//            kdDebug(5229) << "TagName docElem " << docElem.tagName() << endl;
            QDomNode n = docElem.firstChild();
            QString dummy;
            while (!n.isNull() ) {
                QDomElement el = n.toElement();
                if (!el.isNull() ) {
//                    kdDebug(5229) << "e " << el.tagName() << endl;
//                    kdDebug(5229) << "e.isNull not" << endl;

                    if ( el.tagName() == "events") {

                        QDomNode no = el.firstChild();
                        while (!no.isNull() ) {
                            QDomElement e = no.toElement();

                            if (!e.isNull() ) {
                                if (e.tagName() == "event") {
                                    KCal::Event* event = toEvent( e );
                                    if (event != 0 ) {
                                        KSync::EventSyncEntry* entry;
                                        entry = new KSync::EventSyncEntry( event );
                                        syncee->addEntry( entry );
                                    }
                                }
                            }
                            no = no.nextSibling();
                        }
                    }
                    n = n.nextSibling();
                }// n.isNULL
            }
        }
    }
    return syncee;
}

KTempFile* DateBook::fromKDE( KSync::EventSyncee* syncee )
{
    m_kde2opie.clear();
    Kontainer::ValueList newIds = syncee->ids( "EventSyncEntry");
    Kontainer::ValueList::ConstIterator idIt;
    for ( idIt = newIds.begin(); idIt != newIds.end(); ++idIt ) {
        m_helper->addId("EventSyncEntry",  (*idIt).first(),  (*idIt).second() );
    }
    KTempFile* tempFile = file();
    if ( tempFile->textStream() ) {
        QTextStream *stream = tempFile->textStream();
        stream->setEncoding( QTextStream::UnicodeUTF8 );
        *stream <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
        *stream << "<!DOCTYPE DATEBOOK><DATEBOOK>" << endl;
        KSync::EventSyncEntry *entry;
        KCal::Event *event;
        *stream << "<events>" << endl;
        for ( entry = (KSync::EventSyncEntry*) syncee->firstEntry();
              entry != 0;
              entry = (KSync::EventSyncEntry*) syncee->nextEntry() )
        {
            if (entry->state() == KSync::SyncEntry::Removed )
                continue;
            event = entry->incidence();
            *stream << event2string( event ) << endl;
        }
        *stream << "</events>" << endl;
        *stream << "</DATEBOOK>" << endl;

    }
    if (m_helper )
        m_helper->replaceIds( "EventSyncEntry",  m_kde2opie );

    tempFile->close();
    return tempFile;
}
QString DateBook::event2string( KCal::Event *event )
{
    QString str;
    str.append( "<event ");
    str.append( "description=\"" +escape( event->summary() ) + "\" ");
    str.append( "location=\"" + escape( event->location() ) + "\" ");
    str.append( "categories=\"" +  categoriesToNumber( event->categories() ) + "\" ");
    str.append( "uid=\"" +  konnectorId("EventSyncEntry", event->uid() ) + "\" ");
    str.append( "start=\"" + QString::number( toUTC( event->dtStart() ) ) + "\" ");
    str.append( "end=\"" +  QString::number(  toUTC( event->dtEnd() ) ) + "\" ");
    str.append( "note=\"" + escape( event->description() ) + "\" "); //use escapeString copied from TT
    if ( event->doesFloat() )
        str.append( "type=\"AllDay\" ");
    // recurrence
    KCal::Recurrence *rec = event->recurrence();
    if ( rec->doesRecur() ) {
        QString type;
        switch( rec->doesRecur() ) {
        case KCal::Recurrence::rDaily :{
            type = "Daily";
            break;
        }
        case KCal::Recurrence::rWeekly :{
            type = "Weekly";
            char day = 0; // signed
            QBitArray array = rec->days();
            if ( array.testBit(0 ) ) day |= Monday;
            if ( array.testBit(1 ) ) day |= Tuesday;
            if ( array.testBit(2 ) ) day |= Wednesday;
            if ( array.testBit(3 ) ) day |= Thursday;
            if ( array.testBit(4 ) ) day |= Friday;
            if ( array.testBit(5 ) ) day |= Saturday;
            if ( array.testBit(6 ) ) day |= Sunday;

            /* need to be > 0  we set the repeat day to the day where the event takes place*/
            if ( day < 0 ) {
                switch( event->dtStart().date().dayOfWeek() ) {
                case 1: // Monday
                    day = Monday;
                    break;
                case 2: // Tuesday
                    day = Tuesday;
                    break;
                case 3: // Wednesday
                    day = Wednesday;
                    break;
                case 4: //  Thursday
                    day = Thursday;
                    break;
                case 5: // Friday
                    day = Friday;
                    break;
                case 6: // Staurday
                    day = Saturday;
                    break;
                default:// should never happen
                case 7: // Sunday
                    day = Sunday;
                    break;
                }

            }
            str.append( "rweekdays=\"" + QString::number(static_cast<int> (day) ) + "\" ");
            break;
        }
        case KCal::Recurrence::rMonthlyPos :{
            int rpos = week( event->dtStart().date() );
            if ( rpos != 0 )
                str.append( "rposition=\"" + QString::number(rpos)  + "\" ");
            type = "MonthlyDay";
            break;
        }
        case KCal::Recurrence::rMonthlyDay :{
            type = "MonthlyDate";

            break;
        }
        case KCal::Recurrence::rYearlyDay :{
            type = "Yearly";
            break;
        }
        case KCal::Recurrence::rNone : // fall through
        default :
            break;
        }
        str.append( "rtype=\"" + type + "\" ");
        str.append( "rfreq=\"" + QString::number( rec->frequency() ) + "\" ");
        if ( rec->duration() == -1 || rec->duration() != 0 )
            str.append( "rhasenddate=\"0\" ");
        else if ( rec->duration() == 0 ) {
            str.append( "rhasenddate=\"1\" ");
            str.append( "enddt=\"" + QString::number( toUTC(rec->endDate() ) ) + "\" ");
        }
        str.append( "created=\"" + QString::number( toUTC(rec->recurStart() ) ) + "\" ");

    }
    // alarm
    KCal::Alarm *al = event->alarms().first();
    if ( al != 0 ) {
        int sec = al->startOffset().asSeconds();
        sec = sec > 0 ? sec % 60 : sec % -60; // as minutes now
	if( sec != 0 ){
            str.append( "alarm=\"" +QString::number( sec )  + "\" ");
	    QString sound = al->audioFile();
    	    if ( sound != "loud" && sound != "silent" ) {
        	if ( sound.isEmpty() )
                	sound = QString::fromLatin1("silent");
	        else
    	            sound = QString::fromLatin1("loud");
    	    }
    	    str.append( "sound=\"" +  sound + "\" ");
	}
    }
    str.append( " />" );
    return str;
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * customwidgets.cpp: Custom widgets
 ****************************************************************************
 * Copyright (C) 2006 the VideoLAN team
 * Copyright (C) 2004 Daniel Molkentin <molkentin@kde.org>
 * $Id$
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 * The "ClickLineEdit" control is based on code by  Daniel Molkentin
 * <molkentin@kde.org> for libkdepim
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "customwidgets.hpp"
#include <QPainter>
#include <QLineEdit>
#include <QColorGroup>
#include <QRect>
#include <QKeyEvent>
#include <QWheelEvent>

#include <vlc_keys.h>

ClickLineEdit::ClickLineEdit( const QString &msg, QWidget *parent) : QLineEdit( parent )
{
    mDrawClickMsg = true;
    setClickMessage( msg );
}

void ClickLineEdit::setClickMessage( const QString &msg )
{
    mClickMessage = msg;
    repaint();
}

void ClickLineEdit::setText( const QString &txt )
{
    mDrawClickMsg = txt.isEmpty();
    repaint();
    QLineEdit::setText( txt );
}

void ClickLineEdit::paintEvent( QPaintEvent *pe )
{
    QLineEdit::paintEvent( pe );
    if ( mDrawClickMsg == true && !hasFocus() ) {
        QPainter p( this );
        QPen tmp = p.pen();
        p.setPen( palette().color( QPalette::Disabled, QPalette::Text ) );
        QRect cr = contentsRect();
        // Add two pixel margin on the left side
        cr.setLeft( cr.left() + 3 );
        p.drawText( cr, Qt::AlignLeft | Qt::AlignVCenter, mClickMessage );
        p.setPen( tmp );
        p.end();
    }
}

void ClickLineEdit::dropEvent( QDropEvent *ev )
{
    mDrawClickMsg = false;
    QLineEdit::dropEvent( ev );
}

void ClickLineEdit::focusInEvent( QFocusEvent *ev )
{
    if ( mDrawClickMsg == true ) {
        mDrawClickMsg = false;
        repaint();
    }
    QLineEdit::focusInEvent( ev );
}

void ClickLineEdit::focusOutEvent( QFocusEvent *ev )
{
    if ( text().isEmpty() ) {
        mDrawClickMsg = true;
        repaint();
    }
    QLineEdit::focusOutEvent( ev );
}

/***************************************************************************
 * Hotkeys converters
 ***************************************************************************/
int qtKeyModifiersToVLC( QInputEvent* e )
{
    int i_keyModifiers = 0;
    if( e->modifiers() & Qt::ShiftModifier ) i_keyModifiers |= KEY_MODIFIER_SHIFT;
    if( e->modifiers() & Qt::AltModifier ) i_keyModifiers |= KEY_MODIFIER_ALT;
    if( e->modifiers() & Qt::ControlModifier ) i_keyModifiers |= KEY_MODIFIER_CTRL;
    if( e->modifiers() & Qt::MetaModifier ) i_keyModifiers |= KEY_MODIFIER_META;
    return i_keyModifiers;
}

int qtEventToVLCKey( QKeyEvent *e )
{
    int i_vlck = 0;
    /* Handle modifiers */
    i_vlck |= qtKeyModifiersToVLC( e );

    bool found = false;
    /* Look for some special keys */
#define HANDLE( qt, vk ) case Qt::qt : i_vlck |= vk; found = true;break
    switch( e->key() )
    {
        HANDLE( Key_Left, KEY_LEFT );
        HANDLE( Key_Right, KEY_RIGHT );
        HANDLE( Key_Up, KEY_UP );
        HANDLE( Key_Down, KEY_DOWN );
        HANDLE( Key_Space, KEY_SPACE );
        HANDLE( Key_Escape, KEY_ESC );
        HANDLE( Key_Enter, KEY_ENTER );
        HANDLE( Key_F1, KEY_F1 );
        HANDLE( Key_F2, KEY_F2 );
        HANDLE( Key_F3, KEY_F3 );
        HANDLE( Key_F4, KEY_F4 );
        HANDLE( Key_F5, KEY_F5 );
        HANDLE( Key_F6, KEY_F6 );
        HANDLE( Key_F7, KEY_F7 );
        HANDLE( Key_F8, KEY_F8 );
        HANDLE( Key_F9, KEY_F9 );
        HANDLE( Key_F10, KEY_F10 );
        HANDLE( Key_F11, KEY_F11 );
        HANDLE( Key_F12, KEY_F12 );
        HANDLE( Key_PageUp, KEY_PAGEUP );
        HANDLE( Key_PageDown, KEY_PAGEDOWN );
        HANDLE( Key_Home, KEY_HOME );
        HANDLE( Key_End, KEY_END );
        HANDLE( Key_Insert, KEY_INSERT );
        HANDLE( Key_Delete, KEY_DELETE );
        HANDLE( Key_VolumeDown, KEY_VOLUME_DOWN);
        HANDLE( Key_VolumeUp, KEY_VOLUME_UP );
        HANDLE( Key_VolumeMute, KEY_VOLUME_MUTE );
        HANDLE( Key_MediaPlay, KEY_MEDIA_PLAY_PAUSE );
        HANDLE( Key_MediaStop, KEY_MEDIA_STOP );
        HANDLE( Key_MediaPrevious, KEY_MEDIA_PREV_TRACK );
        HANDLE( Key_MediaNext, KEY_MEDIA_NEXT_TRACK );

    }
    if( !found )
    {
        /* Force lowercase */
        if( e->key() >= Qt::Key_A && e->key() <= Qt::Key_Z )
            i_vlck += e->key() + 32;
        /* Rest of the ascii range */
        else if( e->key() >= Qt::Key_Space && e->key() <= Qt::Key_AsciiTilde )
            i_vlck += e->key();
    }
    return i_vlck;
}

int qtWheelEventToVLCKey( QWheelEvent *e )
{
    int i_vlck = 0;
    /* Handle modifiers */
    i_vlck |= qtKeyModifiersToVLC( e );
    if ( e->delta() > 0 )
        i_vlck |= KEY_MOUSEWHEELUP;
    else
        i_vlck |= KEY_MOUSEWHEELDOWN;
    return i_vlck;
}

QString VLCKeyToString( int val )
{
    const char *base = KeyToString (val & ~KEY_MODIFIER);

    QString r = "";
    if( val & KEY_MODIFIER_CTRL )
        r+= "Ctrl+";
    if( val & KEY_MODIFIER_ALT )
        r+= "Alt+";
    if( val & KEY_MODIFIER_SHIFT )
        r+= "Shift+";

    return r + (base ? base : "Unset");
}

<commit_msg>Someone did not RTFM Qt::Key_Enter != Qt::Key_Return :D<commit_after>/*****************************************************************************
 * customwidgets.cpp: Custom widgets
 ****************************************************************************
 * Copyright (C) 2006 the VideoLAN team
 * Copyright (C) 2004 Daniel Molkentin <molkentin@kde.org>
 * $Id$
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
 * The "ClickLineEdit" control is based on code by  Daniel Molkentin
 * <molkentin@kde.org> for libkdepim
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "customwidgets.hpp"
#include <QPainter>
#include <QLineEdit>
#include <QColorGroup>
#include <QRect>
#include <QKeyEvent>
#include <QWheelEvent>

#include <vlc_keys.h>

ClickLineEdit::ClickLineEdit( const QString &msg, QWidget *parent) : QLineEdit( parent )
{
    mDrawClickMsg = true;
    setClickMessage( msg );
}

void ClickLineEdit::setClickMessage( const QString &msg )
{
    mClickMessage = msg;
    repaint();
}

void ClickLineEdit::setText( const QString &txt )
{
    mDrawClickMsg = txt.isEmpty();
    repaint();
    QLineEdit::setText( txt );
}

void ClickLineEdit::paintEvent( QPaintEvent *pe )
{
    QLineEdit::paintEvent( pe );
    if ( mDrawClickMsg == true && !hasFocus() ) {
        QPainter p( this );
        QPen tmp = p.pen();
        p.setPen( palette().color( QPalette::Disabled, QPalette::Text ) );
        QRect cr = contentsRect();
        // Add two pixel margin on the left side
        cr.setLeft( cr.left() + 3 );
        p.drawText( cr, Qt::AlignLeft | Qt::AlignVCenter, mClickMessage );
        p.setPen( tmp );
        p.end();
    }
}

void ClickLineEdit::dropEvent( QDropEvent *ev )
{
    mDrawClickMsg = false;
    QLineEdit::dropEvent( ev );
}

void ClickLineEdit::focusInEvent( QFocusEvent *ev )
{
    if ( mDrawClickMsg == true ) {
        mDrawClickMsg = false;
        repaint();
    }
    QLineEdit::focusInEvent( ev );
}

void ClickLineEdit::focusOutEvent( QFocusEvent *ev )
{
    if ( text().isEmpty() ) {
        mDrawClickMsg = true;
        repaint();
    }
    QLineEdit::focusOutEvent( ev );
}

/***************************************************************************
 * Hotkeys converters
 ***************************************************************************/
int qtKeyModifiersToVLC( QInputEvent* e )
{
    int i_keyModifiers = 0;
    if( e->modifiers() & Qt::ShiftModifier ) i_keyModifiers |= KEY_MODIFIER_SHIFT;
    if( e->modifiers() & Qt::AltModifier ) i_keyModifiers |= KEY_MODIFIER_ALT;
    if( e->modifiers() & Qt::ControlModifier ) i_keyModifiers |= KEY_MODIFIER_CTRL;
    if( e->modifiers() & Qt::MetaModifier ) i_keyModifiers |= KEY_MODIFIER_META;
    return i_keyModifiers;
}

int qtEventToVLCKey( QKeyEvent *e )
{
    int i_vlck = 0;
    /* Handle modifiers */
    i_vlck |= qtKeyModifiersToVLC( e );

    bool found = false;
    /* Look for some special keys */
#define HANDLE( qt, vk ) case Qt::qt : i_vlck |= vk; found = true;break
    switch( e->key() )
    {
        HANDLE( Key_Left, KEY_LEFT );
        HANDLE( Key_Right, KEY_RIGHT );
        HANDLE( Key_Up, KEY_UP );
        HANDLE( Key_Down, KEY_DOWN );
        HANDLE( Key_Space, KEY_SPACE );
        HANDLE( Key_Escape, KEY_ESC );
        HANDLE( Key_Return, KEY_ENTER );
        HANDLE( Key_Enter, KEY_ENTER );
        HANDLE( Key_F1, KEY_F1 );
        HANDLE( Key_F2, KEY_F2 );
        HANDLE( Key_F3, KEY_F3 );
        HANDLE( Key_F4, KEY_F4 );
        HANDLE( Key_F5, KEY_F5 );
        HANDLE( Key_F6, KEY_F6 );
        HANDLE( Key_F7, KEY_F7 );
        HANDLE( Key_F8, KEY_F8 );
        HANDLE( Key_F9, KEY_F9 );
        HANDLE( Key_F10, KEY_F10 );
        HANDLE( Key_F11, KEY_F11 );
        HANDLE( Key_F12, KEY_F12 );
        HANDLE( Key_PageUp, KEY_PAGEUP );
        HANDLE( Key_PageDown, KEY_PAGEDOWN );
        HANDLE( Key_Home, KEY_HOME );
        HANDLE( Key_End, KEY_END );
        HANDLE( Key_Insert, KEY_INSERT );
        HANDLE( Key_Delete, KEY_DELETE );
        HANDLE( Key_VolumeDown, KEY_VOLUME_DOWN);
        HANDLE( Key_VolumeUp, KEY_VOLUME_UP );
        HANDLE( Key_VolumeMute, KEY_VOLUME_MUTE );
        HANDLE( Key_MediaPlay, KEY_MEDIA_PLAY_PAUSE );
        HANDLE( Key_MediaStop, KEY_MEDIA_STOP );
        HANDLE( Key_MediaPrevious, KEY_MEDIA_PREV_TRACK );
        HANDLE( Key_MediaNext, KEY_MEDIA_NEXT_TRACK );

    }
    if( !found )
    {
        /* Force lowercase */
        if( e->key() >= Qt::Key_A && e->key() <= Qt::Key_Z )
            i_vlck += e->key() + 32;
        /* Rest of the ascii range */
        else if( e->key() >= Qt::Key_Space && e->key() <= Qt::Key_AsciiTilde )
            i_vlck += e->key();
    }
    return i_vlck;
}

int qtWheelEventToVLCKey( QWheelEvent *e )
{
    int i_vlck = 0;
    /* Handle modifiers */
    i_vlck |= qtKeyModifiersToVLC( e );
    if ( e->delta() > 0 )
        i_vlck |= KEY_MOUSEWHEELUP;
    else
        i_vlck |= KEY_MOUSEWHEELDOWN;
    return i_vlck;
}

QString VLCKeyToString( int val )
{
    const char *base = KeyToString (val & ~KEY_MODIFIER);

    QString r = "";
    if( val & KEY_MODIFIER_CTRL )
        r+= "Ctrl+";
    if( val & KEY_MODIFIER_ALT )
        r+= "Alt+";
    if( val & KEY_MODIFIER_SHIFT )
        r+= "Shift+";

    return r + (base ? base : "Unset");
}

<|endoftext|>


<commit_before>/*****************************************************************************
 * bitmap_font.cpp
 *****************************************************************************
 * Copyright (C) 2004 the VideoLAN team
 * $Id$
 *
 * Authors: Cyril Deguet     <asmax@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

#include "bitmap_font.hpp"
#include "generic_bitmap.hpp"
#include "../utils/ustring.hpp"

BitmapFont::BitmapFont( intf_thread_t *pIntf, const GenericBitmap &rBitmap,
                        const string &rType ):
    GenericFont( pIntf ), m_rBitmap( rBitmap )
{
    int i;

    // Build the character table
    if( rType == "digits" )
    {
        m_width = 9;
        m_height = 13;
        m_advance = 12;
        m_skip = 6;
        for( i = 0; i <= 9; i++ )
        {
            m_table['0'+i].m_xPos = i * m_width;
        }
        m_table[(size_t)'-'].m_xPos = 11 * m_width;
    }
    else if( rType == "text" )
    {
        m_width = 5;
        m_height = 6;
        m_advance = 5;
        m_skip = 5;
        for( i = 0; i < 26; i++ )
        {
            m_table['A'+i].m_xPos = m_table['a'+i].m_xPos = i * m_width;
        }
        m_table[(size_t)'"'].m_xPos = 26 * m_width;
        m_table[(size_t)'@'].m_xPos = 27 * m_width;
        m_table[(size_t)' '].m_xPos = 29 * m_width;
        for( i = 0; i <= 9; i++ )
        {
            m_table['0'+i].m_xPos = i * m_width;
            m_table['0'+i].m_yPos = m_height;
        }
        static const char specialChars[] = {'.', ':', '(', ')', '-', '\'',
            '!', '_', '+', '\\', '/', '[', ']', '^', '&', '%', ',', '=', '$',
            '#'};
        for( i = 0; i < 19; i++ )
        {
            m_table[(size_t)specialChars[i]].m_xPos = (11 + i) * m_width;
            m_table[(size_t)specialChars[i]].m_yPos = m_height;
        }
        m_table[(size_t)'?'].m_xPos = 4 * m_width;
        m_table[(size_t)'*'].m_xPos = 5 * m_width;
        m_table[(size_t)'?'].m_yPos = m_table[(size_t)'*'].m_yPos = 2 * m_height;
    }
}

GenericBitmap *BitmapFont::drawString( const UString &rString,
                                       uint32_t color, int maxWidth ) const
{
    uint32_t *pString = (uint32_t*)rString.u_str();
    // Compute the text width
    int width = 0;
    for( uint32_t *ptr = pString; *ptr; ptr++ )
    {
        uint32_t c = *ptr;
        if( c < 256 && m_table[c].m_xPos != -1 )
        {
            width += m_advance;
        }
        else
        {
            width += m_skip;
        }
    }
    // Create a bitmap
    BitmapImpl *pBmp = new BitmapImpl( getIntf(), width, m_height );
    int xDest = 0;
    while( *pString )
    {
        uint32_t c = *(pString++);
        if( c < 256 && m_table[c].m_xPos != -1 )
        {
            pBmp->drawBitmap( m_rBitmap, m_table[c].m_xPos, m_table[c].m_yPos,
                              xDest, 0, m_width, m_height );
            xDest += m_advance;
        }
        else
        {
            xDest += m_skip;
        }
    }
    return pBmp;
}

<commit_msg> * skins2/src/bitmap_font.cpp: log a warning if a char cannot be rendered<commit_after>/*****************************************************************************
 * bitmap_font.cpp
 *****************************************************************************
 * Copyright (C) 2004 the VideoLAN team
 * $Id$
 *
 * Authors: Cyril Deguet     <asmax@via.ecp.fr>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

#include "bitmap_font.hpp"
#include "generic_bitmap.hpp"
#include "../utils/ustring.hpp"

BitmapFont::BitmapFont( intf_thread_t *pIntf, const GenericBitmap &rBitmap,
                        const string &rType ):
    GenericFont( pIntf ), m_rBitmap( rBitmap )
{
    int i;

    // Build the character table
    if( rType == "digits" )
    {
        m_width = 9;
        m_height = 13;
        m_advance = 12;
        m_skip = 6;
        for( i = 0; i <= 9; i++ )
        {
            m_table['0'+i].m_xPos = i * m_width;
        }
        m_table[(size_t)'-'].m_xPos = 11 * m_width;
    }
    else if( rType == "text" )
    {
        m_width = 5;
        m_height = 6;
        m_advance = 5;
        m_skip = 5;
        for( i = 0; i < 26; i++ )
        {
            m_table['A'+i].m_xPos = m_table['a'+i].m_xPos = i * m_width;
        }
        m_table[(size_t)'"'].m_xPos = 26 * m_width;
        m_table[(size_t)'@'].m_xPos = 27 * m_width;
        m_table[(size_t)' '].m_xPos = 29 * m_width;
        for( i = 0; i <= 9; i++ )
        {
            m_table['0'+i].m_xPos = i * m_width;
            m_table['0'+i].m_yPos = m_height;
        }
        static const char specialChars[] = {'.', ':', '(', ')', '-', '\'',
            '!', '_', '+', '\\', '/', '[', ']', '^', '&', '%', ',', '=', '$',
            '#'};
        for( i = 0; i < 19; i++ )
        {
            m_table[(size_t)specialChars[i]].m_xPos = (11 + i) * m_width;
            m_table[(size_t)specialChars[i]].m_yPos = m_height;
        }
        m_table[(size_t)'?'].m_xPos = 4 * m_width;
        m_table[(size_t)'*'].m_xPos = 5 * m_width;
        m_table[(size_t)'?'].m_yPos = m_table[(size_t)'*'].m_yPos = 2 * m_height;
    }
}

GenericBitmap *BitmapFont::drawString( const UString &rString,
                                       uint32_t color, int maxWidth ) const
{
    uint32_t *pString = (uint32_t*)rString.u_str();
    // Compute the text width
    int width = 0;
    for( uint32_t *ptr = pString; *ptr; ptr++ )
    {
        uint32_t c = *ptr;
        if( c < 256 && m_table[c].m_xPos != -1 )
        {
            width += m_advance;
        }
        else
        {
            width += m_skip;
        }
    }
    // Create a bitmap
    BitmapImpl *pBmp = new BitmapImpl( getIntf(), width, m_height );
    int xDest = 0;
    while( *pString )
    {
        uint32_t c = *(pString++);
        if( c < 256 && m_table[c].m_xPos != -1 )
        {
            bool res = pBmp->drawBitmap( m_rBitmap, m_table[c].m_xPos,
                                         m_table[c].m_yPos, xDest, 0,
                                         m_width, m_height );
            if ( !res )
                msg_Warn( getIntf(), "BitmapFont::drawString: ignoring char" );
            xDest += m_advance;
        }
        else
        {
            xDest += m_skip;
        }
    }
    return pBmp;
}

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: fmmodel.cxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: fs $ $Date: 2000-10-20 16:27:18 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif

#ifndef SVX_LIGHT
#ifndef _SFX_OBJSH_HXX //autogen
#include <sfx2/objsh.hxx>
#endif
#else
class SfxObjectShell;
#endif

#pragma hdrstop

#ifndef _FM_FMMODEL_HXX
#include "fmmodel.hxx"
#endif

#ifndef _SVDIO_HXX
#include "svdio.hxx"
#endif

#ifndef _FM_PAGE_HXX
#include "fmpage.hxx"
#endif
#ifndef _SVX_FMUNDO_HXX
#include "fmundo.hxx"
#endif
#ifndef _SVX_SVDOBJ_HXX
#include "svdobj.hxx"
#endif

TYPEINIT1(FmFormModel, SdrModel);

struct FmFormModelImplData
{
    FmXUndoEnvironment*     pUndoEnv;
    XubString               sNextPageId;
};

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(SfxItemPool* pPool, SvPersist* pPers)
            :SdrModel(pPool, pPers, LOADREFCOUNTS)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_pImpl(NULL)
            ,m_bOpenInDesignMode(sal_True)
            ,m_bAutoControlFocus(sal_False)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(const XubString& rPath, SfxItemPool* pPool, SvPersist* pPers)
            :SdrModel(rPath, pPool, pPers)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_pImpl(NULL)
            ,m_bOpenInDesignMode(sal_True)
            ,m_bAutoControlFocus(sal_False)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(SfxItemPool* pPool, SvPersist* pPers,
                         FASTBOOL bUseExtColorTable
                         )
            :SdrModel(pPool, pPers, bUseExtColorTable, LOADREFCOUNTS)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_pImpl(NULL)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(const XubString& rPath, SfxItemPool* pPool, SvPersist* pPers,
                         FASTBOOL bUseExtColorTable)
            :SdrModel(rPath, pPool, pPers, bUseExtColorTable, LOADREFCOUNTS)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_bOpenInDesignMode(sal_True)
            ,m_bAutoControlFocus(sal_False)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Dtor
|*
\************************************************************************/
FmFormModel::~FmFormModel()
{
#ifndef SVX_LIGHT
    if (pObjShell && m_pImpl->pUndoEnv->IsListening(*pObjShell))
        SetObjectShell(NULL);

    ClearUndoBuffer();
    // minimale grenze fuer undos
    SetMaxUndoActionCount(1);

    m_pImpl->pUndoEnv->release();
    delete m_pImpl;

#endif
}

/*************************************************************************
|*
|* Copy-Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(const FmFormModel&)
{
    DBG_ERROR("FmFormModel: CopyCtor not implemented");
}

/*************************************************************************
|*
|* Operator=
|*
\************************************************************************/
void FmFormModel::operator=(const FmFormModel&)
{
    DBG_ERROR("FmFormModel: operator= not implemented");
}

/*************************************************************************
|*
|* Operator==
|*
\************************************************************************/
FASTBOOL FmFormModel::operator==(const FmFormModel&) const
{
    DBG_ERROR("FmFormModel: operator== not implemented");
    return sal_False;
}

/*************************************************************************
|*
|* Erzeugt eine neue Seite
|*
\************************************************************************/
SdrPage* FmFormModel::AllocPage(FASTBOOL bMasterPage)
{
    return new FmFormPage(*this, NULL, bMasterPage);
}

/*************************************************************************
|*
|* WriteData
|*
\************************************************************************/

void FmFormModel::WriteData(SvStream& rOut) const
{
#ifndef SVX_LIGHT

    if( rOut.GetVersion() < SOFFICE_FILEFORMAT_50 )
        ((FmFormModel*)this)->bStreamingOldVersion = sal_True;

    SdrModel::WriteData( rOut );

    //////////////////////////////////////////////////////////////////////
    // Speichern der Option OpenInDesignMode
    if (!bStreamingOldVersion)
    {
        SdrDownCompat aModelFormatCompat(rOut,STREAM_WRITE);

        sal_uInt8 nTemp = m_bOpenInDesignMode;
        rOut << nTemp;

        nTemp = m_bAutoControlFocus;
        rOut << nTemp;
    }

    ((FmFormModel*)this)->bStreamingOldVersion = sal_False;

#endif
}

/*************************************************************************
|*
|* ReadData
|*
\************************************************************************/
void FmFormModel::ReadData(const SdrIOHeader& rHead, SvStream& rIn)
{
    if( rIn.GetVersion() < SOFFICE_FILEFORMAT_50 )
        ((FmFormModel*)this)->bStreamingOldVersion = sal_True;

    SdrModel::ReadData( rHead, rIn );

    //////////////////////////////////////////////////////////////////////
    // Lesen der Option OpenInDesignMode
    if (!bStreamingOldVersion)
    {
        SdrDownCompat aCompat(rIn,STREAM_READ);
        sal_uInt8 nTemp = 0;
        rIn >> nTemp;
        m_bOpenInDesignMode = nTemp ? sal_True : sal_False;

        if (aCompat.GetBytesLeft())
        {   // it is a version which already wrote the AutoControlFocus flag
            rIn >> nTemp;
            m_bAutoControlFocus = nTemp ? sal_True : sal_False;
        }
    }

    ((FmFormModel*)this)->bStreamingOldVersion = sal_False;
}

/*************************************************************************
|*
|* InsertPage
|*
\************************************************************************/
void FmFormModel::InsertPage(SdrPage* pPage, sal_uInt16 nPos)
{
#ifndef SVX_LIGHT
    // hack solange Methode intern
    if (pObjShell && !m_pImpl->pUndoEnv->IsListening( *pObjShell ))
        SetObjectShell(pObjShell);
#endif

    SdrModel::InsertPage( pPage, nPos );

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->AddForms(((FmFormPage*)pPage)->GetForms());
#endif
}

/*************************************************************************
|*
|* InsertPage
|*
\************************************************************************/
SdrPage* FmFormModel::RemovePage(sal_uInt16 nPgNum)
{
    FmFormPage* pPage = (FmFormPage*)SdrModel::RemovePage(nPgNum);

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->RemoveForms(pPage->GetForms());
#endif

    return pPage;
}

/*************************************************************************
|*
|* InsertMasterPage
|*
\************************************************************************/
void FmFormModel::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos)
{
#ifndef SVX_LIGHT
    // hack solange Methode intern
    if (pObjShell && !m_pImpl->pUndoEnv->IsListening( *pObjShell ))
        SetObjectShell(pObjShell);
#endif

    SdrModel::InsertMasterPage(pPage, nPos);

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->AddForms(((FmFormPage*)pPage)->GetForms());
#endif
}

/*************************************************************************
|*
|* RemoveMasterPage
|*
\************************************************************************/
SdrPage* FmFormModel::RemoveMasterPage(sal_uInt16 nPgNum)
{
    FmFormPage* pPage = (FmFormPage*)SdrModel::RemoveMasterPage(nPgNum);

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->RemoveForms(pPage->GetForms());
#endif

    return pPage;
}

//------------------------------------------------------------------------
SdrLayerID FmFormModel::GetControlExportLayerId( const SdrObject& rObj ) const
{
    return rObj.GetLayer();
}

//------------------------------------------------------------------------
void FmFormModel::SetOpenInDesignMode( sal_Bool bOpenDesignMode )
{
#ifndef SVX_LIGHT
    if( bOpenDesignMode != m_bOpenInDesignMode )
    {
        m_bOpenInDesignMode = bOpenDesignMode;
        pObjShell->SetModified( sal_True );
    }
#endif
}

//------------------------------------------------------------------------
void FmFormModel::SetAutoControlFocus( sal_Bool _bAutoControlFocus )
{
#ifndef SVX_LIGHT
    if( _bAutoControlFocus != m_bAutoControlFocus )
    {
        m_bAutoControlFocus = _bAutoControlFocus;
        pObjShell->SetModified( sal_True );
    }
#endif
}

//------------------------------------------------------------------------
void FmFormModel::SetObjectShell( SfxObjectShell* pShell )
{
#ifndef SVX_LIGHT
    if (pShell == pObjShell)
        return;

    if (pObjShell)
    {
        m_pImpl->pUndoEnv->EndListening( *this );
        m_pImpl->pUndoEnv->EndListening( *pObjShell );
    }

    pObjShell = pShell;

    if (pObjShell)
    {
        m_pImpl->pUndoEnv->SetReadOnly(pObjShell->IsReadOnly() || pObjShell->IsReadOnlyUI());

        if (!m_pImpl->pUndoEnv->IsReadOnly())
             m_pImpl->pUndoEnv->StartListening(*this);

        m_pImpl->pUndoEnv->StartListening( *pObjShell );
    }
#endif
}

//------------------------------------------------------------------------
FmXUndoEnvironment& FmFormModel::GetUndoEnv()
{
    return *m_pImpl->pUndoEnv;
}

//------------------------------------------------------------------------
XubString FmFormModel::GetUniquePageId()
{
    XubString sReturn = m_pImpl->sNextPageId;

    xub_Unicode aNextChar = m_pImpl->sNextPageId.GetChar(m_pImpl->sNextPageId.Len() - 1);
    sal_Bool bNeedNewChar = sal_False;
    switch (aNextChar)
    {
        case '9' : aNextChar = 'A'; break;
        case 'Z' : aNextChar = 'a'; break;
        case 'z' : aNextChar = '0'; bNeedNewChar = sal_True; break;
        default: ++aNextChar; break;
    }
    m_pImpl->sNextPageId.SetChar(m_pImpl->sNextPageId.Len() - 1, aNextChar);
    if (bNeedNewChar)
        m_pImpl->sNextPageId += '0';

    return sReturn;
}

<commit_msg>94595# change default value for openindesignmode<commit_after>/*************************************************************************
 *
 *  $RCSfile: fmmodel.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: oj $ $Date: 2001-11-15 08:38:30 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (the "License"); You may not use this file
 *  except in compliance with the License. You may obtain a copy of the
 *  License at http://www.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifndef _TOOLS_DEBUG_HXX
#include <tools/debug.hxx>
#endif

#ifndef SVX_LIGHT
#ifndef _SFX_OBJSH_HXX //autogen
#include <sfx2/objsh.hxx>
#endif
#else
class SfxObjectShell;
#endif

#pragma hdrstop

#ifndef _FM_FMMODEL_HXX
#include "fmmodel.hxx"
#endif

#ifndef _SVDIO_HXX
#include "svdio.hxx"
#endif

#ifndef _FM_PAGE_HXX
#include "fmpage.hxx"
#endif
#ifndef _SVX_FMUNDO_HXX
#include "fmundo.hxx"
#endif
#ifndef _SVX_SVDOBJ_HXX
#include "svdobj.hxx"
#endif

TYPEINIT1(FmFormModel, SdrModel);

struct FmFormModelImplData
{
    FmXUndoEnvironment*     pUndoEnv;
    XubString               sNextPageId;
};

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(SfxItemPool* pPool, SvPersist* pPers)
            :SdrModel(pPool, pPers, LOADREFCOUNTS)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_pImpl(NULL)
            ,m_bOpenInDesignMode(sal_False)
            ,m_bAutoControlFocus(sal_False)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(const XubString& rPath, SfxItemPool* pPool, SvPersist* pPers)
            :SdrModel(rPath, pPool, pPers)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_pImpl(NULL)
            ,m_bOpenInDesignMode(sal_False)
            ,m_bAutoControlFocus(sal_False)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(SfxItemPool* pPool, SvPersist* pPers,
                         FASTBOOL bUseExtColorTable
                         )
            :SdrModel(pPool, pPers, bUseExtColorTable, LOADREFCOUNTS)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_pImpl(NULL)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(const XubString& rPath, SfxItemPool* pPool, SvPersist* pPers,
                         FASTBOOL bUseExtColorTable)
            :SdrModel(rPath, pPool, pPers, bUseExtColorTable, LOADREFCOUNTS)
            ,pObjShell(0)
            ,bStreamingOldVersion(sal_False)
            ,m_bOpenInDesignMode(sal_False)
            ,m_bAutoControlFocus(sal_False)
{
#ifndef SVX_LIGHT
    m_pImpl = new FmFormModelImplData;
    m_pImpl->pUndoEnv = new FmXUndoEnvironment(*this);
    m_pImpl->pUndoEnv->acquire();
    m_pImpl->sNextPageId = '0';
#endif
}

/*************************************************************************
|*
|* Dtor
|*
\************************************************************************/
FmFormModel::~FmFormModel()
{
#ifndef SVX_LIGHT
    if (pObjShell && m_pImpl->pUndoEnv->IsListening(*pObjShell))
        SetObjectShell(NULL);

    ClearUndoBuffer();
    // minimale grenze fuer undos
    SetMaxUndoActionCount(1);

    m_pImpl->pUndoEnv->release();
    delete m_pImpl;

#endif
}

/*************************************************************************
|*
|* Copy-Ctor
|*
\************************************************************************/
FmFormModel::FmFormModel(const FmFormModel&)
{
    DBG_ERROR("FmFormModel: CopyCtor not implemented");
}

/*************************************************************************
|*
|* Operator=
|*
\************************************************************************/
void FmFormModel::operator=(const FmFormModel&)
{
    DBG_ERROR("FmFormModel: operator= not implemented");
}

/*************************************************************************
|*
|* Operator==
|*
\************************************************************************/
FASTBOOL FmFormModel::operator==(const FmFormModel&) const
{
    DBG_ERROR("FmFormModel: operator== not implemented");
    return sal_False;
}

/*************************************************************************
|*
|* Erzeugt eine neue Seite
|*
\************************************************************************/
SdrPage* FmFormModel::AllocPage(FASTBOOL bMasterPage)
{
    return new FmFormPage(*this, NULL, bMasterPage);
}

/*************************************************************************
|*
|* WriteData
|*
\************************************************************************/

void FmFormModel::WriteData(SvStream& rOut) const
{
#ifndef SVX_LIGHT

    if( rOut.GetVersion() < SOFFICE_FILEFORMAT_50 )
        ((FmFormModel*)this)->bStreamingOldVersion = sal_True;

    SdrModel::WriteData( rOut );

    //////////////////////////////////////////////////////////////////////
    // Speichern der Option OpenInDesignMode
    if (!bStreamingOldVersion)
    {
        SdrDownCompat aModelFormatCompat(rOut,STREAM_WRITE);

        sal_uInt8 nTemp = m_bOpenInDesignMode;
        rOut << nTemp;

        nTemp = m_bAutoControlFocus;
        rOut << nTemp;
    }

    ((FmFormModel*)this)->bStreamingOldVersion = sal_False;

#endif
}

/*************************************************************************
|*
|* ReadData
|*
\************************************************************************/
void FmFormModel::ReadData(const SdrIOHeader& rHead, SvStream& rIn)
{
    if( rIn.GetVersion() < SOFFICE_FILEFORMAT_50 )
        ((FmFormModel*)this)->bStreamingOldVersion = sal_True;

    SdrModel::ReadData( rHead, rIn );

    //////////////////////////////////////////////////////////////////////
    // Lesen der Option OpenInDesignMode
    if (!bStreamingOldVersion)
    {
        SdrDownCompat aCompat(rIn,STREAM_READ);
        sal_uInt8 nTemp = 0;
        rIn >> nTemp;
        m_bOpenInDesignMode = nTemp ? sal_True : sal_False;

        if (aCompat.GetBytesLeft())
        {   // it is a version which already wrote the AutoControlFocus flag
            rIn >> nTemp;
            m_bAutoControlFocus = nTemp ? sal_True : sal_False;
        }
    }

    ((FmFormModel*)this)->bStreamingOldVersion = sal_False;
}

/*************************************************************************
|*
|* InsertPage
|*
\************************************************************************/
void FmFormModel::InsertPage(SdrPage* pPage, sal_uInt16 nPos)
{
#ifndef SVX_LIGHT
    // hack solange Methode intern
    if (pObjShell && !m_pImpl->pUndoEnv->IsListening( *pObjShell ))
        SetObjectShell(pObjShell);
#endif

    SdrModel::InsertPage( pPage, nPos );

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->AddForms(((FmFormPage*)pPage)->GetForms());
#endif
}

/*************************************************************************
|*
|* InsertPage
|*
\************************************************************************/
SdrPage* FmFormModel::RemovePage(sal_uInt16 nPgNum)
{
    FmFormPage* pPage = (FmFormPage*)SdrModel::RemovePage(nPgNum);

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->RemoveForms(pPage->GetForms());
#endif

    return pPage;
}

/*************************************************************************
|*
|* InsertMasterPage
|*
\************************************************************************/
void FmFormModel::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos)
{
#ifndef SVX_LIGHT
    // hack solange Methode intern
    if (pObjShell && !m_pImpl->pUndoEnv->IsListening( *pObjShell ))
        SetObjectShell(pObjShell);
#endif

    SdrModel::InsertMasterPage(pPage, nPos);

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->AddForms(((FmFormPage*)pPage)->GetForms());
#endif
}

/*************************************************************************
|*
|* RemoveMasterPage
|*
\************************************************************************/
SdrPage* FmFormModel::RemoveMasterPage(sal_uInt16 nPgNum)
{
    FmFormPage* pPage = (FmFormPage*)SdrModel::RemoveMasterPage(nPgNum);

#ifndef SVX_LIGHT
    if (pPage)
        m_pImpl->pUndoEnv->RemoveForms(pPage->GetForms());
#endif

    return pPage;
}

//------------------------------------------------------------------------
SdrLayerID FmFormModel::GetControlExportLayerId( const SdrObject& rObj ) const
{
    return rObj.GetLayer();
}

//------------------------------------------------------------------------
void FmFormModel::SetOpenInDesignMode( sal_Bool bOpenDesignMode )
{
#ifndef SVX_LIGHT
    if( bOpenDesignMode != m_bOpenInDesignMode )
    {
        m_bOpenInDesignMode = bOpenDesignMode;
        pObjShell->SetModified( sal_True );
    }
#endif
}

//------------------------------------------------------------------------
void FmFormModel::SetAutoControlFocus( sal_Bool _bAutoControlFocus )
{
#ifndef SVX_LIGHT
    if( _bAutoControlFocus != m_bAutoControlFocus )
    {
        m_bAutoControlFocus = _bAutoControlFocus;
        pObjShell->SetModified( sal_True );
    }
#endif
}

//------------------------------------------------------------------------
void FmFormModel::SetObjectShell( SfxObjectShell* pShell )
{
#ifndef SVX_LIGHT
    if (pShell == pObjShell)
        return;

    if (pObjShell)
    {
        m_pImpl->pUndoEnv->EndListening( *this );
        m_pImpl->pUndoEnv->EndListening( *pObjShell );
    }

    pObjShell = pShell;

    if (pObjShell)
    {
        m_pImpl->pUndoEnv->SetReadOnly(pObjShell->IsReadOnly() || pObjShell->IsReadOnlyUI());

        if (!m_pImpl->pUndoEnv->IsReadOnly())
             m_pImpl->pUndoEnv->StartListening(*this);

        m_pImpl->pUndoEnv->StartListening( *pObjShell );
    }
#endif
}

//------------------------------------------------------------------------
FmXUndoEnvironment& FmFormModel::GetUndoEnv()
{
    return *m_pImpl->pUndoEnv;
}

//------------------------------------------------------------------------
XubString FmFormModel::GetUniquePageId()
{
    XubString sReturn = m_pImpl->sNextPageId;

    xub_Unicode aNextChar = m_pImpl->sNextPageId.GetChar(m_pImpl->sNextPageId.Len() - 1);
    sal_Bool bNeedNewChar = sal_False;
    switch (aNextChar)
    {
        case '9' : aNextChar = 'A'; break;
        case 'Z' : aNextChar = 'a'; break;
        case 'z' : aNextChar = '0'; bNeedNewChar = sal_True; break;
        default: ++aNextChar; break;
    }
    m_pImpl->sNextPageId.SetChar(m_pImpl->sNextPageId.Len() - 1, aNextChar);
    if (bNeedNewChar)
        m_pImpl->sNextPageId += '0';

    return sReturn;
}

<|endoftext|>


<commit_before>/*******************************************************************************
    Copyright (c) The Taichi Authors (2016- ). All Rights Reserved.
    The use of this software is governed by the LICENSE file.
*******************************************************************************/

#include "taichi/system/traceback.h"

#include <vector>
#include <iostream>
#include <string>
#include <sstream>
#include <cstdio>
#include <algorithm>
#include <memory>
#include <mutex>
#include "spdlog/fmt/bundled/color.h"

#if defined(__APPLE__) || \
    (defined(__unix__) && !defined(__linux__)) && !defined(ANDROID)
#include <execinfo.h>
#include <cxxabi.h>
#endif
#ifdef _WIN64
#include <intrin.h>
#include <dbghelp.h>

#include "taichi/platform/windows/windows.h"

#pragma comment(lib, "dbghelp.lib")
//  https://gist.github.com/rioki/85ca8295d51a5e0b7c56e5005b0ba8b4
//
//  Debug Helpers
//
// Copyright (c) 2015 - 2017 Sean Farrell <sean.farrell@rioki.org>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

namespace dbg {
void trace(const char *msg, ...) {
  char buff[1024];

  va_list args;
  va_start(args, msg);
  vsnprintf(buff, 1024, msg, args);

  OutputDebugStringA(buff);

  va_end(args);
}

std::string basename(const std::string &file) {
  unsigned int i = file.find_last_of("\\/");
  if (i == std::string::npos) {
    return file;
  } else {
    return file.substr(i + 1);
  }
}

struct StackFrame {
  DWORD64 address;
  std::string name;
  std::string module;
  unsigned int line;
  std::string file;
};

inline std::vector<StackFrame> stack_trace() {
#if _WIN64
  DWORD machine = IMAGE_FILE_MACHINE_AMD64;
#else
  DWORD machine = IMAGE_FILE_MACHINE_I386;
#endif
  HANDLE process = GetCurrentProcess();
  HANDLE thread = GetCurrentThread();

  if (SymInitialize(process, NULL, TRUE) == FALSE) {
    trace("Failed to call SymInitialize.");
    return std::vector<StackFrame>();
  }

  SymSetOptions(SYMOPT_LOAD_LINES);

  CONTEXT context = {};
  context.ContextFlags = CONTEXT_FULL;
  RtlCaptureContext(&context);

#if _WIN64
  STACKFRAME frame = {};
  frame.AddrPC.Offset = context.Rip;
  frame.AddrPC.Mode = AddrModeFlat;
  frame.AddrFrame.Offset = context.Rbp;
  frame.AddrFrame.Mode = AddrModeFlat;
  frame.AddrStack.Offset = context.Rsp;
  frame.AddrStack.Mode = AddrModeFlat;
#else
  STACKFRAME frame = {};
  frame.AddrPC.Offset = context.Eip;
  frame.AddrPC.Mode = AddrModeFlat;
  frame.AddrFrame.Offset = context.Ebp;
  frame.AddrFrame.Mode = AddrModeFlat;
  frame.AddrStack.Offset = context.Esp;
  frame.AddrStack.Mode = AddrModeFlat;
#endif

  bool first = true;

  std::vector<StackFrame> frames;
  while (StackWalk(machine, process, thread, &frame, &context, NULL,
                   SymFunctionTableAccess, SymGetModuleBase, NULL)) {
    StackFrame f = {};
    f.address = frame.AddrPC.Offset;

#if _WIN64
    DWORD64 moduleBase = 0;
#else
    DWORD moduleBase = 0;
#endif

    moduleBase = SymGetModuleBase(process, frame.AddrPC.Offset);

    char moduelBuff[MAX_PATH];
    if (moduleBase &&
        GetModuleFileNameA((HINSTANCE)moduleBase, moduelBuff, MAX_PATH)) {
      f.module = basename(moduelBuff);
    } else {
      f.module = "Unknown Module";
    }
#if _WIN64
    DWORD64 offset = 0;
#else
    DWORD offset = 0;
#endif
    char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255];
    PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
    symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL) + 255;
    symbol->MaxNameLength = 254;

    if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &offset, symbol)) {
      f.name = symbol->Name;
    } else {
      DWORD error = GetLastError();
      trace("Failed to resolve address 0x%X: %u\n", frame.AddrPC.Offset, error);
      f.name = "Unknown Function";
    }

    IMAGEHLP_LINE line;
    line.SizeOfStruct = sizeof(IMAGEHLP_LINE);

    DWORD offset_ln = 0;
    if (SymGetLineFromAddr(process, frame.AddrPC.Offset, &offset_ln, &line)) {
      f.file = line.FileName;
      f.line = line.LineNumber;
    } else {
      DWORD error = GetLastError();
      trace("Failed to resolve line for 0x%X: %u\n", frame.AddrPC.Offset,
            error);
      f.line = 0;
    }

    if (!first) {
      frames.push_back(f);
    }
    first = false;
  }

  SymCleanup(process);

  return frames;
}
}  // namespace dbg
#endif
#if defined(__linux__) && !defined(ANDROID)
#include <execinfo.h>
#include <signal.h>
#include <ucontext.h>
#include <unistd.h>
#include <cxxabi.h>
#endif

TI_NAMESPACE_BEGIN

void print_traceback() {
#ifdef __APPLE__
  static std::mutex traceback_printer_mutex;
  // Modified based on
  // http://www.nullptr.me/2013/04/14/generating-stack-trace-on-os-x/
  // TODO: print line number instead of offset
  // (https://stackoverflow.com/questions/8278691/how-to-fix-backtrace-line-number-error-in-c)

  // record stack trace upto 128 frames
  void *callstack[128] = {};
  // collect stack frames
  int frames = backtrace((void **)callstack, 128);
  // get the human-readable symbols (mangled)
  char **strs = backtrace_symbols((void **)callstack, frames);
  std::vector<std::string> stack_frames;
  for (int i = 0; i < frames; i++) {
    char function_symbol[1024] = {};
    char module_name[1024] = {};
    int offset = 0;
    char addr[48] = {};
    // split the string, take out chunks out of stack trace
    // we are primarily interested in module, function and address
    sscanf(strs[i], "%*s %s %s %s %*s %d", module_name, addr, function_symbol,
           &offset);

    int valid_cpp_name = 0;
    //  if this is a C++ library, symbol will be demangled
    //  on success function returns 0
    char *function_name =
        abi::__cxa_demangle(function_symbol, NULL, 0, &valid_cpp_name);

    char stack_frame[4096] = {};
    sprintf(stack_frame, "* %28s | %7d | %s", module_name, offset,
            function_name);
    if (function_name != nullptr)
      free(function_name);

    std::string frameStr(stack_frame);
    stack_frames.push_back(frameStr);
  }
  free(strs);

  // Pretty print the traceback table
  // Exclude this function itself
  stack_frames.erase(stack_frames.begin());
  std::reverse(stack_frames.begin(), stack_frames.end());
  std::lock_guard<std::mutex> guard(traceback_printer_mutex);
  printf("\n");
  printf(
      "                            * Taichi Core - Stack Traceback *           "
      "                  \n");
  printf(
      "========================================================================"
      "==================\n");
  printf(
      "|                       Module |  Offset | Function                     "
      "                 |\n");
  printf(
      "|-----------------------------------------------------------------------"
      "-----------------|\n");
  std::reverse(stack_frames.begin(), stack_frames.end());
  for (auto trace : stack_frames) {
    const int function_start = 39;
    const int line_width = 86;
    const int function_width = line_width - function_start - 2;
    int i;
    for (i = 0; i < (int)trace.size(); i++) {
      std::cout << trace[i];
      if (i > function_start + 3 &&
          (i - 3 - function_start) % function_width == 0) {
        std::cout << " |" << std::endl << " ";
        for (int j = 0; j < function_start; j++) {
          std::cout << " ";
        }
        std::cout << " | ";
      }
    }
    for (int j = 0;
         j < function_width + 2 - (i - 3 - function_start) % function_width;
         j++) {
      std::cout << " ";
    }
    std::cout << "|" << std::endl;
  }
  printf(
      "========================================================================"
      "==================\n");
  printf("\n");
#elif defined(_WIN64)
  // Windows
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "* Taichi Compiler Stack Traceback *\n");
  fmt::print(fg(fmt::color::magenta), "***********************************\n");

  std::vector<dbg::StackFrame> stack = dbg::stack_trace();
  for (unsigned int i = 0; i < stack.size(); i++) {
    fmt::print(fg(fmt::color::magenta),
               fmt::format("0x{:x}: ", stack[i].address));
    fmt::print(fg(fmt::color::red), stack[i].name);
    if (stack[i].file != std::string(""))
      fmt::print(fg(fmt::color::magenta),
                 fmt::format("(line {} in {})", stack[i].line, stack[i].file));
    fmt::print(fg(fmt::color::magenta),
               fmt::format(" in {}\n", stack[i].module));
  }
#elif defined(ANDROID)
  // Not supported
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "* Taichi Compiler Stack Traceback *\n");
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "NOT SUPPORTED ON ANDROID\n");
#else
  // Based on http://man7.org/linux/man-pages/man3/backtrace.3.html
  constexpr int BT_BUF_SIZE = 1024;
  int nptrs;
  void *buffer[BT_BUF_SIZE];
  char **strings;

  nptrs = backtrace(buffer, BT_BUF_SIZE);
  strings = backtrace_symbols(buffer, nptrs);

  if (strings == nullptr) {
    perror("backtrace_symbols");
    exit(EXIT_FAILURE);
  }

  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "* Taichi Compiler Stack Traceback *\n");
  fmt::print(fg(fmt::color::magenta), "***********************************\n");

  // j = 0: taichi::print_traceback
  for (int j = 1; j < nptrs; j++) {
    std::string s(strings[j]);
    std::size_t slash = s.find("/");
    std::size_t start = s.find("(");
    std::size_t end = s.rfind("+");

    std::string line;

    if (slash == 0 && start < end && s[start + 1] != '+') {
      std::string name = s.substr(start + 1, end - start - 1);

      char *demangled_name_;

      int status = -1;

      demangled_name_ = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);

      if (demangled_name_) {
        name = std::string(demangled_name_);
      }

      std::string prefix = s.substr(0, start);

      line = fmt::format("{}: {}", prefix, name);
      free(demangled_name_);
    } else {
      line = s;
    }
    fmt::print(fg(fmt::color::magenta), "{}\n", line);
  }
  std::free(strings);
#endif

  fmt::print(
      fg(fmt::color::orange),
      "\nInternal error occurred. Check out this page for possible solutions:\n"
      "https://docs.taichi.graphics/lang/articles/misc/install\n");
}

TI_NAMESPACE_END
<commit_msg>[javascript] Disable stack trace logging when TI_EMSCRIPTENED (JS 9/n) (#4117)<commit_after>/*******************************************************************************
    Copyright (c) The Taichi Authors (2016- ). All Rights Reserved.
    The use of this software is governed by the LICENSE file.
*******************************************************************************/

#include "taichi/system/traceback.h"

#include <vector>
#include <iostream>
#include <string>
#include <sstream>
#include <cstdio>
#include <algorithm>
#include <memory>
#include <mutex>
#include "spdlog/fmt/bundled/color.h"

#if defined(__APPLE__) || (defined(__unix__) && !defined(__linux__)) && \
                              !defined(ANDROID) && !defined(TI_EMSCRIPTENED)
#include <execinfo.h>
#include <cxxabi.h>
#endif
#ifdef _WIN64
#include <intrin.h>
#include <dbghelp.h>

#include "taichi/platform/windows/windows.h"

#pragma comment(lib, "dbghelp.lib")
//  https://gist.github.com/rioki/85ca8295d51a5e0b7c56e5005b0ba8b4
//
//  Debug Helpers
//
// Copyright (c) 2015 - 2017 Sean Farrell <sean.farrell@rioki.org>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

namespace dbg {
void trace(const char *msg, ...) {
  char buff[1024];

  va_list args;
  va_start(args, msg);
  vsnprintf(buff, 1024, msg, args);

  OutputDebugStringA(buff);

  va_end(args);
}

std::string basename(const std::string &file) {
  unsigned int i = file.find_last_of("\\/");
  if (i == std::string::npos) {
    return file;
  } else {
    return file.substr(i + 1);
  }
}

struct StackFrame {
  DWORD64 address;
  std::string name;
  std::string module;
  unsigned int line;
  std::string file;
};

inline std::vector<StackFrame> stack_trace() {
#if _WIN64
  DWORD machine = IMAGE_FILE_MACHINE_AMD64;
#else
  DWORD machine = IMAGE_FILE_MACHINE_I386;
#endif
  HANDLE process = GetCurrentProcess();
  HANDLE thread = GetCurrentThread();

  if (SymInitialize(process, NULL, TRUE) == FALSE) {
    trace("Failed to call SymInitialize.");
    return std::vector<StackFrame>();
  }

  SymSetOptions(SYMOPT_LOAD_LINES);

  CONTEXT context = {};
  context.ContextFlags = CONTEXT_FULL;
  RtlCaptureContext(&context);

#if _WIN64
  STACKFRAME frame = {};
  frame.AddrPC.Offset = context.Rip;
  frame.AddrPC.Mode = AddrModeFlat;
  frame.AddrFrame.Offset = context.Rbp;
  frame.AddrFrame.Mode = AddrModeFlat;
  frame.AddrStack.Offset = context.Rsp;
  frame.AddrStack.Mode = AddrModeFlat;
#else
  STACKFRAME frame = {};
  frame.AddrPC.Offset = context.Eip;
  frame.AddrPC.Mode = AddrModeFlat;
  frame.AddrFrame.Offset = context.Ebp;
  frame.AddrFrame.Mode = AddrModeFlat;
  frame.AddrStack.Offset = context.Esp;
  frame.AddrStack.Mode = AddrModeFlat;
#endif

  bool first = true;

  std::vector<StackFrame> frames;
  while (StackWalk(machine, process, thread, &frame, &context, NULL,
                   SymFunctionTableAccess, SymGetModuleBase, NULL)) {
    StackFrame f = {};
    f.address = frame.AddrPC.Offset;

#if _WIN64
    DWORD64 moduleBase = 0;
#else
    DWORD moduleBase = 0;
#endif

    moduleBase = SymGetModuleBase(process, frame.AddrPC.Offset);

    char moduelBuff[MAX_PATH];
    if (moduleBase &&
        GetModuleFileNameA((HINSTANCE)moduleBase, moduelBuff, MAX_PATH)) {
      f.module = basename(moduelBuff);
    } else {
      f.module = "Unknown Module";
    }
#if _WIN64
    DWORD64 offset = 0;
#else
    DWORD offset = 0;
#endif
    char symbolBuffer[sizeof(IMAGEHLP_SYMBOL) + 255];
    PIMAGEHLP_SYMBOL symbol = (PIMAGEHLP_SYMBOL)symbolBuffer;
    symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL) + 255;
    symbol->MaxNameLength = 254;

    if (SymGetSymFromAddr(process, frame.AddrPC.Offset, &offset, symbol)) {
      f.name = symbol->Name;
    } else {
      DWORD error = GetLastError();
      trace("Failed to resolve address 0x%X: %u\n", frame.AddrPC.Offset, error);
      f.name = "Unknown Function";
    }

    IMAGEHLP_LINE line;
    line.SizeOfStruct = sizeof(IMAGEHLP_LINE);

    DWORD offset_ln = 0;
    if (SymGetLineFromAddr(process, frame.AddrPC.Offset, &offset_ln, &line)) {
      f.file = line.FileName;
      f.line = line.LineNumber;
    } else {
      DWORD error = GetLastError();
      trace("Failed to resolve line for 0x%X: %u\n", frame.AddrPC.Offset,
            error);
      f.line = 0;
    }

    if (!first) {
      frames.push_back(f);
    }
    first = false;
  }

  SymCleanup(process);

  return frames;
}
}  // namespace dbg
#endif
#if defined(__linux__) && !defined(ANDROID)
#include <execinfo.h>
#include <signal.h>
#include <ucontext.h>
#include <unistd.h>
#include <cxxabi.h>
#endif

TI_NAMESPACE_BEGIN

void print_traceback() {
#ifdef __APPLE__
  static std::mutex traceback_printer_mutex;
  // Modified based on
  // http://www.nullptr.me/2013/04/14/generating-stack-trace-on-os-x/
  // TODO: print line number instead of offset
  // (https://stackoverflow.com/questions/8278691/how-to-fix-backtrace-line-number-error-in-c)

  // record stack trace upto 128 frames
  void *callstack[128] = {};
  // collect stack frames
  int frames = backtrace((void **)callstack, 128);
  // get the human-readable symbols (mangled)
  char **strs = backtrace_symbols((void **)callstack, frames);
  std::vector<std::string> stack_frames;
  for (int i = 0; i < frames; i++) {
    char function_symbol[1024] = {};
    char module_name[1024] = {};
    int offset = 0;
    char addr[48] = {};
    // split the string, take out chunks out of stack trace
    // we are primarily interested in module, function and address
    sscanf(strs[i], "%*s %s %s %s %*s %d", module_name, addr, function_symbol,
           &offset);

    int valid_cpp_name = 0;
    //  if this is a C++ library, symbol will be demangled
    //  on success function returns 0
    char *function_name =
        abi::__cxa_demangle(function_symbol, NULL, 0, &valid_cpp_name);

    char stack_frame[4096] = {};
    sprintf(stack_frame, "* %28s | %7d | %s", module_name, offset,
            function_name);
    if (function_name != nullptr)
      free(function_name);

    std::string frameStr(stack_frame);
    stack_frames.push_back(frameStr);
  }
  free(strs);

  // Pretty print the traceback table
  // Exclude this function itself
  stack_frames.erase(stack_frames.begin());
  std::reverse(stack_frames.begin(), stack_frames.end());
  std::lock_guard<std::mutex> guard(traceback_printer_mutex);
  printf("\n");
  printf(
      "                            * Taichi Core - Stack Traceback *           "
      "                  \n");
  printf(
      "========================================================================"
      "==================\n");
  printf(
      "|                       Module |  Offset | Function                     "
      "                 |\n");
  printf(
      "|-----------------------------------------------------------------------"
      "-----------------|\n");
  std::reverse(stack_frames.begin(), stack_frames.end());
  for (auto trace : stack_frames) {
    const int function_start = 39;
    const int line_width = 86;
    const int function_width = line_width - function_start - 2;
    int i;
    for (i = 0; i < (int)trace.size(); i++) {
      std::cout << trace[i];
      if (i > function_start + 3 &&
          (i - 3 - function_start) % function_width == 0) {
        std::cout << " |" << std::endl << " ";
        for (int j = 0; j < function_start; j++) {
          std::cout << " ";
        }
        std::cout << " | ";
      }
    }
    for (int j = 0;
         j < function_width + 2 - (i - 3 - function_start) % function_width;
         j++) {
      std::cout << " ";
    }
    std::cout << "|" << std::endl;
  }
  printf(
      "========================================================================"
      "==================\n");
  printf("\n");
#elif defined(_WIN64)
  // Windows
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "* Taichi Compiler Stack Traceback *\n");
  fmt::print(fg(fmt::color::magenta), "***********************************\n");

  std::vector<dbg::StackFrame> stack = dbg::stack_trace();
  for (unsigned int i = 0; i < stack.size(); i++) {
    fmt::print(fg(fmt::color::magenta),
               fmt::format("0x{:x}: ", stack[i].address));
    fmt::print(fg(fmt::color::red), stack[i].name);
    if (stack[i].file != std::string(""))
      fmt::print(fg(fmt::color::magenta),
                 fmt::format("(line {} in {})", stack[i].line, stack[i].file));
    fmt::print(fg(fmt::color::magenta),
               fmt::format(" in {}\n", stack[i].module));
  }
#elif defined(ANDROID)
  // Not supported
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "* Taichi Compiler Stack Traceback *\n");
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "NOT SUPPORTED ON ANDROID\n");
#elif defined(TI_EMSCRIPTENED)
  // Not supported
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta),
             "* Emscriptened Taichi Compiler Stack Traceback *\n");
  fmt::print(fg(fmt::color::magenta), "***********************************\n");
#else
  // Based on http://man7.org/linux/man-pages/man3/backtrace.3.html
  constexpr int BT_BUF_SIZE = 1024;
  int nptrs;
  void *buffer[BT_BUF_SIZE];
  char **strings;

  nptrs = backtrace(buffer, BT_BUF_SIZE);
  strings = backtrace_symbols(buffer, nptrs);

  if (strings == nullptr) {
    perror("backtrace_symbols");
    exit(EXIT_FAILURE);
  }

  fmt::print(fg(fmt::color::magenta), "***********************************\n");
  fmt::print(fg(fmt::color::magenta), "* Taichi Compiler Stack Traceback *\n");
  fmt::print(fg(fmt::color::magenta), "***********************************\n");

  // j = 0: taichi::print_traceback
  for (int j = 1; j < nptrs; j++) {
    std::string s(strings[j]);
    std::size_t slash = s.find("/");
    std::size_t start = s.find("(");
    std::size_t end = s.rfind("+");

    std::string line;

    if (slash == 0 && start < end && s[start + 1] != '+') {
      std::string name = s.substr(start + 1, end - start - 1);

      char *demangled_name_;

      int status = -1;

      demangled_name_ = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);

      if (demangled_name_) {
        name = std::string(demangled_name_);
      }

      std::string prefix = s.substr(0, start);

      line = fmt::format("{}: {}", prefix, name);
      free(demangled_name_);
    } else {
      line = s;
    }
    fmt::print(fg(fmt::color::magenta), "{}\n", line);
  }
  std::free(strings);
#endif

  fmt::print(
      fg(fmt::color::orange),
      "\nInternal error occurred. Check out this page for possible solutions:\n"
      "https://docs.taichi.graphics/lang/articles/misc/install\n");
}

TI_NAMESPACE_END
<|endoftext|>