From beecb5b5f7dd650fa5036c850b2f16b25313db6b Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 18 Feb 2022 23:26:24 +0100 Subject: [PATCH] Add tree view for project notes (#41) * Convert the story tree into a stack of item trees * Rename StoryTree to ItemTree * Extend item tre and model to add folders and notes * Restrict which items can hold documents * Let doc editor keep item instead of handle * Rename item type Folder to Group * Implement close project and cleanup functionality --- CMakeLists.txt | 4 +- i18n/collett_en_US.ts | 201 ++++++++++-------- i18n/collett_nb_NO.ts | 201 ++++++++++-------- .../81def5a0-62c6-48b8-9bf3-cb031a09e2bb.json | 19 ++ ...c2290a95-ca41-4181-89f2-18cb610ab716.json} | 0 sample/project/characters.json | 17 ++ sample/project/project.json | 6 +- src/core/data.cpp | 8 - src/core/data.h | 1 - src/editor/doceditor.cpp | 27 ++- src/editor/doceditor.h | 15 +- src/gui/{storytree.cpp => itemtree.cpp} | 104 +++++---- src/gui/{storytree.h => itemtree.h} | 18 +- ...ytreedelegate.cpp => itemtreedelegate.cpp} | 12 +- ...storytreedelegate.h => itemtreedelegate.h} | 16 +- src/gui/maintoolbar.cpp | 8 +- src/gui/maintoolbar.h | 1 + src/gui/treetoolbar.cpp | 69 +++++- src/gui/treetoolbar.h | 29 ++- src/guimain.cpp | 115 +++++++--- src/guimain.h | 24 ++- src/project/item.cpp | 40 +++- src/project/item.h | 8 +- src/project/itemmodel.cpp | 79 +++++-- src/project/itemmodel.h | 8 +- src/project/project.cpp | 8 +- src/project/project.h | 3 + 27 files changed, 701 insertions(+), 340 deletions(-) create mode 100644 sample/content/81def5a0-62c6-48b8-9bf3-cb031a09e2bb.json rename sample/content/{e709ba3f-3141-4b4b-95df-4a8d3e91a8ba.json => c2290a95-ca41-4181-89f2-18cb610ab716.json} (100%) rename src/gui/{storytree.cpp => itemtree.cpp} (58%) rename src/gui/{storytree.h => itemtree.h} (81%) rename src/gui/{storytreedelegate.cpp => itemtreedelegate.cpp} (85%) rename src/gui/{storytreedelegate.h => itemtreedelegate.h} (79%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 688c584..b421b83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,10 +89,10 @@ list(APPEND SRC_FILES src/editor/doceditor src/editor/edittoolbar src/editor/textedit + src/gui/itemtree + src/gui/itemtreedelegate src/gui/maintoolbar src/gui/statusbar - src/gui/storytree - src/gui/storytreedelegate src/gui/treetoolbar src/project/document src/project/item diff --git a/i18n/collett_en_US.ts b/i18n/collett_en_US.ts index 82c38db..f85ab41 100644 --- a/i18n/collett_en_US.ts +++ b/i18n/collett_en_US.ts @@ -110,150 +110,183 @@ - Collett::GuiMain + Collett::GuiItemTree - - %1 %2 Version %3 + + Add Scene - - - Collett::GuiMainToolBar - - No Project + + + + + + + + Inside - - New Project + + + + + + + + Before - - Open Project + + + + + + + + After - - Save Project + + Add Chapter - - Project + + Add Partition - - New Document + + Add Book - - Open Document + + + + Here - - Save Document + + Add Page - - Rename Document + + Add Group - - Documents + + Add Note - - Menu + + Rename Story Item + + + + + New Name: - Collett::GuiStoryTree + Collett::GuiMain - - Add Scene + + %1 %2 Version %3 - - - - - - Inside + + Question - - - - - - Before + + Do you want to close the project? + + + Collett::GuiMainToolBar - - - - - - After + + No Project - - Add Chapter + + New Project - - Add Partition + + Open Project - - Add Book + + Save Project - - Here + + Close Project - - Add Page + + Project - - Rename Story Item + + New Document - - New Name: + + Open Document + + + + + Save Document + + + + + Rename Document + + + + + Documents + + + + + Menu Collett::GuiTreeToolBar - - Story + + Add Action - + Settings @@ -266,37 +299,37 @@ - - Folder - - - - + Book - + Partition - + Chapter - + Scene - + Page - + + Group + + + + Note @@ -304,32 +337,32 @@ Collett::ItemModel - + New %1 - + Story - + Plot - + Characters - + Locations - + No Name @@ -337,7 +370,7 @@ Collett::Project - + Unnamed Project diff --git a/i18n/collett_nb_NO.ts b/i18n/collett_nb_NO.ts index b840889..654c3e3 100644 --- a/i18n/collett_nb_NO.ts +++ b/i18n/collett_nb_NO.ts @@ -110,150 +110,183 @@ - Collett::GuiMain + Collett::GuiItemTree - - %1 %2 Version %3 + + Add Scene - - - Collett::GuiMainToolBar - - No Project + + + + + + + + Inside - - New Project + + + + + + + + Before - - Open Project + + + + + + + + After - - Save Project + + Add Chapter - - Project + + Add Partition - - New Document + + Add Book - - Open Document + + + + Here - - Save Document + + Add Page - - Rename Document + + Add Group - - Documents + + Add Note - - Menu + + Rename Story Item + + + + + New Name: - Collett::GuiStoryTree + Collett::GuiMain - - Add Scene + + %1 %2 Version %3 - - - - - - Inside + + Question - - - - - - Before + + Do you want to close the project? + + + Collett::GuiMainToolBar - - - - - - After + + No Project - - Add Chapter + + New Project - - Add Partition + + Open Project - - Add Book + + Save Project - - Here + + Close Project - - Add Page + + Project - - Rename Story Item + + New Document - - New Name: + + Open Document + + + + + Save Document + + + + + Rename Document + + + + + Documents + + + + + Menu Collett::GuiTreeToolBar - - Story + + Add Action - + Settings @@ -266,37 +299,37 @@ - - Folder - - - - + Book - + Partition - + Chapter - + Scene - + Page - + + Group + + + + Note @@ -304,32 +337,32 @@ Collett::ItemModel - + New %1 - + Story - + Plot - + Characters - + Locations - + No Name @@ -337,7 +370,7 @@ Collett::Project - + Unnamed Project diff --git a/sample/content/81def5a0-62c6-48b8-9bf3-cb031a09e2bb.json b/sample/content/81def5a0-62c6-48b8-9bf3-cb031a09e2bb.json new file mode 100644 index 0000000..8432d70 --- /dev/null +++ b/sample/content/81def5a0-62c6-48b8-9bf3-cb031a09e2bb.json @@ -0,0 +1,19 @@ +{ + "c:format": "document", + "m:created": "2022-02-18T21:03:13", + "m:updated": "2022-02-18T22:20:47", + "x:content": [ + { + "u:fmt": "h1:al", + "u:txt": "t:b|Jane Doe" + }, + { + "u:fmt": "p:al", + "u:txt": "t|Jane Doe is not just anybody, she's Jane Doe, the mysterious woman who no one knows the real name of." + }, + { + "u:fmt": "p:al:ti", + "u:txt": "t|She's not gonna tell you her real name, so there is no point asking. No point being a super hero if everyone knows your name and where you live so they can call you each time the cat goes looking for a treat at the neighbours'!" + } + ] +} diff --git a/sample/content/e709ba3f-3141-4b4b-95df-4a8d3e91a8ba.json b/sample/content/c2290a95-ca41-4181-89f2-18cb610ab716.json similarity index 100% rename from sample/content/e709ba3f-3141-4b4b-95df-4a8d3e91a8ba.json rename to sample/content/c2290a95-ca41-4181-89f2-18cb610ab716.json diff --git a/sample/project/characters.json b/sample/project/characters.json index b959eff..bd8d0ef 100644 --- a/sample/project/characters.json +++ b/sample/project/characters.json @@ -4,5 +4,22 @@ "u:name": "Characters", "u:type": "ROOT", "x:items": [ + { + "m:expanded": true, + "m:handle": "e0428917-b42f-49ac-a481-3c11bc0dc506", + "m:order": 0, + "m:words": 0, + "u:name": "Main Characters", + "u:type": "GROUP", + "x:items": [ + { + "m:handle": "81def5a0-62c6-48b8-9bf3-cb031a09e2bb", + "m:order": 0, + "m:words": 0, + "u:name": "Jane Doe", + "u:type": "NOTE" + } + ] + } ] } diff --git a/sample/project/project.json b/sample/project/project.json index 1a0a559..9fec81b 100644 --- a/sample/project/project.json +++ b/sample/project/project.json @@ -2,13 +2,13 @@ "c:format": "Collett Project", "c:meta": { "m:created": "2021-12-14T22:24:25", - "m:updated": "2022-02-14T20:07:35" + "m:updated": "2022-02-18T22:20:53" }, "c:project": { "s:last-doc-main": "7e5a1a98-d1a3-44a1-ab4e-2b5d21d92201", "u:models": [ - "characters", - "story" + "story", + "characters" ], "u:project-name": "Sample Project" }, diff --git a/src/core/data.cpp b/src/core/data.cpp index 642b15a..df81df9 100644 --- a/src/core/data.cpp +++ b/src/core/data.cpp @@ -101,12 +101,4 @@ Project *CollettData::project() { } } -ItemModel *CollettData::storyModel() { - if (hasProject()) { - return m_project.data()->model("story"); - } else { - return nullptr; - } -} - } // namespace Collett diff --git a/src/core/data.h b/src/core/data.h index 71824d4..3580cc3 100644 --- a/src/core/data.h +++ b/src/core/data.h @@ -53,7 +53,6 @@ class CollettData : public QObject bool hasProject() const; Project *project(); - ItemModel *storyModel(); private: static CollettData *staticInstance; diff --git a/src/editor/doceditor.cpp b/src/editor/doceditor.cpp index 01341b9..f757cca 100644 --- a/src/editor/doceditor.cpp +++ b/src/editor/doceditor.cpp @@ -19,11 +19,12 @@ ** along with this program. If not, see . */ +#include "item.h" #include "collett.h" +#include "document.h" #include "settings.h" -#include "doceditor.h" #include "textedit.h" -#include "document.h" +#include "doceditor.h" #include "edittoolbar.h" #include @@ -43,7 +44,7 @@ GuiDocEditor::GuiDocEditor(QWidget *parent) : QWidget(parent) { m_data = CollettData::instance(); - m_docUuid = QUuid(); + m_item = nullptr; m_document = nullptr; m_textArea = new GuiTextEdit(this); @@ -124,15 +125,15 @@ GuiDocEditor::GuiDocEditor(QWidget *parent) * ============ */ -bool GuiDocEditor::openDocument(const QUuid &uuid) { +bool GuiDocEditor::openDocument(Item *item) { - if (!m_data->hasProject()) { - qWarning() << "No project loaded"; + if (!m_data->hasProject() || !item) { + qWarning() << "Nothing to load"; return false; } - m_docUuid = uuid; - m_document = m_data->project()->document(uuid); + m_item = item; + m_document = m_data->project()->document(m_item->handle()); m_textArea->setJsonContent(m_document->content()); m_autoSave->start(); @@ -167,8 +168,8 @@ void GuiDocEditor::closeDocument() { m_autoSave->stop(); m_textArea->setReadOnly(true); m_textArea->clear(); - m_docUuid = QUuid(); m_document = nullptr; + m_item = nullptr; } /** @@ -177,11 +178,15 @@ void GuiDocEditor::closeDocument() { */ QUuid GuiDocEditor::currentDocument() const { - return m_docUuid; + if (m_item) { + return m_item->handle(); + } else { + return QUuid(); + } } bool GuiDocEditor::hasDocument() const { - return m_document != nullptr && !m_docUuid.isNull(); + return m_document != nullptr && m_item != nullptr; } /**! diff --git a/src/editor/doceditor.h b/src/editor/doceditor.h index c3daea7..cd250fe 100644 --- a/src/editor/doceditor.h +++ b/src/editor/doceditor.h @@ -22,10 +22,11 @@ #ifndef GUI_DOCEDITOR_H #define GUI_DOCEDITOR_H -#include "collett.h" #include "data.h" -#include "textedit.h" +#include "item.h" +#include "collett.h" #include "document.h" +#include "textedit.h" #include "edittoolbar.h" #include @@ -47,7 +48,7 @@ class GuiDocEditor : public QWidget // Data Methods - bool openDocument(const QUuid &uuid); + bool openDocument(Item *item); bool saveDocument(); void closeDocument(); @@ -61,13 +62,13 @@ class GuiDocEditor : public QWidget void popMessage(const Collett::Severity type, const QString &message); private: - GuiTextEdit *m_textArea; + GuiTextEdit *m_textArea; GuiEditToolBar *m_editToolBar; - QTimer *m_autoSave; + QTimer *m_autoSave; CollettData *m_data; - Document *m_document; - QUuid m_docUuid; + Item *m_item; + Document *m_document; private slots: void editorCharFormatChanged(const QTextCharFormat &fmt); diff --git a/src/gui/storytree.cpp b/src/gui/itemtree.cpp similarity index 58% rename from src/gui/storytree.cpp rename to src/gui/itemtree.cpp index 6760380..1b019a7 100644 --- a/src/gui/storytree.cpp +++ b/src/gui/itemtree.cpp @@ -1,6 +1,6 @@ /* -** Collett – GUI Story Tree Class -** ============================== +** Collett – GUI Item Tree Class +** ============================= ** ** This file is a part of Collett ** Copyright 2020–2022, Veronica Berglyd Olsen @@ -21,9 +21,9 @@ #include "collett.h" #include "item.h" -#include "storytree.h" +#include "itemtree.h" #include "itemmodel.h" -#include "storytreedelegate.h" +#include "itemtreedelegate.h" #include #include @@ -47,10 +47,10 @@ namespace Collett { * * @param parent the parent widget. */ -GuiStoryTree::GuiStoryTree(QWidget *parent) +GuiItemTree::GuiItemTree(QWidget *parent) : QTreeView(parent) { - this->setItemDelegate(new GuiStoryTreeDelegate(this)); + this->setItemDelegate(new GuiItemTreeDelegate(this)); this->setHeaderHidden(true); this->setAlternatingRowColors(true); this->setExpandsOnDoubleClick(false); @@ -70,7 +70,7 @@ GuiStoryTree::GuiStoryTree(QWidget *parent) * ============= */ -void GuiStoryTree::setTreeModel(ItemModel *model) { +void GuiItemTree::setTreeModel(ItemModel *model) { m_model = model; this->setModel(m_model); @@ -90,7 +90,7 @@ void GuiStoryTree::setTreeModel(ItemModel *model) { * ============= */ -QModelIndex GuiStoryTree::firstSelectedIndex() { +QModelIndex GuiItemTree::firstSelectedIndex() { QModelIndexList selections = this->selectedIndexes(); if (!selections.isEmpty()) { return selections.at(0); @@ -99,7 +99,7 @@ QModelIndex GuiStoryTree::firstSelectedIndex() { } } -void GuiStoryTree::getAllChildren(const QModelIndex &index, QModelIndexList &children) { +void GuiItemTree::getAllChildren(const QModelIndex &index, QModelIndexList &children) { children.append(index); for (int i = 0; i < model()->rowCount(index); ++i) { getAllChildren(model()->index(i, 0, index), children); @@ -119,14 +119,16 @@ void GuiStoryTree::getAllChildren(const QModelIndex &index, QModelIndexList &chi * * @param pos the position of the cursor. */ -void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { +void GuiItemTree::doOpenContextMenu(const QPoint &pos) { QModelIndex index = this->indexAt(pos); Item *item; + bool root = false; if (index.isValid()) { item = static_cast(index.internalPointer()); } else { item = m_model->rootItem(); + root = true; } if (!item) { return; @@ -139,8 +141,8 @@ void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { // Item Options // ------------ - contextMenu.addAction(m_editItem); - contextMenu.addSeparator(); + // contextMenu.addAction(m_editItem); + // contextMenu.addSeparator(); // New Items // --------- @@ -148,13 +150,13 @@ void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { QMenu *scMenu = new QMenu(tr("Add Scene")); if (item->allowedChild(Item::Scene)) { QAction *inAction = scMenu->addAction(tr("Inside")); - connect(inAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Scene, ItemModel::Inside);}); + connect(inAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Scene, ItemModel::Inside);}); } if (item->allowedSibling(Item::Scene)) { QAction *bfAction = scMenu->addAction(tr("Before")); - connect(bfAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Scene, ItemModel::Before);}); + connect(bfAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Scene, ItemModel::Before);}); QAction *afAction = scMenu->addAction(tr("After")); - connect(afAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Scene, ItemModel::After);}); + connect(afAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Scene, ItemModel::After);}); } if (!scMenu->isEmpty()) { contextMenu.addMenu(scMenu); @@ -163,13 +165,13 @@ void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { QMenu *chMenu = new QMenu(tr("Add Chapter")); if (item->allowedChild(Item::Chapter)) { QAction *inAction = chMenu->addAction(tr("Inside")); - connect(inAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Chapter, ItemModel::Inside);}); + connect(inAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Chapter, ItemModel::Inside);}); } if (item->allowedSibling(Item::Chapter)) { QAction *bfAction = chMenu->addAction(tr("Before")); - connect(bfAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Chapter, ItemModel::Before);}); + connect(bfAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Chapter, ItemModel::Before);}); QAction *afAction = chMenu->addAction(tr("After")); - connect(afAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Chapter, ItemModel::After);}); + connect(afAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Chapter, ItemModel::After);}); } if (!chMenu->isEmpty()) { contextMenu.addMenu(chMenu); @@ -178,13 +180,13 @@ void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { QMenu *ptMenu = new QMenu(tr("Add Partition")); if (item->allowedChild(Item::Partition)) { QAction *inAction = ptMenu->addAction(tr("Inside")); - connect(inAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Partition, ItemModel::Inside);}); + connect(inAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Partition, ItemModel::Inside);}); } if (item->allowedSibling(Item::Partition)) { QAction *bfAction = ptMenu->addAction(tr("Before")); - connect(bfAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Partition, ItemModel::Before);}); + connect(bfAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Partition, ItemModel::Before);}); QAction *afAction = ptMenu->addAction(tr("After")); - connect(afAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Partition, ItemModel::After);}); + connect(afAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Partition, ItemModel::After);}); } if (!ptMenu->isEmpty()) { contextMenu.addMenu(ptMenu); @@ -192,14 +194,14 @@ void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { QMenu *bkMenu = new QMenu(tr("Add Book")); if (item->allowedChild(Item::Book)) { - QAction *inAction = bkMenu->addAction(index.isValid() ? tr("Inside") : tr("Here")); - connect(inAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Book, ItemModel::Inside);}); + QAction *inAction = bkMenu->addAction(root ? tr("Here") : tr("Inside")); + connect(inAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Book, ItemModel::Inside);}); } if (item->allowedSibling(Item::Book)) { QAction *bfAction = bkMenu->addAction(tr("Before")); - connect(bfAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Book, ItemModel::Before);}); + connect(bfAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Book, ItemModel::Before);}); QAction *afAction = bkMenu->addAction(tr("After")); - connect(afAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Book, ItemModel::After);}); + connect(afAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Book, ItemModel::After);}); } if (!bkMenu->isEmpty()) { contextMenu.addMenu(bkMenu); @@ -208,18 +210,48 @@ void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { QMenu *pgMenu = new QMenu(tr("Add Page")); if (item->allowedChild(Item::Page)) { QAction *inAction = pgMenu->addAction(tr("Inside")); - connect(inAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Page, ItemModel::Inside);}); + connect(inAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Page, ItemModel::Inside);}); } if (item->allowedSibling(Item::Page)) { QAction *bfAction = pgMenu->addAction(tr("Before")); - connect(bfAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Page, ItemModel::Before);}); + connect(bfAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Page, ItemModel::Before);}); QAction *afAction = pgMenu->addAction(tr("After")); - connect(afAction, &QAction::triggered, [this, item]{doAddChild(item, Item::Page, ItemModel::After);}); + connect(afAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Page, ItemModel::After);}); } if (!pgMenu->isEmpty()) { contextMenu.addMenu(pgMenu); } + QMenu *fdMenu = new QMenu(tr("Add Group")); + if (item->allowedChild(Item::Group)) { + QAction *inAction = fdMenu->addAction(root ? tr("Here") : tr("Inside")); + connect(inAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Group, ItemModel::Inside);}); + } + if (item->allowedSibling(Item::Group)) { + QAction *bfAction = fdMenu->addAction(tr("Before")); + connect(bfAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Group, ItemModel::Before);}); + QAction *afAction = fdMenu->addAction(tr("After")); + connect(afAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Group, ItemModel::After);}); + } + if (!fdMenu->isEmpty()) { + contextMenu.addMenu(fdMenu); + } + + QMenu *ntMenu = new QMenu(tr("Add Note")); + if (item->allowedChild(Item::Note)) { + QAction *inAction = ntMenu->addAction(root ? tr("Here") : tr("Inside")); + connect(inAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Note, ItemModel::Inside);}); + } + if (item->allowedSibling(Item::Note)) { + QAction *bfAction = ntMenu->addAction(tr("Before")); + connect(bfAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Note, ItemModel::Before);}); + QAction *afAction = ntMenu->addAction(tr("After")); + connect(afAction, &QAction::triggered, [this, index]{doAddChild(index, Item::Note, ItemModel::After);}); + } + if (!ntMenu->isEmpty()) { + contextMenu.addMenu(ntMenu); + } + contextMenu.exec(QWidget::mapToGlobal(pos)); } @@ -228,7 +260,7 @@ void GuiStoryTree::doOpenContextMenu(const QPoint &pos) { * * @param bool unused. */ -void GuiStoryTree::doEditName(bool checked) { +void GuiItemTree::doEditName(bool checked) { Q_UNUSED(checked); QModelIndex index = this->firstSelectedIndex(); @@ -254,13 +286,13 @@ void GuiStoryTree::doEditName(bool checked) { * * The slot will forward the call to create a new story item to the model. * - * @param item the item to add a child relative to. - * @param type the type of item to add. - * @param loc the relative location of where to add the new item. + * @param index the item index to add a child relative to. + * @param type the type of item to add. + * @param loc the relative location of where to add the new item. */ -void GuiStoryTree::doAddChild(Item *item, Item::ItemType type, ItemModel::AddLocation loc) { +void GuiItemTree::doAddChild(const QModelIndex &index, Item::ItemType type, ItemModel::AddLocation loc) { if (m_model) { - if (m_model->addItem(item, type, loc)) { + if (m_model->addItem(index, type, loc)) { qDebug() << "Added" << Item::typeToLabel(type); } else { qWarning() << "Failed to add" << Item::typeToLabel(type); @@ -268,11 +300,11 @@ void GuiStoryTree::doAddChild(Item *item, Item::ItemType type, ItemModel::AddLoc } } -void GuiStoryTree::saveExpanded(const QModelIndex &index) { +void GuiItemTree::saveExpanded(const QModelIndex &index) { if (m_model) m_model->setExpanded(index, true); } -void GuiStoryTree::saveCollapsed(const QModelIndex &index) { +void GuiItemTree::saveCollapsed(const QModelIndex &index) { if (m_model) m_model->setExpanded(index, false); } diff --git a/src/gui/storytree.h b/src/gui/itemtree.h similarity index 81% rename from src/gui/storytree.h rename to src/gui/itemtree.h index bc911e0..7b9be93 100644 --- a/src/gui/storytree.h +++ b/src/gui/itemtree.h @@ -1,6 +1,6 @@ /* -** Collett – GUI Story Tree Class -** ============================== +** Collett – GUI Item Tree Class +** ============================= ** ** This file is a part of Collett ** Copyright 2020–2022, Veronica Berglyd Olsen @@ -19,8 +19,8 @@ ** along with this program. If not, see . */ -#ifndef GUI_STORYTREE_H -#define GUI_STORYTREE_H +#ifndef GUI_ITEMTREE_H +#define GUI_ITEMTREE_H #include "item.h" #include "itemmodel.h" @@ -34,15 +34,15 @@ namespace Collett { -class GuiStoryTree : public QTreeView +class GuiItemTree : public QTreeView { Q_OBJECT public: enum AddLocation{Before, After, Inside}; - GuiStoryTree(QWidget *parent=nullptr); - ~GuiStoryTree() {}; + GuiItemTree(QWidget *parent=nullptr); + ~GuiItemTree() {}; // Class Setters @@ -58,7 +58,7 @@ public slots: private slots: void doOpenContextMenu(const QPoint &pos); - void doAddChild(Item *item, Item::ItemType type, ItemModel::AddLocation loc); + void doAddChild(const QModelIndex &index, Item::ItemType type, ItemModel::AddLocation loc); void saveExpanded(const QModelIndex &index); void saveCollapsed(const QModelIndex &index); @@ -70,4 +70,4 @@ private slots: }; } // namespace Collett -#endif // GUI_STORYTREE_H +#endif // GUI_ITEMTREE_H diff --git a/src/gui/storytreedelegate.cpp b/src/gui/itemtreedelegate.cpp similarity index 85% rename from src/gui/storytreedelegate.cpp rename to src/gui/itemtreedelegate.cpp index 45fb73f..460180c 100644 --- a/src/gui/storytreedelegate.cpp +++ b/src/gui/itemtreedelegate.cpp @@ -1,6 +1,6 @@ /* -** Collett – GUI Story Tree Delegate Class -** ======================================= +** Collett – GUI Item Tree Delegate Class +** ====================================== ** ** This file is a part of Collett ** Copyright 2020–2022, Veronica Berglyd Olsen @@ -19,7 +19,7 @@ ** along with this program. If not, see . */ -#include "storytreedelegate.h" +#include "itemtreedelegate.h" #include #include @@ -36,7 +36,7 @@ namespace Collett { -GuiStoryTreeDelegate::GuiStoryTreeDelegate(QWidget *parent) +GuiItemTreeDelegate::GuiItemTreeDelegate(QWidget *parent) : QAbstractItemDelegate(parent) { m_headFont = qApp->font(); @@ -46,7 +46,7 @@ GuiStoryTreeDelegate::GuiStoryTreeDelegate(QWidget *parent) m_mainHeight = QFontMetrics(m_mainFont).height(); } -void GuiStoryTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { +void GuiItemTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QVariantList data = index.data(Qt::DisplayRole).toList(); Q_ASSERT(data.size() == 3); @@ -82,7 +82,7 @@ void GuiStoryTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem & painter->restore(); } -QSize GuiStoryTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { +QSize GuiItemTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const { return QSize(option.rect.width(), m_headHeight + m_mainHeight + 4); } diff --git a/src/gui/storytreedelegate.h b/src/gui/itemtreedelegate.h similarity index 79% rename from src/gui/storytreedelegate.h rename to src/gui/itemtreedelegate.h index a28fb8e..ae0ca00 100644 --- a/src/gui/storytreedelegate.h +++ b/src/gui/itemtreedelegate.h @@ -1,6 +1,6 @@ /* -** Collett – GUI Story Tree Delegate Class -** ======================================= +** Collett – GUI Item Tree Delegate Class +** ====================================== ** ** This file is a part of Collett ** Copyright 2020–2022, Veronica Berglyd Olsen @@ -19,8 +19,8 @@ ** along with this program. If not, see . */ -#ifndef GUI_STORYTREEDELEGATE_H -#define GUI_STORYTREEDELEGATE_H +#ifndef GUI_ITEMTREEDELEGATE_H +#define GUI_ITEMTREEDELEGATE_H #include #include @@ -32,13 +32,13 @@ namespace Collett { -class GuiStoryTreeDelegate : public QAbstractItemDelegate +class GuiItemTreeDelegate : public QAbstractItemDelegate { Q_OBJECT public: - GuiStoryTreeDelegate(QWidget *parent=nullptr); - ~GuiStoryTreeDelegate() {}; + GuiItemTreeDelegate(QWidget *parent=nullptr); + ~GuiItemTreeDelegate() {}; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override; @@ -52,4 +52,4 @@ class GuiStoryTreeDelegate : public QAbstractItemDelegate }; } // namespace Collett -#endif // GUI_STORYTREEDELEGATE_H +#endif // GUI_ITEMTREEDELEGATE_H diff --git a/src/gui/maintoolbar.cpp b/src/gui/maintoolbar.cpp index d089a73..243846f 100644 --- a/src/gui/maintoolbar.cpp +++ b/src/gui/maintoolbar.cpp @@ -32,9 +32,8 @@ namespace Collett { -GuiMainToolBar::GuiMainToolBar(QWidget *parent) - : QToolBar(parent) -{ +GuiMainToolBar::GuiMainToolBar(QWidget *parent) : QToolBar(parent) { + QWidget *stretch1 = new QWidget(); QWidget *stretch2 = new QWidget(); stretch1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); @@ -75,6 +74,9 @@ void GuiMainToolBar::buildMainMenu() { m_saveProject = m_projectMenu->addAction(tr("Save Project")); m_saveProject->setShortcut(QKeySequence("Ctrl+Shift+S")); + m_closeProject = m_projectMenu->addAction(tr("Close Project")); + m_closeProject->setShortcut(QKeySequence("Ctrl+Shift+W")); + m_projectButton = new QToolButton(this); m_projectButton->setText(tr("Project")); m_projectButton->setIcon(icons->icon("projectMenu")); diff --git a/src/gui/maintoolbar.h b/src/gui/maintoolbar.h index 44a3d97..e80df9a 100644 --- a/src/gui/maintoolbar.h +++ b/src/gui/maintoolbar.h @@ -52,6 +52,7 @@ class GuiMainToolBar : public QToolBar QAction *m_newProject; QAction *m_openProject; QAction *m_saveProject; + QAction *m_closeProject; // Documents Menu QToolButton *m_docsButton; diff --git a/src/gui/treetoolbar.cpp b/src/gui/treetoolbar.cpp index fb0a7bf..f1217fa 100644 --- a/src/gui/treetoolbar.cpp +++ b/src/gui/treetoolbar.cpp @@ -1,6 +1,6 @@ /* -** Collett – GUI Tree Tool Bar Class -** ================================= +** Collett – GUI Tree ToolBar Class +** ================================ ** ** This file is a part of Collett ** Copyright 2020–2022, Veronica Berglyd Olsen @@ -20,27 +20,84 @@ */ #include "treetoolbar.h" +#include "itemtree.h" +#include "itemmodel.h" #include "icons.h" +#include +#include #include +#include #include #include #include +#include namespace Collett { -GuiTreeToolBar::GuiTreeToolBar(QWidget *parent) - : QToolBar(parent) -{ +GuiTreeToolBar::GuiTreeToolBar(QWidget *parent) : QToolBar(parent) { + this->initToolBar(); +} + +/** + * Class Methods + * ============= + */ + +void GuiTreeToolBar::clearModels() { + m_modelButtons.clear(); + m_modelTree.clear(); + this->clear(); + this->initToolBar(); +} + +void GuiTreeToolBar::addModelEntry(const QString &name, ItemModel *model, GuiItemTree *tree) { + + CollettIcons *icons = CollettIcons::instance(); + + QToolButton *button = new QToolButton(this); + button->setIcon(icons->icon(model->modelIcon())); + button->setToolTip(model->modelName()); + this->insertWidget(m_addAction, button); + + m_modelButtons.insert(name, button); + m_modelTree.insert(name, tree); + + connect(button, &QToolButton::clicked, [this, name]{treeButtonTriggered(name);}); +} + +/** + * Internal Functions + * ================== + */ + +void GuiTreeToolBar::initToolBar() { QWidget *stretch = new QWidget(this); stretch->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); CollettIcons *icons = CollettIcons::instance(); + m_addAction = new QAction(this); + m_addAction->setText(tr("Add Action")); + m_addAction->setIcon(icons->icon("add")); + this->setOrientation(Qt::Vertical); - this->addAction(icons->icon("storyModel"), tr("Story")); + this->addAction(m_addAction); this->addWidget(stretch); this->addAction(icons->icon("settings"), tr("Settings")); } +/** + * Private Slots + * ============= + */ + +void GuiTreeToolBar::treeButtonTriggered(const QString &name) { + qDebug() << "Clicked tree button:" << name; + if (m_modelTree.contains(name)) { + emit treeButtonClicked(m_modelTree.value(name)); + } else { + } +} + } // namespace Collett diff --git a/src/gui/treetoolbar.h b/src/gui/treetoolbar.h index 4310c2e..4980d8a 100644 --- a/src/gui/treetoolbar.h +++ b/src/gui/treetoolbar.h @@ -1,6 +1,6 @@ /* -** Collett – GUI Tree Tool Bar Class -** ================================= +** Collett – GUI Tree ToolBar Class +** ================================ ** ** This file is a part of Collett ** Copyright 2020–2022, Veronica Berglyd Olsen @@ -22,8 +22,15 @@ #ifndef GUI_TREETOOLBAR_H #define GUI_TREETOOLBAR_H +#include "itemtree.h" +#include "itemmodel.h" + +#include +#include #include +#include #include +#include namespace Collett { @@ -35,6 +42,24 @@ class GuiTreeToolBar : public QToolBar GuiTreeToolBar(QWidget *parent=nullptr); ~GuiTreeToolBar() {}; + // Class Methods + + void clearModels(); + void addModelEntry(const QString &name, ItemModel *model, GuiItemTree *tree); + +signals: + void treeButtonClicked(GuiItemTree *tree); + +private: + QAction *m_addAction; + QHash m_modelButtons; + QHash m_modelTree; + + void initToolBar(); + +private slots: + void treeButtonTriggered(const QString &name); + }; } // namespace Collett diff --git a/src/guimain.cpp b/src/guimain.cpp index 03b3e66..a312639 100644 --- a/src/guimain.cpp +++ b/src/guimain.cpp @@ -20,13 +20,14 @@ */ #include "data.h" -#include "doceditor.h" -#include "guimain.h" +#include "item.h" #include "icons.h" -#include "maintoolbar.h" +#include "guimain.h" +#include "itemtree.h" #include "settings.h" +#include "doceditor.h" #include "statusbar.h" -#include "storytree.h" +#include "maintoolbar.h" #include "treetoolbar.h" #include @@ -34,8 +35,10 @@ #include #include #include +#include #include #include +#include #include namespace Collett { @@ -48,7 +51,7 @@ GuiMain::GuiMain(QWidget *parent) : QMainWindow(parent) { // Collett Widgets m_mainToolBar = new GuiMainToolBar(this); m_treeToolBar = new GuiTreeToolBar(this); - m_storyTree = new GuiStoryTree(this); + m_treeStack = new QStackedWidget(this); m_mainStatus = new GuiMainStatus(this); m_docEditor = new GuiDocEditor(this); @@ -56,7 +59,7 @@ GuiMain::GuiMain(QWidget *parent) : QMainWindow(parent) { m_splitMain = new QSplitter(Qt::Horizontal, this); m_splitMain->setContentsMargins(0, 0, 0, 0); m_splitMain->setOpaqueResize(false); - m_splitMain->addWidget(m_storyTree); + m_splitMain->addWidget(m_treeStack); m_splitMain->addWidget(m_docEditor); this->addToolBar(Qt::TopToolBarArea, m_mainToolBar); @@ -70,15 +73,16 @@ GuiMain::GuiMain(QWidget *parent) : QMainWindow(parent) { m_splitMain->setSizes(mainConf->mainSplitSizes()); // Connect Signals and Slots - connect(m_storyTree, SIGNAL(doubleClicked(const QModelIndex&)), - this, SLOT(storyTreeDoubleClick(const QModelIndex&))); - + connect(m_mainToolBar->m_closeProject, SIGNAL(triggered()), + this, SLOT(closeProjectRequest())); connect(m_mainToolBar->m_openDocument, SIGNAL(triggered()), this, SLOT(openSelectedDocument())); connect(m_mainToolBar->m_saveDocument, SIGNAL(triggered()), - this, SLOT(saveOpenDocument())); + this, SLOT(saveCurrentDocument())); connect(m_mainToolBar->m_renameDocument, SIGNAL(triggered()), this, SLOT(renameDocument())); + connect(m_treeToolBar, SIGNAL(treeButtonClicked(GuiItemTree*)), + this, SLOT(changeModelTree(GuiItemTree*))); // Connect Actions to Capture Key Sequence this->addAction(m_mainToolBar->m_newProject); @@ -99,6 +103,7 @@ GuiMain::GuiMain(QWidget *parent) : QMainWindow(parent) { GuiMain::~GuiMain() { qDebug() << "Destructor: GuiMain"; + qDeleteAll(m_itemTrees.begin(), m_itemTrees.end()); delete m_data; } @@ -114,13 +119,22 @@ void GuiMain::openProject(const QString &path) { return; } - QItemSelectionModel *m = m_storyTree->selectionModel(); - m_storyTree->setTreeModel(m_data->storyModel()); - delete m; + for (const QString &name : m_data->project()->modelList()) { + qDebug() << "Adding tree:" << name; + this->addItemTree(name); + } + if (m_itemTrees.contains("story")) { + this->m_treeStack->setCurrentWidget(m_itemTrees.value("story")); + } QUuid lastDocMain = m_data->project()->lastDocumentMain(); if (!lastDocMain.isNull()) { - this->openDocument(lastDocMain); + Item *itemMain = nullptr; + for (const GuiItemTree *tree : m_itemTrees) { + itemMain = static_cast(tree->model())->itemFromHandle(lastDocMain); + if (itemMain) break; + } + this->openDocument(itemMain); } m_mainToolBar->setProjectName(m_data->project()->projectName()); @@ -132,6 +146,9 @@ bool GuiMain::saveProject() { } bool GuiMain::closeProject() { + this->closeDocument(); + m_treeToolBar->clearModels(); + m_data->closeProject(); return true; }; @@ -140,9 +157,12 @@ bool GuiMain::closeProject() { * ================ */ -void GuiMain::openDocument(const QUuid &uuid) { +void GuiMain::openDocument(Item *item) { - if (!m_data->hasProject()) { + if (!m_data->hasProject() || !item) { + return; + } + if (!item->canHoldDocument()) { return; } @@ -150,7 +170,7 @@ void GuiMain::openDocument(const QUuid &uuid) { m_docEditor->saveDocument(); m_docEditor->closeDocument(); } - m_docEditor->openDocument(uuid); + m_docEditor->openDocument(item); m_data->project()->setLastDocumentMain(m_docEditor->currentDocument()); } @@ -173,6 +193,33 @@ void GuiMain::closeDocument() { * =========== */ +void GuiMain::addItemTree(const QString &name) { + + if (!m_data->hasProject()) { + return; + } + if (m_itemTrees.contains(name)) { + qWarning() << "ItemTree already exists:" << name; + return; + } + + ItemModel *model = m_data->project()->model(name); + if (!model) { + qWarning() << "Model does not exist:" << name; + return; + } + + GuiItemTree *tree = new GuiItemTree(this); + m_itemTrees.insert(name, tree); + + tree->setTreeModel(model); + m_treeStack->addWidget(tree); + m_treeToolBar->addModelEntry(name, model, tree); + + connect(tree, SIGNAL(doubleClicked(const QModelIndex&)), + this, SLOT(itemTreeDoubleClick(const QModelIndex&))); +} + bool GuiMain::closeMain() { m_docEditor->saveDocument(); @@ -211,18 +258,29 @@ void GuiMain::closeEvent(QCloseEvent *event) { * ============= */ +void GuiMain::closeProjectRequest() { + auto response = QMessageBox::question( + this, tr("Question"), tr("Do you want to close the project?") + ); + if (response == QMessageBox::Yes) { + this->closeProject(); + } +} + void GuiMain::openSelectedDocument() { if (!m_data->hasProject()) return; - QModelIndex index = m_storyTree->firstSelectedIndex(); + GuiItemTree *tree = static_cast(m_treeStack->currentWidget()); + QModelIndex index = tree->firstSelectedIndex(); if (!index.isValid()) return; - this->openDocument(m_data->storyModel()->itemHandle(index)); + ItemModel *model = static_cast(tree->model()); + this->openDocument(model->itemFromIndex(index)); } -void GuiMain::saveOpenDocument() { +void GuiMain::saveCurrentDocument() { if (!m_data->hasProject()) return; if (m_docEditor->anyFocus()) @@ -232,14 +290,21 @@ void GuiMain::saveOpenDocument() { void GuiMain::renameDocument() { if (!m_data->hasProject()) return; - m_storyTree->doEditName(false); + + GuiItemTree *tree = static_cast(m_treeStack->currentWidget()); + tree->doEditName(false); } -void GuiMain::storyTreeDoubleClick(const QModelIndex &index) { - if (!m_data->hasProject() || !index.isValid()) { +void GuiMain::itemTreeDoubleClick(const QModelIndex &index) { + Q_UNUSED(index); + this->openSelectedDocument(); +} + +void GuiMain::changeModelTree(GuiItemTree *tree) { + if (!m_data->hasProject()) return; - } - this->openDocument(m_data->storyModel()->itemHandle(index)); + + m_treeStack->setCurrentWidget(static_cast(tree)); } } // namespace Collett diff --git a/src/guimain.h b/src/guimain.h index 2274d42..0175fa9 100644 --- a/src/guimain.h +++ b/src/guimain.h @@ -22,14 +22,16 @@ #ifndef GUI_MAIN_H #define GUI_MAIN_H -#include "collett.h" #include "data.h" +#include "item.h" +#include "collett.h" +#include "itemtree.h" +#include "doceditor.h" +#include "statusbar.h" #include "maintoolbar.h" #include "treetoolbar.h" -#include "statusbar.h" -#include "storytree.h" -#include "doceditor.h" +#include #include #include #include @@ -37,6 +39,7 @@ #include #include #include +#include namespace Collett { @@ -56,12 +59,13 @@ class GuiMain : public QMainWindow // Document Methods - void openDocument(const QUuid &uuid); + void openDocument(Item *item); void saveDocument(); void closeDocument(); // GUI Methods + void addItemTree(const QString &name); bool closeMain(); private: @@ -70,10 +74,12 @@ class GuiMain : public QMainWindow // Collett Widgets GuiMainToolBar *m_mainToolBar; GuiTreeToolBar *m_treeToolBar; - GuiStoryTree *m_storyTree; + QStackedWidget *m_treeStack; GuiDocEditor *m_docEditor; GuiMainStatus *m_mainStatus; + QHash m_itemTrees; + // GUI Widgets QSplitter *m_splitMain; @@ -82,10 +88,12 @@ class GuiMain : public QMainWindow private slots: + void closeProjectRequest(); void openSelectedDocument(); - void saveOpenDocument(); + void saveCurrentDocument(); void renameDocument(); - void storyTreeDoubleClick(const QModelIndex &index); + void itemTreeDoubleClick(const QModelIndex &index); + void changeModelTree(GuiItemTree *tree); }; } // namespace Collett diff --git a/src/project/item.cpp b/src/project/item.cpp index 76aa4ca..cff200d 100644 --- a/src/project/item.cpp +++ b/src/project/item.cpp @@ -193,10 +193,6 @@ QJsonObject Item::toJsonObject() { type = QLatin1String("ROOT"); expandable = false; break; - case Item::Folder: - type = QLatin1String("FOLDER"); - expandable = true; - break; case Item::Book: type = QLatin1String("BOOK"); expandable = true; @@ -217,6 +213,10 @@ QJsonObject Item::toJsonObject() { type = QLatin1String("PAGE"); expandable = false; break; + case Item::Group: + type = QLatin1String("GROUP"); + expandable = true; + break; case Item::Note: type = QLatin1String("NOTE"); expandable = false; @@ -278,9 +278,9 @@ bool Item::allowedChild(Item::ItemType type) const { } else { switch (m_type) { case Item::Root: - return type == Item::Folder || type == Item::Note; + return type == Item::Group || type == Item::Note; break; - case Item::Folder: + case Item::Group: return type == Item::Note; break; default: @@ -306,6 +306,16 @@ bool Item::allowedSibling(Item::ItemType type) const { } } +bool Item::canHoldDocument() const { + switch (m_type) { + case ItemType::Chapter: return true; + case ItemType::Scene: return true; + case ItemType::Page: return true; + case ItemType::Note: return true; + default: return false; + } +} + /** * Class Setters * ============= @@ -357,6 +367,18 @@ bool Item::isExpanded() const { return m_expanded; } +Item *Item::findItemFromHandle(const QUuid &uuid) const { + if (m_handle == uuid) { + return const_cast(this); + } else { + for (Item* child : m_childItems) { + Item *item = child->findItemFromHandle(uuid); + if (item) return item; + } + } + return nullptr; +} + /** * Static Methods * ============== @@ -375,12 +397,12 @@ QString Item::typeToLabel(ItemType type) { QString name = ""; switch (type) { case Item::Root: name = ""; break; - case Item::Folder: name = tr("Folder"); break; case Item::Book: name = tr("Book"); break; case Item::Partition: name = tr("Partition"); break; case Item::Chapter: name = tr("Chapter"); break; case Item::Scene: name = tr("Scene"); break; case Item::Page: name = tr("Page"); break; + case Item::Group: name = tr("Group"); break; case Item::Note: name = tr("Note"); break; case Item::Invalid: name = ""; break; } @@ -391,8 +413,6 @@ Item::ItemType Item::typeFromString(const QString &value) { QString upper = value.toUpper(); if (upper == "ROOT") { return Item::Root; - } else if (upper == "FOLDER") { - return Item::Folder; } else if (upper == "BOOK") { return Item::Book; } else if (upper == "PARTITION") { @@ -403,6 +423,8 @@ Item::ItemType Item::typeFromString(const QString &value) { return Item::Scene; } else if (upper == "PAGE") { return Item::Page; + } else if (upper == "GROUP") { + return Item::Group; } else if (upper == "NOTE") { return Item::Note; } else { diff --git a/src/project/item.h b/src/project/item.h index 539e5fb..cbe0ed4 100644 --- a/src/project/item.h +++ b/src/project/item.h @@ -34,7 +34,11 @@ class Item : public QObject Q_OBJECT public: - enum ItemType{Invalid, Root, Folder, Book, Partition, Chapter, Scene, Page, Note}; + enum ItemType{ + Invalid, + Root, Book, Partition, Chapter, Scene, Page, + Group, Note + }; explicit Item(const QUuid &uuid, const QString &name, bool story, ItemType type, Item *parentItem=nullptr); ~Item(); @@ -46,6 +50,7 @@ class Item : public QObject QJsonObject toJsonObject(); bool allowedChild(ItemType type) const; bool allowedSibling(ItemType type) const; + bool canHoldDocument() const; // Class Setters @@ -61,6 +66,7 @@ class Item : public QObject int wordCount() const; int childWordCounts() const; bool isExpanded() const; + Item *findItemFromHandle(const QUuid &uuid) const; // Static Methods diff --git a/src/project/itemmodel.cpp b/src/project/itemmodel.cpp index 3aba8eb..d64cd6d 100644 --- a/src/project/itemmodel.cpp +++ b/src/project/itemmodel.cpp @@ -167,24 +167,46 @@ bool ItemModel::fromJsonObject(const QJsonObject &json) { * @param loc the relative location of where to add the new item. * @return true if the item was successfully added, otherwise false. */ -bool ItemModel::addItem(Item *relativeTo, Item::ItemType type, AddLocation loc) { - if (!relativeTo) { - return false; - } - Item *target = relativeTo; - int pos = relativeTo->childCount(); - if (loc == AddLocation::Before || loc == AddLocation::After) { - target = relativeTo->parentItem(); - pos = relativeTo->row() + (loc == AddLocation::After ? 1 : 0); - } - if (!target) { - return false; +bool ItemModel::addItem(const QModelIndex &relative, Item::ItemType type, AddLocation loc) { + + QString name = tr("New %1").arg(Item::typeToLabel(type)); + + if (!relative.isValid()) { + + int pos = m_rootItem->childCount(); + emit beginInsertRows(this->index(0, 0), pos, pos); + Item *item = m_rootItem->addChild(name, type, pos); + emit endInsertRows(); + qDebug() << "Added root item with name:" << name; + return item != nullptr; + + } else { + + QModelIndex index = relative; + + Item *target = itemFromIndex(relative); + if (!target) { + return false; + } + + int pos = target->childCount(); + if (loc == AddLocation::Before || loc == AddLocation::After) { + target = target->parentItem(); + index = this->parent(relative); + pos = target->row() + (loc == AddLocation::After ? 1 : 0); + } + if (!target) { + return false; + } + + emit beginInsertRows(index, pos, pos); + Item *item = target->addChild(name, type, pos); + emit endInsertRows(); + + qDebug() << "Added item with name:" << name; + return item != nullptr; + } - qDebug() << target->row() << pos; - emit beginInsertRows(createIndex(target->row(), 0, target), pos, pos); - Item *item = target->addChild(tr("New %1").arg(Item::typeToLabel(type)), type, pos); - emit endInsertRows(); - return item != nullptr; } /**! @@ -210,11 +232,23 @@ bool ItemModel::isValid() const { * ============= */ +QString ItemModel::modelName() const { + return m_name; +} + +QString ItemModel::modelIcon() const { + return m_icon; +} + +ItemModel::ModelType ItemModel::modelType() const { + return m_type; +} + Item *ItemModel::rootItem() const { return m_rootItem; } -Item *ItemModel::storyItem(const QModelIndex &index) { +Item *ItemModel::itemFromIndex(const QModelIndex &index) { if (index.isValid()) { return static_cast(index.internalPointer()); } else { @@ -222,12 +256,11 @@ Item *ItemModel::storyItem(const QModelIndex &index) { } } -QUuid ItemModel::itemHandle(const QModelIndex &index) { - if (index.isValid()) { - Item *item = static_cast(index.internalPointer()); - return item->handle(); +Item *ItemModel::itemFromHandle(const QUuid &uuid) { + if (!uuid.isNull()) { + return m_rootItem->findItemFromHandle(uuid); } else { - return QUuid(); + return nullptr; } } diff --git a/src/project/itemmodel.h b/src/project/itemmodel.h index e07befe..e17aca2 100644 --- a/src/project/itemmodel.h +++ b/src/project/itemmodel.h @@ -24,6 +24,7 @@ #include "item.h" +#include #include #include #include @@ -49,7 +50,7 @@ class ItemModel : public QAbstractItemModel QJsonObject toJsonObject(); bool fromJsonObject(const QJsonObject &json); - bool addItem(Item *relativeTo, Item::ItemType type, AddLocation loc); + bool addItem(const QModelIndex &relative, Item::ItemType type, AddLocation loc); bool isEmpty() const; bool isValid() const; @@ -57,10 +58,11 @@ class ItemModel : public QAbstractItemModel QString modelName() const; QString modelIcon() const; + ModelType modelType() const; Item *rootItem() const; - Item *storyItem(const QModelIndex &index); - QUuid itemHandle(const QModelIndex &index); + Item *itemFromIndex(const QModelIndex &index); + Item *itemFromHandle(const QUuid &uuid); QString itemName(const QModelIndex &index); bool isExpanded(const QModelIndex &index); diff --git a/src/project/project.cpp b/src/project/project.cpp index e4b88ca..ddb8fa5 100644 --- a/src/project/project.cpp +++ b/src/project/project.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -174,6 +175,10 @@ Storage *Project::store() { return m_store; } +QStringList Project::modelList() const { + return m_modelOrder; +} + ItemModel *Project::model(const QString &name) { if (m_models.contains(name)) { return m_models.value(name); @@ -228,6 +233,7 @@ bool Project::loadProjectStructure() { for (const QJsonValue &value : jProject.value(QLatin1String("u:models")).toArray()) { QString key = value.toString().simplified(); if (!key.isEmpty()) { + m_modelOrder << key; m_models.insert(key, new ItemModel(this)); QJsonObject mData; if (m_store->loadFile(key, mData)) { @@ -256,7 +262,7 @@ bool Project::saveProjectStructure() { jProject[QLatin1String("s:last-doc-main")] = m_lastDocMain.toString(QUuid::WithoutBraces); // Data Models - for (const QString &key : m_models.keys()) { + for (const QString &key : m_modelOrder) { jModels.append(key); qDebug() << "Saving model:" << key; if (!m_store->saveFile(key, m_models[key]->toJsonObject())) { diff --git a/src/project/project.h b/src/project/project.h index 335bdbe..446008f 100644 --- a/src/project/project.h +++ b/src/project/project.h @@ -32,6 +32,7 @@ #include #include #include +#include namespace Collett { @@ -62,6 +63,7 @@ class Project : public QObject QString projectName() const; Storage *store(); + QStringList modelList() const; ItemModel *model(const QString &name); Document *document(const QUuid &uuid); @@ -91,6 +93,7 @@ class Project : public QObject // Content + QStringList m_modelOrder; QHash m_models; QHash m_documents;