lib

KoRichText.cpp
1/****************************************************************************
2** Implementation of the internal TQt classes dealing with rich text
3**
4** Created : 990101
5**
6** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
7**
8** This file is part of the kernel module of the TQt GUI Toolkit.
9**
10** This file may be distributed under the terms of the Q Public License
11** as defined by Trolltech AS of Norway and appearing in the file
12** LICENSE.TQPL included in the packaging of this file.
13**
14** This file may be distributed and/or modified under the terms of the
15** GNU General Public License version 2 as published by the Free Software
16** Foundation and appearing in the file LICENSE.GPL included in the
17** packaging of this file.
18**
19** Licensees holding valid TQt Enterprise Edition or TQt Professional Edition
20** licenses may use this file in accordance with the TQt Commercial License
21** Agreement provided with the Software.
22**
23** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
24** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
25**
26** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
27** information about TQt Commercial License Agreements.
28** See http://www.trolltech.com/qpl/ for TQPL licensing information.
29** See http://www.trolltech.com/gpl/ for GPL licensing information.
30**
31** Contact info@trolltech.com if any conditions of this licensing are
32** not clear to you.
33**
34**********************************************************************/
35
36#include "KoRichText.h"
37#include "KoTextFormat.h"
38#include "KoTextParag.h"
39
40#include <tqpaintdevicemetrics.h>
41#include "tqdrawutil.h" // for KoTextHorizontalLine
42
43#include <stdlib.h>
44#include "KoParagCounter.h"
45#include "KoTextDocument.h"
46#include <kdebug.h>
47#include <tdeglobal.h>
48#include <tdelocale.h>
49#include <tqtextengine_p.h>
50
51//#define DEBUG_COLLECTION
52//#define DEBUG_TABLE_RENDERING
53
54//static KoTextFormatCollection *qFormatCollection = 0;
55
56// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
57
58void KoTextDocCommandHistory::addCommand( KoTextDocCommand *cmd )
59{
60 if ( current < (int)history.count() - 1 ) {
61 TQPtrList<KoTextDocCommand> commands;
62 commands.setAutoDelete( false );
63
64 for( int i = 0; i <= current; ++i ) {
65 commands.insert( i, history.at( 0 ) );
66 history.take( 0 );
67 }
68
69 commands.append( cmd );
70 history.clear();
71 history = commands;
72 history.setAutoDelete( true );
73 } else {
74 history.append( cmd );
75 }
76
77 if ( (int)history.count() > steps )
78 history.removeFirst();
79 else
80 ++current;
81}
82
83KoTextCursor *KoTextDocCommandHistory::undo( KoTextCursor *c )
84{
85 if ( current > -1 ) {
86 KoTextCursor *c2 = history.at( current )->unexecute( c );
87 --current;
88 return c2;
89 }
90 return 0;
91}
92
93KoTextCursor *KoTextDocCommandHistory::redo( KoTextCursor *c )
94{
95 if ( current > -1 ) {
96 if ( current < (int)history.count() - 1 ) {
97 ++current;
98 return history.at( current )->execute( c );
99 }
100 } else {
101 if ( history.count() > 0 ) {
102 ++current;
103 return history.at( current )->execute( c );
104 }
105 }
106 return 0;
107}
108
109bool KoTextDocCommandHistory::isUndoAvailable()
110{
111 return current > -1;
112}
113
114bool KoTextDocCommandHistory::isRedoAvailable()
115{
116 return current > -1 && current < (int)history.count() - 1 || current == -1 && history.count() > 0;
117}
118
119// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
120
121KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextDocument *d, int i, int idx, const TQMemArray<KoTextStringChar> &str )
122 : KoTextDocCommand( d ), id( i ), index( idx ), parag( 0 ), text( str )
123{
124 for ( int j = 0; j < (int)text.size(); ++j ) {
125 if ( text[ j ].format() )
126 text[ j ].format()->addRef();
127 }
128}
129
130/*KoTextDocDeleteCommand::KoTextDocDeleteCommand( KoTextParag *p, int idx, const TQMemArray<KoTextStringChar> &str )
131 : KoTextDocCommand( 0 ), id( -1 ), index( idx ), parag( p ), text( str )
132{
133 for ( int i = 0; i < (int)text.size(); ++i ) {
134 if ( text[ i ].format() )
135 text[ i ].format()->addRef();
136 }
137}*/
138
139KoTextDocDeleteCommand::~KoTextDocDeleteCommand()
140{
141 for ( int i = 0; i < (int)text.size(); ++i ) {
142 if ( text[ i ].format() )
143 text[ i ].format()->removeRef();
144 }
145 text.resize( 0 );
146}
147
148KoTextCursor *KoTextDocDeleteCommand::execute( KoTextCursor *c )
149{
150 KoTextParag *s = doc ? doc->paragAt( id ) : parag;
151 if ( !s ) {
152 if(doc)
153 kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
154 return 0;
155 }
156
157 cursor.setParag( s );
158 cursor.setIndex( index );
159 int len = text.size();
160 if ( c )
161 *c = cursor;
162 if ( doc ) {
163 doc->setSelectionStart( KoTextDocument::Temp, &cursor );
164 for ( int i = 0; i < len; ++i )
165 cursor.gotoNextLetter();
166 doc->setSelectionEnd( KoTextDocument::Temp, &cursor );
167 doc->removeSelectedText( KoTextDocument::Temp, &cursor );
168 if ( c )
169 *c = cursor;
170 } else {
171 s->remove( index, len );
172 }
173
174 return c;
175}
176
177KoTextCursor *KoTextDocDeleteCommand::unexecute( KoTextCursor *c )
178{
179 KoTextParag *s = doc ? doc->paragAt( id ) : parag;
180 if ( !s ) {
181 if(doc)
182 kdWarning(32500) << "can't locate parag at " << id << ", last parag: " << doc->lastParag()->paragId() << endl;
183 return 0;
184 }
185
186 cursor.setParag( s );
187 cursor.setIndex( index );
188 TQString str = KoTextString::toString( text );
189 cursor.insert( str, true, &text );
190 cursor.setParag( s );
191 cursor.setIndex( index );
192 if ( c ) {
193 c->setParag( s );
194 c->setIndex( index );
195 for ( int i = 0; i < (int)text.size(); ++i )
196 c->gotoNextLetter();
197 }
198
199 s = cursor.parag();
200 while ( s ) {
201 s->format();
202 s->setChanged( true );
203 if ( c && s == c->parag() )
204 break;
205 s = s->next();
206 }
207
208 return &cursor;
209}
210
211KoTextDocFormatCommand::KoTextDocFormatCommand( KoTextDocument *d, int sid, int sidx, int eid, int eidx,
212 const TQMemArray<KoTextStringChar> &old, const KoTextFormat *f, int fl )
213 : KoTextDocCommand( d ), startId( sid ), startIndex( sidx ), endId( eid ), endIndex( eidx ), oldFormats( old ), flags( fl )
214{
215 format = d->formatCollection()->format( f );
216 for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
217 if ( oldFormats[ j ].format() )
218 oldFormats[ j ].format()->addRef();
219 }
220}
221
222KoTextDocFormatCommand::~KoTextDocFormatCommand()
223{
224 format->removeRef();
225 for ( int j = 0; j < (int)oldFormats.size(); ++j ) {
226 if ( oldFormats[ j ].format() )
227 oldFormats[ j ].format()->removeRef();
228 }
229}
230
231KoTextCursor *KoTextDocFormatCommand::execute( KoTextCursor *c )
232{
233 KoTextParag *sp = doc->paragAt( startId );
234 KoTextParag *ep = doc->paragAt( endId );
235 if ( !sp || !ep )
236 return c;
237
238 KoTextCursor start( doc );
239 start.setParag( sp );
240 start.setIndex( startIndex );
241 KoTextCursor end( doc );
242 end.setParag( ep );
243 end.setIndex( endIndex );
244
245 doc->setSelectionStart( KoTextDocument::Temp, &start );
246 doc->setSelectionEnd( KoTextDocument::Temp, &end );
247 doc->setFormat( KoTextDocument::Temp, format, flags );
248 doc->removeSelection( KoTextDocument::Temp );
249 if ( endIndex == ep->length() ) // ### Not in TQRT - report sent. Description at http://bugs.kde.org/db/34/34556.html
250 end.gotoLeft();
251 *c = end;
252 return c;
253}
254
255KoTextCursor *KoTextDocFormatCommand::unexecute( KoTextCursor *c )
256{
257 KoTextParag *sp = doc->paragAt( startId );
258 KoTextParag *ep = doc->paragAt( endId );
259 if ( !sp || !ep )
260 return 0;
261
262 int idx = startIndex;
263 int fIndex = 0;
264 if( !oldFormats.isEmpty()) // ## not in TQRT. Not sure how it can happen.
265 {
266 for ( ;; ) {
267 if ( oldFormats.at( fIndex ).c == '\n' ) {
268 if ( idx > 0 ) {
269 if ( idx < sp->length() && fIndex > 0 )
270 sp->setFormat( idx, 1, oldFormats.at( fIndex - 1 ).format() );
271 if ( sp == ep )
272 break;
273 sp = sp->next();
274 idx = 0;
275 }
276 fIndex++;
277 }
278 if ( oldFormats.at( fIndex ).format() )
279 sp->setFormat( idx, 1, oldFormats.at( fIndex ).format() );
280 idx++;
281 fIndex++;
282 if ( fIndex >= (int)oldFormats.size() )
283 break;
284 if ( idx >= sp->length() ) {
285 if ( sp == ep )
286 break;
287 sp = sp->next();
288 idx = 0;
289 }
290 }
291 }
292 KoTextCursor end( doc );
293 end.setParag( ep );
294 end.setIndex( endIndex );
295 if ( endIndex == ep->length() )
296 end.gotoLeft();
297 *c = end;
298 return c;
299}
300
301KoTextAlignmentCommand::KoTextAlignmentCommand( KoTextDocument *d, int fParag, int lParag, int na, const TQMemArray<int> &oa )
302 : KoTextDocCommand( d ), firstParag( fParag ), lastParag( lParag ), newAlign( na ), oldAligns( oa )
303{
304}
305
306KoTextCursor *KoTextAlignmentCommand::execute( KoTextCursor *c )
307{
308 KoTextParag *p = doc->paragAt( firstParag );
309 if ( !p )
310 return c;
311 while ( p ) {
312 p->setAlignment( newAlign );
313 if ( p->paragId() == lastParag )
314 break;
315 p = p->next();
316 }
317 return c;
318}
319
320KoTextCursor *KoTextAlignmentCommand::unexecute( KoTextCursor *c )
321{
322 KoTextParag *p = doc->paragAt( firstParag );
323 if ( !p )
324 return c;
325 int i = 0;
326 while ( p ) {
327 if ( i < (int)oldAligns.size() )
328 p->setAlignment( oldAligns.at( i ) );
329 if ( p->paragId() == lastParag )
330 break;
331 p = p->next();
332 ++i;
333 }
334 return c;
335}
336
337
338// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
339
340KoTextCursor::KoTextCursor( KoTextDocument *d )
341 : doc( d )
342{
343 idx = 0;
344 string = doc ? doc->firstParag() : 0;
345 tmpIndex = -1;
346}
347
348KoTextCursor::KoTextCursor()
349{
350}
351
352KoTextCursor::KoTextCursor( const KoTextCursor &c )
353{
354 doc = c.doc;
355 idx = c.idx;
356 string = c.string;
357 tmpIndex = c.tmpIndex;
358}
359
360KoTextCursor &KoTextCursor::operator=( const KoTextCursor &c )
361{
362 doc = c.doc;
363 idx = c.idx;
364 string = c.string;
365 tmpIndex = c.tmpIndex;
366
367 return *this;
368}
369
370bool KoTextCursor::operator==( const KoTextCursor &c ) const
371{
372 return doc == c.doc && string == c.string && idx == c.idx;
373}
374
375void KoTextCursor::insert( const TQString &str, bool checkNewLine, TQMemArray<KoTextStringChar> *formatting )
376{
377 string->invalidate( idx );
378 tmpIndex = -1;
379 bool justInsert = true;
380 TQString s( str );
381#if defined(TQ_WS_WIN)
382 if ( checkNewLine )
383 s = s.replace( TQRegExp( "\\r" ), "" );
384#endif
385 if ( checkNewLine )
386 justInsert = s.find( '\n' ) == -1;
387 if ( justInsert ) {
388 string->insert( idx, s );
389 if ( formatting ) {
390 for ( int i = 0; i < (int)s.length(); ++i ) {
391 if ( formatting->at( i ).format() ) {
392 formatting->at( i ).format()->addRef();
393 string->string()->setFormat( idx + i, formatting->at( i ).format(), true );
394 }
395 }
396 }
397 idx += s.length();
398 } else {
399 TQStringList lst = TQStringList::split( '\n', s, true );
400 TQStringList::Iterator it = lst.begin();
401 //int y = string->rect().y() + string->rect().height();
402 int lastIndex = 0;
403 KoTextFormat *lastFormat = 0;
404 for ( ; it != lst.end(); ) {
405 if ( it != lst.begin() ) {
406 splitAndInsertEmptyParag( false, true );
407 //string->setEndState( -1 );
408#if 0 // no!
409 string->prev()->format( -1, false );
410#endif
411 if ( lastFormat && formatting && string->prev() ) {
412 lastFormat->addRef();
413 string->prev()->string()->setFormat( string->prev()->length() - 1, lastFormat, true );
414 }
415 }
416 lastFormat = 0;
417 TQString s = *it;
418 ++it;
419 if ( !s.isEmpty() )
420 string->insert( idx, s );
421 else
422 string->invalidate( 0 );
423
424 if ( formatting ) {
425 int len = s.length();
426 for ( int i = 0; i < len; ++i ) {
427 if ( formatting->at( i + lastIndex ).format() ) {
428 formatting->at( i + lastIndex ).format()->addRef();
429 string->string()->setFormat( i + idx, formatting->at( i + lastIndex ).format(), true );
430 }
431 }
432 if ( it != lst.end() )
433 lastFormat = formatting->at( len + lastIndex ).format();
434 ++len;
435 lastIndex += len;
436 }
437
438 idx += s.length();
439 }
440#if 0
441 string->format( -1, false );
442 int dy = string->rect().y() + string->rect().height() - y;
443#endif
444 KoTextParag *p = string;
445 p->setParagId( p->prev()->paragId() + 1 );
446 p = p->next();
447 while ( p ) {
448 p->setParagId( p->prev()->paragId() + 1 );
449 //p->move( dy );
450 p->invalidate( 0 );
451 p = p->next();
452 }
453 }
454
455#if 0
456 int h = string->rect().height();
457 string->format( -1, true );
458#endif
459 fixCursorPosition();
460}
461
462void KoTextCursor::gotoLeft()
463{
464 if ( string->string()->isRightToLeft() )
465 gotoNextLetter();
466 else
467 gotoPreviousLetter();
468}
469
470void KoTextCursor::gotoPreviousLetter()
471{
472 tmpIndex = -1;
473
474 if ( idx > 0 ) {
475 idx = string->string()->previousCursorPosition( idx );
476 } else if ( string->prev() ) {
477 string = string->prev();
478 while ( !string->isVisible() )
479 string = string->prev();
480 idx = string->length() - 1;
481 }
482}
483
484bool KoTextCursor::place( const TQPoint &p, KoTextParag *s, bool link, int *customItemIndex )
485{
486 if ( customItemIndex )
487 *customItemIndex = -1;
488 TQPoint pos( p );
489 TQRect r;
490 if ( pos.y() < s->rect().y() )
491 pos.setY( s->rect().y() );
492 while ( s ) {
493 r = s->rect();
494 r.setWidth( doc ? doc->width() : TQWIDGETSIZE_MAX );
495 if ( !s->next() || ( pos.y() >= r.y() && pos.y() < s->next()->rect().y() ) )
496 break;
497 s = s->next();
498 }
499
500 if ( !s )
501 return false;
502
503 setParag( s, false );
504 int y = s->rect().y();
505 int lines = s->lines();
506 KoTextStringChar *chr = 0;
507 int index = 0;
508 int i = 0;
509 int cy = 0;
510 //int ch = 0;
511 for ( ; i < lines; ++i ) {
512 chr = s->lineStartOfLine( i, &index );
513 cy = s->lineY( i );
514 //ch = s->lineHeight( i );
515 if ( !chr )
516 return false;
517 if ( i < lines - 1 && pos.y() >= y + cy && pos.y() <= y + s->lineY( i+1 ) )
518 break;
519 }
520 int nextLine;
521 if ( i < lines - 1 )
522 s->lineStartOfLine( i+1, &nextLine );
523 else
524 nextLine = s->length();
525 i = index;
526 int x = s->rect().x();
527 if ( pos.x() < x )
528 pos.setX( x + 1 );
529 int cw;
530 int curpos = s->length()-1;
531 int dist = 10000000;
532 while ( i < nextLine ) {
533 chr = s->at(i);
534 int cpos = x + chr->x;
535 cw = chr->width; //s->string()->width( i );
536 if ( chr->isCustom() ) {
537 if ( pos.x() >= cpos && pos.x() <= cpos + cw &&
538 pos.y() >= y + cy && pos.y() <= y + cy + chr->height() ) {
539 if ( customItemIndex )
540 *customItemIndex = i;
541 }
542 }
543 if( chr->rightToLeft )
544 cpos += cw;
545 int d = cpos - pos.x();
546 bool dm = d < 0 ? !chr->rightToLeft : chr->rightToLeft;
547 if ( (TQABS( d ) < dist || (dist == d && dm == true )) && string->string()->validCursorPosition( i ) ) {
548 dist = TQABS( d );
549 if ( !link || pos.x() >= x + chr->x ) {
550 curpos = i;
551 }
552 }
553 i++;
554 }
555 setIndex( curpos, false );
556
557 return true;
558}
559
560void KoTextCursor::gotoRight()
561{
562 if ( string->string()->isRightToLeft() )
563 gotoPreviousLetter();
564 else
565 gotoNextLetter();
566}
567
568void KoTextCursor::gotoNextLetter()
569{
570 tmpIndex = -1;
571
572 int len = string->length() - 1;
573 if ( idx < len ) {
574 idx = string->string()->nextCursorPosition( idx );
575 } else if ( string->next() ) {
576 string = string->next();
577 while ( !string->isVisible() )
578 string = string->next();
579 idx = 0;
580 }
581}
582
583void KoTextCursor::gotoUp()
584{
585 int indexOfLineStart;
586 int line;
587 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
588 if ( !c )
589 return;
590
591 tmpIndex = TQMAX( tmpIndex, idx - indexOfLineStart );
592 if ( indexOfLineStart == 0 ) {
593 if ( !string->prev() ) {
594 return;
595 }
596 string = string->prev();
597 while ( !string->isVisible() )
598 string = string->prev();
599 int lastLine = string->lines() - 1;
600 if ( !string->lineStartOfLine( lastLine, &indexOfLineStart ) )
601 return;
602 if ( indexOfLineStart + tmpIndex < string->length() )
603 idx = indexOfLineStart + tmpIndex;
604 else
605 idx = string->length() - 1;
606 } else {
607 --line;
608 int oldIndexOfLineStart = indexOfLineStart;
609 if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
610 return;
611 if ( indexOfLineStart + tmpIndex < oldIndexOfLineStart )
612 idx = indexOfLineStart + tmpIndex;
613 else
614 idx = oldIndexOfLineStart - 1;
615 }
616 fixCursorPosition();
617}
618
619void KoTextCursor::gotoDown()
620{
621 int indexOfLineStart;
622 int line;
623 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
624 if ( !c )
625 return;
626
627 tmpIndex = TQMAX( tmpIndex, idx - indexOfLineStart );
628 if ( line == string->lines() - 1 ) {
629 if ( !string->next() ) {
630 return;
631 }
632 string = string->next();
633 while ( !string->isVisible() )
634 string = string->next();
635 if ( !string->lineStartOfLine( 0, &indexOfLineStart ) )
636 return;
637 int end;
638 if ( string->lines() == 1 )
639 end = string->length();
640 else
641 string->lineStartOfLine( 1, &end );
642 if ( indexOfLineStart + tmpIndex < end )
643 idx = indexOfLineStart + tmpIndex;
644 else
645 idx = end - 1;
646 } else {
647 ++line;
648 int end;
649 if ( line == string->lines() - 1 )
650 end = string->length();
651 else
652 string->lineStartOfLine( line + 1, &end );
653 if ( !string->lineStartOfLine( line, &indexOfLineStart ) )
654 return;
655 if ( indexOfLineStart + tmpIndex < end )
656 idx = indexOfLineStart + tmpIndex;
657 else
658 idx = end - 1;
659 }
660 fixCursorPosition();
661}
662
663void KoTextCursor::gotoLineEnd()
664{
665 tmpIndex = -1;
666 int indexOfLineStart;
667 int line;
668 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
669 if ( !c )
670 return;
671
672 if ( line == string->lines() - 1 ) {
673 idx = string->length() - 1;
674 } else {
675 c = string->lineStartOfLine( ++line, &indexOfLineStart );
676 indexOfLineStart--;
677 idx = indexOfLineStart;
678 }
679}
680
681void KoTextCursor::gotoLineStart()
682{
683 tmpIndex = -1;
684 int indexOfLineStart;
685 int line;
686 KoTextStringChar *c = string->lineStartOfChar( idx, &indexOfLineStart, &line );
687 if ( !c )
688 return;
689
690 idx = indexOfLineStart;
691}
692
693void KoTextCursor::gotoHome()
694{
695 tmpIndex = -1;
696 if ( doc )
697 string = doc->firstParag();
698 idx = 0;
699}
700
701void KoTextCursor::gotoEnd()
702{
703 // This can happen in a no-auto-resize frame with overflowing contents.
704 // Don't prevent going to the end of the text, even if it's not visible.
705 //if ( doc && !doc->lastParag()->isValid() )
706 //{
707// kdDebug(32500) << "Last parag, " << doc->lastParag()->paragId() << ", is invalid - aborting gotoEnd() !" << endl;
708// return;
709// }
710
711 tmpIndex = -1;
712 if ( doc )
713 string = doc->lastParag();
714 idx = string->length() - 1;
715}
716
717void KoTextCursor::gotoPageUp( int visibleHeight )
718{
719 tmpIndex = -1;
720 KoTextParag *s = string;
721 int h = visibleHeight;
722 int y = s->rect().y();
723 while ( s ) {
724 if ( y - s->rect().y() >= h )
725 break;
726 s = s->prev();
727 }
728
729 if ( !s && doc )
730 s = doc->firstParag();
731
732 string = s;
733 idx = 0;
734}
735
736void KoTextCursor::gotoPageDown( int visibleHeight )
737{
738 tmpIndex = -1;
739 KoTextParag *s = string;
740 int h = visibleHeight;
741 int y = s->rect().y();
742 while ( s ) {
743 if ( s->rect().y() - y >= h )
744 break;
745 s = s->next();
746 }
747
748 if ( !s && doc ) {
749 s = doc->lastParag();
750 string = s;
751 idx = string->length() - 1;
752 return;
753 }
754
755 if ( !s->isValid() )
756 return;
757
758 string = s;
759 idx = 0;
760}
761
762void KoTextCursor::gotoWordRight()
763{
764 if ( string->string()->isRightToLeft() )
765 gotoPreviousWord();
766 else
767 gotoNextWord();
768}
769
770void KoTextCursor::gotoWordLeft()
771{
772 if ( string->string()->isRightToLeft() )
773 gotoNextWord();
774 else
775 gotoPreviousWord();
776}
777
778void KoTextCursor::gotoPreviousWord()
779{
780 gotoPreviousLetter();
781 tmpIndex = -1;
782 KoTextString *s = string->string();
783 bool allowSame = false;
784 if ( idx == ( (int)s->length()-1 ) )
785 return;
786 for ( int i = idx; i >= 0; --i ) {
787 if ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
788 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) {
789 if ( !allowSame )
790 continue;
791 idx = i + 1;
792 return;
793 }
794 if ( !allowSame && !( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
795 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) )
796 allowSame = true;
797 }
798 idx = 0;
799}
800
801void KoTextCursor::gotoNextWord()
802{
803 tmpIndex = -1;
804 KoTextString *s = string->string();
805 bool allowSame = false;
806 for ( int i = idx; i < (int)s->length(); ++i ) {
807 if ( ! ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
808 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) ) {
809 if ( !allowSame )
810 continue;
811 idx = i;
812 return;
813 }
814 if ( !allowSame && ( s->at( i ).c.isSpace() || s->at( i ).c == '\t' || s->at( i ).c == '.' ||
815 s->at( i ).c == ',' || s->at( i ).c == ':' || s->at( i ).c == ';' ) )
816 allowSame = true;
817 }
818
819 if ( idx < ((int)s->length()-1) ) {
820 gotoLineEnd();
821 } else if ( string->next() ) {
822 string = string->next();
823 while ( !string->isVisible() )
824 string = string->next();
825 idx = 0;
826 } else {
827 gotoLineEnd();
828 }
829}
830
831bool KoTextCursor::atParagStart() const
832{
833 return idx == 0;
834}
835
836bool KoTextCursor::atParagEnd() const
837{
838 return idx == string->length() - 1;
839}
840
841void KoTextCursor::splitAndInsertEmptyParag( bool ind, bool updateIds )
842{
843 if ( !doc )
844 return;
845 tmpIndex = -1;
846 KoTextFormat *f = 0;
847 if ( doc->useFormatCollection() ) {
848 f = string->at( idx )->format();
849 if ( idx == string->length() - 1 && idx > 0 )
850 f = string->at( idx - 1 )->format();
851 if ( f->isMisspelled() ) {
852 KoTextFormat fNoMisspelled( *f );
853 fNoMisspelled.setMisspelled( false );
854 f = doc->formatCollection()->format( &fNoMisspelled );
855 }
856 }
857
858 if ( atParagEnd() ) {
859 KoTextParag *n = string->next();
860 KoTextParag *s = doc->createParag( doc, string, n, updateIds );
861 if ( f )
862 s->setFormat( 0, 1, f, true );
863 s->copyParagData( string );
864#if 0
865 if ( ind ) {
866 int oi, ni;
867 s->indent( &oi, &ni );
868 string = s;
869 idx = ni;
870 } else
871#endif
872 {
873 string = s;
874 idx = 0;
875 }
876 } else if ( atParagStart() ) {
877 KoTextParag *p = string->prev();
878 KoTextParag *s = doc->createParag( doc, p, string, updateIds );
879 if ( f )
880 s->setFormat( 0, 1, f, true );
881 s->copyParagData( string );
882 if ( ind ) {
883 //s->indent();
884 s->format();
885 //indent();
886 string->format();
887 }
888 } else {
889 TQString str = string->string()->toString().mid( idx, 0xFFFFFF );
890 KoTextParag *n = string->next();
891 KoTextParag *s = doc->createParag( doc, string, n, updateIds );
892 s->copyParagData( string );
893 s->remove( 0, 1 );
894 s->append( str, true );
895 for ( uint i = 0; i < str.length(); ++i ) {
896 KoTextStringChar* tsc = string->at( idx + i );
897 s->setFormat( i, 1, tsc->format(), true );
898 if ( tsc->isCustom() ) {
899 KoTextCustomItem * item = tsc->customItem();
900 s->at( i )->setCustomItem( item );
901 tsc->loseCustomItem();
902#if 0
903 s->addCustomItem();
904 string->removeCustomItem();
905#endif
906 doc->unregisterCustomItem( item, string );
907 doc->registerCustomItem( item, s );
908 }
909 }
910 string->truncate( idx );
911#if 0
912 if ( ind ) {
913 int oi, ni;
914 s->indent( &oi, &ni );
915 string = s;
916 idx = ni;
917 } else
918#endif
919 {
920 string = s;
921 idx = 0;
922 }
923 }
924}
925
926bool KoTextCursor::removePreviousChar()
927{
928 tmpIndex = -1;
929 if ( !atParagStart() ) {
930 string->remove( idx-1, 1 );
931 idx--;
932 // shouldn't be needed, just to make sure.
933 fixCursorPosition();
934 string->format( -1, true );
935 //else if ( string->document() && string->document()->parent() )
936 // string->document()->nextDoubleBuffered = true;
937 return false;
938 } else if ( string->prev() ) {
939 string = string->prev();
940 string->join( string->next() );
941 string->invalidateCounters();
942 return true;
943 }
944 return false;
945}
946
947bool KoTextCursor::remove()
948{
949 tmpIndex = -1;
950 if ( !atParagEnd() ) {
951 int next = string->string()->nextCursorPosition( idx );
952 string->remove( idx, next-idx );
953 string->format( -1, true );
954 //else if ( doc && doc->parent() )
955 // doc->nextDoubleBuffered = true;
956 return false;
957 } else if ( string->next() ) {
958 if ( string->length() == 1 ) {
959 string->next()->setPrev( string->prev() );
960 if ( string->prev() )
961 string->prev()->setNext( string->next() );
962 KoTextParag *p = string->next();
963 delete string;
964 string = p;
965 string->invalidate( 0 );
967 string->invalidateCounters();
969 KoTextParag *s = string;
970 while ( s ) {
971 s->id = s->p ? s->p->id + 1 : 0;
972 //s->state = -1;
973 //s->needPreProcess = true;
974 s->changed = true;
975 s = s->n;
976 }
977 string->format();
978 } else {
979 string->join( string->next() );
980 }
981 return true;
982 }
983 return false;
984}
985
986void KoTextCursor::killLine()
987{
988 if ( atParagEnd() )
989 return;
990 string->remove( idx, string->length() - idx - 1 );
991 string->format( -1, true );
992 //else if ( doc && doc->parent() )
993 //doc->nextDoubleBuffered = true;
994}
995
996#if 0
997void KoTextCursor::indent()
998{
999 int oi = 0, ni = 0;
1000 string->indent( &oi, &ni );
1001 if ( oi == ni )
1002 return;
1003
1004 if ( idx >= oi )
1005 idx += ni - oi;
1006 else
1007 idx = ni;
1008}
1009#endif
1010
1011void KoTextCursor::setDocument( KoTextDocument *d )
1012{
1013 doc = d;
1014 string = d->firstParag();
1015 idx = 0;
1016 tmpIndex = -1;
1017}
1018
1019
1020int KoTextCursor::x() const
1021{
1022 KoTextStringChar *c = string->at( idx );
1023 int curx = c->x;
1024 if ( c->rightToLeft )
1025 curx += c->width; //string->string()->width( idx );
1026 return curx;
1027}
1028
1029int KoTextCursor::y() const
1030{
1031 int dummy, line;
1032 string->lineStartOfChar( idx, &dummy, &line );
1033 return string->lineY( line );
1034}
1035
1036
1037void KoTextCursor::fixCursorPosition()
1038{
1039 // searches for the closest valid cursor position
1040 if ( string->string()->validCursorPosition( idx ) )
1041 return;
1042
1043 int lineIdx;
1044 KoTextStringChar *start = string->lineStartOfChar( idx, &lineIdx, 0 );
1045 int x = string->string()->at( idx ).x;
1046 int diff = TQABS(start->x - x);
1047 int best = lineIdx;
1048
1049 KoTextStringChar *c = start;
1050 ++c;
1051
1052 KoTextStringChar *end = &string->string()->at( string->length()-1 );
1053 while ( c <= end && !c->lineStart ) {
1054 int xp = c->x;
1055 if ( c->rightToLeft )
1056 xp += c->pixelwidth; //string->string()->width( lineIdx + (c-start) );
1057 int ndiff = TQABS(xp - x);
1058 if ( ndiff < diff && string->string()->validCursorPosition(lineIdx + (c-start)) ) {
1059 diff = ndiff;
1060 best = lineIdx + (c-start);
1061 }
1062 ++c;
1063 }
1064 idx = best;
1065}
1066
1067// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1068
1069KoTextString::KoTextString()
1070{
1071 bidiDirty = true;
1072 bNeedsSpellCheck = true;
1073 bidi = false;
1074 rightToLeft = false;
1075 dir = TQChar::DirON;
1076}
1077
1078KoTextString::KoTextString( const KoTextString &s )
1079{
1080 bidiDirty = s.bidiDirty;
1081 bNeedsSpellCheck = s.bNeedsSpellCheck;
1082 bidi = s.bidi;
1083 rightToLeft = s.rightToLeft;
1084 dir = s.dir;
1085 data = s.data;
1086 data.detach();
1087 for ( int i = 0; i < (int)data.size(); ++i ) {
1088 KoTextFormat *f = data[i].format();
1089 if ( f )
1090 f->addRef();
1091 }
1092}
1093
1094void KoTextString::insert( int index, const TQString &s, KoTextFormat *f )
1095{
1096 int os = data.size();
1097 data.resize( data.size() + s.length() );
1098 if ( index < os ) {
1099 memmove( data.data() + index + s.length(), data.data() + index,
1100 sizeof( KoTextStringChar ) * ( os - index ) );
1101 }
1102 for ( int i = 0; i < (int)s.length(); ++i ) {
1103 KoTextStringChar &ch = data[ (int)index + i ];
1104 ch.x = 0;
1105 ch.pixelxadj = 0;
1106 ch.pixelwidth = 0;
1107 ch.width = 0;
1108 ch.lineStart = 0;
1109 ch.d.format = 0;
1110 ch.type = KoTextStringChar::Regular;
1111 ch.rightToLeft = 0;
1112 ch.startOfRun = 0;
1113 ch.c = s[ i ];
1114#ifdef DEBUG_COLLECTION
1115 kdDebug(32500) << "KoTextString::insert setting format " << f << " to character " << (int)index+i << endl;
1116#endif
1117 ch.setFormat( f );
1118 }
1119 bidiDirty = true;
1120 bNeedsSpellCheck = true;
1121}
1122
1123KoTextString::~KoTextString()
1124{
1125 clear();
1126}
1127
1128void KoTextString::insert( int index, KoTextStringChar *c )
1129{
1130 int os = data.size();
1131 data.resize( data.size() + 1 );
1132 if ( index < os ) {
1133 memmove( data.data() + index + 1, data.data() + index,
1134 sizeof( KoTextStringChar ) * ( os - index ) );
1135 }
1136 KoTextStringChar &ch = data[ (int)index ];
1137 ch.c = c->c;
1138 ch.x = 0;
1139 ch.pixelxadj = 0;
1140 ch.pixelwidth = 0;
1141 ch.width = 0;
1142 ch.lineStart = 0;
1143 ch.rightToLeft = 0;
1144 ch.d.format = 0;
1145 ch.type = KoTextStringChar::Regular;
1146 ch.setFormat( c->format() );
1147 bidiDirty = true;
1148 bNeedsSpellCheck = true;
1149}
1150
1151void KoTextString::truncate( int index )
1152{
1153 index = TQMAX( index, 0 );
1154 index = TQMIN( index, (int)data.size() - 1 );
1155 if ( index < (int)data.size() ) {
1156 for ( int i = index + 1; i < (int)data.size(); ++i ) {
1157 KoTextStringChar &ch = data[ i ];
1158 if ( ch.isCustom() ) {
1159 delete ch.customItem();
1160 if ( ch.d.custom->format )
1161 ch.d.custom->format->removeRef();
1162 delete ch.d.custom;
1163 ch.d.custom = 0;
1164 } else if ( ch.format() ) {
1165 ch.format()->removeRef();
1166 }
1167 }
1168 }
1169 data.truncate( index );
1170 bidiDirty = true;
1171 bNeedsSpellCheck = true;
1172}
1173
1174void KoTextString::remove( int index, int len )
1175{
1176 for ( int i = index; i < (int)data.size() && i - index < len; ++i ) {
1177 KoTextStringChar &ch = data[ i ];
1178 if ( ch.isCustom() ) {
1179 delete ch.customItem();
1180 if ( ch.d.custom->format )
1181 ch.d.custom->format->removeRef();
1182 delete ch.d.custom;
1183 ch.d.custom = 0;
1184 } else if ( ch.format() ) {
1185 ch.format()->removeRef();
1186 }
1187 }
1188 memmove( data.data() + index, data.data() + index + len,
1189 sizeof( KoTextStringChar ) * ( data.size() - index - len ) );
1190 data.resize( data.size() - len, TQGArray::SpeedOptim );
1191 bidiDirty = true;
1192 bNeedsSpellCheck = true;
1193}
1194
1195void KoTextString::clear()
1196{
1197 for ( int i = 0; i < (int)data.count(); ++i ) {
1198 KoTextStringChar &ch = data[ i ];
1199 if ( ch.isCustom() ) {
1200 // Can't do that here, no access to the doc. See ~KoTextParag instead.
1201 // However clear() is also called by operator=, many times in kotextobject.cpp...
1202 // Hopefully not with customitems in there...
1203 //if ( doc )
1204 // doc->unregisterCustomItem( ch->customItem(), this );
1205
1206 delete ch.customItem();
1207 if ( ch.d.custom->format )
1208 ch.d.custom->format->removeRef();
1209 delete ch.d.custom;
1210 ch.d.custom = 0;
1211
1212 } else if ( ch.format() ) {
1213 ch.format()->removeRef();
1214 }
1215 }
1216 data.resize( 0 );
1217}
1218
1219void KoTextString::setFormat( int index, KoTextFormat *f, bool useCollection, bool setFormatAgain )
1220{
1221 KoTextStringChar &ch = data[ index ];
1222// kdDebug(32500) << "KoTextString::setFormat index=" << index << " f=" << f << endl;
1223 if ( useCollection && ch.format() )
1224 {
1225 //kdDebug(32500) << "KoTextString::setFormat removing ref on old format " << ch.format() << endl;
1226 ch.format()->removeRef();
1227 }
1228 ch.setFormat( f, setFormatAgain );
1229}
1230
1231void KoTextString::checkBidi() const
1232{
1233 KoTextString *that = (KoTextString *)this;
1234 that->bidiDirty = false;
1235 int length = data.size();
1236 if ( !length ) {
1237 that->bidi = false;
1238 that->rightToLeft = dir == TQChar::DirR;
1239 return;
1240 }
1241 const KoTextStringChar *start = data.data();
1242 const KoTextStringChar *end = start + length;
1243
1244 // determines the properties we need for layouting
1245 TQTextEngine textEngine( toString(), 0 );
1246 textEngine.direction = (TQChar::Direction) dir;
1247 textEngine.itemize(TQTextEngine::SingleLine);
1248 const TQCharAttributes *ca = textEngine.attributes() + length-1;
1249 KoTextStringChar *ch = (KoTextStringChar *)end - 1;
1250 TQScriptItem *item = &textEngine.items[textEngine.items.size()-1];
1251 unsigned char bidiLevel = item->analysis.bidiLevel;
1252 if ( bidiLevel )
1253 that->bidi = true;
1254 int pos = length-1;
1255 while ( ch >= start ) {
1256 if ( item->position > pos ) {
1257 --item;
1258 Q_ASSERT( item >= &textEngine.items[0] );
1259 Q_ASSERT( item < &textEngine.items[textEngine.items.size()] );
1260 bidiLevel = item->analysis.bidiLevel;
1261 if ( bidiLevel )
1262 that->bidi = true;
1263 }
1264 ch->softBreak = ca->softBreak;
1265 ch->whiteSpace = ca->whiteSpace;
1266 ch->charStop = ca->charStop;
1267 ch->wordStop = ca->wordStop;
1268 //ch->bidiLevel = bidiLevel;
1269 ch->rightToLeft = (bidiLevel%2);
1270 --ch;
1271 --ca;
1272 --pos;
1273 }
1274
1275 if ( dir == TQChar::DirR ) {
1276 that->bidi = true;
1277 that->rightToLeft = true;
1278 } else if ( dir == TQChar::DirL ) {
1279 that->rightToLeft = false;
1280 } else {
1281 that->rightToLeft = (textEngine.direction == TQChar::DirR);
1282 }
1283}
1284
1285TQMemArray<KoTextStringChar> KoTextString::subString( int start, int len ) const
1286{
1287 if ( len == 0xFFFFFF )
1288 len = data.size();
1289 TQMemArray<KoTextStringChar> a;
1290 a.resize( len );
1291 for ( int i = 0; i < len; ++i ) {
1292 KoTextStringChar *c = &data[ i + start ];
1293 a[ i ].c = c->c;
1294 a[ i ].x = 0;
1295 a[ i ].pixelxadj = 0;
1296 a[ i ].pixelwidth = 0;
1297 a[ i ].width = 0;
1298 a[ i ].lineStart = 0;
1299 a[ i ].rightToLeft = 0;
1300 a[ i ].d.format = 0;
1301 a[ i ].type = KoTextStringChar::Regular;
1302 a[ i ].setFormat( c->format() );
1303 if ( c->format() )
1304 c->format()->addRef();
1305 }
1306 return a;
1307}
1308
1309TQString KoTextString::mid( int start, int len ) const
1310{
1311 if ( len == 0xFFFFFF )
1312 len = data.size();
1313 TQString res;
1314 res.setLength( len );
1315 for ( int i = 0; i < len; ++i ) {
1316 KoTextStringChar *c = &data[ i + start ];
1317 res[ i ] = c->c;
1318 }
1319 return res;
1320}
1321
1322TQString KoTextString::toString( const TQMemArray<KoTextStringChar> &data )
1323{
1324 TQString s;
1325 int l = data.size();
1326 s.setUnicode( 0, l );
1327 KoTextStringChar *c = data.data();
1328 TQChar *uc = (TQChar *)s.unicode();
1329 while ( l-- ) {
1330 *uc = c->c;
1331 uc++;
1332 c++;
1333 }
1334
1335 return s;
1336}
1337
1338TQString KoTextString::toReverseString() const
1339{
1340 TQString s;
1341 int l = length();
1342 s.setUnicode(0, l);
1343 KoTextStringChar *c = data.data() + (l-1);
1344 TQChar *uc = (TQChar *)s.unicode();
1345 while ( l-- ) {
1346 *uc = c->c;
1347 uc++;
1348 c--;
1349 }
1350
1351 return s;
1352}
1353
1354TQString KoTextString::stringToSpellCheck()
1355{
1356 if ( !bNeedsSpellCheck )
1357 return TQString();
1358
1359 bNeedsSpellCheck = false;
1360 if ( length() <= 1 )
1361 return TQString();
1362
1363 TQString str = toString();
1364 str.truncate( str.length() - 1 ); // remove trailing space
1365 return str;
1366}
1367
1368int KoTextString::nextCursorPosition( int next )
1369{
1370 if ( bidiDirty )
1371 checkBidi();
1372
1373 const KoTextStringChar *c = data.data();
1374 int len = length();
1375
1376 if ( next < len - 1 ) {
1377 next++;
1378 while ( next < len - 1 && !c[next].charStop )
1379 next++;
1380 }
1381 return next;
1382}
1383
1384int KoTextString::previousCursorPosition( int prev )
1385{
1386 if ( bidiDirty )
1387 checkBidi();
1388
1389 const KoTextStringChar *c = data.data();
1390
1391 if ( prev ) {
1392 prev--;
1393 while ( prev && !c[prev].charStop )
1394 prev--;
1395 }
1396 return prev;
1397}
1398
1399bool KoTextString::validCursorPosition( int idx )
1400{
1401 if ( bidiDirty )
1402 checkBidi();
1403
1404 return (at( idx ).charStop);
1405}
1406
1408
1409void KoTextStringChar::setFormat( KoTextFormat *f, bool setFormatAgain )
1410{
1411 if ( type == Regular ) {
1412 d.format = f;
1413 } else {
1414 if ( !d.custom ) {
1415 d.custom = new CustomData;
1416 d.custom->custom = 0;
1417 }
1418 d.custom->format = f;
1419 if ( d.custom->custom && setFormatAgain )
1420 d.custom->custom->setFormat( f );
1421 }
1422}
1423
1424void KoTextStringChar::setCustomItem( KoTextCustomItem *i )
1425{
1426 if ( type == Regular ) {
1427 KoTextFormat *f = format();
1428 d.custom = new CustomData;
1429 d.custom->format = f;
1430 type = Custom;
1431 } else {
1432 delete d.custom->custom;
1433 }
1434 d.custom->custom = i;
1435}
1436
1437void KoTextStringChar::loseCustomItem() // setRegular() might be a better name
1438{
1439 if ( isCustom() ) {
1440 KoTextFormat *f = d.custom->format;
1441 d.custom->custom = 0;
1442 delete d.custom;
1443 type = Regular;
1444 d.format = f;
1445 }
1446}
1447
1448KoTextStringChar::~KoTextStringChar()
1449{
1450 if ( format() )
1451 format()->removeRef();
1452 switch ( type ) {
1453 case Custom:
1454 delete d.custom; break;
1455 default:
1456 break;
1457 }
1458}
1459
1460int KoTextStringChar::height() const
1461{
1462 return !isCustom() ? format()->height() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->height : 0 );
1463}
1464
1465int KoTextStringChar::ascent() const
1466{
1467 return !isCustom() ? format()->ascent() : ( customItem()->placement() == KoTextCustomItem::PlaceInline ? customItem()->ascent() : 0 );
1468}
1469
1470int KoTextStringChar::descent() const
1471{
1472 return !isCustom() ? format()->descent() : 0;
1473}
1474
1475// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1476
1477KoTextFormatterBase::KoTextFormatterBase()
1478 : wrapColumn( -1 ), //wrapEnabled( true ),
1479 m_bViewFormattingChars( false ),
1480 biw( true /*default in kotext*/ )
1481{
1482}
1483
1484#ifdef BIDI_DEBUG
1485#include <iostream>
1486#endif
1487
1488// collects one line of the paragraph and transforms it to visual order
1489KoTextParagLineStart *KoTextFormatterBase::bidiReorderLine( KoTextParag * /*parag*/, KoTextString *text, KoTextParagLineStart *line,
1490 KoTextStringChar *startChar, KoTextStringChar *lastChar, int align, int space )
1491{
1492 int start = (startChar - &text->at(0));
1493 int last = (lastChar - &text->at(0) );
1494 //kdDebug(32500) << "doing BiDi reordering from " << start << " to " << last << "!" << endl;
1495
1496 KoBidiControl *control = new KoBidiControl( line->context(), line->status );
1497 TQString str;
1498 str.setUnicode( 0, last - start + 1 );
1499 // fill string with logically ordered chars.
1500 KoTextStringChar *ch = startChar;
1501 TQChar *qch = (TQChar *)str.unicode();
1502 while ( ch <= lastChar ) {
1503 *qch = ch->c;
1504 qch++;
1505 ch++;
1506 }
1507 int x = startChar->x;
1508
1509 TQPtrList<KoTextRun> *runs;
1510 runs = KoComplexText::bidiReorderLine(control, str, 0, last - start + 1,
1511 (text->isRightToLeft() ? TQChar::DirR : TQChar::DirL) );
1512
1513 // now construct the reordered string out of the runs...
1514
1515 int numSpaces = 0;
1516 // set the correct alignment. This is a bit messy....
1517 if( align == TQt::AlignAuto ) {
1518 // align according to directionality of the paragraph...
1519 if ( text->isRightToLeft() )
1520 align = TQt::AlignRight;
1521 }
1522
1523 if ( align & TQt::AlignHCenter )
1524 x += space/2;
1525 else if ( align & TQt::AlignRight )
1526 x += space;
1527 else if ( align & TQt::AlignJustify ) {
1528 for ( int j = start; j < last; ++j ) {
1529 if( isBreakable( text, j ) ) {
1530 numSpaces++;
1531 }
1532 }
1533 }
1534 int toAdd = 0;
1535 bool first = true;
1536 KoTextRun *r = runs->first();
1537 int xmax = -0xffffff;
1538 while ( r ) {
1539 if(r->level %2) {
1540 // odd level, need to reverse the string
1541 int pos = r->stop + start;
1542 while(pos >= r->start + start) {
1543 KoTextStringChar *c = &text->at(pos);
1544 if( numSpaces && !first && isBreakable( text, pos ) ) {
1545 int s = space / numSpaces;
1546 toAdd += s;
1547 space -= s;
1548 numSpaces--;
1549 } else if ( first ) {
1550 first = false;
1551 if ( c->c == ' ' )
1552 x -= c->format()->width( ' ' );
1553 }
1554 c->x = x + toAdd;
1555 c->rightToLeft = true;
1556 c->startOfRun = false;
1557 int ww = 0;
1558 if ( c->c.unicode() >= 32 || c->c == '\t' || c->c == '\n' || c->isCustom() ) {
1559 ww = c->width;
1560 } else {
1561 ww = c->format()->width( ' ' );
1562 }
1563 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
1564 x += ww;
1565 pos--;
1566 }
1567 } else {
1568 int pos = r->start + start;
1569 while(pos <= r->stop + start) {
1570 KoTextStringChar* c = &text->at(pos);
1571 if( numSpaces && !first && isBreakable( text, pos ) ) {
1572 int s = space / numSpaces;
1573 toAdd += s;
1574 space -= s;
1575 numSpaces--;
1576 } else if ( first ) {
1577 first = false;
1578 if ( c->c == ' ' )
1579 x -= c->format()->width( ' ' );
1580 }
1581 c->x = x + toAdd;
1582 c->rightToLeft = false;
1583 c->startOfRun = false;
1584 int ww = 0;
1585 if ( c->c.unicode() >= 32 || c->c == '\t' || c->isCustom() ) {
1586 ww = c->width;
1587 } else {
1588 ww = c->format()->width( ' ' );
1589 }
1590 //kdDebug(32500) << "setting char " << pos << " at pos " << x << endl;
1591 if ( xmax < x + toAdd + ww ) xmax = x + toAdd + ww;
1592 x += ww;
1593 pos++;
1594 }
1595 }
1596 text->at( r->start + start ).startOfRun = true;
1597 r = runs->next();
1598 }
1599
1600 line->w = xmax + 10;
1601 KoTextParagLineStart *ls = new KoTextParagLineStart( control->context, control->status );
1602 delete control;
1603 delete runs;
1604 return ls;
1605}
1606
1607bool KoTextFormatterBase::isStretchable( KoTextString *string, int pos ) const
1608{
1609 if ( string->at( pos ).c == TQChar(160) ) //non-breaking space
1610 return true;
1611 KoTextStringChar& chr = string->at( pos );
1612 return chr.whiteSpace;
1613 //return isBreakable( string, pos );
1614}
1615
1616bool KoTextFormatterBase::isBreakable( KoTextString *string, int pos ) const
1617{
1618 //if (string->at(pos).nobreak)
1619 // return false;
1620 return (pos < string->length()-1 && string->at(pos+1).softBreak);
1621}
1622
1623void KoTextParag::insertLineStart( int index, KoTextParagLineStart *ls )
1624{
1625 // This tests if we break at the same character in more than one line,
1626 // i.e. there no space even for _one_ char in a given line.
1627 // However this shouldn't happen, KoTextFormatter prevents it, otherwise
1628 // we could loop forever (e.g. if one char is wider than the page...)
1629#ifndef NDEBUG
1630 TQMap<int, KoTextParagLineStart*>::Iterator it;
1631 if ( ( it = lineStarts.find( index ) ) == lineStarts.end() ) {
1632 lineStarts.insert( index, ls );
1633 } else {
1634 kdWarning(32500) << "insertLineStart: there's already a line for char index=" << index << endl;
1635 delete *it;
1636 lineStarts.remove( it );
1637 lineStarts.insert( index, ls );
1638 }
1639#else // non-debug code, take the fast route
1640 lineStarts.insert( index, ls );
1641#endif
1642}
1643
1644
1645/* Standard pagebreak algorithm using KoTextFlow::adjustFlow. Returns
1646 the shift of the paragraphs bottom line.
1647 */
1648int KoTextFormatterBase::formatVertically( KoTextDocument* doc, KoTextParag* parag )
1649{
1650 int oldHeight = parag->rect().height();
1651 TQMap<int, KoTextParagLineStart*>& lineStarts = parag->lineStartList();
1652 TQMap<int, KoTextParagLineStart*>::Iterator it = lineStarts.begin();
1653 int h = doc->addMargins() ? parag->topMargin() : 0;
1654 for ( ; it != lineStarts.end() ; ++it ) {
1655 KoTextParagLineStart * ls = it.data();
1656 ls->y = h;
1657 KoTextStringChar *c = &parag->string()->at(it.key());
1658 if ( c && c->customItem() && c->customItem()->ownLine() ) {
1659 int h = c->customItem()->height;
1660 c->customItem()->pageBreak( parag->rect().y() + ls->y + ls->baseLine - h, doc->flow() );
1661 int delta = c->customItem()->height - h;
1662 ls->h += delta;
1663 if ( delta )
1664 parag->setMovedDown( true );
1665 } else {
1666 int shift = doc->flow()->adjustFlow( parag->rect().y() + ls->y, ls->w, ls->h );
1667 ls->y += shift;
1668 if ( shift )
1669 parag->setMovedDown( true );
1670 }
1671 h = ls->y + ls->h;
1672 }
1673 int m = parag->bottomMargin();
1674 if ( parag->next() && doc && !doc->addMargins() )
1675 m = TQMAX( m, parag->next()->topMargin() );
1676 //if ( parag->next() && parag->next()->isLineBreak() )
1677 // m = 0;
1678 h += m;
1679 parag->setHeight( h );
1680 return h - oldHeight;
1681}
1682
1683// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1684
1685KoTextCustomItem::KoTextCustomItem( KoTextDocument *p )
1686 : width(-1), height(0), parent(p), xpos(0), ypos(-1), parag(0)
1687{
1688 m_deleted = false; // added for kotext
1689}
1690
1691KoTextCustomItem::~KoTextCustomItem()
1692{
1693}
1694
1695KoTextFlow::KoTextFlow()
1696{
1697 w = 0;
1698 leftItems.setAutoDelete( false );
1699 rightItems.setAutoDelete( false );
1700}
1701
1702KoTextFlow::~KoTextFlow()
1703{
1704}
1705
1706void KoTextFlow::clear()
1707{
1708 leftItems.clear();
1709 rightItems.clear();
1710}
1711
1712// Called by KoTextDocument::setWidth
1713void KoTextFlow::setWidth( int width )
1714{
1715 w = width;
1716}
1717
1718void KoTextFlow::adjustMargins( int, int, int, int&, int&, int& pageWidth, KoTextParag* )
1719{
1720 pageWidth = w;
1721}
1722
1723
1724int KoTextFlow::adjustFlow( int /*y*/, int, int /*h*/ )
1725{
1726 return 0;
1727}
1728
1729void KoTextFlow::unregisterFloatingItem( KoTextCustomItem* item )
1730{
1731 leftItems.removeRef( item );
1732 rightItems.removeRef( item );
1733}
1734
1735void KoTextFlow::registerFloatingItem( KoTextCustomItem* item )
1736{
1737 if ( item->placement() == KoTextCustomItem::PlaceRight ) {
1738 if ( !rightItems.contains( item ) )
1739 rightItems.append( item );
1740 } else if ( item->placement() == KoTextCustomItem::PlaceLeft &&
1741 !leftItems.contains( item ) ) {
1742 leftItems.append( item );
1743 }
1744}
1745
1746int KoTextFlow::availableHeight() const
1747{
1748 return -1; // no limit
1749}
1750
1751void KoTextFlow::drawFloatingItems( TQPainter* p, int cx, int cy, int cw, int ch, const TQColorGroup& cg, bool selected )
1752{
1753 KoTextCustomItem *item;
1754 for ( item = leftItems.first(); item; item = leftItems.next() ) {
1755 if ( item->x() == -1 || item->y() == -1 )
1756 continue;
1757 item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
1758 }
1759
1760 for ( item = rightItems.first(); item; item = rightItems.next() ) {
1761 if ( item->x() == -1 || item->y() == -1 )
1762 continue;
1763 item->draw( p, item->x(), item->y(), cx, cy, cw, ch, cg, selected );
1764 }
1765}
1766
1767//void KoTextFlow::setPageSize( int ps ) { pagesize = ps; }
1768bool KoTextFlow::isEmpty() { return leftItems.isEmpty() && rightItems.isEmpty(); }
KoTextCustomItem is the base class for custom items (i.e.
Each character (KoTextStringChar) points to a KoTextFormat that defines the formatting of that charac...
int width(const TQChar &c) const
Return the width of one char (from a string, not necessarily from a paragraph) in LU pixels.
TDEAction * next(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)