lib Library API Documentation

kotextdocument.cc

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