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 Wed Nov 17 06:54:18 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003