/** * Nested list helper * * SPDX-FileCopyrightText: 2008 Stephen Kelly * * SPDX-License-Identifier: LGPL-2.1-or-later */ #include "nestedlisthelper_p.h" #include #include #include #include NestedListHelper::NestedListHelper() { listBottomMargin = 12; listTopMargin = 12; listNoMargin = 0; } bool NestedListHelper::handleBeforeKeyPressEvent(QKeyEvent *event, const QTextCursor &cursor) { // Only attempt to handle Backspace while on a list if ((event->key() != Qt::Key_Backspace) || (!cursor.currentList())) { return false; } bool handled = false; if (!cursor.hasSelection() && cursor.currentList() && event->key() == Qt::Key_Backspace && cursor.atBlockStart()) { handleOnIndentLess(cursor); handled = true; } return handled; } bool NestedListHelper::canIndent(const QTextCursor &textCursor) const { if ((textCursor.block().isValid()) // && ( textEdit->textCursor().block().previous().isValid() ) ) { const QTextBlock block = textCursor.block(); const QTextBlock prevBlock = textCursor.block().previous(); if (block.textList()) { if (prevBlock.textList()) { return block.textList()->format().indent() <= prevBlock.textList()->format().indent(); } } else { return true; } } return false; } bool NestedListHelper::canDedent(const QTextCursor &textCursor) const { QTextBlock thisBlock = textCursor.block(); QTextBlock nextBlock = thisBlock.next(); if (thisBlock.isValid()) { int nextBlockIndent = 0; if (nextBlock.isValid() && nextBlock.textList()) { nextBlockIndent = nextBlock.textList()->format().indent(); } if (thisBlock.textList()) { const int thisBlockIndent = thisBlock.textList()->format().indent(); if (thisBlockIndent >= nextBlockIndent) { return thisBlockIndent > 0; } } } return false; } bool NestedListHelper::handleAfterKeyPressEvent(QKeyEvent *event, const QTextCursor &cursor) { // Only attempt to handle Backspace and Return if ((event->key() != Qt::Key_Backspace) && (event->key() != Qt::Key_Return)) { return false; } bool handled = false; if (!cursor.hasSelection() && cursor.currentList()) { // Check if we're on the last list item. // itemNumber is zero indexed QTextBlock currentBlock = cursor.block(); if (cursor.currentList()->count() == cursor.currentList()->itemNumber(currentBlock) + 1) { // Last block in this list, but may have just gained another list below. if (currentBlock.next().textList()) { reformatList(cursor.block()); } reformatList(cursor.block()); // No need to reformatList in this case. reformatList is slow. if ((event->key() == Qt::Key_Return) || (event->key() == Qt::Key_Backspace)) { handled = true; } } else { reformatList(cursor.block()); } } return handled; } void NestedListHelper::processList(QTextList *list) { QTextBlock block = list->item(0); const int thisListIndent = list->format().indent(); QTextCursor cursor = QTextCursor(block); list = cursor.createList(list->format()); bool processingSubList = false; while (block.next().textList() != nullptr) { block = block.next(); QTextList *nextList = block.textList(); const int nextItemIndent = nextList->format().indent(); if (nextItemIndent < thisListIndent) { return; } else if (nextItemIndent > thisListIndent) { if (processingSubList) { continue; } processingSubList = true; processList(nextList); } else { processingSubList = false; list->add(block); } } // delete nextList; // nextList = 0; } void NestedListHelper::reformatList(QTextBlock block) { if (block.textList()) { const int minimumIndent = block.textList()->format().indent(); // Start at the top of the list while (block.previous().textList() != nullptr) { if (block.previous().textList()->format().indent() < minimumIndent) { break; } block = block.previous(); } processList(block.textList()); } } QTextCursor NestedListHelper::topOfSelection(QTextCursor cursor) { if (cursor.hasSelection()) { cursor.setPosition(qMin(cursor.position(), cursor.anchor())); } return cursor; } QTextCursor NestedListHelper::bottomOfSelection(QTextCursor cursor) { if (cursor.hasSelection()) { cursor.setPosition(qMax(cursor.position(), cursor.anchor())); } return cursor; } void NestedListHelper::handleOnIndentMore(const QTextCursor &textCursor) { QTextCursor cursor = textCursor; QTextListFormat listFmt; if (!cursor.currentList()) { QTextListFormat::Style style; cursor = topOfSelection(textCursor); cursor.movePosition(QTextCursor::PreviousBlock); if (cursor.currentList()) { style = cursor.currentList()->format().style(); } else { cursor = bottomOfSelection(textCursor); cursor.movePosition(QTextCursor::NextBlock); if (cursor.currentList()) { style = cursor.currentList()->format().style(); } else { style = QTextListFormat::ListDisc; } } handleOnBulletType(style, textCursor); } else { listFmt = cursor.currentList()->format(); listFmt.setIndent(listFmt.indent() + 1); cursor.createList(listFmt); reformatList(textCursor.block()); } } void NestedListHelper::handleOnIndentLess(const QTextCursor &textCursor) { QTextCursor cursor = textCursor; QTextList *currentList = cursor.currentList(); if (!currentList) { return; } QTextListFormat listFmt = currentList->format(); if (listFmt.indent() > 1) { listFmt.setIndent(listFmt.indent() - 1); cursor.createList(listFmt); reformatList(cursor.block()); } else { QTextBlockFormat bfmt; bfmt.setObjectIndex(-1); cursor.setBlockFormat(bfmt); reformatList(cursor.block().next()); } } void NestedListHelper::handleOnBulletType(QTextListFormat::Style style, QTextCursor cursor) { if (cursor.isNull()) { return; } QTextListFormat::Style currentListStyle = QTextListFormat::ListStyleUndefined; if (cursor.currentList()) { currentListStyle = cursor.currentList()->format().style(); } if (style != currentListStyle && style != QTextListFormat::ListStyleUndefined) { QTextList *currentList = cursor.currentList(); QTextListFormat listFmt; cursor.beginEditBlock(); if (currentList) { listFmt = currentList->format(); listFmt.setStyle(style); currentList->setFormat(listFmt); } else { listFmt.setStyle(style); cursor.createList(listFmt); } cursor.endEditBlock(); } else { QTextBlockFormat bfmt; bfmt.setObjectIndex(-1); cursor.setBlockFormat(bfmt); } reformatList(cursor.block()); }