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