File indexing completed on 2024-05-19 05:21:42
0001 /** 0002 * Nested list helper 0003 * 0004 * SPDX-FileCopyrightText: 2008 Stephen Kelly <steveire@gmail.com> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "nestedlisthelper_p.h" 0010 0011 #include <QKeyEvent> 0012 #include <QTextBlock> 0013 #include <QTextCursor> 0014 #include <QTextEdit> 0015 #include <QTextList> 0016 using namespace KPIMTextEdit; 0017 NestedListHelper::NestedListHelper(QTextEdit *te) 0018 : textEdit(te) 0019 { 0020 listBottomMargin = 12; 0021 listTopMargin = 12; 0022 listNoMargin = 0; 0023 } 0024 0025 bool NestedListHelper::handleBeforeKeyPressEvent(QKeyEvent *event) 0026 { 0027 QTextCursor cursor = textEdit->textCursor(); 0028 0029 // Only attempt to handle Backspace while on a list 0030 if ((event->key() != Qt::Key_Backspace) || (!cursor.currentList())) { 0031 return false; 0032 } 0033 0034 bool handled = false; 0035 0036 if (!cursor.hasSelection() && cursor.currentList() && event->key() == Qt::Key_Backspace && cursor.atBlockStart()) { 0037 handleOnIndentLess(); 0038 handled = true; 0039 } 0040 0041 return handled; 0042 } 0043 0044 bool NestedListHelper::canIndent() const 0045 { 0046 if ((textEdit->textCursor().block().isValid()) 0047 // && ( textEdit->textCursor().block().previous().isValid() ) 0048 ) { 0049 const QTextBlock block = textEdit->textCursor().block(); 0050 const QTextBlock prevBlock = textEdit->textCursor().block().previous(); 0051 if (block.textList()) { 0052 if (prevBlock.textList()) { 0053 return block.textList()->format().indent() <= prevBlock.textList()->format().indent(); 0054 } 0055 } else { 0056 return true; 0057 } 0058 } 0059 return false; 0060 } 0061 0062 bool NestedListHelper::canDedent() const 0063 { 0064 QTextBlock thisBlock = textEdit->textCursor().block(); 0065 QTextBlock nextBlock = thisBlock.next(); 0066 if (thisBlock.isValid()) { 0067 int nextBlockIndent = 0; 0068 if (nextBlock.isValid() && nextBlock.textList()) { 0069 nextBlockIndent = nextBlock.textList()->format().indent(); 0070 } 0071 if (thisBlock.textList()) { 0072 const int thisBlockIndent = thisBlock.textList()->format().indent(); 0073 if (thisBlockIndent >= nextBlockIndent) { 0074 return thisBlockIndent > 0; 0075 } 0076 } 0077 } 0078 return false; 0079 } 0080 0081 bool NestedListHelper::handleAfterKeyPressEvent(QKeyEvent *event) 0082 { 0083 // Only attempt to handle Backspace and Return 0084 if ((event->key() != Qt::Key_Backspace) && (event->key() != Qt::Key_Return)) { 0085 return false; 0086 } 0087 0088 QTextCursor cursor = textEdit->textCursor(); 0089 bool handled = false; 0090 0091 if (!cursor.hasSelection() && cursor.currentList()) { 0092 // Check if we're on the last list item. 0093 // itemNumber is zero indexed 0094 QTextBlock currentBlock = cursor.block(); 0095 if (cursor.currentList()->count() == cursor.currentList()->itemNumber(currentBlock) + 1) { 0096 // Last block in this list, but may have just gained another list below. 0097 if (currentBlock.next().textList()) { 0098 reformatList(); 0099 } 0100 0101 // No need to reformatList in this case. reformatList is slow. 0102 if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Backspace)) { 0103 handled = true; 0104 } 0105 } else { 0106 reformatList(); 0107 } 0108 } 0109 return handled; 0110 } 0111 0112 void NestedListHelper::processList(QTextList *list) 0113 { 0114 QTextBlock block = list->item(0); 0115 const int thisListIndent = list->format().indent(); 0116 0117 QTextCursor cursor = QTextCursor(block); 0118 list = cursor.createList(list->format()); 0119 bool processingSubList = false; 0120 while (block.next().textList() != nullptr) { 0121 block = block.next(); 0122 0123 QTextList *nextList = block.textList(); 0124 const int nextItemIndent = nextList->format().indent(); 0125 if (nextItemIndent < thisListIndent) { 0126 return; 0127 } else if (nextItemIndent > thisListIndent) { 0128 if (processingSubList) { 0129 continue; 0130 } 0131 processingSubList = true; 0132 processList(nextList); 0133 } else { 0134 processingSubList = false; 0135 list->add(block); 0136 } 0137 } 0138 // delete nextList; 0139 // nextList = 0; 0140 } 0141 0142 void NestedListHelper::reformatList(QTextBlock block) 0143 { 0144 if (block.textList()) { 0145 const int minimumIndent = block.textList()->format().indent(); 0146 0147 // Start at the top of the list 0148 while (block.previous().textList() != nullptr) { 0149 if (block.previous().textList()->format().indent() < minimumIndent) { 0150 break; 0151 } 0152 block = block.previous(); 0153 } 0154 0155 processList(block.textList()); 0156 } 0157 } 0158 0159 void NestedListHelper::reformatList() 0160 { 0161 const QTextCursor cursor = textEdit->textCursor(); 0162 reformatList(cursor.block()); 0163 } 0164 0165 QTextCursor NestedListHelper::topOfSelection() 0166 { 0167 QTextCursor cursor = textEdit->textCursor(); 0168 0169 if (cursor.hasSelection()) { 0170 cursor.setPosition(qMin(cursor.position(), cursor.anchor())); 0171 } 0172 return cursor; 0173 } 0174 0175 QTextCursor NestedListHelper::bottomOfSelection() 0176 { 0177 QTextCursor cursor = textEdit->textCursor(); 0178 0179 if (cursor.hasSelection()) { 0180 cursor.setPosition(qMax(cursor.position(), cursor.anchor())); 0181 } 0182 return cursor; 0183 } 0184 0185 void NestedListHelper::handleOnIndentMore() 0186 { 0187 QTextCursor cursor = textEdit->textCursor(); 0188 0189 QTextListFormat listFmt; 0190 if (!cursor.currentList()) { 0191 QTextListFormat::Style style; 0192 cursor = topOfSelection(); 0193 cursor.movePosition(QTextCursor::PreviousBlock); 0194 if (cursor.currentList()) { 0195 style = cursor.currentList()->format().style(); 0196 } else { 0197 cursor = bottomOfSelection(); 0198 cursor.movePosition(QTextCursor::NextBlock); 0199 0200 if (cursor.currentList()) { 0201 style = cursor.currentList()->format().style(); 0202 } else { 0203 style = QTextListFormat::ListDisc; 0204 } 0205 } 0206 handleOnBulletType(style); 0207 } else { 0208 listFmt = cursor.currentList()->format(); 0209 listFmt.setIndent(listFmt.indent() + 1); 0210 0211 cursor.createList(listFmt); 0212 reformatList(); 0213 } 0214 } 0215 0216 void NestedListHelper::handleOnIndentLess() 0217 { 0218 QTextCursor cursor = textEdit->textCursor(); 0219 QTextList *currentList = cursor.currentList(); 0220 if (!currentList) { 0221 return; 0222 } 0223 QTextListFormat listFmt = currentList->format(); 0224 if (listFmt.indent() > 1) { 0225 listFmt.setIndent(listFmt.indent() - 1); 0226 cursor.createList(listFmt); 0227 reformatList(cursor.block()); 0228 } else { 0229 QTextBlockFormat bfmt; 0230 bfmt.setObjectIndex(-1); 0231 cursor.setBlockFormat(bfmt); 0232 reformatList(cursor.block().next()); 0233 } 0234 } 0235 0236 void NestedListHelper::handleOnBulletType(int styleIndex) 0237 { 0238 QTextCursor cursor = textEdit->textCursor(); 0239 if (styleIndex != 0) { 0240 auto style = static_cast<QTextListFormat::Style>(styleIndex); 0241 QTextList *currentList = cursor.currentList(); 0242 QTextListFormat listFmt; 0243 0244 cursor.beginEditBlock(); 0245 0246 if (currentList) { 0247 listFmt = currentList->format(); 0248 listFmt.setStyle(style); 0249 currentList->setFormat(listFmt); 0250 } else { 0251 listFmt.setStyle(style); 0252 cursor.createList(listFmt); 0253 } 0254 0255 cursor.endEditBlock(); 0256 } else { 0257 QTextBlockFormat bfmt; 0258 bfmt.setObjectIndex(-1); 0259 cursor.setBlockFormat(bfmt); 0260 } 0261 0262 reformatList(); 0263 }