1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <qabstracttextdocumentlayout.h>
43 #include <qtextformat.h>
44 #include "qtextdocument_p.h"
45 #include "qtextengine_p.h"
47 #include "qabstracttextdocumentlayout_p.h"
52 \class QAbstractTextDocumentLayout
55 \brief The QAbstractTextDocumentLayout class is an abstract base
56 class used to implement custom layouts for QTextDocuments.
59 \ingroup richtext-processing
61 The standard layout provided by Qt can handle simple word processing
62 including inline images, lists and tables.
64 Some applications, e.g., a word processor or a DTP application might need
65 more features than the ones provided by Qt's layout engine, in which case
66 you can subclass QAbstractTextDocumentLayout to provide custom layout
67 behavior for your text documents.
69 An instance of the QAbstractTextDocumentLayout subclass can be installed
70 on a QTextDocument object with the
71 \l{QTextDocument::}{setDocumentLayout()} function.
73 You can insert custom objects into a QTextDocument; see the
74 QTextObjectInterface class description for details.
76 \sa QTextObjectInterface
80 \class QTextObjectInterface
81 \brief The QTextObjectInterface class allows drawing of
82 custom text objects in \l{QTextDocument}s.
86 A text object describes the structure of one or more elements in a
87 text document; for instance, images imported from HTML are
88 implemented using text objects. A text object knows how to lay out
89 and draw its elements when a document is being rendered.
91 Qt allows custom text objects to be inserted into a document by
92 registering a custom \l{QTextCharFormat::objectType()}{object
93 type} with QTextCharFormat. A QTextObjectInterface must also be
94 implemented for this type and be
95 \l{QAbstractTextDocumentLayout::registerHandler()}{registered}
96 with the QAbstractTextDocumentLayout of the document. When the
97 object type is encountered while rendering a QTextDocument, the
98 intrinsicSize() and drawObject() functions of the interface are
101 The following list explains the required steps of inserting a
102 custom text object into a document:
105 \li Choose an \a objectType. The \a objectType is an integer with a
106 value greater or equal to QTextFormat::UserObject.
107 \li Create a QTextCharFormat object and set the object type to the
108 chosen type using the setObjectType() function.
109 \li Implement the QTextObjectInterface class.
110 \li Call QAbstractTextDocumentLayout::registerHandler() with an instance of your
111 QTextObjectInterface subclass to register your object type.
112 \li Insert QChar::ObjectReplacementCharacter with the aforementioned
113 QTextCharFormat of the chosen object type into the document.
114 As mentioned, the functions of QTextObjectInterface
115 \l{QTextObjectInterface::}{intrinsicSize()} and
116 \l{QTextObjectInterface::}{drawObject()} will then be called with the
117 QTextFormat as parameter whenever the replacement character is
121 A class implementing a text object needs to inherit both QObject
122 and QTextObjectInterface. QObject must be the first class
123 inherited. For instance:
125 \snippet richtext/textobject/svgtextobject.h 1
127 The data of a text object is usually stored in the QTextCharFormat
128 using QTextCharFormat::setProperty(), and then retrieved with
129 QTextCharFormat::property().
131 \warning Copy and Paste operations ignore custom text objects.
133 \sa {Text Object Example}, QTextCharFormat, QTextLayout
137 \fn QTextObjectInterface::~QTextObjectInterface()
139 Destroys this QTextObjectInterface.
143 \fn virtual QSizeF QTextObjectInterface::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
145 The intrinsicSize() function returns the size of the text object
146 represented by \a format in the given document (\a doc) at the
147 given position (\a posInDocument).
149 The size calculated will be used for subsequent calls to
150 drawObject() for this \a format.
156 \fn virtual void QTextObjectInterface::drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
158 Draws this text object using the specified \a painter.
160 The size of the rectangle, \a rect, to draw in is the size
161 previously calculated by intrinsicSize(). The rectangles position
162 is relative to the \a painter.
164 You also get the document (\a doc) and the position (\a
165 posInDocument) of the \a format in that document.
171 \fn void QAbstractTextDocumentLayout::update(const QRectF &rect)
173 This signal is emitted when the rectangle \a rect has been updated.
175 Subclasses of QAbstractTextDocumentLayout should emit this signal when
176 the layout of the contents change in order to repaint.
180 \fn void QAbstractTextDocumentLayout::updateBlock(const QTextBlock &block)
183 This signal is emitted when the specified \a block has been updated.
185 Subclasses of QAbstractTextDocumentLayout should emit this signal when
186 the layout of \a block has changed in order to repaint.
190 \fn void QAbstractTextDocumentLayout::documentSizeChanged(const QSizeF &newSize)
192 This signal is emitted when the size of the document layout changes to
195 Subclasses of QAbstractTextDocumentLayout should emit this signal when the
196 document's entire layout size changes. This signal is useful for widgets
197 that display text documents since it enables them to update their scroll
204 \fn void QAbstractTextDocumentLayout::pageCountChanged(int newPages)
206 This signal is emitted when the number of pages in the layout changes;
207 \a newPages is the updated page count.
209 Subclasses of QAbstractTextDocumentLayout should emit this signal when
210 the number of pages in the layout has changed. Changes to the page count
211 are caused by changes to the layout or the document content itself.
217 \fn int QAbstractTextDocumentLayout::pageCount() const
219 Returns the number of pages contained in the layout.
221 \sa pageCountChanged()
225 \fn QSizeF QAbstractTextDocumentLayout::documentSize() const
227 Returns the total size of the document's layout.
229 This information can be used by display widgets to update their scroll bars
232 \sa documentSizeChanged(), QTextDocument::pageSize
236 \fn void QAbstractTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
238 Draws the layout with the given \a painter using the given \a context.
242 \fn int QAbstractTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
244 Returns the cursor position for the given \a point with the specified
245 \a accuracy. Returns -1 if no valid cursor position was found.
249 \fn void QAbstractTextDocumentLayout::documentChanged(int position, int charsRemoved, int charsAdded)
251 This function is called whenever the contents of the document change. A
252 change occurs when text is inserted, removed, or a combination of these
253 two. The change is specified by \a position, \a charsRemoved, and
254 \a charsAdded corresponding to the starting character position of the
255 change, the number of characters removed from the document, and the
256 number of characters added.
258 For example, when inserting the text "Hello" into an empty document,
259 \a charsRemoved would be 0 and \a charsAdded would be 5 (the length of
262 Replacing text is a combination of removing and inserting. For example, if
263 the text "Hello" gets replaced by "Hi", \a charsRemoved would be 5 and
264 \a charsAdded would be 2.
266 For subclasses of QAbstractTextDocumentLayout, this is the central function
267 where a large portion of the work to lay out and position document contents
270 For example, in a subclass that only arranges blocks of text, an
271 implementation of this function would have to do the following:
274 \li Determine the list of changed \l{QTextBlock}(s) using the parameters
276 \li Each QTextBlock object's corresponding QTextLayout object needs to
277 be processed. You can access the \l{QTextBlock}'s layout using the
278 QTextBlock::layout() function. This processing should take the
279 document's page size into consideration.
280 \li If the total number of pages changed, the pageCountChanged() signal
282 \li If the total size changed, the documentSizeChanged() signal should
284 \li The update() signal should be emitted to schedule a repaint of areas
285 in the layout that require repainting.
292 \class QAbstractTextDocumentLayout::PaintContext
296 \brief The QAbstractTextDocumentLayout::PaintContext class is a convenience
297 class defining the parameters used when painting a document's layout.
299 A paint context is used when rendering custom layouts for QTextDocuments
300 with the QAbstractTextDocumentLayout::draw() function. It is specified by
301 a \l {cursorPosition}{cursor position}, \l {palette}{default text color},
302 \l clip rectangle and a collection of \l selections.
304 \sa QAbstractTextDocumentLayout
308 \fn QAbstractTextDocumentLayout::PaintContext::PaintContext()
313 \variable QAbstractTextDocumentLayout::PaintContext::cursorPosition
315 \brief the position within the document, where the cursor line should be
318 The default value is -1.
322 \variable QAbstractTextDocumentLayout::PaintContext::palette
324 \brief the default color that is used for the text, when no color is
327 The default value is the application's default palette.
331 \variable QAbstractTextDocumentLayout::PaintContext::clip
333 \brief a hint to the layout specifying the area around paragraphs, frames
334 or text require painting.
336 Everything outside of this rectangle does not need to be painted.
338 Specifying a clip rectangle can speed up drawing of large documents
339 significantly. Note that the clip rectangle is in document coordinates (not
340 in viewport coordinates). It is not a substitute for a clip region set on
341 the painter but merely a hint.
343 The default value is a null rectangle indicating everything needs to be
348 \variable QAbstractTextDocumentLayout::PaintContext::selections
350 \brief the collection of selections that will be rendered when passing this
351 paint context to QAbstractTextDocumentLayout's draw() function.
353 The default value is an empty vector indicating no selection.
357 \class QAbstractTextDocumentLayout::Selection
361 \brief The QAbstractTextDocumentLayout::Selection class is a convenience
362 class defining the parameters of a selection.
364 A selection can be used to specify a part of a document that should be
365 highlighted when drawing custom layouts for QTextDocuments with the
366 QAbstractTextDocumentLayout::draw() function. It is specified using
367 \l cursor and a \l format.
369 \sa QAbstractTextDocumentLayout, PaintContext
373 \variable QAbstractTextDocumentLayout::Selection::format
375 \brief the format of the selection
377 The default value is QTextFormat::InvalidFormat.
381 \variable QAbstractTextDocumentLayout::Selection::cursor
382 \brief the selection's cursor
384 The default value is a null cursor.
388 Creates a new text document layout for the given \a document.
390 QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QTextDocument *document)
391 : QObject(*new QAbstractTextDocumentLayoutPrivate, document)
393 Q_D(QAbstractTextDocumentLayout);
394 d->setDocument(document);
400 QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QAbstractTextDocumentLayoutPrivate &p, QTextDocument *document)
401 :QObject(p, document)
403 Q_D(QAbstractTextDocumentLayout);
404 d->setDocument(document);
410 QAbstractTextDocumentLayout::~QAbstractTextDocumentLayout()
415 \fn void QAbstractTextDocumentLayout::registerHandler(int objectType, QObject *component)
417 Registers the given \a component as a handler for items of the given \a objectType.
419 \note registerHandler() has to be called once for each object type. This
420 means that there is only one handler for multiple replacement characters
421 of the same object type.
423 void QAbstractTextDocumentLayout::registerHandler(int formatType, QObject *component)
425 Q_D(QAbstractTextDocumentLayout);
427 QTextObjectInterface *iface = qobject_cast<QTextObjectInterface *>(component);
429 return; // ### print error message on terminal?
431 connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*)));
433 QTextObjectHandler h;
435 h.component = component;
436 d->handlers.insert(formatType, h);
440 Returns a handler for objects of the given \a objectType.
442 QTextObjectInterface *QAbstractTextDocumentLayout::handlerForObject(int objectType) const
444 Q_D(const QAbstractTextDocumentLayout);
446 QTextObjectHandler handler = d->handlers.value(objectType);
447 if (!handler.component)
450 return handler.iface;
454 Sets the size of the inline object \a item corresponding to the text
457 \a posInDocument specifies the position of the object within the document.
459 The default implementation resizes the \a item to the size returned by
460 the object handler's intrinsicSize() function. This function is called only
461 within Qt. Subclasses can reimplement this function to customize the
462 resizing of inline objects.
464 void QAbstractTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
466 Q_D(QAbstractTextDocumentLayout);
468 QTextCharFormat f = format.toCharFormat();
469 Q_ASSERT(f.isValid());
470 QTextObjectHandler handler = d->handlers.value(f.objectType());
471 if (!handler.component)
474 QSizeF s = handler.iface->intrinsicSize(document(), posInDocument, format);
475 item.setWidth(s.width());
476 item.setAscent(s.height());
481 Lays out the inline object \a item using the given text \a format.
483 \a posInDocument specifies the position of the object within the document.
485 The default implementation does nothing. This function is called only
486 within Qt. Subclasses can reimplement this function to customize the
487 position of inline objects.
489 \sa drawInlineObject()
491 void QAbstractTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
494 Q_UNUSED(posInDocument);
499 \fn void QAbstractTextDocumentLayout::drawInlineObject(QPainter *painter, const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextFormat &format)
501 This function is called to draw the inline object, \a object, with the
502 given \a painter within the rectangle specified by \a rect using the
503 specified text \a format.
505 \a posInDocument specifies the position of the object within the document.
507 The default implementation calls drawObject() on the object handlers. This
508 function is called only within Qt. Subclasses can reimplement this function
509 to customize the drawing of inline objects.
513 void QAbstractTextDocumentLayout::drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
514 int posInDocument, const QTextFormat &format)
517 Q_D(QAbstractTextDocumentLayout);
519 QTextCharFormat f = format.toCharFormat();
520 Q_ASSERT(f.isValid());
521 QTextObjectHandler handler = d->handlers.value(f.objectType());
522 if (!handler.component)
525 handler.iface->drawObject(p, rect, document(), posInDocument, format);
528 void QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed(QObject *obj)
530 HandlerHash::Iterator it = handlers.begin();
531 while (it != handlers.end())
532 if ((*it).component == obj)
533 it = handlers.erase(it);
541 Returns the index of the format at position \a pos.
543 int QAbstractTextDocumentLayout::formatIndex(int pos)
545 QTextDocumentPrivate *pieceTable = qobject_cast<QTextDocument *>(parent())->docHandle();
546 return pieceTable->find(pos).value()->format;
550 \fn QTextCharFormat QAbstractTextDocumentLayout::format(int position)
552 Returns the character format that is applicable at the given \a position.
554 QTextCharFormat QAbstractTextDocumentLayout::format(int pos)
556 QTextDocumentPrivate *pieceTable = qobject_cast<QTextDocument *>(parent())->docHandle();
557 int idx = pieceTable->find(pos).value()->format;
558 return pieceTable->formatCollection()->charFormat(idx);
564 Returns the text document that this layout is operating on.
566 QTextDocument *QAbstractTextDocumentLayout::document() const
568 Q_D(const QAbstractTextDocumentLayout);
573 \fn QString QAbstractTextDocumentLayout::anchorAt(const QPointF &position) const
575 Returns the reference of the anchor the given \a position, or an empty
576 string if no anchor exists at that point.
578 QString QAbstractTextDocumentLayout::anchorAt(const QPointF& pos) const
580 int cursorPos = hitTest(pos, Qt::ExactHit);
584 // compensate for preedit in the hit text block
585 QTextBlock block = document()->firstBlock();
586 while (block.isValid()) {
587 QRectF blockBr = blockBoundingRect(block);
588 if (blockBr.contains(pos)) {
589 QTextLayout *layout = block.layout();
590 int relativeCursorPos = cursorPos - block.position();
591 const int preeditLength = layout ? layout->preeditAreaText().length() : 0;
592 if (preeditLength > 0 && relativeCursorPos > layout->preeditAreaPosition())
593 cursorPos -= qMin(cursorPos - layout->preeditAreaPosition(), preeditLength);
596 block = block.next();
599 QTextDocumentPrivate *pieceTable = qobject_cast<const QTextDocument *>(parent())->docHandle();
600 QTextDocumentPrivate::FragmentIterator it = pieceTable->find(cursorPos);
601 QTextCharFormat fmt = pieceTable->formatCollection()->charFormat(it->format);
602 return fmt.anchorHref();
606 \fn QRectF QAbstractTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
608 Returns the bounding rectangle of \a frame.
612 \fn QRectF QAbstractTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
614 Returns the bounding rectangle of \a block.
618 Sets the paint device used for rendering the document's layout to the given
623 void QAbstractTextDocumentLayout::setPaintDevice(QPaintDevice *device)
625 Q_D(QAbstractTextDocumentLayout);
626 d->paintDevice = device;
630 Returns the paint device used to render the document's layout.
634 QPaintDevice *QAbstractTextDocumentLayout::paintDevice() const
636 Q_D(const QAbstractTextDocumentLayout);
637 return d->paintDevice;
642 #include "moc_qabstracttextdocumentlayout.cpp"