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 #ifndef QTEXTDOCUMENT_P_H
41 #define QTEXTDOCUMENT_P_H
42 
43 //
44 //  W A R N I N G
45 //  -------------
46 //
47 // This file is not part of the Qt API.  It exists purely as an
48 // implementation detail.  This header file may change from version to
49 // version without notice, or even be removed.
50 //
51 // We mean it.
52 //
53 
54 #include <QtGui/private/qtguiglobal_p.h>
55 #include "QtCore/qstring.h"
56 #include "QtCore/qvector.h"
57 #include "QtCore/qlist.h"
58 #include "private/qobject_p.h"
59 #include "private/qfragmentmap_p.h"
60 #include "QtGui/qtextlayout.h"
61 #include "QtGui/qtextoption.h"
62 #include "private/qtextformat_p.h"
63 #include "QtGui/qtextdocument.h"
64 #include "QtGui/qtextobject.h"
65 #include "QtGui/qtextcursor.h"
66 #include "QtCore/qmap.h"
67 #include "QtCore/qvariant.h"
68 #include "QtCore/qurl.h"
69 #include "private/qcssparser_p.h"
70 
71 // #define QT_QMAP_DEBUG
72 
73 #ifdef QT_QMAP_DEBUG
74 #include <iostream>
75 #endif
76 
77 QT_BEGIN_NAMESPACE
78 
79 class QTextFormatCollection;
80 class QTextFormat;
81 class QTextBlockFormat;
82 class QTextCursorPrivate;
83 class QAbstractTextDocumentLayout;
84 class QTextDocument;
85 class QTextFrame;
86 
87 #define QTextBeginningOfFrame QChar(0xfdd0)
88 #define QTextEndOfFrame QChar(0xfdd1)
89 
90 class QTextFragmentData : public QFragment<>
91 {
92 public:
initialize()93     inline void initialize() {}
invalidate()94     inline void invalidate() const {}
free()95     inline void free() {}
96     int stringPosition;
97     int format;
98 };
99 
100 class QTextBlockData : public QFragment<3>
101 {
102 public:
initialize()103     inline void initialize()
104         { layout = nullptr; userData = nullptr; userState = -1; revision = 0; hidden = 0; }
105     void invalidate() const;
free()106     inline void free()
107     { delete layout; layout = nullptr; delete userData; userData = nullptr; }
108 
109     mutable int format;
110     // ##### probably store a QTextEngine * here!
111     mutable QTextLayout *layout;
112     mutable QTextBlockUserData *userData;
113     mutable int userState;
114     mutable signed int revision : 31;
115     mutable uint hidden : 1;
116 };
117 
118 
119 class QAbstractUndoItem;
120 
121 class QTextUndoCommand
122 {
123 public:
124     enum Command {
125         Inserted = 0,
126         Removed = 1,
127         CharFormatChanged = 2,
128         BlockFormatChanged = 3,
129         BlockInserted = 4,
130         BlockRemoved = 5,
131         BlockAdded = 6,
132         BlockDeleted = 7,
133         GroupFormatChange = 8,
134         CursorMoved = 9,
135         Custom = 256
136     };
137     enum Operation {
138         KeepCursor = 0,
139         MoveCursor = 1
140     };
141     quint16 command;
142     uint block_part : 1; // all commands that are part of an undo block (including the first and the last one) have this set to 1
143     uint block_end : 1; // the last command in an undo block has this set to 1.
144     uint block_padding : 6; // padding since block used to be a quint8
145     quint8 operation;
146     int format;
147     quint32 strPos;
148     quint32 pos;
149     union {
150         int blockFormat;
151         quint32 length;
152         QAbstractUndoItem *custom;
153         int objectIndex;
154     };
155     quint32 revision;
156 
157     bool tryMerge(const QTextUndoCommand &other);
158 };
159 Q_DECLARE_TYPEINFO(QTextUndoCommand, Q_PRIMITIVE_TYPE);
160 
161 class Q_GUI_EXPORT QTextDocumentPrivate : public QObjectPrivate
162 {
163     Q_DECLARE_PUBLIC(QTextDocument)
164 public:
165     typedef QFragmentMap<QTextFragmentData> FragmentMap;
166     typedef FragmentMap::ConstIterator FragmentIterator;
167     typedef QFragmentMap<QTextBlockData> BlockMap;
168 
169     QTextDocumentPrivate();
170     ~QTextDocumentPrivate();
171 
172     void init();
173     void clear();
174 
175     void setLayout(QAbstractTextDocumentLayout *layout);
176 
177     void insert(int pos, const QString &text, int format);
178     void insert(int pos, int strPos, int strLength, int format);
179     int insertBlock(int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
180     int insertBlock(QChar blockSeparator, int pos, int blockFormat, int charFormat,
181                      QTextUndoCommand::Operation op = QTextUndoCommand::MoveCursor);
182 
183     void move(int from, int to, int length, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
184     void remove(int pos, int length, QTextUndoCommand::Operation = QTextUndoCommand::MoveCursor);
185 
186     void aboutToRemoveCell(int cursorFrom, int cursorEnd);
187 
188     QTextFrame *insertFrame(int start, int end, const QTextFrameFormat &format);
189     void removeFrame(QTextFrame *frame);
190 
191     enum FormatChangeMode { MergeFormat, SetFormat, SetFormatAndPreserveObjectIndices };
192 
193     void setCharFormat(int pos, int length, const QTextCharFormat &newFormat, FormatChangeMode mode = SetFormat);
194     void setBlockFormat(const QTextBlock &from, const QTextBlock &to,
195                         const QTextBlockFormat &newFormat, FormatChangeMode mode = SetFormat);
196 
197     void emitUndoAvailable(bool available);
198     void emitRedoAvailable(bool available);
199 
200     int undoRedo(bool undo);
undo()201     inline void undo() { undoRedo(true); }
redo()202     inline void redo() { undoRedo(false); }
203     void appendUndoItem(QAbstractUndoItem *);
beginEditBlock()204     inline void beginEditBlock() { if (0 == editBlock++) ++revision; }
205     void joinPreviousEditBlock();
206     void endEditBlock();
207     void finishEdit();
isInEditBlock()208     inline bool isInEditBlock() const { return editBlock; }
209     void enableUndoRedo(bool enable);
isUndoRedoEnabled()210     inline bool isUndoRedoEnabled() const { return undoEnabled; }
211 
isUndoAvailable()212     inline bool isUndoAvailable() const { return undoEnabled && undoState > 0; }
isRedoAvailable()213     inline bool isRedoAvailable() const { return undoEnabled && undoState < undoStack.size(); }
214 
availableUndoSteps()215     inline int availableUndoSteps() const { return undoEnabled ? undoState : 0; }
availableRedoSteps()216     inline int availableRedoSteps() const { return undoEnabled ? qMax(undoStack.size() - undoState - 1, 0) : 0; }
217 
buffer()218     inline QString buffer() const { return text; }
219     QString plainText() const;
length()220     inline int length() const { return fragments.length(); }
221 
formatCollection()222     inline QTextFormatCollection *formatCollection() { return &formats; }
formatCollection()223     inline const QTextFormatCollection *formatCollection() const { return &formats; }
layout()224     inline QAbstractTextDocumentLayout *layout() const { return lout; }
225 
find(int pos)226     inline FragmentIterator find(int pos) const { return fragments.find(pos); }
begin()227     inline FragmentIterator begin() const { return fragments.begin(); }
end()228     inline FragmentIterator end() const { return fragments.end(); }
229 
blocksBegin()230     inline QTextBlock blocksBegin() const { return QTextBlock(const_cast<QTextDocumentPrivate *>(this), blocks.firstNode()); }
blocksEnd()231     inline QTextBlock blocksEnd() const { return QTextBlock(const_cast<QTextDocumentPrivate *>(this), 0); }
blocksFind(int pos)232     inline QTextBlock blocksFind(int pos) const { return QTextBlock(const_cast<QTextDocumentPrivate *>(this), blocks.findNode(pos)); }
233     int blockCharFormatIndex(int node) const;
234 
numBlocks()235     inline int numBlocks() const { return blocks.numNodes(); }
236 
blockMap()237     const BlockMap &blockMap() const { return blocks; }
fragmentMap()238     const FragmentMap &fragmentMap() const { return fragments; }
blockMap()239     BlockMap &blockMap() { return blocks; }
fragmentMap()240     FragmentMap &fragmentMap() { return fragments; }
241 
block(const QTextBlock & it)242     static const QTextBlockData *block(const QTextBlock &it) { return it.p->blocks.fragment(it.n); }
243 
244     int nextCursorPosition(int position, QTextLayout::CursorMode mode) const;
245     int previousCursorPosition(int position, QTextLayout::CursorMode mode) const;
246     int leftCursorPosition(int position) const;
247     int rightCursorPosition(int position) const;
248 
249     void changeObjectFormat(QTextObject *group, int format);
250 
251     void setModified(bool m);
isModified()252     inline bool isModified() const { return modified; }
253 
defaultFont()254     inline QFont defaultFont() const { return formats.defaultFont(); }
setDefaultFont(const QFont & f)255     inline void setDefaultFont(const QFont &f) { formats.setDefaultFont(f); }
256 
257     void clearUndoRedoStacks(QTextDocument::Stacks stacksToClear, bool emitSignals = false);
258 
259 private:
260     bool split(int pos);
261     bool unite(uint f);
262 
263     void insert_string(int pos, uint strPos, uint length, int format, QTextUndoCommand::Operation op);
264     int insert_block(int pos, uint strPos, int format, int blockformat, QTextUndoCommand::Operation op, int command);
265     int remove_string(int pos, uint length, QTextUndoCommand::Operation op);
266     int remove_block(int pos, int *blockformat, int command, QTextUndoCommand::Operation op);
267 
268     void insert_frame(QTextFrame *f);
269     void scan_frames(int pos, int charsRemoved, int charsAdded);
270     static void clearFrame(QTextFrame *f);
271 
272     void adjustDocumentChangesAndCursors(int from, int addedOrRemoved, QTextUndoCommand::Operation op);
273 
274     bool wasUndoAvailable;
275     bool wasRedoAvailable;
276 
277 public:
278     void documentChange(int from, int length);
279 
addCursor(QTextCursorPrivate * c)280     inline void addCursor(QTextCursorPrivate *c) { cursors.insert(c); }
removeCursor(QTextCursorPrivate * c)281     inline void removeCursor(QTextCursorPrivate *c) { cursors.remove(c); }
282 
283     QTextFrame *frameAt(int pos) const;
284     QTextFrame *rootFrame() const;
285 
286     QTextObject *objectForIndex(int objectIndex) const;
287     QTextObject *objectForFormat(int formatIndex) const;
288     QTextObject *objectForFormat(const QTextFormat &f) const;
289 
290     QTextObject *createObject(const QTextFormat &newFormat, int objectIndex = -1);
291     void deleteObject(QTextObject *object);
292 
document()293     QTextDocument *document() { return q_func(); }
document()294     const QTextDocument *document() const { return q_func(); }
295 
296     bool ensureMaximumBlockCount();
297 
298 private:
299     QTextDocumentPrivate(const QTextDocumentPrivate& m);
300     QTextDocumentPrivate& operator= (const QTextDocumentPrivate& m);
301 
302     void appendUndoItem(const QTextUndoCommand &c);
303 
304     void contentsChanged();
305 
306     void compressPieceTable();
307 
308     QString text;
309     uint unreachableCharacterCount;
310 
311     QVector<QTextUndoCommand> undoStack;
312     bool undoEnabled;
313     int undoState;
314     int revision;
315     // position in undo stack of the last setModified(false) call
316     int modifiedState;
317     bool modified;
318 
319     int editBlock;
320     int editBlockCursorPosition;
321     int docChangeFrom;
322     int docChangeOldLength;
323     int docChangeLength;
324     bool framesDirty;
325 
326     QTextFormatCollection formats;
327     mutable QTextFrame *rtFrame;
328     QAbstractTextDocumentLayout *lout;
329     FragmentMap fragments;
330     BlockMap blocks;
331     int initialBlockCharFormatIndex;
332 
333     QSet<QTextCursorPrivate *> cursors;
334     QMap<int, QTextObject *> objects;
335     QMap<QUrl, QVariant> resources;
336     QMap<QUrl, QVariant> cachedResources;
337     QString defaultStyleSheet;
338 
339     int lastBlockCount;
340 
341 public:
342     bool inContentsChange;
343     QTextOption defaultTextOption;
344     Qt::CursorMoveStyle defaultCursorMoveStyle;
345 #ifndef QT_NO_CSSPARSER
346     QCss::StyleSheet parsedDefaultStyleSheet;
347 #endif
348     int maximumBlockCount;
349     uint needsEnsureMaximumBlockCount : 1;
350     uint blockCursorAdjustment : 1;
351     QSizeF pageSize;
352     QString title;
353     QString url;
354     qreal indentWidth;
355     qreal documentMargin;
356     QUrl baseUrl;
357 
358     void mergeCachedResources(const QTextDocumentPrivate *priv);
359 
360     friend struct QTextHtmlParserNode;
361     friend class QTextHtmlExporter;
362     friend class QTextCursor;
363 };
364 
365 class QTextTable;
366 class QTextHtmlExporter
367 {
368 public:
369     QTextHtmlExporter(const QTextDocument *_doc);
370 
371     enum ExportMode {
372         ExportEntireDocument,
373         ExportFragment
374     };
375 
376     QString toHtml(const QByteArray &encoding, ExportMode mode = ExportEntireDocument);
377 
378 private:
379     enum StyleMode { EmitStyleTag, OmitStyleTag };
380     enum FrameType { TextFrame, TableFrame, RootFrame };
381 
382     void emitFrame(const QTextFrame::Iterator &frameIt);
383     void emitTextFrame(const QTextFrame *frame);
384     void emitBlock(const QTextBlock &block);
385     void emitTable(const QTextTable *table);
386     void emitFragment(const QTextFragment &fragment);
387 
388     void emitBlockAttributes(const QTextBlock &block);
389     bool emitCharFormatStyle(const QTextCharFormat &format);
390     void emitTextLength(const char *attribute, const QTextLength &length);
391     void emitAlignment(Qt::Alignment alignment);
392     void emitFloatStyle(QTextFrameFormat::Position pos, StyleMode mode = EmitStyleTag);
393     void emitMargins(const QString &top, const QString &bottom, const QString &left, const QString &right);
394     void emitAttribute(const char *attribute, const QString &value);
395     void emitFrameStyle(const QTextFrameFormat &format, FrameType frameType);
396     void emitBorderStyle(QTextFrameFormat::BorderStyle style);
397     void emitPageBreakPolicy(QTextFormat::PageBreakFlags policy);
398 
399     void emitFontFamily(const QStringList &families);
400 
401     void emitBackgroundAttribute(const QTextFormat &format);
402     QString findUrlForImage(const QTextDocument *doc, qint64 cacheKey, bool isPixmap);
403 
404     QString html;
405     QTextCharFormat defaultCharFormat;
406     const QTextDocument *doc;
407     bool fragmentMarkers;
408 };
409 
410 QT_END_NAMESPACE
411 
412 #endif // QTEXTDOCUMENT_P_H
413