File indexing completed on 2024-04-14 03:53:50

0001 /*
0002     SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "dynamictreewidget.h"
0008 
0009 #include <QComboBox>
0010 #include <QDebug>
0011 #include <QHBoxLayout>
0012 #include <QPlainTextEdit>
0013 #include <QPushButton>
0014 #include <QRadioButton>
0015 #include <QTabWidget>
0016 #include <QTreeView>
0017 
0018 #include "dynamictreemodel.h"
0019 
0020 static const char *const treePredefinesNames[] = {"Flat List", "Straight Line Tree", "Dragon Teeth 1", "Dragon Teeth 2", "Random Tree 1"};
0021 
0022 static const char *const treePredefinesContent[] = {
0023     " - 1"
0024     " - 1"
0025     " - 1"
0026     " - 1"
0027     " - 1"
0028     " - 1"
0029     " - 1"
0030     " - 1"
0031     " - 1"
0032     " - 1"
0033     " - 1"
0034     " - 1"
0035     " - 1"
0036     " - 1"
0037     " - 1"
0038     " - 1",
0039 
0040     " - 1"
0041     " - - 1"
0042     " - - - 1"
0043     " - - - - 1"
0044     " - - - - - 1"
0045     " - - - - - - 1"
0046     " - - - - - - - 1"
0047     " - - - - - - - - 1"
0048     " - - - - - - - - - 1"
0049     " - - - - - - - - - - 1"
0050     " - - - - - - - - - - - 1"
0051     " - - - - - - - - - - - - 1"
0052     " - - - - - - - - - - - - - 1"
0053     " - - - - - - - - - - - - - - 1"
0054     " - - - - - - - - - - - - - - - 1"
0055     " - - - - - - - - - - - - - - - - 1",
0056 
0057     " - 1"
0058     " - - 1"
0059     " - - - 1"
0060     " - - - - 1"
0061     " - 1"
0062     " - - 1"
0063     " - - - 1"
0064     " - - - - 1"
0065     " - 1"
0066     " - - 1"
0067     " - - - 1"
0068     " - - - - 1"
0069     " - 1"
0070     " - - 1"
0071     " - - - 1"
0072     " - - - - 1",
0073 
0074     " - 1"
0075     " - - 1"
0076     " - - - 1"
0077     " - - - - 1"
0078     " - - - - - 1"
0079     " - 1"
0080     " - - 1"
0081     " - - - 1"
0082     " - - - - 1"
0083     " - - - - - 1"
0084     " - 1"
0085     " - - 1"
0086     " - - - 1"
0087     " - - - - 1"
0088     " - - - - - 1"
0089     " - 1"
0090     " - - 1"
0091     " - - - 1"
0092     " - - - - 1"
0093     " - - - - - 1",
0094 
0095     " - 1"
0096     " - 2"
0097     " - - 3"
0098     " - - - 4"
0099     " - 5"
0100     " - 6"
0101     " - 7"
0102     " - - 8"
0103     " - - - 9"
0104     " - - - 10"
0105     " - - - - 11"
0106     " - - - 12"
0107     " - - - - 13"
0108     " - 14"
0109     " - 15"};
0110 
0111 static const char *const insertSubTreePredefinesNames[] = {"Flat List", "Straight Line Tree", "Dragon Teeth 1", "Dragon Teeth 2", "Random Tree 1"};
0112 
0113 static const char *const insertSubTreePredefinesContent[] = {
0114     " - 1\n"
0115     " - 1\n"
0116     " - 1\n"
0117     " - 1\n",
0118 
0119     " - 1\n"
0120     " - - 1\n"
0121     " - - - 1\n"
0122     " - - - - 1\n",
0123 
0124     " - 1\n"
0125     " - - 1\n"
0126     " - 1\n"
0127     " - - 1\n",
0128 
0129     " - 1\n"
0130     " - - 1\n"
0131     " - - - 1\n"
0132     " - 1\n"
0133     " - - 1\n"
0134     " - - - 1\n",
0135 
0136     " - 1\n"
0137     " - 2\n"
0138     " - - 3\n"
0139     " - - - 4\n"
0140     " - 5\n"};
0141 
0142 DynamicTreeWidget::DynamicTreeWidget(DynamicTreeModel *rootModel, QWidget *parent, Qt::WindowFlags f)
0143     : QWidget(parent, f)
0144     , m_dynamicTreeModel(rootModel)
0145 {
0146     QTabWidget *tabWidget = new QTabWidget(this);
0147 
0148     QHBoxLayout *layout = new QHBoxLayout(this);
0149     layout->addWidget(tabWidget);
0150 
0151     QWidget *editContainer = new QWidget(tabWidget);
0152     QVBoxLayout *editLayout = new QVBoxLayout(editContainer);
0153 
0154     m_treePredefines = new QComboBox(editContainer);
0155     for (uint i = 0; i < sizeof treePredefinesNames / sizeof *treePredefinesNames; ++i) {
0156         m_treePredefines->addItem(*(treePredefinesNames + i), *(treePredefinesContent + i));
0157     }
0158     editLayout->addWidget(m_treePredefines);
0159     connect(m_treePredefines, &QComboBox::currentIndexChanged, this, &DynamicTreeWidget::setTreePredefine);
0160 
0161     m_textEdit = new QPlainTextEdit(editContainer);
0162     editLayout->addWidget(m_textEdit);
0163 
0164     QWidget *viewContainer = new QWidget(tabWidget);
0165 
0166     QVBoxLayout *viewLayout = new QVBoxLayout(viewContainer);
0167 
0168     m_treeView = new QTreeView(tabWidget);
0169     m_treeView->setModel(rootModel);
0170     m_treeView->setSelectionMode(QAbstractItemView::ExtendedSelection);
0171     m_treeView->setDragDropMode(QAbstractItemView::InternalMove);
0172     m_treeView->setDragEnabled(true);
0173     m_treeView->setAcceptDrops(true);
0174     m_treeView->setDropIndicatorShown(true);
0175 
0176     QPushButton *m_removeButton = new QPushButton(QStringLiteral("Remove"), tabWidget);
0177 
0178     connect(m_removeButton, &QAbstractButton::clicked, this, &DynamicTreeWidget::removeSelected);
0179 
0180     m_insertSubTreePredefines = new QComboBox(this);
0181     for (uint i = 0; i < sizeof insertSubTreePredefinesNames / sizeof *insertSubTreePredefinesNames; ++i) {
0182         m_insertSubTreePredefines->addItem(*(insertSubTreePredefinesNames + i), *(insertSubTreePredefinesContent + i));
0183     }
0184     editLayout->addWidget(m_insertSubTreePredefines);
0185     connect(m_insertSubTreePredefines, &QComboBox::currentIndexChanged, this, &DynamicTreeWidget::setInsertSubTreePredefine);
0186 
0187     m_insertPatternTextEdit = new QPlainTextEdit(tabWidget);
0188     m_insertPatternTextEdit->setMaximumHeight(100);
0189 
0190     m_insertChildren = new QRadioButton(QStringLiteral("Insert Children"), tabWidget);
0191     m_insertSiblingsAbove = new QRadioButton(QStringLiteral("Insert Siblings Above"), tabWidget);
0192     m_insertSiblingsBelow = new QRadioButton(QStringLiteral("Insert Siblings Below"), tabWidget);
0193 
0194     m_insertChildren->setChecked(true);
0195 
0196     QPushButton *m_insertButton = new QPushButton(QStringLiteral("Insert"), tabWidget);
0197 
0198     connect(m_insertButton, &QAbstractButton::clicked, this, &DynamicTreeWidget::insertSelected);
0199 
0200     QPushButton *m_resetButton = new QPushButton(QStringLiteral("Reset"), tabWidget);
0201 
0202     connect(m_resetButton, &QAbstractButton::clicked, this, &DynamicTreeWidget::resetModel);
0203 
0204     viewLayout->addWidget(m_treeView);
0205 
0206     viewLayout->addWidget(m_removeButton);
0207 
0208     viewLayout->addWidget(m_insertSubTreePredefines);
0209     viewLayout->addWidget(m_insertPatternTextEdit);
0210     viewLayout->addWidget(m_insertChildren);
0211     viewLayout->addWidget(m_insertSiblingsAbove);
0212     viewLayout->addWidget(m_insertSiblingsBelow);
0213     viewLayout->addWidget(m_insertButton);
0214     viewLayout->addWidget(m_resetButton);
0215 
0216     tabWidget->addTab(editContainer, QStringLiteral("Edit"));
0217     tabWidget->addTab(viewContainer, QStringLiteral("View"));
0218 
0219     tabWidget->setCurrentIndex(ViewTab);
0220 
0221     connect(tabWidget, &QTabWidget::currentChanged, this, &DynamicTreeWidget::currentChanged);
0222     stringToModel(
0223         QLatin1String(" - 1"
0224                       " - 2"
0225                       " - - 3"
0226                       " - - 4"
0227                       " - - 5"
0228                       " - 6"
0229                       " - 7"
0230                       " - - 8"
0231                       " - - - 9"
0232                       " - - - 10"
0233                       " - - 11"
0234                       " - - 12"
0235                       " - 13"
0236                       " - 14"
0237                       " - 15"
0238                       " - - 16"
0239                       " - - - 17"
0240                       " - - - 18"
0241                       " - 19"
0242                       " - 20"
0243                       " - 21"));
0244 }
0245 
0246 void DynamicTreeWidget::setInitialTree(const QString &treeString)
0247 {
0248     stringToModel(treeString);
0249 }
0250 
0251 void DynamicTreeWidget::currentChanged(int index)
0252 {
0253     switch (index) {
0254     case EditTab:
0255         m_textEdit->setPlainText(modelTreeToString(0, QModelIndex()));
0256         break;
0257     case ViewTab:
0258         if (m_textEdit->document()->isModified()) {
0259             stringToModel(m_textEdit->toPlainText());
0260         }
0261         m_textEdit->document()->setModified(false);
0262         break;
0263     }
0264 }
0265 
0266 void DynamicTreeWidget::stringToModel(const QString &treeString)
0267 {
0268     if (treeString.isEmpty()) {
0269         return;
0270     }
0271 
0272     m_dynamicTreeModel->clear();
0273     ModelInsertCommand *command = new ModelInsertCommand(m_dynamicTreeModel, this);
0274     command->setStartRow(0);
0275     command->interpret(treeString);
0276     command->doCommand();
0277     m_treeView->expandAll();
0278 }
0279 
0280 QString DynamicTreeWidget::modelTreeToString(int depth, const QModelIndex &parent)
0281 {
0282     QString result;
0283     QModelIndex idx;
0284     static const int column = 0;
0285     QString prefix;
0286 
0287     for (int i = 0; i <= depth; ++i) {
0288         prefix.append(" -");
0289     }
0290 
0291     for (int row = 0; row < m_dynamicTreeModel->rowCount(parent); ++row) {
0292         idx = m_dynamicTreeModel->index(row, column, parent);
0293         result.append(prefix + " " + idx.data().toString() + "\n");
0294         if (m_dynamicTreeModel->hasChildren(idx)) {
0295             result.append(modelTreeToString(depth + 1, idx));
0296         }
0297     }
0298     return result;
0299 }
0300 
0301 void DynamicTreeWidget::removeSelected()
0302 {
0303     QModelIndex parent;
0304     ModelRemoveCommand *removeCommand = new ModelRemoveCommand(m_dynamicTreeModel, this);
0305     QItemSelection selection = m_treeView->selectionModel()->selection();
0306     while (!selection.isEmpty()) {
0307         const QItemSelectionRange range = selection.takeFirst(); // The selection model will take care of updating persistent indexes.
0308         Q_ASSERT(range.isValid());
0309         qDebug() << range.parent() << range.top() << range.bottom();
0310         removeCommand->setAncestorRowNumbers(m_dynamicTreeModel->indexToPath(range.parent()));
0311         removeCommand->setStartRow(range.top());
0312         removeCommand->setEndRow(range.bottom());
0313 
0314         qDebug() << m_dynamicTreeModel->indexToPath(range.parent());
0315 
0316         removeCommand->doCommand();
0317         selection = m_treeView->selectionModel()->selection();
0318     }
0319 }
0320 
0321 void DynamicTreeWidget::insertSelected()
0322 {
0323     const QModelIndexList selectedRows = m_treeView->selectionModel()->selectedRows();
0324 
0325     if (selectedRows.size() != 1) {
0326         return;
0327     }
0328 
0329     const QModelIndex selectedRow = selectedRows.first();
0330 
0331     ModelInsertCommand *ins = new ModelInsertCommand(m_dynamicTreeModel, this);
0332     if (m_insertChildren->isChecked()) {
0333         ins->setAncestorRowNumbers(m_dynamicTreeModel->indexToPath(selectedRow));
0334         ins->setStartRow(0);
0335     } else if (m_insertSiblingsAbove->isChecked()) {
0336         ins->setAncestorRowNumbers(m_dynamicTreeModel->indexToPath(selectedRow.parent()));
0337         ins->setStartRow(selectedRow.row());
0338     } else {
0339         Q_ASSERT(m_insertSiblingsBelow->isChecked());
0340         ins->setAncestorRowNumbers(m_dynamicTreeModel->indexToPath(selectedRow.parent()));
0341         ins->setStartRow(selectedRow.row() + 1);
0342     }
0343     ins->interpret(m_insertPatternTextEdit->toPlainText());
0344     ins->doCommand();
0345 }
0346 
0347 void DynamicTreeWidget::resetModel()
0348 {
0349     ModelResetCommand *resetCommand = new ModelResetCommand(m_dynamicTreeModel, this);
0350 
0351     resetCommand->setInitialTree(m_insertPatternTextEdit->toPlainText().trimmed());
0352     resetCommand->doCommand();
0353 }
0354 
0355 void DynamicTreeWidget::setTreePredefine(int index)
0356 {
0357     stringToModel(m_treePredefines->itemData(index).toString());
0358     m_textEdit->setPlainText(modelTreeToString(0, QModelIndex()));
0359 }
0360 
0361 void DynamicTreeWidget::setInsertSubTreePredefine(int index)
0362 {
0363     m_insertPatternTextEdit->setPlainText(m_insertSubTreePredefines->itemData(index).toString());
0364 }
0365 
0366 #include "moc_dynamictreewidget.cpp"