Files
neochat/src/libneochat/nestedlisthelper.cpp
2026-02-14 19:52:44 +00:00

255 lines
7.4 KiB
C++

/**
* Nested list helper
*
* SPDX-FileCopyrightText: 2008 Stephen Kelly <steveire@gmail.com>
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "nestedlisthelper_p.h"
#include <QKeyEvent>
#include <QTextBlock>
#include <QTextCursor>
#include <QTextList>
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());
}