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