1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <qabstracttextdocumentlayout.h>
41#include <qtextformat.h>
42#include "qtextdocument_p.h"
43#include "qtextengine_p.h"
44#include "qtextlist.h"
45
46#include "qabstracttextdocumentlayout_p.h"
47
48QT_BEGIN_NAMESPACE
49
50QAbstractTextDocumentLayoutPrivate::~QAbstractTextDocumentLayoutPrivate()
51{
52}
53
54QTextObjectInterface::~QTextObjectInterface()
55{
56}
57
58/*!
59 \class QAbstractTextDocumentLayout
60 \reentrant
61
62 \brief The QAbstractTextDocumentLayout class is an abstract base
63 class used to implement custom layouts for QTextDocuments.
64 \inmodule QtGui
65
66 \ingroup richtext-processing
67
68 The standard layout provided by Qt can handle simple word processing
69 including inline images, lists and tables.
70
71 Some applications, e.g., a word processor or a DTP application might need
72 more features than the ones provided by Qt's layout engine, in which case
73 you can subclass QAbstractTextDocumentLayout to provide custom layout
74 behavior for your text documents.
75
76 An instance of the QAbstractTextDocumentLayout subclass can be installed
77 on a QTextDocument object with the
78 \l{QTextDocument::}{setDocumentLayout()} function.
79
80 You can insert custom objects into a QTextDocument; see the
81 QTextObjectInterface class description for details.
82
83 \sa QTextObjectInterface
84*/
85
86/*!
87 \class QTextObjectInterface
88 \brief The QTextObjectInterface class allows drawing of
89 custom text objects in \l{QTextDocument}s.
90 \since 4.5
91 \inmodule QtGui
92
93 A text object describes the structure of one or more elements in a
94 text document; for instance, images imported from HTML are
95 implemented using text objects. A text object knows how to lay out
96 and draw its elements when a document is being rendered.
97
98 Qt allows custom text objects to be inserted into a document by
99 registering a custom \l{QTextCharFormat::objectType()}{object
100 type} with QTextCharFormat. A QTextObjectInterface must also be
101 implemented for this type and be
102 \l{QAbstractTextDocumentLayout::registerHandler()}{registered}
103 with the QAbstractTextDocumentLayout of the document. When the
104 object type is encountered while rendering a QTextDocument, the
105 intrinsicSize() and drawObject() functions of the interface are
106 called.
107
108 The following list explains the required steps of inserting a
109 custom text object into a document:
110
111 \list
112 \li Choose an \a objectType. The \a objectType is an integer with a
113 value greater or equal to QTextFormat::UserObject.
114 \li Create a QTextCharFormat object and set the object type to the
115 chosen type using the setObjectType() function.
116 \li Implement the QTextObjectInterface class.
117 \li Call QAbstractTextDocumentLayout::registerHandler() with an instance of your
118 QTextObjectInterface subclass to register your object type.
119 \li Insert QChar::ObjectReplacementCharacter with the aforementioned
120 QTextCharFormat of the chosen object type into the document.
121 As mentioned, the functions of QTextObjectInterface
122 \l{QTextObjectInterface::}{intrinsicSize()} and
123 \l{QTextObjectInterface::}{drawObject()} will then be called with the
124 QTextFormat as parameter whenever the replacement character is
125 encountered.
126 \endlist
127
128 A class implementing a text object needs to inherit both QObject
129 and QTextObjectInterface. QObject must be the first class
130 inherited. For instance:
131
132 \snippet qtextobject/textobjectinterface.h 0
133
134 The data of a text object is usually stored in the QTextCharFormat
135 using QTextCharFormat::setProperty(), and then retrieved with
136 QTextCharFormat::property().
137
138 \warning Copy and Paste operations ignore custom text objects.
139
140 \sa {Text Object Example}, QTextCharFormat, QTextLayout
141*/
142
143/*!
144 \fn QTextObjectInterface::~QTextObjectInterface()
145
146 Destroys this QTextObjectInterface.
147*/
148
149/*!
150 \fn virtual QSizeF QTextObjectInterface::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
151
152 The intrinsicSize() function returns the size of the text object
153 represented by \a format in the given document (\a doc) at the
154 given position (\a posInDocument).
155
156 The size calculated will be used for subsequent calls to
157 drawObject() for this \a format.
158
159 \sa drawObject()
160*/
161
162/*!
163 \fn virtual void QTextObjectInterface::drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
164
165 Draws this text object using the specified \a painter.
166
167 The size of the rectangle, \a rect, to draw in is the size
168 previously calculated by intrinsicSize(). The rectangles position
169 is relative to the \a painter.
170
171 You also get the document (\a doc) and the position (\a
172 posInDocument) of the \a format in that document.
173
174 \sa intrinsicSize()
175*/
176
177/*!
178 \fn void QAbstractTextDocumentLayout::update(const QRectF &rect)
179
180 This signal is emitted when the rectangle \a rect has been updated.
181
182 Subclasses of QAbstractTextDocumentLayout should emit this signal when
183 the layout of the contents change in order to repaint.
184*/
185
186/*!
187 \fn void QAbstractTextDocumentLayout::updateBlock(const QTextBlock &block)
188 \since 4.4
189
190 This signal is emitted when the specified \a block has been updated.
191
192 Subclasses of QAbstractTextDocumentLayout should emit this signal when
193 the layout of \a block has changed in order to repaint.
194*/
195
196/*!
197 \fn void QAbstractTextDocumentLayout::documentSizeChanged(const QSizeF &newSize)
198
199 This signal is emitted when the size of the document layout changes to
200 \a newSize.
201
202 Subclasses of QAbstractTextDocumentLayout should emit this signal when the
203 document's entire layout size changes. This signal is useful for widgets
204 that display text documents since it enables them to update their scroll
205 bars correctly.
206
207 \sa documentSize()
208*/
209
210/*!
211 \fn void QAbstractTextDocumentLayout::pageCountChanged(int newPages)
212
213 This signal is emitted when the number of pages in the layout changes;
214 \a newPages is the updated page count.
215
216 Subclasses of QAbstractTextDocumentLayout should emit this signal when
217 the number of pages in the layout has changed. Changes to the page count
218 are caused by changes to the layout or the document content itself.
219
220 \sa pageCount()
221*/
222
223/*!
224 \fn int QAbstractTextDocumentLayout::pageCount() const
225
226 Returns the number of pages contained in the layout.
227
228 \sa pageCountChanged()
229*/
230
231/*!
232 \fn QSizeF QAbstractTextDocumentLayout::documentSize() const
233
234 Returns the total size of the document's layout.
235
236 This information can be used by display widgets to update their scroll bars
237 correctly.
238
239 \sa documentSizeChanged(), QTextDocument::pageSize
240*/
241
242/*!
243 \fn void QAbstractTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
244
245 Draws the layout with the given \a painter using the given \a context.
246*/
247
248/*!
249 \fn int QAbstractTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
250
251 Returns the cursor position for the given \a point with the specified
252 \a accuracy. Returns -1 if no valid cursor position was found.
253*/
254
255/*!
256 \fn void QAbstractTextDocumentLayout::documentChanged(int position, int charsRemoved, int charsAdded)
257
258 This function is called whenever the contents of the document change. A
259 change occurs when text is inserted, removed, or a combination of these
260 two. The change is specified by \a position, \a charsRemoved, and
261 \a charsAdded corresponding to the starting character position of the
262 change, the number of characters removed from the document, and the
263 number of characters added.
264
265 For example, when inserting the text "Hello" into an empty document,
266 \a charsRemoved would be 0 and \a charsAdded would be 5 (the length of
267 the string).
268
269 Replacing text is a combination of removing and inserting. For example, if
270 the text "Hello" gets replaced by "Hi", \a charsRemoved would be 5 and
271 \a charsAdded would be 2.
272
273 For subclasses of QAbstractTextDocumentLayout, this is the central function
274 where a large portion of the work to lay out and position document contents
275 is done.
276
277 For example, in a subclass that only arranges blocks of text, an
278 implementation of this function would have to do the following:
279
280 \list
281 \li Determine the list of changed \l{QTextBlock}(s) using the parameters
282 provided.
283 \li Each QTextBlock object's corresponding QTextLayout object needs to
284 be processed. You can access the \l{QTextBlock}'s layout using the
285 QTextBlock::layout() function. This processing should take the
286 document's page size into consideration.
287 \li If the total number of pages changed, the pageCountChanged() signal
288 should be emitted.
289 \li If the total size changed, the documentSizeChanged() signal should
290 be emitted.
291 \li The update() signal should be emitted to schedule a repaint of areas
292 in the layout that require repainting.
293 \endlist
294
295 \sa QTextLayout
296*/
297
298/*!
299 \class QAbstractTextDocumentLayout::PaintContext
300 \reentrant
301 \inmodule QtGui
302
303 \brief The QAbstractTextDocumentLayout::PaintContext class is a convenience
304 class defining the parameters used when painting a document's layout.
305
306 A paint context is used when rendering custom layouts for QTextDocuments
307 with the QAbstractTextDocumentLayout::draw() function. It is specified by
308 a \l {cursorPosition}{cursor position}, \l {palette}{default text color},
309 \l clip rectangle and a collection of \l selections.
310
311 \sa QAbstractTextDocumentLayout
312*/
313
314/*!
315 \fn QAbstractTextDocumentLayout::PaintContext::PaintContext()
316 \internal
317*/
318
319/*!
320 \variable QAbstractTextDocumentLayout::PaintContext::cursorPosition
321
322 \brief the position within the document, where the cursor line should be
323 drawn.
324
325 The default value is -1.
326*/
327
328/*!
329 \variable QAbstractTextDocumentLayout::PaintContext::palette
330
331 \brief the default color that is used for the text, when no color is
332 specified.
333
334 The default value is the application's default palette.
335*/
336
337/*!
338 \variable QAbstractTextDocumentLayout::PaintContext::clip
339
340 \brief a hint to the layout specifying the area around paragraphs, frames
341 or text require painting.
342
343 Everything outside of this rectangle does not need to be painted.
344
345 Specifying a clip rectangle can speed up drawing of large documents
346 significantly. Note that the clip rectangle is in document coordinates (not
347 in viewport coordinates). It is not a substitute for a clip region set on
348 the painter but merely a hint.
349
350 The default value is a null rectangle indicating everything needs to be
351 painted.
352*/
353
354/*!
355 \variable QAbstractTextDocumentLayout::PaintContext::selections
356
357 \brief the collection of selections that will be rendered when passing this
358 paint context to QAbstractTextDocumentLayout's draw() function.
359
360 The default value is an empty list indicating no selection.
361*/
362
363/*!
364 \class QAbstractTextDocumentLayout::Selection
365 \reentrant
366 \inmodule QtGui
367
368 \brief The QAbstractTextDocumentLayout::Selection class is a convenience
369 class defining the parameters of a selection.
370
371 A selection can be used to specify a part of a document that should be
372 highlighted when drawing custom layouts for QTextDocuments with the
373 QAbstractTextDocumentLayout::draw() function. It is specified using
374 \l cursor and a \l format.
375
376 \sa QAbstractTextDocumentLayout, PaintContext
377*/
378
379/*!
380 \variable QAbstractTextDocumentLayout::Selection::format
381
382 \brief the format of the selection
383
384 The default value is QTextFormat::InvalidFormat.
385*/
386
387/*!
388 \variable QAbstractTextDocumentLayout::Selection::cursor
389 \brief the selection's cursor
390
391 The default value is a null cursor.
392*/
393
394/*!
395 Creates a new text document layout for the given \a document.
396*/
397QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QTextDocument *document)
398 : QObject(*new QAbstractTextDocumentLayoutPrivate, document)
399{
400 Q_D(QAbstractTextDocumentLayout);
401 d->setDocument(document);
402}
403
404/*!
405 \internal
406*/
407QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QAbstractTextDocumentLayoutPrivate &p, QTextDocument *document)
408 :QObject(p, document)
409{
410 Q_D(QAbstractTextDocumentLayout);
411 d->setDocument(document);
412}
413
414/*!
415 \internal
416*/
417QAbstractTextDocumentLayout::~QAbstractTextDocumentLayout()
418{
419}
420
421/*!
422 Registers the given \a component as a handler for items of the given \a objectType.
423
424 \note registerHandler() has to be called once for each object type. This
425 means that there is only one handler for multiple replacement characters
426 of the same object type.
427
428 The text document layout does not take ownership of \c component.
429*/
430void QAbstractTextDocumentLayout::registerHandler(int objectType, QObject *component)
431{
432 Q_D(QAbstractTextDocumentLayout);
433
434 QTextObjectInterface *iface = qobject_cast<QTextObjectInterface *>(component);
435 if (!iface)
436 return; // ### print error message on terminal?
437
438 connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*)));
439
440 QTextObjectHandler h;
441 h.iface = iface;
442 h.component = component;
443 d->handlers.insert(objectType, h);
444}
445
446/*!
447 \since 5.2
448
449 Unregisters the given \a component as a handler for items of the given \a objectType, or
450 any handler if the \a component is not specified.
451*/
452void QAbstractTextDocumentLayout::unregisterHandler(int objectType, QObject *component)
453{
454 Q_D(QAbstractTextDocumentLayout);
455
456 const auto it = d->handlers.constFind(objectType);
457 if (it != d->handlers.cend() && (!component || component == it->component)) {
458 if (component)
459 disconnect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*)));
460 d->handlers.erase(it);
461 }
462}
463
464/*!
465 Returns a handler for objects of the given \a objectType.
466*/
467QTextObjectInterface *QAbstractTextDocumentLayout::handlerForObject(int objectType) const
468{
469 Q_D(const QAbstractTextDocumentLayout);
470
471 QTextObjectHandler handler = d->handlers.value(objectType);
472 if (!handler.component)
473 return nullptr;
474
475 return handler.iface;
476}
477
478/*!
479 Sets the size of the inline object \a item corresponding to the text
480 \a format.
481
482 \a posInDocument specifies the position of the object within the document.
483
484 The default implementation resizes the \a item to the size returned by
485 the object handler's intrinsicSize() function. This function is called only
486 within Qt. Subclasses can reimplement this function to customize the
487 resizing of inline objects.
488*/
489void QAbstractTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
490{
491 Q_D(QAbstractTextDocumentLayout);
492
493 QTextCharFormat f = format.toCharFormat();
494 Q_ASSERT(f.isValid());
495 QTextObjectHandler handler = d->handlers.value(f.objectType());
496 if (!handler.component)
497 return;
498
499 QSizeF s = handler.iface->intrinsicSize(document(), posInDocument, format);
500 item.setWidth(s.width());
501 item.setAscent(s.height());
502 item.setDescent(0);
503}
504
505/*!
506 Lays out the inline object \a item using the given text \a format.
507
508 \a posInDocument specifies the position of the object within the document.
509
510 The default implementation does nothing. This function is called only
511 within Qt. Subclasses can reimplement this function to customize the
512 position of inline objects.
513
514 \sa drawInlineObject()
515*/
516void QAbstractTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
517{
518 Q_UNUSED(item);
519 Q_UNUSED(posInDocument);
520 Q_UNUSED(format);
521}
522
523/*!
524 \fn void QAbstractTextDocumentLayout::drawInlineObject(QPainter *painter, const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextFormat &format)
525
526 This function is called to draw the inline object, \a object, with the
527 given \a painter within the rectangle specified by \a rect using the
528 specified text \a format.
529
530 \a posInDocument specifies the position of the object within the document.
531
532 The default implementation calls drawObject() on the object handlers. This
533 function is called only within Qt. Subclasses can reimplement this function
534 to customize the drawing of inline objects.
535
536 \sa draw()
537*/
538void QAbstractTextDocumentLayout::drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
539 int posInDocument, const QTextFormat &format)
540{
541 Q_UNUSED(item);
542 Q_D(QAbstractTextDocumentLayout);
543
544 QTextCharFormat f = format.toCharFormat();
545 Q_ASSERT(f.isValid());
546 QTextObjectHandler handler = d->handlers.value(f.objectType());
547 if (!handler.component)
548 return;
549
550 handler.iface->drawObject(p, rect, document(), posInDocument, format);
551}
552
553void QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed(QObject *obj)
554{
555 HandlerHash::Iterator it = handlers.begin();
556 while (it != handlers.end())
557 if ((*it).component == obj)
558 it = handlers.erase(it);
559 else
560 ++it;
561}
562
563/*!
564 \internal
565
566 Returns the index of the format at position \a pos.
567*/
568int QAbstractTextDocumentLayout::formatIndex(int pos)
569{
570 QTextDocumentPrivate *pieceTable = QTextDocumentPrivate::get(qobject_cast<QTextDocument *>(parent()));
571 return pieceTable->find(pos).value()->format;
572}
573
574/*!
575 \fn QTextCharFormat QAbstractTextDocumentLayout::format(int position)
576
577 Returns the character format that is applicable at the given \a position.
578*/
579QTextCharFormat QAbstractTextDocumentLayout::format(int pos)
580{
581 QTextDocumentPrivate *pieceTable = QTextDocumentPrivate::get(qobject_cast<QTextDocument *>(parent()));
582 int idx = pieceTable->find(pos).value()->format;
583 return pieceTable->formatCollection()->charFormat(idx);
584}
585
586
587
588/*!
589 Returns the text document that this layout is operating on.
590*/
591QTextDocument *QAbstractTextDocumentLayout::document() const
592{
593 Q_D(const QAbstractTextDocumentLayout);
594 return d->document;
595}
596
597/*!
598 \fn QString QAbstractTextDocumentLayout::anchorAt(const QPointF &position) const
599
600 Returns the reference of the anchor the given \a position, or an empty
601 string if no anchor exists at that point.
602*/
603QString QAbstractTextDocumentLayout::anchorAt(const QPointF& pos) const
604{
605 QTextCharFormat fmt = formatAt(pos).toCharFormat();
606 return fmt.anchorHref();
607}
608
609/*!
610 \since 5.8
611
612 Returns the source of the image at the given position \a pos, or an empty
613 string if no image exists at that point.
614*/
615QString QAbstractTextDocumentLayout::imageAt(const QPointF &pos) const
616{
617 QTextImageFormat fmt = formatAt(pos).toImageFormat();
618 return fmt.name();
619}
620
621/*!
622 \since 5.8
623
624 Returns the text format at the given position \a pos.
625*/
626QTextFormat QAbstractTextDocumentLayout::formatAt(const QPointF &pos) const
627{
628 int cursorPos = hitTest(pos, Qt::ExactHit);
629 if (cursorPos == -1)
630 return QTextFormat();
631
632 // compensate for preedit in the hit text block
633 QTextBlock block = document()->firstBlock();
634 while (block.isValid()) {
635 QRectF blockBr = blockBoundingRect(block);
636 if (blockBr.contains(pos)) {
637 QTextLayout *layout = block.layout();
638 int relativeCursorPos = cursorPos - block.position();
639 const int preeditLength = layout ? layout->preeditAreaText().length() : 0;
640 if (preeditLength > 0 && relativeCursorPos > layout->preeditAreaPosition())
641 cursorPos -= qMin(cursorPos - layout->preeditAreaPosition(), preeditLength);
642 break;
643 }
644 block = block.next();
645 }
646
647 const QTextDocumentPrivate *pieceTable = QTextDocumentPrivate::get(qobject_cast<const QTextDocument *>(parent()));
648 QTextDocumentPrivate::FragmentIterator it = pieceTable->find(cursorPos);
649 return pieceTable->formatCollection()->format(it->format);
650}
651
652/*!
653 \since 5.14
654
655 Returns the block (probably a list item) whose \l{QTextBlockFormat::marker()}{marker}
656 is found at the given position \a pos.
657*/
658QTextBlock QAbstractTextDocumentLayout::blockWithMarkerAt(const QPointF &pos) const
659{
660 QTextBlock block = document()->firstBlock();
661 while (block.isValid()) {
662 if (block.blockFormat().marker() != QTextBlockFormat::MarkerType::NoMarker) {
663 QRectF blockBr = blockBoundingRect(block);
664 QTextBlockFormat blockFmt = block.blockFormat();
665 QFontMetrics fm(block.charFormat().font());
666 qreal totalIndent = blockFmt.indent() + blockFmt.leftMargin() + blockFmt.textIndent();
667 if (block.textList())
668 totalIndent += block.textList()->format().indent() * 40;
669 QRectF adjustedBr = blockBr.adjusted(totalIndent - fm.height(), 0, totalIndent - blockBr.width(), fm.height() - blockBr.height());
670 if (adjustedBr.contains(pos)) {
671 //qDebug() << "hit block" << block.text() << blockBr << adjustedBr << "marker" << block.blockFormat().marker()
672 // << "font" << block.charFormat().font() << "adj" << lineHeight << totalIndent;
673 if (block.blockFormat().hasProperty(QTextFormat::BlockMarker))
674 return block;
675 }
676 }
677 block = block.next();
678 }
679 return QTextBlock();
680}
681
682/*!
683 \fn QRectF QAbstractTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
684
685 Returns the bounding rectangle of \a frame.
686*/
687
688/*!
689 \fn QRectF QAbstractTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
690
691 Returns the bounding rectangle of \a block.
692*/
693
694/*!
695 Sets the paint device used for rendering the document's layout to the given
696 \a device.
697
698 \sa paintDevice()
699*/
700void QAbstractTextDocumentLayout::setPaintDevice(QPaintDevice *device)
701{
702 Q_D(QAbstractTextDocumentLayout);
703 d->paintDevice = device;
704}
705
706/*!
707 Returns the paint device used to render the document's layout.
708
709 \sa setPaintDevice()
710*/
711QPaintDevice *QAbstractTextDocumentLayout::paintDevice() const
712{
713 Q_D(const QAbstractTextDocumentLayout);
714 return d->paintDevice;
715}
716
717QT_END_NAMESPACE
718
719#include "moc_qabstracttextdocumentlayout.cpp"
720