F:/KPlato/koffice/libs/kotext/KoTextDocument.cpp

Aller à la documentation de ce fichier.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2001-2005 David Faure <faure@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "KoTextDocument.h"
00021 #include "KoTextParag.h"
00022 #include "KoTextZoomHandler.h"
00023 #include "KoTextFormatter.h"
00024 #include "KoTextFormat.h"
00025 #include "KoParagCounter.h"
00026 #include "KoTextCommand.h"
00027 #include "KoOasisContext.h"
00028 #include "KoVariable.h"
00029 #include <KoXmlWriter.h>
00030 #include <KoXmlNS.h>
00031 #include <KoDom.h>
00032 #include <kdebug.h>
00033 #include <kdeversion.h>
00034 #include <QApplication>
00035 //Added by qt3to4:
00036 #include <QPixmap>
00037 #include <Q3MemArray>
00038 #include <Q3PtrList>
00039 #include <Q3ValueList>
00040 #include <assert.h>
00041 
00042 //#define DEBUG_PAINTING
00043 
00046 
00047 KoTextDocument::KoTextDocument( KoTextZoomHandler *zoomHandler, KoTextFormatCollection *fc,
00048                                 KoTextFormatter *formatter, bool createInitialParag )
00049     : m_zoomHandler( zoomHandler ),
00050       m_bDestroying( false ),
00051 #ifdef QTEXTTABLE_AVAILABLE
00052       par( 0L /*we don't use parent documents */ ),
00053       tc( 0 ),
00054 #endif
00055       tArray( 0 ), tStopWidth( 0 )
00056 {
00057     fCollection = fc;
00058     init(); // see korichtext.cpp
00059 
00060     m_drawingFlags = 0;
00061     if ( !formatter )
00062         formatter = new KoTextFormatter;
00063     setFormatter( formatter );
00064 
00065     setY( 0 );
00066     setLeftMargin( 0 );
00067     setRightMargin( 0 );
00068 
00069     // Delete the KoTextParag created by KoTextDocument::init() if createInitialParag is false.
00070     if ( !createInitialParag )
00071         clear( false );
00072 }
00073 
00074 void KoTextDocument::init()
00075 {
00076     //pProcessor = 0;
00077     useFC = true;
00078     pFormatter = 0;
00079     fParag = 0;
00080     m_pageBreakEnabled = false;
00081     //minw = 0;
00082     align = Qt::AlignLeft;
00083     nSelections = 2;
00084 
00085     underlLinks = true;
00086     backBrush = 0;
00087     buf_pixmap = 0;
00088     //nextDoubleBuffered = false;
00089 
00090     //if ( par )
00091 //      withoutDoubleBuffer = par->withoutDoubleBuffer;
00092 //    else
00093         withoutDoubleBuffer = false;
00094 
00095     lParag = fParag = createParag( this, 0, 0 );
00096 
00097     //cx = 0;
00098     //cy = 2;
00099     //if ( par )
00100         cx = cy = 0;
00101     //cw = 600; // huh?
00102     //vw = 0;
00103     flow_ = new KoTextFlow;
00104     //flow_->setWidth( cw );
00105 
00106     leftmargin = 0; // 4 in QRT
00107     rightmargin = 0; // 4 in QRT
00108 
00109     selectionColors[ Standard ] = QApplication::palette().color( QPalette::Active, QColorGroup::Highlight );
00110     selectionText[ Standard ] = true;
00111     assert( Standard < nSelections );
00112     selectionText[ InputMethodPreedit ] = false;
00113     assert( InputMethodPreedit < nSelections );
00114     commandHistory = new KoTextDocCommandHistory( 100 );
00115     tStopWidth = formatCollection()->defaultFormat()->width( 'x' ) * 8;
00116 }
00117 
00118 KoTextDocument::~KoTextDocument()
00119 {
00120     //if ( par )
00121 //      par->removeChild( this );
00123     m_bDestroying = true;
00124     clear( false );
00126     delete commandHistory;
00127     delete flow_;
00128     //if ( !par )
00129         delete pFormatter;
00130     delete fCollection;
00131     //delete pProcessor;
00132     delete buf_pixmap;
00133     delete backBrush;
00134     if ( tArray )
00135         delete [] tArray;
00136 }
00137 
00138 void KoTextDocument::clear( bool createEmptyParag )
00139 {
00140     if ( flow_ )
00141         flow_->clear();
00142     while ( fParag ) {
00143         KoTextParag *p = fParag->next();
00144         fParag->string()->clear(); // avoid the "unregister custom items" code, not needed
00145         delete fParag;
00146         fParag = p;
00147     }
00148     fParag = lParag = 0;
00149     if ( createEmptyParag )
00150         fParag = lParag = createParag( this );
00151     selections.clear();
00152     customItems.clear();
00153 }
00154 
00155 /*
00156    // Looks slow!
00157 int KoTextDocument::widthUsed() const
00158 {
00159     KoTextParag *p = fParag;
00160     int w = 0;
00161     while ( p ) {
00162         int a = p->alignment();
00163         p->setAlignment( Qt::AlignLeft );
00164         p->invalidate( 0 );
00165         p->format();
00166         w = qMax( w, p->rect().width() );
00167         p->setAlignment( a );
00168         p->invalidate( 0 );
00169         p = p->next();
00170     }
00171     return w;
00172 }
00173 */
00174 
00175 int KoTextDocument::height() const
00176 {
00177     int h = 0;
00178     if ( lParag )
00179         h = lParag->rect().top() + lParag->rect().height() + 1;
00180     //int fh = flow_->boundingRect().height();
00181     //return qMax( h, fh );
00182     return h;
00183 }
00184 
00185 
00186 KoTextParag *KoTextDocument::createParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx, bool updateIds )
00187 {
00188     return new KoTextParag( d, pr, nx, updateIds );
00189 }
00190 
00191 void KoTextDocument::setPlainText( const QString &text )
00192 {
00193     clear();
00194     //preferRichText = false;
00195     //oTextValid = true;
00196     //oText = text;
00197 
00198     int lastNl = 0;
00199     int nl = text.find( '\n' );
00200     if ( nl == -1 ) {
00201         lParag = createParag( this, lParag, 0 );
00202         if ( !fParag )
00203             fParag = lParag;
00204         QString s = text;
00205         if ( !s.isEmpty() ) {
00206             if ( s[ (int)s.length() - 1 ] == '\r' )
00207                 s.remove( s.length() - 1, 1 );
00208             lParag->append( s );
00209         }
00210     } else {
00211         for (;;) {
00212             lParag = createParag( this, lParag, 0 );
00213             if ( !fParag )
00214                 fParag = lParag;
00215             QString s = text.mid( lastNl, nl - lastNl );
00216             if ( !s.isEmpty() ) {
00217                 if ( s[ (int)s.length() - 1 ] == '\r' )
00218                     s.remove( s.length() - 1, 1 );
00219                 lParag->append( s );
00220             }
00221             if ( nl == 0xffffff )
00222                 break;
00223             lastNl = nl + 1;
00224             nl = text.find( '\n', nl + 1 );
00225             if ( nl == -1 )
00226                 nl = 0xffffff;
00227         }
00228     }
00229     if ( !lParag )
00230         lParag = fParag = createParag( this, 0, 0 );
00231 }
00232 
00233 void KoTextDocument::setText( const QString &text, const QString & /*context*/ )
00234 {
00235     selections.clear();
00236     setPlainText( text );
00237 }
00238 
00239 QString KoTextDocument::plainText() const
00240 {
00241     QString buffer;
00242     QString s;
00243     KoTextParag *p = fParag;
00244     while ( p ) {
00245         s = p->string()->toString();
00246         s.remove( s.length() - 1, 1 );
00247         if ( p->next() )
00248             s += '\n';
00249         buffer += s;
00250         p = p->next();
00251     }
00252     return buffer;
00253 }
00254 
00255 void KoTextDocument::invalidate()
00256 {
00257     KoTextParag *s = fParag;
00258     while ( s ) {
00259         s->invalidate( 0 );
00260         s = s->next();
00261     }
00262 }
00263 
00264 void KoTextDocument::informParagraphDeleted( KoTextParag* parag )
00265 {
00266     QMap<int, KoTextDocumentSelection>::Iterator it = selections.begin();
00267     for ( ; it != selections.end(); ++it )
00268     {
00269         if ( (*it).startCursor.parag() == parag ) {
00270             if ( parag->prev() ) {
00271                 KoTextParag* prevP = parag->prev();
00272                 (*it).startCursor.setParag( prevP );
00273                 (*it).startCursor.setIndex( prevP->length()-1 );
00274             } else
00275                 (*it).startCursor.setParag( parag->next() ); // sets index to 0
00276         }
00277         if ( (*it).endCursor.parag() == parag ) {
00278             if ( parag->prev() ) {
00279                 KoTextParag* prevP = parag->prev();
00280                 (*it).endCursor.setParag( prevP );
00281                 (*it).endCursor.setIndex( prevP->length()-1 );
00282             } else
00283                 (*it).endCursor.setParag( parag->next() ); // sets index to 0
00284         }
00285     }
00286     emit paragraphDeleted( parag );
00287 }
00288 
00289 void KoTextDocument::selectionStart( int id, int &paragId, int &index )
00290 {
00291     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00292     if ( it == selections.end() )
00293         return;
00294     KoTextDocumentSelection &sel = *it;
00295     paragId = !sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
00296     index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
00297 }
00298 
00299 KoTextCursor KoTextDocument::selectionStartCursor( int id)
00300 {
00301     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00302     if ( it == selections.end() )
00303         return KoTextCursor( this );
00304     KoTextDocumentSelection &sel = *it;
00305     if ( sel.swapped )
00306         return sel.endCursor;
00307     return sel.startCursor;
00308 }
00309 
00310 KoTextCursor KoTextDocument::selectionEndCursor( int id)
00311 {
00312     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00313     if ( it == selections.end() )
00314         return KoTextCursor( this );
00315     KoTextDocumentSelection &sel = *it;
00316     if ( !sel.swapped )
00317         return sel.endCursor;
00318     return sel.startCursor;
00319 }
00320 
00321 void KoTextDocument::selectionEnd( int id, int &paragId, int &index )
00322 {
00323     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00324     if ( it == selections.end() )
00325         return;
00326     KoTextDocumentSelection &sel = *it;
00327     paragId = sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
00328     index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
00329 }
00330 
00331 bool KoTextDocument::isSelectionSwapped( int id )
00332 {
00333     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00334     if ( it == selections.end() )
00335         return false;
00336     KoTextDocumentSelection &sel = *it;
00337     return sel.swapped;
00338 }
00339 
00340 KoTextParag *KoTextDocument::selectionStart( int id )
00341 {
00342     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00343     if ( it == selections.end() )
00344         return 0;
00345     KoTextDocumentSelection &sel = *it;
00346     if ( sel.startCursor.parag()->paragId() <  sel.endCursor.parag()->paragId() )
00347         return sel.startCursor.parag();
00348     return sel.endCursor.parag();
00349 }
00350 
00351 KoTextParag *KoTextDocument::selectionEnd( int id )
00352 {
00353     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00354     if ( it == selections.end() )
00355         return 0;
00356     KoTextDocumentSelection &sel = *it;
00357     if ( sel.startCursor.parag()->paragId() >  sel.endCursor.parag()->paragId() )
00358         return sel.startCursor.parag();
00359     return sel.endCursor.parag();
00360 }
00361 
00362 void KoTextDocument::addSelection( int id )
00363 {
00364     nSelections = qMax( nSelections, id + 1 );
00365 }
00366 
00367 static void setSelectionEndHelper( int id, KoTextDocumentSelection &sel, KoTextCursor &start, KoTextCursor &end )
00368 {
00369     KoTextCursor c1 = start;
00370     KoTextCursor c2 = end;
00371     if ( sel.swapped ) {
00372         c1 = end;
00373         c2 = start;
00374     }
00375 
00376     c1.parag()->removeSelection( id );
00377     c2.parag()->removeSelection( id );
00378     if ( c1.parag() != c2.parag() ) {
00379         c1.parag()->setSelection( id, c1.index(), c1.parag()->length() - 1 );
00380         c2.parag()->setSelection( id, 0, c2.index() );
00381     } else {
00382         c1.parag()->setSelection( id, qMin( c1.index(), c2.index() ), qMax( c1.index(), c2.index() ) );
00383     }
00384 
00385     sel.startCursor = start;
00386     sel.endCursor = end;
00387     if ( sel.startCursor.parag() == sel.endCursor.parag() )
00388         sel.swapped = sel.startCursor.index() > sel.endCursor.index();
00389 }
00390 
00391 bool KoTextDocument::setSelectionEnd( int id, KoTextCursor *cursor )
00392 {
00393     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00394     if ( it == selections.end() )
00395         return false;
00396     KoTextDocumentSelection &sel = *it;
00397 
00398     KoTextCursor start = sel.startCursor;
00399     KoTextCursor end = *cursor;
00400 
00401     if ( start == end ) {
00402         removeSelection( id );
00403         setSelectionStart( id, cursor );
00404         return true;
00405     }
00406 
00407     if ( sel.endCursor.parag() == end.parag() ) {
00408         setSelectionEndHelper( id, sel, start, end );
00409         return true;
00410     }
00411 
00412     bool inSelection = false;
00413     KoTextCursor c( this );
00414     KoTextCursor tmp = sel.startCursor;
00415     if ( sel.swapped )
00416         tmp = sel.endCursor;
00417     KoTextCursor tmp2 = *cursor;
00418     c.setParag( tmp.parag()->paragId() < tmp2.parag()->paragId() ? tmp.parag() : tmp2.parag() );
00419     KoTextCursor old;
00420     bool hadStart = false;
00421     bool hadEnd = false;
00422     bool hadStartParag = false;
00423     bool hadEndParag = false;
00424     bool hadOldStart = false;
00425     bool hadOldEnd = false;
00426     bool leftSelection = false;
00427     sel.swapped = false;
00428     for ( ;; ) {
00429         if ( c == start )
00430             hadStart = true;
00431         if ( c == end )
00432             hadEnd = true;
00433         if ( c.parag() == start.parag() )
00434             hadStartParag = true;
00435         if ( c.parag() == end.parag() )
00436             hadEndParag = true;
00437         if ( c == sel.startCursor )
00438             hadOldStart = true;
00439         if ( c == sel.endCursor )
00440             hadOldEnd = true;
00441 
00442         if ( !sel.swapped &&
00443              ( hadEnd && !hadStart ||
00444                hadEnd && hadStart && start.parag() == end.parag() && start.index() > end.index() ) )
00445             sel.swapped = true;
00446 
00447         if ( c == end && hadStartParag ||
00448              c == start && hadEndParag ) {
00449             KoTextCursor tmp = c;
00450             if ( tmp.parag() != c.parag() ) {
00451                 int sstart = tmp.parag()->selectionStart( id );
00452                 tmp.parag()->removeSelection( id );
00453                 tmp.parag()->setSelection( id, sstart, tmp.index() );
00454             }
00455         }
00456 
00457         if ( inSelection &&
00458              ( c == end && hadStart || c == start && hadEnd ) )
00459              leftSelection = true;
00460         else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
00461             inSelection = true;
00462 
00463         bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
00464         c.parag()->removeSelection( id );
00465         if ( inSelection ) {
00466             if ( c.parag() == start.parag() && start.parag() == end.parag() ) {
00467                 c.parag()->setSelection( id, qMin( start.index(), end.index() ), qMax( start.index(), end.index() ) );
00468             } else if ( c.parag() == start.parag() && !hadEndParag ) {
00469                 c.parag()->setSelection( id, start.index(), c.parag()->length() - 1 );
00470             } else if ( c.parag() == end.parag() && !hadStartParag ) {
00471                 c.parag()->setSelection( id, end.index(), c.parag()->length() - 1 );
00472             } else if ( c.parag() == end.parag() && hadEndParag ) {
00473                 c.parag()->setSelection( id, 0, end.index() );
00474             } else if ( c.parag() == start.parag() && hadStartParag ) {
00475                 c.parag()->setSelection( id, 0, start.index() );
00476             } else {
00477                 c.parag()->setSelection( id, 0, c.parag()->length() - 1 );
00478             }
00479         }
00480 
00481         if ( leftSelection )
00482             inSelection = false;
00483 
00484         old = c;
00485         c.gotoNextLetter();
00486         if ( old == c || noSelectionAnymore )
00487             break;
00488     }
00489 
00490     if ( !sel.swapped )
00491         sel.startCursor.parag()->setSelection( id, sel.startCursor.index(), sel.startCursor.parag()->length() - 1 );
00492 
00493     sel.startCursor = start;
00494     sel.endCursor = end;
00495     if ( sel.startCursor.parag() == sel.endCursor.parag() )
00496         sel.swapped = sel.startCursor.index() > sel.endCursor.index();
00497 
00498     setSelectionEndHelper( id, sel, start, end );
00499 
00500     return true;
00501 }
00502 
00503 void KoTextDocument::selectAll( int id )
00504 {
00505     removeSelection( id );
00506 
00507     KoTextDocumentSelection sel;
00508     sel.swapped = false;
00509     KoTextCursor c( this );
00510 
00511     c.setParag( fParag );
00512     c.setIndex( 0 );
00513     sel.startCursor = c;
00514 
00515     c.setParag( lParag );
00516     c.setIndex( lParag->length() - 1 );
00517     sel.endCursor = c;
00518 
00519     KoTextParag *p = fParag;
00520     while ( p ) {
00521         p->setSelection( id, 0, p->length() - 1 );
00522 #ifdef QTEXTTABLE_AVAILABLE
00523         for ( int i = 0; i < (int)p->length(); ++i ) {
00524             if ( p->at( i )->isCustom() && p->at( i )->customItem()->isNested() ) {
00525                 KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
00526                 Q3PtrList<KoTextTableCell> tableCells = t->tableCells();
00527                 for ( KoTextTableCell *c = tableCells.first(); c; c = tableCells.next() )
00528                     c->richText()->selectAll( id );
00529             }
00530         }
00531 #endif
00532         p = p->next();
00533     }
00534 
00535     selections.insert( id, sel );
00536 }
00537 
00538 bool KoTextDocument::removeSelection( int id )
00539 {
00540     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00541     if ( it == selections.end() )
00542         return false;
00543 
00544     KoTextDocumentSelection &sel = *it;
00545 
00546     KoTextCursor c( this );
00547     KoTextCursor tmp = sel.startCursor;
00548     if ( sel.swapped )
00549         tmp = sel.endCursor;
00550     c.setParag( tmp.parag() );
00551     KoTextCursor old;
00552     bool hadStart = false;
00553     bool hadEnd = false;
00554     KoTextParag *lastParag = 0;
00555     bool leftSelection = false;
00556     bool inSelection = false;
00557     sel.swapped = false;
00558     for ( ;; ) {
00559         if ( !hadStart && c.parag() == sel.startCursor.parag() )
00560             hadStart = true;
00561         if ( !hadEnd && c.parag() == sel.endCursor.parag() )
00562             hadEnd = true;
00563 
00564         if ( !leftSelection && !inSelection && ( c.parag() == sel.startCursor.parag() || c.parag() == sel.endCursor.parag() ) )
00565             inSelection = true;
00566 
00567         if ( inSelection &&
00568              ( c == sel.endCursor && hadStart || c == sel.startCursor && hadEnd ) ) {
00569              leftSelection = true;
00570              inSelection = false;
00571         }
00572 
00573         bool noSelectionAnymore = leftSelection && !inSelection && !c.parag()->hasSelection( id ) && c.atParagEnd();
00574 
00575         if ( lastParag != c.parag() )
00576             c.parag()->removeSelection( id );
00577 
00578         old = c;
00579         lastParag = c.parag();
00580         c.gotoNextLetter();
00581         if ( old == c || noSelectionAnymore )
00582             break;
00583     }
00584 
00585     selections.remove( id );
00586     return true;
00587 }
00588 
00589 QString KoTextDocument::selectedText( int id, bool withCustom ) const
00590 {
00591     // ######## TODO: look at textFormat() and return rich text or plain text (like the text() method!)
00592     QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( id );
00593     if ( it == selections.end() )
00594         return QString::null;
00595 
00596     KoTextDocumentSelection sel = *it;
00597 
00598 
00599     KoTextCursor c1 = sel.startCursor;
00600     KoTextCursor c2 = sel.endCursor;
00601     if ( sel.swapped ) {
00602         c2 = sel.startCursor;
00603         c1 = sel.endCursor;
00604     }
00605 
00606     if ( c1.parag() == c2.parag() ) {
00607         QString s;
00608         KoTextParag *p = c1.parag();
00609         int end = c2.index();
00610         if ( p->at( qMax( 0, end - 1 ) )->isCustom() )
00611             ++end;
00612         if ( !withCustom || !p->customItems() ) {
00613             s += p->string()->toString().mid( c1.index(), end - c1.index() );
00614         } else {
00615             for ( int i = c1.index(); i < end; ++i ) {
00616                 if ( p->at( i )->isCustom() ) {
00617 #ifdef QTEXTTABLE_AVAILABLE
00618                     if ( p->at( i )->customItem()->isNested() ) {
00619                         s += '\n';
00620                         KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
00621                         Q3PtrList<KoTextTableCell> cells = t->tableCells();
00622                         for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
00623                             s += c->richText()->plainText() + '\n';
00624                         s += '\n';
00625                     }
00626 #endif
00627                 } else {
00628                     s += p->at( i )->c;
00629                 }
00630                 s += '\n';
00631             }
00632         }
00633         return s;
00634     }
00635 
00636     QString s;
00637     KoTextParag *p = c1.parag();
00638     int start = c1.index();
00639     while ( p ) {
00640         int end = p == c2.parag() ? c2.index() : p->length() - 1;
00641         if ( p == c2.parag() && p->at( qMax( 0, end - 1 ) )->isCustom() )
00642             ++end;
00643         if ( !withCustom || !p->customItems() ) {
00644             s += p->string()->toString().mid( start, end - start );
00645             if ( p != c2.parag() )
00646                 s += '\n';
00647         } else {
00648             for ( int i = start; i < end; ++i ) {
00649                 if ( p->at( i )->isCustom() ) {
00650 #ifdef QTEXTTABLE_AVAILABLE
00651                     if ( p->at( i )->customItem()->isNested() ) {
00652                         s += '\n';
00653                         KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
00654                         Q3PtrList<KoTextTableCell> cells = t->tableCells();
00655                         for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
00656                             s += c->richText()->plainText() + '\n';
00657                         s += '\n';
00658                     }
00659 #endif
00660                 } else {
00661                     s += p->at( i )->c;
00662                 }
00663                 s += '\n';
00664             }
00665         }
00666         start = 0;
00667         if ( p == c2.parag() )
00668             break;
00669         p = p->next();
00670     }
00671     return s;
00672 }
00673 
00674 QString KoTextDocument::copySelection( KoXmlWriter& writer, KoSavingContext& context, int selectionId )
00675 {
00676     KoTextCursor c1 = selectionStartCursor( selectionId );
00677     KoTextCursor c2 = selectionEndCursor( selectionId );
00678     QString text;
00679     if ( c1.parag() == c2.parag() )
00680     {
00681         text = c1.parag()->toString( c1.index(), c2.index() - c1.index() );
00682 
00683         c1.parag()->saveOasis( writer, context, c1.index(), c2.index()-1, true );
00684     }
00685     else
00686     {
00687         text += c1.parag()->toString( c1.index() ) + '\n';
00688 
00689         c1.parag()->saveOasis( writer, context, c1.index(), c1.parag()->length()-2, true );
00690         KoTextParag *p = c1.parag()->next();
00691         while ( p && p != c2.parag() ) {
00692             text += p->toString() + '\n';
00693             p->saveOasis( writer, context, 0, p->length()-2, true );
00694             p = p->next();
00695         }
00696         text += c2.parag()->toString( 0, c2.index() );
00697         c2.parag()->saveOasis( writer, context, 0, c2.index() - 1, true );
00698     }
00699     return text;
00700 }
00701 
00702 void KoTextDocument::setFormat( int id, const KoTextFormat *f, int flags )
00703 {
00704     QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( id );
00705     if ( it == selections.end() )
00706         return;
00707 
00708     KoTextDocumentSelection sel = *it;
00709 
00710     KoTextCursor c1 = sel.startCursor;
00711     KoTextCursor c2 = sel.endCursor;
00712     if ( sel.swapped ) {
00713         c2 = sel.startCursor;
00714         c1 = sel.endCursor;
00715     }
00716 
00717     if ( c1.parag() == c2.parag() ) {
00718         c1.parag()->setFormat( c1.index(), c2.index() - c1.index(), f, true, flags );
00719         return;
00720     }
00721 
00722     c1.parag()->setFormat( c1.index(), c1.parag()->length() - c1.index(), f, true, flags );
00723     KoTextParag *p = c1.parag()->next();
00724     while ( p && p != c2.parag() ) {
00725         p->setFormat( 0, p->length(), f, true, flags );
00726         p = p->next();
00727     }
00728     c2.parag()->setFormat( 0, c2.index(), f, true, flags );
00729 }
00730 
00731 /*void KoTextDocument::copySelectedText( int id )
00732 {
00733 #ifndef QT_NO_CLIPBOARD
00734     if ( !hasSelection( id ) )
00735         return;
00736 
00737     QApplication::clipboard()->setText( selectedText( id ) );
00738 #endif
00739 }*/
00740 
00741 void KoTextDocument::removeSelectedText( int id, KoTextCursor *cursor )
00742 {
00743     QMap<int, KoTextDocumentSelection>::Iterator it = selections.find( id );
00744     if ( it == selections.end() )
00745         return;
00746 
00747     KoTextDocumentSelection sel = *it;
00748 
00749     KoTextCursor c1 = sel.startCursor;
00750     KoTextCursor c2 = sel.endCursor;
00751     if ( sel.swapped ) {
00752         c2 = sel.startCursor;
00753         c1 = sel.endCursor;
00754     }
00755 
00756     *cursor = c1;
00757     removeSelection( id );
00758 
00759     if ( c1.parag() == c2.parag() ) {
00760         c1.parag()->remove( c1.index(), c2.index() - c1.index() );
00761         return;
00762     }
00763 
00764     // ## Qt has a strange setValid/isValid on QTextCursor, only used in the few lines below !?!?
00765     bool valid = true;
00766     if ( c1.parag() == fParag && c1.index() == 0 &&
00767          c2.parag() == lParag && c2.index() == lParag->length() - 1 )
00768         valid = false;
00769 
00770     bool didGoLeft = false;
00771     if (  c1.index() == 0 && c1.parag() != fParag ) {
00772         cursor->gotoPreviousLetter();
00773         if ( valid )
00774             didGoLeft = true;
00775     }
00776 
00777     c1.parag()->remove( c1.index(), c1.parag()->length() - 1 - c1.index() );
00778     KoTextParag *p = c1.parag()->next();
00779     int dy = 0;
00780     KoTextParag *tmp;
00781     while ( p && p != c2.parag() ) {
00782         tmp = p->next();
00783         dy -= p->rect().height();
00784         //emit paragraphDeleted( p ); // done by KoTextParag dtor already
00785         delete p;
00786         p = tmp;
00787     }
00788     c2.parag()->remove( 0, c2.index() );
00789     while ( p ) {
00790         p->move( dy );
00792         if ( p->paragLayout().counter )
00793             p->paragLayout().counter->invalidate();
00795         p->invalidate( 0 );
00796         //p->setEndState( -1 );
00797         p = p->next();
00798     }
00799 
00800     c1.parag()->join( c2.parag() );
00801 
00802     if ( didGoLeft )
00803         cursor->gotoNextLetter();
00804 }
00805 
00806 void KoTextDocument::addCommand( KoTextDocCommand *cmd )
00807 {
00808     commandHistory->addCommand( cmd );
00809 }
00810 
00811 KoTextCursor *KoTextDocument::undo( KoTextCursor *c )
00812 {
00813     return commandHistory->undo( c );
00814 }
00815 
00816 KoTextCursor *KoTextDocument::redo( KoTextCursor *c )
00817 {
00818     return commandHistory->redo( c );
00819 }
00820 
00821 bool KoTextDocument::find( const QString &expr, bool cs, bool wo, bool forward,
00822                           int *parag, int *index, KoTextCursor *cursor )
00823 {
00824     KoTextParag *p = forward ? fParag : lParag;
00825     if ( parag )
00826         p = paragAt( *parag );
00827     else if ( cursor )
00828         p = cursor->parag();
00829     bool first = true;
00830 
00831     while ( p ) {
00832         QString s = p->string()->toString();
00833         s.remove( s.length() - 1, 1 ); // get rid of trailing space
00834         int start = forward ? 0 : s.length() - 1;
00835         if ( first && index )
00836             start = *index;
00837         else if ( first && cursor )
00838             start = cursor->index();
00839         if ( !forward && first ) {
00840             start -= expr.length() + 1;
00841             if ( start < 0 ) {
00842                 first = false;
00843                 p = p->prev();
00844                 continue;
00845             }
00846         }
00847         first = false;
00848 
00849         for ( ;; ) {
00850             int res = forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
00851             if ( res == -1 )
00852                 break;
00853 
00854             bool ok = true;
00855             if ( wo ) {
00856                 int end = res + expr.length();
00857                 if ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) &&
00858                      ( end == (int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) )
00859                     ok = true;
00860                 else
00861                     ok = false;
00862             }
00863             if ( ok && cursor ) {
00864                 cursor->setParag( p );
00865                 cursor->setIndex( res );
00866                 setSelectionStart( Standard, cursor );
00867                 cursor->setIndex( res + expr.length() );
00868                 setSelectionEnd( Standard, cursor );
00869                 if ( parag )
00870                     *parag = p->paragId();
00871                 if ( index )
00872                     *index = res;
00873                 return true;
00874             }
00875             if ( forward ) {
00876                 start = res + 1;
00877             } else {
00878                 if ( res == 0 )
00879                     break;
00880                 start = res - 1;
00881             }
00882         }
00883         p = forward ? p->next() : p->prev();
00884     }
00885 
00886     return false;
00887 }
00888 
00889 bool KoTextDocument::inSelection( int selId, const QPoint &pos ) const
00890 {
00891     QMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( selId );
00892     if ( it == selections.end() )
00893         return false;
00894 
00895     KoTextDocumentSelection sel = *it;
00896     KoTextParag *startParag = sel.startCursor.parag();
00897     KoTextParag *endParag = sel.endCursor.parag();
00898     if ( sel.startCursor.parag() == sel.endCursor.parag() &&
00899          sel.startCursor.parag()->selectionStart( selId ) == sel.endCursor.parag()->selectionEnd( selId ) )
00900         return false;
00901     if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
00902         endParag = sel.startCursor.parag();
00903         startParag = sel.endCursor.parag();
00904     }
00905 
00906     KoTextParag *p = startParag;
00907     while ( p ) {
00908         if ( p->rect().contains( pos ) ) {
00909             bool inSel = false;
00910             int selStart = p->selectionStart( selId );
00911             int selEnd = p->selectionEnd( selId );
00912             int y = 0;
00913             int h = 0;
00914             for ( int i = 0; i < p->length(); ++i ) {
00915                 if ( i == selStart )
00916                     inSel = true;
00917                 if ( i == selEnd )
00918                     break;
00919                 if ( p->at( i )->lineStart ) {
00920                     y = (*p->lineStarts.find( i ))->y;
00921                     h = (*p->lineStarts.find( i ))->h;
00922                 }
00923                 if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) {
00924                     if ( inSel && pos.x() >= p->at( i )->x &&
00925                          pos.x() <= p->at( i )->x + p->at( i )->width /*p->at( i )->format()->width( p->at( i )->c )*/ )
00926                         return true;
00927                 }
00928             }
00929         }
00930         if ( pos.y() < p->rect().y() )
00931             break;
00932         if ( p == endParag )
00933             break;
00934         p = p->next();
00935     }
00936 
00937     return false;
00938 }
00939 
00940 QPixmap *KoTextDocument::bufferPixmap( const QSize &s )
00941 {
00942     if ( !buf_pixmap ) {
00943         int w = QABS( s.width() );
00944         int h = QABS( s.height() );
00945         buf_pixmap = new QPixmap( w, h );
00946     } else {
00947         if ( buf_pixmap->width() < s.width() ||
00948              buf_pixmap->height() < s.height() ) {
00949             buf_pixmap->resize( qMax( s.width(), buf_pixmap->width() ),
00950                                 qMax( s.height(), buf_pixmap->height() ) );
00951         }
00952     }
00953 
00954     return buf_pixmap;
00955 }
00956 
00957 void KoTextDocument::registerCustomItem( KoTextCustomItem *i, KoTextParag *p )
00958 {
00959     if (!i)
00960         return;
00961 
00962     if ( i->placement() != KoTextCustomItem::PlaceInline )
00963         flow_->registerFloatingItem( i );
00964     p->registerFloatingItem( i );
00965     i->setParagraph( p );
00966     //kDebug(32500) << "KoTextDocument::registerCustomItem " << (void*)i << endl;
00967     customItems.append( i );
00968 }
00969 
00970 void KoTextDocument::unregisterCustomItem( KoTextCustomItem *i, KoTextParag *p )
00971 {
00972     flow_->unregisterFloatingItem( i );
00973     p->unregisterFloatingItem( i );
00974     i->setParagraph( 0 );
00975     customItems.removeRef( i );
00976 }
00977 
00978 int KoTextDocument::length() const
00979 {
00980     int l = 0;
00981     KoTextParag *p = fParag;
00982     while ( p ) {
00983         l += p->length() - 1; // don't count trailing space
00984         p = p->next();
00985     }
00986     return l;
00987 }
00988 
00989 bool KoTextDocument::visitSelection( int selectionId, KoParagVisitor* visitor, bool forward )
00990 {
00991     KoTextCursor c1 = selectionStartCursor( selectionId );
00992     KoTextCursor c2 = selectionEndCursor( selectionId );
00993     if ( c1 == c2 )
00994         return true;
00995     return visitFromTo( c1.parag(), c1.index(), c2.parag(), c2.index(), visitor, forward );
00996 }
00997 
00998 bool KoTextDocument::hasSelection( int id, bool visible ) const
00999 {
01000     return ( selections.find( id ) != selections.end() &&
01001              ( !visible ||
01002                ( (KoTextDocument*)this )->selectionStartCursor( id ) !=
01003                ( (KoTextDocument*)this )->selectionEndCursor( id ) ) );
01004 }
01005 
01006 void KoTextDocument::setSelectionStart( int id, KoTextCursor *cursor )
01007 {
01008     KoTextDocumentSelection sel;
01009     sel.startCursor = *cursor;
01010     sel.endCursor = *cursor;
01011     sel.swapped = false;
01012     selections[ id ] = sel;
01013 }
01014 
01015 KoTextParag *KoTextDocument::paragAt( int i ) const
01016 {
01017     KoTextParag *s = fParag;
01018     while ( s ) {
01019         if ( s->paragId() == i )
01020             return s;
01021         s = s->next();
01022     }
01023     return 0;
01024 }
01025 
01026 bool KoTextDocument::visitDocument( KoParagVisitor *visitor, bool forward )
01027 {
01028     return visitFromTo( firstParag(), 0, lastParag(), lastParag()->length()-1, visitor, forward );
01029 }
01030 
01031 bool KoTextDocument::visitFromTo( KoTextParag *firstParag, int firstIndex, KoTextParag* lastParag, int lastIndex, KoParagVisitor* visitor, bool forw )
01032 {
01033     if ( firstParag == lastParag )
01034     {
01035         return visitor->visit( firstParag, firstIndex, lastIndex );
01036     }
01037     else
01038     {
01039         bool ret = true;
01040         if ( forw )
01041         {
01042             // the -1 is for the trailing space
01043             ret = visitor->visit( firstParag, firstIndex, firstParag->length() - 1 );
01044             if (!ret) return false;
01045         }
01046         else
01047         {
01048             ret = visitor->visit( lastParag, 0, lastIndex );
01049             if (!ret) return false;
01050         }
01051 
01052         KoTextParag* currentParag = forw ? firstParag->next() : lastParag->prev();
01053         KoTextParag * endParag = forw ? lastParag : firstParag;
01054         while ( currentParag && currentParag != endParag )
01055         {
01056             ret = visitor->visit( currentParag, 0, currentParag->length() - 1 );
01057             if (!ret) return false;
01058             currentParag = forw ? currentParag->next() : currentParag->prev();
01059         }
01060         Q_ASSERT( currentParag );
01061         Q_ASSERT( endParag == currentParag );
01062         if ( forw )
01063             ret = visitor->visit( lastParag, 0, lastIndex );
01064         else
01065             ret = visitor->visit( currentParag, firstIndex, currentParag->length() - 1 );
01066         return ret;
01067     }
01068 }
01069 
01070 static bool is_printer( QPainter *p )
01071 {
01072     return p && p->device() && p->device()->devType() == QInternal::Printer;
01073 }
01074 
01075 KoTextParag *KoTextDocument::drawWYSIWYG( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
01076                                           KoTextZoomHandler* zoomHandler, bool onlyChanged,
01077                                           bool drawCursor, KoTextCursor *cursor,
01078                                           bool resetChanged, uint drawingFlags )
01079 {
01080     m_drawingFlags = drawingFlags;
01081     // We need to draw without double-buffering if
01082     // 1) printing (to send text and not bitmaps to the printer)
01083     // 2) drawing a transparent embedded document
01084     //
01085     if ( is_printer( p ) || ( drawingFlags & TransparentBackground ) ) {
01086     // This stuff relies on doLayout()... simpler to just test for Printer.
01087     // If someone understand doLayout() please tell me (David)
01088     /*if ( isWithoutDoubleBuffer() || par && par->withoutDoubleBuffer ) { */
01089         //setWithoutDoubleBuffer( true );
01090         QRect crect( cx, cy, cw, ch );
01091         drawWithoutDoubleBuffer( p, crect, cg, zoomHandler );
01092         return 0;
01093     }
01094     //setWithoutDoubleBuffer( false );
01095 
01096     if ( !firstParag() )
01097         return 0;
01098 
01099     KoTextParag *lastFormatted = 0;
01100     KoTextParag *parag = firstParag();
01101 
01102     QPixmap *doubleBuffer = 0;
01103     QPainter painter;
01104     // All the coordinates in this method are in view pixels
01105     QRect crect( cx, cy, cw, ch );
01106     Q_ASSERT( ch > 0 );
01107 #ifdef DEBUG_PAINTING
01108     kDebug(32500) << "\nKoTextDocument::drawWYSIWYG crect=" << crect << endl;
01109 #endif
01110 
01111     // Space above first parag
01112     QRect pixelRect = parag->pixelRect( zoomHandler );
01113     if ( isPageBreakEnabled() && parag && cy <= pixelRect.y() && pixelRect.y() > 0 ) {
01114         QRect r( 0, 0,
01115                  zoomHandler->layoutUnitToPixelX( parag->document()->x() + parag->document()->width() ),
01116                  pixelRect.y() );
01117         r &= crect;
01118         if ( !r.isEmpty() ) {
01119 #ifdef DEBUG_PAINTING
01120             kDebug(32500) << " drawWYSIWYG: space above first parag: " << r << " (pixels)" << endl;
01121 #endif
01122             p->fillRect( r, cg.brush( QColorGroup::Base ) );
01123         }
01124     }
01125 
01126     while ( parag ) {
01127         lastFormatted = parag;
01128         if ( !parag->isValid() )
01129             parag->format();
01130 
01131         QRect ir = parag->pixelRect( zoomHandler );
01132 #ifdef DEBUG_PAINTING
01133         kDebug(32500) << " drawWYSIWYG: ir=" << ir << endl;
01134 #endif
01135         if ( isPageBreakEnabled() && parag->next() && ( drawingFlags & TransparentBackground ) == 0 )
01136         {
01137             int nexty = parag->next()->pixelRect(zoomHandler).y();
01138             // Test ir.y+ir.height, which is the first pixel _under_ the parag
01139             // (as opposed ir.bottom() which is the last pixel of the parag)
01140             if ( ir.y() + ir.height() < nexty ) {
01141                 QRect r( 0, ir.y() + ir.height(),
01142                          zoomHandler->layoutUnitToPixelX( parag->document()->x() + parag->document()->width() ),
01143                          nexty - ( ir.y() + ir.height() ) );
01144                 r &= crect;
01145                 if ( !r.isEmpty() )
01146                 {
01147 #ifdef DEBUG_PAINTING
01148                     kDebug(32500) << " drawWYSIWYG: space between parag " << parag->paragId() << " and " << parag->next()->paragId() << " : " << r << " (pixels)" << endl;
01149 #endif
01150                     p->fillRect( r, cg.brush( QColorGroup::Base ) );
01151                 }
01152             }
01153         }
01154 
01155         if ( !ir.intersects( crect ) ) {
01156             // Paragraph is not in the crect - but let's check if the area on its right is.
01157             ir.setWidth( zoomHandler->layoutUnitToPixelX( parag->document()->width() ) );
01158             if ( ir.intersects( crect ) && ( drawingFlags & TransparentBackground ) == 0 )
01159                 p->fillRect( ir.intersect( crect ), cg.brush( QColorGroup::Base ) );
01160             if ( ir.y() > cy + ch ) {
01161                 goto floating;
01162             }
01163         }
01164         else if ( parag->hasChanged() || !onlyChanged ) {
01165             // lineChanged() only makes sense if we're drawing with onlyChanged=true
01166             // otherwise, call setChanged() to make sure we'll paint it all (lineChanged=-1).
01167             // (this avoids having to send onlyChanged to drawParagWYSIWYG)
01168             if ( !onlyChanged && parag->lineChanged() > 0 )
01169                 parag->setChanged( false );
01170             drawParagWYSIWYG( p, parag, cx, cy, cw, ch, doubleBuffer, cg,
01171                               zoomHandler, drawCursor, cursor, resetChanged, drawingFlags );
01172         }
01173         parag = parag->next();
01174     }
01175 
01176     parag = lastParag();
01177 
01178 floating:
01179     pixelRect = parag->pixelRect(zoomHandler);
01180     int docheight = zoomHandler->layoutUnitToPixelY( parag->document()->height() );
01181     if ( pixelRect.y() + pixelRect.height() < docheight ) {
01182         int docwidth = zoomHandler->layoutUnitToPixelX( parag->document()->width() );
01183         if ( ( drawingFlags & TransparentBackground ) == 0 ) {
01184             p->fillRect( 0, pixelRect.y() + pixelRect.height(),
01185                          docwidth, docheight - ( pixelRect.y() + pixelRect.height() ),
01186                          cg.brush( QColorGroup::Base ) );
01187         }
01188         if ( !flow()->isEmpty() ) {
01189             QRect cr( cx, cy, cw, ch );
01190             cr = cr.intersect( QRect( 0, pixelRect.y() + pixelRect.height(), docwidth,
01191                                       docheight - ( pixelRect.y() + pixelRect.height() ) ) );
01192             flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, false );
01193         }
01194     }
01195 
01196     if ( buf_pixmap && buf_pixmap->height() > 300 ) {
01197         delete buf_pixmap;
01198         buf_pixmap = 0;
01199     }
01200 
01201     return lastFormatted;
01202 }
01203 
01204 
01205 // Used for printing
01206 void KoTextDocument::drawWithoutDoubleBuffer( QPainter *p, const QRect &cr, const QColorGroup &cg,
01207                                               KoTextZoomHandler* zoomHandler, const QBrush *paper )
01208 {
01209     if ( !firstParag() )
01210         return;
01211 
01212     Q_ASSERT( (m_drawingFlags & DrawSelections) == 0 );
01213     if (m_drawingFlags & DrawSelections)
01214            kDebug() << kBacktrace();
01215     if ( paper && ( m_drawingFlags & TransparentBackground ) == 0 ) {
01216         p->setBrushOrigin(  -(int)p->translationX(),  -(int)p->translationY() );
01217         p->fillRect( cr, *paper );
01218     }
01219 
01220     KoTextParag *parag = firstParag();
01221     while ( parag ) {
01222         if ( !parag->isValid() )
01223             parag->format();
01224 
01225         QRect pr( parag->pixelRect( zoomHandler ) );
01226         pr.setLeft( 0 );
01227         pr.setWidth( QWIDGETSIZE_MAX );
01228         // The cliprect is checked in layout units, in KoTextParag::paint
01229         QRect crect_lu( parag->rect() );
01230 
01231         if ( !cr.isNull() && !cr.intersects( pr ) ) {
01232             parag = parag->next();
01233             continue;
01234         }
01235 
01236         p->translate( 0, pr.y() );
01237 
01238         // No need to brush plain white on a printer. Brush all
01239         // other cases (except "full transparent" case).
01240         QBrush brush = cg.brush( QColorGroup::Base );;
01241         bool needBrush = brush.style() != Qt::NoBrush &&
01242                          !(brush.style() == Qt::SolidPattern &&
01243                            brush.color() == Qt::white &&
01244                            is_printer(p));
01245         if ( needBrush && ( m_drawingFlags & TransparentBackground ) == 0 )
01246             p->fillRect( QRect( 0, 0, pr.width(), pr.height() ), brush );
01247 
01248         //p->setBrushOrigin( p->brushOrigin() + QPoint( 0, pr.y() ) );
01249         parag->paint( *p, cg, 0, false,
01250                       crect_lu.x(), crect_lu.y(),
01251                       crect_lu.width(), crect_lu.height() );
01252         p->translate( 0, -pr.y() );
01253         //p->setBrushOrigin( p->brushOrigin() - QPoint( 0, pr.y() ) );
01254         parag = parag->next();
01255     }
01256 }
01257 
01258 // Used for screen display (and also printing?)
01259 // Called by drawWYSIWYG and the app's drawCursor
01260 void KoTextDocument::drawParagWYSIWYG( QPainter *p, KoTextParag *parag, int cx, int cy, int cw, int ch,
01261                                        QPixmap *&doubleBuffer, const QColorGroup &cg,
01262                                        KoTextZoomHandler* zoomHandler, bool drawCursor,
01263                                        KoTextCursor *cursor, bool resetChanged, uint drawingFlags )
01264 {
01265     if ( cw <= 0 || ch <= 0 ) { Q_ASSERT( cw > 0 ); Q_ASSERT( ch > 0 ); return; }
01266 #ifdef DEBUG_PAINTING
01267     kDebug(32500) << "KoTextDocument::drawParagWYSIWYG " << (void*)parag << " id:" << parag->paragId() << endl;
01268 #endif
01269     m_drawingFlags = drawingFlags;
01270     QPainter *painter = 0;
01271     // Those three rects are in pixels, in the document coordinates (0,0 == topleft of first parag)
01272     QRect rect = parag->pixelRect( zoomHandler ); // the parag rect
01273 
01274     int offsetY = 0;
01275     // Start painting from a given line number.
01276     if ( parag->lineChanged() > -1 )
01277     {
01278         offsetY = zoomHandler->layoutUnitToPixelY( parag->lineY( parag->lineChanged() ) - parag->topMargin() );
01279 #ifdef DEBUG_PAINTING
01280         kDebug(32500) << " Repainting from lineChanged=" << parag->lineChanged() << " -> adding " << offsetY << " to rect" << endl;
01281 #endif
01282         // Skip the lines that are not repainted by moving Top. The bottom doesn't change.
01283         rect.rTop() += offsetY;
01284     }
01285 
01286     QRect crect( cx, cy, cw, ch ); // the overall crect
01287     QRect ir( rect ); // will be the rect to be repainted
01288 
01289     QBrush brush = cg.brush( QColorGroup::Base );
01290 
01291     // No need to brush plain white on a printer. Brush all
01292     // other cases (except "full transparent" case).
01293     bool needBrush = brush.style() != Qt::NoBrush &&
01294                      ( drawingFlags & TransparentBackground ) == 0 &&
01295                      !(brush.style() == Qt::SolidPattern &&
01296                        brush.color() == Qt::white &&
01297                        is_printer(p));
01298 
01299     bool useDoubleBuffer = !parag->document()->parent();
01300     if ( is_printer(p) )
01301         useDoubleBuffer = false;
01302     // Can't handle transparency using double-buffering, in case of rotation/scaling (due to bitBlt)
01303     // The test on mat is almost like isIdentity(), but allows for translation.
01305     // of being white.
01306     QMatrix mat = p->worldMatrix();
01307     if ( ( mat.m11() != 1.0 || mat.m22() != 1.0 || mat.m12() != 0.0 || mat.m21() != 0.0 )
01308          && brush.style() != Qt::SolidPattern )
01309         useDoubleBuffer = false;
01310 
01311 #ifdef DEBUG_PAINTING
01312     kDebug(32500) << "KoTextDocument::drawParagWYSIWYG parag->rect=" << parag->rect()
01313                    << " pixelRect(ir)=" << ir
01314                    << " crect (pixels)=" << crect
01315                    << " useDoubleBuffer=" << useDoubleBuffer << endl;
01316 #endif
01317 
01318     if ( useDoubleBuffer  ) {
01319         painter = new QPainter;
01320         if ( cx >= 0 && cy >= 0 )
01321             ir = ir.intersect( crect );
01322         if ( !doubleBuffer ||
01323              ir.width() > doubleBuffer->width() ||
01324              ir.height() > doubleBuffer->height() )
01325         {
01326             doubleBuffer = bufferPixmap( ir.size() );
01327         }
01328         painter->begin( doubleBuffer );
01329 
01330     } else {
01331         p->save();
01332         painter = p;
01333         painter->translate( ir.x(), ir.y() );
01334     }
01335     // Until the next translate(), (0,0) in the painter will be at ir.topLeft() in reality
01336     //kDebug() << "KoTextDocument::drawParagWYSIWYG ir=" << ir << endl;
01337 
01338 
01339     // Cumulate ir.x(), ir.y() with the current brush origin
01340     //painter->setBrushOrigin( painter->brushOrigin() + ir.topLeft() );
01341 
01342     if ( useDoubleBuffer || is_printer( painter ) ) {
01343         // Transparent -> grab background from p's device
01344         if ( brush.style() != Qt::SolidPattern ) {
01345             bitBlt( doubleBuffer, 0, 0, p->device(),
01346                     ir.x() + (int)p->translationX(), ir.y() + (int)p->translationY(),
01347                     ir.width(), ir.height() );
01348         }
01349     }
01350 
01351     if ( needBrush )
01352         painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ), brush );
01353 
01354     // Now revert the previous painter translation, and instead make (0,0) the topleft of the PARAGRAPH
01355     painter->translate( rect.x() - ir.x(), rect.y() - ir.y() );
01356 #ifdef DEBUG_PAINTING
01357     kDebug(32500) << "KoTextDocument::drawParagWYSIWYG translate " << rect.x() - ir.x() << "," << rect.y() - ir.y() << endl;
01358 #endif
01359     //painter->setBrushOrigin( painter->brushOrigin() + rect.topLeft() - ir.topLeft() );
01360 
01361     // The cliprect is checked in layout units, in KoTextParag::paint
01362     QRect crect_lu( zoomHandler->pixelToLayoutUnit( crect ) );
01363 #ifdef DEBUG_PAINTING
01364     kDebug(32500) << "KoTextDocument::drawParagWYSIWYG crect_lu=" << crect_lu << endl;
01365 #endif
01366 
01367     // paintDefault will paint line 'lineChanged' at its normal Y position.
01368     // But the buffer-pixmap below starts at Y. We need to translate by -Y
01369     // so that the painting happens at the right place.
01370     painter->translate( 0, -offsetY );
01371 
01372     parag->paint( *painter, cg, drawCursor ? cursor : 0, (m_drawingFlags & DrawSelections),
01373                   crect_lu.x(), crect_lu.y(), crect_lu.width(), crect_lu.height() );
01374 
01375 
01376     if ( useDoubleBuffer ) {
01377         delete painter;
01378         painter = 0;
01379         p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) );
01380 #if 0 // for debug!
01381         p->save();
01382         p->setPen( Qt::blue );
01383         p->drawRect( ir.x(), ir.y(), ir.width(), ir.height() );
01384         p->restore();
01385 #endif
01386     } else {
01387         // undo previous translations, painter is 'p', i.e. will be used later on
01388         p->restore();
01389         //painter->translate( -ir.x(), -ir.y() );
01390         //painter->translate( 0, +offsetY );
01391         //painter->setBrushOrigin( painter->brushOrigin() - ir.topLeft() );
01392     }
01393 
01394     if ( needBrush ) {
01395         int docright = zoomHandler->layoutUnitToPixelX( parag->document()->x() + parag->document()->width() );
01396 #ifdef DEBUG_PAINTING
01397 //        kDebug(32500) << "KoTextDocument::drawParagWYSIWYG my rect is: " << rect << endl;
01398 #endif
01399         if ( rect.x() + rect.width() < docright ) {
01400 #ifdef DEBUG_PAINTING
01401             kDebug(32500) << "KoTextDocument::drawParagWYSIWYG rect doesn't go up to docright=" << docright << endl;
01402 #endif
01403             p->fillRect( rect.x() + rect.width(), rect.y(),
01404                          docright - ( rect.x() + rect.width() ),
01405                          rect.height(), cg.brush( QColorGroup::Base ) );
01406         }
01407     }
01408 
01409     if ( resetChanged )
01410         parag->setChanged( false );
01411 }
01412 
01413 
01414 KoTextDocCommand *KoTextDocument::deleteTextCommand( KoTextDocument *textdoc, int id, int index, const Q3MemArray<KoTextStringChar> & str, const CustomItemsMap & customItemsMap, const Q3ValueList<KoParagLayout> & oldParagLayouts )
01415 {
01416     return new KoTextDeleteCommand( textdoc, id, index, str, customItemsMap, oldParagLayouts );
01417 }
01418 
01419 KoTextParag* KoTextDocument::loadOasisText( const QDomElement& bodyElem, KoOasisContext& context, KoTextParag* lastParagraph, KoStyleCollection* styleColl, KoTextParag* nextParagraph )
01420 {
01421     // was OoWriterImport::parseBodyOrSimilar
01422     QDomElement tag;
01423     forEachElement( tag, bodyElem )
01424     {
01425         context.styleStack().save();
01426         const QString localName = tag.localName();
01427         const bool isTextNS = tag.namespaceURI() == KoXmlNS::text;
01428         uint pos = 0;
01429         if ( isTextNS && localName == "p" ) {  // text paragraph
01430             context.fillStyleStack( tag, KoXmlNS::text, "style-name", "paragraph" );
01431 
01432             KoTextParag *parag = createParag( this, lastParagraph, nextParagraph );
01433             parag->loadOasis( tag, context, styleColl, pos );
01434             if ( !lastParagraph )        // First parag
01435                 setFirstParag( parag );
01436             lastParagraph = parag;
01437         }
01438         else if ( isTextNS && localName == "h" ) // heading
01439         {
01440             //kDebug(32500) << " heading " << endl;
01441             context.fillStyleStack( tag, KoXmlNS::text, "style-name", "paragraph" );
01442             int level = tag.attributeNS( KoXmlNS::text, "outline-level", QString::null ).toInt();
01443             bool listOK = false;
01444             // When a heading is inside a list, it seems that the list prevails.
01445             // Example:
01446             //    <text:list text:style-name="Numbering 1">
01447             //      <text:list-item text:start-value="5">
01448             //        <text:h text:style-name="P2" text:level="4">The header</text:h>
01449             // where P2 has list-style-name="something else"
01450             // Result: the numbering of the header follows "Numbering 1".
01451             // So we use the style for the outline level only if we're not inside a list:
01452             //if ( !context.atStartOfListItem() )
01453             // === The new method for this is that we simply override it after loading.
01454             listOK = context.pushOutlineListLevelStyle( level );
01455             int restartNumbering = -1;
01456             if ( tag.hasAttributeNS( KoXmlNS::text, "start-value" ) )
01457                 // OASIS extension http://lists.oasis-open.org/archives/office/200310/msg00033.html
01458                 restartNumbering = tag.attributeNS( KoXmlNS::text, "start-value", QString::null ).toInt();
01459 
01460             KoTextParag *parag = createParag( this, lastParagraph, nextParagraph );
01461             parag->loadOasis( tag, context, styleColl, pos );
01462             if ( !lastParagraph )        // First parag
01463                 setFirstParag( parag );
01464             lastParagraph = parag;
01465             if ( listOK ) {
01466                 parag->applyListStyle( context, restartNumbering, true /*ordered*/, true /*heading*/, level );
01467                 context.listStyleStack().pop();
01468             }
01469         }
01470         else if ( isTextNS &&
01471                   ( localName == "unordered-list" || localName == "ordered-list" // OOo-1.1
01472                     || localName == "list" || localName == "numbered-paragraph" ) )  // OASIS
01473         {
01474             lastParagraph = loadList( tag, context, lastParagraph, styleColl, nextParagraph );
01475         }
01476         else if ( isTextNS && localName == "section" ) // Temporary support (###TODO)
01477         {
01478             kDebug(32500) << "Section found!" << endl;
01479             context.fillStyleStack( tag, KoXmlNS::text, "style-name", "section" );
01480             lastParagraph = loadOasisText( tag, context, lastParagraph, styleColl, nextParagraph );
01481         }
01482         else if ( isTextNS && localName == "variable-decls" )
01483         {
01484             // We don't parse variable-decls since we ignore var types right now
01485             // (and just storing a list of available var names wouldn't be much use)
01486         }
01487         else if ( isTextNS && localName == "user-field-decls" )
01488         {
01489             QDomElement fd;
01490             forEachElement( fd, tag )
01491             {
01492                 if ( fd.namespaceURI() == KoXmlNS::text && fd.localName() == "user-field-decl" )
01493                 {
01494                     const QString name = fd.attributeNS( KoXmlNS::text, "name", QString::null );
01495                     const QString value = fd.attributeNS( KoXmlNS::office, "value", QString::null );
01496                     if ( !name.isEmpty() )
01497                         context.variableCollection().setVariableValue( name, value );
01498                 }
01499             }
01500         }
01501         else if ( isTextNS && localName == "number" ) // text:number
01502         {
01503             // This is the number in front of a numbered paragraph,
01504             // written out to help export filters. We can ignore it.
01505         }
01506         else if ( !loadOasisBodyTag( tag, context, lastParagraph, styleColl, nextParagraph ) )
01507         {
01508             kWarning(32500) << "Unsupported body element '" << localName << "'" << endl;
01509         }
01510 
01511         context.styleStack().restore(); // remove the styles added by the paragraph or list
01512         //use signal slot ?
01513         //m_doc->progressItemLoaded(); // ## check
01514     }
01515     return lastParagraph;
01516 }
01517 
01518 KoTextParag* KoTextDocument::loadList( const QDomElement& list, KoOasisContext& context, KoTextParag* lastParagraph, KoStyleCollection * styleColl, KoTextParag* nextParagraph )
01519 {
01520     //kDebug(32500) << "loadList: " << list.attributeNS( KoXmlNS::text, "style-name", QString::null ) << endl;
01521 
01522     const QString oldListStyleName = context.currentListStyleName();
01523     if ( list.hasAttributeNS( KoXmlNS::text, "style-name" ) )
01524         context.setCurrentListStyleName( list.attributeNS( KoXmlNS::text, "style-name", QString::null ) );
01525     bool listOK = !context.currentListStyleName().isEmpty();
01526     int level;
01527     if ( list.localName() == "numbered-paragraph" )
01528         level = list.attributeNS( KoXmlNS::text, "level", "1" ).toInt();
01529     else
01530         level = context.listStyleStack().level() + 1;
01531     if ( listOK )
01532         listOK = context.pushListLevelStyle( context.currentListStyleName(), level );
01533 
01534     const QDomElement listStyle = context.listStyleStack().currentListStyle();
01535     // The tag is either list-level-style-number or list-level-style-bullet
01536     const bool orderedList = listStyle.localName() == "list-level-style-number";
01537 
01538     if ( list.localName() == "numbered-paragraph" )
01539     {
01540         // A numbered-paragraph contains paragraphs directly (it's both a list and a list-item)
01541         int restartNumbering = -1;
01542         if ( list.hasAttributeNS( KoXmlNS::text, "start-value" ) )
01543             restartNumbering = list.attributeNS( KoXmlNS::text, "start-value", QString::null ).toInt();
01544         KoTextParag* oldLast = lastParagraph;
01545         lastParagraph = loadOasisText( list, context, lastParagraph, styleColl, nextParagraph );
01546         KoTextParag* firstListItem = oldLast ? oldLast->next() : firstParag();
01547         // Apply list style to first paragraph inside numbered-parag - there's only one anyway
01548         // Keep the "is outline" property though
01549         bool isOutline = firstListItem->counter() && firstListItem->counter()->numbering() == KoParagCounter::NUM_CHAPTER;
01550         firstListItem->applyListStyle( context, restartNumbering, orderedList,
01551                                        isOutline, level );
01552     }
01553     else
01554     {
01555         // Iterate over list items
01556         for ( QDomNode n = list.firstChild(); !n.isNull(); n = n.nextSibling() )
01557         {
01558             QDomElement listItem = n.toElement();
01559             int restartNumbering = -1;
01560             if ( listItem.hasAttributeNS( KoXmlNS::text, "start-value" ) )
01561                 restartNumbering = listItem.attributeNS( KoXmlNS::text, "start-value", QString::null ).toInt();
01562             bool isListHeader = listItem.localName() == "list-header" || listItem.attributeNS( KoXmlNS::text, "is-list-header", QString::null ) == "is-list-header";
01563             KoTextParag* oldLast = lastParagraph;
01564             lastParagraph = loadOasisText( listItem, context, lastParagraph, styleColl, nextParagraph );
01565             KoTextParag* firstListItem = oldLast ? oldLast->next() : firstParag();
01566             KoTextParag* p = firstListItem;
01567             // It's either list-header (normal text on top of list) or list-item
01568             if ( !isListHeader && firstListItem ) {
01569                 // Apply list style to first paragraph inside list-item
01570                 bool isOutline = firstListItem->counter() && firstListItem->counter()->numbering() == KoParagCounter::NUM_CHAPTER;
01571                 firstListItem->applyListStyle( context, restartNumbering, orderedList, isOutline, level );
01572                 p = p->next();
01573             }
01574             // Make text:h inside list-item (as non first child) unnumbered.
01575             while ( p && p != lastParagraph->next() ) {
01576                 if ( p->counter() )
01577                     p->counter()->setNumbering( KoParagCounter::NUM_NONE );
01578                 p = p->next();
01579             }
01580         }
01581     }
01582     if ( listOK )
01583         context.listStyleStack().pop();
01584     context.setCurrentListStyleName( oldListStyleName );
01585     return lastParagraph;
01586 }
01587 
01588 void KoTextDocument::saveOasisContent( KoXmlWriter& writer, KoSavingContext& context ) const
01589 {
01590     // Basically just call saveOasis on every paragraph.
01591     // KWord doesn't use this method because it does table-of-contents-handling in addition.
01592     KoTextParag* parag = firstParag();
01593     while ( parag ) {
01594         // Save the whole parag, without the trailing space.
01595         parag->saveOasis( writer, context, 0, parag->lastCharPos() );
01596         parag = parag->next();
01597     }
01598 }
01599 
01600 #include "KoTextDocument.moc"

Généré le Wed Nov 22 23:41:09 2006 pour KPlato par  doxygen 1.5.1-p1