1 #ifndef KOSECTIONMODEL_H
2 #define KOSECTIONMODEL_H
3 
4 #include <QTextDocument>
5 #include <QAbstractItemModel>
6 #include <QVector>
7 #include <QSet>
8 
9 #include <KoSection.h>
10 #include <KoSectionEnd.h>
11 
12 /**
13  * Used to handle all the sections in the document
14  *
15  * Now there actually two levels of section handling:
16  * 1) Formatting Level: on this level we should be sure, that
17  * pointers to KoSection and KoSectionEnd in the KoParagraphStyles
18  * properties SectionEndings and SectionStartings are consistent.
19  * Handling on this level is provided on the level of text editing
20  * commands: DeleteCommand, NewSectionCommand
21  * We can't move it to another place, because we should know the
22  * semantics of operation to handle it right way.
23  * 2) Model(Tree) Level: on this level we should update KoSectionModel
24  * right way, so it in any moment represents the actual tree
25  * of sections. Tree is built easily:
26  *    One section is son of another, if it is directly nested in it.
27  * As text editing commands have access to change Formatting Level,
28  * they are declared as friend classes of KoSectionModel to be able
29  * affect Model structure without changing something on Formatting
30  * Level. Also affected by RenameSectionCommand.
31  *
32  * Also we need to look at the consistency of some section properties:
33  *
34  * 1) Bounds. Those now are handled with QTextCursors that are placed
35  * on start and end of the section. In default state start cursor
36  * isn't moving if text inserted in its position, and end cursor
37  * moves. But in the case of initial document loading, it is necessary
38  * to make some end cursors stop moving, so we have:
39  *         KoTextLoader -> calling -> KoSection::setKeepEndBound()
40  *         KoTextLoader -> calling -> KoSectionModel::allowMovingEndBound()
41  *      ^-- this needed to restore default behaviour after load
42  *
43  * 2) Level. Level means the depth of the section in tree. Root
44  * sections has 0 (zero) level. Now if you look at the possible
45  * text editing command affecting sections you may notice that
46  * level of section doesn't change in any case. Initial level
47  * is set in KoSection constructor as parent's level plus one.
48  * TODO: write about drag-n-drop here, when its implemented
49  *
50  * 3) Name. Each KoSection has a name that must be unique. We have
51  * two groups of KoSections in each moment of time: the first group
52  * consists of the sections that are present in document now,
53  * the second group consists of the sections that were deleted, but
54  * we still need them as they may be restored with "undo".
55  * This groups are stored in m_registeredSections and m_sectionNames.
56  *
57  * Sections are created through this newSection() and newSectionEnd()
58  * functions.
59  *
60  * This object is created for QTextDocument on the first query of it.
61  */
62 class KOTEXT_EXPORT KoSectionModel : public QAbstractItemModel
63 {
64     Q_OBJECT
65 public:
66     static const int PointerRole = Qt::UserRole;
67 
68     explicit KoSectionModel(QTextDocument *doc);
69     ~KoSectionModel() override;
70 
71     QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
72     QModelIndex parent(const QModelIndex &child) const override;
73 
74     int rowCount(const QModelIndex &parent = QModelIndex()) const override;
75     int columnCount(const QModelIndex &parent = QModelIndex()) const override;
76 
77     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
78 
79     /// Creates KoSection in position of @p cursor with some allowed name
80     KoSection *createSection(const QTextCursor &cursor, KoSection *parent);
81 
82     /// Creates KoSection in position of @p cursor with specified @p name
83     KoSection *createSection(const QTextCursor &cursor, KoSection *parent, const QString &name);
84 
85     /// Creates KoSectionEnd in pair for a @p section
86     KoSectionEnd *createSectionEnd(KoSection *section);
87 
88     /** Tries to set @p section name to @p name
89      * @return @c false if there is a section with such name
90      * and new name isn't accepted and @c true otherwise.
91      */
92     bool setName(KoSection *section, const QString &name);
93 
94     /**
95      * Returns pointer to the deepest KoSection that covers @p pos
96      * or NULL if there is no such section
97      */
98     KoSection *sectionAtPosition(int pos) const;
99 
100     /// Returns name for the new section.
101     QString possibleNewName();
102 
103     /// Returns if this name is possible.
104     bool isValidNewName(const QString &name) const;
105 
106     /// Setting all sections end bound cursor to move with text inserting.
107     void allowMovingEndBound();
108 
109     /// Finds index of @p child inside his parent.
110     int findRowOfChild(KoSection *child) const;
111 
112 private:
113     Q_DISABLE_COPY(KoSectionModel)
114 
115     friend class DeleteCommand;
116     friend class NewSectionCommand;
117 
118     /**
119      * Inserts @p section to it's parent (should be
120      * stored in @p section already) in position childIdx.
121      * Affects only Model Level(@see KoSectionModel).
122      */
123     void insertToModel(KoSection* section, int childIdx);
124     /**
125      * Deletes @p section from it's parent (should be
126      * stored in @p section already).
127      * Affects only Model Level
128      * @see KoSectionModel
129      */
130     void deleteFromModel(KoSection *section);
131 
132     QTextDocument *m_doc;
133     QSet<KoSection *> m_registeredSections; ///< stores pointer to sections that sometime was registered
134     QHash<QString, KoSection *> m_sectionNames; ///< stores name -> pointer reference, for sections that are visible in document now
135     QHash<KoSection *, QPersistentModelIndex> m_modelIndex;
136 
137     QVector<KoSection *> m_rootSections;
138 
139 };
140 
141 Q_DECLARE_METATYPE(KoSectionModel *)
142 
143 #endif //KOSECTIONMODEL_H
144