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