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 "kozoomhandler.h"
00022 #include "kotextformatter.h"
00023 #include <kdebug.h>
00024 #include <kdeversion.h>
00025 #if ! KDE_IS_VERSION(3,1,90)
00026 #include <kdebugclasses.h>
00027 #endif
00028 #include "kocommand.h"
00029 
00030 //#define DEBUG_PAINTING
00031 
00034 
00035 KoTextDocument::KoTextDocument( KoZoomHandler *zoomHandler, KoTextFormatCollection *fc,
00036                                 KoTextFormatter *formatter, bool createInitialParag )
00037     : m_zoomHandler( zoomHandler ),
00038       m_bDestroying( false ),
00039 #ifdef QTEXTTABLE_AVAILABLE
00040       par( 0L /*we don't use parent documents */ ),
00041       tc( 0 ),
00042 #endif
00043       tArray( 0 ), tStopWidth( 0 )
00044 {
00045     fCollection = fc;
00046     init(); // see korichtext.cpp
00047 
00048     m_drawingFlags = 0;
00049     setAddMargins( true );                 // top margin and bottom are added, not max'ed
00050     if ( !formatter )
00051         formatter = new KoTextFormatter;
00052     setFormatter( formatter );
00053 
00054     setY( 0 );
00055     setLeftMargin( 0 );
00056     setRightMargin( 0 );
00057 
00058     // Delete the KoTextParag created by KoTextDocument::init() if createInitialParag is false.
00059     if ( !createInitialParag )
00060         clear( false );
00061 }
00062 
00063 bool KoTextDocument::visitSelection( int selectionId, KoParagVisitor* visitor, bool forward )
00064 {
00065     KoTextCursor c1 = selectionStartCursor( selectionId );
00066     KoTextCursor c2 = selectionEndCursor( selectionId );
00067     if ( c1 == c2 )
00068         return true;
00069     return visitFromTo( c1.parag(), c1.index(), c2.parag(), c2.index(), visitor, forward );
00070 }
00071 
00072 bool KoTextDocument::hasSelection( int id, bool visible ) const
00073 {
00074     return ( selections.find( id ) != selections.end() &&
00075              ( !visible ||
00076                ( (KoTextDocument*)this )->selectionStartCursor( id ) !=
00077                ( (KoTextDocument*)this )->selectionEndCursor( id ) ) );
00078 }
00079 
00080 void KoTextDocument::setSelectionStart( int id, KoTextCursor *cursor )
00081 {
00082     KoTextDocumentSelection sel;
00083     sel.startCursor = *cursor;
00084     sel.endCursor = *cursor;
00085     sel.swapped = FALSE;
00086     selections[ id ] = sel;
00087 }
00088 
00089 KoTextParag *KoTextDocument::paragAt( int i ) const
00090 {
00091     KoTextParag *s = fParag;
00092     while ( s ) {
00093     if ( s->paragId() == i )
00094         return s;
00095     s = s->next();
00096     }
00097     return 0;
00098 }
00099 
00100 bool KoTextDocument::visitDocument( KoParagVisitor *visitor, bool forward )
00101 {
00102     return visitFromTo( firstParag(), 0, lastParag(), lastParag()->length()-1, visitor, forward );
00103 }
00104 
00105 bool KoTextDocument::visitFromTo( KoTextParag *firstParag, int firstIndex, KoTextParag* lastParag, int lastIndex, KoParagVisitor* visitor, bool forw )
00106 {
00107     if ( firstParag == lastParag )
00108     {
00109         return visitor->visit( firstParag, firstIndex, lastIndex );
00110     }
00111     else
00112     {
00113         bool ret = true;
00114         if ( forw )
00115         {
00116             // the -1 is for the trailing space
00117             ret = visitor->visit( firstParag, firstIndex, firstParag->length() - 1 );
00118             if (!ret) return false;
00119         }
00120         else
00121         {
00122             ret = visitor->visit( lastParag, 0, lastIndex );
00123             if (!ret) return false;
00124         }
00125 
00126         KoTextParag* currentParag = forw ? firstParag->next() : lastParag->prev();
00127         KoTextParag * endParag = forw ? lastParag : firstParag;
00128         while ( currentParag && currentParag != endParag )
00129         {
00130             ret = visitor->visit( currentParag, 0, currentParag->length() - 1 );
00131             if (!ret) return false;
00132             currentParag = forw ? currentParag->next() : currentParag->prev();
00133         }
00134         Q_ASSERT( currentParag );
00135         Q_ASSERT( endParag == currentParag );
00136         if ( forw )
00137             ret = visitor->visit( lastParag, 0, lastIndex );
00138         else
00139             ret = visitor->visit( currentParag, firstIndex, currentParag->length() - 1 );
00140         return ret;
00141     }
00142 }
00143 
00144 static bool is_printer( QPainter *p )
00145 {
00146     return p && p->device() && p->device()->devType() == QInternal::Printer;
00147 }
00148 
00149 KoTextParag *KoTextDocument::drawWYSIWYG( QPainter *p, int cx, int cy, int cw, int ch, const QColorGroup &cg,
00150                                           KoZoomHandler* zoomHandler, bool onlyChanged,
00151                                           bool drawCursor, KoTextCursor *cursor,
00152                                           bool resetChanged, uint drawingFlags )
00153 {
00154     m_drawingFlags = drawingFlags;
00155     if ( is_printer( p ) ) {
00156     // This stuff relies on doLayout()... simpler to just test for Printer.
00157     // If someone understand doLayout() please tell me (David)
00158     /*if ( isWithoutDoubleBuffer() || par && par->withoutDoubleBuffer ) { */
00159     //setWithoutDoubleBuffer( TRUE );
00160     QRect crect( cx, cy, cw, ch );
00161     drawWithoutDoubleBuffer( p, crect, cg, zoomHandler );
00162     return 0;
00163     }
00164     //setWithoutDoubleBuffer( FALSE );
00165 
00166     if ( !firstParag() )
00167         return 0;
00168 
00169     KoTextParag *lastFormatted = 0;
00170     KoTextParag *parag = firstParag();
00171 
00172     QPixmap *doubleBuffer = 0;
00173     QPainter painter;
00174     // All the coordinates in this method are in view pixels
00175     QRect crect( cx, cy, cw, ch );
00176 #ifdef DEBUG_PAINTING
00177     kdDebug(32500) << "\nKoTextDocument::drawWYSIWYG crect=" << crect << endl;
00178 #endif
00179 
00180     // Space above first parag
00181     QRect pixelRect = parag->pixelRect( zoomHandler );
00182     if ( isPageBreakEnabled() && parag && cy <= pixelRect.y() && pixelRect.y() > 0 ) {
00183         QRect r( 0, 0,
00184                  zoomHandler->layoutUnitToPixelX( parag->document()->x() + parag->document()->width() ),
00185                  pixelRect.y() );
00186         r &= crect;
00187         if ( !r.isEmpty() ) {
00188 #ifdef DEBUG_PAINTING
00189             kdDebug(32500) << " drawWYSIWYG: space above first parag: " << r << " (pixels)" << endl;
00190             p->fillRect( r, cg.brush( QColorGroup::Base ) );
00191 #endif
00192         }
00193     }
00194 
00195     while ( parag ) {
00196     lastFormatted = parag;
00197     if ( !parag->isValid() )
00198         parag->format();
00199 
00200     QRect ir = parag->pixelRect( zoomHandler );
00201 #ifdef DEBUG_PAINTING
00202         kdDebug(32500) << " drawWYSIWYG: ir=" << ir << endl;
00203 #endif
00204     if ( isPageBreakEnabled() && parag->next() )
00205         {
00206             int nexty = parag->next()->pixelRect(zoomHandler).y();
00207             // Test ir.y+ir.height, which is the first pixel _under_ the parag
00208             // (as opposed ir.bottom() which is the last pixel of the parag)
00209         if ( ir.y() + ir.height() < nexty ) {
00210         QRect r( 0, ir.y() + ir.height(),
00211              zoomHandler->layoutUnitToPixelX( parag->document()->x() + parag->document()->width() ),
00212              nexty - ( ir.y() + ir.height() ) );
00213         r &= crect;
00214         if ( !r.isEmpty() )
00215                 {
00216 #ifdef DEBUG_PAINTING
00217                     kdDebug(32500) << " drawWYSIWYG: space between parag " << parag->paragId() << " and " << parag->next()->paragId() << " : " << r << " (pixels)" << endl;
00218 #endif
00219             p->fillRect( r, cg.brush( QColorGroup::Base ) );
00220                 }
00221         }
00222         }
00223     if ( !ir.intersects( crect ) ) {
00224             // Paragraph is not in the crect - but let's check if the area on its right is.
00225         ir.setWidth( zoomHandler->layoutUnitToPixelX( parag->document()->width() ) );
00226         if ( ir.intersects( crect ) )
00227         p->fillRect( ir.intersect( crect ), cg.brush( QColorGroup::Base ) );
00228         if ( ir.y() > cy + ch ) {
00229         //tmpCursor = 0;
00230                 goto floating;
00231         }
00232     }
00233         else if ( parag->hasChanged() || !onlyChanged ) {
00234             // lineChanged() only makes sense if we're drawing with onlyChanged=true
00235             // otherwise, call setChanged() to make sure we'll paint it all (lineChanged=-1).
00236             // (this avoids having to send onlyChanged to drawParagWYSIWYG)
00237             if ( !onlyChanged && parag->lineChanged() > 0 )
00238                 parag->setChanged( false );
00239             drawParagWYSIWYG( p, parag, cx, cy, cw, ch, doubleBuffer, cg,
00240                               zoomHandler, drawCursor, cursor, resetChanged, drawingFlags );
00241         }
00242 
00243     parag = parag->next();
00244     }
00245 
00246     parag = lastParag();
00247 
00248 floating:
00249     pixelRect = parag->pixelRect(zoomHandler);
00250     int docheight = zoomHandler->layoutUnitToPixelY( parag->document()->height() );
00251     if ( pixelRect.y() + pixelRect.height() < docheight ) {
00252         int docwidth = zoomHandler->layoutUnitToPixelX( parag->document()->width() );
00253     p->fillRect( 0, pixelRect.y() + pixelRect.height(),
00254                      docwidth, docheight - ( pixelRect.y() + pixelRect.height() ),
00255              cg.brush( QColorGroup::Base ) );
00256     if ( !flow()->isEmpty() ) {
00257         QRect cr( cx, cy, cw, ch );
00258         cr = cr.intersect( QRect( 0, pixelRect.y() + pixelRect.height(), docwidth,
00259                       docheight - ( pixelRect.y() + pixelRect.height() ) ) );
00260         flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg, FALSE );
00261     }
00262     }
00263 
00264     if ( buf_pixmap && buf_pixmap->height() > 300 ) {
00265     delete buf_pixmap;
00266     buf_pixmap = 0;
00267     }
00268 
00269     //tmpCursor = 0;
00270     return lastFormatted;
00271 }
00272 
00273 void KoTextDocument::drawWithoutDoubleBuffer( QPainter *p, const QRect &cr, const QColorGroup &cg,
00274                                               KoZoomHandler* zoomHandler, const QBrush *paper )
00275 {
00276     if ( !firstParag() )
00277     return;
00278 
00279     Q_ASSERT( (m_drawingFlags & DrawSelections) == 0 );
00280     if (m_drawingFlags & DrawSelections)
00281            kdDebug() << kdBacktrace();
00282     if ( paper ) {
00283     p->setBrushOrigin( -(int)p->translationX(),
00284                -(int)p->translationY() );
00285     p->fillRect( cr, *paper );
00286     }
00287 
00288     KoTextParag *parag = firstParag();
00289     while ( parag ) {
00290     if ( !parag->isValid() )
00291         parag->format();
00292 
00293     QRect pr( parag->pixelRect( zoomHandler ) );
00294         pr.setLeft( 0 );
00295         pr.setWidth( QWIDGETSIZE_MAX );
00296         // The cliprect is checked in layout units, in KoTextParag::paint
00297         QRect crect_lu( parag->rect() );
00298 
00299     if ( !cr.isNull() && !cr.intersects( pr ) ) {
00300         parag = parag->next();
00301         continue;
00302     }
00303     p->translate( 0, pr.y() );
00304         QBrush brush = /*parag->backgroundColor() ? *parag->backgroundColor() :*/
00305             cg.brush( QColorGroup::Base );
00306         if ( brush != Qt::NoBrush )
00307         p->fillRect( QRect( 0, 0, pr.width(), pr.height() ), brush );
00308         //p->setBrushOrigin( p->brushOrigin() + QPoint( 0, pr.y() ) );
00309     parag->paint( *p, cg, 0, FALSE,
00310                       crect_lu.x(), crect_lu.y(), crect_lu.width(), crect_lu.height() );
00311     p->translate( 0, -pr.y() );
00312         //p->setBrushOrigin( p->brushOrigin() - QPoint( 0, pr.y() ) );
00313     parag = parag->next();
00314     }
00315 }
00316 
00317 // Called by drawWYSIWYG and the app's drawCursor
00318 void KoTextDocument::drawParagWYSIWYG( QPainter *p, KoTextParag *parag, int cx, int cy, int cw, int ch,
00319                                        QPixmap *&doubleBuffer, const QColorGroup &cg,
00320                                        KoZoomHandler* zoomHandler, bool drawCursor,
00321                                        KoTextCursor *cursor, bool resetChanged, uint drawingFlags )
00322 {
00323     if ((cw == 0) || (ch == 0)) return;
00324 
00325 #ifdef DEBUG_PAINTING
00326     kdDebug(32500) << "KoTextDocument::drawParagWYSIWYG " << (void*)parag << " id:" << parag->paragId() << endl;
00327 #endif
00328     m_drawingFlags = drawingFlags;
00329     QPainter *painter = 0;
00330     // Those three rects are in pixels, in the document coordinates (0,0 == topleft of first parag)
00331     QRect rect = parag->pixelRect( zoomHandler ); // the parag rect
00332 
00333     int offsetY = 0;
00334     // Start painting from a given line number.
00335     if ( parag->lineChanged() > -1 )
00336     {
00337         offsetY = zoomHandler->layoutUnitToPixelY( parag->lineY( parag->lineChanged() ) - parag->topMargin() );
00338 #ifdef DEBUG_PAINTING
00339         kdDebug(32500) << " Repainting from lineChanged=" << parag->lineChanged() << " -> adding " << offsetY << " to rect" << endl;
00340 #endif
00341         // Skip the lines that are not repainted by moving Top. The bottom doesn't change.
00342         rect.rTop() += offsetY;
00343     }
00344 
00345     QRect crect( cx, cy, cw, ch ); // the overall crect
00346     QRect ir( rect ); // will be the rect to be repainted
00347     QBrush brush = /*parag->backgroundColor() ? *parag->backgroundColor() :*/
00348         cg.brush( QColorGroup::Base );
00349     // No need to brush plain white on a printer. Brush all other cases (except "full transparent" case).
00350     bool needBrush = brush.style() != Qt::NoBrush &&
00351                      !(brush.style() == Qt::SolidPattern && brush.color() == Qt::white && is_printer(p));
00352 
00353     bool useDoubleBuffer = !parag->document()->parent();
00354     if ( is_printer(p) )
00355     useDoubleBuffer = FALSE;
00356     // Can't handle transparency using double-buffering, in case of rotation/scaling (due to bitBlt)
00357     // The test on mat is almost like isIdentity(), but allows for translation.
00359     // of being white.
00360     QWMatrix mat = p->worldMatrix();
00361     if ( ( mat.m11() != 1.0 || mat.m22() != 1.0 || mat.m12() != 0.0 || mat.m21() != 0.0 )
00362          && brush.style() != Qt::SolidPattern )
00363         useDoubleBuffer = FALSE;
00364 
00365 #ifdef DEBUG_PAINTING
00366     kdDebug(32500) << "KoTextDocument::drawParagWYSIWYG parag->rect=" << parag->rect()
00367                    << " pixelRect(ir)=" << ir
00368                    << " crect (pixels)=" << crect
00369                    << " useDoubleBuffer=" << useDoubleBuffer << endl;
00370 #endif
00371 
00372     if ( useDoubleBuffer  ) {
00373     painter = new QPainter;
00374     if ( cx >= 0 && cy >= 0 )
00375         ir = ir.intersect( crect );
00376     if ( !doubleBuffer ||
00377          ir.width() > doubleBuffer->width() ||
00378          ir.height() > doubleBuffer->height() )
00379         {
00380         doubleBuffer = bufferPixmap( ir.size() );
00381         }
00382         painter->begin( doubleBuffer );
00383 
00384     } else {
00385         p->save();
00386     painter = p;
00387     painter->translate( ir.x(), ir.y() );
00388     }
00389     // Until the next translate(), (0,0) in the painter will be at ir.topLeft() in reality
00390     //kdDebug() << "KoTextDocument::drawParagWYSIWYG ir=" << ir << endl;
00391 
00392 
00393     // Cumulate ir.x(), ir.y() with the current brush origin
00394     //painter->setBrushOrigin( painter->brushOrigin() + ir.topLeft() );
00395 
00396     if ( useDoubleBuffer || is_printer( painter ) ) {
00397         // Transparent -> grab background from p's device
00398         if ( brush.style() != Qt::SolidPattern ) {
00399             bitBlt( doubleBuffer, 0, 0, p->device(),
00400                     ir.x() + (int)p->translationX(), ir.y() + (int)p->translationY(),
00401                     ir.width(), ir.height() );
00402         }
00403     }
00404     if ( needBrush )
00405         painter->fillRect( QRect( 0, 0, ir.width(), ir.height() ), brush );
00406 
00407     // Now revert the previous painter translation, and instead make (0,0) the topleft of the PARAGRAPH
00408     painter->translate( rect.x() - ir.x(), rect.y() - ir.y() );
00409 #ifdef DEBUG_PAINTING
00410     kdDebug(32500) << "KoTextDocument::drawParagWYSIWYG translate " << rect.x() - ir.x() << "," << rect.y() - ir.y() << endl;
00411 #endif
00412     //painter->setBrushOrigin( painter->brushOrigin() + rect.topLeft() - ir.topLeft() );
00413 
00414     // The cliprect is checked in layout units, in KoTextParag::paint
00415     QRect crect_lu( zoomHandler->pixelToLayoutUnit( crect ) );
00416 #ifdef DEBUG_PAINTING
00417     kdDebug(32500) << "KoTextDocument::drawParagWYSIWYG crect_lu=" << crect_lu << endl;
00418 #endif
00419 
00420     // paintDefault will paint line 'lineChanged' at its normal Y position.
00421     // But the buffer-pixmap below starts at Y. We need to translate by -Y
00422     // so that the painting happens at the right place.
00423     painter->translate( 0, -offsetY );
00424 
00425     parag->paint( *painter, cg, drawCursor ? cursor : 0, (m_drawingFlags & DrawSelections),
00426                   crect_lu.x(), crect_lu.y(), crect_lu.width(), crect_lu.height() );
00427 
00428 
00429     if ( useDoubleBuffer ) {
00430     delete painter;
00431     painter = 0;
00432     p->drawPixmap( ir.topLeft(), *doubleBuffer, QRect( QPoint( 0, 0 ), ir.size() ) );
00433 #if 0 // for debug!
00434         p->save();
00435         p->setPen( Qt::blue );
00436         p->drawRect( ir.x(), ir.y(), ir.width(), ir.height() );
00437         p->restore();
00438 #endif
00439     } else {
00440         // undo previous translations, painter is 'p', i.e. will be used later on
00441         p->restore();
00442     //painter->translate( -ir.x(), -ir.y() );
00443         //painter->translate( 0, +offsetY );
00444         //painter->setBrushOrigin( painter->brushOrigin() - ir.topLeft() );
00445     }
00446 
00447     if ( needBrush ) {
00448         int docright = zoomHandler->layoutUnitToPixelX( parag->document()->x() + parag->document()->width() );
00449 #ifdef DEBUG_PAINTING
00450 //        kdDebug(32500) << "KoTextDocument::drawParagWYSIWYG my rect is: " << rect << endl;
00451 #endif
00452         if ( rect.x() + rect.width() < docright ) {
00453 #ifdef DEBUG_PAINTING
00454             kdDebug(32500) << "KoTextDocument::drawParagWYSIWYG rect doesn't go up to docright=" << docright << endl;
00455 #endif
00456             p->fillRect( rect.x() + rect.width(), rect.y(),
00457                          docright - ( rect.x() + rect.width() ),
00458                          rect.height(), cg.brush( QColorGroup::Base ) );
00459         }
00460     }
00461 
00462     if ( resetChanged )
00463     parag->setChanged( FALSE );
00464 }
00465 
00466 
00467 KoTextDocCommand *KoTextDocument::deleteTextCommand( KoTextDocument *textdoc, int id, int index, const QMemArray<KoTextStringChar> & str, const CustomItemsMap & customItemsMap, const QValueList<KoParagLayout> & oldParagLayouts )
00468 {
00469     return new KoTextDeleteCommand( textdoc, id, index, str, customItemsMap, oldParagLayouts );
00470 }
00471 
00472 #include "kotextdocument.moc"
KDE Logo
This file is part of the documentation for lib Library Version 1.3.5.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Mar 11 11:47:44 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003