1 /****************************************************************************
2 **
3 ** Copyright (C) 2006-2009 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 "qdocumentline.h"
17 
18 /*!
19 	\file qdocumentline.cpp
20 	\brief Implementation of the QDocumentLine class.
21 */
22 
23 #include "qdocument_p.h"
24 
25 /*!
26 	\ingroup document
27 	@{
28 */
29 
30 /*!
31        \class QDocumentLine
32 
33        \brief A reference to line objects
34 
35        In QCodeEdit, documents are stored as a list of lines. A QDocumentLine holds
36        a pointer to the data of one line and gives access to its content.
37 
38        It is not meant to be used to iterate over the document, though it is possible
39        for conveneience and compatibility reasons. Indeed, QDocumentLine does not now
40 	   where in the document it is located. It can obtain that information but this is
41 	   a O(n) operation. Navigation within the document is one of the task devoted to
42        QDocumentCursor which can move around in O(1) (or amortized O(1) in some rare
43 	   cases).
44 
45        Lines can be given formatting in various way : "regular" formatting used for
46        highlighting, overlays used mainly to display search matches and similar informations
47        and marks.
48 */
49 
QDocumentLine(QDocument * doc)50 QDocumentLine::QDocumentLine(QDocument *doc)
51  : m_handle(doc ? doc->impl()->at(0) : nullptr)
52 {
53 	//m_lines_backing_store << this;
54 
55 	if ( m_handle )
56 		m_handle->ref();
57 
58 }
59 
QDocumentLine(const QDocumentLine & line)60 QDocumentLine::QDocumentLine(const QDocumentLine& line)
61  : m_handle(line.m_handle)
62 {
63 	//m_lines_backing_store << this;
64 	if ( m_handle )
65 		m_handle->ref();
66 
67 }
68 
QDocumentLine(QDocumentLineHandle * handle)69 QDocumentLine::QDocumentLine(QDocumentLineHandle* handle)
70  : m_handle(handle)
71 {
72 	//m_lines_backing_store << this;
73 	if ( m_handle )
74 		m_handle->ref();
75 
76 }
77 
~QDocumentLine()78 QDocumentLine::~QDocumentLine()
79 {
80 	if ( m_handle )
81 		m_handle->deref();
82 
83 	//m_lines_backing_store.removeAll(this);
84 }
85 
86 /*!
87 	\brief Comparison operator
88 */
operator ==(const QDocumentLine & l) const89 bool QDocumentLine::operator == (const QDocumentLine& l) const
90 {
91 	return m_handle == l.m_handle;
92 	//return lineNumber() == l.lineNumber();
93 }
94 
95 /*!
96 	\brief Comparison operator
97 */
operator !=(const QDocumentLine & l) const98 bool QDocumentLine::operator != (const QDocumentLine& l) const
99 {
100 	return m_handle != l.m_handle;
101 	//return lineNumber() != l.lineNumber();
102 }
103 
104 
105 /*!
106 	\brief copy operator
107 
108 	QDocumentLine objects are just wrappers around the "real" line data.
109 	Copies of a QDocumentLine all points to the same underlying data and
110 	modifying one affect them all (no implicit sharing).
111 */
operator =(const QDocumentLine & l)112 QDocumentLine& QDocumentLine::operator = (const QDocumentLine& l)
113 {
114 	if ( m_handle )
115 		m_handle->deref();
116 
117 	m_handle = l.m_handle;
118 
119 	if ( m_handle )
120 		m_handle->ref();
121 
122 	return *this;
123 }
124 
125 /*!
126 	\return the document to which that line belongs
127 */
document() const128 QDocument* QDocumentLine::document() const
129 {
130     return m_handle ? m_handle->document() : nullptr;
131 }
132 
133 /*!
134 	\brief Check whether a given flag is set to the line
135 */
hasFlag(State s) const136 bool QDocumentLine::hasFlag(State s) const
137 {
138 	return m_handle ? m_handle->hasFlag(s) : false;
139 }
140 
141 /*!
142 	\brief Check whether any of the flags in a given combination is set to the line
143 */
hasAnyFlag(int s) const144 bool QDocumentLine::hasAnyFlag(int s) const
145 {
146 	return m_handle ? m_handle->hasFlag(s) : false;
147 }
148 
149 /*!
150 	\brief Set a flag of the line
151 
152 	\warning Do not mess with flags unless you know what you are doing
153 */
setFlag(State s,bool y)154 void QDocumentLine::setFlag(State s, bool y)
155 {
156 	if ( m_handle )
157 		m_handle->setFlag(s, y);
158 
159 }
160 
161 /*!
162 	\return whether the line object is null (i.e invalid)
163 */
isNull() const164 bool QDocumentLine::isNull() const
165 {
166 	return m_handle ? !m_handle->document() : true;
167 }
168 
169 /*!
170 	\return whether the line object is valid
171 */
isValid() const172 bool QDocumentLine::isValid() const
173 {
174     return m_handle ? m_handle->document()!=nullptr : false;
175 }
176 
177 /*!
178 	\return the text of the line
179 */
text() const180 QString QDocumentLine::text() const
181 {
182 	return m_handle ? m_handle->text() : QString();
183 }
184 
185 /*!
186 	\return The length of the line, in characters
187 */
length() const188 int QDocumentLine::length() const
189 {
190 	return m_handle ? m_handle->text().length() : 0;
191 }
192 
193 /*!
194 	\return the number of visual lines occupied by the line
195 
196 	This is NOT set to zero when the line is hidden (e.g due to folding).
197 */
lineSpan() const198 int QDocumentLine::lineSpan() const
199 {
200 	return m_handle && m_handle->document() ? m_handle->m_frontiers.count() + 1 : 0;
201 }
202 
203 /*!
204 	\brief Returns the position of the first non-whitespace character
205 	\return position of first non-whitespace char or -1 if there is none
206 */
firstChar() const207 int QDocumentLine::firstChar() const
208 {
209 	return nextNonSpaceChar(0);
210 }
211 
212 /*!
213 	\brief Returns the position of the last non-whitespace character
214 	\return position of last non-whitespace char or -1 if there is none
215 */
lastChar() const216 int QDocumentLine::lastChar() const
217 {
218 	return previousNonSpaceChar(length() - 1);
219 }
220 
221 /*!
222 	\return true if the line starts with txt, neglecting spaces
223 */
startsWith(const QString & txt)224 bool QDocumentLine::startsWith(const QString &txt)
225 {
226 	int pos = firstChar();
227 	return pos >= 0 && pos == text().indexOf(txt);
228 }
229 
indent() const230 int QDocumentLine::indent() const
231 {
232 	return m_handle ? m_handle->indent() : 0;
233 }
234 
235 /*!
236 	Find the position of the next char that is not a space.
237 	\param pos Column of the character which is examined first.
238 	\return True if the specified or a following character is not a space
239 	Otherwise false.
240 */
nextNonSpaceChar(int pos) const241 int QDocumentLine::nextNonSpaceChar(int pos) const
242 {
243 	return m_handle ? m_handle->nextNonSpaceChar(pos) : -1;
244 }
245 
246 /*!
247 	\brief Find the position of the previous char that is not a space.
248 	\param pos Column of the character which is examined first.
249 	\return The position of the first non-whitespace character preceding pos,
250    or -1 if none is found.
251 */
previousNonSpaceChar(int pos) const252 int QDocumentLine::previousNonSpaceChar(int pos) const
253 {
254 	return m_handle ? m_handle->previousNonSpaceChar(pos) : -1;
255 }
256 
257 /*!
258 	\brief Converts a cursor position (column) to a document position (unconstrained viewport)
259 
260 	\deprecated Use cursorToDocumentOffset() instead
261 
262 	This function is kept for compatribility only. It dates back to the time before line wrapping
263 	was implemented. Due to the limitation of its design (i.e signature) it works in a somewhat
264 	awkard way : the x position returned is that the cursor would have in an unconstrained viewport,
265 	even when the line is wrapped so it does not map to an actual viewport coordinate, unless of
266 	course no wrapping is used.
267 */
cursorToX(int cpos) const268 int QDocumentLine::cursorToX(int cpos) const
269 {
270 	return m_handle ? m_handle->cursorToX(cpos) : -1;
271 }
272 
273 /*!
274 	\brief Converts a document position (unconstrained viewport) to a cursor position (column)
275 
276 	\deprecated Use cursorToDocumentOffset() instead
277 
278 	\see cursorToX() for more informations about this function
279 */
xToCursor(int xpos) const280 int QDocumentLine::xToCursor(int xpos) const
281 {
282 	return m_handle ? m_handle->xToCursor(xpos) : -1;
283 }
284 
285 /*!
286 	\return The wrapped line (i.e "subline") to which a given cursor position resides
287 	\param cpos cursor position, as a text column
288 */
wrappedLineForCursor(int cpos) const289 int QDocumentLine::wrappedLineForCursor(int cpos) const
290 {
291 	return m_handle ? m_handle->wrappedLineForCursor(cpos) : -1;
292 }
293 
294 /*!
295 	\brief Converts a document offset (viewport) to a cursor position (character / text column)
296 
297 	The (x, y) coordinates given by this function are relative to the absolute
298 	position of the line, which can be obtained from the document.
299 */
documentOffsetToCursor(qreal x,qreal y,bool disallowPositionBeyondLine) const300 int QDocumentLine::documentOffsetToCursor(qreal x, qreal y, bool disallowPositionBeyondLine) const
301 {
302     return m_handle ? m_handle->documentOffsetToCursor(x, y,disallowPositionBeyondLine) : -1;
303 }
304 
305 /*!
306 	\brief Converts a cursor position (character / text column) to a document offset (viewport)
307 
308 	The (x, y) coordinates given by this function are relative to the absolute
309 	position of the line, which can be obtained from the document.
310 */
cursorToDocumentOffset(int cpos,qreal & x,qreal & y) const311 void QDocumentLine::cursorToDocumentOffset(int cpos, qreal& x, qreal& y) const
312 {
313 	if ( m_handle )
314 		m_handle->cursorToDocumentOffset(cpos, x, y);
315 }
316 
317 /*!
318 	\overload
319 */
cursorToDocumentOffset(int cpos) const320 QPointF QDocumentLine::cursorToDocumentOffset(int cpos) const
321 {
322     return m_handle ? m_handle->cursorToDocumentOffset(cpos) : QPointF();
323 }
324 
325 /*!
326 	\brief Toggle a mark on the line
327 */
addMark(int id)328 void QDocumentLine::addMark(int id)
329 {
330 	if ( !document() )
331 		return;
332 
333 	document()->impl()->addMark(m_handle, id);
334 	setFlag(QDocumentLine::LayoutDirty,true);
335 }
336 
337 /*!
338 	\brief Toggle a mark on the line
339 */
toggleMark(int id)340 void QDocumentLine::toggleMark(int id)
341 {
342 	if ( !document() )
343 		return;
344 
345 	document()->impl()->toggleMark(m_handle, id);
346 	setFlag(QDocumentLine::LayoutDirty,true);
347 }
348 
349 /*!
350 	\brief Remove a mark from the line
351 */
removeMark(int id)352 void QDocumentLine::removeMark(int id)
353 {
354 	if ( !document() )
355 		return;
356 
357 	document()->impl()->removeMark(m_handle, id);
358 	setFlag(QDocumentLine::LayoutDirty,true);
359 }
360 
361 /*!
362 	\return the list of marks set on this line
363 */
marks() const364 QList<int> QDocumentLine::marks() const
365 {
366 	return document() ? document()->impl()->marks(m_handle) : QList<int>();
367 }
368 
369 /*!
370 	\return Whether a given mark has been set on the line
371 */
hasMark(int id) const372 bool QDocumentLine::hasMark(int id) const
373 {
374 	return marks().contains(id);
375 }
376 
377 /*!
378 	\brief Set the formatting of the line
379 
380 	\note this method is made available for syntax engine use.
381 	If you want to apply extra formatting on a line use overlays
382 	instead
383 
384 	\see addOverlay()
385 */
setFormats(const QVector<int> & formats)386 void QDocumentLine::setFormats(const QVector<int>& formats)
387 {
388 	if ( !m_handle )
389 		return;
390 
391 	m_handle->setFormats(formats);
392 }
393 
compose()394 QVector<int> QDocumentLine::compose(){
395     if(!m_handle) return QVector<int> ();
396     m_handle->lockForRead();
397     QVector<int> result=m_handle->compose();
398     m_handle->unlock();
399     return result;
400 }
401 
getFormats()402 QVector<int> QDocumentLine::getFormats(){
403     if(!m_handle) return QVector<int> ();
404     return m_handle->getFormats();
405 }
406 
getFormatAt(int pos) const407 int QDocumentLine::getFormatAt(int pos) const{
408 	if( !m_handle ) return -1;
409 	if (pos < 0 ) return -1;
410 	const QVector<int>& formats = m_handle->getFormats();
411 	if( pos >= formats.size() ) return -1;
412 	return formats.at(pos);
413 }
414 
getCachedFormatAt(int pos) const415 int QDocumentLine::getCachedFormatAt(int pos) const{
416     if( !m_handle ) return -1;
417     if (pos < 0 ) return -1;
418     const QVector<int>& formats = m_handle->getCachedFormats();
419     if( pos >= formats.size() ) return -1;
420     return formats.at(pos);
421 }
422 
getCookie(int type)423 QVariant QDocumentLine::getCookie(int type){
424     if(!m_handle) return QVariant();
425     m_handle->lockForRead();
426     QVariant result=m_handle->getCookie(type);
427     m_handle->unlock();
428     return result;
429 }
430 
setCookie(int type,QVariant data)431 void QDocumentLine::setCookie(int type,QVariant data){
432 	if(!m_handle) return;
433     m_handle->lockForWrite();
434 	m_handle->setCookie(type,data);
435     m_handle->unlock();
436 }
437 
removeCookie(int type)438 void QDocumentLine::removeCookie(int type){
439 	if (!m_handle) return;
440     m_handle->lockForWrite();
441 	m_handle->removeCookie(type);
442     m_handle->unlock();
443 }
444 
isRTLByLayout() const445 bool QDocumentLine::isRTLByLayout() const{
446     if (!m_handle) return false;
447      return m_handle->isRTLByLayout();
448 }
449 
isRTLByText() const450 bool QDocumentLine::isRTLByText() const{
451     if (!m_handle) return false;
452      return m_handle->isRTLByText();
453 }
454 
getLayout() const455 QTextLayout* QDocumentLine::getLayout() const{
456     return m_handle ? m_handle->m_layout : nullptr;
457 }
458 
leftCursorPosition(int oldPos) const459 int QDocumentLine::leftCursorPosition(int oldPos) const{
460 	if (!m_handle) return 0;
461 	if (m_handle->m_layout) {
462 		QReadLocker locker(&m_handle->mLock); //no idea if this is needed
463 		try {
464 			return m_handle->m_layout->leftCursorPosition(oldPos);
465 		} catch (std::bad_alloc &){
466 
467 		}
468 	}
469 	return qBound(0, oldPos - 1, length());
470 }
rightCursorPosition(int oldPos) const471 int QDocumentLine::rightCursorPosition(int oldPos) const{
472 	if (!m_handle) return 0;
473 	if (m_handle->m_layout) {
474 		QReadLocker locker(&m_handle->mLock); //no idea if this is needed
475 		try {
476 			return m_handle->m_layout->rightCursorPosition(oldPos);
477 		} catch (std::bad_alloc &){ //this is sometimes thrown on qt4.8.7 with 7*hex:(d8ba d8b1) 2478 5e32 2409
478 
479 		}
480 	}
481 	return qBound(0, oldPos + 1, length());
482 }
483 
484 /*!
485 	\return whether the line has at least one overlay of a given format id
486 */
hasOverlay(int fid) const487 bool QDocumentLine::hasOverlay(int fid) const
488 {
489 	if ( !m_handle )
490 		return false;
491 
492 	foreach ( const QFormatRange& r, m_handle->m_overlays )
493 		if ( r.format == fid )
494 			return true;
495 
496 	return false;
497 }
498 
499 /*!
500 	\brief Clear all overlays applied to the line
501 */
overlays() const502 QList<QFormatRange> QDocumentLine::overlays() const
503 {
504 	if ( !m_handle )
505 		return QList<QFormatRange>();
506 
507 	return m_handle->m_overlays;
508 }
509 
510 /*!
511 	\brief Clear all overlays applied to the line
512 */
clearOverlays()513 void QDocumentLine::clearOverlays()
514 {
515 	if ( !m_handle )
516 		return;
517 
518 	m_handle->clearOverlays();
519 }
520 
clearOverlays(int formatToClear)521 void QDocumentLine::clearOverlays(int formatToClear){
522 	if ( !m_handle )
523 		return;
524 
525     m_handle->clearOverlays(formatToClear);
526 }
527 
clearOverlays(QList<int> formatsToClear)528 void QDocumentLine::clearOverlays(QList<int> formatsToClear){
529     if ( !m_handle )
530         return;
531 
532     m_handle->clearOverlays(formatsToClear);
533 }
534 
535 /*!
536 	\brief Add an overlay to the line
537 
538 	Overlays are format range that get applied on top of regular formatting.
539 
540 	They are typically used to display search matches, matching braces, ...
541 */
addOverlay(const QFormatRange & over)542 void QDocumentLine::addOverlay(const QFormatRange& over)
543 {
544 	if ( !m_handle )
545 		return;
546 
547 	m_handle->addOverlay(over);
548 }
549 
550 /*!
551 	\brief Remove an overlay from the line
552 */
removeOverlay(const QFormatRange & over)553 void QDocumentLine::removeOverlay(const QFormatRange& over)
554 {
555 	if ( !m_handle )
556 		return;
557 
558 	m_handle->removeOverlay(over);
559 }
560 
hasOverlay(int id)561 bool QDocumentLine::hasOverlay(int id){
562     if ( !m_handle ) return false;
563     return m_handle->hasOverlay(id);
564 }
565 
566 /*!
567  * \brief Returns all QFormatRanges of the given \a preferredFormat.
568  * Any format will match for \a preferredFormat == -1.
569  */
getOverlays(int preferredFormat) const570 QList<QFormatRange> QDocumentLine::getOverlays(int preferredFormat) const {
571 	if ( !m_handle )
572 		return QList<QFormatRange>();
573 
574 	return m_handle->getOverlays(preferredFormat);
575 }
576 
577 /*!
578  * \brief Returns the QFormatRange for the given \a preferredFormat at the given column number (\a index).
579  * Any format will match for \a preferredFormat == -1.
580  */
getOverlayAt(int index,int preferredFormat) const581 QFormatRange QDocumentLine::getOverlayAt(int index, int preferredFormat) const {
582 	if ( !m_handle )
583 		return QFormatRange();
584 
585 	return m_handle->getOverlayAt(index,preferredFormat);
586 }
587 
588 /*!
589  * \brief Returns the first QFormatRange between columns \a start and \a end for the given \a preferredFormat.
590  * Any format will match for \a preferredFormat == -1.
591  */
getFirstOverlay(int start,int end,int preferredFormat) const592 QFormatRange QDocumentLine::getFirstOverlay(int start, int end, int preferredFormat) const {
593 	if ( !m_handle )
594 		return QFormatRange();
595 
596 	return m_handle->getFirstOverlay(start,end,preferredFormat);
597 }
598 
599 /*!
600  * \brief Returns the last QFormatRange between columns \a start and \a end for the given \a preferredFormat.
601  * Any format will match for \a preferredFormat == -1.
602  */
getLastOverlay(int start,int end,int preferredFormat) const603 QFormatRange QDocumentLine::getLastOverlay(int start, int end, int preferredFormat) const {
604 	if ( !m_handle )
605 		return QFormatRange();
606 
607 	return m_handle->getLastOverlay(start,end,preferredFormat);
608 }
609 
610 
611 /*!
612 	\return the list of parentheses present on the line
613 
614 	\note This is language dependent.
615 */
parentheses() const616 const QVector<QParenthesis>& QDocumentLine::parentheses() const
617 {
618 	Q_CHECK_PTR(m_handle);
619 
620 	return m_handle->m_parens;
621 }
622 
623 /*!
624 	\brief Set the parentheses present on that line
625 
626 	\note this should only be used by syntax engines
627 */
setParentheses(const QVector<QParenthesis> & parentheses)628 void QDocumentLine::setParentheses(const QVector<QParenthesis>& parentheses)
629 {
630 	if ( !m_handle )
631 		return;
632 
633 	m_handle->m_parens = parentheses;
634 }
635 
636 /*!
637 	Reserved to syntax engines. do not mess with this unless you know what you are doing.
638 */
matchContext()639 QNFAMatchContext* QDocumentLine::matchContext()
640 {
641     return m_handle ? &m_handle->m_context : nullptr;
642 }
643 
644 /*! @} */
645 
646