KPimTextEdit

nestedlisthelper.cpp
1/**
2 * Nested list helper
3 *
4 * SPDX-FileCopyrightText: 2008 Stephen Kelly <steveire@gmail.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8
9#include "nestedlisthelper_p.h"
10
11#include <QKeyEvent>
12#include <QTextBlock>
13#include <QTextCursor>
14#include <QTextEdit>
15#include <QTextList>
16using namespace KPIMTextEdit;
17NestedListHelper::NestedListHelper(QTextEdit *te)
18 : textEdit(te)
19{
20 listBottomMargin = 12;
21 listTopMargin = 12;
22 listNoMargin = 0;
23}
24
25bool NestedListHelper::handleBeforeKeyPressEvent(QKeyEvent *event)
26{
27 QTextCursor cursor = textEdit->textCursor();
28
29 // Only attempt to handle Backspace while on a list
30 if ((event->key() != Qt::Key_Backspace) || (!cursor.currentList())) {
31 return false;
32 }
33
34 bool handled = false;
35
36 if (!cursor.hasSelection() && cursor.currentList() && event->key() == Qt::Key_Backspace && cursor.atBlockStart()) {
37 handleOnIndentLess();
38 handled = true;
39 }
40
41 return handled;
42}
43
44bool NestedListHelper::canIndent() const
45{
46 if ((textEdit->textCursor().block().isValid())
47 // && ( textEdit->textCursor().block().previous().isValid() )
48 ) {
49 const QTextBlock block = textEdit->textCursor().block();
50 const QTextBlock prevBlock = textEdit->textCursor().block().previous();
51 if (block.textList()) {
52 if (prevBlock.textList()) {
53 return block.textList()->format().indent() <= prevBlock.textList()->format().indent();
54 }
55 } else {
56 return true;
57 }
58 }
59 return false;
60}
61
62bool NestedListHelper::canDedent() const
63{
64 QTextBlock thisBlock = textEdit->textCursor().block();
65 QTextBlock nextBlock = thisBlock.next();
66 if (thisBlock.isValid()) {
67 int nextBlockIndent = 0;
68 if (nextBlock.isValid() && nextBlock.textList()) {
69 nextBlockIndent = nextBlock.textList()->format().indent();
70 }
71 if (thisBlock.textList()) {
72 const int thisBlockIndent = thisBlock.textList()->format().indent();
73 if (thisBlockIndent >= nextBlockIndent) {
74 return thisBlockIndent > 0;
75 }
76 }
77 }
78 return false;
79}
80
81bool NestedListHelper::handleAfterKeyPressEvent(QKeyEvent *event)
82{
83 // Only attempt to handle Backspace and Return
84 if ((event->key() != Qt::Key_Backspace) && (event->key() != Qt::Key_Return)) {
85 return false;
86 }
87
88 QTextCursor cursor = textEdit->textCursor();
89 bool handled = false;
90
91 if (!cursor.hasSelection() && cursor.currentList()) {
92 // Check if we're on the last list item.
93 // itemNumber is zero indexed
94 QTextBlock currentBlock = cursor.block();
95 if (cursor.currentList()->count() == cursor.currentList()->itemNumber(currentBlock) + 1) {
96 // Last block in this list, but may have just gained another list below.
97 if (currentBlock.next().textList()) {
98 reformatList();
99 }
100
101 // No need to reformatList in this case. reformatList is slow.
102 if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Backspace)) {
103 handled = true;
104 }
105 } else {
106 reformatList();
107 }
108 }
109 return handled;
110}
111
112void NestedListHelper::processList(QTextList *list)
113{
114 QTextBlock block = list->item(0);
115 const int thisListIndent = list->format().indent();
116
117 QTextCursor cursor = QTextCursor(block);
118 list = cursor.createList(list->format());
119 bool processingSubList = false;
120 while (block.next().textList() != nullptr) {
121 block = block.next();
122
123 QTextList *nextList = block.textList();
124 const int nextItemIndent = nextList->format().indent();
125 if (nextItemIndent < thisListIndent) {
126 return;
127 } else if (nextItemIndent > thisListIndent) {
128 if (processingSubList) {
129 continue;
130 }
131 processingSubList = true;
132 processList(nextList);
133 } else {
134 processingSubList = false;
135 list->add(block);
136 }
137 }
138 // delete nextList;
139 // nextList = 0;
140}
141
142void NestedListHelper::reformatList(QTextBlock block)
143{
144 if (block.textList()) {
145 const int minimumIndent = block.textList()->format().indent();
146
147 // Start at the top of the list
148 while (block.previous().textList() != nullptr) {
149 if (block.previous().textList()->format().indent() < minimumIndent) {
150 break;
151 }
152 block = block.previous();
153 }
154
155 processList(block.textList());
156 }
157}
158
159void NestedListHelper::reformatList()
160{
161 const QTextCursor cursor = textEdit->textCursor();
162 reformatList(cursor.block());
163}
164
165QTextCursor NestedListHelper::topOfSelection()
166{
167 QTextCursor cursor = textEdit->textCursor();
168
169 if (cursor.hasSelection()) {
170 cursor.setPosition(qMin(cursor.position(), cursor.anchor()));
171 }
172 return cursor;
173}
174
175QTextCursor NestedListHelper::bottomOfSelection()
176{
177 QTextCursor cursor = textEdit->textCursor();
178
179 if (cursor.hasSelection()) {
180 cursor.setPosition(qMax(cursor.position(), cursor.anchor()));
181 }
182 return cursor;
183}
184
185void NestedListHelper::handleOnIndentMore()
186{
187 QTextCursor cursor = textEdit->textCursor();
188
189 QTextListFormat listFmt;
190 if (!cursor.currentList()) {
192 cursor = topOfSelection();
194 if (cursor.currentList()) {
195 style = cursor.currentList()->format().style();
196 } else {
197 cursor = bottomOfSelection();
199
200 if (cursor.currentList()) {
201 style = cursor.currentList()->format().style();
202 } else {
204 }
205 }
206 handleOnBulletType(style);
207 } else {
208 listFmt = cursor.currentList()->format();
209 listFmt.setIndent(listFmt.indent() + 1);
210
211 cursor.createList(listFmt);
212 reformatList();
213 }
214}
215
216void NestedListHelper::handleOnIndentLess()
217{
218 QTextCursor cursor = textEdit->textCursor();
219 QTextList *currentList = cursor.currentList();
220 if (!currentList) {
221 return;
222 }
223 QTextListFormat listFmt = currentList->format();
224 if (listFmt.indent() > 1) {
225 listFmt.setIndent(listFmt.indent() - 1);
226 cursor.createList(listFmt);
227 reformatList(cursor.block());
228 } else {
229 QTextBlockFormat bfmt;
230 bfmt.setObjectIndex(-1);
231 cursor.setBlockFormat(bfmt);
232 reformatList(cursor.block().next());
233 }
234}
235
236void NestedListHelper::handleOnBulletType(int styleIndex)
237{
238 QTextCursor cursor = textEdit->textCursor();
239 if (styleIndex != 0) {
240 auto style = static_cast<QTextListFormat::Style>(styleIndex);
241 QTextList *currentList = cursor.currentList();
242 QTextListFormat listFmt;
243
244 cursor.beginEditBlock();
245
246 if (currentList) {
247 listFmt = currentList->format();
248 listFmt.setStyle(style);
249 currentList->setFormat(listFmt);
250 } else {
251 listFmt.setStyle(style);
252 cursor.createList(listFmt);
253 }
254
255 cursor.endEditBlock();
256 } else {
257 QTextBlockFormat bfmt;
258 bfmt.setObjectIndex(-1);
259 cursor.setBlockFormat(bfmt);
260 }
261
262 reformatList();
263}
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
Key_Backspace
bool isValid() const const
QTextBlock next() const const
QTextBlock previous() const const
QTextList * textList() const const
int anchor() const const
bool atBlockStart() const const
void beginEditBlock()
QTextBlock block() const const
QTextList * createList(QTextListFormat::Style style)
QTextList * currentList() const const
void endEditBlock()
bool hasSelection() const const
bool movePosition(MoveOperation operation, MoveMode mode, int n)
int position() const const
void setBlockFormat(const QTextBlockFormat &format)
void setPosition(int pos, MoveMode m)
void setObjectIndex(int index)
int count() const const
QTextListFormat format() const const
int itemNumber(const QTextBlock &block) const const
void setFormat(const QTextListFormat &format)
int indent() const const
void setIndent(int indentation)
void setStyle(Style style)
Style style() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:45 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.