blob: 83f99a83a850fbd4eef5e73c210bd79282b9da86 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qabstracttextdocumentlayout.h>
#include <qtextformat.h>
#include "qtextdocument_p.h"
#include "qtextengine_p.h"
#include "qabstracttextdocumentlayout_p.h"
QT_BEGIN_NAMESPACE
/*!
\class QAbstractTextDocumentLayout
\reentrant
\brief The QAbstractTextDocumentLayout class is an abstract base
class used to implement custom layouts for QTextDocuments.
\ingroup richtext-processing
The standard layout provided by Qt can handle simple word processing
including inline images, lists and tables.
Some applications, e.g., a word processor or a DTP application might need
more features than the ones provided by Qt's layout engine, in which case
you can subclass QAbstractTextDocumentLayout to provide custom layout
behavior for your text documents.
An instance of the QAbstractTextDocumentLayout subclass can be installed
on a QTextDocument object with the
\l{QTextDocument::}{setDocumentLayout()} function.
You can insert custom objects into a QTextDocument; see the
QTextObjectInterface class description for details.
\sa QTextObjectInterface
*/
/*!
\class QTextObjectInterface
\brief The QTextObjectInterface class allows drawing of
custom text objects in \l{QTextDocument}s.
\since 4.5
A text object describes the structure of one or more elements in a
text document; for instance, images imported from HTML are
implemented using text objects. A text object knows how to lay out
and draw its elements when a document is being rendered.
Qt allows custom text objects to be inserted into a document by
registering a custom \l{QTextCharFormat::objectType()}{object
type} with QTextCharFormat. A QTextObjectInterface must also be
implemented for this type and be
\l{QAbstractTextDocumentLayout::registerHandler()}{registered}
with the QAbstractTextDocumentLayout of the document. When the
object type is encountered while rendering a QTextDocument, the
intrinsicSize() and drawObject() functions of the interface are
called.
The following list explains the required steps of inserting a
custom text object into a document:
\list
\o Choose an \a objectType. The \a objectType is an integer with a
value greater or equal to QTextFormat::UserObject.
\o Create a QTextCharFormat object and set the object type to the
chosen type using the setObjectType() function.
\o Implement the QTextObjectInterface class.
\o Call QAbstractTextDocumentLayout::registerHandler() with an instance of your
QTextObjectInterface subclass to register your object type.
\o Insert QChar::ObjectReplacementCharacter with the aforementioned
QTextCharFormat of the chosen object type into the document.
As mentioned, the functions of QTextObjectInterface
\l{QTextObjectInterface::}{intrinsicSize()} and
\l{QTextObjectInterface::}{drawObject()} will then be called with the
QTextFormat as parameter whenever the replacement character is
encountered.
\endlist
A class implementing a text object needs to inherit both QObject
and QTextObjectInterface. QObject must be the first class
inherited. For instance:
\snippet examples/richtext/textobject/svgtextobject.h 1
The data of a text object is usually stored in the QTextCharFormat
using QTextCharFormat::setProperty(), and then retrieved with
QTextCharFormat::property().
\warning Copy and Paste operations ignore custom text objects.
\sa {Text Object Example}, QTextCharFormat, QTextLayout
*/
/*!
\fn QTextObjectInterface::~QTextObjectInterface()
Destroys this QTextObjectInterface.
*/
/*!
\fn virtual QSizeF QTextObjectInterface::intrinsicSize(QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
The intrinsicSize() function returns the size of the text object
represented by \a format in the given document (\a doc) at the
given position (\a posInDocument).
The size calculated will be used for subsequent calls to
drawObject() for this \a format.
\sa drawObject()
*/
/*!
\fn virtual void QTextObjectInterface::drawObject(QPainter *painter, const QRectF &rect, QTextDocument *doc, int posInDocument, const QTextFormat &format) = 0
Draws this text object using the specified \a painter.
The size of the rectangle, \a rect, to draw in is the size
previously calculated by intrinsicSize(). The rectangles position
is relative to the \a painter.
You also get the document (\a doc) and the position (\a
posInDocument) of the \a format in that document.
\sa intrinsicSize()
*/
/*!
\fn void QAbstractTextDocumentLayout::update(const QRectF &rect)
This signal is emitted when the rectangle \a rect has been updated.
Subclasses of QAbstractTextDocumentLayout should emit this signal when
the layout of the contents change in order to repaint.
*/
/*!
\fn void QAbstractTextDocumentLayout::updateBlock(const QTextBlock &block)
\since 4.4
This signal is emitted when the specified \a block has been updated.
Subclasses of QAbstractTextDocumentLayout should emit this signal when
the layout of \a block has changed in order to repaint.
*/
/*!
\fn void QAbstractTextDocumentLayout::documentSizeChanged(const QSizeF &newSize)
This signal is emitted when the size of the document layout changes to
\a newSize.
Subclasses of QAbstractTextDocumentLayout should emit this signal when the
document's entire layout size changes. This signal is useful for widgets
that display text documents since it enables them to update their scroll
bars correctly.
\sa documentSize()
*/
/*!
\fn void QAbstractTextDocumentLayout::pageCountChanged(int newPages)
This signal is emitted when the number of pages in the layout changes;
\a newPages is the updated page count.
Subclasses of QAbstractTextDocumentLayout should emit this signal when
the number of pages in the layout has changed. Changes to the page count
are caused by changes to the layout or the document content itself.
\sa pageCount()
*/
/*!
\fn int QAbstractTextDocumentLayout::pageCount() const
Returns the number of pages contained in the layout.
\sa pageCountChanged()
*/
/*!
\fn QSizeF QAbstractTextDocumentLayout::documentSize() const
Returns the total size of the document's layout.
This information can be used by display widgets to update their scroll bars
correctly.
\sa documentSizeChanged(), QTextDocument::pageSize
*/
/*!
\fn void QAbstractTextDocumentLayout::draw(QPainter *painter, const PaintContext &context)
Draws the layout with the given \a painter using the given \a context.
*/
/*!
\fn int QAbstractTextDocumentLayout::hitTest(const QPointF &point, Qt::HitTestAccuracy accuracy) const
Returns the cursor postion for the given \a point with the specified
\a accuracy. Returns -1 if no valid cursor position was found.
*/
/*!
\fn void QAbstractTextDocumentLayout::documentChanged(int position, int charsRemoved, int charsAdded)
This function is called whenever the contents of the document change. A
change occurs when text is inserted, removed, or a combination of these
two. The change is specified by \a position, \a charsRemoved, and
\a charsAdded corresponding to the starting character position of the
change, the number of characters removed from the document, and the
number of characters added.
For example, when inserting the text "Hello" into an empty document,
\a charsRemoved would be 0 and \a charsAdded would be 5 (the length of
the string).
Replacing text is a combination of removing and inserting. For example, if
the text "Hello" gets replaced by "Hi", \a charsRemoved would be 5 and
\a charsAdded would be 2.
For subclasses of QAbstractTextDocumentLayout, this is the central function
where a large portion of the work to lay out and position document contents
is done.
For example, in a subclass that only arranges blocks of text, an
implementation of this function would have to do the following:
\list
\o Determine the list of changed \l{QTextBlock}(s) using the parameters
provided.
\o Each QTextBlock object's corresponding QTextLayout object needs to
be processed. You can access the \l{QTextBlock}'s layout using the
QTextBlock::layout() function. This processing should take the
document's page size into consideration.
\o If the total number of pages changed, the pageCountChanged() signal
should be emitted.
\o If the total size changed, the documentSizeChanged() signal should
be emitted.
\o The update() signal should be emitted to schedule a repaint of areas
in the layout that require repainting.
\endlist
\sa QTextLayout
*/
/*!
\class QAbstractTextDocumentLayout::PaintContext
\reentrant
\brief The QAbstractTextDocumentLayout::PaintContext class is a convenience
class defining the parameters used when painting a document's layout.
A paint context is used when rendering custom layouts for QTextDocuments
with the QAbstractTextDocumentLayout::draw() function. It is specified by
a \l {cursorPosition}{cursor position}, \l {palette}{default text color},
\l clip rectangle and a collection of \l selections.
\sa QAbstractTextDocumentLayout
*/
/*!
\fn QAbstractTextDocumentLayout::PaintContext::PaintContext()
\internal
*/
/*!
\variable QAbstractTextDocumentLayout::PaintContext::cursorPosition
\brief the position within the document, where the cursor line should be
drawn.
The default value is -1.
*/
/*!
\variable QAbstractTextDocumentLayout::PaintContext::palette
\brief the default color that is used for the text, when no color is
specified.
The default value is the application's default palette.
*/
/*!
\variable QAbstractTextDocumentLayout::PaintContext::clip
\brief a hint to the layout specifying the area around paragraphs, frames
or text require painting.
Everything outside of this rectangle does not need to be painted.
Specifying a clip rectangle can speed up drawing of large documents
significantly. Note that the clip rectangle is in document coordinates (not
in viewport coordinates). It is not a substitute for a clip region set on
the painter but merely a hint.
The default value is a null rectangle indicating everything needs to be
painted.
*/
/*!
\variable QAbstractTextDocumentLayout::PaintContext::selections
\brief the collection of selections that will be rendered when passing this
paint context to QAbstractTextDocumentLayout's draw() function.
The default value is an empty vector indicating no selection.
*/
/*!
\class QAbstractTextDocumentLayout::Selection
\reentrant
\brief The QAbstractTextDocumentLayout::Selection class is a convenience
class defining the parameters of a selection.
A selection can be used to specify a part of a document that should be
highlighted when drawing custom layouts for QTextDocuments with the
QAbstractTextDocumentLayout::draw() function. It is specified using
\l cursor and a \l format.
\sa QAbstractTextDocumentLayout, PaintContext
*/
/*!
\variable QAbstractTextDocumentLayout::Selection::format
\brief the format of the selection
The default value is QTextFormat::InvalidFormat.
*/
/*!
\variable QAbstractTextDocumentLayout::Selection::cursor
\brief the selection's cursor
The default value is a null cursor.
*/
/*!
Creates a new text document layout for the given \a document.
*/
QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QTextDocument *document)
: QObject(*new QAbstractTextDocumentLayoutPrivate, document)
{
Q_D(QAbstractTextDocumentLayout);
d->setDocument(document);
}
/*!
\internal
*/
QAbstractTextDocumentLayout::QAbstractTextDocumentLayout(QAbstractTextDocumentLayoutPrivate &p, QTextDocument *document)
:QObject(p, document)
{
Q_D(QAbstractTextDocumentLayout);
d->setDocument(document);
}
/*!
\internal
*/
QAbstractTextDocumentLayout::~QAbstractTextDocumentLayout()
{
}
/*!
\fn void QAbstractTextDocumentLayout::registerHandler(int objectType, QObject *component)
Registers the given \a component as a handler for items of the given \a objectType.
\note registerHandler() has to be called once for each object type. This
means that there is only one handler for multiple replacement characters
of the same object type.
*/
void QAbstractTextDocumentLayout::registerHandler(int formatType, QObject *component)
{
Q_D(QAbstractTextDocumentLayout);
QTextObjectInterface *iface = qobject_cast<QTextObjectInterface *>(component);
if (!iface)
return; // ### print error message on terminal?
connect(component, SIGNAL(destroyed(QObject*)), this, SLOT(_q_handlerDestroyed(QObject*)));
QTextObjectHandler h;
h.iface = iface;
h.component = component;
d->handlers.insert(formatType, h);
}
/*!
Returns a handler for objects of the given \a objectType.
*/
QTextObjectInterface *QAbstractTextDocumentLayout::handlerForObject(int objectType) const
{
Q_D(const QAbstractTextDocumentLayout);
QTextObjectHandler handler = d->handlers.value(objectType);
if (!handler.component)
return 0;
return handler.iface;
}
/*!
Sets the size of the inline object \a item corresponding to the text
\a format.
\a posInDocument specifies the position of the object within the document.
The default implementation resizes the \a item to the size returned by
the object handler's intrinsicSize() function. This function is called only
within Qt. Subclasses can reimplement this function to customize the
resizing of inline objects.
*/
void QAbstractTextDocumentLayout::resizeInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
{
Q_D(QAbstractTextDocumentLayout);
QTextCharFormat f = format.toCharFormat();
Q_ASSERT(f.isValid());
QTextObjectHandler handler = d->handlers.value(f.objectType());
if (!handler.component)
return;
QSizeF s = handler.iface->intrinsicSize(document(), posInDocument, format);
item.setWidth(s.width());
item.setAscent(s.height() - 1);
item.setDescent(0);
}
/*!
Lays out the inline object \a item using the given text \a format.
\a posInDocument specifies the position of the object within the document.
The default implementation does nothing. This function is called only
within Qt. Subclasses can reimplement this function to customize the
position of inline objects.
\sa drawInlineObject()
*/
void QAbstractTextDocumentLayout::positionInlineObject(QTextInlineObject item, int posInDocument, const QTextFormat &format)
{
Q_UNUSED(item);
Q_UNUSED(posInDocument);
Q_UNUSED(format);
}
/*!
\fn void QAbstractTextDocumentLayout::drawInlineObject(QPainter *painter, const QRectF &rect, QTextInlineObject object, int posInDocument, const QTextFormat &format)
This function is called to draw the inline object, \a object, with the
given \a painter within the rectangle specified by \a rect using the
specified text \a format.
\a posInDocument specifies the position of the object within the document.
The default implementation calls drawObject() on the object handlers. This
function is called only within Qt. Subclasses can reimplement this function
to customize the drawing of inline objects.
\sa draw()
*/
void QAbstractTextDocumentLayout::drawInlineObject(QPainter *p, const QRectF &rect, QTextInlineObject item,
int posInDocument, const QTextFormat &format)
{
Q_UNUSED(item);
Q_D(QAbstractTextDocumentLayout);
QTextCharFormat f = format.toCharFormat();
Q_ASSERT(f.isValid());
QTextObjectHandler handler = d->handlers.value(f.objectType());
if (!handler.component)
return;
handler.iface->drawObject(p, rect, document(), posInDocument, format);
}
void QAbstractTextDocumentLayoutPrivate::_q_handlerDestroyed(QObject *obj)
{
HandlerHash::Iterator it = handlers.begin();
while (it != handlers.end())
if ((*it).component == obj)
it = handlers.erase(it);
else
++it;
}
/*!
\internal
Returns the index of the format at position \a pos.
*/
int QAbstractTextDocumentLayout::formatIndex(int pos)
{
QTextDocumentPrivate *pieceTable = qobject_cast<QTextDocument *>(parent())->docHandle();
return pieceTable->find(pos).value()->format;
}
/*!
\fn QTextCharFormat QAbstractTextDocumentLayout::format(int position)
Returns the character format that is applicable at the given \a position.
*/
QTextCharFormat QAbstractTextDocumentLayout::format(int pos)
{
QTextDocumentPrivate *pieceTable = qobject_cast<QTextDocument *>(parent())->docHandle();
int idx = pieceTable->find(pos).value()->format;
return pieceTable->formatCollection()->charFormat(idx);
}
/*!
Returns the text document that this layout is operating on.
*/
QTextDocument *QAbstractTextDocumentLayout::document() const
{
Q_D(const QAbstractTextDocumentLayout);
return d->document;
}
/*!
\fn QString QAbstractTextDocumentLayout::anchorAt(const QPointF &position) const
Returns the reference of the anchor the given \a position, or an empty
string if no anchor exists at that point.
*/
QString QAbstractTextDocumentLayout::anchorAt(const QPointF& pos) const
{
int cursorPos = hitTest(pos, Qt::ExactHit);
if (cursorPos == -1)
return QString();
QTextDocumentPrivate *pieceTable = qobject_cast<const QTextDocument *>(parent())->docHandle();
QTextDocumentPrivate::FragmentIterator it = pieceTable->find(cursorPos);
QTextCharFormat fmt = pieceTable->formatCollection()->charFormat(it->format);
return fmt.anchorHref();
}
/*!
\fn QRectF QAbstractTextDocumentLayout::frameBoundingRect(QTextFrame *frame) const
Returns the bounding rectangle of \a frame.
*/
/*!
\fn QRectF QAbstractTextDocumentLayout::blockBoundingRect(const QTextBlock &block) const
Returns the bounding rectangle of \a block.
*/
/*!
Sets the paint device used for rendering the document's layout to the given
\a device.
\sa paintDevice()
*/
void QAbstractTextDocumentLayout::setPaintDevice(QPaintDevice *device)
{
Q_D(QAbstractTextDocumentLayout);
d->paintDevice = device;
}
/*!
Returns the paint device used to render the document's layout.
\sa setPaintDevice()
*/
QPaintDevice *QAbstractTextDocumentLayout::paintDevice() const
{
Q_D(const QAbstractTextDocumentLayout);
return d->paintDevice;
}
QT_END_NAMESPACE
#include "moc_qabstracttextdocumentlayout.cpp"