KDEUI
nestedlisthelper.cpp
Go to the documentation of this file.
00001 00022 #include "nestedlisthelper.h" 00023 00024 #include <QtGui/QKeyEvent> 00025 #include <QtGui/QTextCursor> 00026 #include <QtGui/QTextList> 00027 #include <QtGui/QTextBlock> 00028 #include <QtGui/QTextDocumentFragment> 00029 00030 #include <ktextedit.h> 00031 #include <kdebug.h> 00032 00033 NestedListHelper::NestedListHelper(QTextEdit *te) 00034 { 00035 textEdit = te; 00036 listBottomMargin = 12; 00037 listTopMargin = 12; 00038 listNoMargin = 0; 00039 } 00040 00041 NestedListHelper::~NestedListHelper() 00042 { 00043 } 00044 00045 bool NestedListHelper::handleBeforeKeyPressEvent(QKeyEvent *event) 00046 { 00047 QTextCursor cursor = textEdit->textCursor(); 00048 00049 // Only attempt to handle Backspace while on a list 00050 if ((event->key() != Qt::Key_Backspace) 00051 || (!cursor.currentList())) 00052 return false; 00053 00054 bool handled = false; 00055 00056 if (!cursor.hasSelection() 00057 && cursor.currentList() 00058 && event->key() == Qt::Key_Backspace 00059 && cursor.atBlockStart()) { 00060 handleOnIndentLess(); 00061 handled = true; 00062 } 00063 00064 if (cursor.hasSelection() 00065 && cursor.currentList() 00066 && event->key() == Qt::Key_Backspace 00067 && cursor.atBlockStart()) { 00068 00069 // Workaround for qt bug 211460: 00070 // If there is a list with selection like this: 00071 // 00072 // * one 00073 // * <cursor>t<anchor>wo 00074 // 00075 // and backspace is pressed, the bullet is removed, but not 00076 // the 't'. 00077 // Fixed scheduled for qt4.5 00078 // -- Stephen Kelly, 8th June 2008 00079 00080 cursor.removeSelectedText(); 00081 handled = true; 00082 } 00083 00084 return handled; 00085 } 00086 00087 bool NestedListHelper::canIndent() const 00088 { 00089 if ((textEdit->textCursor().block().isValid()) 00090 // && ( textEdit->textCursor().block().previous().isValid() ) 00091 ) { 00092 QTextBlock block = textEdit->textCursor().block(); 00093 QTextBlock prevBlock = textEdit->textCursor().block().previous(); 00094 if (block.textList()) { 00095 if (prevBlock.textList()) { 00096 return block.textList()->format().indent() <= prevBlock.textList()->format().indent(); 00097 } 00098 } else { 00099 return true; 00100 } 00101 } 00102 return false; 00103 } 00104 00105 bool NestedListHelper::canDedent() const 00106 { 00107 QTextBlock thisBlock = textEdit->textCursor().block(); 00108 QTextBlock nextBlock = thisBlock.next(); 00109 if (thisBlock.isValid()) { 00110 int nextBlockIndent = 0; 00111 int thisBlockIndent = 0; 00112 if (nextBlock.isValid() && nextBlock.textList()) 00113 nextBlockIndent = nextBlock.textList()->format().indent(); 00114 if (thisBlock.textList()) { 00115 thisBlockIndent = thisBlock.textList()->format().indent(); 00116 if (thisBlockIndent >= nextBlockIndent) 00117 return thisBlock.textList()->format().indent() > 0; 00118 } 00119 } 00120 return false; 00121 00122 } 00123 00124 bool NestedListHelper::handleAfterKeyPressEvent(QKeyEvent *event) 00125 { 00126 // Only attempt to handle Backspace and Return 00127 if ((event->key() != Qt::Key_Backspace) 00128 && (event->key() != Qt::Key_Return)) 00129 return false; 00130 00131 QTextCursor cursor = textEdit->textCursor(); 00132 bool handled = false; 00133 00134 if (!cursor.hasSelection() && cursor.currentList()) { 00135 00136 // Check if we're on the last list item. 00137 // itemNumber is zero indexed 00138 QTextBlock currentBlock = cursor.block(); 00139 if (cursor.currentList()->count() == cursor.currentList()->itemNumber(currentBlock) + 1) { 00140 // Last block in this list, but may have just gained another list below. 00141 if (currentBlock.next().textList()) { 00142 reformatList(); 00143 } 00144 00145 // No need to reformatList in this case. reformatList is slow. 00146 if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Backspace)) { 00147 reformatBoundingItemSpacing(); 00148 handled = true; 00149 } 00150 } else { 00151 reformatList(); 00152 } 00153 } 00154 return handled; 00155 } 00156 00157 00158 bool NestedListHelper::handleAfterDropEvent(QDropEvent *dropEvent) 00159 { 00160 Q_UNUSED(dropEvent); 00161 QTextCursor cursor = topOfSelection(); 00162 00163 QTextBlock droppedBlock = cursor.block(); 00164 int firstDroppedItemIndent = droppedBlock.textList()->format().indent(); 00165 00166 int minimumIndent = droppedBlock.previous().textList()->format().indent(); 00167 00168 if (firstDroppedItemIndent < minimumIndent) { 00169 cursor = QTextCursor(droppedBlock); 00170 QTextListFormat fmt = droppedBlock.textList()->format(); 00171 fmt.setIndent(minimumIndent); 00172 QTextList* list = cursor.createList(fmt); 00173 00174 int endOfDrop = bottomOfSelection().position(); 00175 while (droppedBlock.next().position() < endOfDrop) { 00176 droppedBlock = droppedBlock.next(); 00177 if (droppedBlock.textList()->format().indent() != firstDroppedItemIndent) { 00178 00179 // new list? 00180 } 00181 list->add(droppedBlock); 00182 } 00183 // list.add( droppedBlock ); 00184 } 00185 00186 reformatBoundingItemSpacing(); 00187 return true; 00188 } 00189 00190 void NestedListHelper::processList(QTextList* list) 00191 { 00192 QTextBlock block = list->item(0); 00193 int thisListIndent = list->format().indent(); 00194 00195 QTextCursor cursor = QTextCursor(block); 00196 list = cursor.createList(list->format()); 00197 bool processingSubList = false; 00198 while (block.next().textList() != 0) { 00199 block = block.next(); 00200 00201 QTextList* nextList = block.textList(); 00202 int nextItemIndent = nextList->format().indent(); 00203 if (nextItemIndent < thisListIndent) { 00204 return; 00205 } else if (nextItemIndent > thisListIndent) { 00206 if (processingSubList) { 00207 continue; 00208 } 00209 processingSubList = true; 00210 processList(nextList); 00211 } else { 00212 processingSubList = false; 00213 list->add(block); 00214 } 00215 } 00216 // delete nextList; 00217 // nextList = 0; 00218 } 00219 00220 void NestedListHelper::reformatList(QTextBlock block) 00221 { 00222 if (block.textList()) { 00223 int minimumIndent = block.textList()->format().indent(); 00224 00225 // Start at the top of the list 00226 while (block.previous().textList() != 0) { 00227 if (block.previous().textList()->format().indent() < minimumIndent) { 00228 break; 00229 } 00230 block = block.previous(); 00231 } 00232 00233 processList(block.textList()); 00234 00235 } 00236 } 00237 00238 void NestedListHelper::reformatList() 00239 { 00240 QTextCursor cursor = textEdit->textCursor(); 00241 reformatList(cursor.block()); 00242 } 00243 00244 QTextCursor NestedListHelper::topOfSelection() 00245 { 00246 QTextCursor cursor = textEdit->textCursor(); 00247 00248 if (cursor.hasSelection()) 00249 cursor.setPosition(qMin(cursor.position(), cursor.anchor())); 00250 return cursor; 00251 } 00252 00253 QTextCursor NestedListHelper::bottomOfSelection() 00254 { 00255 QTextCursor cursor = textEdit->textCursor(); 00256 00257 if (cursor.hasSelection()) 00258 cursor.setPosition(qMax(cursor.position(), cursor.anchor())); 00259 return cursor; 00260 } 00261 00262 void NestedListHelper::handleOnIndentMore() 00263 { 00264 QTextCursor cursor = textEdit->textCursor(); 00265 00266 QTextListFormat listFmt; 00267 if (!cursor.currentList()) { 00268 00269 QTextListFormat::Style style; 00270 cursor = topOfSelection(); 00271 cursor.movePosition(QTextCursor::PreviousBlock); 00272 if (cursor.currentList()) { 00273 style = cursor.currentList()->format().style(); 00274 } else { 00275 00276 cursor = bottomOfSelection(); 00277 cursor.movePosition(QTextCursor::NextBlock); 00278 00279 if (cursor.currentList()) { 00280 style = cursor.currentList()->format().style(); 00281 } else { 00282 style = QTextListFormat::ListDisc; 00283 } 00284 } 00285 handleOnBulletType(style); 00286 } else { 00287 listFmt = cursor.currentList()->format(); 00288 listFmt.setIndent(listFmt.indent() + 1); 00289 00290 cursor.createList(listFmt); 00291 reformatList(); 00292 } 00293 00294 reformatBoundingItemSpacing(); 00295 } 00296 00297 void NestedListHelper::handleOnIndentLess() 00298 { 00299 QTextCursor cursor = textEdit->textCursor(); 00300 QTextList* currentList = cursor.currentList(); 00301 if (!currentList) 00302 return; 00303 QTextListFormat listFmt; 00304 listFmt = currentList->format(); 00305 if (listFmt.indent() > 1) { 00306 listFmt.setIndent(listFmt.indent() - 1); 00307 cursor.createList(listFmt); 00308 reformatList(cursor.block()); 00309 } else { 00310 QTextBlockFormat bfmt; 00311 bfmt.setObjectIndex(-1); 00312 cursor.setBlockFormat(bfmt); 00313 reformatList(cursor.block().next()); 00314 } 00315 reformatBoundingItemSpacing(); 00316 } 00317 00318 00319 void NestedListHelper::handleOnBulletType(int styleIndex) 00320 { 00321 QTextCursor cursor = textEdit->textCursor(); 00322 if (styleIndex != 0) { 00323 QTextListFormat::Style style = (QTextListFormat::Style)styleIndex; 00324 QTextList *currentList = cursor.currentList(); 00325 QTextListFormat listFmt; 00326 00327 cursor.beginEditBlock(); 00328 00329 if (currentList) { 00330 listFmt = currentList->format(); 00331 listFmt.setStyle(style); 00332 currentList->setFormat(listFmt); 00333 } else { 00334 listFmt.setStyle(style); 00335 cursor.createList(listFmt); 00336 } 00337 00338 cursor.endEditBlock(); 00339 } else { 00340 QTextBlockFormat bfmt; 00341 bfmt.setObjectIndex(-1); 00342 cursor.setBlockFormat(bfmt); 00343 reformatBoundingItemSpacing(); 00344 } 00345 00346 reformatBoundingItemSpacing(); 00347 reformatList(); 00348 } 00349 00350 void NestedListHelper::reformatBoundingItemSpacing(QTextBlock block) 00351 { 00352 // This is a workaround for qt bug 201228. Spacing between items is not kept 00353 // consistent. 00354 // Fixed scheduled for qt4.5 00355 // -- Stephen Kelly, 8th June 2008 00356 00357 int nextBlockTopMargin = listNoMargin; 00358 int previousBlockBottomMargin = listNoMargin; 00359 int thisBlockBottomMargin = listBottomMargin; 00360 int thisBlockTopMargin = listTopMargin; 00361 bool prevBlockValid = block.previous().isValid(); 00362 bool nextBlockValid = block.next().isValid(); 00363 00364 if (block.textList()) { 00365 if (prevBlockValid && block.previous().textList()) { 00366 thisBlockTopMargin = listNoMargin; 00367 } 00368 00369 if (nextBlockValid && block.next().textList()) { 00370 thisBlockBottomMargin = listNoMargin; 00371 } 00372 } else { 00373 if (prevBlockValid && !block.previous().textList()) { 00374 thisBlockTopMargin = listNoMargin; 00375 } 00376 if (nextBlockValid && !block.next().textList()) { 00377 thisBlockBottomMargin = listNoMargin; 00378 } 00379 00380 } 00381 QTextBlockFormat fmt; 00382 QTextCursor cursor; 00383 00384 fmt = block.blockFormat(); 00385 fmt.setBottomMargin(thisBlockBottomMargin); 00386 fmt.setTopMargin(thisBlockTopMargin); 00387 cursor = QTextCursor(block); 00388 cursor.setBlockFormat(fmt); 00389 00390 if (nextBlockValid) { 00391 block = block.next(); 00392 fmt = block.blockFormat(); 00393 fmt.setTopMargin(nextBlockTopMargin); 00394 cursor = QTextCursor(block); 00395 cursor.setBlockFormat(fmt); 00396 00397 block = block.previous(); 00398 } 00399 if (prevBlockValid) { 00400 block = block.previous(); 00401 fmt = block.blockFormat(); 00402 fmt.setBottomMargin(previousBlockBottomMargin); 00403 cursor = QTextCursor(block); 00404 cursor.setBlockFormat(fmt); 00405 } 00406 } 00407 00408 void NestedListHelper::reformatBoundingItemSpacing() 00409 { 00410 reformatBoundingItemSpacing(topOfSelection().block()); 00411 reformatBoundingItemSpacing(bottomOfSelection().block()); 00412 }
KDE 4.6 API Reference