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