20#include "KoTextDocument.h"
21#include "KoTextParag.h"
22#include "KoTextZoomHandler.h"
23#include "KoTextFormatter.h"
24#include "KoTextFormat.h"
25#include "KoParagCounter.h"
26#include "KoTextCommand.h"
27#include "KoOasisContext.h"
28#include "KoVariable.h"
29#include <KoXmlWriter.h>
33#include <tdeversion.h>
34#include <tqapplication.h>
42KoTextDocument::KoTextDocument(
KoTextZoomHandler *zoomHandler, KoTextFormatCollection *fc,
44 : m_zoomHandler( zoomHandler ),
45 m_bDestroying( false ),
46#ifdef TQTEXTTABLE_AVAILABLE
50 tArray( 0 ), tStopWidth( 0 )
58 setFormatter( formatter );
65 if ( !createInitialParag )
69void KoTextDocument::init()
75 m_pageBreakEnabled =
false;
77 align = TQt::AlignAuto;
88 withoutDoubleBuffer =
false;
90 lParag = fParag = createParag(
this, 0, 0 );
98 flow_ =
new KoTextFlow;
104 selectionColors[ Standard ] = TQApplication::palette().color( TQPalette::Active, TQColorGroup::Highlight );
105 selectionText[ Standard ] =
true;
106 assert( Standard < nSelections );
107 selectionText[ InputMethodPreedit ] =
false;
108 assert( InputMethodPreedit < nSelections );
109 commandHistory =
new KoTextDocCommandHistory( 100 );
110 tStopWidth = formatCollection()->defaultFormat()->width(
'x' ) * 8;
113KoTextDocument::~KoTextDocument()
118 m_bDestroying =
true;
121 delete commandHistory;
133void KoTextDocument::clear(
bool createEmptyParag )
138 KoTextParag *p = fParag->next();
139 fParag->string()->clear();
144 if ( createEmptyParag )
145 fParag = lParag = createParag(
this );
170int KoTextDocument::height()
const
174 h = lParag->rect().top() + lParag->rect().height() + 1;
181KoTextParag *KoTextDocument::createParag( KoTextDocument *d, KoTextParag *pr, KoTextParag *nx,
bool updateIds )
183 return new KoTextParag( d, pr, nx, updateIds );
186void KoTextDocument::setPlainText(
const TQString &text )
194 int nl = text.find(
'\n' );
196 lParag = createParag(
this, lParag, 0 );
200 if ( !s.isEmpty() ) {
201 if ( s[ (
int)s.length() - 1 ] ==
'\r' )
202 s.remove( s.length() - 1, 1 );
207 lParag = createParag(
this, lParag, 0 );
210 TQString s = text.mid( lastNl, nl - lastNl );
211 if ( !s.isEmpty() ) {
212 if ( s[ (
int)s.length() - 1 ] ==
'\r' )
213 s.remove( s.length() - 1, 1 );
216 if ( nl == 0xffffff )
219 nl = text.find(
'\n', nl + 1 );
225 lParag = fParag = createParag(
this, 0, 0 );
228void KoTextDocument::setText(
const TQString &text,
const TQString & )
231 setPlainText( text );
234TQString KoTextDocument::plainText()
const
238 KoTextParag *p = fParag;
240 s = p->string()->toString();
241 s.remove( s.length() - 1, 1 );
250void KoTextDocument::invalidate()
252 KoTextParag *s = fParag;
259void KoTextDocument::informParagraphDeleted( KoTextParag* parag )
261 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.begin();
262 for ( ; it != selections.end(); ++it )
264 if ( (*it).startCursor.parag() == parag ) {
265 if ( parag->prev() ) {
266 KoTextParag* prevP = parag->prev();
267 (*it).startCursor.setParag( prevP );
268 (*it).startCursor.setIndex( prevP->length()-1 );
270 (*it).startCursor.setParag( parag->next() );
272 if ( (*it).endCursor.parag() == parag ) {
273 if ( parag->prev() ) {
274 KoTextParag* prevP = parag->prev();
275 (*it).endCursor.setParag( prevP );
276 (*it).endCursor.setIndex( prevP->length()-1 );
278 (*it).endCursor.setParag( parag->next() );
281 emit paragraphDeleted( parag );
284void KoTextDocument::selectionStart(
int id,
int ¶gId,
int &index )
286 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
287 if ( it == selections.end() )
289 KoTextDocumentSelection &sel = *it;
290 paragId = !sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
291 index = !sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
294KoTextCursor KoTextDocument::selectionStartCursor(
int id)
296 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
297 if ( it == selections.end() )
298 return KoTextCursor(
this );
299 KoTextDocumentSelection &sel = *it;
301 return sel.endCursor;
302 return sel.startCursor;
305KoTextCursor KoTextDocument::selectionEndCursor(
int id)
307 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
308 if ( it == selections.end() )
309 return KoTextCursor(
this );
310 KoTextDocumentSelection &sel = *it;
312 return sel.endCursor;
313 return sel.startCursor;
316void KoTextDocument::selectionEnd(
int id,
int ¶gId,
int &index )
318 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
319 if ( it == selections.end() )
321 KoTextDocumentSelection &sel = *it;
322 paragId = sel.swapped ? sel.startCursor.parag()->paragId() : sel.endCursor.parag()->paragId();
323 index = sel.swapped ? sel.startCursor.index() : sel.endCursor.index();
326bool KoTextDocument::isSelectionSwapped(
int id )
328 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
329 if ( it == selections.end() )
331 KoTextDocumentSelection &sel = *it;
335KoTextParag *KoTextDocument::selectionStart(
int id )
337 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
338 if ( it == selections.end() )
340 KoTextDocumentSelection &sel = *it;
341 if ( sel.startCursor.parag()->paragId() < sel.endCursor.parag()->paragId() )
342 return sel.startCursor.parag();
343 return sel.endCursor.parag();
346KoTextParag *KoTextDocument::selectionEnd(
int id )
348 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
349 if ( it == selections.end() )
351 KoTextDocumentSelection &sel = *it;
352 if ( sel.startCursor.parag()->paragId() > sel.endCursor.parag()->paragId() )
353 return sel.startCursor.parag();
354 return sel.endCursor.parag();
357void KoTextDocument::addSelection(
int id )
359 nSelections = TQMAX( nSelections,
id + 1 );
362static void setSelectionEndHelper(
int id, KoTextDocumentSelection &sel, KoTextCursor &start, KoTextCursor &end )
364 KoTextCursor c1 = start;
365 KoTextCursor c2 = end;
371 c1.parag()->removeSelection(
id );
372 c2.parag()->removeSelection(
id );
373 if ( c1.parag() != c2.parag() ) {
374 c1.parag()->setSelection(
id, c1.index(), c1.parag()->length() - 1 );
375 c2.parag()->setSelection(
id, 0, c2.index() );
377 c1.parag()->setSelection(
id, TQMIN( c1.index(), c2.index() ), TQMAX( c1.index(), c2.index() ) );
380 sel.startCursor = start;
382 if ( sel.startCursor.parag() == sel.endCursor.parag() )
383 sel.swapped = sel.startCursor.index() > sel.endCursor.index();
386bool KoTextDocument::setSelectionEnd(
int id, KoTextCursor *cursor )
388 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
389 if ( it == selections.end() )
391 KoTextDocumentSelection &sel = *it;
393 KoTextCursor start = sel.startCursor;
394 KoTextCursor end = *cursor;
396 if ( start == end ) {
397 removeSelection(
id );
398 setSelectionStart(
id, cursor );
402 if ( sel.endCursor.parag() == end.parag() ) {
403 setSelectionEndHelper(
id, sel, start, end );
407 bool inSelection =
false;
408 KoTextCursor c(
this );
409 KoTextCursor tmp = sel.startCursor;
412 KoTextCursor tmp2 = *cursor;
413 c.setParag( tmp.parag()->paragId() < tmp2.parag()->paragId() ? tmp.parag() : tmp2.parag() );
415 bool hadStart =
false;
417 bool hadStartParag =
false;
418 bool hadEndParag =
false;
419 bool hadOldStart =
false;
420 bool hadOldEnd =
false;
421 bool leftSelection =
false;
428 if ( c.parag() == start.parag() )
429 hadStartParag =
true;
430 if ( c.parag() == end.parag() )
432 if ( c == sel.startCursor )
434 if ( c == sel.endCursor )
438 ( hadEnd && !hadStart ||
439 hadEnd && hadStart && start.parag() == end.parag() && start.index() > end.index() ) )
442 if ( c == end && hadStartParag ||
443 c == start && hadEndParag ) {
444 KoTextCursor tmp = c;
445 if ( tmp.parag() != c.parag() ) {
446 int sstart = tmp.parag()->selectionStart(
id );
447 tmp.parag()->removeSelection(
id );
448 tmp.parag()->setSelection(
id, sstart, tmp.index() );
453 ( c == end && hadStart || c == start && hadEnd ) )
454 leftSelection =
true;
455 else if ( !leftSelection && !inSelection && ( hadStart || hadEnd ) )
458 bool noSelectionAnymore = hadOldStart && hadOldEnd && leftSelection && !inSelection && !c.parag()->hasSelection(
id ) && c.atParagEnd();
459 c.parag()->removeSelection(
id );
461 if ( c.parag() == start.parag() && start.parag() == end.parag() ) {
462 c.parag()->setSelection(
id, TQMIN( start.index(), end.index() ), TQMAX( start.index(), end.index() ) );
463 }
else if ( c.parag() == start.parag() && !hadEndParag ) {
464 c.parag()->setSelection(
id, start.index(), c.parag()->length() - 1 );
465 }
else if ( c.parag() == end.parag() && !hadStartParag ) {
466 c.parag()->setSelection(
id, end.index(), c.parag()->length() - 1 );
467 }
else if ( c.parag() == end.parag() && hadEndParag ) {
468 c.parag()->setSelection(
id, 0, end.index() );
469 }
else if ( c.parag() == start.parag() && hadStartParag ) {
470 c.parag()->setSelection(
id, 0, start.index() );
472 c.parag()->setSelection(
id, 0, c.parag()->length() - 1 );
481 if ( old == c || noSelectionAnymore )
486 sel.startCursor.parag()->setSelection(
id, sel.startCursor.index(), sel.startCursor.parag()->length() - 1 );
488 sel.startCursor = start;
490 if ( sel.startCursor.parag() == sel.endCursor.parag() )
491 sel.swapped = sel.startCursor.index() > sel.endCursor.index();
493 setSelectionEndHelper(
id, sel, start, end );
498void KoTextDocument::selectAll(
int id )
500 removeSelection(
id );
502 KoTextDocumentSelection sel;
504 KoTextCursor c(
this );
506 c.setParag( fParag );
510 c.setParag( lParag );
511 c.setIndex( lParag->length() - 1 );
514 KoTextParag *p = fParag;
516 p->setSelection(
id, 0, p->length() - 1 );
517#ifdef TQTEXTTABLE_AVAILABLE
518 for (
int i = 0; i < (int)p->length(); ++i ) {
519 if ( p->at( i )->isCustom() && p->at( i )->customItem()->isNested() ) {
520 KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
521 TQPtrList<KoTextTableCell> tableCells = t->tableCells();
522 for ( KoTextTableCell *c = tableCells.first(); c; c = tableCells.next() )
523 c->richText()->selectAll(
id );
530 selections.insert(
id, sel );
533bool KoTextDocument::removeSelection(
int id )
535 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
536 if ( it == selections.end() )
539 KoTextDocumentSelection &sel = *it;
541 KoTextCursor c(
this );
542 KoTextCursor tmp = sel.startCursor;
545 c.setParag( tmp.parag() );
547 bool hadStart =
false;
549 KoTextParag *lastParag = 0;
550 bool leftSelection =
false;
551 bool inSelection =
false;
554 if ( !hadStart && c.parag() == sel.startCursor.parag() )
556 if ( !hadEnd && c.parag() == sel.endCursor.parag() )
559 if ( !leftSelection && !inSelection && ( c.parag() == sel.startCursor.parag() || c.parag() == sel.endCursor.parag() ) )
563 ( c == sel.endCursor && hadStart || c == sel.startCursor && hadEnd ) ) {
564 leftSelection =
true;
568 bool noSelectionAnymore = leftSelection && !inSelection && !c.parag()->hasSelection(
id ) && c.atParagEnd();
570 if ( lastParag != c.parag() )
571 c.parag()->removeSelection(
id );
574 lastParag = c.parag();
576 if ( old == c || noSelectionAnymore )
580 selections.remove(
id );
584TQString KoTextDocument::selectedText(
int id,
bool withCustom )
const
587 TQMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find(
id );
588 if ( it == selections.end() )
591 KoTextDocumentSelection sel = *it;
594 KoTextCursor c1 = sel.startCursor;
595 KoTextCursor c2 = sel.endCursor;
597 c2 = sel.startCursor;
601 if ( c1.parag() == c2.parag() ) {
603 KoTextParag *p = c1.parag();
604 int end = c2.index();
605 if ( p->at( TQMAX( 0, end - 1 ) )->isCustom() )
607 if ( !withCustom || !p->customItems() ) {
608 s += p->string()->toString().mid( c1.index(), end - c1.index() );
610 for (
int i = c1.index(); i < end; ++i ) {
611 if ( p->at( i )->isCustom() ) {
612#ifdef TQTEXTTABLE_AVAILABLE
613 if ( p->at( i )->customItem()->isNested() ) {
615 KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
616 TQPtrList<KoTextTableCell> cells = t->tableCells();
617 for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
618 s += c->richText()->plainText() +
"\n";
632 KoTextParag *p = c1.parag();
633 int start = c1.index();
635 int end = p == c2.parag() ? c2.index() : p->length() - 1;
636 if ( p == c2.parag() && p->at( TQMAX( 0, end - 1 ) )->isCustom() )
638 if ( !withCustom || !p->customItems() ) {
639 s += p->string()->toString().mid( start, end - start );
640 if ( p != c2.parag() )
643 for (
int i = start; i < end; ++i ) {
644 if ( p->at( i )->isCustom() ) {
645#ifdef TQTEXTTABLE_AVAILABLE
646 if ( p->at( i )->customItem()->isNested() ) {
648 KoTextTable *t = (KoTextTable*)p->at( i )->customItem();
649 TQPtrList<KoTextTableCell> cells = t->tableCells();
650 for ( KoTextTableCell *c = cells.first(); c; c = cells.next() )
651 s += c->richText()->plainText() +
"\n";
662 if ( p == c2.parag() )
671 KoTextCursor c1 = selectionStartCursor( selectionId );
672 KoTextCursor c2 = selectionEndCursor( selectionId );
674 if ( c1.parag() == c2.parag() )
676 text = c1.parag()->toString( c1.index(), c2.index() - c1.index() );
678 c1.parag()->saveOasis( writer, context, c1.index(), c2.index()-1,
true );
682 text += c1.parag()->toString( c1.index() ) +
"\n";
684 c1.parag()->saveOasis( writer, context, c1.index(), c1.parag()->length()-2,
true );
685 KoTextParag *p = c1.parag()->next();
686 while ( p && p != c2.parag() ) {
687 text += p->toString() +
"\n";
688 p->saveOasis( writer, context, 0, p->length()-2,
true );
691 text += c2.parag()->toString( 0, c2.index() );
692 c2.parag()->saveOasis( writer, context, 0, c2.index() - 1,
true );
697void KoTextDocument::setFormat(
int id,
const KoTextFormat *f,
int flags )
699 TQMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find(
id );
700 if ( it == selections.end() )
703 KoTextDocumentSelection sel = *it;
705 KoTextCursor c1 = sel.startCursor;
706 KoTextCursor c2 = sel.endCursor;
708 c2 = sel.startCursor;
712 if ( c1.parag() == c2.parag() ) {
713 c1.parag()->setFormat( c1.index(), c2.index() - c1.index(), f,
true, flags );
717 c1.parag()->setFormat( c1.index(), c1.parag()->length() - c1.index(), f,
true, flags );
718 KoTextParag *p = c1.parag()->next();
719 while ( p && p != c2.parag() ) {
720 p->setFormat( 0, p->length(), f,
true, flags );
723 c2.parag()->setFormat( 0, c2.index(), f,
true, flags );
736void KoTextDocument::removeSelectedText(
int id, KoTextCursor *cursor )
738 TQMap<int, KoTextDocumentSelection>::Iterator it = selections.find(
id );
739 if ( it == selections.end() )
742 KoTextDocumentSelection sel = *it;
744 KoTextCursor c1 = sel.startCursor;
745 KoTextCursor c2 = sel.endCursor;
747 c2 = sel.startCursor;
752 removeSelection(
id );
754 if ( c1.parag() == c2.parag() ) {
755 c1.parag()->remove( c1.index(), c2.index() - c1.index() );
761 if ( c1.parag() == fParag && c1.index() == 0 &&
762 c2.parag() == lParag && c2.index() == lParag->length() - 1 )
765 bool didGoLeft =
false;
766 if ( c1.index() == 0 && c1.parag() != fParag ) {
767 cursor->gotoPreviousLetter();
772 c1.parag()->remove( c1.index(), c1.parag()->length() - 1 - c1.index() );
773 KoTextParag *p = c1.parag()->next();
776 while ( p && p != c2.parag() ) {
778 dy -= p->rect().height();
783 c2.parag()->remove( 0, c2.index() );
787 if ( p->paragLayout().counter )
788 p->paragLayout().counter->invalidate();
795 c1.parag()->join( c2.parag() );
798 cursor->gotoNextLetter();
801void KoTextDocument::addCommand( KoTextDocCommand *cmd )
803 commandHistory->addCommand( cmd );
806KoTextCursor *KoTextDocument::undo( KoTextCursor *c )
808 return commandHistory->undo( c );
811KoTextCursor *KoTextDocument::redo( KoTextCursor *c )
813 return commandHistory->redo( c );
816bool KoTextDocument::find(
const TQString &expr,
bool cs,
bool wo,
bool forward,
817 int *parag,
int *index, KoTextCursor *cursor )
819 KoTextParag *p =
forward ? fParag : lParag;
821 p = paragAt( *parag );
827 TQString s = p->string()->toString();
828 s.remove( s.length() - 1, 1 );
829 int start =
forward ? 0 : s.length() - 1;
830 if ( first && index )
833 start = cursor->index();
834 if ( !forward && first ) {
835 start -= expr.length() + 1;
845 int res =
forward ? s.find( expr, start, cs ) : s.findRev( expr, start, cs );
851 int end = res + expr.length();
852 if ( ( res == 0 || s[ res - 1 ].isSpace() || s[ res - 1 ].isPunct() ) &&
853 ( end == (
int)s.length() || s[ end ].isSpace() || s[ end ].isPunct() ) )
859 cursor->setParag( p );
860 cursor->setIndex( res );
861 setSelectionStart( Standard, cursor );
862 cursor->setIndex( res + expr.length() );
863 setSelectionEnd( Standard, cursor );
865 *parag = p->paragId();
878 p =
forward ? p->next() : p->prev();
884bool KoTextDocument::inSelection(
int selId,
const TQPoint &pos )
const
886 TQMap<int, KoTextDocumentSelection>::ConstIterator it = selections.find( selId );
887 if ( it == selections.end() )
890 KoTextDocumentSelection sel = *it;
891 KoTextParag *startParag = sel.startCursor.parag();
892 KoTextParag *endParag = sel.endCursor.parag();
893 if ( sel.startCursor.parag() == sel.endCursor.parag() &&
894 sel.startCursor.parag()->selectionStart( selId ) == sel.endCursor.parag()->selectionEnd( selId ) )
896 if ( sel.endCursor.parag()->paragId() < sel.startCursor.parag()->paragId() ) {
897 endParag = sel.startCursor.parag();
898 startParag = sel.endCursor.parag();
901 KoTextParag *p = startParag;
903 if ( p->rect().contains( pos ) ) {
905 int selStart = p->selectionStart( selId );
906 int selEnd = p->selectionEnd( selId );
909 for (
int i = 0; i < p->length(); ++i ) {
914 if ( p->at( i )->lineStart ) {
915 y = (*p->lineStarts.find( i ))->y;
916 h = (*p->lineStarts.find( i ))->h;
918 if ( pos.y() - p->rect().y() >= y && pos.y() - p->rect().y() <= y + h ) {
919 if ( inSel && pos.x() >= p->at( i )->x &&
920 pos.x() <= p->at( i )->x + p->at( i )->width )
925 if ( pos.y() < p->rect().y() )
935TQPixmap *KoTextDocument::bufferPixmap(
const TQSize &s )
938 int w = TQABS( s.width() );
939 int h = TQABS( s.height() );
940 buf_pixmap =
new TQPixmap( w, h );
942 if ( buf_pixmap->width() < s.width() ||
943 buf_pixmap->height() < s.height() ) {
944 buf_pixmap->resize( TQMAX( s.width(), buf_pixmap->width() ),
945 TQMAX( s.height(), buf_pixmap->height() ) );
952void KoTextDocument::registerCustomItem(
KoTextCustomItem *i, KoTextParag *p )
954 if ( i && i->placement() != KoTextCustomItem::PlaceInline )
955 flow_->registerFloatingItem( i );
956 p->registerFloatingItem( i );
957 i->setParagraph( p );
959 customItems.append( i );
962void KoTextDocument::unregisterCustomItem(
KoTextCustomItem *i, KoTextParag *p )
964 flow_->unregisterFloatingItem( i );
965 p->unregisterFloatingItem( i );
966 i->setParagraph( 0 );
967 customItems.removeRef( i );
970int KoTextDocument::length()
const
973 KoTextParag *p = fParag;
975 l += p->length() - 1;
981bool KoTextDocument::visitSelection(
int selectionId,
KoParagVisitor* visitor,
bool forward )
983 KoTextCursor c1 = selectionStartCursor( selectionId );
984 KoTextCursor c2 = selectionEndCursor( selectionId );
987 return visitFromTo( c1.parag(), c1.index(), c2.parag(), c2.index(), visitor, forward );
990bool KoTextDocument::hasSelection(
int id,
bool visible )
const
992 return ( selections.find(
id ) != selections.end() &&
994 ( (KoTextDocument*)
this )->selectionStartCursor(
id ) !=
995 ( (KoTextDocument*)
this )->selectionEndCursor(
id ) ) );
998void KoTextDocument::setSelectionStart(
int id, KoTextCursor *cursor )
1000 KoTextDocumentSelection sel;
1001 sel.startCursor = *cursor;
1002 sel.endCursor = *cursor;
1003 sel.swapped =
false;
1004 selections[ id ] = sel;
1007KoTextParag *KoTextDocument::paragAt(
int i )
const
1009 KoTextParag *s = fParag;
1011 if ( s->paragId() == i )
1018bool KoTextDocument::visitDocument(
KoParagVisitor *visitor,
bool forward )
1020 return visitFromTo( firstParag(), 0, lastParag(), lastParag()->length()-1, visitor, forward );
1023bool KoTextDocument::visitFromTo( KoTextParag *firstParag,
int firstIndex, KoTextParag* lastParag,
int lastIndex,
KoParagVisitor* visitor,
bool forw )
1025 if ( firstParag == lastParag )
1027 return visitor->
visit( firstParag, firstIndex, lastIndex );
1035 ret = visitor->
visit( firstParag, firstIndex, firstParag->length() - 1 );
1036 if (!ret)
return false;
1040 ret = visitor->
visit( lastParag, 0, lastIndex );
1041 if (!ret)
return false;
1044 KoTextParag* currentParag = forw ? firstParag->next() : lastParag->prev();
1045 KoTextParag * endParag = forw ? lastParag : firstParag;
1046 while ( currentParag && currentParag != endParag )
1048 ret = visitor->
visit( currentParag, 0, currentParag->length() - 1 );
1049 if (!ret)
return false;
1050 currentParag = forw ? currentParag->next() : currentParag->prev();
1052 Q_ASSERT( currentParag );
1053 Q_ASSERT( endParag == currentParag );
1055 ret = visitor->
visit( lastParag, 0, lastIndex );
1057 ret = visitor->
visit( currentParag, firstIndex, currentParag->length() - 1 );
1062static bool is_printer( TQPainter *p )
1064 return p && p->device() && p->device()->devType() == TQInternal::Printer;
1067KoTextParag *KoTextDocument::drawWYSIWYG( TQPainter *p,
int cx,
int cy,
int cw,
int ch,
const TQColorGroup &cg,
1069 bool drawCursor, KoTextCursor *cursor,
1070 bool resetChanged, uint drawingFlags )
1072 m_drawingFlags = drawingFlags;
1077 if ( is_printer( p ) || ( drawingFlags & TransparentBackground ) ) {
1082 TQRect crect( cx, cy, cw, ch );
1083 drawWithoutDoubleBuffer( p, crect, cg, zoomHandler );
1088 if ( !firstParag() )
1091 KoTextParag *lastFormatted = 0;
1092 KoTextParag *parag = firstParag();
1094 TQPixmap *doubleBuffer = 0;
1097 TQRect crect( cx, cy, cw, ch );
1099#ifdef DEBUG_PAINTING
1100 kdDebug(32500) <<
"\nKoTextDocument::drawWYSIWYG crect=" << crect << endl;
1104 TQRect pixelRect = parag->pixelRect( zoomHandler );
1105 if ( isPageBreakEnabled() && parag && cy <= pixelRect.y() && pixelRect.y() > 0 ) {
1107 zoomHandler->
layoutUnitToPixelX( parag->document()->x() + parag->document()->width() ),
1110 if ( !r.isEmpty() ) {
1111#ifdef DEBUG_PAINTING
1112 kdDebug(32500) <<
" drawWYSIWYG: space above first parag: " << r <<
" (pixels)" << endl;
1114 p->fillRect( r, cg.brush( TQColorGroup::Base ) );
1119 lastFormatted = parag;
1120 if ( !parag->isValid() )
1123 TQRect ir = parag->pixelRect( zoomHandler );
1124#ifdef DEBUG_PAINTING
1125 kdDebug(32500) <<
" drawWYSIWYG: ir=" << ir << endl;
1127 if ( isPageBreakEnabled() && parag->next() && ( drawingFlags & TransparentBackground ) == 0 )
1129 int nexty = parag->next()->pixelRect(zoomHandler).y();
1132 if ( ir.y() + ir.height() < nexty ) {
1133 TQRect r( 0, ir.y() + ir.height(),
1134 zoomHandler->
layoutUnitToPixelX( parag->document()->x() + parag->document()->width() ),
1135 nexty - ( ir.y() + ir.height() ) );
1139#ifdef DEBUG_PAINTING
1140 kdDebug(32500) <<
" drawWYSIWYG: space between parag " << parag->paragId() <<
" and " << parag->next()->paragId() <<
" : " << r <<
" (pixels)" << endl;
1142 p->fillRect( r, cg.brush( TQColorGroup::Base ) );
1147 if ( !ir.intersects( crect ) ) {
1150 if ( ir.intersects( crect ) && ( drawingFlags & TransparentBackground ) == 0 )
1151 p->fillRect( ir.intersect( crect ), cg.brush( TQColorGroup::Base ) );
1152 if ( ir.y() > cy + ch ) {
1156 else if ( parag->hasChanged() || !onlyChanged ) {
1160 if ( !onlyChanged && parag->lineChanged() > 0 )
1161 parag->setChanged(
false );
1162 drawParagWYSIWYG( p, parag, cx, cy, cw, ch, doubleBuffer, cg,
1163 zoomHandler, drawCursor, cursor, resetChanged, drawingFlags );
1165 parag = parag->next();
1168 parag = lastParag();
1171 pixelRect = parag->pixelRect(zoomHandler);
1172 int docheight = zoomHandler->layoutUnitToPixelY( parag->document()->height() );
1173 if ( pixelRect.y() + pixelRect.height() < docheight ) {
1175 if ( ( drawingFlags & TransparentBackground ) == 0 ) {
1176 p->fillRect( 0, pixelRect.y() + pixelRect.height(),
1177 docwidth, docheight - ( pixelRect.y() + pixelRect.height() ),
1178 cg.brush( TQColorGroup::Base ) );
1180 if ( !flow()->isEmpty() ) {
1181 TQRect cr( cx, cy, cw, ch );
1182 cr = cr.intersect( TQRect( 0, pixelRect.y() + pixelRect.height(), docwidth,
1183 docheight - ( pixelRect.y() + pixelRect.height() ) ) );
1184 flow()->drawFloatingItems( p, cr.x(), cr.y(), cr.width(), cr.height(), cg,
false );
1188 if ( buf_pixmap && buf_pixmap->height() > 300 ) {
1193 return lastFormatted;
1198void KoTextDocument::drawWithoutDoubleBuffer( TQPainter *p,
const TQRect &cr,
const TQColorGroup &cg,
1201 if ( !firstParag() )
1204 Q_ASSERT( (m_drawingFlags & DrawSelections) == 0 );
1205 if (m_drawingFlags & DrawSelections)
1206 kdDebug() << kdBacktrace();
1207 if ( paper && ( m_drawingFlags & TransparentBackground ) == 0 ) {
1208 p->setBrushOrigin( -(
int)p->translationX(), -(
int)p->translationY() );
1209 p->fillRect( cr, *paper );
1212 KoTextParag *parag = firstParag();
1214 if ( !parag->isValid() )
1217 TQRect pr( parag->pixelRect( zoomHandler ) );
1219 pr.setWidth( TQWIDGETSIZE_MAX );
1221 TQRect crect_lu( parag->rect() );
1223 if ( !cr.isNull() && !cr.intersects( pr ) ) {
1224 parag = parag->next();
1228 p->translate( 0, pr.y() );
1232 TQBrush brush = cg.brush( TQColorGroup::Base );;
1233 bool needBrush = brush.style() != TQt::NoBrush &&
1234 !(brush.style() == TQt::SolidPattern &&
1235 brush.color() == TQt::white &&
1237 if ( needBrush && ( m_drawingFlags & TransparentBackground ) == 0 )
1238 p->fillRect( TQRect( 0, 0, pr.width(), pr.height() ), brush );
1241 parag->paint( *p, cg, 0,
false,
1242 crect_lu.x(), crect_lu.y(),
1243 crect_lu.width(), crect_lu.height() );
1244 p->translate( 0, -pr.y() );
1246 parag = parag->next();
1252void KoTextDocument::drawParagWYSIWYG( TQPainter *p, KoTextParag *parag,
int cx,
int cy,
int cw,
int ch,
1253 TQPixmap *&doubleBuffer,
const TQColorGroup &cg,
1255 KoTextCursor *cursor,
bool resetChanged, uint drawingFlags )
1257 if ( cw <= 0 || ch <= 0 ) { Q_ASSERT( cw > 0 ); Q_ASSERT( ch > 0 );
return; }
1258#ifdef DEBUG_PAINTING
1259 kdDebug(32500) <<
"KoTextDocument::drawParagWYSIWYG " << (
void*)parag <<
" id:" << parag->paragId() << endl;
1261 m_drawingFlags = drawingFlags;
1262 TQPainter *painter = 0;
1264 TQRect rect = parag->pixelRect( zoomHandler );
1268 if ( parag->lineChanged() > -1 )
1270 offsetY = zoomHandler->layoutUnitToPixelY( parag->lineY( parag->lineChanged() ) - parag->topMargin() );
1271#ifdef DEBUG_PAINTING
1272 kdDebug(32500) <<
" Repainting from lineChanged=" << parag->lineChanged() <<
" -> adding " << offsetY <<
" to rect" << endl;
1275 rect.rTop() += offsetY;
1278 TQRect crect( cx, cy, cw, ch );
1281 TQBrush brush = cg.brush( TQColorGroup::Base );
1285 bool needBrush = brush.style() != TQt::NoBrush &&
1286 ( drawingFlags & TransparentBackground ) == 0 &&
1287 !(brush.style() == TQt::SolidPattern &&
1288 brush.color() == TQt::white &&
1291 bool useDoubleBuffer = !parag->document()->parent();
1292 if ( is_printer(p) )
1293 useDoubleBuffer =
false;
1298 TQWMatrix mat = p->worldMatrix();
1299 if ( ( mat.m11() != 1.0 || mat.m22() != 1.0 || mat.m12() != 0.0 || mat.m21() != 0.0 )
1300 && brush.style() != TQt::SolidPattern )
1301 useDoubleBuffer =
false;
1303#ifdef DEBUG_PAINTING
1304 kdDebug(32500) <<
"KoTextDocument::drawParagWYSIWYG parag->rect=" << parag->rect()
1305 <<
" pixelRect(ir)=" << ir
1306 <<
" crect (pixels)=" << crect
1307 <<
" useDoubleBuffer=" << useDoubleBuffer << endl;
1310 if ( useDoubleBuffer ) {
1311 painter =
new TQPainter;
1312 if ( cx >= 0 && cy >= 0 )
1313 ir = ir.intersect( crect );
1314 if ( !doubleBuffer ||
1315 ir.width() > doubleBuffer->width() ||
1316 ir.height() > doubleBuffer->height() )
1318 doubleBuffer = bufferPixmap( ir.size() );
1320 painter->begin( doubleBuffer );
1325 painter->translate( ir.x(), ir.y() );
1334 if ( useDoubleBuffer || is_printer( painter ) ) {
1336 if ( brush.style() != TQt::SolidPattern ) {
1337 bitBlt( doubleBuffer, 0, 0, p->device(),
1338 ir.x() + (
int)p->translationX(), ir.y() + (
int)p->translationY(),
1339 ir.width(), ir.height() );
1344 painter->fillRect( TQRect( 0, 0, ir.width(), ir.height() ), brush );
1347 painter->translate( rect.x() - ir.x(), rect.y() - ir.y() );
1348#ifdef DEBUG_PAINTING
1349 kdDebug(32500) <<
"KoTextDocument::drawParagWYSIWYG translate " << rect.x() - ir.x() <<
"," << rect.y() - ir.y() << endl;
1354 TQRect crect_lu( zoomHandler->pixelToLayoutUnit( crect ) );
1355#ifdef DEBUG_PAINTING
1356 kdDebug(32500) <<
"KoTextDocument::drawParagWYSIWYG crect_lu=" << crect_lu << endl;
1362 painter->translate( 0, -offsetY );
1364 parag->paint( *painter, cg, drawCursor ? cursor : 0, (m_drawingFlags & DrawSelections),
1365 crect_lu.x(), crect_lu.y(), crect_lu.width(), crect_lu.height() );
1368 if ( useDoubleBuffer ) {
1371 p->drawPixmap( ir.topLeft(), *doubleBuffer, TQRect( TQPoint( 0, 0 ), ir.size() ) );
1374 p->setPen( TQt::blue );
1375 p->drawRect( ir.x(), ir.y(), ir.width(), ir.height() );
1387 int docright = zoomHandler->
layoutUnitToPixelX( parag->document()->x() + parag->document()->width() );
1388#ifdef DEBUG_PAINTING
1391 if ( rect.x() + rect.width() < docright ) {
1392#ifdef DEBUG_PAINTING
1393 kdDebug(32500) <<
"KoTextDocument::drawParagWYSIWYG rect doesn't go up to docright=" << docright << endl;
1395 p->fillRect( rect.x() + rect.width(), rect.y(),
1396 docright - ( rect.x() + rect.width() ),
1397 rect.height(), cg.brush( TQColorGroup::Base ) );
1402 parag->setChanged(
false );
1406KoTextDocCommand *KoTextDocument::deleteTextCommand( KoTextDocument *textdoc,
int id,
int index,
const TQMemArray<KoTextStringChar> & str,
const CustomItemsMap & customItemsMap,
const TQValueList<KoParagLayout> & oldParagLayouts )
1408 return new KoTextDeleteCommand( textdoc,
id, index, str, customItemsMap, oldParagLayouts );
1411KoTextParag* KoTextDocument::loadOasisText(
const TQDomElement& bodyElem,
KoOasisContext& context, KoTextParag* lastParagraph,
KoStyleCollection* styleColl, KoTextParag* nextParagraph )
1415 forEachElement( tag, bodyElem )
1417 context.styleStack().
save();
1418 const TQString localName = tag.localName();
1419 const bool isTextNS = tag.namespaceURI() == KoXmlNS::text;
1421 if ( isTextNS && localName ==
"p" ) {
1422 context.
fillStyleStack( tag, KoXmlNS::text,
"style-name",
"paragraph" );
1424 KoTextParag *parag = createParag(
this, lastParagraph, nextParagraph );
1425 parag->loadOasis( tag, context, styleColl, pos );
1426 if ( !lastParagraph )
1427 setFirstParag( parag );
1428 lastParagraph = parag;
1430 else if ( isTextNS && localName ==
"h" )
1433 context.
fillStyleStack( tag, KoXmlNS::text,
"style-name",
"paragraph" );
1434 int level = tag.attributeNS( KoXmlNS::text,
"outline-level", TQString() ).toInt();
1435 bool listOK =
false;
1447 int restartNumbering = -1;
1448 if ( tag.hasAttributeNS( KoXmlNS::text,
"start-value" ) )
1450 restartNumbering = tag.attributeNS( KoXmlNS::text,
"start-value", TQString() ).toInt();
1452 KoTextParag *parag = createParag(
this, lastParagraph, nextParagraph );
1453 parag->loadOasis( tag, context, styleColl, pos );
1454 if ( !lastParagraph )
1455 setFirstParag( parag );
1456 lastParagraph = parag;
1458 parag->applyListStyle( context, restartNumbering,
true ,
true , level );
1459 context.listStyleStack().
pop();
1462 else if ( isTextNS &&
1463 ( localName ==
"unordered-list" || localName ==
"ordered-list"
1464 || localName ==
"list" || localName ==
"numbered-paragraph" ) )
1466 lastParagraph = loadList( tag, context, lastParagraph, styleColl, nextParagraph );
1468 else if ( isTextNS && localName ==
"section" )
1470 kdDebug(32500) <<
"Section found!" << endl;
1471 context.
fillStyleStack( tag, KoXmlNS::text,
"style-name",
"section" );
1472 lastParagraph = loadOasisText( tag, context, lastParagraph, styleColl, nextParagraph );
1474 else if ( isTextNS && localName ==
"variable-decls" )
1479 else if ( isTextNS && localName ==
"user-field-decls" )
1482 forEachElement( fd, tag )
1484 if ( fd.namespaceURI() == KoXmlNS::text && fd.localName() ==
"user-field-decl" )
1486 const TQString
name = fd.attributeNS( KoXmlNS::text,
"name", TQString() );
1487 const TQString value = fd.attributeNS( KoXmlNS::office,
"value", TQString() );
1488 if ( !
name.isEmpty() )
1489 context.variableCollection().setVariableValue( name, value );
1493 else if ( isTextNS && localName ==
"number" )
1498 else if ( !loadOasisBodyTag( tag, context, lastParagraph, styleColl, nextParagraph ) )
1500 kdWarning(32500) <<
"Unsupported body element '" << localName <<
"'" << endl;
1503 context.styleStack().
restore();
1507 return lastParagraph;
1510KoTextParag* KoTextDocument::loadList(
const TQDomElement& list,
KoOasisContext& context, KoTextParag* lastParagraph,
KoStyleCollection * styleColl, KoTextParag* nextParagraph )
1514 const TQString oldListStyleName = context.currentListStyleName();
1515 if ( list.hasAttributeNS( KoXmlNS::text,
"style-name" ) )
1516 context.setCurrentListStyleName( list.attributeNS( KoXmlNS::text,
"style-name", TQString() ) );
1517 bool listOK = !context.currentListStyleName().isEmpty();
1519 if ( list.localName() ==
"numbered-paragraph" )
1520 level = list.attributeNS( KoXmlNS::text,
"level",
"1" ).toInt();
1522 level = context.listStyleStack().
level() + 1;
1526 const TQDomElement listStyle = context.listStyleStack().
currentListStyle();
1528 const bool orderedList = listStyle.localName() ==
"list-level-style-number";
1530 if ( list.localName() ==
"numbered-paragraph" )
1533 int restartNumbering = -1;
1534 if ( list.hasAttributeNS( KoXmlNS::text,
"start-value" ) )
1535 restartNumbering = list.attributeNS( KoXmlNS::text,
"start-value", TQString() ).toInt();
1536 KoTextParag* oldLast = lastParagraph;
1537 lastParagraph = loadOasisText( list, context, lastParagraph, styleColl, nextParagraph );
1538 KoTextParag* firstListItem = oldLast ? oldLast->next() : firstParag();
1541 bool isOutline = firstListItem->counter() && firstListItem->counter()->numbering() == KoParagCounter::NUM_CHAPTER;
1542 firstListItem->applyListStyle( context, restartNumbering, orderedList,
1548 for ( TQDomNode n = list.firstChild(); !n.isNull(); n = n.nextSibling() )
1550 TQDomElement listItem = n.toElement();
1551 int restartNumbering = -1;
1552 if ( listItem.hasAttributeNS( KoXmlNS::text,
"start-value" ) )
1553 restartNumbering = listItem.attributeNS( KoXmlNS::text,
"start-value", TQString() ).toInt();
1554 bool isListHeader = listItem.localName() ==
"list-header" || listItem.attributeNS( KoXmlNS::text,
"is-list-header", TQString() ) ==
"is-list-header";
1555 KoTextParag* oldLast = lastParagraph;
1556 lastParagraph = loadOasisText( listItem, context, lastParagraph, styleColl, nextParagraph );
1557 KoTextParag* firstListItem = oldLast ? oldLast->next() : firstParag();
1558 KoTextParag* p = firstListItem;
1560 if ( !isListHeader && firstListItem ) {
1562 bool isOutline = firstListItem->counter() && firstListItem->counter()->numbering() == KoParagCounter::NUM_CHAPTER;
1563 firstListItem->applyListStyle( context, restartNumbering, orderedList, isOutline, level );
1567 while ( p && p != lastParagraph->next() ) {
1569 p->counter()->setNumbering( KoParagCounter::NUM_NONE );
1575 context.listStyleStack().
pop();
1576 context.setCurrentListStyleName( oldListStyleName );
1577 return lastParagraph;
1584 KoTextParag* parag = firstParag();
1587 parag->saveOasis( writer, context, 0, parag->lastCharPos() );
1588 parag = parag->next();
1592#include "KoTextDocument.moc"
A CustomItemsMap associates a custom item to an index Used in the undo/redo info for insert/delete te...
void pop()
Removes the style on top of the stack.
TQDomElement currentListStyle() const
Used during loading of Oasis format (and discarded at the end of the loading).
bool pushListLevelStyle(const TQString &listStyleName, int level)
Used for lists (numbered paragraphs)
bool pushOutlineListLevelStyle(int level)
Used for outline levels.
void fillStyleStack(const TQDomElement &element, const char *nsURI, const char *attrName, const char *family)
Convenience method for loading the style of an object before loading that object.
Base class for "visitors".
virtual bool visit(KoTextParag *parag, int start, int end)=0
Visit the paragraph parag, from index start to index end.
Used during saving to Oasis format (and discarded at the end of the saving).
TODO rename to KoParagStyleCollection - or should char styles be part of it too?
void save()
Save the current state of the stack.
void restore()
Restore the stack to the state it was at the corresponding save() call.
KoTextCustomItem is the base class for custom items (i.e.
Command created when deleting some text.
Each character (KoTextStringChar) points to a KoTextFormat that defines the formatting of that charac...
We implement our own text formatter to implement WYSIWYG: It is heavily based on KoTextFormatterBaseB...
This class extends KoZoomHandler to add support for WYSIWYG text layouting.
int layoutUnitToPixelX(int lupix) const
The "layout unit pixel" -> "[zoomed] view pixel" conversions.
A class for writing out XML (to any TQIODevice), with a special attention on performance.
TDEAction * forward(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
TDEAction * clear(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
const char * name(StdAction id)