1 /****************************************************************************
2 **
3 ** Copyright (C) 2006-2008 fullmetalcoder <fullmetalcoder@hotmail.fr>
4 **
5 ** This file is part of the Edyuk project <http://edyuk.org>
6 **
7 ** This file may be used under the terms of the GNU General Public License
8 ** version 3 as published by the Free Software Foundation and appearing in the
9 ** file GPL.txt included in the packaging of this file.
10 **
11 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13 **
14 ****************************************************************************/
15
16 #include "qdocument.h"
17
18 /*
19 Document model :
20
21 Goals :
22 * KISS : Keep It Simple Stupid
23 * FAST : ...
24 * LIGHTWEIGHT : reduce memory usage
25 * FLEXIBLE : allow punctual bidi through QTextLayout
26
27 Implementation :
28 QDocument
29 QDocumentPrivate
30
31 QDocumentLine and QDocumentLineHandle => equivalent of QTextBlock
32 QDocumentCursor and QDocumentCursorHandle => equivalent of QTextCursor
33
34 Note :
35 The KISS principle has been kinda mistreated in the private API due to
36 the addition of some complex features which where not planned to be
37 supported when defining the goals (e.g line wrapping, variable width
38 fonts, ...). Such a compromission is affordable but should be avoided
39 whenever possible in the future. And of course the public API should
40 never suffer from such a thing.
41 */
42
43 /*!
44 \ingroup document
45 @{
46 */
47
48 /*!
49 \class QDocument
50
51 \brief A class storing a document
52
53 QCE uses an architecture very similar to that of QTextEdit/QTextDocument
54 which closely ressembles model/view. The document holds all the textual
55 and formatting data. It offers some (mostly indirect) ways of modifying
56 its content and is usable without any GUI.
57
58 In QCE, a document is merely a list of QDocumentLine objects on which some
59 extra formatting can be applied and which can be wrapped/hidden in various
60 ways.
61
62 The document model has been designed with three goals in mind :
63 <ul>
64 <li>performance
65 <li>flexibility
66 <li>low memory usage
67 </ul>
68
69 QDocument supports Bidi by using QTextLayout on lines that require it and
70 prefers custom rendering in other cases to achieve the above goals.
71
72 All the actual text editing is done through QDocumentCursor objects.
73
74 \see QDocumentLine
75 \see QDocumentCursor
76 */
77
78 #include "qdocument_p.h"
79 #include "qdocumentcommand.h"
80
81 #include "qformat.h"
82 #include "qformatscheme.h"
83 #include "qlanguagedefinition.h"
84 #include "qlinemarksinfocenter.h"
85
86 #include <QPen>
87 #include <QTime>
88 #include <QRect>
89 #include <QLine>
90 #include <QPainter>
91 #include <QPrinter>
92 #include <QTextStream>
93 #include <QTextLayout>
94 #include <QApplication>
95 #include <QVarLengthArray>
96 #include <QMessageBox>
97
98 static int m_spaceSignOffset = 2;
99
100 static QPoint m_spaceSign[] = {
101 QPoint(2, -1),
102 QPoint(2, 0),
103 QPoint(3, 0)
104 };
105
isWord(QChar c)106 inline static bool isWord(QChar c)
107 { return c.isLetterOrNumber() || (c == QLatin1Char('_')); }
108
screenLength(const QChar * d,int l,int tabStop)109 int QDocument::screenLength(const QChar *d, int l, int tabStop)
110 {
111 if ( tabStop == 1 )
112 return l;
113
114 int idx, column = idx = 0;
115
116 while ( idx < l )
117 {
118 QChar c = d[idx];
119
120 if ( c == QLatin1Char('\t') )
121 {
122 int taboffset = tabStop - (column % tabStop);
123 column += taboffset;
124 } else {
125 ++column;
126 }
127
128 ++idx;
129 }
130
131 //qDebug("%s : %i", qPrintable(QString(d, l)), column);
132
133 return column;
134 }
135
screenable(const QChar * d,int l,int tabStop)136 QString QDocument::screenable(const QChar *d, int l, int tabStop)
137 {
138 if ( tabStop == 1 )
139 return QString(d, l);
140
141 QString fragment;
142 int idx, column = idx = 0;
143
144 while ( idx < l )
145 {
146 QChar c = d[idx];
147
148 if ( c == QLatin1Char('\t') )
149 {
150 int taboffset = tabStop - (column % tabStop);
151
152 fragment += QString(taboffset, QLatin1Char(' '));
153 column += taboffset;
154 } else {
155 fragment += c;
156 ++column;
157 }
158
159 ++idx;
160 }
161
162 return fragment;
163 }
164
165 struct InitStruct
166 {
InitStructInitStruct167 InitStruct()
168 {
169 qRegisterMetaType<QDocumentIterator>("QDocumentIterator");
170 qRegisterMetaType<QDocumentConstIterator>("QDocumentConstIterator");
171 }
172 };
173
174 static InitStruct init_inst;
175
176 /*!
177 \brief ctor
178 */
QDocument(QObject * p)179 QDocument::QDocument(QObject *p)
180 : QObject(p), m_impl(new QDocumentPrivate(this))
181 {
182 if ( !QDocumentPrivate::m_font )
183 {
184 // must not happen if config dialog plugged in...
185 //setFont(QFont("Monospace", 10));
186 setFont(QFont("Sans serif", 10));
187 }
188
189 setText(QString());
190 setLineEnding(QDocument::Conservative);
191
192 connect(&(m_impl->m_commands) , SIGNAL( cleanChanged(bool) ),
193 this , SIGNAL( cleanChanged(bool) ) );
194
195 connect(&(m_impl->m_commands) , SIGNAL( canUndoChanged(bool) ),
196 this , SIGNAL( undoAvailable(bool) ) );
197
198 connect(&(m_impl->m_commands) , SIGNAL( canRedoChanged(bool) ),
199 this , SIGNAL( redoAvailable(bool) ) );
200
201 connect(this ,
202 SIGNAL( lineDeleted(QDocumentLineHandle*) ),
203 QLineMarksInfoCenter::instance(),
204 SLOT ( lineDeleted(QDocumentLineHandle*) ) );
205
206 }
207
208 /*!
209 \brief dtor
210 */
~QDocument()211 QDocument::~QDocument()
212 {
213 delete m_impl;
214 }
215
216 /*!
217 \brief Clear the content of the document
218 */
clear()219 void QDocument::clear()
220 {
221 setText(QString());
222 }
223
224 /*!
225 \return whether there commands to undo on the command stack
226 */
canUndo() const227 bool QDocument::canUndo() const
228 {
229 return m_impl ? m_impl->m_commands.canUndo() : false;
230 }
231
232 /*!
233 \return whether there are commands to redo on the command stack
234 */
canRedo() const235 bool QDocument::canRedo() const
236 {
237 return m_impl ? m_impl->m_commands.canRedo() : false;
238 }
239
240 /*!
241 \brief Undo the last editing operation
242 */
undo()243 void QDocument::undo()
244 {
245 if ( m_impl )
246 {
247 m_impl->m_commands.undo();
248 m_impl->m_lastModified = QDateTime::currentDateTime();
249 }
250 }
251
252 /*!
253 \brief Redo the last undone editing operation
254 */
redo()255 void QDocument::redo()
256 {
257 if ( m_impl )
258 {
259 m_impl->m_commands.redo();
260 m_impl->m_lastModified = QDateTime::currentDateTime();
261 }
262
263 }
264
265 /*!
266 \return The content of the document
267 \param mode extra processing to perform on text
268 */
text(int mode) const269 QString QDocument::text(int mode) const
270 {
271 QString s;
272
273 if ( !m_impl || m_impl->m_lines.isEmpty() )
274 return s;
275
276 int line = 0;
277 int prevIndent = 0, curIndent = 0, nextIndent = m_impl->m_lines.at(0)->nextNonSpaceChar(0);
278
279 if ( nextIndent < 0 )
280 nextIndent = 0;
281
282 foreach ( QDocumentLineHandle *l, m_impl->m_lines )
283 {
284 prevIndent = curIndent;
285 curIndent = nextIndent;
286 nextIndent = ++line < m_impl->m_lines.count() ? m_impl->m_lines.at(line)->nextNonSpaceChar(0) : 0;
287
288 if ( nextIndent < 0 )
289 nextIndent = 0;
290
291 QString buf = l->text();
292 int avgIndent = qMax(prevIndent, nextIndent);
293
294 if ( (mode & RestoreTrailingIndent) && buf.isEmpty() && avgIndent )
295 {
296 buf = QString(avgIndent, '\t');
297 } else if ( mode & RemoveTrailingWS ) {
298
299 int len = 0, idx = buf.length();
300
301 while ( --idx >= 0 )
302 {
303 if ( !buf.at(idx).isSpace() )
304 break;
305
306 ++len;
307 }
308
309 ++idx;
310
311 if ( len && (idx || !(mode & PreserveIndent)) )
312 buf.remove(idx, len);
313 }
314
315 s += buf + m_impl->m_lineEndingString;
316 }
317
318 //s.chop(m_impl->m_lineEndingString.count());
319 return s;
320 }
321
322 /*!
323 \return The content of the document
324 \param removeTrailing whether to remove trailing whitespaces
325 \param preserveIndent whether to keep trailing whitespaces when they are indent
326 */
text(bool removeTrailing,bool preserveIndent) const327 QString QDocument::text(bool removeTrailing, bool preserveIndent) const
328 {
329 int mode = 0;
330
331 if ( removeTrailing )
332 mode |= RemoveTrailingWS;
333
334 if ( preserveIndent )
335 mode |= PreserveIndent;
336
337 return text(mode);
338 }
339
340 /*!
341 \brief Set the content of the document
342 */
setText(const QString & s)343 void QDocument::setText(const QString& s)
344 {
345 if ( !m_impl )
346 return;
347
348 int last = 0, idx = 0;
349
350 m_impl->m_deleting = true;
351
352 //qDeleteAll(m_impl->m_lines);
353 foreach ( QDocumentLineHandle *h, m_impl->m_lines )
354 {
355 h->m_doc = 0;
356 h->deref();
357 }
358
359 QDocumentCommand::discardHandlesFromDocument(this);
360
361 m_impl->m_lines.clear();
362 m_impl->m_marks.clear();
363 m_impl->m_status.clear();
364 m_impl->m_hidden.clear();
365 m_impl->m_wrapped.clear();
366 m_impl->m_matches.clear();
367 m_impl->m_largest.clear();
368 m_impl->m_commands.clear();
369
370 m_impl->m_deleting = false;
371
372 if ( s.isEmpty() )
373 m_impl->m_lines << new QDocumentLineHandle(QString(), this);
374
375 m_impl->_nix = 0;
376 m_impl->_dos = 0;
377 m_impl->_mac = 0;
378
379 while ( idx < s.length() )
380 {
381 if ( s.at(idx) == '\n' )
382 {
383 if ( (idx > 0) && (s.at(idx - 1) == '\r') )
384 {
385 ++(m_impl->_dos);
386
387 m_impl->m_lines << new QDocumentLineHandle(
388 s.mid(last, idx - last - 1),
389 this
390 );
391 } else {
392 ++(m_impl->_nix);
393
394 m_impl->m_lines << new QDocumentLineHandle(
395 s.mid(last, idx - last),
396 this
397 );
398 }
399
400 last = ++idx;
401 } else if ( s.at(idx) == '\r' ) {
402 ++idx;
403 if ( idx >= s.length() || (s.at(idx) != '\n') )
404 {
405 ++(m_impl->_mac);
406
407 m_impl->m_lines << new QDocumentLineHandle(
408 s.mid(last, idx - last - 1),
409 this
410 );
411
412 last = idx;
413 }
414 } else {
415 ++idx;
416 }
417 }
418
419 if ( idx != last )
420 {
421 m_impl->m_lines << new QDocumentLineHandle(
422 s.mid(last, s.length() - last),
423 this
424 );
425
426 }
427 //
428 // if ( (idx > 0) && ((idx - 1) < s.length()) && ((s.at(idx - 1) == '\n') || (s.at(idx - 1) == '\r')) )
429 // m_impl->m_lines << new QDocumentLineHandle(this);
430 //
431
432 //qDebug("[one go] dos : %i; nix : %i", m_impl->_dos, m_impl->_nix);
433
434 m_impl->m_lastModified = QDateTime::currentDateTime();
435
436 if ( lineEnding() == Conservative )
437 setLineEnding(Conservative);
438
439 m_impl->setWidth();
440 m_impl->setHeight();
441
442 emit lineCountChanged(lineCount());
443
444 m_impl->emitContentsChange(0, m_impl->m_lines.count());
445 }
446
447 /*!
448 \brief Start a chunk loading
449
450 It is possible to load document contents in one piece
451 or by chunks. To achieve the later you have to proceed as follows :
452
453 \code
454 QDocument doc;
455 doc.startChunkLoading();
456
457 // fetch data and add it using doc.addChunk();
458
459 doc.stopChunkLoading();
460 \endcode
461
462 \see addChunk(const QString&)
463 \see stopChunkLoading()
464 */
startChunkLoading()465 void QDocument::startChunkLoading()
466 {
467 if ( !m_impl )
468 return;
469
470 m_impl->m_deleting = true;
471
472 //qDeleteAll(m_impl->m_lines);
473 foreach ( QDocumentLineHandle *h, m_impl->m_lines )
474 {
475 h->m_doc = 0;
476 h->deref();
477 }
478
479 m_impl->m_lines.clear();
480 m_impl->m_marks.clear();
481 m_impl->m_status.clear();
482 m_impl->m_hidden.clear();
483 m_impl->m_wrapped.clear();
484 m_impl->m_matches.clear();
485 m_impl->m_largest.clear();
486 m_impl->m_commands.clear();
487
488 m_impl->m_deleting = false;
489
490 m_impl->_nix = 0;
491 m_impl->_dos = 0;
492 m_impl->_mac = 0;
493
494 m_leftOver.clear();
495 }
496
497 /*!
498 \brief Stop chunk loading
499
500 \see startChunkLoading()
501 */
stopChunkLoading()502 void QDocument::stopChunkLoading()
503 {
504 if ( m_leftOver.count() )
505 {
506 m_impl->m_lines << new QDocumentLineHandle(
507 m_leftOver,
508 this
509 );
510
511 m_leftOver.clear();
512
513 } else {
514 //m_impl->m_lines << new QDocumentLineHandle(this);
515 }
516
517 //qDebug("[chunk] dos : %i; nix : %i", m_impl->_dos, m_impl->_nix);
518
519 m_impl->m_lastModified = QDateTime::currentDateTime();
520
521 if ( lineEnding() == Conservative )
522 setLineEnding(Conservative);
523
524 m_impl->setWidth();
525 m_impl->setHeight();
526
527 emit lineCountChanged(lineCount());
528
529 emit m_impl->emitContentsChange(0, m_impl->m_lines.count());
530 }
531
532 /*!
533 \return The format scheme used by the document
534 */
formatScheme() const535 QFormatScheme* QDocument::formatScheme() const
536 {
537 return m_impl ? m_impl->m_formatScheme : 0;
538 }
539
540 /*!
541 \brief Set the format scheme used by the document
542 */
setFormatScheme(QFormatScheme * f)543 void QDocument::setFormatScheme(QFormatScheme *f)
544 {
545 if ( m_impl )
546 m_impl->setFormatScheme(f);
547 }
548
549 /*!
550 \return the language definition set to the document
551 */
languageDefinition() const552 QLanguageDefinition* QDocument::languageDefinition() const
553 {
554 return m_impl ? m_impl->m_language : 0;
555 }
556
557 /*!
558 \brief Set the language definition
559 */
setLanguageDefinition(QLanguageDefinition * f)560 void QDocument::setLanguageDefinition(QLanguageDefinition *f)
561 {
562 if ( m_impl )
563 m_impl->m_language = f;
564 }
565
566 /*!
567 \brief Update the formatting of the whole document
568 This function is only useful when changing the language definition
569 of a non-empty document. Make sure you do not call it more often
570 than needed.
571 */
highlight()572 void QDocument::highlight()
573 {
574 if ( m_impl )
575 m_impl->emitContentsChange(0, lines());
576 }
577
578 /*!
579 \brief Add a chunk of text to the document
580 */
addChunk(const QString & txt)581 void QDocument::addChunk(const QString& txt)
582 {
583 if ( !m_impl || txt.isEmpty() )
584 return;
585
586 m_leftOver += txt;
587 int idx = 0, last = 0;
588
589 while ( idx < m_leftOver.length() )
590 {
591 if ( m_leftOver.at(idx) == '\n' )
592 {
593 if ( (idx > 0) && (m_leftOver.at(idx - 1) == '\r') )
594 {
595 ++(m_impl->_dos);
596
597 m_impl->m_lines << new QDocumentLineHandle(
598 m_leftOver.mid(last, idx - last - 1),
599 this
600 );
601 } else {
602 ++(m_impl->_nix);
603
604 m_impl->m_lines << new QDocumentLineHandle(
605 m_leftOver.mid(last, idx - last),
606 this
607 );
608 }
609
610 last = ++idx;
611 } else if ( m_leftOver.at(idx) == '\r' ) {
612 ++idx;
613 if ( idx >= m_leftOver.length() || (m_leftOver.at(idx) != '\n') )
614 {
615 ++(m_impl->_mac);
616
617 m_impl->m_lines << new QDocumentLineHandle(
618 m_leftOver.mid(last, idx - last - 1),
619 this
620 );
621
622 last = idx;
623 }
624 } else {
625 ++idx;
626 }
627 }
628
629 if ( idx != last )
630 m_leftOver = m_leftOver.mid(last);
631 else
632 m_leftOver.clear();
633
634 }
635
636 /*!
637 \brief Print the content of the document
638 \param pr printer to use
639
640 \note the printer MUST be initialized (probably using a printing dialog)
641 */
print(QPrinter * pr)642 void QDocument::print(QPrinter *pr)
643 {
644 QRect fit = pr->pageRect();
645
646 if ( pr->printRange() == QPrinter::Selection )
647 {
648 qWarning("printing selection not implemented yet");
649 return;
650 }
651
652 if ( fit.width() < width() )
653 {
654 // TODO: got to temporarily wrap text to fit page size
655
656 qWarning("temporary wrapping not implementated yet");
657 }
658
659 const int lineCount = lines();
660 const int linesPerPage = fit.height() / m_impl->m_lineSpacing;
661 int pageCount = lineCount / linesPerPage;
662
663 if ( lineCount % linesPerPage )
664 ++pageCount;
665
666 //qDebug("%i lines per page -> %i pages", linesPerPage, pageCount);
667
668 const int pageWidth = fit.width();
669 const int pageHeight = linesPerPage * m_impl->m_lineSpacing;
670
671 int firstPage = pr->fromPage(), lastPage = pr->toPage();
672
673 if ( !lastPage )
674 lastPage = pageCount - 1;
675
676 QPainter p(pr);
677 PaintContext cxt;
678 cxt.xoffset = 0;
679 cxt.yoffset = firstPage * pageHeight;
680 cxt.width = pageWidth;
681 cxt.height = pageHeight - m_impl->m_lineSpacing;
682 cxt.palette = QApplication::palette();
683 cxt.fillCursorRect = false;
684 cxt.blinkingCursor = false;
685
686 for ( int i = firstPage; i <= lastPage; ++i )
687 {
688 draw(&p, cxt);
689
690 cxt.yoffset += pageHeight;
691
692 if ( i != lastPage )
693 {
694 pr->newPage();
695 p.translate(0, -pageHeight);
696 }
697 }
698 }
699
700 /*!
701 \return The line ending policy of the document
702
703 The line ending policy determines how line endings
704 are used when saving the document (which includes
705 fetching the document's text()).
706
707 It can either be conservative (auto detect upon loading
708 and do not modify when saving later on) or enforce
709 a particular line ending (either local line ending
710 or a specific value).
711 */
lineEnding() const712 QDocument::LineEnding QDocument::lineEnding() const
713 {
714 return m_impl ? m_impl->m_lineEnding : Local;
715 }
716
717 /*!
718 \return the lin endings detected upon loading
719
720 This should only ever take the the Window of Linux value
721 if a document has been loaded. If no content has been
722 loaded it will fall back to Local.
723 */
originalLineEnding() const724 QDocument::LineEnding QDocument::originalLineEnding() const
725 {
726 if ( !m_impl )
727 return Local;
728
729 bool dosVSnix = m_impl->_dos > m_impl->_nix;
730 bool dosVSmac = m_impl->_dos > m_impl->_mac;
731 bool nixVSmac = m_impl->_nix > m_impl->_mac;
732
733 // nothing loaded thus far
734 if ( !(dosVSnix || dosVSmac || nixVSmac) )
735 return Local;
736
737 return dosVSnix ? (dosVSmac ? Windows : OldMac) : (nixVSmac ? Unix : OldMac);
738 }
739
740 /*!
741 \brief Set the line ending policy of the document
742 */
setLineEnding(LineEnding le)743 void QDocument::setLineEnding(LineEnding le)
744 {
745 if ( !m_impl )
746 return;
747
748 m_impl->m_lineEnding = le;
749 QString& les = m_impl->m_lineEndingString;
750
751 /*
752 Notes about "local" line endings :
753 * Dos & co : well defined CRLF
754 * Unix & co : well defined LF
755 * Mac : CR up until OS 9 LF afterwards (OSX is Unix-based)
756
757 Guess about macros (to be confirmed/infirmed) :
758 Q_OS_UNIX *should* be set under OSX
759 Q_OS_MAC *might* be set under OSX
760
761 => order #ifdefs to try avoiding troubles
762 */
763 #ifdef Q_OS_WIN
764 const QString loc_les = "\r\n";
765 #elif defined(Q_OS_UNIX)
766 const QString loc_les = "\n";
767 #elif defined(Q_OS_MAC)
768 const QString loc_les = "\r";
769 #else
770
771 const QString loc_les = "\n";
772 #endif
773
774 switch ( le )
775 {
776 case Conservative :
777 switch ( originalLineEnding() )
778 {
779 case Windows :
780 les = "\r\n";
781 break;
782 case OldMac :
783 les = "\r";
784 break;
785 case Unix :
786 les = "\n";
787 default:
788 les = loc_les;
789 break;
790 }
791 break;
792
793 case Unix :
794 les = "\n";
795 break;
796
797 case OldMac :
798 les = "\r";
799 break;
800
801 case Windows :
802 les = "\r\n";
803 break;
804
805 default :
806 les = "\n";
807 break;
808 }
809
810 emit lineEndingChanged(le);
811 }
812
813 /*!
814 \return the font used by ALL documents to render their content
815
816 This font is also used to do calculations (such as converting
817 (line, column) cursor position to (x, y) document position (or
818 the inverse transformation))
819
820 \note this limitation is historic and may disappear
821 in future versions
822 */
font()823 QFont QDocument::font()
824 {
825 return *(QDocumentPrivate::m_font);
826 }
827
828 /*!
829 \brief Set the font of ALL documents
830
831 \note this limitation is historic and may disappear
832 in future versions
833 */
setFont(const QFont & f)834 void QDocument::setFont(const QFont& f)
835 {
836 QDocumentPrivate::setFont(f);
837 //emit contentsChanged();
838 }
839
840 /*!
841 \return The font metrics used by ALL documents
842
843 \note this limitation is historic and may disappear
844 in future versions
845 */
fontMetrics()846 const QFontMetrics& QDocument::fontMetrics()
847 {
848 return *(QDocumentPrivate::m_fontMetrics);
849 }
850
851 /*!
852 \return The default tab stop common to ALL documents
853
854 \note this limitation is historic and may disappear
855 in future versions
856 */
tabStop()857 int QDocument::tabStop()
858 {
859 return QDocumentPrivate::m_defaultTabStop;
860 }
861
862 /*!
863 \brief Set the default tab stop common to all documents
864
865 \note this limitation is historic and may disappear
866 in future versions
867 */
setTabStop(int n)868 void QDocument::setTabStop(int n)
869 {
870 QDocumentPrivate::m_defaultTabStop = n;
871
872 foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents )
873 {
874 d->m_tabStop = n;
875 d->emitFormatsChanged();
876 }
877 }
878
879 /*!
880 \return the whitesapce display mode
881 */
showSpaces()882 QDocument::WhiteSpaceMode QDocument::showSpaces()
883 {
884 return QDocumentPrivate::m_showSpaces;
885 }
886
887 /*!
888 \brief Set the whitespace display mode
889 */
setShowSpaces(WhiteSpaceMode m)890 void QDocument::setShowSpaces(WhiteSpaceMode m)
891 {
892 QDocumentPrivate::m_showSpaces = m;
893
894 foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents )
895 d->emitFormatsChanged();
896
897 }
898
899 /*!
900 \brief Set the edit cursor
901
902 Archaic concept designed for use in QEditor
903 (is it still used???)
904 */
editCursor() const905 QDocumentCursor* QDocument::editCursor() const
906 {
907 return m_impl ? m_impl->m_editCursor : 0;
908 }
909
910 /*!
911 \brief Set the edit cursor
912
913 \see editCursor()
914 */
setEditCursor(QDocumentCursor * c)915 void QDocument::setEditCursor(QDocumentCursor *c)
916 {
917 if ( m_impl )
918 m_impl->m_editCursor = c;
919
920 }
921
922 /*!
923 \return the width of the document, in pixels
924
925 The width of the document is that of longest text line.
926 */
width() const927 int QDocument::width() const
928 {
929 return m_impl ? m_impl->m_width : 0;
930 }
931
932 /*!
933 \return the height of the document, in pixels
934 */
height() const935 int QDocument::height() const
936 {
937 return m_impl ? m_impl->m_height : 0;
938 }
939
940 /*!
941 \return The width constraint imposed on that document
942
943 Setting a width constraint on a document achieves line
944 wrapping.
945 */
widthConstraint() const946 int QDocument::widthConstraint() const
947 {
948 return (m_impl && m_impl->m_constrained) ? m_impl->m_width : 0;
949 }
950
951 /*!
952 \return the number of text lines in the document
953
954 The number of visual lines may differ from that of text
955 lines as soon as line wrapping and/or folding are enabled.
956
957 \deprecated Use lineCount() instead
958 */
lines() const959 int QDocument::lines() const
960 {
961 return m_impl ? m_impl->m_lines.count() : 0;
962 }
963
964 /*!
965 \return the number of text lines in the document
966
967 The number of visual lines may differ from that of text
968 lines as soon as line wrapping and/or folding are enabled.
969 */
lineCount() const970 int QDocument::lineCount() const
971 {
972 return m_impl ? m_impl->m_lines.count() : 0;
973 }
974
975 /*!
976 \return the number of visual lines in the document
977 \deprecated Use visualLineCount() instead
978 */
visualLines() const979 int QDocument::visualLines() const
980 {
981 return m_impl ? m_impl->visualLine(m_impl->m_lines.count() - 1) : 0;
982 }
983
984 /*!
985 \return the number of visual lines in the document
986 */
visualLineCount() const987 int QDocument::visualLineCount() const
988 {
989 return m_impl ? m_impl->visualLine(m_impl->m_lines.count() - 1) : 0;
990 }
991
992 /*!
993 \brief Convert a text (logical) line number int a visual line number
994
995 \note this is not a 1:1 mapping as logical lines can span over several visual lines
996 */
visualLineNumber(int textLineNumber) const997 int QDocument::visualLineNumber(int textLineNumber) const
998 {
999 return m_impl ? m_impl->visualLine(textLineNumber) : -1;
1000 }
1001
1002 /*!
1003 \brief Convert a visual line number int a text (logical) line number
1004
1005 \note this is not a 1:1 mapping as logical lines can span over several visual lines
1006 */
textLineNumber(int visualLineNumber) const1007 int QDocument::textLineNumber(int visualLineNumber) const
1008 {
1009 return m_impl ? m_impl->textLine(visualLineNumber) : -1;
1010 }
1011
1012 /*!
1013 \brief Clear the width constraint, if any
1014 */
clearWidthConstraint()1015 void QDocument::clearWidthConstraint()
1016 {
1017 if ( m_impl )
1018 m_impl->setWidth(0);
1019 }
1020
1021 /*!
1022 \brief Set a new width constraint
1023 \param width maximum width to allow
1024
1025 Passing a value inferior (or equal) to zero clear the width constraint, if any.
1026 */
setWidthConstraint(int width)1027 void QDocument::setWidthConstraint(int width)
1028 {
1029 if ( m_impl )
1030 m_impl->setWidth(qMax(0, width));
1031 }
1032
1033 /*!
1034 \return the line object at a given line number
1035 \param line Text line to acces
1036 */
line(int line) const1037 QDocumentLine QDocument::line(int line) const
1038 {
1039 return QDocumentLine(m_impl->at(line));
1040 }
1041
1042 /*!
1043 \return the line number corresponding to a given document y coordinate
1044 \param ypos Y document coordinate of the target
1045 \param wrap if not null, will be set to the wrap offset (position of the
1046 visual line among the sublines of the wrapped text line).
1047
1048 */
lineNumber(int ypos,int * wrap) const1049 int QDocument::lineNumber(int ypos, int *wrap) const
1050 {
1051 int ln = ypos / m_impl->m_lineSpacing;
1052
1053 return m_impl->textLine(ln, wrap);
1054 }
1055
1056 /*!
1057 \return the line object to which an iterator points
1058 */
line(QDocumentConstIterator iterator) const1059 QDocumentLine QDocument::line(QDocumentConstIterator iterator) const
1060 {
1061 return (m_impl && (m_impl->constEnd() != iterator)) ? QDocumentLine(*iterator) : QDocumentLine();
1062 }
1063
1064 /*!
1065 \return A cursor operating on the document, placed at a given position
1066 \param line target line number (text line)
1067 \param column target text column
1068 */
cursor(int line,int column) const1069 QDocumentCursor QDocument::cursor(int line, int column) const
1070 {
1071 return QDocumentCursor(const_cast<QDocument*>(this), line, column);
1072 }
1073
1074 /*!
1075 \return the document line which contains a given (document-wide) text position
1076
1077 \note The sole purpose of this method is to have an API close to that of QTextDocument.
1078 This method being ridiculously slow it is recommended to avoid it whenever possible.
1079 */
findLine(int & position) const1080 QDocumentLine QDocument::findLine(int& position) const
1081 {
1082 if ( !m_impl )
1083 return QDocumentLine();
1084
1085 return QDocumentLine(m_impl->lineForPosition(position));
1086 }
1087
1088 /*!
1089 \return The Y document coordinate of a given line
1090 \param ln textual line number
1091 */
y(int ln) const1092 int QDocument::y(int ln) const
1093 {
1094 if ( !m_impl )
1095 return -1;
1096
1097 return m_impl->m_lineSpacing * m_impl->visualLine(ln);
1098 }
1099
1100 /*!
1101 \overload
1102
1103 \return The Y document coordinate of a given line
1104 \param l line object
1105
1106 \note Significantly slower than the line number based version.
1107 */
y(const QDocumentLine & l) const1108 int QDocument::y(const QDocumentLine& l) const
1109 {
1110 qDebug("bad perf...");
1111
1112 return y(l.lineNumber());
1113 }
1114
1115 /*!
1116 \return the rectangle (in document position) occupied by the line
1117 \param line textual line number
1118
1119 \note the width of the returned rectangle is the DOCUMENT's width
1120 */
lineRect(int line) const1121 QRect QDocument::lineRect(int line) const
1122 {
1123 const int yoff = y(line);
1124
1125 return (yoff != -1) ? QRect(0, yoff, width(), this->line(line).lineSpan() * m_impl->m_lineSpacing) : QRect();
1126 }
1127
1128 /*!
1129 \overload
1130 \return the rectangle (in document position) occupied by the line
1131
1132 \note the width of the returned rectangle is the DOCUMENT's width
1133 \note Significantly slower than the line number based version.
1134 */
lineRect(const QDocumentLine & l) const1135 QRect QDocument::lineRect(const QDocumentLine& l) const
1136 {
1137 //return lineRect(l.lineNumber());
1138 const int yoff = y(l);
1139
1140 return (yoff != -1) ? QRect(0, yoff, width(), m_impl->m_lineSpacing) : QRect();
1141 }
1142
1143 /*!
1144 \return the line at a given document position
1145 */
lineAt(const QPoint & p) const1146 QDocumentLine QDocument::lineAt(const QPoint& p) const
1147 {
1148 if ( !m_impl )
1149 return QDocumentLine();
1150
1151 return line(lineNumber(p.y()));
1152 }
1153
1154 /*!
1155 \return a document iterator pointing to the first line
1156 */
begin() const1157 QDocumentConstIterator QDocument::begin() const
1158 {
1159 Q_ASSERT(m_impl);
1160
1161 return m_impl->m_lines.constBegin();
1162 }
1163
1164 /*!
1165 \return a document iterator pointing past the last line
1166 */
end() const1167 QDocumentConstIterator QDocument::end() const
1168 {
1169 Q_ASSERT(m_impl);
1170
1171 return m_impl->m_lines.constEnd();
1172 }
1173
1174 /*!
1175 \return a document iterator pointing to a given line
1176 */
iterator(int ln) const1177 QDocumentConstIterator QDocument::iterator(int ln) const
1178 {
1179 Q_ASSERT(m_impl);
1180
1181 return begin() + ln;
1182 }
1183
1184 /*!
1185 \overload
1186 \note If you can avoid using this method, do so unless performance really isn't an issue
1187 */
iterator(const QDocumentLine & l) const1188 QDocumentConstIterator QDocument::iterator(const QDocumentLine& l) const
1189 {
1190 Q_ASSERT(m_impl);
1191
1192 QDocumentConstIterator it = begin(), e = end();
1193
1194 while ( (*it != l.handle()) && (it != e) )
1195 ++it;
1196
1197 return it;
1198 }
1199
1200 /*!
1201 \brief Convert a document (or viewport) (x, y) position to a (line, column) cursor position
1202 \param p document position
1203 \param line where the line number will be stored
1204 \param column where the column (text position within line) will be stored
1205 */
cursorForDocumentPosition(const QPoint & p,int & line,int & column) const1206 void QDocument::cursorForDocumentPosition(const QPoint& p, int& line, int& column) const
1207 {
1208 if ( !m_impl )
1209 return;
1210
1211 int wrap = 0;
1212 line = lineNumber(p.y(), &wrap);
1213 QDocumentLine l = this->line(line);
1214
1215 if ( !l.isValid() )
1216 return;
1217
1218 //qDebug("%i %i", line, wrap);
1219 column = l.documentOffsetToCursor(p.x(), wrap * QDocumentPrivate::m_lineSpacing);
1220
1221 //qDebug("(%i, %i) -> (%i [+%i], %i)", p.x(), p.y(), line, wrap, column);
1222 }
1223
1224 /*!
1225 \return The cursor nearest to a document (x, y) position
1226 */
cursorAt(const QPoint & p) const1227 QDocumentCursor QDocument::cursorAt(const QPoint& p) const
1228 {
1229 int ln = -1, col = -1;
1230
1231 cursorForDocumentPosition(p, ln, col);
1232
1233 return QDocumentCursor(const_cast<QDocument*>(this), ln, col);
1234 }
1235
1236 /*!
1237 \brief Draw the contents of the document
1238 \param p painter to use
1239 \param cxt paint context (specifies what part of the document to draw, among other things)
1240 */
draw(QPainter * p,PaintContext & cxt)1241 void QDocument::draw(QPainter *p, PaintContext& cxt)
1242 {
1243 m_impl->draw(p, cxt);
1244 }
1245
1246 /*!
1247 \brief Execute a document command (editing operation)
1248 */
execute(QDocumentCommand * cmd)1249 void QDocument::execute(QDocumentCommand *cmd)
1250 {
1251 if ( m_impl && cmd )
1252 m_impl->execute(cmd);
1253
1254 }
1255
1256 /*!
1257 \return The default line ending policy
1258 */
defaultLineEnding()1259 QDocument::LineEnding QDocument::defaultLineEnding()
1260 {
1261 return QDocumentPrivate::m_defaultLineEnding;
1262 }
1263
1264 /*!
1265 \brief Set the default line ending policy
1266
1267 \note The line ending policy of existing documents is changed accordingly
1268 */
setDefaultLineEnding(QDocument::LineEnding le)1269 void QDocument::setDefaultLineEnding(QDocument::LineEnding le)
1270 {
1271 QDocumentPrivate::m_defaultLineEnding = le;
1272
1273 foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents )
1274 {
1275 d->m_doc->setLineEnding(le);
1276 }
1277 }
1278
1279 /*!
1280 \return The default format scheme used to draw document contents
1281 */
defaultFormatScheme()1282 QFormatScheme* QDocument::defaultFormatScheme()
1283 {
1284 return QDocumentPrivate::m_defaultFormatScheme;
1285 }
1286
1287 /*!
1288 \brief Set the default format scheme
1289
1290 \note Existing documents using the default format scheme will see their format scheme changed
1291 */
setDefaultFormatScheme(QFormatScheme * f)1292 void QDocument::setDefaultFormatScheme(QFormatScheme *f)
1293 {
1294 foreach ( QDocumentPrivate *d, QDocumentPrivate::m_documents )
1295 {
1296 if ( d->m_formatScheme == QDocumentPrivate::m_defaultFormatScheme )
1297 d->setFormatScheme(f);
1298 }
1299
1300 QDocumentPrivate::m_defaultFormatScheme = f;
1301 }
1302
1303 /*!
1304 \deprecated
1305 \brief Compatibility alias for defaultFormatScheme()
1306 */
formatFactory()1307 QFormatScheme* QDocument::formatFactory()
1308 {
1309 return defaultFormatScheme();
1310 }
1311
1312 /*!
1313 \deprecated
1314 \brief Compatibility alias for setDefaultFormatScheme()
1315 */
setFormatFactory(QFormatScheme * f)1316 void QDocument::setFormatFactory(QFormatScheme *f)
1317 {
1318 setDefaultFormatScheme(f);
1319 }
1320
1321 /*!
1322 \brief Begin a macro
1323
1324 Macro in QDocument map directly to macro in the underlying undo stack
1325 */
beginMacro()1326 void QDocument::beginMacro()
1327 {
1328 if ( m_impl )
1329 m_impl->beginChangeBlock();
1330
1331 }
1332
1333 /*!
1334 \brief End a macro
1335 */
endMacro()1336 void QDocument::endMacro()
1337 {
1338 if ( m_impl )
1339 m_impl->endChangeBlock();
1340
1341 }
1342
1343 /*!
1344 \brief Get an available group id for matches
1345 */
getNextGroupId()1346 int QDocument::getNextGroupId()
1347 {
1348 return m_impl ? m_impl->getNextGroupId() : -1;
1349 }
1350
releaseGroupId(int groupId)1351 void QDocument::releaseGroupId(int groupId)
1352 {
1353 if ( m_impl )
1354 m_impl->releaseGroupId(groupId);
1355 }
1356
1357 /*!
1358 \brief Clear matches
1359 */
clearMatches(int gid)1360 void QDocument::clearMatches(int gid)
1361 {
1362 if ( m_impl )
1363 {
1364 m_impl->clearMatches(gid);
1365 }
1366 }
1367
1368 /*!
1369 \brief Highlight the matched sequences
1370
1371 \note Both position are BEFORE the matched characters (cursor position).
1372 */
addMatch(int gid,int line,int pos,int len,int format)1373 void QDocument::addMatch(int gid, int line, int pos, int len, int format)
1374 {
1375 if ( m_impl )
1376 {
1377 m_impl->addMatch(gid, line, pos, len, format);
1378 }
1379 }
1380
1381 /*!
1382 \
1383 */
flushMatches(int gid)1384 void QDocument::flushMatches(int gid)
1385 {
1386 if ( m_impl )
1387 {
1388 m_impl->flushMatches(gid);
1389 }
1390 }
1391
1392 /*!
1393 \return Whether the document is in a clean state
1394
1395 This is undo stak terminology. A clean document is one
1396 whose undo stack is at the same index it was upon last
1397 save/load. In other words : clean = !modified
1398 */
isClean() const1399 bool QDocument::isClean() const
1400 {
1401 return m_impl ? m_impl->m_commands.isClean() : true;
1402 }
1403
1404 /*!
1405 \brief Set the document to clean state
1406
1407 This method does not go back to clean state but tell
1408 the stack that the current state is to be considered
1409 as the clean state.
1410 */
setClean()1411 void QDocument::setClean()
1412 {
1413 if ( m_impl )
1414 {
1415 m_impl->m_commands.setClean();
1416 //m_impl->m_status.clear();
1417
1418 QHash<QDocumentLineHandle*, QPair<int, int> >::iterator it = m_impl->m_status.begin();
1419
1420 while ( it != m_impl->m_status.end() )
1421 {
1422 it->second = it->first;
1423 ++it;
1424 }
1425 }
1426 }
1427
1428 /*!
1429 \return Whether a given line has been modified since last save/load
1430 */
isLineModified(const QDocumentLine & l) const1431 bool QDocument::isLineModified(const QDocumentLine& l) const
1432 {
1433 if ( !m_impl )
1434 return false;
1435
1436 QHash<QDocumentLineHandle*, QPair<int, int> >::const_iterator it = m_impl->m_status.constFind(l.handle());
1437
1438 //if ( it != m_impl->m_status.constEnd() )
1439 // qDebug("%i vs %i", it->first, it->second);
1440
1441 return it != m_impl->m_status.constEnd() ? it->first != it->second : false;
1442 }
1443
1444 /*!
1445 \return Whether a given line has been modified since last load
1446 */
hasLineEverBeenModified(const QDocumentLine & l) const1447 bool QDocument::hasLineEverBeenModified(const QDocumentLine& l) const
1448 {
1449 return m_impl ? m_impl->m_status.contains(l.handle()) : false;
1450 }
1451
1452 /*!
1453 \return the maximum number of marks on a single line
1454
1455 This is meant for the line mark panel to smartly adapt its size.
1456 */
maxMarksPerLine() const1457 int QDocument::maxMarksPerLine() const
1458 {
1459 return m_impl ? m_impl->maxMarksPerLine() : 0;
1460 }
1461
1462 /*!
1463 \brief Find the next mark of a given type
1464 \return the line on which a mark was found
1465 \param id mark identifier to find
1466 \param from line from which to start search
1467 \param until line until which to search
1468
1469 \a from and \a until can be negatives, in which case they
1470 indicate positions from the end of the document (i.e -1 is
1471 last line, -2 the line before last line and so on).
1472
1473 If \a until is inferior to \a from and no matching mark
1474 is found in the [from, end] range then the [0, until]
1475 range will also be searched.
1476 */
findNextMark(int id,int from,int until) const1477 int QDocument::findNextMark(int id, int from, int until) const
1478 {
1479 return m_impl ? m_impl->findNextMark(id, from, until) : -1;
1480 }
1481
1482 /*!
1483 \brief Find the previous mark of a given type
1484 \return the line on which a mark was found
1485 \param id mark identifier to find
1486 \param from line from which to start search
1487 \param until line until which to search
1488
1489 \a from and \a until can be negatives, in which case they
1490 indicate positions from the end of the document (i.e -1 is
1491 last line, -2 the line before last line and so on).
1492
1493 If \a until is superior to \a from and no matching mark
1494 is found in the [0, from] range then the [until, end]
1495 range will also be searched (both range being searched backward,
1496 of course).
1497 */
findPreviousMark(int id,int from,int until) const1498 int QDocument::findPreviousMark(int id, int from, int until) const
1499 {
1500 return m_impl ? m_impl->findPreviousMark(id, from, until) : -1;
1501 }
1502
1503 /*!
1504 \return the date/time of the last modification of the document
1505
1506 If the document has not been modified since its load the date/time
1507 of last modification (as reported by QFileInfo) will be returned.
1508 */
lastModified() const1509 QDateTime QDocument::lastModified() const
1510 {
1511 return m_impl ? m_impl->m_lastModified : QDateTime();
1512 }
1513
1514 /*!
1515 \brief set the date/time of the last modification of the document
1516
1517 You should not need to use that EVER. It is only provided for use
1518 in QEditor (and possibly in some panels).
1519 */
setLastModified(const QDateTime & d)1520 void QDocument::setLastModified(const QDateTime& d)
1521 {
1522 if ( m_impl )
1523 m_impl->m_lastModified = d;
1524 }
1525
1526 /////////////////////////
1527 // QDocumentLineHandle
1528 /////////////////////////
1529 //static quint32 q_line_counter = 0;
1530
1531 /*!
1532 \class QDocumentLineHandle
1533 \brief Private implementation of a document line
1534 */
1535
1536 /*!
1537 \
1538 */
QDocumentLineHandle(QDocument * d)1539 QDocumentLineHandle::QDocumentLineHandle(QDocument *d)
1540 : m_doc(d)
1541 #if QT_VERSION >= 0x040400
1542 , m_ref(1)
1543 #endif
1544 , m_indent(0)
1545 , m_state(QDocumentLine::LayoutDirty)
1546 , m_layout(0)
1547 {
1548 #if QT_VERSION < 0x040400
1549 m_ref.init(1);
1550 #endif
1551 }
1552
1553 /*!
1554 \
1555 */
QDocumentLineHandle(const QString & s,QDocument * d)1556 QDocumentLineHandle::QDocumentLineHandle(const QString& s, QDocument *d)
1557 : m_text(s)
1558 , m_doc(d)
1559 #if QT_VERSION >= 0x040400
1560 , m_ref(1)
1561 #endif
1562 , m_indent(0)
1563 , m_state(QDocumentLine::LayoutDirty)
1564 , m_layout(0)
1565 {
1566 #if QT_VERSION < 0x040400
1567 m_ref.init(1);
1568 #endif
1569 }
1570
~QDocumentLineHandle()1571 QDocumentLineHandle::~QDocumentLineHandle()
1572 {
1573 Q_ASSERT(!m_ref);
1574
1575 if ( m_doc && m_doc->impl() )
1576 m_doc->impl()->emitLineDeleted(this);
1577 }
1578
count() const1579 int QDocumentLineHandle::count() const
1580 {
1581 return m_text.count();
1582 }
1583
length() const1584 int QDocumentLineHandle::length() const
1585 {
1586 return m_text.length();
1587 }
1588
line() const1589 int QDocumentLineHandle::line() const
1590 {
1591 return (m_doc && m_doc->impl()) ? m_doc->impl()->indexOf(this) : -1;
1592 }
1593
position() const1594 int QDocumentLineHandle::position() const
1595 {
1596 return (m_doc && m_doc->impl()) ? m_doc->impl()->position(this) : -1;
1597 }
1598
text() const1599 QString QDocumentLineHandle::text() const
1600 {
1601 return m_text;
1602 }
1603
indent() const1604 int QDocumentLineHandle::indent() const
1605 {
1606 int l = nextNonSpaceChar(0);
1607 return QDocument::screenLength(m_text.constData(), l == -1 ? m_text.length() : l, m_doc->tabStop());
1608 }
1609
nextNonSpaceChar(uint pos) const1610 int QDocumentLineHandle::nextNonSpaceChar(uint pos) const
1611 {
1612 const int len = m_text.length();
1613 const QChar *unicode = m_text.unicode();
1614
1615 for ( int i = pos; i < len; ++i )
1616 {
1617 if ( !unicode[i].isSpace() )
1618 return i;
1619 }
1620
1621 return -1;
1622 }
1623
previousNonSpaceChar(int pos) const1624 int QDocumentLineHandle::previousNonSpaceChar(int pos) const
1625 {
1626 const int len = m_text.length();
1627 const QChar *unicode = m_text.unicode();
1628
1629 if ( pos < 0 )
1630 pos = 0;
1631
1632 if ( pos >= len )
1633 pos = len - 1;
1634
1635 for ( int i = pos; i >= 0; --i )
1636 {
1637 if ( !unicode[i].isSpace() )
1638 return i;
1639 }
1640
1641 return -1;
1642 }
1643
document() const1644 QDocument* QDocumentLineHandle::document() const
1645 {
1646 return m_doc;
1647 }
1648
hasFlag(int s) const1649 bool QDocumentLineHandle::hasFlag(int s) const
1650 {
1651 return m_state & s;
1652 }
1653
setFlag(int s,bool y) const1654 void QDocumentLineHandle::setFlag(int s, bool y) const
1655 {
1656 if ( y )
1657 m_state |= s;
1658 else
1659 m_state &= ~s;
1660 }
1661
next() const1662 QDocumentLineHandle* QDocumentLineHandle::next() const
1663 {
1664 return (m_doc && m_doc->impl()) ? m_doc->impl()->next(this) : 0;
1665 }
1666
previous() const1667 QDocumentLineHandle* QDocumentLineHandle::previous() const
1668 {
1669 return (m_doc && m_doc->impl()) ? m_doc->impl()->previous(this) : 0;
1670 }
1671
updateWrap() const1672 void QDocumentLineHandle::updateWrap() const
1673 {
1674 m_indent = 0;
1675 m_frontiers.clear();
1676
1677 if ( !m_doc->impl()->m_constrained )
1678 {
1679 if ( m_layout )
1680 setFlag(QDocumentLine::LayoutDirty, true);
1681
1682 return;
1683 }
1684
1685 const int tabStop = m_doc->impl()->m_tabStop;
1686 const int maxWidth = m_doc->widthConstraint();
1687
1688 if ( m_layout )
1689 {
1690 layout();
1691 } else if ( QDocumentPrivate::m_fixedPitch ) {
1692
1693 int idx = 0, minx = 0, lastBreak = 0, lastWidth = 0, lastX = 0, rx,
1694 x = QDocumentPrivate::m_leftMargin, column = 0, cwidth;
1695
1696 QChar c;
1697 int indent = 0;
1698 const int sw = QDocumentPrivate::m_spaceWidth;
1699
1700 while ( idx < m_text.length() && m_text.at(idx).isSpace() )
1701 {
1702 c = m_text.at(idx);
1703
1704 if ( c.unicode() == '\t' )
1705 {
1706 int taboffset = tabStop - (column % tabStop);
1707
1708 column += taboffset;
1709 cwidth = sw * taboffset;
1710 } else {
1711 ++column;
1712 cwidth = sw;
1713 }
1714
1715 x += cwidth;
1716 ++idx;
1717 }
1718
1719 indent = idx;
1720 minx = rx = x;
1721
1722 if ( (minx + sw) >= maxWidth )
1723 {
1724 //qWarning("Please stop shrinking so aggressively.\nNo attempt will be made to show something decent");
1725 // shrinking too aggresively (or too much spaces...) ungraceful fallback
1726
1727 indent = idx = 0;
1728 minx = rx = x = QDocumentPrivate::m_leftMargin;
1729 }
1730
1731 m_indent = minx - QDocumentPrivate::m_leftMargin;
1732
1733 while ( idx < m_text.length() )
1734 {
1735 if ( c.isSpace() ) //!isWord(c) || !isWord(m_text.at(idx)) )
1736 {
1737 lastX = rx;
1738 lastWidth = x;
1739 lastBreak = idx;
1740 }
1741
1742 c = m_text.at(idx);
1743
1744 if ( c.unicode() == '\t' )
1745 {
1746 int taboffset = tabStop - (column % tabStop);
1747
1748 column += taboffset;
1749 cwidth = sw * taboffset;
1750 } else {
1751 ++column;
1752 cwidth = sw;
1753 }
1754
1755 if ( x + cwidth > maxWidth )
1756 {
1757 if ( lastBreak == (m_frontiers.count() ? m_frontiers.last().first : indent) )
1758 {
1759 // perfect cut or fallback to aggressive cut
1760 m_frontiers << qMakePair(idx, rx);
1761 lastBreak = idx;
1762 lastWidth = x;
1763 lastX = rx;
1764 x = minx;
1765 } else if ( lastBreak < idx ) {
1766 // word cut at a non-ideal position
1767 m_frontiers << qMakePair(lastBreak, lastX);
1768 x = minx + (x - lastWidth);
1769 } else {
1770 m_frontiers << qMakePair(idx, rx);
1771 x = minx;
1772 }
1773 }
1774
1775 rx += cwidth;
1776 x += cwidth;
1777 ++idx;
1778 }
1779 } else {
1780 QMediumArray composited = compose();
1781
1782 int idx = 0, minx = 0, lastBreak = 0, lastWidth = 0, lastX = 0, rx,
1783 x = QDocumentPrivate::m_leftMargin, column = 0, cwidth;
1784
1785 QChar c;
1786 int indent = 0;
1787 int fmt = 0;
1788 const QVector<QFont>& fonts = m_doc->impl()->m_fonts;
1789
1790 while ( idx < m_text.length() && m_text.at(idx).isSpace() )
1791 {
1792 c = m_text.at(idx);
1793 fmt = idx < composited.count() ? composited[idx] : 0;
1794 QFontMetrics fm(fmt < fonts.count() ? fonts.at(fmt) : m_doc->font());
1795
1796 if ( c.unicode() == '\t' )
1797 {
1798 int taboffset = tabStop - (column % tabStop);
1799
1800 column += taboffset;
1801 cwidth = fm.width(' ') * taboffset;
1802 } else {
1803 ++column;
1804 cwidth = fm.width(c);
1805 }
1806
1807 x += cwidth;
1808 ++idx;
1809 }
1810
1811 indent = idx;
1812 minx = rx = x;
1813
1814 if ( (minx + QDocumentPrivate::m_spaceWidth) >= maxWidth )
1815 {
1816 //qWarning("Please stop shrinking so aggressively.\nNo attempt will be made to show something decent");
1817
1818 indent = idx = 0;
1819 minx = rx = x = QDocumentPrivate::m_leftMargin;
1820 }
1821
1822 m_indent = minx - QDocumentPrivate::m_leftMargin;
1823
1824 while ( idx < m_text.length() )
1825 {
1826 if ( c.isSpace() ) //!isWord(c) || !isWord(m_text.at(idx)) )
1827 {
1828 lastX = rx;
1829 lastWidth = x;
1830 lastBreak = idx;
1831 }
1832
1833 c = m_text.at(idx);
1834 fmt = idx < composited.count() ? composited[idx] : 0;
1835 QFontMetrics fm(fmt < fonts.count() ? fonts.at(fmt) : m_doc->font());
1836
1837 if ( c.unicode() == '\t' )
1838 {
1839 int taboffset = tabStop - (column % tabStop);
1840
1841 column += taboffset;
1842 cwidth = fm.width(' ') * taboffset;
1843 } else {
1844 ++column;
1845 cwidth = fm.width(c);
1846 }
1847
1848 if ( x + cwidth > maxWidth )
1849 {
1850 if ( lastBreak == (m_frontiers.count() ? m_frontiers.last().first : indent) )
1851 {
1852 // perfect cut or fallback to aggressive cut
1853 m_frontiers << qMakePair(idx, rx);
1854 lastBreak = idx;
1855 lastWidth = x;
1856 lastX = rx;
1857 x = minx;
1858 } else if ( lastBreak < idx ) {
1859 // word cut at a non-ideal position
1860 m_frontiers << qMakePair(lastBreak, lastX);
1861 x = minx + (x - lastWidth);
1862 } else {
1863 m_frontiers << qMakePair(idx, rx);
1864 x = minx;
1865 }
1866 }
1867
1868 rx += cwidth;
1869 x += cwidth;
1870 ++idx;
1871 }
1872 }
1873 }
1874
cursorToX(int cpos) const1875 int QDocumentLineHandle::cursorToX(int cpos) const
1876 {
1877 cpos = qBound(0, cpos, m_text.length());
1878
1879 if ( m_layout )
1880 {
1881 int xoff = QDocumentPrivate::m_leftMargin, coff = 0, line = m_frontiers.count();
1882
1883 for ( int i = 0; i < m_frontiers.count(); ++i )
1884 {
1885 if ( m_frontiers.at(i).first >= cpos )
1886 {
1887 line = i;
1888
1889 break;
1890 }
1891 }
1892
1893 if ( line )
1894 {
1895 //coff = m_frontiers.at(line - 1).first;
1896 xoff = m_frontiers.at(line - 1).second;
1897 }
1898
1899 //qDebug("c:%i (wrap:%i) => c2x(x - %i) + %i", cpos, line, coff, xoff);
1900 return qRound(m_layout->lineAt(line).cursorToX(cpos - coff)) + xoff;
1901 }
1902
1903 int tabStop = m_doc->impl()->m_tabStop;
1904
1905 if ( QDocumentPrivate::m_fixedPitch )
1906 {
1907 return QDocument::screenLength(m_text.constData(), cpos, tabStop)
1908 * QDocumentPrivate::m_spaceWidth
1909 + QDocumentPrivate::m_leftMargin;
1910 }
1911
1912 //qDebug("c->x(%i) unsafe computations...", cpos);
1913
1914 QMediumArray composited = compose();
1915 const QVector<QFont>& fonts = m_doc->impl()->m_fonts;
1916
1917 if ( (composited.count() < cpos) || fonts.isEmpty() )
1918 return QDocumentPrivate::m_fontMetrics->width(m_text.left(cpos));
1919
1920 int idx = 0, column = 0, cwidth;
1921 int screenx = QDocumentPrivate::m_leftMargin;
1922
1923 while ( idx < cpos )
1924 {
1925 QChar c = m_text.at(idx);
1926 int fmt = idx < composited.count() ? composited[idx] : 0;
1927 QFontMetrics fm(fmt < fonts.count() ? fonts.at(fmt) : m_doc->font());
1928
1929 if ( c == '\t' )
1930 {
1931 int taboffset = tabStop - (column % tabStop);
1932
1933 column += taboffset;
1934 cwidth = fm.width(' ') * taboffset;
1935 } else {
1936 ++column;
1937 cwidth = fm.width(c);
1938 }
1939
1940 screenx += cwidth;
1941 ++idx;
1942 }
1943
1944 //qDebug("cursorToX(%i) = %i", cpos, screenx);
1945
1946 return screenx;
1947 }
1948
xToCursor(int xpos) const1949 int QDocumentLineHandle::xToCursor(int xpos) const
1950 {
1951 //qDebug("x->c(%i) unsafe computations...", xpos);
1952 if ( m_layout )
1953 {
1954 int xoff = QDocumentPrivate::m_leftMargin, coff = 0, line = m_frontiers.count();
1955
1956 for ( int i = 0; i < m_frontiers.count(); ++i )
1957 {
1958 if ( m_frontiers.at(i).second >= xpos )
1959 {
1960 line = i;
1961
1962 break;
1963 }
1964 }
1965
1966 if ( line )
1967 {
1968 //coff = m_frontiers.at(line - 1).first;
1969 xoff = m_frontiers.at(line - 1).second;
1970 }
1971
1972 //qDebug("x:%i (wrap:%i) => x2c(x - %i) + %i", xpos, line, xoff, coff);
1973 return m_layout->lineAt(line).xToCursor(xpos - xoff) + coff;
1974 }
1975
1976 int screenx = xpos;
1977 int tabStop = m_doc->impl()->m_tabStop;
1978 const QVector<QFont>& fonts = m_doc->impl()->m_fonts;
1979
1980 if ( QDocumentPrivate::m_fixedPitch )
1981 {
1982 int screenPos =
1983 (screenx - QDocumentPrivate::m_leftMargin / 2)
1984 /
1985 QDocumentPrivate::m_spaceWidth
1986 ;
1987
1988 if ( tabStop == 1 )
1989 return screenPos;
1990
1991 int idx = 0, column = 0;
1992
1993 while ( (column < screenPos) && (idx < m_text.length()) )
1994 {
1995 QChar c = m_text.at(idx);
1996
1997 if ( c == QLatin1Char('\t') )
1998 {
1999 int taboffset = tabStop - (column % tabStop);
2000 column += taboffset;
2001 } else {
2002 ++column;
2003 }
2004
2005 ++idx;
2006 }
2007
2008 return idx;
2009 } else {
2010 if ( screenx <= QDocumentPrivate::m_leftMargin )
2011 return 0;
2012
2013 QMediumArray composited = compose();
2014
2015 int idx = 0, x = 0, column = 0, cwidth;
2016 screenx -= QDocumentPrivate::m_leftMargin;
2017
2018 while ( idx < m_text.length() )
2019 {
2020 int fmt = idx < composited.count() ? composited[idx] : 0;
2021 QFontMetrics fm(fmt < fonts.count() ? fonts.at(fmt) : m_doc->font());
2022
2023 if ( m_text.at(idx) == '\t' )
2024 {
2025 int taboffset = tabStop - (column % tabStop);
2026
2027 column += taboffset;
2028 cwidth = fm.width(' ') * taboffset;
2029 } else {
2030 ++column;
2031 cwidth = fm.width(m_text.at(idx));
2032 }
2033
2034 int mid = (x + (cwidth / 2) + 1);
2035
2036 if ( screenx <= mid )
2037 return idx;
2038 else if ( screenx <= (x + cwidth) )
2039 return idx + 1;
2040
2041 x += cwidth;
2042 ++idx;
2043 }
2044
2045 return m_text.length();
2046 }
2047 }
2048
wrappedLineForCursor(int cpos) const2049 int QDocumentLineHandle::wrappedLineForCursor(int cpos) const
2050 {
2051 int wrap = m_frontiers.count();
2052
2053 for ( int i = 0; i < m_frontiers.count(); ++i )
2054 {
2055 if ( m_frontiers.at(i).first > cpos )
2056 {
2057 wrap = i;
2058 break;
2059 }
2060 }
2061
2062 return wrap;
2063 }
2064
documentOffsetToCursor(int x,int y) const2065 int QDocumentLineHandle::documentOffsetToCursor(int x, int y) const
2066 {
2067 int wrap = y / QDocumentPrivate::m_lineSpacing;
2068
2069 if ( wrap > m_frontiers.count() )
2070 {
2071 // return an invalid value instead?
2072 //qDebug("a bit too far : (%i, %i)", x, y);
2073 //wrap = m_frontiers.count();
2074
2075 return m_text.length();
2076 }
2077
2078 if ( m_frontiers.count() )
2079 {
2080 //qDebug("(%i, %i) : %i", x, y, wrap);
2081 x = qMin(x, m_doc->widthConstraint());
2082 }
2083
2084 int cpos = 0;
2085 int max = m_text.length();
2086
2087 if ( wrap < m_frontiers.count() )
2088 max = m_frontiers.at(wrap).first - 1;
2089
2090 if ( wrap > 0 )
2091 cpos = m_frontiers.at(wrap - 1).first;
2092
2093 x -= QDocumentPrivate::m_leftMargin;
2094
2095 int idx = cpos, column = 0;
2096 const int ts = m_doc->tabStop();
2097
2098 if ( m_layout )
2099 {
2100 cpos = m_layout->lineAt(wrap).xToCursor(x);
2101 } else if ( QDocumentPrivate::m_fixedPitch ) {
2102 if ( wrap )
2103 x -= m_indent;
2104
2105 while ( (idx < max) && (x > 0) )
2106 {
2107 bool tab = m_text.at(idx).unicode() == '\t';
2108 int cwidth = QDocumentPrivate::m_spaceWidth;
2109
2110 if ( tab )
2111 {
2112 int coff = ts - (column % ts);
2113 column += coff;
2114 cwidth *= coff;
2115 } else {
2116 ++column;
2117 }
2118
2119 int thresold = (2 * cwidth) / 3;
2120
2121 if ( x <= thresold )
2122 break;
2123
2124 x -= cwidth;
2125 ++idx;
2126 }
2127
2128 cpos = idx;
2129 } else {
2130 if ( !hasFlag(QDocumentLine::FormatsApplied) )
2131 compose();
2132
2133 if ( wrap )
2134 x -= m_indent;
2135
2136 const QVector<QFont>& fonts = m_doc->impl()->m_fonts;
2137
2138 while ( (idx < max) && (x > 0) )
2139 {
2140 int fid = idx < m_cache.count() ? m_cache[idx] : 0;
2141
2142 if ( fid < 0 )
2143 fid = 0;
2144
2145 QFontMetrics fm = (fid < fonts.count()) ? QFontMetrics(fonts.at(fid)) : m_doc->fontMetrics();
2146
2147 QChar c = m_text.at(idx);
2148 bool tab = c.unicode() == '\t';
2149 int cwidth = 0;
2150
2151 if ( tab )
2152 {
2153 int coff = ts - (column % ts);
2154 column += coff;
2155 cwidth = fm.width(' ');
2156 cwidth *= coff;
2157 } else {
2158 ++column;
2159 cwidth = fm.width(c);
2160 }
2161
2162 int thresold = (2 * cwidth) / 3;
2163
2164 if ( x <= thresold )
2165 break;
2166
2167 x -= cwidth;
2168 ++idx;
2169 }
2170
2171 cpos = idx;
2172 }
2173
2174 return cpos;
2175 }
2176
cursorToDocumentOffset(int cpos,int & x,int & y) const2177 void QDocumentLineHandle::cursorToDocumentOffset(int cpos, int& x, int& y) const
2178 {
2179 if ( cpos > m_text.length() )
2180 cpos = m_text.length();
2181 else if ( cpos < 0 )
2182 cpos = 0;
2183
2184 int idx = 0;
2185 int wrap = wrappedLineForCursor(cpos);
2186
2187 x = QDocumentPrivate::m_leftMargin;
2188 y = wrap * QDocumentPrivate::m_lineSpacing;
2189
2190 if ( wrap )
2191 {
2192 idx = m_frontiers.at(wrap - 1).first;
2193 }
2194
2195 int column = 0;
2196 const int ts = m_doc->tabStop();
2197
2198 if ( m_layout )
2199 {
2200 x += m_layout->lineAt(wrap).cursorToX(cpos);
2201 } else if ( QDocumentPrivate::m_fixedPitch ) {
2202 if ( wrap )
2203 x += m_indent;
2204
2205 while ( idx < cpos )
2206 {
2207 bool tab = m_text.at(idx).unicode() == '\t';
2208 int cwidth = QDocumentPrivate::m_spaceWidth;
2209
2210 if ( tab )
2211 {
2212 int coff = ts - (column % ts);
2213 column += coff;
2214 cwidth *= coff;
2215 } else {
2216 ++column;
2217 }
2218
2219 x += cwidth;
2220 ++idx;
2221 }
2222 } else {
2223 if ( !hasFlag(QDocumentLine::FormatsApplied) )
2224 compose();
2225
2226 if ( wrap )
2227 x += m_indent;
2228
2229 const QVector<QFont>& fonts = m_doc->impl()->m_fonts;
2230
2231 while ( idx < cpos )
2232 {
2233 int fid = idx < m_cache.count() ? m_cache[idx] : 0;
2234
2235 if ( fid < 0 )
2236 fid = 0;
2237
2238 QFontMetrics fm = (fid < fonts.count()) ? QFontMetrics(fonts.at(fid)) : m_doc->fontMetrics();
2239
2240 QChar c = m_text.at(idx);
2241 bool tab = c.unicode() == '\t';
2242 int cwidth = 0;
2243
2244 if ( tab )
2245 {
2246 int coff = ts - (column % ts);
2247 column += coff;
2248 cwidth = fm.width(' ');
2249 cwidth *= coff;
2250 } else {
2251 ++column;
2252 cwidth = fm.width(c);
2253 }
2254
2255 x += cwidth;
2256 ++idx;
2257 }
2258 }
2259 }
2260
cursorToDocumentOffset(int cpos) const2261 QPoint QDocumentLineHandle::cursorToDocumentOffset(int cpos) const
2262 {
2263 QPoint p;
2264 cursorToDocumentOffset(cpos, p.rx(), p.ry());
2265 return p;
2266 #if 0
2267 int y = 0;
2268 int x = cursorToX(cpos);
2269
2270 if ( m_frontiers.isEmpty() )
2271 return QPoint(x, y);
2272
2273 int first = m_frontiers.at(0).first;
2274
2275 if ( cpos < first )
2276 return QPoint(x, y);
2277
2278 int wrappedX = 0;
2279 int fns = nextNonSpaceChar(0), off = 0;
2280
2281 if ( fns != -1 && fns < first )
2282 off = cursorToX(fns);
2283 else
2284 off = QDocumentPrivate::m_leftMargin;
2285
2286 for ( int j = 0; j < m_frontiers.count(); ++j )
2287 {
2288 if ( m_frontiers[j].first <= cpos )
2289 {
2290 wrappedX = m_frontiers[j].second;
2291 y += QDocumentPrivate::m_lineSpacing;
2292 } else {
2293 break;
2294 }
2295 }
2296
2297 return QPoint(x - wrappedX + off, y);
2298 #endif
2299 }
2300
clearOverlays()2301 void QDocumentLineHandle::clearOverlays()
2302 {
2303 m_overlays.clear();
2304
2305 //setFlag(QDocumentLine::LayoutDirty, true);
2306 setFlag(QDocumentLine::FormatsApplied, false);
2307 //applyOverlays();
2308 }
2309
addOverlay(const QFormatRange & over)2310 void QDocumentLineHandle::addOverlay(const QFormatRange& over)
2311 {
2312 m_overlays << over;
2313
2314 //setFlag(QDocumentLine::LayoutDirty, true);
2315 setFlag(QDocumentLine::FormatsApplied, false);
2316 //applyOverlays();
2317 }
2318
removeOverlay(const QFormatRange & over)2319 void QDocumentLineHandle::removeOverlay(const QFormatRange& over)
2320 {
2321 int i = m_overlays.removeAll(over);
2322
2323 //setFlag(QDocumentLine::LayoutDirty, true);
2324 if ( i )
2325 setFlag(QDocumentLine::FormatsApplied, false);
2326 //applyOverlays();
2327 }
2328
shiftOverlays(int position,int offset)2329 void QDocumentLineHandle::shiftOverlays(int position, int offset)
2330 {
2331 if ( offset > 0 )
2332 {
2333 for ( int i = 0; i < m_overlays.count(); ++i )
2334 {
2335 QFormatRange& r = m_overlays[i];
2336
2337 if ( r.offset >= position )
2338 {
2339 r.offset += offset;
2340 } else if ( r.offset + r.length > position ) {
2341 m_overlays.removeAt(i);
2342 --i;
2343 }
2344 }
2345 } else if ( offset < 0 ) {
2346 const int max = position - offset;
2347
2348 for ( int i = 0; i < m_overlays.count(); ++i )
2349 {
2350 QFormatRange& r = m_overlays[i];
2351
2352 if ( r.offset >= max )
2353 {
2354 r.offset += offset;
2355 } else if ( r.offset + r.length >= position ) {
2356 m_overlays.removeAt(i);
2357 --i;
2358 }
2359 }
2360 }
2361
2362 setFlag(QDocumentLine::FormatsApplied, false);
2363 }
2364
setFormats(const QVector<int> & fmts)2365 void QDocumentLineHandle::setFormats(const QVector<int>& fmts)
2366 {
2367 m_formats = fmts;
2368
2369 while ( m_formats.count() > m_text.length() )
2370 m_formats.pop_back();
2371
2372 while ( m_formats.count() < m_text.length() )
2373 m_formats.append(0);
2374
2375 //setFlag(QDocumentLine::LayoutDirty, true);
2376 setFlag(QDocumentLine::FormatsApplied, false);
2377 //applyOverlays();
2378 }
2379
compose() const2380 QMediumArray QDocumentLineHandle::compose() const
2381 {
2382 //QMediumArray m_composited(m_text.length());
2383 if ( hasFlag(QDocumentLine::FormatsApplied) )
2384 return m_cache;
2385
2386 m_cache.resize(m_text.length());
2387
2388 for ( int i = 0; i < qMin(m_formats.count(), m_text.length()); ++i )
2389 m_cache[i] = m_formats.at(i);
2390
2391 for ( int i = m_formats.count(); i < m_text.length(); ++i )
2392 m_cache[i] = 0;
2393
2394 // compositing formats and overlays
2395 foreach ( const QFormatRange& r, m_overlays )
2396 {
2397 int beg = qMax(0, r.offset);
2398 int end = qMin(r.offset + r.length, m_cache.count());
2399
2400 for ( int i = beg; i < end; ++i )
2401 {
2402 m_cache[i] = r.format;
2403 }
2404 }
2405
2406 setFlag(QDocumentLine::FormatsApplied, true);
2407
2408 return m_cache;
2409 }
2410
decorations() const2411 QList<QTextLayout::FormatRange> QDocumentLineHandle::decorations() const
2412 {
2413 if ( !hasFlag(QDocumentLine::FormatsApplied) )
2414 compose();
2415
2416 // turning format "map" into ranges that QTextLayout can understand...
2417 QList<QTextLayout::FormatRange> m_ranges;
2418
2419 int fid = 0;
2420 QTextLayout::FormatRange r;
2421 r.start = r.length = -1;
2422
2423 int i = 0;
2424
2425 //if ( m_cache.isEmpty() )
2426 // qWarning("empty cache...");
2427
2428 while ( i < m_cache.count() )
2429 {
2430 while ( (i < m_cache.count()) && !m_cache[i] )
2431 ++i;
2432
2433 if ( i >= m_cache.count() )
2434 break;
2435
2436 fid = m_cache[i];
2437
2438 r.start = i;
2439 r.format = m_doc->formatScheme()->format(fid).toTextCharFormat();
2440
2441 while ( (i < m_cache.count()) && (m_cache[i] == fid) )
2442 ++i;
2443
2444 if ( i >= m_cache.count() )
2445 break;
2446
2447 r.length = i - r.start;
2448 m_ranges << r;
2449
2450 r.start = r.length = -1;
2451 }
2452
2453 if ( r.start != -1 )
2454 {
2455 r.length = m_cache.count() - r.start;
2456 m_ranges << r;
2457 }
2458
2459 return m_ranges;
2460 }
2461
applyOverlays() const2462 void QDocumentLineHandle::applyOverlays() const
2463 {
2464 if ( !m_layout )
2465 return;
2466
2467 //m_layout->setAdditionalFormats(decorations());
2468
2469 //setFlag(QDocumentLine::FormatsApplied, true);
2470 }
2471
layout() const2472 void QDocumentLineHandle::layout() const
2473 {
2474 bool needLayout = false;
2475 static QList<QChar::Direction> m_layoutRequirements = QList<QChar::Direction>()
2476 << QChar::DirR
2477 << QChar::DirAL
2478 << QChar::DirRLE
2479 << QChar::DirRLO
2480 << QChar::DirPDF
2481 << QChar::DirAN;
2482
2483 for ( int i = 0; (i < m_text.length()) && !needLayout; ++i )
2484 {
2485 QChar c = m_text.at(i);
2486
2487 needLayout = m_layoutRequirements.contains(c.direction());
2488 }
2489
2490 if ( needLayout )
2491 {
2492 //qDebug("layout needed at line %i", this->line());
2493
2494 if ( !m_layout )
2495 {
2496 m_layout = new QTextLayout(m_text, QDocument::font());
2497 } else {
2498 m_layout->setText(m_text);
2499 //m_layout->setFont(config()->font());
2500 }
2501
2502 m_layout->setCacheEnabled(false);
2503 // Initial setup of the QTextLayout.
2504
2505 // Tab width
2506 QTextOption opt;
2507 opt.setFlags(QTextOption::IncludeTrailingSpaces);
2508 opt.setTabStop(m_doc->tabStop() * QDocumentPrivate::m_spaceWidth);
2509
2510 //opt.setWrapMode(QTextOption::NoWrap);
2511 opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2512
2513 // Find the first strong character in the string.
2514 // If it is an RTL character, set the base layout direction of the string to RTL.
2515 //
2516 // See http://www.unicode.org/reports/tr9/#The_Paragraph_Level (Sections P2 & P3).
2517 // Qt's text renderer ("scribe") version 4.2 assumes a "higher-level protocol"
2518 // (such as KatePart) will specify the paragraph level, so it does not apply P2 & P3
2519 // by itself. If this ever change in Qt, the next code block could be removed.
2520 if ( m_text.isRightToLeft() )
2521 opt.setTextDirection(Qt::RightToLeft);
2522
2523 m_layout->setTextOption(opt);
2524
2525 // Syntax highlighting, inbuilt and arbitrary
2526 //m_layout->setAdditionalFormats(m_ranges);
2527
2528 // Begin layouting
2529 m_layout->beginLayout();
2530
2531 m_frontiers.clear();
2532
2533 int i = 0, rx = 0, height = 0, minwidth = 0;
2534
2535 forever
2536 {
2537 QTextLine line = m_layout->createLine();
2538
2539 if ( !line.isValid() )
2540 {
2541 //if ( m_layout->lineCount() > 1 )
2542 // qWarning("Troubles expected on line %i", this->line());
2543
2544 break;
2545 }
2546
2547 if ( m_doc->widthConstraint() )
2548 line.setLineWidth(m_doc->widthConstraint() - QDocumentPrivate::m_leftMargin);
2549 else
2550 line.setNumColumns(m_text.length());
2551
2552 rx += qRound(line.naturalTextWidth()); //qRound(line.cursorToX(line.textLength()));
2553
2554 if ( m_doc->impl()->m_constrained && m_layout->textOption().textDirection() == Qt::RightToLeft )
2555 {
2556 line.setPosition(QPoint(qRound(qreal(m_doc->widthConstraint() - 2 * QDocumentPrivate::m_leftMargin) - line.naturalTextWidth()), height));
2557 } else {
2558 line.setPosition(QPoint(minwidth, height));
2559
2560 if ( !i )
2561 {
2562 m_indent = minwidth = cursorToX(nextNonSpaceChar(0)) - QDocumentPrivate::m_leftMargin;
2563
2564 if ( minwidth < 0 || minwidth >= m_doc->widthConstraint() )
2565 minwidth = 0;
2566 }
2567 }
2568
2569 m_frontiers << qMakePair(line.textStart() + line.textLength(), rx);
2570
2571 ++i;
2572
2573 height += QDocumentPrivate::m_lineSpacing;
2574 //height += QDocument::fontMetrics().height();
2575 }
2576
2577 m_frontiers.pop_back();
2578
2579 m_layout->endLayout();
2580 } else {
2581 if ( m_layout )
2582 delete m_layout;
2583
2584 m_layout = 0;
2585 //updateWrap();
2586 }
2587
2588 setFlag(QDocumentLine::LayoutDirty, false);
2589 }
2590
2591 struct RenderRange
2592 {
2593 int position;
2594 int length;
2595 quint16 format;
2596 int wrap;
2597 };
2598
draw(QPainter * p,int xOffset,int vWidth,const QSmallArray & sel,const QSmallArray & cursor,const QPalette & pal,bool fullSel) const2599 void QDocumentLineHandle::draw( QPainter *p,
2600 int xOffset,
2601 int vWidth,
2602 const QSmallArray& sel,
2603 const QSmallArray& cursor,
2604 const QPalette& pal,
2605 bool fullSel) const
2606 {
2607 if ( hasFlag(QDocumentLine::LayoutDirty) )
2608 layout();
2609
2610 if ( m_layout && !hasFlag(QDocumentLine::FormatsApplied) )
2611 m_layout->setAdditionalFormats(decorations());
2612
2613 QMediumArray m_composited = compose();
2614
2615 if ( m_layout )
2616 {
2617 //if ( !hasFlag(QDocumentLine::FormatsApplied) )
2618 // applyOverlays();
2619
2620 const int lh = QDocument::fontMetrics().height();
2621
2622 QVector<QTextLayout::FormatRange> selections;
2623
2624 QTextCharFormat fmt;
2625 fmt.setBackground(pal.highlight());
2626 fmt.setForeground(pal.highlightedText());
2627
2628 QTextLayout::FormatRange range;
2629 if ( fullSel )
2630 {
2631 range.start = 0;
2632 range.format = fmt;
2633 range.length = m_text.length();
2634 selections << range;
2635 } else {
2636 for ( int i = 0; i < sel.count(); ++i )
2637 {
2638 range.start = sel[i];
2639 range.format = fmt;
2640
2641 if ( (i + 1) < sel.count() )
2642 {
2643 // regular selection subset
2644 range.length = sel[++i] - range.start;
2645
2646 } else if ( m_layout->lineCount() ) {
2647 // span to end of line, not only text
2648 range.length = m_text.length() - range.start;
2649 qreal lineWidth = m_layout->lineAt(m_layout->lineCount() - 1).naturalTextWidth();
2650 const int endX = QDocumentPrivate::m_leftMargin + qRound(lineWidth) - xOffset;
2651
2652 QRect area(endX, lh * i, vWidth - endX, lh);
2653
2654 p->fillRect(area, fmt.background());
2655 }
2656
2657 selections << range;
2658 }
2659 }
2660
2661 QPoint off(QDocumentPrivate::m_leftMargin, 0);
2662
2663 m_layout->draw(p, off, selections);
2664
2665 for ( int i = 0; i < cursor.count(); ++i )
2666 m_layout->drawCursor(p, off, cursor[i]);
2667
2668 //m_layout->clearAdditionalFormats();
2669 } else if ( m_text.isEmpty() ) {
2670 // enforce selection drawing on empty lines
2671 if ( sel.count() == 1 )
2672 p->fillRect(
2673 qMax(xOffset, QDocumentPrivate::m_leftMargin),
2674 0,
2675 vWidth,
2676 QDocumentPrivate::m_lineSpacing,
2677 pal.highlight()
2678 );
2679
2680 // enforce cursor drawing on empty lines
2681 if ( cursor.count() && (xOffset < QDocumentPrivate::m_leftMargin) )
2682 p->drawLine(
2683 QDocumentPrivate::m_leftMargin,
2684 0,
2685 QDocumentPrivate::m_leftMargin,
2686 QDocumentPrivate::m_lineSpacing
2687 );
2688
2689 } else if ( true ) {
2690
2691 QVector<quint16> merged;
2692 merged.fill(0, m_text.count());
2693
2694 QList<RenderRange> ranges;
2695
2696 // find start of trailing whitespaces
2697 int last = m_text.length();
2698
2699 while ( (last > 0) && m_text.at(last - 1).isSpace() )
2700 --last;
2701
2702 // TODO : format-level (not format id) merging of formats and opverlays...
2703
2704 // merge selection ranges with the rest (formats + overlays)
2705 if ( true ) //m_composited.count() || sel.count() )
2706 {
2707 //int max = qMin(m_text.count(), m_composited.count());
2708
2709 for ( int i = 0; i < m_text.count(); ++i )
2710 {
2711 if ( m_composited.count() > i )
2712 merged[i] = m_composited.at(i);
2713
2714 // separate spaces to ease rendering loop
2715 if ( m_text.at(i).isSpace() )
2716 merged[i] |= 0x4000;
2717 }
2718
2719 for ( int i = 0; i < sel.count(); i += 2 )
2720 {
2721 int max = m_text.length();
2722
2723 if ( (i + 1) < sel.count() )
2724 max = qMin(sel[i + 1], max);
2725
2726 for ( int j = sel[i]; j < max; ++j )
2727 merged[j] |= 0x8000;
2728 }
2729 }
2730
2731 // generate render ranges
2732 if ( merged.count() )
2733 {
2734 int i = 0, wrap = 0, max = m_text.count(),
2735 frontier = m_frontiers.count() ? m_frontiers.first().first : max;
2736
2737 while ( i < max )
2738 {
2739 RenderRange r;
2740 r.position = i;
2741 r.length = 1;
2742 r.wrap = wrap;
2743 r.format = merged.at(i);
2744
2745 while ( ((i + 1) < frontier) && (merged.at(i + 1) == r.format) )
2746 {
2747 ++r.length;
2748 ++i;
2749 }
2750
2751 ranges << r;
2752 ++i;
2753
2754 if ( i == frontier )
2755 {
2756 ++wrap;
2757 frontier = wrap < m_frontiers.count() ? m_frontiers.at(wrap).first : max;
2758 }
2759 }
2760 } else if ( m_frontiers.count() ) {
2761 // no formatting (nor selection) : simpler
2762 int i = 0, wrap = 0, max = m_text.count(),
2763 frontier = m_frontiers.count() ? m_frontiers.first().first : max;
2764
2765 while ( i < max )
2766 {
2767 RenderRange r;
2768 r.position = i;
2769 r.length = 1;
2770 r.wrap = wrap;
2771 r.format = fullSel ? 0x8000 : 0;
2772
2773 while ( ((i + 1) < frontier) )
2774 {
2775 ++r.length;
2776 ++i;
2777 }
2778
2779 ranges << r;
2780 ++i;
2781
2782 if ( i == frontier )
2783 {
2784 ++wrap;
2785 frontier = wrap < m_frontiers.count() ? m_frontiers.at(wrap).first : max;
2786 }
2787 }
2788 } else {
2789 // neither formatting nor line wrapping : simple drawText()
2790 RenderRange r;
2791 r.position = 0;
2792 r.length = m_text.length();
2793 r.format = fullSel ? 0x8000 : 0;
2794
2795 ranges << r;
2796 }
2797
2798 quint16 fmt = fullSel ? 0x8000 : 0;
2799 QDocumentPrivate *d = m_doc->impl();
2800 const int ts = d->m_tabStop;
2801 const int maxWidth = xOffset + vWidth;
2802 const bool unbounded = sel.count() & 1;
2803 const QColor ht = pal.highlightedText().color();
2804
2805 const bool showTabs = QDocument::showSpaces() & QDocument::ShowTabs,
2806 showLeading = QDocument::showSpaces() & QDocument::ShowLeading,
2807 showTrailing = QDocument::showSpaces() & QDocument::ShowTrailing;
2808
2809 //const int fns = nextNonSpaceChar(0);
2810 int indent = qMax(0, m_indent) + QDocumentPrivate::m_leftMargin;
2811
2812 int cidx = 0;
2813 int rngIdx = 0;
2814 int column = 0;
2815 bool continuingWave = false, brokenWave = false;
2816 int dir = 0; // 0 = down; 1 = up
2817 int wrap = 0, xpos = QDocumentPrivate::m_leftMargin, ypos = 0;
2818 bool leading = ranges.first().format & 0x4000, pastLead = false;
2819
2820 foreach ( const RenderRange& r, ranges )
2821 {
2822 ++rngIdx;
2823
2824 if ( wrap != r.wrap )
2825 {
2826 continuingWave = false;
2827
2828 if ( fmt & 0x8000 )
2829 {
2830 // finish selection
2831 p->fillRect(
2832 xpos, ypos,
2833 maxWidth - xpos, QDocumentPrivate::m_lineSpacing,
2834 pal.highlight()
2835 );
2836
2837 }
2838
2839 /*
2840 if ( pastLead && (r.format & 0x4000) )
2841 {
2842 indent = QDocumentPrivate::m_leftMargin;
2843 }
2844 */
2845
2846 ++wrap;
2847 column = 0;
2848 ypos += QDocumentPrivate::m_lineSpacing;
2849 xpos = indent;
2850
2851 if ( r.format & 0x8000 )
2852 {
2853 // finish selection
2854 p->fillRect(
2855 QDocumentPrivate::m_leftMargin, ypos,
2856 xpos, QDocumentPrivate::m_lineSpacing,
2857 pal.highlight()
2858 );
2859
2860 }
2861 }
2862 if ( leading && !(r.format & 0x4000) )
2863 {
2864 //indent = xpos;
2865 leading = false;
2866 pastLead = true;
2867 }
2868
2869 // TODO : clip more accurately (i.e inside ranges)
2870 if ( xpos > maxWidth )
2871 break;
2872
2873 if ( r.format != fmt )
2874 {
2875 d->tunePainter(p, r.format & 0x0fff);
2876
2877 fmt = r.format;
2878 }
2879
2880 int rwidth = 0;
2881 int tcol = column;
2882 const QString rng = m_text.mid(r.position, r.length);
2883
2884 if ( r.format & 0x4000 )
2885 {
2886 foreach ( QChar c, rng )
2887 {
2888 if ( c.unicode() == '\t' )
2889 {
2890 int toff = ts - (tcol % ts);
2891 rwidth += toff * QDocumentPrivate::m_spaceWidth;
2892 tcol += toff;
2893 } else {
2894 rwidth += QDocumentPrivate::m_spaceWidth;
2895 ++tcol;
2896 }
2897 }
2898 } else {
2899 column += r.length;
2900
2901 if ( QDocumentPrivate::m_fixedPitch )
2902 rwidth = QDocumentPrivate::m_spaceWidth * r.length;
2903 else
2904 rwidth = p->fontMetrics().width(rng);
2905 }
2906
2907 if ( (xpos + rwidth) <= xOffset )
2908 {
2909 xpos += rwidth;
2910
2911 if ( r.format & 0x4000 )
2912 column = tcol;
2913
2914 continue;
2915 }
2916
2917 QFormat format;
2918 int xspos = xpos;
2919 const QPen oldpen = p->pen();
2920 const int baseline = ypos + QDocumentPrivate::m_ascent;
2921
2922 if ( d->m_formatScheme )
2923 format = d->m_formatScheme->format(fmt & 0x0fff);
2924
2925 if ( fullSel || (fmt & 0x8000) )
2926 {
2927 p->setPen(ht);
2928
2929 p->fillRect(xpos, ypos,
2930 rwidth, QDocumentPrivate::m_lineSpacing,
2931 pal.highlight());
2932
2933 } else {
2934 if ( format.foreground.isValid() )
2935 {
2936 p->setPen(format.foreground);
2937 } else {
2938 p->setBrush(pal.text());
2939 }
2940
2941 if ( format.background.isValid() )
2942 {
2943 p->fillRect(xpos, ypos,
2944 rwidth, QDocumentPrivate::m_lineSpacing,
2945 format.background);
2946 }
2947 }
2948
2949 if ( r.format & 0x4000 )
2950 {
2951 // spaces
2952 int max = r.position + r.length;
2953
2954 if ( rngIdx == ranges.count() )
2955 ++max;
2956
2957 for ( int i = r.position; i < max; ++i )
2958 {
2959 while ( cidx < cursor.count() )
2960 {
2961 if ( cursor.at(cidx) == i )
2962 {
2963 p->drawLine(xpos, ypos, xpos, ypos + QDocumentPrivate::m_lineSpacing);
2964
2965 ++cidx;
2966 } else {
2967 break;
2968 }
2969 }
2970
2971 if ( i == r.position + r.length )
2972 break;
2973
2974 bool isTab = m_text.at(i).unicode() == '\t';
2975
2976 if ( isTab )
2977 {
2978 int toff = ts - (column % ts);
2979 column += toff;
2980 int xoff = toff * QDocumentPrivate::m_spaceWidth;
2981 /*
2982 if ( r.format & 0x8000 )
2983 {
2984 p->fillRect(xpos, ypos,
2985 xoff, QDocumentPrivate::m_lineSpacing,
2986 pal.highlight());
2987
2988 }
2989 */
2990 if ( showTabs )
2991 {
2992 p->translate(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent);
2993 p->drawPoints(m_spaceSign, sizeof(m_spaceSign) / sizeof(QPoint));
2994 p->translate(m_spaceSignOffset - xpos,-ypos - QDocumentPrivate::m_ascent);
2995 }
2996
2997 xpos += xoff;
2998 } else {
2999 ++column;
3000 /*
3001 if ( r.format & 0x8000 )
3002 {
3003 p->fillRect(xpos, ypos,
3004 QDocumentPrivate::m_spaceWidth, QDocumentPrivate::m_lineSpacing,
3005 pal.highlight());
3006
3007 }
3008 */
3009 if (
3010 (
3011 leading
3012 &&
3013 showLeading
3014 )
3015 ||
3016 (
3017 (r.position >= last)
3018 &&
3019 showTrailing
3020 )
3021 )
3022 {
3023 p->translate(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent);
3024 p->drawPoints(m_spaceSign, sizeof(m_spaceSign) / sizeof(QPoint));
3025 p->translate(m_spaceSignOffset - xpos,-ypos - QDocumentPrivate::m_ascent);
3026 }
3027
3028 xpos += QDocumentPrivate::m_spaceWidth;
3029 }
3030 }
3031
3032 /*
3033 if ( leading )
3034 {
3035 //indent = xpos;
3036 leading = false;
3037 pastLead = true;
3038 }
3039 */
3040
3041 } else {
3042 p->drawText(xpos, baseline, rng);
3043
3044 while ( cidx < cursor.count() )
3045 {
3046 const int xcoff = cursor.at(cidx) - r.position;
3047
3048 if ( xcoff < 0 )
3049 {
3050 ++cidx;
3051 continue;
3052 }
3053
3054 if ( xcoff < (rngIdx == ranges.count() ? r.length + 1 : r.length) )
3055 {
3056 int xcpos = xpos;
3057
3058 if ( xcoff )
3059 {
3060 if ( QDocumentPrivate::m_fixedPitch )
3061 {
3062 xcpos += xcoff * QDocumentPrivate::m_spaceWidth;
3063 } else {
3064 xcpos += p->fontMetrics().width(rng.left(xcoff));
3065 }
3066 }
3067
3068 //qDebug("drawing cursor %i (col %i, x=%i)", cidx, cursor.at(cidx), xcpos);
3069
3070 p->drawLine(xcpos, ypos, xcpos, ypos + QDocumentPrivate::m_lineSpacing);
3071
3072 ++cidx;
3073 } else {
3074 break;
3075 }
3076 }
3077
3078 xpos += rwidth;
3079 }
3080
3081 //qDebug("underline pos : %i", p->fontMetrics().underlinePos());
3082
3083 if ( format.linescolor.isValid() )
3084 p->setPen(format.linescolor);
3085
3086 const int ydo = qMin(baseline + p->fontMetrics().underlinePos(), ypos + QDocumentPrivate::m_lineSpacing - 1);
3087 const int yin = baseline - p->fontMetrics().strikeOutPos();
3088 const int yup = qMax(baseline - p->fontMetrics().overlinePos() + 1, ypos);
3089
3090 if ( format.overline )
3091 {
3092 p->drawLine(xspos, yup, xpos, yup);
3093 }
3094
3095 if ( format.strikeout )
3096 {
3097 p->drawLine(xspos, yin, xpos, yin);
3098 }
3099
3100 if ( format.underline )
3101 {
3102 p->drawLine(xspos, ydo, xpos, ydo);
3103 }
3104
3105 if ( format.waveUnderline )
3106 {
3107 /*
3108 those goddamn font makers take liberties with common sense
3109 and make it so damn harder to figure proper y offset for wave
3110 underline (keeping the regular underline pos make it look
3111 weird or even invisible on some fonts... reference rendering
3112 with DejaVu Sans Mono)
3113 */
3114
3115 // we used fixed wave amplitude of 3 (well strictly speaking
3116 // amplitude would be 1.5 but let's forget about waves and
3117 // focus on getting that code to work...
3118
3119 // gotta center things
3120 const int ycenter = ypos + QDocumentPrivate::m_lineSpacing - 3;
3121 /*
3122 qMin(
3123 ypos + (QDocumentPrivate::m_ascent + QDocumentPrivate::m_lineSpacing) / 2,
3124 ypos + QDocumentPrivate::m_lineSpacing - 3
3125 );*/
3126
3127 //if (format.waveUnderlineForeground.isValid())
3128 // p->setPen(format.waveUnderlineForeground);
3129
3130 int cp = 0;
3131 brokenWave = false;
3132
3133 while ( cp < rwidth )
3134 {
3135 if ( !cp && !continuingWave )
3136 {
3137 dir = 0;
3138 p->drawLine(xspos, ycenter, xspos + 1, ycenter + 1);
3139 ++cp;
3140 } else if ( !cp && brokenWave ) {
3141 if ( !dir )
3142 p->drawLine(xspos, ycenter, xspos + 1, ycenter + 1);
3143 else
3144 p->drawLine(xspos, ycenter, xspos + 1, ycenter - 1);
3145
3146 } else {
3147 if ( cp + 2 > rwidth)
3148 {
3149 if ( !dir )
3150 p->drawLine(xspos + cp, ycenter - 1, xspos + cp + 1, ycenter);
3151 else
3152 p->drawLine(xspos + cp, ycenter + 1, xspos + cp + 1, ycenter);
3153
3154 // trick to keep current direction
3155 dir ^= 1;
3156
3157 brokenWave = true;
3158 ++cp;
3159 } else {
3160 if ( !dir )
3161 p->drawLine(xspos + cp, ycenter - 1, xspos + cp + 2, ycenter + 1);
3162 else
3163 p->drawLine(xspos + cp, ycenter + 1, xspos + cp + 2, ycenter - 1);
3164
3165 cp += 2;
3166 }
3167 }
3168
3169 dir ^= 1;
3170 }
3171
3172 continuingWave = true;
3173 } else {
3174 continuingWave = false;
3175 dir = 0;
3176 }
3177
3178 p->setPen(oldpen);
3179 }
3180
3181 if ( unbounded )
3182 p->fillRect(
3183 xpos, ypos,
3184 maxWidth - xpos, QDocumentPrivate::m_lineSpacing,
3185 pal.highlight()
3186 );
3187
3188 } else {
3189 QChar c;
3190
3191 int maxWidth = xOffset + vWidth;
3192
3193 const bool wrapped = m_frontiers.count();
3194
3195 int fmt = 0;
3196
3197 bool leading = true, unbounded = false, leftSel = false,
3198 showTabs = QDocument::showSpaces() & QDocument::ShowTabs,
3199 showLeading = QDocument::showSpaces() & QDocument::ShowLeading,
3200 showTrailing = QDocument::showSpaces() & QDocument::ShowTrailing;
3201
3202 int cwidth,
3203 ypos = 0,
3204 indent = 0,
3205 idx = 0, column = 0,
3206 selidx = 0, sellen = 0, wrap = 0,
3207 xpos = QDocumentPrivate::m_leftMargin;
3208
3209 const int ts = QDocument::tabStop();
3210
3211 // find start of trailing whitespaces
3212 int last = m_text.length();
3213
3214 while ( (last > 0) && m_text.at(last - 1).isSpace() )
3215 --last;
3216
3217 //p->save();
3218
3219 //qDebug("drawing from %i to %i", xpos, maxWidth);
3220
3221 while ( idx < m_text.length() )
3222 {
3223 if ( (selidx < sel.count()) && (idx == sel[selidx]) )
3224 {
3225 if ( (selidx + 1) < sel.count() )
3226 {
3227 sellen = sel[selidx + 1] - sel[selidx];
3228 selidx += 2;
3229 } else {
3230 // unbounded selection
3231 unbounded = true;
3232
3233 ++selidx;
3234 sellen = m_text.length() - idx;
3235
3236 p->fillRect(
3237 xpos, ypos,
3238 maxWidth - xpos, QDocumentPrivate::m_lineSpacing,
3239 pal.highlight()
3240 );
3241 }
3242 }
3243
3244 c = m_text.at(idx);
3245
3246 if ( c == '\t' )
3247 {
3248 int toff = ts - (column % ts);
3249 cwidth = toff * QDocumentPrivate::m_spaceWidth;
3250 column += toff - 1;
3251 } else if ( c == ' ' ) {
3252 cwidth = QDocumentPrivate::m_spaceWidth;
3253 } else if ( idx < m_composited.count() ) {
3254 int nfmt = m_composited[idx];
3255
3256 if ( fmt != nfmt )
3257 {
3258 m_doc->impl()->tunePainter(p, nfmt);
3259 fmt = nfmt;
3260 }
3261
3262 // make sure bold/italicized/.. chars are taken into account...
3263 cwidth = p->fontMetrics().width(c);
3264 } else {
3265 // char uses default font...
3266 cwidth = QDocumentPrivate::m_fontMetrics->width(c);
3267 }
3268
3269 if ( (xpos + cwidth) > xOffset )
3270 {
3271 // MUST be done after setting the proper chararcter width!
3272 if ( wrapped && (wrap < m_frontiers.count()) && (idx >= m_frontiers.at(wrap).first) )
3273 {
3274 if ( sellen || leftSel )
3275 {
3276 p->fillRect(
3277 xpos, ypos,
3278 maxWidth - xpos, QDocumentPrivate::m_lineSpacing,
3279 pal.highlight()
3280 );
3281
3282 }
3283
3284 ++wrap;
3285 xpos = indent;
3286 ypos += QDocumentPrivate::m_lineSpacing;
3287
3288 if ( sellen || leftSel )
3289 {
3290 p->fillRect(
3291 QDocumentPrivate::m_leftMargin, ypos,
3292 xpos - QDocumentPrivate::m_leftMargin + (leftSel ? 0 : cwidth),
3293 QDocumentPrivate::m_lineSpacing,
3294 pal.highlight()
3295 );
3296
3297 }
3298
3299 } else {
3300 if ( sellen )
3301 {
3302 p->fillRect(
3303 xpos, ypos,
3304 cwidth, QDocumentPrivate::m_lineSpacing,
3305 pal.highlight()
3306 );
3307
3308 }
3309 }
3310
3311 const int baseline = ypos + QDocumentPrivate::m_ascent;
3312
3313 if ( !c.isSpace() )
3314 {
3315 if ( leading )
3316 indent = xpos;
3317
3318 leading = false;
3319
3320 if ( fullSel || sellen )
3321 p->setPen(pal.highlightedText().color());
3322
3323 p->drawText(xpos, baseline, QString(c));
3324 } else if (
3325 (
3326 (c == ' ')
3327 &&
3328 (
3329 (
3330 leading
3331 &&
3332 showLeading
3333 )
3334 ||
3335 (
3336 (idx > last)
3337 &&
3338 showTrailing
3339 )
3340 )
3341 )
3342 ||
3343 (
3344 (c == '\t')
3345 &&
3346 showTabs
3347 )
3348 )
3349 {
3350 //p->drawText(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent, QChar(0x231E));
3351
3352 p->translate(xpos - m_spaceSignOffset, ypos + QDocumentPrivate::m_ascent);
3353 p->drawPoints(m_spaceSign, sizeof(m_spaceSign) / sizeof(QPoint));
3354 p->translate(m_spaceSignOffset - xpos,-ypos - QDocumentPrivate::m_ascent);
3355 }
3356
3357 for ( int cidx = 0; cidx < cursor.count(); ++cidx )
3358 {
3359 if ( cursor[cidx] == idx )
3360 p->drawLine(xpos, ypos, xpos, ypos + QDocumentPrivate::m_lineSpacing);
3361 }
3362
3363 if ( idx < m_composited.count() )
3364 {
3365 QFormat format = m_doc->impl()->m_formatScheme->format(m_composited[idx]);
3366
3367 //qDebug("underline pos : %i", p->fontMetrics().underlinePos());
3368
3369 const int ydo = baseline + p->fontMetrics().underlinePos();
3370 const int yin = baseline - p->fontMetrics().strikeOutPos();
3371 const int yup = baseline - p->fontMetrics().overlinePos();
3372
3373 if ( format.overline )
3374 {
3375 p->drawLine(xpos, yup, xpos + cwidth, yup);
3376 }
3377
3378 if ( format.strikeout )
3379 {
3380 p->drawLine(xpos, yin, xpos + cwidth, yin);
3381 }
3382
3383 if ( format.underline )
3384 {
3385 p->drawLine(xpos, ydo, xpos + cwidth, ydo);
3386 }
3387
3388 if ( format.waveUnderline )
3389 {
3390 QPen oldpen = p->pen();
3391 p->setPen(QColor(255, 0, 0));
3392
3393 p->drawLine(xpos, ydo,
3394 xpos + cwidth / 2 , ypos + QDocumentPrivate::m_lineSpacing - 1);
3395
3396 p->drawLine(xpos + cwidth / 2, ypos + QDocumentPrivate::m_lineSpacing - 1,
3397 xpos + cwidth, ydo);
3398
3399 p->setPen(oldpen);
3400 }
3401 }
3402 } else {
3403 if ( wrapped && (wrap < m_frontiers.count()) && (idx >= m_frontiers.at(wrap).first) )
3404 {
3405 ++wrap;
3406 xpos = indent;
3407 ypos += QDocumentPrivate::m_lineSpacing;
3408 }
3409
3410 if ( !c.isSpace() )
3411 {
3412 if ( leading )
3413 indent = xpos;
3414
3415 leading = false;
3416 }
3417 }
3418
3419 if ( sellen )
3420 leftSel = !(--sellen);
3421 else
3422 leftSel = false;
3423
3424 if ( leftSel )
3425 {
3426 p->setPen(pal.text().color());
3427 fmt = 0;
3428 }
3429
3430 xpos += cwidth;
3431 ++column;
3432 ++idx;
3433
3434 if ( !wrapped && xpos > maxWidth )
3435 break;
3436 }
3437
3438 // ensure drawing of cursor at EOL
3439 for ( int cidx = 0; cidx < cursor.count(); ++cidx )
3440 {
3441 if ( cursor[cidx] == idx )
3442 p->drawLine(xpos, ypos, xpos, ypos + QDocumentPrivate::m_lineSpacing);
3443 }
3444
3445 if ( wrapped && unbounded )
3446 p->fillRect(
3447 xpos, ypos,
3448 maxWidth - xpos, QDocumentPrivate::m_lineSpacing,
3449 pal.highlight()
3450 );
3451
3452 }
3453 }
3454
3455 //////////////////
3456
3457
3458 /////////////////////////
3459 // QDocumentCursorHandle
3460 /////////////////////////
QDocumentCursorHandle(QDocument * d,int line)3461 QDocumentCursorHandle::QDocumentCursorHandle(QDocument *d, int line)
3462 : m_flags(ColumnMemory), m_doc(d),
3463 #if QT_VERSION >= 0x040400
3464 m_ref(0),
3465 #endif
3466 m_begOffset(0), m_endOffset(0), m_max(0), m_begLine(line), m_endLine(-1)
3467 {
3468 #if QT_VERSION < 0x040400
3469 m_ref.init(0);
3470 #endif
3471
3472 //m_blocks.push(0);
3473 //qDebug("Cursor handle created : 0x%x", this);
3474 }
3475
~QDocumentCursorHandle()3476 QDocumentCursorHandle::~QDocumentCursorHandle()
3477 {
3478 //qDebug("Cursor handle deleted : 0x%x", this);
3479 Q_ASSERT(!m_ref);
3480
3481 QDocumentCommand::disableAutoUpdate(this);
3482 }
3483
copy(const QDocumentCursorHandle * c)3484 void QDocumentCursorHandle::copy(const QDocumentCursorHandle *c)
3485 {
3486 if ( !c )
3487 return;
3488
3489 m_begLine = c->m_begLine;
3490 m_begOffset = c->m_begOffset;
3491 m_endLine = c->m_endLine;
3492 m_endOffset = c->m_endOffset;
3493 m_flags = c->m_flags;
3494 m_max = c->m_max;
3495 }
3496
document() const3497 QDocument* QDocumentCursorHandle::document() const
3498 {
3499 return m_doc;
3500 }
3501
clone() const3502 QDocumentCursorHandle* QDocumentCursorHandle::clone() const
3503 {
3504 QDocumentCursorHandle *c = new QDocumentCursorHandle(m_doc);
3505 c->copy(this);
3506
3507 c->setAutoUpdated(isAutoUpdated());
3508
3509 return c;
3510 }
3511
atEnd() const3512 bool QDocumentCursorHandle::atEnd() const
3513 {
3514 if ( !m_doc )
3515 return true;
3516
3517 bool atLineEnd;
3518 QDocumentLine l = m_doc->line(m_begLine);
3519
3520 //qDebug("Cursor handle : 0x%x->atEnd() => 0x%x", this, m_begLine.handle());
3521
3522 if ( l.isValid() )
3523 {
3524 atLineEnd = m_begOffset == l.length();
3525 l = m_doc->line(m_begLine + 1);
3526 } else {
3527 //qWarning("Invalid cursor...");
3528 return true;
3529 }
3530
3531 return l.isNull() && atLineEnd;
3532 }
3533
atStart() const3534 bool QDocumentCursorHandle::atStart() const
3535 {
3536 if ( !m_doc )
3537 return true;
3538
3539 QDocumentLine l = m_doc->line(m_begLine - 1);
3540
3541 return l.isNull() && !m_begOffset;
3542 }
3543
atBlockEnd() const3544 bool QDocumentCursorHandle::atBlockEnd() const
3545 {
3546 return atLineEnd();
3547 }
3548
atBlockStart() const3549 bool QDocumentCursorHandle::atBlockStart() const
3550 {
3551 return atLineStart();
3552 }
3553
atLineEnd() const3554 bool QDocumentCursorHandle::atLineEnd() const
3555 {
3556 if ( !m_doc )
3557 return true;
3558
3559 QDocumentLine l = m_doc->line(m_begLine);
3560
3561 return l.isValid() ? l.length() == m_begOffset : false;
3562 }
3563
atLineStart() const3564 bool QDocumentCursorHandle::atLineStart() const
3565 {
3566 if ( !m_doc )
3567 return true;
3568
3569 QDocumentLine l = m_doc->line(m_begLine);
3570
3571 return l.isValid() ? !m_begOffset : false;
3572 }
3573
hasSelection() const3574 bool QDocumentCursorHandle::hasSelection() const
3575 {
3576 if ( !m_doc )
3577 return false;
3578
3579 QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine);
3580
3581 return l1.isValid() && l2.isValid();
3582 }
3583
isSilent() const3584 bool QDocumentCursorHandle::isSilent() const
3585 {
3586 return hasFlag(Silent);
3587 }
3588
setSilent(bool y)3589 void QDocumentCursorHandle::setSilent(bool y)
3590 {
3591 if ( y )
3592 setFlag(Silent);
3593 else
3594 clearFlag(Silent);
3595 }
3596
isAutoUpdated() const3597 bool QDocumentCursorHandle::isAutoUpdated() const
3598 {
3599 return QDocumentCommand::isAutoUpdated(this);
3600 }
3601
setAutoUpdated(bool y)3602 void QDocumentCursorHandle::setAutoUpdated(bool y)
3603 {
3604 if ( y )
3605 QDocumentCommand::enableAutoUpdate(this);
3606 else
3607 QDocumentCommand::disableAutoUpdate(this);
3608 }
3609
line() const3610 QDocumentLine QDocumentCursorHandle::line() const
3611 {
3612 if ( !m_doc )
3613 return QDocumentLine();
3614
3615 return m_doc->line(m_begLine);
3616 }
3617
anchorLine() const3618 QDocumentLine QDocumentCursorHandle::anchorLine() const
3619 {
3620 if ( !m_doc )
3621 return QDocumentLine();
3622
3623 return m_endLine != -1 ? m_doc->line(m_endLine) : line();
3624 }
3625
lineNumber() const3626 int QDocumentCursorHandle::lineNumber() const
3627 {
3628 return m_begLine;
3629 }
3630
anchorLineNumber() const3631 int QDocumentCursorHandle::anchorLineNumber() const
3632 {
3633 return m_endLine != -1 ? m_endLine : m_begLine;
3634 }
3635
anchorColumnNumber() const3636 int QDocumentCursorHandle::anchorColumnNumber() const
3637 {
3638 if ( !m_doc )
3639 return -1;
3640
3641 return m_doc->line(m_endLine).isValid() ? m_endOffset : m_begOffset;
3642 }
3643
visualColumnNumber() const3644 int QDocumentCursorHandle::visualColumnNumber() const
3645 {
3646 return QDocument::screenLength(
3647 line().text().constData(),
3648 m_begOffset,
3649 QDocument::tabStop()
3650 );
3651
3652 }
3653
columnNumber() const3654 int QDocumentCursorHandle::columnNumber() const
3655 {
3656 return m_begOffset;
3657 }
3658
setColumnNumber(int c,int m)3659 void QDocumentCursorHandle::setColumnNumber(int c, int m)
3660 {
3661 if ( !m_doc )
3662 return;
3663
3664 QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine);
3665
3666 if ( m & QDocumentCursor::KeepAnchor )
3667 {
3668 if ( l2.isNull() )
3669 {
3670 m_endLine = m_begLine;
3671 m_endOffset = m_begOffset;
3672 }
3673
3674 m_begOffset = c; //qBound(0, c, l1.length());
3675 } else {
3676 m_endLine = -1;
3677 m_endOffset = 0;
3678 m_begOffset = c; //qBound(0, c, l1.length());
3679 }
3680
3681 refreshColumnMemory();
3682 }
3683
documentPosition() const3684 QPoint QDocumentCursorHandle::documentPosition() const
3685 {
3686 if ( !m_doc )
3687 return QPoint();
3688
3689 return QPoint(0, m_doc->y(m_begLine)) + m_doc->line(m_begLine).cursorToDocumentOffset(m_begOffset);
3690 }
3691
anchorDocumentPosition() const3692 QPoint QDocumentCursorHandle::anchorDocumentPosition() const
3693 {
3694 if ( !m_doc )
3695 return QPoint();
3696
3697 if ( m_endLine < 0 || m_endOffset < 0 )
3698 return documentPosition();
3699
3700 return QPoint(0, m_doc->y(m_endLine)) + m_doc->line(m_endLine).cursorToDocumentOffset(m_endOffset);
3701 }
3702
documentRegion() const3703 QPolygon QDocumentCursorHandle::documentRegion() const
3704 {
3705 QPolygon poly;
3706 QPoint p = documentPosition(), ap = anchorDocumentPosition();
3707
3708 int w = m_doc->width();
3709 const int lm = m_doc->impl()->m_leftMargin;
3710 const int ls = m_doc->impl()->m_lineSpacing - 1;
3711
3712 if ( p == ap )
3713 {
3714 poly
3715 << p
3716 << QPoint(p.x() + 1, p.y())
3717 << QPoint(p.x() + 1, p.y() + ls)
3718 << QPoint(p.x(), p.y() + ls);
3719 } else if ( p.y() == ap.y() ) {
3720 poly
3721 << p
3722 << ap
3723 << QPoint(ap.x(), ap.y() + ls)
3724 << QPoint(p.x(), p.y() + ls);
3725 } else if ( p.y() < ap.y() ) {
3726 poly
3727 << p
3728 << QPoint(w, p.y());
3729
3730 if ( ap.x() < w )
3731 poly << QPoint(w, ap.y()) << ap;
3732
3733 poly
3734 << QPoint(ap.x(), ap.y() + ls)
3735 << QPoint(lm, ap.y() + ls)
3736 << QPoint(lm, p.y() + ls);
3737
3738 if ( p.x() > lm )
3739 poly << QPoint(p.x(), p.y() + ls);
3740 } else {
3741 poly
3742 << ap
3743 << QPoint(w, ap.y());
3744
3745 if ( p.x() < w )
3746 poly << QPoint(w, p.y()) << p;
3747
3748 poly
3749 << QPoint(p.x(), p.y() + ls)
3750 << QPoint(lm, p.y() + ls)
3751 << QPoint(lm, ap.y() + ls);
3752
3753 if ( ap.x() > lm )
3754 poly << QPoint(ap.x(), ap.y() + ls);
3755 }
3756
3757 return poly;
3758 }
3759
position() const3760 int QDocumentCursorHandle::position() const
3761 {
3762 if ( !m_doc )
3763 return -1;
3764
3765 int pos = m_doc->line(m_begLine).position();
3766
3767 return (pos != -1) ? pos + m_begOffset : pos;
3768 }
3769
shift(int offset)3770 void QDocumentCursorHandle::shift(int offset)
3771 {
3772 if ( !m_doc )
3773 return;
3774
3775 QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine);
3776
3777 if ( l1.isValid() )
3778 m_begOffset = qBound(0, m_begOffset + offset, l1.length());
3779
3780 if ( l2.isValid() )
3781 m_endOffset = qBound(0, m_endOffset + offset, l2.length());
3782 }
3783
refreshColumnMemory()3784 void QDocumentCursorHandle::refreshColumnMemory()
3785 {
3786 //m_max = m_doc->line(line).cursorToX(offset);
3787 m_max = hasFlag(ColumnMemory) ? m_doc->line(m_begLine).cursorToDocumentOffset(m_begOffset).x() : 0;
3788 }
3789
hasColumnMemory() const3790 bool QDocumentCursorHandle::hasColumnMemory() const
3791 {
3792 return hasFlag(ColumnMemory);
3793 }
3794
setColumnMemory(bool y)3795 void QDocumentCursorHandle::setColumnMemory(bool y)
3796 {
3797 if ( y )
3798 setFlag(ColumnMemory);
3799 else
3800 clearFlag(ColumnMemory);
3801 }
3802
setPosition(int pos,int m)3803 void QDocumentCursorHandle::setPosition(int pos, int m)
3804 {
3805 Q_UNUSED(pos)
3806 Q_UNUSED(m)
3807
3808 qWarning("Set position to cursor using character index : forbidden...");
3809 /*
3810 if ( m == QDocumentCursor::MoveAnchor )
3811 {
3812 m_begLine = m_doc->findLine(pos);
3813 m_begOffset = (m_begLine.isValid() ? pos : 0);
3814
3815 m_endLine = QDocumentLine();
3816 m_endOffset = 0;
3817
3818 m_max = m_begLine.cursorToX(m_begOffset);
3819 } else {
3820 m_endLine = m_doc->findLine(pos);
3821 m_endOffset = (m_begLine.isValid() ? pos : 0);
3822
3823 //m_max = 0;
3824 }
3825 */
3826 }
3827
movePosition(int count,int op,int m)3828 bool QDocumentCursorHandle::movePosition(int count, int op, int m)
3829 {
3830 if ( !m_doc )
3831 return false;
3832
3833 QDocumentLine l, l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine);
3834
3835 int &line = m_begLine;
3836 int &offset = m_begOffset;
3837 static QRegExp wordStart("\\b\\w+$"), wordEnd("^\\w+\\b");
3838
3839 if ( !(m & QDocumentCursor::KeepAnchor) )
3840 {
3841 m_endLine = -1;
3842 m_endOffset = 0;
3843 } else if ( !l2.isValid() ) {
3844 m_endLine = m_begLine;
3845 m_endOffset = m_begOffset;
3846 }
3847
3848 int beg = 0, end = m_doc->lines();
3849
3850 switch ( op )
3851 {
3852 case QDocumentCursor::Left :
3853 {
3854 if ( atStart() )
3855 return false;
3856
3857 int remaining = offset;
3858
3859 do
3860 {
3861 if ( remaining >= count )
3862 {
3863 offset = remaining - count;
3864 break;
3865 } else if ( line == beg ) {
3866 offset = 0;
3867 break;
3868 }
3869
3870 do
3871 {
3872 --line;
3873 } while ( (line > beg) && m_doc->line(line).hasFlag(QDocumentLine::Hidden) );
3874
3875 //*line = *it;
3876
3877 count -= remaining + 1; // jumping a line is one char
3878 offset = remaining = m_doc->line(line).length();
3879 } while ( count && remaining );
3880
3881 refreshColumnMemory();
3882
3883 break;
3884 }
3885
3886 case QDocumentCursor::Right :
3887 {
3888 if ( atEnd() )
3889 return false;
3890
3891 int remaining = m_doc->line(line).length() - offset;
3892
3893 do
3894 {
3895 if ( remaining >= count )
3896 {
3897 offset += count;
3898 break;
3899 } else if ( (line + 1) == end ) {
3900 offset = remaining;
3901 break;
3902 }
3903
3904 do
3905 {
3906 ++line;
3907 } while ( ((line+1) < end) && m_doc->line(line).hasFlag(QDocumentLine::Hidden) );
3908
3909 //*line = *it;
3910
3911 offset = 0;
3912 count -= remaining + 1;
3913 remaining = m_doc->line(line).length();
3914 } while ( count && remaining );
3915
3916 refreshColumnMemory();
3917
3918 break;
3919 }
3920
3921 case QDocumentCursor::Up :
3922 {
3923 if ( atStart() )
3924 return false;
3925
3926 //qDebug("%i, %i : up", line, offset);
3927
3928 if ( m & QDocumentCursor::ThroughWrap )
3929 {
3930 QPoint p = documentPosition();
3931
3932 if ( hasColumnMemory() )
3933 p.rx() = qMax(p.x(), m_max);
3934
3935 p.ry() -= QDocumentPrivate::m_lineSpacing * count;
3936
3937 if ( p.y() >= 0 )
3938 {
3939 m_doc->cursorForDocumentPosition(p, line, offset);
3940 } else {
3941 line = 0;
3942 offset = 0;
3943 }
3944
3945 return true;
3946 }
3947
3948 while ( count && (line > beg) )
3949 {
3950 --line;
3951
3952 if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) )
3953 --count;
3954
3955 }
3956
3957 //*line = QDocumentLine(*it);
3958 //*offset = line->xToCursor(qMin(line->cursorToX(*offset), m_max), 0);
3959 l = m_doc->line(line);
3960
3961 if ( count )
3962 offset = 0;
3963 else if ( m == QDocumentCursor::MoveAnchor )
3964 offset = l.xToCursor(
3965 qMax(
3966 l.cursorToX(
3967 qMin(
3968 offset,
3969 l.length()
3970 )
3971 ),
3972 m_max
3973 )
3974 );
3975 else
3976 offset = qMin(l.length(), offset);
3977
3978 break;
3979 }
3980
3981 case QDocumentCursor::Down :
3982 {
3983 if ( atEnd() )
3984 return false;
3985
3986 if ( m & QDocumentCursor::ThroughWrap )
3987 {
3988 QPoint p = documentPosition();
3989
3990 if ( hasColumnMemory() )
3991 p.rx() = qMax(p.x(), m_max);
3992
3993 p.ry() += QDocumentPrivate::m_lineSpacing * count;
3994
3995 int oldLine = line, oldCol = offset;
3996 m_doc->cursorForDocumentPosition(p, line, offset);
3997 if ( oldLine == line && oldCol == offset )
3998 offset = m_doc->line(line).length();
3999 return true;
4000 }
4001
4002 while ( count && ((line + 1) < end) )
4003 {
4004 ++line;
4005
4006 if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) )
4007 --count;
4008
4009 }
4010
4011 //*line = QDocumentLine(*it);
4012 l = m_doc->line(line);
4013
4014 if ( count )
4015 offset = l.length();
4016 else if ( m == QDocumentCursor::MoveAnchor )
4017 offset = l.xToCursor(
4018 qMax(
4019 l.cursorToX(
4020 qMin(
4021 offset,
4022 l.length()
4023 )
4024 ),
4025 m_max
4026 )
4027 );
4028 else
4029 offset = qMin(l.length(), offset);
4030
4031 break;
4032 }
4033
4034 case QDocumentCursor::Start :
4035 if ( atStart() )
4036 return false;
4037
4038 m_max = offset = 0;
4039 line = 0; //m_doc->line(0);
4040 break;
4041
4042 case QDocumentCursor::End :
4043 if ( atEnd() )
4044 return false;
4045
4046 line = end - 1; //QDocumentLine(*(m_doc->impl()->end() - 1));
4047 offset = m_doc->line(line).length();
4048 refreshColumnMemory();
4049 break;
4050
4051 case QDocumentCursor::StartOfBlock :
4052 if ( atBlockStart() )
4053 return false;
4054
4055 m_max = offset = 0;
4056 break;
4057
4058 case QDocumentCursor::EndOfBlock :
4059 if ( atBlockEnd() )
4060 return false;
4061
4062 offset = m_doc->line(line).length();
4063 refreshColumnMemory();
4064 break;
4065
4066 case QDocumentCursor::NextBlock :
4067
4068 if ( atEnd() )
4069 return false;
4070
4071 while ( ((line + 1) < end) && count )
4072 {
4073 ++line;
4074
4075 if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) )
4076 --count;
4077
4078 }
4079
4080 if ( !count )
4081 {
4082 //*line = *it;
4083 offset = 0;
4084 } else {
4085 //*line = QDocumentLine(*(m_doc->impl()->end() - 1));
4086 offset = m_doc->line(line).length();
4087 }
4088
4089 break;
4090
4091 case QDocumentCursor::PreviousBlock :
4092
4093 if ( atStart() )
4094 return false;
4095
4096 offset = 0;
4097
4098 while ( (line > beg) && count )
4099 {
4100 --line;
4101
4102 if ( !m_doc->line(line).hasFlag(QDocumentLine::Hidden) )
4103 --count;
4104
4105 }
4106
4107 if ( !count )
4108 {
4109 //*line = *it;
4110 offset = m_doc->line(line).length();
4111 } else {
4112 offset = 0;
4113 //*line = QDocumentLine(*(m_doc->impl()->begin()));
4114 }
4115
4116 //*line = *it;
4117
4118 break;
4119
4120 case QDocumentCursor::WordLeft :
4121 case QDocumentCursor::PreviousWord :
4122 {
4123 if ( atStart() )
4124 return false;
4125
4126 l = m_doc->line(line);
4127
4128 //for ( int loop = 0; loop <= 1; ++loop )
4129 //{
4130 // while ( l.isValid() )
4131
4132 // -- patch --
4133 /* eats up white space */
4134 while ( (offset > 0) && !isWord(l.text().at(offset - 1)) )
4135 --offset;
4136
4137 /* start of line */
4138 if ( offset == 0 )
4139 {
4140 /* first line, first char => nothing to do */
4141 if( line == beg )
4142 return true;
4143
4144 do
4145 // -- patch --
4146 {
4147 // //offset = qMin(offset, l.length() - 1);
4148 // bool next = (l.length() && offset >= 0) ? isWord(l.text().at(offset)) : true;
4149 //
4150 // if ( loop )
4151 // next = !next;
4152 //
4153 // if ( !next )
4154 // break;
4155 //
4156 // if ( offset > 0 )
4157 // {
4158 // --offset;
4159 // } else if ( line != beg ) {
4160 // do
4161 // {
4162 // //*line = *(--it);
4163 // --line;
4164 // l = m_doc->line(line);
4165 // offset = l.length() - 1;
4166 // } while ( l.isValid() && (line != beg) && l.hasFlag(QDocumentLine::Hidden) );
4167 // } else {
4168 // break;
4169 // }
4170 // }
4171 // }
4172 //
4173 // while ( l.isValid() )
4174 // {
4175 // offset = qMin(offset, l.length());
4176 // bool next = (offset <= 0) ? false : isWord(l.text().at(offset - 1));
4177 //
4178 // if ( !next )
4179 // break;
4180 //
4181 // --offset;
4182
4183 // -- patch --
4184 --line;
4185 l = m_doc->line(line);
4186 offset = l.length();
4187 } while ( (line != beg) && l.isValid() && l.hasFlag(QDocumentLine::Hidden) );
4188 return true;
4189 // -- patch --
4190 }
4191
4192 // -- patch --
4193 /* eats up whole word */
4194 while ( (offset > 0) && isWord(l.text().at(offset - 1)) )
4195 --offset;
4196 // -- patch --
4197
4198 refreshColumnMemory();
4199
4200 break;
4201 }
4202
4203 case QDocumentCursor::WordRight :
4204 case QDocumentCursor::NextWord :
4205 {
4206 if ( atEnd() )
4207 return false;
4208
4209 l = m_doc->line(line);
4210 int lineLength = l.text().length();
4211
4212 // for ( int loop = 0; loop <= 1; ++loop )
4213 // -- patch --
4214 /* end of line */
4215 if ( offset == lineLength )
4216 {
4217 // while ( l.isValid() )
4218 /* last line, last char => nothing to do */
4219 if ( line == end )
4220 return true;
4221 // -- patch --
4222 do
4223 {
4224 // //offset = qBound(0, offset, l.length() - 1);
4225 // bool next = (offset < l.length()) ? isWord(l.text().at(offset)) : true;
4226 //
4227 // if ( loop )
4228 // next = !next;
4229 //
4230 // if ( !next )
4231 // break;
4232 //
4233 // if ( offset < l.length() )
4234 // {
4235 // ++offset;
4236 // } else if ( (line + 1) != end ) {
4237 // offset = 0;
4238 // do
4239 // {
4240 // ++line;
4241 // l = m_doc->line(line);
4242 // } while ( l.isValid() && ((line + 1) != end) && (l.hasFlag(QDocumentLine::Hidden) || !l.length()) );
4243 // } else {
4244 // -- patch --
4245 ++line;
4246 l = m_doc->line(line);
4247 offset = 0;
4248 } while ( (line != end) && l.isValid() && l.hasFlag(QDocumentLine::Hidden) );
4249
4250 lineLength = l.text().length();
4251 /* empty line */
4252 if ( lineLength == 0 )
4253 return true;
4254
4255 /* eats up white space */
4256 while ( !isWord(l.text().at(offset)) )
4257 {
4258 ++offset;
4259 /* move to end of line */
4260 if ( offset == lineLength )
4261 break;
4262 // -- patch --
4263 // }
4264 }
4265 // -- patch --
4266 return true;
4267 // -- patch --
4268 }
4269
4270 // -- patch --
4271 /* next char */
4272 ++offset;
4273 /* eats up whole word */
4274 while ( (offset < lineLength) && isWord(l.text().at(offset)) )
4275 ++offset;
4276
4277 /* eats up white space */
4278 while ( (offset < lineLength) && !isWord(l.text().at(offset)) )
4279 ++offset;
4280 // -- patch --
4281
4282 refreshColumnMemory();
4283
4284 break;
4285 }
4286
4287 case QDocumentCursor::StartOfWord :
4288 {
4289 int x = wordStart.indexIn(m_doc->line(line).text().left(offset));
4290
4291 if ( x != -1 )
4292 {
4293 offset = x;
4294 } else {
4295 qDebug("failed to find SOW : %i + %i != %i",
4296 x, wordStart.matchedLength(), offset);
4297
4298 return false;
4299 }
4300
4301 refreshColumnMemory();
4302
4303 break;
4304 }
4305
4306 case QDocumentCursor::EndOfWord :
4307 {
4308 int x = wordEnd.indexIn(m_doc->line(line).text(), offset, QRegExp::CaretAtOffset);
4309
4310 if ( x == offset )
4311 {
4312 offset += wordEnd.matchedLength();
4313 } else {
4314 qDebug("failed to find EOW");
4315 return false;
4316 }
4317
4318 refreshColumnMemory();
4319
4320 break;
4321 }
4322
4323 default:
4324 qWarning("Unhandled move operation...");
4325 return false;
4326 };
4327
4328 return true;
4329 }
4330
moveTo(const QDocumentCursor & c)4331 void QDocumentCursorHandle::moveTo(const QDocumentCursor &c)
4332 {
4333 if ( !c.isValid() || !m_doc )
4334 return;
4335
4336 m_begLine = c.handle()->m_begLine;
4337 m_begOffset = c.handle()->m_begOffset;
4338
4339 m_endLine = -1;
4340 m_endOffset = 0;
4341
4342 refreshColumnMemory();
4343 }
4344
moveTo(int line,int column)4345 void QDocumentCursorHandle::moveTo(int line, int column)
4346 {
4347 m_begLine = line;
4348 m_begOffset = column;
4349
4350 m_endLine = -1;
4351 m_endOffset = 0;
4352
4353 refreshColumnMemory();
4354 }
4355
insertText(const QString & s,bool keepAnchor)4356 void QDocumentCursorHandle::insertText(const QString& s, bool keepAnchor)
4357 {
4358 if ( !m_doc || s.isEmpty() || m_doc->line(m_begLine).isNull() )
4359 return;
4360
4361 bool sel = hasSelection();
4362
4363 if ( sel )
4364 {
4365 beginEditBlock();
4366 removeSelectedText(keepAnchor);
4367 }
4368
4369 QDocumentCommand *command = new QDocumentInsertCommand(
4370 m_begLine,
4371 m_begOffset,
4372 s,
4373 m_doc
4374 );
4375
4376 command->setKeepAnchor(keepAnchor);
4377 command->setTargetCursor(this);
4378 execute(command);
4379
4380 if ( sel )
4381 endEditBlock();
4382 }
4383
eraseLine()4384 void QDocumentCursorHandle::eraseLine()
4385 {
4386 if ( !m_doc )
4387 return;
4388
4389 QDocumentCommand *command = 0;
4390
4391 if ( m_endLine == -1 )
4392 {
4393 command = new QDocumentEraseCommand(
4394 m_begLine,
4395 0,
4396 m_begLine + 1,
4397 0,
4398 m_doc
4399 );
4400 } else {
4401 command = new QDocumentEraseCommand(
4402 qMin(m_begLine, m_endLine),
4403 0,
4404 qMax(m_begLine, m_endLine) + 1,
4405 0,
4406 m_doc
4407 );
4408 }
4409
4410 command->setTargetCursor(this);
4411 execute(command);
4412 }
4413
nextChar() const4414 QChar QDocumentCursorHandle::nextChar() const
4415 {
4416 if ( !m_doc )
4417 return QChar();
4418
4419 QDocumentLine l = m_doc->line(m_begLine);
4420
4421 if ( !l.isValid() )
4422 return QChar();
4423
4424 return m_begOffset < l.length() ? l.text().at(m_begOffset) : QLatin1Char('\n');
4425 }
4426
previousChar() const4427 QChar QDocumentCursorHandle::previousChar() const
4428 {
4429 if ( !m_doc || (m_begLine <= 0 && m_begOffset <= 0) )
4430 return QChar();
4431
4432 QDocumentLine l = m_doc->line(m_begLine);
4433
4434 if ( !l.isValid() || m_begOffset > l.length() )
4435 return QChar();
4436
4437 return m_begOffset ? l.text().at(m_begOffset - 1) : QLatin1Char('\n');
4438 }
4439
deleteChar()4440 void QDocumentCursorHandle::deleteChar()
4441 {
4442 if ( !m_doc )
4443 return;
4444
4445 QDocumentLine l = m_doc->line(m_begLine);
4446
4447 if ( l.isNull() || atEnd() )
4448 return;
4449
4450 QDocumentCommand *command = 0;
4451
4452 if ( !atLineEnd() )
4453 {
4454 command = new QDocumentEraseCommand(
4455 m_begLine,
4456 m_begOffset,
4457 m_begLine,
4458 m_begOffset + 1,
4459 m_doc
4460 );
4461
4462 } else {
4463 // merge two blocks...
4464 command = new QDocumentEraseCommand(
4465 m_begLine,
4466 m_begOffset,
4467 m_begLine + 1,
4468 0,
4469 m_doc
4470 );
4471
4472 }
4473
4474 command->setTargetCursor(this);
4475 command->setUndoOffset(-1);
4476 execute(command);
4477 }
4478
deletePreviousChar()4479 void QDocumentCursorHandle::deletePreviousChar()
4480 {
4481 if ( !m_doc )
4482 return;
4483
4484 QDocumentLine l = m_doc->line(m_begLine);
4485
4486 if ( l.isNull() || atStart() )
4487 return;
4488
4489 QDocumentCommand *command = 0;
4490
4491 if ( !atLineStart() )
4492 {
4493 command = new QDocumentEraseCommand(
4494 m_begLine,
4495 m_begOffset - 1,
4496 m_begLine,
4497 m_begOffset,
4498 m_doc
4499 );
4500
4501 } else {
4502 // merge two blocks...
4503 QDocumentLine prev = m_doc->line(m_begLine - 1);
4504
4505 command = new QDocumentEraseCommand(
4506 m_begLine - 1,
4507 prev.length(),
4508 m_begLine,
4509 m_begOffset,
4510 m_doc
4511 );
4512
4513 }
4514
4515 command->setTargetCursor(this);
4516 execute(command);
4517 }
4518
execute(QDocumentCommand * c)4519 void QDocumentCursorHandle::execute(QDocumentCommand *c)
4520 {
4521 if ( !m_doc )
4522 return;
4523
4524 if ( isSilent() && !c->isSilent() )
4525 c->setSilent(isSilent());
4526
4527 if ( m_blocks.count() )
4528 {
4529 c->redo();
4530 m_blocks.top()->addCommand(c);
4531
4532 } else if ( m_doc ) {
4533 //qDebug("Cursor handle executing command : 0x%x", this);
4534
4535 m_doc->execute(c);
4536 }
4537 }
4538
beginEditBlock()4539 void QDocumentCursorHandle::beginEditBlock()
4540 {
4541 m_blocks.push(new QDocumentCommandBlock(m_doc));
4542 }
4543
endEditBlock()4544 void QDocumentCursorHandle::endEditBlock()
4545 {
4546 if ( !m_doc || m_blocks.isEmpty() )
4547 return;
4548
4549 //qDebug("Cursor handle executing command : 0x%x [block]", this);
4550
4551 QDocumentCommandBlock *block = m_blocks.pop();
4552
4553 // special trick to prevent double redo() while getting rid of
4554 // bugs occuring in when inserting/erasing in overlapping lines
4555 // inside a command block
4556 block->setWeakLock(true);
4557
4558 m_doc->execute(block);
4559 }
4560
selectionStart() const4561 QDocumentCursor QDocumentCursorHandle::selectionStart() const
4562 {
4563 if ( !m_doc )
4564 return QDocumentCursor();
4565
4566 if ( !hasSelection() )
4567 return QDocumentCursor(clone());
4568
4569 QDocumentCursor pos(m_doc, m_begLine, m_begOffset),
4570 anc(m_doc, m_endLine, m_endOffset);
4571
4572 return (pos < anc) ? pos : anc;
4573 }
4574
selectionEnd() const4575 QDocumentCursor QDocumentCursorHandle::selectionEnd() const
4576 {
4577 if ( !m_doc )
4578 return QDocumentCursor();
4579
4580 if ( !hasSelection() )
4581 return QDocumentCursor(clone());
4582
4583 QDocumentCursor pos(m_doc, m_begLine, m_begOffset),
4584 anc(m_doc, m_endLine, m_endOffset);
4585
4586 return (pos > anc) ? pos : anc;
4587 }
4588
eq(const QDocumentCursorHandle * h)4589 bool QDocumentCursorHandle::eq(const QDocumentCursorHandle *h)
4590 {
4591 return (m_begLine == h->m_begLine) && (m_begOffset == h->m_begOffset);
4592 /*
4593 if ( !hasSelection() )
4594 return (m_begLine == h->m_begLine) && (m_begOffset == h->m_begOffset);
4595
4596 return
4597 (m_begLine == h->m_begLine)
4598 &&
4599 (m_begOffset == h->m_begOffset)
4600 &&
4601 (m_endLine == h->m_endLine)
4602 &&
4603 (m_endOffset == h->m_endOffset)
4604 ;
4605 */
4606 }
4607
lt(const QDocumentCursorHandle * h)4608 bool QDocumentCursorHandle::lt(const QDocumentCursorHandle *h)
4609 {
4610 return
4611 (m_begLine < h->m_begLine)
4612 ||
4613 ((m_begLine == h->m_begLine) && (m_begOffset < h->m_begOffset))
4614 ;
4615 }
4616
gt(const QDocumentCursorHandle * h)4617 bool QDocumentCursorHandle::gt(const QDocumentCursorHandle *h)
4618 {
4619 return
4620 (m_begLine > h->m_begLine)
4621 ||
4622 ((m_begLine == h->m_begLine) && (m_begOffset > h->m_begOffset))
4623 ;
4624 }
4625
selectedText() const4626 QString QDocumentCursorHandle::selectedText() const
4627 {
4628 if ( !m_doc )
4629 return QString();
4630
4631 QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine);
4632
4633 if ( l1.isNull() || l2.isNull() )
4634 return QString();
4635
4636 QString s;
4637
4638 if ( m_begLine == m_endLine )
4639 {
4640 int min = qMin(m_begOffset, m_endOffset),
4641 max = qMax(m_begOffset, m_endOffset);
4642
4643 s = l1.text().mid(min, max - min);
4644 } else if ( m_begLine < m_endLine ) {
4645 s = l1.text().mid(m_begOffset);
4646
4647 int it = m_begLine;
4648 //QDocumentConstIterator it = m_doc->impl()->index(m_begLine.handle());
4649
4650 while ( ++it < m_endLine )
4651 {
4652 s += '\n';
4653 s += m_doc->line(it).text();
4654 }
4655
4656 s += '\n';
4657 s += l2.text().left(m_endOffset);
4658 } else {
4659 s = l2.text().mid(m_endOffset);
4660
4661 int it = m_endLine;
4662 //QDocumentConstIterator it = m_doc->impl()->index(m_endLine.handle());
4663
4664 while ( ++it < m_begLine )
4665 {
4666 s += '\n';
4667 s += m_doc->line(it).text();
4668 }
4669
4670 s += '\n';
4671 s += l1.text().left(m_begOffset);
4672 }
4673
4674 return s;
4675 }
4676
clearSelection()4677 void QDocumentCursorHandle::clearSelection()
4678 {
4679 if ( m_doc && m_doc->line(m_endLine).isValid() )
4680 {
4681 //m_begLine = m_endLine;
4682 //m_begOffset = m_endOffset;
4683
4684 m_endLine = -1;
4685 m_endOffset = -1;
4686 }
4687 }
4688
replaceSelectedText(const QString & text)4689 void QDocumentCursorHandle::replaceSelectedText(const QString& text)
4690 {
4691 int begline, begcol;
4692 beginBoundary(begline, begcol);
4693
4694 bool atStart = (begline == m_begLine && begcol == m_begOffset);
4695
4696 if ( text.isEmpty() )
4697 {
4698 removeSelectedText();
4699 } else {
4700 insertText(text, true);
4701
4702 /*
4703 Adjust selection around the new text and preserve the order
4704 of position and anchor
4705 */
4706 /*
4707 if ( atStart )
4708 {
4709 m_endLine = m_begLine;
4710 m_begLine = begline;
4711 m_endOffset = m_begOffset;
4712 m_begOffset = begcol;
4713 } else {
4714 m_endLine = begline;
4715 m_endOffset = begcol;
4716 }
4717 */
4718 }
4719
4720 //qDebug("[%i, %i] => ( (%i, %i), (%i, %i) )", begline, begcol, m_begLine, m_begOffset, m_endLine, m_endOffset);
4721 }
4722
select(QDocumentCursor::SelectionType t)4723 void QDocumentCursorHandle::select(QDocumentCursor::SelectionType t)
4724 {
4725 if ( !m_doc || !m_doc->line(m_begLine).isValid() )
4726 return;
4727
4728 if ( t == QDocumentCursor::LineUnderCursor )
4729 {
4730 movePosition(1, QDocumentCursor::StartOfLine, QDocumentCursor::MoveAnchor);
4731 movePosition(1, QDocumentCursor::EndOfLine, QDocumentCursor::KeepAnchor);
4732
4733 } else if ( t == QDocumentCursor::WordUnderCursor ) {
4734
4735 movePosition(1, QDocumentCursor::StartOfWord, QDocumentCursor::MoveAnchor);
4736 movePosition(1, QDocumentCursor::EndOfWord, QDocumentCursor::KeepAnchor);
4737 }
4738 }
4739
setSelectionBoundary(const QDocumentCursor & c)4740 void QDocumentCursorHandle::setSelectionBoundary(const QDocumentCursor& c)
4741 {
4742 if (
4743 !m_doc
4744 ||
4745 (m_begLine == -1)
4746 ||
4747 (
4748 (c.lineNumber() == m_begLine)
4749 &&
4750 (c.columnNumber() == m_begOffset)
4751 )
4752 )
4753 return;
4754
4755 //qDebug("setting new selection boundary... ");
4756
4757 if ( !hasSelection() )
4758 {
4759 m_endLine = m_begLine;
4760 m_endOffset = m_begOffset;
4761 }
4762
4763 m_begLine = c.lineNumber();
4764 m_begOffset = c.columnNumber();
4765 }
4766
isWithinSelection(const QDocumentCursor & c) const4767 bool QDocumentCursorHandle::isWithinSelection(const QDocumentCursor& c) const
4768 {
4769 if ( !hasSelection() ) //|| c.hasSelection() )
4770 return false;
4771
4772 int minOff, maxOff, min, max;
4773
4774 if ( m_begLine > m_endLine )
4775 {
4776 max = m_begLine;
4777 maxOff = m_begOffset;
4778
4779 min = m_endLine;
4780 minOff = m_endOffset;
4781 } else {
4782 min = m_begLine;
4783 minOff = m_begOffset;
4784
4785 max = m_endLine;
4786 maxOff = m_endOffset;
4787 }
4788
4789 return (m_begLine == m_endLine)
4790 ?
4791 (
4792 (c.lineNumber() == m_begLine)
4793 &&
4794 (qMin(m_begOffset, m_endOffset) <= c.columnNumber())
4795 &&
4796 (qMax(m_begOffset, m_endOffset) >= c.columnNumber())
4797 )
4798 :
4799 (
4800 (
4801 (c.lineNumber() > min)
4802 &&
4803 (c.lineNumber() < max)
4804 )
4805 ||
4806 (
4807 (c.lineNumber() == min)
4808 &&
4809 (minOff <= c.columnNumber())
4810 )
4811 ||
4812 (
4813 (c.lineNumber() == max)
4814 &&
4815 (maxOff >= c.columnNumber())
4816 )
4817 )
4818 ;
4819
4820 }
4821
4822 /*
4823 beware when modifying these as their current form handle the special
4824 case of no selection (m_endLine == -1) and a hasty change may break
4825 that behavior : no selection -> both boundary are the cursor pos = (m_begLine, m_begOffset)
4826 */
beginBoundary(int & begline,int & begcol) const4827 void QDocumentCursorHandle::beginBoundary(int& begline, int& begcol) const
4828 {
4829 if ( m_begLine < m_endLine )
4830 {
4831 begline = m_begLine;
4832 begcol = m_begOffset;
4833 } else {
4834 begline = m_endLine;
4835 begcol = (m_begLine == m_endLine && m_begOffset < m_endOffset) ? m_begOffset : m_endOffset;
4836 }
4837 }
4838
endBoundary(int & endline,int & endcol) const4839 void QDocumentCursorHandle::endBoundary(int& endline, int& endcol) const
4840 {
4841 if ( m_begLine < m_endLine )
4842 {
4843 endline = m_endLine;
4844 endcol = m_endOffset;
4845 } else {
4846 endline = m_begLine;
4847 endcol = (m_begLine == m_endLine && m_begOffset < m_endOffset) ? m_endOffset : m_begOffset;
4848 }
4849 }
4850
boundaries(int & begline,int & begcol,int & endline,int & endcol) const4851 void QDocumentCursorHandle::boundaries(int& begline, int& begcol, int& endline, int& endcol) const
4852 {
4853 beginBoundary(begline, begcol);
4854 endBoundary(endline, endcol);
4855
4856 /*
4857 if ( m_begLine == m_endLine )
4858 {
4859 begline = m_begLine;
4860 endline = m_endLine;
4861 if ( m_begOffset < m_endOffset )
4862 {
4863 begcol = m_begOffset;
4864 endcol = m_endOffset;
4865 } else {
4866 endcol = m_begOffset;
4867 begcol = m_endOffset;
4868 }
4869 } else if ( m_begLine < m_endLine ) {
4870 begline = m_begLine;
4871 endline = m_endLine;
4872 begcol = m_begOffset;
4873 endcol = m_endOffset;
4874 } else {
4875 endline = m_begLine;
4876 begline = m_endLine;
4877 endcol = m_begOffset;
4878 begcol = m_endOffset;
4879 }
4880 */
4881 }
4882
substractBoundaries(int lbeg,int cbeg,int lend,int cend)4883 void QDocumentCursorHandle::substractBoundaries(int lbeg, int cbeg, int lend, int cend)
4884 {
4885 int tlmin, tlmax, tcmin, tcmax;
4886
4887 boundaries(tlmin, tcmin, tlmax, tcmax);
4888
4889 bool begFirst = tlmin == m_begLine && tcmin == m_begOffset;
4890
4891 if ( tlmax < lbeg || tlmin > lend || (tlmax == lbeg && tcmax < cbeg) || (tlmin == lend && tcmin > cend) )
4892 {
4893 // no intersection
4894 return;
4895 }
4896
4897 int numLines = lend - lbeg;
4898 bool beyondBeg = (tlmin > lbeg || (tlmin == lbeg && tcmin >= cbeg));
4899 bool beyondEnd = (tlmax < lend || (tlmax == lend && tcmax <= cend));
4900
4901 if ( beyondBeg && beyondEnd )
4902 {
4903 //qDebug("(%i, %i : %i, %i) erased as in (%i, %i : %i, %i)", tlmin, tcmin, tlmax, tcmax, lbeg, cbeg, lend, cend);
4904 // cursor erased...
4905 m_begLine = m_endLine = lbeg;
4906 m_begOffset = m_endOffset = cbeg;
4907 } else if ( beyondEnd ) {
4908 //qDebug("beyond end");
4909 if ( begFirst )
4910 {
4911 m_endLine = lbeg;
4912 m_endOffset = cbeg;
4913 } else {
4914 m_begLine = lbeg;
4915 m_begOffset = cbeg;
4916 }
4917 } else if ( beyondBeg ) {
4918 //qDebug("beyond beg");
4919 if ( begFirst )
4920 {
4921 m_begLine = lend;
4922 m_begOffset = cend;
4923 if ( numLines )
4924 {
4925 m_begLine -= numLines;
4926 m_endLine -= numLines;
4927 } else {
4928 m_begOffset = cbeg;
4929 }
4930 if ( m_begLine == m_endLine )
4931 m_endOffset -= (cend - cbeg);
4932 } else {
4933 m_endLine = lend;
4934 m_endOffset = cend;
4935 if ( numLines )
4936 {
4937 m_endLine -= numLines;
4938 m_begLine -= numLines;
4939 } else {
4940 m_endOffset = cbeg;
4941 }
4942 if ( m_begLine == m_endLine )
4943 m_begOffset -= (cend - cbeg);
4944 }
4945 } else {
4946 int off = cend - cbeg;
4947
4948 //qDebug("correcting by %i", off);
4949
4950 if ( begFirst )
4951 {
4952 m_endLine -= numLines;
4953 if ( tlmax == lend )
4954 {
4955 m_endOffset -= off;
4956 }
4957 } else {
4958 m_begLine -= numLines;
4959 if ( tlmax == lend )
4960 {
4961 m_begOffset -= off;
4962 }
4963 }
4964 }
4965
4966 //qDebug("(%i, %i : %i, %i) corrected to (%i, %i : %i, %i)", tlmin, tcmin, tlmax, tcmax, m_begLine, m_begOffset, m_endLine, m_endOffset);
4967 }
4968
intersectBoundaries(int & lbeg,int & cbeg,int & lend,int & cend) const4969 void QDocumentCursorHandle::intersectBoundaries(int& lbeg, int& cbeg, int& lend, int& cend) const
4970 {
4971 int tlmin, tlmax, tcmin, tcmax, clmin, clmax, ccmin, ccmax;
4972
4973 boundaries(tlmin, tcmin, tlmax, tcmax);
4974 clmin = lbeg;
4975 clmax = lend;
4976 ccmin = cbeg;
4977 ccmax = cend;
4978
4979 if ( tlmax < clmin || tlmin > clmax || (tlmax == clmin && tcmax < ccmin) || (tlmin == clmax && tcmin > ccmax) )
4980 {
4981 lbeg = cbeg = lend = cend = -1;
4982 return;
4983 }
4984
4985 if ( tlmin == clmin )
4986 {
4987 lbeg = tlmin;
4988 cbeg = qMax(tcmin, ccmin);
4989 } else if ( tlmin < clmin ) {
4990 lbeg = clmin;
4991 cbeg = ccmin;
4992 } else {
4993 lbeg = tlmin;
4994 cbeg = tcmin;
4995 }
4996
4997 if ( tlmax == clmax )
4998 {
4999 lend = tlmax;
5000 cend = qMin(tcmax, ccmax);
5001 } else if ( tlmax < clmax ) {
5002 lend = tlmax;
5003 cend = tcmax;
5004 } else {
5005 lend = clmax;
5006 cend = ccmax;
5007 }
5008 }
5009
intersectBoundaries(QDocumentCursorHandle * h,int & lbeg,int & cbeg,int & lend,int & cend) const5010 void QDocumentCursorHandle::intersectBoundaries(QDocumentCursorHandle *h, int& lbeg, int& cbeg, int& lend, int& cend) const
5011 {
5012 int tlmin, tlmax, tcmin, tcmax, clmin, clmax, ccmin, ccmax;
5013
5014 boundaries(tlmin, tcmin, tlmax, tcmax);
5015 h->boundaries(clmin, ccmin, clmax, ccmax);
5016
5017 if ( tlmax < clmin || tlmin > clmax || (tlmax == clmin && tcmax < ccmin) || (tlmin == clmax && tcmin > ccmax) )
5018 {
5019 lbeg = cbeg = lend = cend = -1;
5020 return;
5021 }
5022
5023 if ( tlmin == clmin )
5024 {
5025 lbeg = tlmin;
5026 cbeg = qMax(tcmin, ccmin);
5027 } else if ( tlmin < clmin ) {
5028 lbeg = clmin;
5029 cbeg = ccmin;
5030 } else {
5031 lbeg = tlmin;
5032 cbeg = tcmin;
5033 }
5034
5035 if ( tlmax == clmax )
5036 {
5037 lend = tlmax;
5038 cend = qMin(tcmax, ccmax);
5039 } else if ( tlmax < clmax ) {
5040 lend = tlmax;
5041 cend = tcmax;
5042 } else {
5043 lend = clmax;
5044 cend = ccmax;
5045 }
5046 }
5047
intersect(const QDocumentCursor & c) const5048 QDocumentCursor QDocumentCursorHandle::intersect(const QDocumentCursor& c) const
5049 {
5050 if ( !hasSelection() )
5051 {
5052 //if ( c.hasSelection() && c.isWithinSelection(QDocumentCursor(this)) )
5053 // return QDocumentCursor(clone());
5054
5055 } else if ( !c.hasSelection() ) {
5056
5057 if ( isWithinSelection(c) )
5058 return c;
5059
5060 } else {
5061 QDocumentCursorHandle *h = c.handle();
5062
5063 int lbeg, lend, cbeg, cend;
5064 intersectBoundaries(h, lbeg, cbeg, lend, cend);
5065
5066 if ( lbeg != -1 )
5067 {
5068 QDocumentCursor c(m_doc, lbeg, cbeg);
5069
5070 if ( lend != -1 && (lbeg != lend || cbeg != cend) )
5071 {
5072 c.setSelectionBoundary(QDocumentCursor(m_doc, lend, cend));
5073 }
5074
5075 return c;
5076 }
5077 }
5078
5079 return QDocumentCursor();
5080 }
5081
removeSelectedText(bool keepAnchor)5082 void QDocumentCursorHandle::removeSelectedText(bool keepAnchor)
5083 {
5084 if ( !m_doc )
5085 return;
5086
5087 QDocumentLine l1 = m_doc->line(m_begLine), l2 = m_doc->line(m_endLine);
5088
5089 if ( l1.isNull() || l2.isNull() )
5090 return;
5091
5092 QDocumentCommand *c;
5093
5094 if ( m_begLine < m_endLine )
5095 {
5096 c = new QDocumentEraseCommand(
5097 m_begLine,
5098 m_begOffset,
5099 m_endLine,
5100 m_endOffset,
5101 m_doc
5102 );
5103
5104 } else if ( m_begLine > m_endLine ) {
5105 c = new QDocumentEraseCommand(
5106 m_endLine,
5107 m_endOffset,
5108 m_begLine,
5109 m_begOffset,
5110 m_doc
5111 );
5112
5113 //m_begLine = m_endLine;
5114 //m_begOffset = m_endOffset;
5115
5116 } else {
5117 c = new QDocumentEraseCommand(
5118 m_begLine,
5119 qMin(m_begOffset, m_endOffset),
5120 m_endLine,
5121 qMax(m_begOffset, m_endOffset),
5122 m_doc
5123 );
5124
5125 //m_begOffset = qMin(m_begOffset, m_endOffset);
5126 //m_endLine = -1;
5127 //m_endOffset = -1;
5128 }
5129
5130 c->setKeepAnchor(keepAnchor);
5131 c->setTargetCursor(this);
5132 execute(c);
5133 }
5134
5135 //////////////////
5136
5137 /////////////////////////
5138 // QDocumentPrivate
5139 /////////////////////////
5140
getStaticDefault()5141 template <class T> T* getStaticDefault() { static T _globStatInst; return &_globStatInst; }
5142
5143 QFont* QDocumentPrivate::m_font = 0;// = QApplication::font();
5144 QFontMetrics* QDocumentPrivate::m_fontMetrics = 0;//(m_font);
5145
5146 int QDocumentPrivate::m_defaultTabStop = 4;
5147 QFormatScheme* QDocumentPrivate::m_defaultFormatScheme = getStaticDefault<QFormatScheme>();
5148
5149 QList<QDocumentPrivate*> QDocumentPrivate::m_documents;
5150
5151 bool QDocumentPrivate::m_fixedPitch;
5152 int QDocumentPrivate::m_ascent;// = m_fontMetrics.ascent();
5153 int QDocumentPrivate::m_descent;// = m_fontMetrics.descent();
5154 int QDocumentPrivate::m_leading;// = m_fontMetrics.leading();
5155 int QDocumentPrivate::m_spaceWidth;// = m_fontMetrics.width(' ');
5156 int QDocumentPrivate::m_lineHeight;// = m_fontMetrics.height();
5157 int QDocumentPrivate::m_lineSpacing;// = m_fontMetrics.lineSpacing();
5158
5159 int QDocumentPrivate::m_leftMargin = 5;
5160 QDocument::WhiteSpaceMode QDocumentPrivate::m_showSpaces = QDocument::ShowNone;
5161 QDocument::LineEnding QDocumentPrivate::m_defaultLineEnding = QDocument::Conservative;
5162
5163 int QDocumentPrivate::m_wrapMargin = 15;
5164
QDocumentPrivate(QDocument * d)5165 QDocumentPrivate::QDocumentPrivate(QDocument *d)
5166 : m_doc(d),
5167 m_editCursor(0),
5168 m_lastGroupId(-1),
5169 m_constrained(false),
5170 m_width(0),
5171 m_height(0),
5172 m_tabStop(m_defaultTabStop),
5173 m_formatScheme(0),
5174 m_language(0),
5175 m_maxMarksPerLine(0),
5176 _nix(0),
5177 _dos(0),
5178 _mac(0),
5179 m_lineEnding(m_defaultLineEnding)
5180 {
5181 m_documents << this;
5182 updateFormatCache();
5183 }
5184
~QDocumentPrivate()5185 QDocumentPrivate::~QDocumentPrivate()
5186 {
5187 m_marks.clear();
5188 m_largest.clear();
5189
5190 m_deleting = true;
5191
5192 //qDeleteAll(m_lines);
5193 foreach ( QDocumentLineHandle *h, m_lines )
5194 h->deref();
5195
5196 m_lines.clear();
5197
5198 m_deleting = false;
5199
5200 m_commands.clear();
5201
5202 m_documents.removeAll(this);
5203 }
5204
findNextMark(int id,int from,int until)5205 int QDocumentPrivate::findNextMark(int id, int from, int until)
5206 {
5207 if ( from < 0 )
5208 from += m_lines.count();
5209
5210 QHash<QDocumentLineHandle*, QList<int> >::const_iterator e = m_marks.constEnd();
5211
5212 int max = until;
5213
5214 if ( max < 0 )
5215 max += m_lines.count();
5216 else if ( max < from )
5217 max = m_lines.count() - 1;
5218
5219 for ( int i = from; i <= max; ++i )
5220 {
5221 QDocumentLineHandle *h = m_lines.at(i);
5222
5223 QHash<QDocumentLineHandle*, QList<int> >::const_iterator it = m_marks.constFind(h);
5224
5225 if ( it != e && it->contains(id) )
5226 return i;
5227
5228 }
5229
5230 if ( until > 0 && until < from )
5231 {
5232 for ( int i = 0; i <= until; ++i )
5233 {
5234 QDocumentLineHandle *h = m_lines.at(i);
5235
5236 QHash<QDocumentLineHandle*, QList<int> >::const_iterator it = m_marks.constFind(h);
5237
5238 if ( it != e && it->contains(id) )
5239 return i;
5240
5241 }
5242 }
5243
5244 return -1;
5245 }
5246
findPreviousMark(int id,int from,int until)5247 int QDocumentPrivate::findPreviousMark(int id, int from, int until)
5248 {
5249 if ( from < 0 )
5250 from += m_lines.count();
5251
5252 if ( until < 0 )
5253 {
5254 until += m_lines.count();
5255 } else if ( until >= m_lines.count() ) {
5256 until = m_lines.count() - 1;
5257 }
5258
5259 QHash<QDocumentLineHandle*, QList<int> >::const_iterator e = m_marks.constEnd();
5260
5261 int min = until;
5262
5263 if ( min > from )
5264 min = 0;
5265
5266 for ( int i = from; i >= min; --i )
5267 {
5268 QDocumentLineHandle *h = m_lines.at(i);
5269
5270 QHash<QDocumentLineHandle*, QList<int> >::const_iterator it = m_marks.constFind(h);
5271
5272 if ( it != e && it->contains(id) )
5273 return i;
5274
5275 }
5276
5277 if ( until > 0 && until > from )
5278 {
5279 for ( int i = m_lines.count() - 1; i >= until; --i )
5280 {
5281 QDocumentLineHandle *h = m_lines.at(i);
5282
5283 QHash<QDocumentLineHandle*, QList<int> >::const_iterator it = m_marks.constFind(h);
5284
5285 if ( it != e && it->contains(id) )
5286 return i;
5287
5288 }
5289 }
5290
5291 return -1;
5292 }
5293
execute(QDocumentCommand * cmd)5294 void QDocumentPrivate::execute(QDocumentCommand *cmd)
5295 {
5296 if ( !cmd )
5297 return;
5298
5299 m_lastModified = QDateTime::currentDateTime();
5300
5301 if ( m_macros.count() )
5302 {
5303 cmd->redo();
5304 m_macros.top()->addCommand(cmd);
5305 } else {
5306 m_commands.push(cmd);
5307 }
5308 }
5309
draw(QPainter * p,QDocument::PaintContext & cxt)5310 void QDocumentPrivate::draw(QPainter *p, QDocument::PaintContext& cxt)
5311 {
5312 QDocumentLineHandle *h;
5313 bool inSel = false, fullSel;
5314 QList<QDocumentCursorHandle*>::iterator cit;
5315 int i, realln, pos = 0, xOffset,
5316 firstLine = qMax(0, cxt.yoffset / m_lineSpacing),
5317 lastLine = qMax(0, firstLine + (cxt.height / m_lineSpacing));
5318
5319 if ( cxt.height % m_lineSpacing )
5320 ++lastLine;
5321
5322 p->setFont(*m_font);
5323
5324 QBrush bg,
5325 base = cxt.palette.base(),
5326 selbg = cxt.palette.highlight(),
5327 alternate = QLineMarksInfoCenter::instance()->markType("current").color;
5328
5329 if ( !alternate.color().isValid() )
5330 alternate = cxt.palette.alternateBase();
5331
5332 QSmallArray m_cursorLines(0), m_selectionBoundaries(0);
5333
5334 int wrap = 0;
5335 i = textLine(firstLine, &wrap);
5336 firstLine -= wrap;
5337 realln = firstLine;
5338
5339 //qDebug("lines [%i, %i]", firstLine, lastLine);
5340
5341 pos += firstLine * m_lineSpacing;
5342
5343 // adjust first line to take selections into account...
5344 foreach ( const QDocumentSelection& s, cxt.selections )
5345 {
5346 if ( (s.startLine < i) && (s.endLine >= i) )
5347 {
5348 inSel = true;
5349 break;
5350 }
5351 }
5352
5353 for ( ; realln <= lastLine; ++i )
5354 {
5355 if ( i >= m_lines.count() )
5356 {
5357 //qDebug("line %i not valid", i);
5358 break;
5359 }
5360
5361 h = m_lines.at(i);
5362
5363 // ugly workaround...
5364 if( !m_fixedPitch )
5365 adjustWidth(i);
5366
5367 const int wrap = h->m_frontiers.count();
5368 const bool wrapped = wrap;
5369
5370 //if ( wrapped )
5371 // qDebug("line %i is wrapped over %i sublines", i, *wit);
5372
5373 // selections stuff (must do it before whatever the visibility...)
5374 m_selectionBoundaries.clear();
5375
5376 fullSel = false;
5377
5378 if ( inSel )
5379 m_selectionBoundaries.prepend(0);
5380
5381 foreach ( const QDocumentSelection& s, cxt.selections )
5382 {
5383 if ( i == s.startLine )
5384 {
5385 if ( !(m_selectionBoundaries.count() & 1) )
5386 m_selectionBoundaries.append(s.start);
5387
5388 if ( i == s.endLine )
5389 {
5390 m_selectionBoundaries.append(s.end);
5391 } else {
5392 //++selLevel;
5393 inSel = true;
5394 //selEnd = h->m_text.length();
5395 }
5396 } else if ( inSel && (i == s.endLine) ) {
5397
5398 if ( m_selectionBoundaries.count() % 2 )
5399 m_selectionBoundaries.append(s.end);
5400
5401 //--selLevel;
5402 inSel = false;
5403 }
5404 }
5405
5406 if ( inSel && m_selectionBoundaries.count() == 1 && m_selectionBoundaries.at(0) == 0 )
5407 {
5408 m_selectionBoundaries.clear();
5409 fullSel = true;
5410 }
5411
5412 if ( h->hasFlag(QDocumentLine::Hidden) )
5413 {
5414 continue;
5415 } else
5416 ++realln;
5417
5418 if ( wrapped )
5419 realln += wrap;
5420
5421 m_cursorLines.clear();
5422
5423 bg = base;
5424
5425 // idx = column = 0;
5426 xOffset = m_leftMargin; // margin
5427
5428 // cursor(s) stuff
5429 cit = cxt.cursors.begin();
5430
5431 while ( cit != cxt.cursors.end() )
5432 {
5433 if ( (*cit)->lineNumber() == i )
5434 {
5435 if ( cxt.blinkingCursor )
5436 m_cursorLines.append((*cit)->columnNumber());
5437
5438 if ( cxt.fillCursorRect )
5439 bg = alternate;
5440
5441 cit = cxt.cursors.erase(cit);
5442 } else {
5443 ++cit;
5444 }
5445 }
5446
5447 cit = cxt.extra.begin();
5448
5449 while ( cit != cxt.extra.end() )
5450 {
5451 if ( (*cit)->lineNumber() == i )
5452 {
5453 m_cursorLines.append((*cit)->columnNumber());
5454
5455 cit = cxt.extra.erase(cit);
5456 } else {
5457 ++cit;
5458 }
5459 }
5460
5461 qSort(m_cursorLines);
5462
5463 QList<int> m = marks(h);
5464
5465 // line marks stuff
5466 if ( m.count() )
5467 {
5468 QLineMarksInfoCenter *mic = QLineMarksInfoCenter::instance();
5469
5470 QColor c = mic->markType(mic->priority(m)).color;
5471
5472 if ( c.isValid() )
5473 bg = c;
5474
5475 }
5476
5477 if ( realln < firstLine )
5478 continue;
5479
5480 //qDebug("drawing line %i (visual %i)", i, realln);
5481
5482 p->fillRect(qMax(cxt.xoffset, m_leftMargin), pos,
5483 cxt.width, m_lineSpacing,
5484 fullSel ? selbg : bg);
5485
5486 if ( wrapped )
5487 p->fillRect(qMax(cxt.xoffset, m_leftMargin), pos + m_lineSpacing,
5488 cxt.width, m_lineSpacing * wrap, fullSel ? selbg : bg);
5489
5490 //p->fillRect(cxt.xoffset, pos + 1,
5491 // cxt.width, m_lineHeight,
5492 // bg);
5493
5494 p->save();
5495
5496 // simplify line drawing
5497 p->translate(0, pos);
5498
5499 // draw text
5500 h->draw(p, cxt.xoffset, cxt.width, m_selectionBoundaries, m_cursorLines, cxt.palette, fullSel);
5501
5502 // see above
5503 p->translate(0, -pos);
5504
5505 // draw fold rect indicator
5506 if ( h->hasFlag(QDocumentLine::CollapsedBlockStart) )
5507 {
5508 p->setBrush(Qt::NoBrush);
5509 p->setPen(QPen(Qt::blue, 1, Qt::DotLine));
5510
5511 //p->drawRect(cxt.xoffset + 2, pos,
5512 // cxt.width - 4, m_lineSpacing - 1);
5513
5514 p->drawRect(m_leftMargin, pos,
5515 cxt.width - 4, m_lineSpacing * (wrap + 1) - 1);
5516
5517 }
5518
5519 p->restore();
5520
5521 pos += m_lineSpacing;
5522
5523 if ( wrapped )
5524 {
5525 pos += m_lineSpacing * wrap;
5526 }
5527
5528 //qDebug("drawing line %i in %i ms", i, t.elapsed());
5529 }
5530
5531 //qDebug("painting done"); // in %i ms...", t.elapsed());
5532 }
5533
position(const QDocumentLineHandle * l) const5534 int QDocumentPrivate::position(const QDocumentLineHandle *l) const
5535 {
5536 int pos = 0;
5537
5538 int idx = m_lines.indexOf(const_cast<QDocumentLineHandle*>(l));
5539
5540 if ( idx == -1 )
5541 return -1;
5542
5543 for ( int i = 0; i < idx; i++ )
5544 pos += m_lines.at(i)->length();
5545
5546 return pos;
5547 }
5548
lineForPosition(int & position) const5549 QDocumentLineHandle* QDocumentPrivate::lineForPosition(int& position) const
5550 {
5551 int pos = 0, idx = 0;
5552
5553 while ( (pos + m_lines.at(idx)->length()) < position )
5554 pos += m_lines.at(idx++)->length();
5555
5556
5557 return 0;
5558 }
5559
setWidth(int width)5560 void QDocumentPrivate::setWidth(int width)
5561 {
5562 int oldConstraint = m_constrained;
5563 m_constrained = width > 0;
5564
5565 if ( m_constrained )
5566 {
5567 int oldWidth = m_width;
5568
5569 m_width = width;
5570
5571 if ( oldConstraint && oldWidth < width )
5572 {
5573 // expand : simply remove old wraps if possible
5574
5575 QMap<int, int>::iterator it = m_wrapped.begin();
5576
5577 while ( it != m_wrapped.end() )
5578 {
5579 QDocumentLineHandle *h = it.key() < m_lines.count() ? m_lines.at(it.key()) : 0;
5580
5581 if ( h )
5582 h->updateWrap();
5583
5584 int sz = h ? h->m_frontiers.count() : 0;
5585
5586 if ( sz )
5587 {
5588 //qDebug("changing wrap at line %i from %i to %i", it.key(), *it, sz);
5589 *it = sz;
5590 ++it;
5591 } else {
5592 //qDebug("removing wrap at line %i", it.key());
5593 it = m_wrapped.erase(it);
5594 }
5595 }
5596 } else if ( oldWidth > width ) {
5597 // shrink : scan whole document and create new wraps wherever needed
5598 //qDebug("global width scan [constraint on]");
5599 //m_wrapped.clear();
5600 setWidth();
5601 }
5602 } else {
5603 //qDebug("global width scan [constraint off]");
5604 m_wrapped.clear();
5605 setWidth();
5606 }
5607
5608 if ( m_editCursor )
5609 {
5610 m_editCursor->refreshColumnMemory();
5611 }
5612
5613 emitWidthChanged();
5614 setHeight();
5615
5616 emitFormatsChanged();
5617 }
5618
setWidth()5619 void QDocumentPrivate::setWidth()
5620 {
5621 m_largest.clear();
5622 const int max = m_lines.count();
5623
5624 if ( m_constrained )
5625 {
5626 int first = -1;
5627
5628 for ( int i = 0; i < max; ++i )
5629 {
5630 QDocumentLineHandle *l = m_lines.at(i);
5631 int olw = l->m_frontiers.count();
5632
5633 l->updateWrap();
5634
5635 int lw = l->m_frontiers.count();
5636
5637 if ( olw == lw )
5638 continue;
5639
5640 if ( lw )
5641 {
5642 //qDebug("added wrap on line %i", line);
5643 m_wrapped[i] = lw;
5644 } else {
5645 //qDebug("removed wrap on line %i", line);
5646 m_wrapped.remove(i);
5647 }
5648
5649 if ( first != -1 )
5650 first = i;
5651 }
5652
5653 if ( first != -1 )
5654 emitFormatsChange(first, -1);
5655 } else {
5656 int oldWidth = m_width;
5657
5658 m_width = 0;
5659
5660 foreach ( QDocumentLineHandle *l, m_lines )
5661 {
5662 if ( l->hasFlag(QDocumentLine::Hidden) )
5663 continue;
5664
5665 l->m_frontiers.clear();
5666
5667 int w = l->cursorToX(l->length());
5668
5669 if ( w > m_width )
5670 {
5671 m_width = w;
5672
5673 m_largest.clear();
5674 m_largest << qMakePair(l, w);
5675 }
5676 }
5677
5678 if ( m_width != oldWidth )
5679 emitWidthChanged();
5680 }
5681 }
5682
5683 static const int widthCacheSize = 5;
5684
adjustWidth(int line)5685 void QDocumentPrivate::adjustWidth(int line)
5686 {
5687 if ( line < 0 || line >= m_lines.count() )
5688 return;
5689
5690 QDocumentLineHandle *l = m_lines.at(line);
5691
5692 if ( m_constrained )
5693 {
5694 int olw = l->m_frontiers.count();
5695
5696 l->updateWrap();
5697
5698 int lw = l->m_frontiers.count();
5699
5700 if ( olw == lw )
5701 return;
5702
5703 if ( l->m_layout )
5704 l->setFlag(QDocumentLine::LayoutDirty);
5705
5706 if ( lw )
5707 {
5708 //qDebug("added wrap on line %i", line);
5709 m_wrapped[line] = lw;
5710 } else {
5711 //qDebug("removed wrap on line %i", line);
5712 m_wrapped.remove(line);
5713 }
5714
5715 emitFormatsChange(line, -1);
5716 setHeight();
5717
5718 } else {
5719 l->m_frontiers.clear();
5720
5721 int w = l->cursorToX(l->length());
5722
5723 if ( w > m_width )
5724 {
5725 m_width = w;
5726 emitWidthChanged();
5727
5728 m_largest.clear();
5729 m_largest << qMakePair(l, w);
5730 } else if ( m_largest.count() && (m_largest.at(0).first == l) ) {
5731 int old = m_largest.at(0).second;
5732
5733 if ( w < old )
5734 setWidth();
5735 }
5736 }
5737 }
5738
setHeight()5739 void QDocumentPrivate::setHeight()
5740 {
5741 int oldHeight = m_height;
5742 int last = visualLine(m_lines.count() - 1) + 1;
5743
5744 if ( m_lines.count() )
5745 last += m_lines.last()->m_frontiers.count();
5746
5747 m_height = last * m_lineSpacing;
5748
5749 if ( oldHeight != m_height )
5750 emitHeightChanged();
5751 }
5752
setFont(const QFont & f)5753 void QDocumentPrivate::setFont(const QFont& f)
5754 {
5755 if ( !m_font )
5756 {
5757 m_font = new QFont;
5758 m_fontMetrics = new QFontMetrics(*m_font);
5759 }
5760
5761 *m_font = f;
5762
5763 // ensures the font is fixed pitch to avoid idsplay glitches
5764 // and inconsistency of column selections
5765 // :( does not work well...
5766 //m_font->setFixedPitch(true);
5767
5768 // set the styling so that if the font is not found Courier one will be used
5769 m_font->setStyleHint(QFont::Courier, QFont::PreferQuality);
5770
5771 *m_fontMetrics = QFontMetrics(*m_font);
5772
5773 m_spaceWidth = m_fontMetrics->width(' ');
5774 m_lineSpacing = m_fontMetrics->lineSpacing();
5775 m_ascent = m_fontMetrics->ascent();
5776 m_descent = m_fontMetrics->descent();
5777 m_leading = m_fontMetrics->leading();
5778
5779 m_lineHeight = m_fontMetrics->height();
5780 //m_lineHeight = m_ascent + m_descent - 2;
5781
5782 m_fixedPitch = QFontInfo(*m_font).fixedPitch();
5783
5784 //if ( !m_fixedPitch )
5785 // qDebug("unsafe computations...");
5786
5787 foreach ( QDocumentPrivate *d, m_documents )
5788 {
5789 d->updateFormatCache();
5790 d->setWidth();
5791 d->setHeight();
5792 }
5793 }
5794
setFormatScheme(QFormatScheme * f)5795 void QDocumentPrivate::setFormatScheme(QFormatScheme *f)
5796 {
5797 m_formatScheme = f;
5798 updateFormatCache();
5799 }
5800
tunePainter(QPainter * p,int fid)5801 void QDocumentPrivate::tunePainter(QPainter *p, int fid)
5802 {
5803 if ( fid < m_fonts.count() )
5804 {
5805 p->setFont(m_fonts.at(fid));
5806 //p->setPen(m_colors.at(fid));
5807 } else {
5808 p->setFont(*m_font);
5809 //p->setPen(Qt::black);
5810 }
5811 }
5812
updateFormatCache()5813 void QDocumentPrivate::updateFormatCache()
5814 {
5815 m_fonts.clear();
5816
5817 if ( !m_font )
5818 return;
5819
5820 if ( !m_formatScheme )
5821 {
5822 m_fonts << *m_font;
5823 return;
5824 }
5825
5826 QFont f(*m_font);
5827 const int end = m_formatScheme->formatCount();
5828
5829 m_fonts.reserve(end);
5830
5831 for ( int i = 0; i < end; i++ )
5832 {
5833 QFormat fmt = m_formatScheme->format(i);
5834
5835 f.setWeight(fmt.weight);
5836 f.setItalic(fmt.italic);
5837
5838 m_fonts << f;
5839 }
5840
5841 //foreach ( QDocumentPrivate *d, m_documents )
5842 // d->emitFormatsChanged();
5843
5844 emitFormatsChanged();
5845 }
5846
emitWidthChanged()5847 void QDocumentPrivate::emitWidthChanged()
5848 {
5849 if ( !m_doc )
5850 return;
5851
5852 emit m_doc->widthChanged(m_width);
5853
5854 emit m_doc->sizeChanged(QSize(m_width, m_height));
5855 }
5856
emitHeightChanged()5857 void QDocumentPrivate::emitHeightChanged()
5858 {
5859 if ( !m_doc )
5860 return;
5861
5862 emit m_doc->heightChanged(m_height);
5863
5864 emit m_doc->sizeChanged(QSize(m_width, m_height));
5865 }
5866
insertLines(int after,const QList<QDocumentLineHandle * > & l)5867 void QDocumentPrivate::insertLines(int after, const QList<QDocumentLineHandle*>& l)
5868 {
5869 //qDebug("inserting : %i, %i", after, l.count());
5870
5871 int i = 0;
5872
5873 foreach ( QDocumentLineHandle *h, l )
5874 {
5875 h->setFlag(QDocumentLine::Hidden, false);
5876 h->setFlag(QDocumentLine::CollapsedBlockStart, false);
5877 h->setFlag(QDocumentLine::CollapsedBlockEnd, false);
5878 h->m_frontiers.clear();
5879 }
5880
5881 QMap<int, int>::iterator it = m_hidden.begin();
5882
5883 while ( it != m_hidden.end() )
5884 {
5885 if ( (it.key() <= after) && ((it.key() + *it) > after) )
5886 {
5887 *it += l.count();
5888
5889 foreach ( QDocumentLineHandle *h, l )
5890 h->setFlag(QDocumentLine::Hidden, true);
5891 }
5892
5893 ++it;
5894 }
5895
5896 ++after;
5897 updateHidden(after, l.count());
5898 updateWrapped(after, l.count());
5899
5900 while ( i < l.count() )
5901 {
5902 // TODO : move (and abstract somehow) inside the line (handle?)
5903 l.at(i)->m_context.reset();
5904
5905 m_lines.insert(after + i, l.at(i));
5906
5907 adjustWidth(after + i);
5908
5909 ++i;
5910 }
5911
5912 emit m_doc->lineCountChanged(m_lines.count());
5913 setHeight();
5914 }
5915
removeLines(int after,int n)5916 void QDocumentPrivate::removeLines(int after, int n)
5917 {
5918 if ( (after >= 0) && (after < m_lines.count()) )
5919 m_lines.at(after)->setFlag(QDocumentLine::CollapsedBlockStart, false);
5920
5921 QMap<int, int>::iterator it = m_hidden.begin();
5922
5923 //qDebug("translating %i", visualLine);
5924
5925 while ( it != m_hidden.end() )
5926 {
5927 if ( (it.key() >= after) && (it.key() < (after + n)) )
5928 {
5929 int i = it.key(), end = i + *it, depth = 0;
5930
5931 while ( i <= end )
5932 {
5933 if ( !depth )
5934 m_lines.at(i)->setFlag(QDocumentLine::Hidden, false);
5935
5936 if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockStart) )
5937 ++depth;
5938 else if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockEnd) )
5939 --depth;
5940
5941 ++i;
5942 }
5943
5944 it = m_hidden.erase(it);
5945
5946 } else if ( (it.key() < after) && (it.key() + *it) >= after ) {
5947
5948 if ( (it.key() + *it) > (after + n) )
5949 {
5950 // fully inside
5951 *it -= n;
5952 ++it;
5953 } else {
5954 // goes beyond...
5955 int i = it.key(), end = i + *it, depth = 0;
5956
5957 while ( i <= end )
5958 {
5959 if ( !depth )
5960 m_lines.at(i)->setFlag(QDocumentLine::Hidden, false);
5961
5962 if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockStart) )
5963 ++depth;
5964 else if ( m_lines.at(i)->hasFlag(QDocumentLine::CollapsedBlockEnd) )
5965 --depth;
5966
5967 ++i;
5968 }
5969
5970 it = m_hidden.erase(it);
5971 }
5972 } else {
5973 ++it;
5974 }
5975 }
5976
5977 it = m_wrapped.begin();
5978
5979 while ( it != m_wrapped.end() )
5980 {
5981 if ( (it.key() > after) && (it.key() <= (after + n)) )
5982 {
5983 //qDebug("eraser %i", it.key());
5984 it = m_wrapped.erase(it);
5985 } else {
5986 ++it;
5987 }
5988 }
5989
5990 ++after;
5991 updateHidden(after, -n);
5992 updateWrapped(after, -n);
5993 m_lines.remove(after, n);
5994
5995 emit m_doc->lineCountChanged(m_lines.count());
5996 setHeight();
5997 }
5998
at(int line) const5999 QDocumentLineHandle* QDocumentPrivate::at(int line) const
6000 {
6001 return ((line >= 0) && (line < m_lines.count())) ? m_lines.at(line) : 0;
6002 }
6003
indexOf(const QDocumentLineHandle * l) const6004 int QDocumentPrivate::indexOf(const QDocumentLineHandle *l) const
6005 {
6006 return m_lines.indexOf(const_cast<QDocumentLineHandle*>(l));
6007 }
6008
index(const QDocumentLineHandle * l)6009 QDocumentIterator QDocumentPrivate::index(const QDocumentLineHandle *l)
6010 {
6011 QDocumentIterator i = m_lines.begin();
6012
6013 int idx = indexOf(l);
6014
6015 return (idx != -1) ? i + idx : m_lines.end();
6016 }
6017
index(const QDocumentLineHandle * l) const6018 QDocumentConstIterator QDocumentPrivate::index(const QDocumentLineHandle *l) const
6019 {
6020 QDocumentConstIterator i = m_lines.constBegin();
6021
6022 int idx = indexOf(l);
6023
6024 return (idx != -1) ? i + idx : m_lines.end();
6025 }
6026
next(const QDocumentLineHandle * l) const6027 QDocumentLineHandle* QDocumentPrivate::next(const QDocumentLineHandle *l) const
6028 {
6029 if ( !l )
6030 return m_lines.count() ? m_lines.first() : 0;
6031
6032 int idx = m_lines.indexOf(const_cast<QDocumentLineHandle*>(l));
6033
6034 return ((idx != -1) && ((idx + 1) < m_lines.count())) ? m_lines.at(idx + 1) : 0;
6035 }
6036
previous(const QDocumentLineHandle * l) const6037 QDocumentLineHandle* QDocumentPrivate::previous(const QDocumentLineHandle *l) const
6038 {
6039 if ( !l )
6040 return m_lines.count() ? m_lines.last() : 0;
6041
6042 int idx = m_lines.indexOf(const_cast<QDocumentLineHandle*>(l));
6043
6044 return ((idx != -1) && (idx > 0)) ? m_lines.at(idx - 1) : 0;
6045 }
6046
beginChangeBlock()6047 void QDocumentPrivate::beginChangeBlock()
6048 {
6049 QDocumentCommandBlock *b = new QDocumentCommandBlock(m_doc);
6050
6051 m_macros.push(b);
6052 }
6053
endChangeBlock()6054 void QDocumentPrivate::endChangeBlock()
6055 {
6056 if ( !m_macros.count() )
6057 return;
6058
6059 QDocumentCommandBlock *b = m_macros.pop();
6060 b->setWeakLock(true);
6061
6062 execute(b);
6063 }
6064
6065 /*!
6066 \brief Acquire group id
6067 */
getNextGroupId()6068 int QDocumentPrivate::getNextGroupId()
6069 {
6070 if ( m_freeGroupIds.count() )
6071 return m_freeGroupIds.takeFirst();
6072
6073 return ++m_lastGroupId;
6074 }
6075
6076 /*!
6077 \brief Relase group id
6078 */
releaseGroupId(int groupId)6079 void QDocumentPrivate::releaseGroupId(int groupId)
6080 {
6081 if ( groupId == m_lastGroupId )
6082 {
6083 --m_lastGroupId;
6084 while ( m_freeGroupIds.removeAll(m_lastGroupId) )
6085 {
6086 --m_lastGroupId;
6087 }
6088 } else {
6089 m_freeGroupIds << groupId;
6090 }
6091 }
6092
6093 /*!
6094 \brief Clear matches
6095 */
clearMatches(int groupId)6096 void QDocumentPrivate::clearMatches(int groupId)
6097 {
6098 QHash<int, MatchList>::iterator mit = m_matches.find(groupId);
6099
6100 if ( mit == m_matches.end() )
6101 {
6102 return;
6103 }
6104
6105 MatchList& matches = *mit;
6106
6107 foreach ( const Match& m, matches )
6108 {
6109 m.h->removeOverlay(m.range);
6110 }
6111
6112 matches.index = matches.count();
6113 }
6114
6115 /*!
6116 \brief Highlight the matched sequences
6117
6118 \note Both position are BEFORE the matched characters (cursor position).
6119 */
addMatch(int groupId,int line,int pos,int len,int format)6120 void QDocumentPrivate::addMatch(int groupId, int line, int pos, int len, int format)
6121 {
6122 //qDebug("match (%i, %i, %i)", line, pos, len);
6123
6124 Match m;
6125 m.line = line;
6126 m.h = at(line);
6127 m.range = QFormatRange(pos, len, format);
6128 m_matches[groupId] << m;
6129
6130 m.h->addOverlay(m.range);
6131 }
6132
flushMatches(int groupId)6133 void QDocumentPrivate::flushMatches(int groupId)
6134 {
6135 QHash<int, MatchList>::iterator mit = m_matches.find(groupId);
6136
6137 if ( mit == m_matches.end() )
6138 {
6139 return;
6140 }
6141
6142 MatchList& matches = *mit;
6143
6144 QMap<int, int> areas;
6145
6146 foreach ( const Match& m, matches )
6147 {
6148 int n = 1;
6149 int l = m.line;
6150
6151 //qDebug("simple:(%i, %i)", l, 1);
6152
6153 QMap<int, int>::iterator tmp, it = areas.find(l);
6154
6155 if ( it != areas.end() )
6156 continue;
6157
6158 it = areas.insert(m.line, n);
6159
6160 if ( it != areas.end() && (it - 1) != areas.end() )
6161 {
6162 tmp = it - 1;
6163 int off = tmp.key() + *tmp - l;
6164
6165 if ( off >= 0 && (off < n) )
6166 {
6167 *tmp += n - off;
6168 it = areas.erase(it) - 1;
6169 }
6170 }
6171
6172 if ( it != areas.end() && (it + 1) != areas.end() )
6173 {
6174 tmp = it + 1;
6175 int off = it.key() + *it - tmp.key();
6176
6177 if ( off >= 0 && (off < *tmp) )
6178 {
6179 *it += *tmp;
6180 areas.erase(tmp);
6181 }
6182 }
6183 //emitFormatsChange(m.line, 1);
6184 }
6185
6186 // remove old matches
6187 while ( matches.index )
6188 {
6189 matches.removeFirst();
6190 --matches.index;
6191 }
6192
6193 // send update messages
6194 QMap<int, int>::const_iterator it = areas.constBegin();
6195
6196 while ( it != areas.constEnd() )
6197 {
6198 //qDebug("merged:(%i, %i)", it.key(), *it);
6199 emitFormatsChange(it.key(), *it);
6200
6201 ++it;
6202 }
6203
6204 // update storage "meta-data"
6205 if ( matches.isEmpty() )
6206 {
6207 m_matches.remove(groupId);
6208
6209 releaseGroupId(groupId);
6210 }
6211 //qDebug("done with matches");
6212 }
6213
marks(QDocumentLineHandle * h) const6214 QList<int> QDocumentPrivate::marks(QDocumentLineHandle *h) const
6215 {
6216 //return QList<int>() << 1; //testcase
6217
6218 return m_marks.contains(h) ? m_marks.value(h) : QList<int>();
6219 }
6220
addMark(QDocumentLineHandle * h,int mid)6221 void QDocumentPrivate::addMark(QDocumentLineHandle *h, int mid)
6222 {
6223 QList<int>& l = m_marks[h];
6224
6225 l << mid;
6226
6227 m_maxMarksPerLine = qMax(l.count(), m_maxMarksPerLine);
6228
6229 emitMarkChanged(h, mid, true);
6230 }
6231
toggleMark(QDocumentLineHandle * h,int mid)6232 void QDocumentPrivate::toggleMark(QDocumentLineHandle *h, int mid)
6233 {
6234 if ( m_marks.value(h).contains(mid) )
6235 {
6236 removeMark(h, mid);
6237 } else {
6238 addMark(h, mid);
6239 }
6240 }
6241
removeMark(QDocumentLineHandle * h,int mid)6242 void QDocumentPrivate::removeMark(QDocumentLineHandle *h, int mid)
6243 {
6244 QHash<QDocumentLineHandle*, QList<int> >::iterator it = m_marks.find(h);
6245
6246 if ( it == m_marks.end() )
6247 return;
6248
6249 int count = it->count();
6250 int n = it->removeAll(mid);
6251
6252 if ( it->isEmpty() )
6253 m_marks.erase(it);
6254
6255 if ( n && (count == m_maxMarksPerLine) )
6256 {
6257 QHash<QDocumentLineHandle*, QList<int> >::const_iterator
6258 rit = m_marks.constBegin(),
6259 end = m_marks.constEnd();
6260
6261 m_maxMarksPerLine = 0;
6262
6263 while ( rit != end )
6264 {
6265 m_maxMarksPerLine = qMax(rit->count(), m_maxMarksPerLine);
6266 ++rit;
6267 }
6268 }
6269
6270 emitMarkChanged(h, mid, false);
6271 }
6272
visualLine(int textLine) const6273 int QDocumentPrivate::visualLine(int textLine) const
6274 {
6275 if ( textLine < 0 )
6276 return 0;
6277
6278 int hiddenLines = 0, wrappedLines = 0;
6279 QMap<int, int>::const_iterator hit, wit, he, we;
6280 hit = m_hidden.constBegin();
6281 wit = m_wrapped.constBegin();
6282 he = m_hidden.constEnd();
6283 we = m_wrapped.constEnd();
6284
6285 //qDebug("translating %i", visualLine);
6286
6287 while ( hit != he || wit != we )
6288 {
6289 if ( hit != he && (wit == we || hit.key() <= wit.key()) )
6290 {
6291 int hl = hit.key();
6292
6293 if ( hl >= textLine )
6294 break;
6295
6296 int max = 0;
6297
6298 do
6299 {
6300 max = qMax(max, hit.key() - hl + *hit);
6301 ++hit;
6302 } while ( (hit != he) && (hit.key() <= hl + max) );
6303
6304 hiddenLines += max;
6305
6306 if ( wit != we && wit.key() == hl )
6307 {
6308 wrappedLines += *wit;
6309 ++wit;
6310 }
6311
6312 while ( wit != we )
6313 {
6314 if ( wit.key() > hl + max )
6315 break;
6316
6317 ++wit;
6318 }
6319
6320 } else {
6321 if ( wit.key() >= textLine )
6322 break;
6323
6324 if ( m_lines.at(wit.key())->hasFlag(QDocumentLine::Hidden) )
6325 {
6326 ++wit;
6327 continue;
6328 }
6329
6330 wrappedLines += *wit;
6331 ++wit;
6332 }
6333 }
6334
6335 //qDebug("translating %i => %i", textLine, textLine - hiddenLines + wrappedLines);
6336
6337 return textLine - hiddenLines + wrappedLines;
6338 }
6339
textLine(int visualLine,int * wrap) const6340 int QDocumentPrivate::textLine(int visualLine, int *wrap) const
6341 {
6342 if ( visualLine < 0 )
6343 return 0;
6344
6345 int hiddenLines = 0, wrappedLines = 0, vis = 0, txt = 0, mess = 0;
6346 QMap<int, int>::const_iterator
6347 h = m_hidden.constBegin(),
6348 w = m_wrapped.constBegin(),
6349 he = m_hidden.constEnd(),
6350 we = m_wrapped.constEnd();
6351
6352 //qDebug("translating %i", visualLine);
6353
6354 while ( vis < visualLine )
6355 {
6356 if ( h != he )
6357 {
6358 int hl = h.key();
6359
6360 if ( w == we || hl <= w.key() )
6361 {
6362 if ( visualLine + mess <= hl )
6363 break;
6364
6365 if ( w != we && w.key() == hl )
6366 {
6367 //qDebug("trying to solve : h=(%i, %i), w=(%i, %i)", hl, *h, w.key(), *w);
6368 const int off = (visualLine + mess) - hl;
6369 if ( off <= *w )
6370 {
6371 //qDebug("%i -> %i + %i", visualLine, hl, off);
6372 if ( wrap )
6373 *wrap = off;
6374
6375 return hl;
6376 }
6377 }
6378
6379 int max = 0;
6380
6381 do
6382 {
6383 max = qMax(max, h.key() - hl + *h);
6384 ++h;
6385 } while ( (h != he) && (h.key() <= hl + max) );
6386
6387 // very important : do not forget possible wrapping on folded block start
6388 if ( w != we && w.key() == hl )
6389 {
6390 wrappedLines += *w;
6391 ++w;
6392 }
6393
6394 while ( w != we )
6395 {
6396 if ( w.key() > txt + max )
6397 break;
6398
6399 ++w;
6400 }
6401
6402 hiddenLines += max;
6403
6404 } else {
6405 txt = w.key();
6406
6407 if ( m_lines.at(txt)->hasFlag(QDocumentLine::Hidden) )
6408 {
6409 ++w;
6410 continue;
6411 }
6412
6413 if ( visualLine + mess < txt )
6414 break;
6415
6416 wrappedLines += *w;
6417 ++w;
6418 }
6419 } else if ( w != we ) {
6420 txt = w.key();
6421
6422 if ( m_lines.at(txt)->hasFlag(QDocumentLine::Hidden) )
6423 {
6424 ++w;
6425 continue;
6426 }
6427
6428 if ( visualLine + mess < txt )
6429 break;
6430
6431 wrappedLines += *w;
6432 ++w;
6433 } else {
6434 break;
6435 }
6436
6437 mess = hiddenLines - wrappedLines;
6438 vis = txt - mess;
6439 }
6440
6441 we = m_wrapped.constBegin();
6442
6443 if ( m_wrapped.count() && w != we )
6444 {
6445 --w;
6446
6447 int wl = w.key();
6448 bool hwrap = m_lines.at(wl)->hasFlag(QDocumentLine::Hidden);
6449
6450 if ( !hwrap )
6451 {
6452 int base = this->visualLine(wl);
6453
6454 if ( visualLine >= base && visualLine <= base + *w )
6455 {
6456 //qDebug("rebased : %i to %i", visualLine, base);
6457
6458 if ( wrap )
6459 *wrap = visualLine - base;
6460
6461 return wl;
6462 }
6463 }
6464 }
6465
6466 //qDebug("[visual:text] : %i : %i", visualLine, visualLine + mess);
6467 int off = visualLine + mess - (m_lines.count() - 1);
6468
6469 if ( off > 0 )
6470 {
6471 if ( wrap )
6472 *wrap = m_lines.last()->m_frontiers.count();
6473
6474 return m_lines.count() - 1;
6475 }
6476
6477 return visualLine + mess;
6478 }
6479
hideEvent(int line,int count)6480 void QDocumentPrivate::hideEvent(int line, int count)
6481 {
6482 m_hidden.insertMulti(line, count);
6483
6484 setHeight();
6485 //emitFormatsChange(line, count);
6486 emitFormatsChanged();
6487 }
6488
showEvent(int line,int count)6489 void QDocumentPrivate::showEvent(int line, int count)
6490 {
6491 QMap<int, int>::iterator it = m_hidden.find(line);
6492
6493 while ( (it != m_hidden.end()) && (it.key() == line) )
6494 {
6495 if ( *it == count )
6496 {
6497 // qDebug("showing %i lines from %i", count, line);
6498 it = m_hidden.erase(it);
6499 } else
6500 ++it;
6501 }
6502
6503 setHeight();
6504 //emitFormatsChange(line, count);
6505 emitFormatsChanged();
6506 }
6507
updateHidden(int line,int count)6508 void QDocumentPrivate::updateHidden(int line, int count)
6509 {
6510 if ( m_hidden.isEmpty() || (line > (m_hidden.constEnd() - 1).key() ) )
6511 return;
6512
6513 QMap<int, int> prev = m_hidden;
6514 m_hidden.clear();
6515
6516 QMap<int, int>::const_iterator it = prev.constBegin();
6517
6518 //qDebug("shifting by %i from %i", count, line);
6519
6520 while ( it != prev.constEnd() )
6521 {
6522 if ( it.key() < line )
6523 {
6524 m_hidden.insertMulti(it.key(), *it);
6525 } else {
6526 m_hidden.insertMulti(it.key() + count, *it);
6527 }
6528
6529 ++it;
6530 }
6531 }
6532
updateWrapped(int line,int count)6533 void QDocumentPrivate::updateWrapped(int line, int count)
6534 {
6535 if ( m_wrapped.isEmpty() || (line > (m_wrapped.constEnd() - 1).key() ) )
6536 return;
6537
6538 QMap<int, int> prev = m_wrapped;
6539 QMap<int, int>::iterator it = prev.begin();
6540
6541 m_wrapped.clear();
6542
6543 //qDebug("shifting by %i from %i", count, line);
6544
6545 while ( it != prev.end() )
6546 {
6547 if ( it.key() < line )
6548 {
6549 m_wrapped.insert(it.key(), *it);
6550 } else {
6551 //qDebug("moved wrap from line %i to line %i", it.key(), it.key() + count);
6552 m_wrapped.insert(it.key() + count, *it);
6553 }
6554
6555 ++it;
6556 }
6557 }
6558
emitFormatsChange(int line,int lines)6559 void QDocumentPrivate::emitFormatsChange(int line, int lines)
6560 {
6561 emit m_doc->formatsChange(line, lines);
6562 }
6563
emitContentsChange(int line,int lines)6564 void QDocumentPrivate::emitContentsChange(int line, int lines)
6565 {
6566 //for ( int i = line; i < (line + lines); i++ )
6567 // m_lines.at(i)->cache();
6568
6569 int n = lines;
6570
6571 if ( m_language )
6572 {
6573 n = m_language->tokenize(m_doc, line, lines);
6574 }
6575 //qDebug("%i, %i : %i", line, lines, n);
6576
6577 emit m_doc->contentsChange(line, lines);
6578 emit m_doc->contentsChanged();
6579
6580 if ( n > lines )
6581 emitFormatsChange(line + lines, n - lines);
6582 }
6583
emitFormatsChanged()6584 void QDocumentPrivate::emitFormatsChanged()
6585 {
6586 emit m_doc->formatsChanged();
6587 }
6588
emitContentsChanged()6589 void QDocumentPrivate::emitContentsChanged()
6590 {
6591 //emit m_doc->contentsChanged();
6592 }
6593
emitLineDeleted(QDocumentLineHandle * h)6594 void QDocumentPrivate::emitLineDeleted(QDocumentLineHandle *h)
6595 {
6596 if ( !m_deleting )
6597 {
6598 m_marks.remove(h);
6599
6600 int idx = m_lines.indexOf(h);
6601
6602 if ( idx != -1 )
6603 {
6604 //qDebug("removing line %i", idx);
6605
6606 m_lines.remove(idx);
6607
6608 if ( m_largest.count() && (m_largest.at(0).first == h) )
6609 {
6610 m_largest.remove(0);
6611 setWidth();
6612 }
6613
6614 m_hidden.remove(idx);
6615 m_wrapped.remove(idx);
6616
6617 setHeight();
6618 }
6619 }
6620
6621 emit m_doc->lineDeleted(h);
6622 }
6623
emitMarkChanged(QDocumentLineHandle * l,int m,bool on)6624 void QDocumentPrivate::emitMarkChanged(QDocumentLineHandle *l, int m, bool on)
6625 {
6626 emitFormatsChanged();
6627 emit m_doc->markChanged(l, m, on);
6628 }
6629
6630 /*! @} */
6631