File indexing completed on 2024-05-19 05:42:28
0001 // ct_lvtqtw_tabwidget.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtprj_projectfile.h> 0021 0022 #include <ct_lvtqtc_graphicsscene.h> 0023 #include <ct_lvtqtc_graphicsview.h> 0024 #include <ct_lvtqtc_undo_manager.h> 0025 0026 #include <ct_lvtqtw_graphtabelement.h> 0027 #include <ct_lvtqtw_tabwidget.h> 0028 0029 #include <ct_lvtldr_nodestorage.h> 0030 0031 #include <ct_lvtclr_colormanagement.h> 0032 #include <ct_lvtmdl_modelhelpers.h> 0033 0034 #include <ct_lvtqtc_inputdialog.h> 0035 0036 #include <QDialog> 0037 #include <QJsonDocument> 0038 #include <QJsonObject> 0039 #include <QMenu> 0040 #include <QString> 0041 #include <QTabBar> 0042 #include <QToolButton> 0043 0044 #include <unordered_map> 0045 #include <utility> 0046 0047 using namespace Codethink::lvtldr; 0048 using namespace Codethink::lvtqtc; 0049 using namespace Codethink::lvtmdl; 0050 using namespace Codethink::lvtshr; 0051 using namespace Codethink::lvtprj; 0052 0053 namespace Codethink::lvtqtw { 0054 0055 struct TabWidget::Private { 0056 QToolButton *addGraphBtn = nullptr; 0057 std::shared_ptr<lvtclr::ColorManagement> colorManagement; 0058 QString cdbPath; 0059 NodeStorage& nodeStorage; 0060 lvtprj::ProjectFile& projectFile; 0061 UndoManager *undoManager = nullptr; 0062 std::unordered_map<const QUndoCommand *, GraphTabElement *> commandToTab; 0063 lvtplg::PluginManager *pluginManager = nullptr; 0064 0065 explicit Private(lvtldr::NodeStorage& nodeStorage, 0066 lvtprj::ProjectFile& projectFile, 0067 std::shared_ptr<lvtclr::ColorManagement> colorManagement, 0068 lvtplg::PluginManager *pluginManager = nullptr): 0069 colorManagement(std::move(colorManagement)), 0070 nodeStorage(nodeStorage), 0071 projectFile(projectFile), 0072 pluginManager(pluginManager) 0073 { 0074 } 0075 }; 0076 0077 // -------------------------------------------- 0078 // class TabWidget 0079 // -------------------------------------------- 0080 0081 TabWidget::TabWidget(NodeStorage& nodeStorage, 0082 lvtprj::ProjectFile& projectFile, 0083 std::shared_ptr<lvtclr::ColorManagement> colorManagement, 0084 lvtplg::PluginManager *pluginManager, 0085 QWidget *parent): 0086 QTabWidget(parent), 0087 d(std::make_unique<TabWidget::Private>(nodeStorage, projectFile, std::move(colorManagement), pluginManager)) 0088 { 0089 d->addGraphBtn = new QToolButton(); 0090 d->addGraphBtn->setText("+"); 0091 0092 // TODO: Icons 0093 setCornerWidget(d->addGraphBtn, Qt::TopLeftCorner); 0094 connect(d->addGraphBtn, &QToolButton::clicked, this, [this]() { 0095 this->openNewGraphTab(); 0096 }); 0097 connect(this, &QTabWidget::tabCloseRequested, this, &TabWidget::closeTab); 0098 setTabsClosable(true); 0099 0100 setDocumentMode(true); 0101 openNewGraphTab(); 0102 0103 tabBar()->setContextMenuPolicy(Qt::CustomContextMenu); 0104 connect(tabBar(), &QWidget::customContextMenuRequested, this, [this](const QPoint& pos) { 0105 const auto idx = tabBar()->tabAt(pos); 0106 if (idx == -1) { 0107 return; 0108 } 0109 0110 QMenu menu; 0111 if (tabIcon(idx).isNull()) { 0112 auto *action = menu.addAction(tr("Bookmark this tab")); 0113 connect(action, &QAction::triggered, this, [this, idx] { 0114 saveBookmarkByTabIndex(idx); 0115 }); 0116 } else { 0117 auto *action = menu.addAction(tr("Remove bookmark")); 0118 connect(action, &QAction::triggered, this, [this, idx] { 0119 setTabIcon(idx, QIcon()); 0120 0121 // QWidgets sometimes adds a `&` to denote mnemonic, 0122 // remove it. 0123 auto text = tabText(idx); 0124 text.remove(QLatin1Char('&')); 0125 d->projectFile.removeBookmark(text); 0126 }); 0127 } 0128 menu.exec(tabBar()->mapToGlobal(pos)); 0129 }); 0130 } 0131 0132 TabWidget::~TabWidget() noexcept = default; 0133 0134 void TabWidget::saveBookmarkByTabIndex(int tabIdx) 0135 { 0136 InputDialog dlg; 0137 dlg.addTextField("name", tr("Bookmark Name:")); 0138 dlg.finish(); 0139 auto res = dlg.exec(); 0140 if (res == QDialog::DialogCode::Rejected) { 0141 return; 0142 } 0143 0144 const auto text = std::any_cast<QString>(dlg.fieldValue("name")); 0145 saveBookmark(text, tabIdx, Codethink::lvtprj::ProjectFile::Bookmark); 0146 } 0147 0148 void TabWidget::closeTab(int idx) 0149 { 0150 QWidget *widgetAt = widget(idx); 0151 removeTab(idx); 0152 0153 widgetAt->deleteLater(); 0154 0155 if (count() == 0) { 0156 openNewGraphTab(); 0157 } 0158 } 0159 0160 void TabWidget::setCurrentTabText(const QString& fullyQualifiedName) 0161 { 0162 setTabText(currentIndex(), fullyQualifiedName); 0163 Q_EMIT currentTabTextChanged(fullyQualifiedName); 0164 } 0165 0166 GraphTabElement *TabWidget::createTabElement() 0167 { 0168 auto *tabElement = new GraphTabElement(d->nodeStorage, d->projectFile, this); 0169 if (d->pluginManager) { 0170 tabElement->setPluginManager(*d->pluginManager); 0171 } 0172 if (d->undoManager) { 0173 auto *gv = tabElement->graphicsView(); 0174 gv->setUndoManager(d->undoManager); 0175 connect(gv, 0176 &GraphicsView::onUndoCommandReceived, 0177 this, 0178 [this, tabElement](GraphicsView *view, QUndoCommand *command) { 0179 d->commandToTab[command] = tabElement; 0180 }); 0181 } 0182 auto *view = tabElement->graphicsView(); 0183 view->setColorManagement(d->colorManagement); 0184 0185 connect(tabElement, &GraphTabElement::historyUpdate, this, [this](const QString& bookmark) { 0186 loadBookmark(d->projectFile.getBookmark(bookmark), HistoryType::NoHistory); 0187 }); 0188 return tabElement; 0189 } 0190 0191 void TabWidget::replaceGraphAt(int idx, const QString& qualifiedName) 0192 { 0193 auto *tabElement = qobject_cast<Codethink::lvtqtw::GraphTabElement *>(widget(idx)); 0194 auto *scene = qobject_cast<Codethink::lvtqtc::GraphicsScene *>(tabElement->graphicsView()->scene()); 0195 scene->clearGraph(); 0196 scene->loadEntityByQualifiedName(qualifiedName, QPoint{}); 0197 } 0198 0199 void TabWidget::openNewGraphTab(std::optional<QSet<QString>> qualifiedNames) 0200 { 0201 auto *tabElement = createTabElement(); 0202 int tabIdx = addTab(tabElement, tr("Unnamed %1").arg(count())); 0203 setCurrentIndex(tabIdx); 0204 0205 if (!qualifiedNames || qualifiedNames.value().empty()) { 0206 return; 0207 } 0208 0209 auto *scene = qobject_cast<Codethink::lvtqtc::GraphicsScene *>(tabElement->graphicsView()->scene()); 0210 for (const auto& qualifiedName : qualifiedNames.value()) { 0211 scene->loadEntityByQualifiedName(qualifiedName, QPoint{}); 0212 } 0213 scene->reLayout(); 0214 } 0215 0216 lvtqtc::GraphicsView *TabWidget::graphicsView() 0217 { 0218 auto *tabElement = qobject_cast<lvtqtw::GraphTabElement *>(currentWidget()); 0219 if (!tabElement) { 0220 return nullptr; // RETURN 0221 } 0222 return tabElement->graphicsView(); 0223 } 0224 0225 void TabWidget::setUndoManager(lvtqtc::UndoManager *undoManager) 0226 { 0227 d->undoManager = undoManager; 0228 auto onBeforeUndoRedo = [this](const QUndoCommand *command) { 0229 try { 0230 auto *tab = d->commandToTab.at(command); 0231 setCurrentWidget(tab); 0232 } catch (std::out_of_range const&) { 0233 // Ignore tab change if not found 0234 return; 0235 } 0236 }; 0237 connect(d->undoManager, &UndoManager::onBeforeUndo, this, onBeforeUndoRedo); 0238 connect(d->undoManager, &UndoManager::onBeforeRedo, this, onBeforeUndoRedo); 0239 for (int index = 0; index < count(); ++index) { 0240 auto *tab = qobject_cast<GraphTabElement *>(widget(index)); 0241 auto *gv = tab->graphicsView(); 0242 gv->setUndoManager(undoManager); 0243 connect(gv, &GraphicsView::onUndoCommandReceived, this, [this, tab](GraphicsView *view, QUndoCommand *command) { 0244 d->commandToTab[command] = tab; 0245 }); 0246 } 0247 } 0248 0249 void TabWidget::saveTabsOnProject(ProjectFile::BookmarkType type) 0250 { 0251 for (int i = 0; i < count(); i++) { 0252 saveBookmark(tabText(i), i, type); 0253 } 0254 } 0255 0256 void TabWidget::saveBookmark(const QString& title, int idx, ProjectFile::BookmarkType type) 0257 { 0258 auto *tabElement = qobject_cast<Codethink::lvtqtw::GraphTabElement *>(widget(idx)); 0259 tabElement->saveBookmark(title, type); 0260 0261 setTabText(idx, title); 0262 } 0263 0264 void TabWidget::loadBookmark(const QJsonDocument& doc, lvtshr::HistoryType historyType) 0265 { 0266 QJsonObject obj = doc.object(); 0267 const auto idx = currentIndex(); 0268 auto *tabElement = qobject_cast<Codethink::lvtqtw::GraphTabElement *>(widget(idx)); 0269 0270 setTabText(idx, obj["tabname"].toString()); 0271 setTabIcon(idx, QIcon(":/icons/build")); 0272 0273 tabElement->loadBookmark(doc, historyType); 0274 } 0275 0276 } // end namespace Codethink::lvtqtw