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