File indexing completed on 2024-05-19 05:42:17
0001 // ct_lvtqtc_graphicsscene.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 #ifndef INCLUDED_LVTQTC_GRAPHICSSCENE 0021 #define INCLUDED_LVTQTC_GRAPHICSSCENE 0022 0023 #include <ct_lvtshr_uniqueid.h> 0024 #include <lvtqtc_export.h> 0025 0026 #include <ct_lvtqtc_packagedependency.h> 0027 0028 #include <ct_lvtldr_igraphloader.h> 0029 #include <ct_lvtplg_pluginmanager.h> 0030 #include <ct_lvtprj_projectfile.h> 0031 0032 #include <ct_lvtshr_graphenums.h> 0033 0034 #include <QElapsedTimer> 0035 #include <QGraphicsScene> 0036 #include <QJsonDocument> 0037 #include <QMenu> 0038 #include <QTimer> 0039 0040 #include <oup/observable_unique_ptr.hpp> 0041 0042 #include <memory> 0043 #include <tuple> 0044 0045 class QKeyEvent; 0046 0047 namespace Codethink::lvtclr { 0048 class ColorManagement; 0049 } 0050 0051 namespace Codethink::lvtldr { 0052 class NodeStorage; 0053 class LakosianNode; 0054 } // namespace Codethink::lvtldr 0055 0056 namespace Codethink::lvtqtc { 0057 class LakosEntity; 0058 class LakosRelation; 0059 0060 class LVTQTC_EXPORT GraphicsScene : public QGraphicsScene, 0061 public Codethink::lvtldr::IGraphLoader, 0062 public oup::enable_observer_from_this_unique<GraphicsScene> 0063 // Visualizes a Tree model and reacts to changes 0064 { 0065 Q_OBJECT 0066 0067 public: 0068 struct Private; 0069 0070 // The layers are "sheets of translucent plastic" where the objects 0071 // are drawn. lower values means that it's on the bottom, and high 0072 // values are on the top. This controls the drawing order of things. 0073 enum class UnloadDepth : unsigned short { Entity, Children }; 0074 0075 enum class CreateAction : unsigned short { 0076 LoadGraphAction, // The entity is created at the `loadFromDb` step, populating the whole canvas. 0077 UserAction // The entity is created by the User, when the canvas is already populated. 0078 }; 0079 0080 // The possible errors that can occur during the load phase. 0081 0082 // CREATORS 0083 GraphicsScene(Codethink::lvtldr::NodeStorage& nodeStorage, 0084 lvtprj::ProjectFile const& projectFile, 0085 QObject *parent = nullptr); 0086 // Constructor 0087 0088 ~GraphicsScene() noexcept override; 0089 // Destructor 0090 0091 void updateBoundingRect(); 0092 // recalculates and updates the bounding rectangle based 0093 // on the children bounding rect. 0094 // must be called when we add a new element on the screen via 0095 // a tool, or after we load entities from the database. 0096 // this call is expensive, it should not be called whenever 0097 // we add something on the view, just after all elements 0098 // are finished loading. 0099 0100 void setColorManagement(const std::shared_ptr<lvtclr::ColorManagement>& colorManagement); 0101 0102 std::vector<LakosEntity *> selectedEntities() const; 0103 0104 // This class defines what we need to implement on classes that load graphs visually 0105 void clearGraph() override; 0106 0107 LakosEntity *addUdtVertex(lvtldr::LakosianNode *node, 0108 bool selected = false, 0109 LakosEntity *parent = nullptr, 0110 lvtshr::LoaderInfo info = {}) override; 0111 0112 LakosEntity *addRepositoryVertex(lvtldr::LakosianNode *node, 0113 bool selected = false, 0114 LakosEntity *parent = nullptr, 0115 lvtshr::LoaderInfo info = {}) override; 0116 0117 LakosEntity *addPkgVertex(lvtldr::LakosianNode *node, 0118 bool selected = false, 0119 LakosEntity *parent = nullptr, 0120 lvtshr::LoaderInfo info = {}) override; 0121 0122 LakosEntity *addCompVertex(lvtldr::LakosianNode *node, 0123 bool selected = false, 0124 LakosEntity *parent = nullptr, 0125 lvtshr::LoaderInfo info = {}) override; 0126 0127 LakosRelation *addIsARelation(LakosEntity *source, LakosEntity *target) override; 0128 LakosRelation *addUsesInTheInterfaceRelation(LakosEntity *source, LakosEntity *target) override; 0129 LakosRelation *addUsesInTheImplementationRelation(LakosEntity *source, LakosEntity *target) override; 0130 LakosRelation *addPackageDependencyRelation(LakosEntity *source, LakosEntity *target) override; 0131 0132 void connectEntitySignals(LakosEntity *entity); 0133 0134 [[nodiscard]] std::vector<LakosEntity *>& allEntities() const; 0135 0136 Q_SIGNAL void selectedEntityChanged(Codethink::lvtqtc::LakosEntity *entity); 0137 0138 void runLayoutAlgorithm(); 0139 // ignores the layout saved on the cache, and relayouts the items on screen. 0140 0141 void setEntityPos(const lvtshr::UniqueId& uid, QPointF pos) const; 0142 0143 void setBlockNodeResizeOnHover(bool block); 0144 // forbids / enables node resize on hover. 0145 0146 [[nodiscard]] bool blockNodeResizeOnHover() const; 0147 // return the value of the blockNodeResizeOnHover flag. 0148 0149 // Those calls handle the MachineState that controls the flow of information 0150 // for loading the database. 0151 // First we try to load from the Cache database, the cache database has a 0152 // different graph data structure than the other database, that already 0153 // holds information on positions - so we don't need to call layoutNodes() 0154 // and layoutEdges(). 0155 // 0156 // TODO: Merge the data structures of both graphs. 0157 Q_SIGNAL void graphLoadStarted(); 0158 Q_SIGNAL void refuseToLoad(const QString& fullyQualifiedName); 0159 0160 enum class CacheDbLoadStatus { CacheFound, CacheNotFound, Error }; 0161 0162 enum class CodeDbLoadStatus { Success, Error }; 0163 0164 CacheDbLoadStatus layoutFromCacheDatabase(); 0165 0166 void reLayout(); 0167 // runs the layout algorithm again, on the current loaded graph. 0168 0169 void pannelCollapse(); 0170 void enableLayoutUpdates(); 0171 void layoutDone(); 0172 [[nodiscard]] QString fetchErrorMessage() const; 0173 Q_SIGNAL void errorMessage(const QString& error); 0174 Q_SIGNAL void graphLoadFinished(); 0175 0176 void populateMenu(QMenu& menu, QMenu *debugMenu); 0177 0178 void fixTransitiveEdgeVisibility(); 0179 // some changes on the nodes can trigger a visibility change 0180 // on the edges. it's important that we fix the transitive 0181 // edge visibility after they are possibily made visible. 0182 // this is an inexpensive operation, there's no painting 0183 // done until we finish running this. 0184 0185 Q_SIGNAL void requestEnableWindow(); 0186 Q_SIGNAL void requestDisableWindow(); 0187 0188 Q_SIGNAL void createReportActionClicked(std::string const& title, std::string const& htmlContents); 0189 0190 Q_SIGNAL void requestNewTab(const QSet<QString>& qualifiedNames); 0191 0192 /* Debug Methods, methods to help us visualize contextual information 0193 * of elements */ 0194 void updateEdgeDebugInfo(); 0195 void toggleEdgeBoundingRects(); 0196 void toggleEdgeShapes(); 0197 void toggleEdgeTextualInformation(); 0198 void toggleEdgeIntersectionPaths(); 0199 void toggleEdgeOriginalLine(); 0200 0201 static LakosEntity *outermostParent(LakosEntity *a, LakosEntity *b); 0202 // returns the outermost parent of two different lakos entities, or nullptr 0203 0204 [[nodiscard]] LakosEntity *entityById(const std::string& uniqueId) const; 0205 // Returns an entity by it's internal ID. 0206 0207 [[nodiscard]] LakosEntity *entityByQualifiedName(const std::string& qualName) const; 0208 // returns an entity by it's qualified name. 0209 0210 void loadEntityByQualifiedName(const QString& qualifiedName, const QPointF& pos); 0211 // A Qualified name is just dropped onto the scene. We need to build 0212 // the entities for it. 0213 0214 [[nodiscard]] LakosRelation *addRelation(LakosRelation *relation, bool isVisible = true); 0215 // Adds a relation, called internally by all of the `add*Relation` methods. 0216 // should not be used outside of the implementation of this class, but it cannot be private 0217 // because we are acessing this from the unammed namespace. 0218 0219 LakosEntity *findLakosEntityFromUid(lvtshr::UniqueId uid) const; 0220 0221 void collapseSecondaryEntities(); 0222 0223 void loadEntity(lvtshr::UniqueId uuid, UnloadDepth depth); 0224 void unloadEntity(lvtshr::UniqueId uuid, UnloadDepth depth); 0225 // public request to unload one or many elements. 0226 // the uuid is the element where the action will happen 0227 // the depth is "Element" or "Children", meaning unload itself, or only 0228 // the children. 0229 // Removing the children is a recursive operation. 0230 0231 void removeEdge(LakosEntity& fromEntity, LakosEntity& toEntity); 0232 0233 lvtprj::ProjectFile const& projectFile() const; 0234 0235 QJsonObject toJson() const; 0236 void fromJson(const QJsonObject& doc); 0237 0238 void setPluginManager(Codethink::lvtplg::PluginManager& pm); 0239 0240 private: 0241 void unloadEntity(LakosEntity *entity); 0242 0243 void finalizeEntityPartialLoad(LakosEntity *entity); 0244 // Finishes the layout of partial graphs. 0245 // partial graphs are the ones requested by a right click on the elements on the canvas. 0246 0247 lvtldr::NodeLoadFlags loadFlagsFor(lvtldr::LakosianNode *node) const override; 0248 0249 void fixRelationsParentRelationship(); 0250 // HACK. Goes through the list of edges and fixes the parent relationship 0251 // this is needed because the PackageLoader loads some component children before loading 0252 // the component parent, and the edge needs the parent to position itself correctly. 0253 0254 void searchTransitiveRelations(); 0255 // ask the thread to look for transitive relations. 0256 0257 void transitiveRelationSearchFinished(); 0258 // the thread to look for transitive relations just finished. 0259 0260 void toggleTransitiveRelationVisibility(bool show); 0261 // shows / hides the transitive edges. 0262 0263 void addEdgeBetween(LakosEntity *fromEntity, LakosEntity *toEntity, lvtshr::LakosRelationType type); 0264 0265 // DATA TYPES 0266 std::unique_ptr<Private> d; 0267 }; 0268 0269 } // end namespace Codethink::lvtqtc 0270 0271 #endif