File indexing completed on 2024-05-19 05:42:26
0001 // ct_lvtqtw_graphtabelement.h -*-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_lvtqtw_graphtabelement.h> 0021 0022 #include <ui_ct_lvtqtw_graphtabelement.h> 0023 0024 #include <ct_lvtqtc_graphicsscene.h> 0025 #include <ct_lvtqtc_graphicsview.h> 0026 #include <ct_lvtqtc_lakosentity.h> 0027 0028 #include <ct_lvtshr_graphenums.h> 0029 0030 #include <ct_lvtldr_nodestorage.h> 0031 #include <ct_lvtprj_projectfile.h> 0032 0033 #include <ct_lvtmdl_historylistmodel.h> 0034 0035 #include <ct_lvtqtc_iconhelpers.h> 0036 #include <ct_lvtqtc_itool.h> 0037 #include <ct_lvtqtc_tool_add_component.h> 0038 #include <ct_lvtqtc_tool_add_logical_entity.h> 0039 #include <ct_lvtqtc_tool_add_logical_relation.h> 0040 #include <ct_lvtqtc_tool_add_package.h> 0041 #include <ct_lvtqtc_tool_add_physical_dependency.h> 0042 #include <ct_lvtqtc_tool_reparent_entity.h> 0043 #include <ct_lvtqtc_tool_zoom.h> 0044 0045 #include <ct_lvtqtw_searchwidget.h> 0046 #include <ct_lvtqtw_toolbox.h> 0047 0048 // Using QMap because it has the .keys() method that makes it easier to pass 0049 // the key list to the comboboxes. 0050 #include <QComboBox> 0051 #include <QDebug> 0052 #include <QLabel> 0053 #include <QMap> 0054 #include <QResizeEvent> 0055 #include <QSpinBox> 0056 #include <QString> 0057 #include <QToolBar> 0058 #include <QToolButton> 0059 #include <QtGlobal> 0060 0061 #include <preferences.h> 0062 0063 using namespace Codethink::lvtldr; 0064 using namespace Codethink::lvtqtc; 0065 using namespace Codethink::lvtshr; 0066 0067 namespace Codethink::lvtqtw { 0068 0069 // defines how the load algorithm will handle the packages 0070 enum PackageView { DependencyTree, ReverseDependencyTree, DependencyGraph }; 0071 0072 struct GraphTabElement::Private { 0073 lvtprj::ProjectFile& projectFile; 0074 0075 lvtmdl::HistoryListModel historyModel; 0076 0077 // Actions that will trigger a reload of the scene 0078 QAction *actRelayout = nullptr; 0079 // Actions that allow the user to Edit the elements 0080 lvtqtc::ITool *toolAddPhysicalDependency = nullptr; 0081 lvtqtc::ITool *toolAddIsALogicalRelation = nullptr; 0082 lvtqtc::ITool *toolAddUsesInTheImplementationRelation = nullptr; 0083 lvtqtc::ITool *toolAddUsesInTheInterfaceRelation = nullptr; 0084 lvtqtc::ITool *toolAddPackage = nullptr; 0085 lvtqtc::ITool *toolAddComponent = nullptr; 0086 lvtqtc::ITool *toolAddLogEntity = nullptr; 0087 lvtqtc::ITool *toolReparentEntity = nullptr; 0088 0089 // Floating panel. 0090 SearchWidget *searchWidget = nullptr; 0091 0092 std::vector<QAction *> classOnlyActions; 0093 std::vector<lvtqtc::ITool *> tools; 0094 0095 GraphicsView *graphicsView = nullptr; 0096 0097 Private(lvtprj::ProjectFile& prj): projectFile(prj) 0098 { 0099 } 0100 }; 0101 0102 GraphTabElement::GraphTabElement(NodeStorage& nodeStorage, lvtprj::ProjectFile& projectFile, QWidget *parent): 0103 QWidget(parent), 0104 ui(std::make_unique<Ui::GraphTabElement>()), 0105 d(std::make_unique<GraphTabElement::Private>(projectFile)) 0106 { 0107 ui->setupUi(this); 0108 0109 d->graphicsView = new GraphicsView(nodeStorage, projectFile, ui->splitter); 0110 d->graphicsView->setObjectName(QString::fromUtf8("graphicsView")); 0111 QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding); 0112 sizePolicy.setHorizontalStretch(0); 0113 sizePolicy.setVerticalStretch(0); 0114 sizePolicy.setHeightForWidth(ui->splitter->sizePolicy().hasHeightForWidth()); 0115 sizePolicy.setHeightForWidth(d->graphicsView->sizePolicy().hasHeightForWidth()); 0116 d->graphicsView->setSizePolicy(sizePolicy); 0117 ui->splitter->addWidget(d->graphicsView); 0118 0119 // Floating search panel. 0120 d->searchWidget = new SearchWidget(this); 0121 d->searchWidget->setAttribute(Qt::WA_StyledBackground, true); 0122 d->searchWidget->setBackgroundRole(QPalette::Window); 0123 d->searchWidget->setAutoFillBackground(true); 0124 d->graphicsView->installEventFilter(d->searchWidget); 0125 // End Toolbar Setup. 0126 0127 ui->zoomToolBox->setValue(Preferences::zoomLevel()); 0128 0129 // sets up the scene. 0130 connect(d->graphicsView, &Codethink::lvtqtc::GraphicsView::zoomFactorChanged, ui->zoomToolBox, &QSpinBox::setValue); 0131 0132 connect(ui->zoomToolBox, 0133 QOverload<int>::of(&QSpinBox::valueChanged), 0134 d->graphicsView, 0135 &Codethink::lvtqtc::GraphicsView::setZoomFactor); 0136 0137 // We don't need to fix the search on the history model, as it will 0138 // load a different graph, that's already handled by setCurentDiagramFromHistory. 0139 connect(d->graphicsView, &lvtqtc::GraphicsView::requestNext, this, [this] { 0140 d->historyModel.next(); 0141 }); 0142 0143 connect(d->graphicsView, &lvtqtc::GraphicsView::requestPrevious, this, [this] { 0144 d->historyModel.previous(); 0145 }); 0146 0147 connect(ui->backHistory, &QToolButton::clicked, this, [this] { 0148 d->historyModel.previous(); 0149 }); 0150 0151 connect(ui->forwardHistory, &QToolButton::clicked, this, [this] { 0152 d->historyModel.next(); 0153 }); 0154 connect(&d->historyModel, 0155 &lvtmdl::HistoryListModel::currentIndexChanged, 0156 this, 0157 &GraphTabElement::setCurrentDiagramFromHistory); 0158 0159 connect(d->searchWidget, &SearchWidget::searchModeChanged, d->graphicsView, &lvtqtc::GraphicsView::setSearchMode); 0160 0161 connect(d->searchWidget, 0162 &SearchWidget::searchStringChanged, 0163 d->graphicsView, 0164 &lvtqtc::GraphicsView::setSearchString); 0165 0166 connect(d->searchWidget, 0167 &SearchWidget::requestNextElement, 0168 d->graphicsView, 0169 &lvtqtc::GraphicsView::highlightedNextSearchElement); 0170 0171 connect(d->searchWidget, 0172 &SearchWidget::requestPreviousElement, 0173 d->graphicsView, 0174 &lvtqtc::GraphicsView::highlightedPreviousSearchElement); 0175 0176 connect(d->graphicsView, 0177 &lvtqtc::GraphicsView::searchTotal, 0178 d->searchWidget, 0179 &SearchWidget::setNumberOfMatchedItems); 0180 0181 connect(d->graphicsView, 0182 &lvtqtc::GraphicsView::currentSearchItemHighlighted, 0183 d->searchWidget, 0184 &SearchWidget::setCurrentItem); 0185 0186 d->graphicsView->toggleLegend(Preferences::showLegend()); 0187 d->graphicsView->toggleMinimap(Preferences::showMinimap()); 0188 0189 d->searchWidget->setVisible(false); 0190 0191 setupToolBar(nodeStorage); 0192 } 0193 0194 GraphTabElement::~GraphTabElement() = default; 0195 0196 void GraphTabElement::setupToolBar(NodeStorage& nodeStorage) 0197 { 0198 auto *scene = qobject_cast<lvtqtc::GraphicsScene *>(d->graphicsView->scene()); 0199 0200 lvtqtc::ITool *zoomTool = new lvtqtc::ToolZoom(d->graphicsView); 0201 d->toolAddPhysicalDependency = new lvtqtc::ToolAddPhysicalDependency(d->graphicsView, nodeStorage); 0202 d->toolAddIsALogicalRelation = new lvtqtc::ToolAddIsARelation(d->graphicsView, nodeStorage); 0203 d->toolAddUsesInTheImplementationRelation = 0204 new lvtqtc::ToolAddUsesInTheImplementation(d->graphicsView, nodeStorage); 0205 d->toolAddUsesInTheInterfaceRelation = new lvtqtc::ToolAddUsesInTheInterface(d->graphicsView, nodeStorage); 0206 d->toolAddPackage = new lvtqtc::ToolAddPackage(d->graphicsView, nodeStorage); 0207 d->toolAddComponent = new lvtqtc::ToolAddComponent(d->graphicsView, nodeStorage); 0208 d->toolAddLogEntity = new lvtqtc::ToolAddLogicalEntity(d->graphicsView, nodeStorage); 0209 d->toolReparentEntity = new lvtqtc::ToolReparentEntity(d->graphicsView, nodeStorage); 0210 0211 d->tools = { 0212 /* 0 */ zoomTool, 0213 /* 1 */ d->toolAddPhysicalDependency, 0214 /* 2 */ d->toolAddPackage, 0215 /* 3 */ d->toolAddComponent, 0216 /* 4 */ d->toolAddLogEntity, 0217 /* 5 */ d->toolAddIsALogicalRelation, 0218 /* 6 */ d->toolAddUsesInTheImplementationRelation, 0219 /* 7 */ d->toolAddUsesInTheInterfaceRelation, 0220 /* 8 */ d->toolReparentEntity, 0221 }; 0222 for (auto *tool : d->tools) { 0223 connect(tool, &lvtqtc::ITool::sendMessage, this, &GraphTabElement::sendMessage); 0224 } 0225 0226 d->actRelayout = new QAction(); 0227 d->actRelayout->setToolTip(tr("Recalculates the layout of the current view elements")); 0228 d->actRelayout->setText(tr("Refresh Layout")); 0229 d->actRelayout->setIcon(IconHelpers::iconFrom(":/icons/refresh")); 0230 connect(d->actRelayout, &QAction::triggered, scene, &lvtqtc::GraphicsScene::reLayout); 0231 0232 auto *dumpVisibleRectAction = new QAction(); 0233 dumpVisibleRectAction->setToolTip(tr("Output to stdout the items on the visible rectangle")); 0234 dumpVisibleRectAction->setText(tr("Output visible rect")); 0235 dumpVisibleRectAction->setIcon(IconHelpers::iconFrom(":/icons/debug")); 0236 connect(dumpVisibleRectAction, &QAction::triggered, d->graphicsView, &lvtqtc::GraphicsView::debugVisibleScreen); 0237 0238 auto *dumpSceneAction = new QAction(); 0239 dumpSceneAction->setToolTip(tr("Output to stdout the whole scene")); 0240 dumpSceneAction->setText(tr("Output scene")); 0241 dumpSceneAction->setIcon(IconHelpers::iconFrom(":/icons/fatal")); 0242 connect(dumpSceneAction, &QAction::triggered, scene, [scene] { 0243 qDebug() << scene->toJson(); 0244 }); 0245 0246 auto *minimapAction = new QAction(); 0247 minimapAction->setToolTip(tr("Show Minimap")); 0248 minimapAction->setText(tr("Show Minimap")); 0249 minimapAction->setIcon(IconHelpers::iconFrom(":/icons/map")); 0250 minimapAction->setCheckable(true); 0251 minimapAction->setChecked(Preferences::showMinimap()); 0252 connect(minimapAction, &QAction::triggered, d->graphicsView, &lvtqtc::GraphicsView::toggleMinimap); 0253 0254 auto *legendAction = new QAction(); 0255 legendAction->setToolTip(tr("Show Information Panel")); 0256 legendAction->setText(tr("Information Panel")); 0257 legendAction->setIcon(IconHelpers::iconFrom(":/icons/help")); 0258 legendAction->setCheckable(true); 0259 legendAction->setChecked(Preferences::showLegend()); 0260 connect(legendAction, &QAction::triggered, d->graphicsView, &lvtqtc::GraphicsView::toggleLegend); 0261 0262 auto *fitInViewAction = new QAction(); 0263 fitInViewAction->setToolTip(tr("Fits the entire graph on the view.")); 0264 fitInViewAction->setText(tr("Fit in View")); 0265 fitInViewAction->setIcon(IconHelpers::iconFrom(":/icons/expand")); 0266 connect(fitInViewAction, &QAction::triggered, this, [this] { 0267 d->graphicsView->fitAllInView(); 0268 }); 0269 0270 auto *resetZoomAction = new QAction(); 0271 resetZoomAction->setToolTip(tr("Reset the zoom level to 100%")); 0272 resetZoomAction->setText(tr("Reset Zoom")); 0273 resetZoomAction->setIcon(IconHelpers::iconFrom(":/icons/remove_zoom")); 0274 connect(resetZoomAction, &QAction::triggered, this, [this] { 0275 d->graphicsView->setZoomFactor(100); 0276 }); 0277 0278 // Manipulation: 0279 const QString manipulationId = tr("Manipulation"); 0280 ui->toolBox->createGroup(manipulationId); 0281 ui->toolBox->createToolButton(manipulationId, d->toolAddPackage); 0282 ui->toolBox->createToolButton(manipulationId, d->toolAddComponent); 0283 ui->toolBox->createToolButton(manipulationId, d->toolAddLogEntity); 0284 ui->toolBox->createToolButton(manipulationId, d->toolAddPhysicalDependency); 0285 ui->toolBox->createToolButton(manipulationId, d->toolAddIsALogicalRelation); 0286 ui->toolBox->createToolButton(manipulationId, d->toolAddUsesInTheImplementationRelation); 0287 ui->toolBox->createToolButton(manipulationId, d->toolAddUsesInTheInterfaceRelation); 0288 ui->toolBox->createToolButton(manipulationId, d->toolReparentEntity); 0289 0290 // Common Tools: 0291 const QString visualizationId = tr("Visualization"); 0292 ui->toolBox->createGroup(visualizationId); 0293 ui->toolBox->createToolButton(visualizationId, d->actRelayout); 0294 ui->toolBox->createToolButton(visualizationId, zoomTool); 0295 ui->toolBox->createToolButton(visualizationId, fitInViewAction); 0296 ui->toolBox->createToolButton(visualizationId, resetZoomAction); 0297 ui->toolBox->createToolButton(visualizationId, minimapAction); 0298 ui->toolBox->createToolButton(visualizationId, legendAction); 0299 0300 // Debug 0301 const QString debugId = tr("Debug"); 0302 ui->toolBox->createGroup(debugId); 0303 ui->toolBox->createToolButton(debugId, dumpVisibleRectAction); 0304 ui->toolBox->createToolButton(debugId, dumpSceneAction); 0305 0306 ui->splitter->setSizes({100, 999}); 0307 ui->splitter->setStretchFactor(0, 0); 0308 ui->splitter->setStretchFactor(1, 1); 0309 0310 if (!Preferences::enableSceneContextMenu()) { 0311 ui->toolBox->hideElements("Debug"); 0312 } 0313 } 0314 0315 lvtqtc::GraphicsView *GraphTabElement::graphicsView() const 0316 { 0317 return d->graphicsView; 0318 } 0319 0320 void GraphTabElement::toggleFilterVisibility() 0321 { 0322 d->searchWidget->setVisible(!d->searchWidget->isVisible()); 0323 } 0324 0325 void GraphTabElement::setCurrentDiagramFromHistory(int idx) 0326 { 0327 Q_EMIT historyUpdate(d->historyModel.at(idx)); 0328 } 0329 0330 void GraphTabElement::resizeEvent(QResizeEvent *ev) 0331 { 0332 Q_UNUSED(ev); 0333 constexpr int horizontalSpacing = 100; 0334 const int x = width() - d->searchWidget->width() - horizontalSpacing; 0335 const int y = d->graphicsView->y(); 0336 0337 d->searchWidget->adjustSize(); 0338 d->searchWidget->setGeometry(x, y, d->searchWidget->width(), d->searchWidget->height()); 0339 } 0340 0341 std::vector<lvtqtc::ITool *> GraphTabElement::tools() const 0342 { 0343 return d->tools; 0344 } 0345 0346 void GraphTabElement::setPluginManager(Codethink::lvtplg::PluginManager& pm) 0347 { 0348 d->graphicsView->setPluginManager(pm); 0349 } 0350 0351 void GraphTabElement::saveBookmark(const QString& title, lvtprj::ProjectFile::BookmarkType type) 0352 { 0353 auto *scene = qobject_cast<Codethink::lvtqtc::GraphicsScene *>(graphicsView()->scene()); 0354 auto jsonObj = scene->toJson(); 0355 0356 QJsonObject mainObj{{"scene", jsonObj}, 0357 {"tabname", title}, 0358 {"id", title}, 0359 {"zoom_level", graphicsView()->zoomFactor()}}; 0360 0361 const cpp::result<void, lvtprj::ProjectFileError> ret = d->projectFile.saveBookmark(QJsonDocument(mainObj), type); 0362 0363 if (ret.has_error()) { 0364 Q_EMIT sendMessage(tr("Error saving bookmark."), KMessageWidget::MessageType::Error); 0365 } 0366 } 0367 0368 void GraphTabElement::loadBookmark(const QJsonDocument& doc, lvtshr::HistoryType historyType) 0369 { 0370 QJsonObject obj = doc.object(); 0371 auto *scene = qobject_cast<Codethink::lvtqtc::GraphicsScene *>(graphicsView()->scene()); 0372 0373 scene->fromJson(obj["scene"].toObject()); 0374 0375 graphicsView()->setZoomFactor(obj["zoom_level"].toInt()); 0376 0377 if (historyType == lvtshr::HistoryType::History) { 0378 d->historyModel.append(doc["id"].toString()); 0379 } 0380 } 0381 0382 } // namespace Codethink::lvtqtw