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