diff --git a/bin/app/data/color_schemes/bad_rainbow.xml b/bin/app/data/color_schemes/bad_rainbow.xml index 8a81fbc2..e8e4868d 100644 --- a/bin/app/data/color_schemes/bad_rainbow.xml +++ b/bin/app/data/color_schemes/bad_rainbow.xml @@ -593,4 +593,25 @@ #666666 + + + #494949 + #808080 + #CCCCCC + #FFFFFF + darkgrey + #696969 + + diff --git a/bin/app/data/color_schemes/bright.xml b/bin/app/data/color_schemes/bright.xml index 4c9d0545..dd049e8e 100644 --- a/bin/app/data/color_schemes/bright.xml +++ b/bin/app/data/color_schemes/bright.xml @@ -463,7 +463,7 @@ #F4BC3D - #878787 + #F56B3D @@ -567,4 +567,25 @@ #CECECE + + + white + #F5F5F5 + #C0C0C0 + black + #A0A0A0 + #F2F2F2 + + diff --git a/bin/app/data/color_schemes/dark.xml b/bin/app/data/color_schemes/dark.xml index af191000..488302b7 100644 --- a/bin/app/data/color_schemes/dark.xml +++ b/bin/app/data/color_schemes/dark.xml @@ -570,4 +570,25 @@ #555555 + + + #272728 + #555555 + #CCC + #F7F7F7 + darkgrey + #474748 + + diff --git a/bin/app/data/gui/custom_trail_view/custom_trail_view.css b/bin/app/data/gui/custom_trail_view/custom_trail_view.css new file mode 100644 index 00000000..5748ce51 --- /dev/null +++ b/bin/app/data/gui/custom_trail_view/custom_trail_view.css @@ -0,0 +1,64 @@ +* { + color: ; +} + +#panelA { + background: ; +} + +#panelB, #panelB2 { + background: ; + border-top: 1px solid ; + border-bottom: 1px solid ; +} + +#panelB2 { + border-bottom: none +} + +#search_box_container { + border-color: ; + border-radius: 8px; + border-width: 1px; +} + +#search_box_container:disabled { + background: ; + border-color: ; +} + +#search_box:disabled { + color: ; +} + +#button, #button_small { + background: ; + border: 1px solid ; + border-radius: 8px; + color: ; + padding: 3px 8px 2px; + font-size: px; + min-width: 5em; +} + +#button_small { + border-radius: 6px; + font-size: px; + min-width: 4em; +} + +#button:hover, #button_small:hover { + background: ; + border-color: ; + color: ; +} + +#button:disabled, #button_small:disabled { + background: ; + border-color: ; + color: ; +} + +#error { + color: red; +} diff --git a/bin/app/data/gui/custom_trail_view/images/circle.png b/bin/app/data/gui/custom_trail_view/images/circle.png new file mode 100644 index 00000000..4f7f1cfc Binary files /dev/null and b/bin/app/data/gui/custom_trail_view/images/circle.png differ diff --git a/bin/app/data/gui/graph_view/images/graph_custom.png b/bin/app/data/gui/graph_view/images/graph_custom.png new file mode 100644 index 00000000..dcd12ed7 Binary files /dev/null and b/bin/app/data/gui/graph_view/images/graph_custom.png differ diff --git a/bin/app/data/gui/search_view/search_view.css b/bin/app/data/gui/search_view/search_view.css index 1eb59843..75be0244 100644 --- a/bin/app/data/gui/search_view/search_view.css +++ b/bin/app/data/gui/search_view/search_view.css @@ -11,7 +11,7 @@ background-color: transparent; } -QWidget#search_box_container { +#search_box_container { background: ; border: 2px solid ; border-bottom-left-radius: 12px; @@ -20,19 +20,19 @@ QWidget#search_box_container { margin-right: 2px; } -QLineEdit#search_box { +#search_box { background-color: transparent; border: none; color: ; selection-background-color: ; } -QWidget#search_box_highlight { +#search_box_highlight { background-color: ; border: none; } -QAbstractItemView#search_box_popup { +#search_box_popup { background-color: ; border: 1px solid ; color: ; diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index e1c54358..338c03ff 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -38,6 +38,8 @@ add_files( component/controller/CodeController.h component/controller/Controller.cpp component/controller/Controller.h + component/controller/CustomTrailController.cpp + component/controller/CustomTrailController.h component/controller/ErrorController.cpp component/controller/ErrorController.h component/controller/GraphController.cpp @@ -71,6 +73,7 @@ add_files( component/view/CodeView.h component/view/CompositeView.cpp component/view/CompositeView.h + component/view/CustomTrailView.h component/view/DialogView.cpp component/view/DialogView.h component/view/ErrorView.cpp @@ -469,6 +472,7 @@ add_files( utility/messaging/filter_types/MessageFilterSearchAutocomplete.h utility/messaging/type/activation/MessageActivateLegend.h + utility/messaging/type/activation/MessageActivateFullTextSearch.h utility/messaging/type/bookmark/MessageBookmarkActivate.h utility/messaging/type/bookmark/MessageBookmarkBrowse.h @@ -479,6 +483,8 @@ add_files( utility/messaging/type/code/MessageCodeShowDefinition.h + utility/messaging/type/custom_trail/MessageCustomTrailShow.h + utility/messaging/type/error/MessageActivateErrors.h utility/messaging/type/error/MessageErrorCountClear.h utility/messaging/type/error/MessageErrorCountUpdate.h @@ -497,6 +503,9 @@ add_files( utility/messaging/type/indexing/MessageIndexingStarted.h utility/messaging/type/indexing/MessageIndexingStatus.h + utility/messaging/type/search/MessageSearch.h + utility/messaging/type/search/MessageSearchAutocomplete.h + utility/messaging/type/tab/MessageTabClose.h utility/messaging/type/tab/MessageTabOpen.h utility/messaging/type/tab/MessageTabOpenWith.h @@ -507,7 +516,6 @@ add_files( utility/messaging/type/MessageActivateBase.h utility/messaging/type/MessageActivateEdge.h utility/messaging/type/MessageActivateFile.h - utility/messaging/type/MessageActivateFullTextSearch.h utility/messaging/type/MessageActivateLocalSymbols.h utility/messaging/type/MessageActivateNodes.h utility/messaging/type/MessageActivateSourceLocations.h @@ -544,8 +552,6 @@ add_files( utility/messaging/type/MessageScrollCode.h utility/messaging/type/MessageScrollGraph.h utility/messaging/type/MessageScrollToLine.h - utility/messaging/type/MessageSearch.h - utility/messaging/type/MessageSearchAutocomplete.h utility/messaging/type/MessageShowReference.h utility/messaging/type/MessageShowScope.h utility/messaging/type/MessageShowStatus.h diff --git a/src/lib/component/ComponentFactory.cpp b/src/lib/component/ComponentFactory.cpp index 484e834d..40fd245a 100644 --- a/src/lib/component/ComponentFactory.cpp +++ b/src/lib/component/ComponentFactory.cpp @@ -3,27 +3,30 @@ #include "Component.h" #include "ActivationController.h" #include "BookmarkController.h" -#include "CodeController.h" -#include "ErrorController.h" -#include "GraphController.h" -#include "RefreshController.h" -#include "ScreenSearchController.h" -#include "SearchController.h" -#include "StatusBarController.h" -#include "StatusController.h" -#include "TabsController.h" -#include "TooltipController.h" -#include "UndoRedoController.h" #include "BookmarkView.h" +#include "CodeController.h" #include "CodeView.h" +#include "CustomTrailController.h" +#include "CustomTrailView.h" +#include "ErrorController.h" #include "ErrorView.h" +#include "GraphController.h" +#include "GraphView.h" +#include "RefreshController.h" #include "RefreshView.h" +#include "ScreenSearchController.h" #include "ScreenSearchView.h" +#include "SearchController.h" #include "SearchView.h" +#include "StatusBarController.h" #include "StatusBarView.h" +#include "StatusController.h" #include "StatusView.h" +#include "TabsController.h" #include "TabsView.h" +#include "TooltipController.h" #include "TooltipView.h" +#include "UndoRedoController.h" #include "UndoRedoView.h" #include "ViewFactory.h" @@ -66,6 +69,14 @@ std::shared_ptr ComponentFactory::createCodeComponent(ViewLayout* vie return std::make_shared(view, controller); } +std::shared_ptr ComponentFactory::createCustomTrailComponent() +{ + std::shared_ptr view = m_viewFactory->createCustomTrailView(); + std::shared_ptr controller = std::make_shared(m_storageAccess); + + return std::make_shared(view, controller); +} + std::shared_ptr ComponentFactory::createErrorComponent(ViewLayout* viewLayout) { std::shared_ptr view = m_viewFactory->createErrorView(viewLayout); diff --git a/src/lib/component/ComponentFactory.h b/src/lib/component/ComponentFactory.h index 1326d92d..660e5e13 100644 --- a/src/lib/component/ComponentFactory.h +++ b/src/lib/component/ComponentFactory.h @@ -21,6 +21,7 @@ public: std::shared_ptr createActivationComponent(); std::shared_ptr createBookmarkComponent(ViewLayout* viewLayout); std::shared_ptr createCodeComponent(ViewLayout* viewLayout); + std::shared_ptr createCustomTrailComponent(); std::shared_ptr createErrorComponent(ViewLayout* viewLayout); std::shared_ptr createGraphComponent(ViewLayout* viewLayout); std::shared_ptr createRefreshComponent(ViewLayout* viewLayout); diff --git a/src/lib/component/ComponentManager.cpp b/src/lib/component/ComponentManager.cpp index 09f23d91..25feac96 100644 --- a/src/lib/component/ComponentManager.cpp +++ b/src/lib/component/ComponentManager.cpp @@ -98,6 +98,9 @@ void ComponentManager::setupMain(ViewLayout* viewLayout, Id appId) std::shared_ptr statusBarComponent = m_componentFactory.createStatusBarComponent(viewLayout); m_components.push_back(statusBarComponent); + + std::shared_ptr customTrailComponent = m_componentFactory.createCustomTrailComponent(); + m_components.push_back(customTrailComponent); } void ComponentManager::setupTab(ViewLayout* viewLayout, Id tabId, ScreenSearchSender* screenSearchSender) diff --git a/src/lib/component/controller/CodeController.cpp b/src/lib/component/controller/CodeController.cpp index b2e3f175..8e67dc2a 100644 --- a/src/lib/component/controller/CodeController.cpp +++ b/src/lib/component/controller/CodeController.cpp @@ -240,6 +240,17 @@ void CodeController::handleMessage(MessageActivateTokens* message) MessageStatus(status).dispatch(); } +void CodeController::handleMessage(MessageActivateTrail* message) +{ + if (message->custom) + { + getView()->clear(); + Id nodeId = { message->originId ? message->originId : message->targetId }; + MessageCodeShowDefinition msg(nodeId); + handleMessage(&msg); + } +} + void CodeController::handleMessage(MessageActivateTrailEdge* message) { TRACE("trail edge activate"); @@ -375,6 +386,11 @@ void CodeController::handleMessage(MessageCodeShowDefinition* message) } snippets[0].insertSnippet = true; + + if (!m_collection) + { + m_collection = std::make_shared(); + } m_collection->addSourceLocationCopies(collection.get()); saveOrRestoreViewMode(message); diff --git a/src/lib/component/controller/CodeController.h b/src/lib/component/controller/CodeController.h index f32a62c5..db465947 100644 --- a/src/lib/component/controller/CodeController.h +++ b/src/lib/component/controller/CodeController.h @@ -15,6 +15,7 @@ #include "MessageActivateFullTextSearch.h" #include "MessageActivateLocalSymbols.h" #include "MessageActivateTokens.h" +#include "MessageActivateTrail.h" #include "MessageActivateTrailEdge.h" #include "MessageChangeFileView.h" #include "MessageDeactivateEdge.h" @@ -43,6 +44,7 @@ class CodeController , public MessageListener , public MessageListener , public MessageListener + , public MessageListener , public MessageListener , public MessageListener , public MessageListener @@ -69,6 +71,7 @@ private: void handleMessage(MessageActivateLegend* message) override; void handleMessage(MessageActivateLocalSymbols* message) override; void handleMessage(MessageActivateTokens* message) override; + void handleMessage(MessageActivateTrail* message) override; void handleMessage(MessageActivateTrailEdge* message) override; void handleMessage(MessageChangeFileView* message) override; void handleMessage(MessageCodeShowDefinition* message) override; diff --git a/src/lib/component/controller/CustomTrailController.cpp b/src/lib/component/controller/CustomTrailController.cpp new file mode 100644 index 00000000..1dc886b8 --- /dev/null +++ b/src/lib/component/controller/CustomTrailController.cpp @@ -0,0 +1,55 @@ +#include "CustomTrailController.h" + +#include "CustomTrailView.h" +#include "NodeTypeSet.h" +#include "StorageAccess.h" + +CustomTrailController::CustomTrailController(StorageAccess* storageAccess) + : m_storageAccess(storageAccess) +{ +} + +void CustomTrailController::clear() +{ + getView()->clearView(); + getView()->setAvailableNodeAndEdgeTypes( + m_storageAccess->getAvailableNodeTypes(), m_storageAccess->getAvailableEdgeTypes() + ); +} + +void CustomTrailController::autocomplete(const std::wstring query, bool from) +{ + NodeTypeSet nodeTypes = NodeTypeSet::all(); + nodeTypes.remove(NodeType(NodeType::NODE_MODULE)); + nodeTypes.remove(NodeType(NodeType::NODE_NAMESPACE)); + nodeTypes.remove(NodeType(NodeType::NODE_PACKAGE)); + + getView()->showAutocompletions(m_storageAccess->getAutocompletionMatches(query, nodeTypes, false), from); +} + +void CustomTrailController::activateTrail(MessageActivateTrail message) +{ + Id nodeId = message.originId ? message.originId : message.targetId; + message.searchMatches = m_storageAccess->getSearchMatchesForTokenIds({ nodeId }); + message.dispatch(); +} + +void CustomTrailController::handleMessage(MessageCustomTrailShow* message) +{ + getView()->showView(); +} + +void CustomTrailController::handleMessage(MessageIndexingFinished* message) +{ + clear(); +} + +void CustomTrailController::handleMessage(MessageWindowClosed* message) +{ + getView()->hideView(); +} + +CustomTrailView* CustomTrailController::getView() +{ + return Controller::getView(); +} diff --git a/src/lib/component/controller/CustomTrailController.h b/src/lib/component/controller/CustomTrailController.h new file mode 100644 index 00000000..a8ec9ef7 --- /dev/null +++ b/src/lib/component/controller/CustomTrailController.h @@ -0,0 +1,40 @@ +#ifndef CUSTOM_TRAIL_CONTROLLER_H +#define CUSTOM_TRAIL_CONTROLLER_H + +#include "Controller.h" +#include "MessageCustomTrailShow.h" +#include "MessageListener.h" +#include "MessageWindowClosed.h" +#include "MessageIndexingFinished.h" +#include "MessageActivateTrail.h" + +class CustomTrailView; +class StorageAccess; + +class CustomTrailController + : public Controller + , public MessageListener + , public MessageListener + , public MessageListener +{ +public: + CustomTrailController(StorageAccess* storageAccess); + ~CustomTrailController() = default; + + // Controller implementation + void clear() override; + + void autocomplete(const std::wstring query, bool from); + void activateTrail(MessageActivateTrail message); + +private: + void handleMessage(MessageCustomTrailShow* message) override; + void handleMessage(MessageIndexingFinished* message) override; + void handleMessage(MessageWindowClosed* message) override; + + CustomTrailView* getView(); + + StorageAccess* m_storageAccess; +}; + +#endif // CUSTOM_TRAIL_CONTROLLER_H diff --git a/src/lib/component/controller/GraphController.cpp b/src/lib/component/controller/GraphController.cpp index 0c464b94..92162392 100644 --- a/src/lib/component/controller/GraphController.cpp +++ b/src/lib/component/controller/GraphController.cpp @@ -215,10 +215,11 @@ void GraphController::handleMessage(MessageActivateTrail* message) m_activeEdgeIds.clear(); std::shared_ptr graph = m_storageAccess->getGraphForTrail( - message->originId, message->targetId, message->trailType, message->depth); + message->originId, message->targetId, message->nodeTypes, message->edgeTypes, message->nodeNonIndexed, message->depth, + true /* !message->custom || (message->originId && message->targetId) */); // remove non-indexed files from include graph if indexed file is origin - if (message->trailType & Edge::EDGE_INCLUDE) + if (!message->custom && message->edgeTypes & Edge::EDGE_INCLUDE) { Node* fileNode = graph->getNodeById(message->originId ? message->originId : message->targetId); if (fileNode && fileNode->isDefined()) @@ -262,11 +263,13 @@ void GraphController::handleMessage(MessageActivateTrail* message) m_graph->setTrailMode(message->horizontalLayout ? Graph::TRAIL_HORIZONTAL : Graph::TRAIL_VERTICAL); m_graph->setHasTrailOrigin(message->originId); - setVisibility(setActive(m_activeNodeIds, true)); + m_activeNodeIds = { message->originId ? message->originId : message->targetId }; + setActive(m_activeNodeIds, true); + setVisibility(true); MessageStatus(L"Layouting depth graph", false, true).dispatch(); - if (message->trailType & Edge::EDGE_INHERITANCE) + if (!message->custom && message->edgeTypes & Edge::EDGE_INHERITANCE) { groupTrailNodes(GroupType::INHERITANCE); } @@ -274,6 +277,15 @@ void GraphController::handleMessage(MessageActivateTrail* message) layoutNesting(); layoutTrail(message->horizontalLayout, message->originId); + if (message->originId && message->targetId) + { + DummyNode* targetNode = getDummyGraphNodeById(message->targetId).get(); + if (targetNode) + { + targetNode->active = true; + } + } + MessageStatus(L"Displaying depth graph", false, true).dispatch(); GraphView::GraphParams params; diff --git a/src/lib/component/controller/SearchController.cpp b/src/lib/component/controller/SearchController.cpp index 8f2df7d3..0d732e2e 100644 --- a/src/lib/component/controller/SearchController.cpp +++ b/src/lib/component/controller/SearchController.cpp @@ -44,6 +44,14 @@ void SearchController::handleMessage(MessageActivateTokens* message) } } +void SearchController::handleMessage(MessageActivateTrail* message) +{ + if (message->custom) + { + updateMatches(message); + } +} + void SearchController::handleMessage(MessageFind* message) { if (message->findFulltext) @@ -69,7 +77,7 @@ void SearchController::handleMessage(MessageSearchAutocomplete* message) } LOG_INFO(L"autocomplete string: \"" + message->query + L"\""); - view->setAutocompletionList(m_storageAccess->getAutocompletionMatches(message->query, message->acceptedNodeTypes)); + view->setAutocompletionList(m_storageAccess->getAutocompletionMatches(message->query, message->acceptedNodeTypes, true)); } SearchView* SearchController::getView() diff --git a/src/lib/component/controller/SearchController.h b/src/lib/component/controller/SearchController.h index 159f44e2..34bb3955 100644 --- a/src/lib/component/controller/SearchController.h +++ b/src/lib/component/controller/SearchController.h @@ -8,6 +8,7 @@ #include "MessageActivateAll.h" #include "MessageActivateFullTextSearch.h" #include "MessageActivateTokens.h" +#include "MessageActivateTrail.h" #include "MessageFind.h" #include "MessageSearchAutocomplete.h" @@ -21,6 +22,7 @@ class SearchController , public MessageListener , public MessageListener , public MessageListener + , public MessageListener , public MessageListener , public MessageListener { @@ -36,6 +38,7 @@ private: void handleMessage(MessageActivateFullTextSearch* message) override; void handleMessage(MessageActivateLegend* message) override; void handleMessage(MessageActivateTokens* message) override; + void handleMessage(MessageActivateTrail* message) override; void handleMessage(MessageFind* message) override; void handleMessage(MessageSearchAutocomplete* message) override; diff --git a/src/lib/component/controller/UndoRedoController.cpp b/src/lib/component/controller/UndoRedoController.cpp index 481a04c1..d36988b5 100644 --- a/src/lib/component/controller/UndoRedoController.cpp +++ b/src/lib/component/controller/UndoRedoController.cpp @@ -132,7 +132,10 @@ void UndoRedoController::handleMessage(MessageActivateTrail* message) return; } - Command command(std::make_shared(*message), Command::ORDER_ADAPT, true); + Command command( + std::make_shared(*message), + message->custom ? Command::ORDER_ACTIVATE : Command::ORDER_ADAPT + ); processCommand(command); } diff --git a/src/lib/component/view/CustomTrailView.h b/src/lib/component/view/CustomTrailView.h new file mode 100644 index 00000000..0c2ee72b --- /dev/null +++ b/src/lib/component/view/CustomTrailView.h @@ -0,0 +1,29 @@ +#ifndef CUSTOM_TRAIL_VIEW_H +#define CUSTOM_TRAIL_VIEW_H + +#include "SearchMatch.h" +#include "View.h" + +class CustomTrailView + : public View +{ +public: + CustomTrailView(ViewLayout*) + : View(nullptr) + { + } + + virtual std::string getName() const + { + return "custom trail"; + } + + virtual void clearView() = 0; + virtual void setAvailableNodeAndEdgeTypes(NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes) = 0; + + virtual void showView() = 0; + virtual void hideView() = 0; + virtual void showAutocompletions(const std::vector& autocompletions, bool from) = 0; +}; + +#endif // CUSTOM_TRAIL_VIEW_H diff --git a/src/lib/component/view/ViewFactory.h b/src/lib/component/view/ViewFactory.h index 3643ae55..a1916cb9 100644 --- a/src/lib/component/view/ViewFactory.h +++ b/src/lib/component/view/ViewFactory.h @@ -9,6 +9,7 @@ class BookmarkButtonsView; class BookmarkView; class CodeView; +class CustomTrailView; class ErrorView; class GraphView; class GraphViewStyleImpl; @@ -38,6 +39,7 @@ public: virtual std::shared_ptr createBookmarkButtonsView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createBookmarkView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createCodeView(ViewLayout* viewLayout) const = 0; + virtual std::shared_ptr createCustomTrailView() const = 0; virtual std::shared_ptr createErrorView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createGraphView(ViewLayout* viewLayout) const = 0; virtual std::shared_ptr createRefreshView(ViewLayout* viewLayout) const = 0; diff --git a/src/lib/data/NodeType.cpp b/src/lib/data/NodeType.cpp index 6eeea415..7d3fbca4 100644 --- a/src/lib/data/NodeType.cpp +++ b/src/lib/data/NodeType.cpp @@ -139,7 +139,7 @@ std::wstring NodeType::getReadableTypeWString(NodeType::Type type) return std::wstring(str.begin(), str.end()); } -NodeType::Type NodeType::getTypeForReadableTypeString(const std::wstring str) +NodeType::Type NodeType::getTypeForReadableTypeString(const std::wstring& str) { for (NodeType::TypeMask mask = 1; mask <= NodeType::NODE_MAX_VALUE; mask *= 2) { diff --git a/src/lib/data/NodeType.h b/src/lib/data/NodeType.h index 2989a16b..aede7291 100644 --- a/src/lib/data/NodeType.h +++ b/src/lib/data/NodeType.h @@ -84,7 +84,7 @@ public: static std::string getReadableTypeString(NodeType::Type type); static std::wstring getReadableTypeWString(NodeType::Type type); - static NodeType::Type getTypeForReadableTypeString(const std::wstring str); + static NodeType::Type getTypeForReadableTypeString(const std::wstring& str); NodeType(Type type); diff --git a/src/lib/data/graph/Edge.cpp b/src/lib/data/graph/Edge.cpp index 4ffdf1be..9c99e005 100644 --- a/src/lib/data/graph/Edge.cpp +++ b/src/lib/data/graph/Edge.cpp @@ -168,6 +168,20 @@ std::wstring Edge::getReadableTypeString(EdgeType type) return L""; } +Edge::EdgeType Edge::getTypeForReadableTypeString(const std::wstring& str) +{ + for (TypeMask mask = 1; mask <= EDGE_MAX_VALUE; mask *= 2) + { + EdgeType type = intToType(mask); + if (getReadableTypeString(type) == str) + { + return type; + } + } + + return EDGE_UNDEFINED; +} + std::wstring Edge::getReadableTypeString() const { return getReadableTypeString(m_type); diff --git a/src/lib/data/graph/Edge.h b/src/lib/data/graph/Edge.h index ca9fd312..15e9c742 100644 --- a/src/lib/data/graph/Edge.h +++ b/src/lib/data/graph/Edge.h @@ -31,7 +31,9 @@ public: EDGE_IMPORT = 1 << 12, EDGE_AGGREGATION = 1 << 13, EDGE_MACRO_USAGE = 1 << 14, - EDGE_ANNOTATION_USAGE = 1 << 15 + EDGE_ANNOTATION_USAGE = 1 << 15, + + EDGE_MAX_VALUE = EDGE_ANNOTATION_USAGE }; static int typeToInt(EdgeType type); @@ -58,6 +60,8 @@ public: static std::wstring getUnderscoredTypeString(EdgeType type); static std::wstring getReadableTypeString(EdgeType type); + static EdgeType getTypeForReadableTypeString(const std::wstring& str); + // Logging. virtual std::wstring getReadableTypeString() const override; std::wstring getAsString() const; diff --git a/src/lib/data/storage/PersistentStorage.cpp b/src/lib/data/storage/PersistentStorage.cpp index e8786133..7f3ed5b5 100644 --- a/src/lib/data/storage/PersistentStorage.cpp +++ b/src/lib/data/storage/PersistentStorage.cpp @@ -670,7 +670,7 @@ std::shared_ptr PersistentStorage::getFullTextSearchLo return collection; } -std::vector PersistentStorage::getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes) const +std::vector PersistentStorage::getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes, bool acceptCommands) const { TRACE(); @@ -692,7 +692,10 @@ std::vector PersistentStorage::getAutocompletionMatches(const std:: utility::append(matches, getAutocompletionFileMatches(query, maxResultsCount)); } - utility::append(matches, getAutocompletionCommandMatches(query, acceptedNodeTypes)); + if (acceptCommands) + { + utility::append(matches, getAutocompletionCommandMatches(query, acceptedNodeTypes)); + } // Rescore search matches to check if better score is achieved with higher indices std::vector rescoredMatches; @@ -1233,19 +1236,37 @@ std::shared_ptr PersistentStorage::getGraphForChildrenOfNodeId(Id nodeId) } std::shared_ptr PersistentStorage::getGraphForTrail( - Id originId, Id targetId, Edge::TypeMask trailType, size_t depth) const + Id originId, Id targetId, NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes, bool nodeNonIndexed, + size_t depth, bool directed) const { TRACE(); std::set nodeIds; std::set edgeIds; - std::vector nodeIdsToProcess; - nodeIdsToProcess.push_back(originId ? originId : targetId); + nodeIds.insert(originId ? originId : targetId); bool forward = originId; size_t currentDepth = 0; - nodeIds.insert(nodeIdsToProcess.back()); + std::vector nodeIdsToProcess = { *nodeIds.begin() }; + + struct TrailNode + { + Id id = 0; + std::set parents; + std::set edgeIds; + }; + + bool isTerminatedTrail = originId && targetId; + std::map trailNodes; + + if (isTerminatedTrail) + { + TrailNode root; + root.id = originId; + trailNodes.emplace(originId, root); + } + while (nodeIdsToProcess.size() && (!depth || currentDepth < depth)) { std::vector edges = @@ -1253,7 +1274,7 @@ std::shared_ptr PersistentStorage::getGraphForTrail( m_sqliteIndexStorage.getEdgesBySourceIds(nodeIdsToProcess) : m_sqliteIndexStorage.getEdgesByTargetIds(nodeIdsToProcess); - if (trailType & Edge::LAYOUT_VERTICAL) + if (!directed || edgeTypes & Edge::LAYOUT_VERTICAL) { utility::append(edges, forward ? @@ -1262,33 +1283,165 @@ std::shared_ptr PersistentStorage::getGraphForTrail( ); } - nodeIdsToProcess.clear(); + std::vector nodeIdsToCheck; + std::map> edgesToInsert; for (const StorageEdge& edge : edges) { - if (Edge::intToType(edge.type) & trailType) + if (Edge::intToType(edge.type) & edgeTypes && + edgeIds.find(edge.id) == edgeIds.end()) { bool isForward = forward == !(Edge::intToType(edge.type) & Edge::LAYOUT_VERTICAL); - const Id nodeId = isForward ? edge.targetNodeId : edge.sourceNodeId; - const Id otherNodeId = isForward ? edge.sourceNodeId : edge.targetNodeId; + const Id targetNodeId = isForward ? edge.targetNodeId : edge.sourceNodeId; + const Id sourceNodeId = isForward ? edge.sourceNodeId : edge.targetNodeId; - if (nodeIds.find(nodeId) == nodeIds.end()) + if (nodeIds.find(targetNodeId) == nodeIds.end()) { - nodeIdsToProcess.push_back(nodeId); - nodeIds.insert(nodeId); - edgeIds.insert(edge.id); + nodeIdsToCheck.push_back(targetNodeId); + edgesToInsert[targetNodeId].push_back(edge); } - else if (nodeIds.find(otherNodeId) != nodeIds.end()) + else if (nodeIds.find(sourceNodeId) == nodeIds.end()) + { + if (!directed) + { + nodeIdsToCheck.push_back(sourceNodeId); + edgesToInsert[sourceNodeId].push_back(edge); + } + } + else + { + edgeIds.insert(edge.id); + + if (isTerminatedTrail) + { + TrailNode& target = trailNodes[targetNodeId]; + TrailNode& origin = trailNodes[sourceNodeId]; + target.id = targetNodeId; + origin.id = sourceNodeId; + target.parents.insert(&origin); + target.edgeIds.insert(edge.id); + } + } + } + } + + nodeIdsToProcess.clear(); + + if (nodeTypes != 0) + { + for (const StorageNode& node : m_sqliteIndexStorage.getAllByIds(nodeIdsToCheck)) + { + NodeType::Type type = NodeType::intToType(node.type); + if (type & nodeTypes || (type == NodeType::NODE_SYMBOL && nodeNonIndexed)) + { + if (!nodeNonIndexed) + { + if (type == NodeType::NODE_FILE) + { + auto it = m_fileNodeIndexed.find(node.id); + if (it == m_fileNodeIndexed.end() || !it->second) + { + continue; + } + } + else + { + auto it = m_symbolDefinitionKinds.find(node.id); + if (it == m_symbolDefinitionKinds.end() || it->second == DEFINITION_NONE) + { + continue; + } + } + } + + // FIXME: don't add namespace nodes to the graph, because it destroys trail layouting + // Remove when namespaces are proper nodes with children + if ((type & (NodeType::NODE_MODULE | NodeType::NODE_NAMESPACE | NodeType::NODE_PACKAGE)) == 0) + { + nodeIds.insert(node.id); + for (const StorageEdge& edge : edgesToInsert[node.id]) + { + if ((Edge::intToType(edge.type) & Edge::EDGE_MEMBER) == 0) + { + edgeIds.insert(edge.id); + } + } + } + nodeIdsToProcess.push_back(node.id); + + if (isTerminatedTrail) + { + TrailNode& targetNode = trailNodes[node.id]; + targetNode.id = node.id; + + for (const StorageEdge& edge : edgesToInsert[node.id]) + { + targetNode.edgeIds.insert(edge.id); + + Id sourceNodeId = (edge.targetNodeId == node.id ? edge.sourceNodeId : edge.targetNodeId); + TrailNode& oldNode = trailNodes[sourceNodeId]; + targetNode.parents.insert(&oldNode); + } + } + } + } + } + else + { + for (const Id nodeId : nodeIdsToCheck) + { + nodeIds.insert(nodeId); + nodeIdsToProcess.push_back(nodeId); + + for (const StorageEdge& edge : edgesToInsert[nodeId]) { edgeIds.insert(edge.id); } } } + edgesToInsert.clear(); + currentDepth++; } + if (isTerminatedTrail) + { + nodeIds.clear(); + edgeIds.clear(); + + if (trailNodes.find(targetId) != trailNodes.end()) + { + std::queue nodesToProcess; + nodesToProcess.push(&trailNodes[targetId]); + + while (nodesToProcess.size()) + { + TrailNode* currentNode = nodesToProcess.front(); + nodesToProcess.pop(); + + if (nodeIds.find(currentNode->id) != nodeIds.end()) + { + continue; + } + + nodeIds.insert(currentNode->id); + edgeIds.insert(currentNode->edgeIds.begin(), currentNode->edgeIds.end()); + + for (TrailNode* parent : currentNode->parents) + { + nodesToProcess.push(parent); + } + } + } + else + { + nodeIds.insert(originId); + nodeIds.insert(targetId); + } + } + std::shared_ptr graph = std::make_shared(); addNodesWithParentsAndEdgesToGraph(utility::toVector(nodeIds), utility::toVector(edgeIds), graph.get(), false); @@ -1298,6 +1451,30 @@ std::shared_ptr PersistentStorage::getGraphForTrail( return graph; } +NodeType::TypeMask PersistentStorage::getAvailableNodeTypes() const +{ + TRACE(); + + NodeType::TypeMask mask = 0; + for (int type : m_sqliteIndexStorage.getAvailableNodeTypes()) + { + mask |= NodeType::intToType(type); + } + return mask; +} + +Edge::TypeMask PersistentStorage::getAvailableEdgeTypes() const +{ + TRACE(); + + Edge::TypeMask mask = 0; + for (int type : m_sqliteIndexStorage.getAvailableEdgeTypes()) + { + mask |= Edge::intToType(type); + } + return mask; +} + // TODO: rename: getActiveElementIdsForId; TODO: make separate function for declarationId std::vector PersistentStorage::getActiveTokenIdsForId(Id tokenId, Id* declarationId) const { diff --git a/src/lib/data/storage/PersistentStorage.h b/src/lib/data/storage/PersistentStorage.h index b8b88889..738659a2 100644 --- a/src/lib/data/storage/PersistentStorage.h +++ b/src/lib/data/storage/PersistentStorage.h @@ -104,7 +104,7 @@ public: std::shared_ptr getFullTextSearchLocations( const std::wstring& searchTerm, bool caseSensitive) const override; - std::vector getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes) const override; + std::vector getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes, bool acceptCommands) const override; std::vector getAutocompletionSymbolMatches( const std::wstring& query, const NodeTypeSet& acceptedNodeTypes, size_t maxResultsCount, size_t maxBestScoredResultsLength) const; std::vector getAutocompletionFileMatches(const std::wstring& query, size_t maxResultsCount) const; @@ -116,7 +116,11 @@ public: std::shared_ptr getGraphForActiveTokenIds( const std::vector& tokenIds, const std::vector& expandedNodeIds, bool* isActiveNamespace = nullptr) const override; std::shared_ptr getGraphForChildrenOfNodeId(Id nodeId) const override; - std::shared_ptr getGraphForTrail(Id originId, Id targetId, Edge::TypeMask trailType, size_t depth) const override; + std::shared_ptr getGraphForTrail( + Id originId, Id targetId, NodeType::TypeMask nodeTypes, Edge::TypeMask trailType, bool nodeNonIndexed, size_t depth, bool directed) const override; + + NodeType::TypeMask getAvailableNodeTypes() const override; + Edge::TypeMask getAvailableEdgeTypes() const override; std::vector getActiveTokenIdsForId(Id tokenId, Id* declarationId) const override; std::vector getNodeIdsForLocationIds(const std::vector& locationIds) const override; diff --git a/src/lib/data/storage/StorageAccess.h b/src/lib/data/storage/StorageAccess.h index 83206f36..1efb8863 100644 --- a/src/lib/data/storage/StorageAccess.h +++ b/src/lib/data/storage/StorageAccess.h @@ -49,7 +49,7 @@ public: virtual std::shared_ptr getFullTextSearchLocations( const std::wstring& searchTerm, bool caseSensitive) const = 0; - virtual std::vector getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes) const = 0; + virtual std::vector getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes, bool acceptCommands) const = 0; virtual std::vector getSearchMatchesForTokenIds(const std::vector& tokenIds) const = 0; virtual std::shared_ptr getGraphForAll() const = 0; @@ -57,7 +57,10 @@ public: virtual std::shared_ptr getGraphForActiveTokenIds( const std::vector& tokenIds, const std::vector& expandedNodeIds, bool* isActiveNamespace = nullptr) const = 0; virtual std::shared_ptr getGraphForChildrenOfNodeId(Id nodeId) const = 0; - virtual std::shared_ptr getGraphForTrail(Id originId, Id targetId, Edge::TypeMask trailType, size_t depth) const = 0; + virtual std::shared_ptr getGraphForTrail(Id originId, Id targetId, NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes, bool nodeNonIndexed, size_t depth, bool directed) const = 0; + + virtual NodeType::TypeMask getAvailableNodeTypes() const = 0; + virtual Edge::TypeMask getAvailableEdgeTypes() const = 0; virtual std::vector getActiveTokenIdsForId(Id tokenId, Id* declarationId) const = 0; virtual std::vector getNodeIdsForLocationIds(const std::vector& locationIds) const = 0; diff --git a/src/lib/data/storage/StorageAccessProxy.cpp b/src/lib/data/storage/StorageAccessProxy.cpp index ebdac858..33747ec9 100644 --- a/src/lib/data/storage/StorageAccessProxy.cpp +++ b/src/lib/data/storage/StorageAccessProxy.cpp @@ -66,6 +66,35 @@ void StorageAccessProxy::setSubject(std::weak_ptr subject) return _DEFAULT_VALUE_; \ } +#define DEF_GETTER_5(_METHOD_NAME_, _PARAM_1_TYPE_, _PARAM_2_TYPE_, _PARAM_3_TYPE_, _PARAM_4_TYPE_, _PARAM_5_TYPE_, _RETURN_TYPE_, _DEFAULT_VALUE_) \ + UNWRAP(_RETURN_TYPE_) StorageAccessProxy::_METHOD_NAME_(_PARAM_1_TYPE_ p1, _PARAM_2_TYPE_ p2, _PARAM_3_TYPE_ p3, _PARAM_4_TYPE_ p4, _PARAM_5_TYPE_ p5) const \ + { \ + if (std::shared_ptr subject = m_subject.lock()) \ + { \ + return subject->_METHOD_NAME_(p1, p2, p3, p4, p5); \ + } \ + return _DEFAULT_VALUE_; \ + } + +#define DEF_GETTER_6(_METHOD_NAME_, _PARAM_1_TYPE_, _PARAM_2_TYPE_, _PARAM_3_TYPE_, _PARAM_4_TYPE_, _PARAM_5_TYPE_, _PARAM_6_TYPE_, _RETURN_TYPE_, _DEFAULT_VALUE_) \ + UNWRAP(_RETURN_TYPE_) StorageAccessProxy::_METHOD_NAME_(_PARAM_1_TYPE_ p1, _PARAM_2_TYPE_ p2, _PARAM_3_TYPE_ p3, _PARAM_4_TYPE_ p4, _PARAM_5_TYPE_ p5, _PARAM_6_TYPE_ p6) const \ + { \ + if (std::shared_ptr subject = m_subject.lock()) \ + { \ + return subject->_METHOD_NAME_(p1, p2, p3, p4, p5, p6); \ + } \ + return _DEFAULT_VALUE_; \ + } + +#define DEF_GETTER_7(_METHOD_NAME_, _PARAM_1_TYPE_, _PARAM_2_TYPE_, _PARAM_3_TYPE_, _PARAM_4_TYPE_, _PARAM_5_TYPE_, _PARAM_6_TYPE_, _PARAM_7_TYPE_, _RETURN_TYPE_, _DEFAULT_VALUE_) \ + UNWRAP(_RETURN_TYPE_) StorageAccessProxy::_METHOD_NAME_(_PARAM_1_TYPE_ p1, _PARAM_2_TYPE_ p2, _PARAM_3_TYPE_ p3, _PARAM_4_TYPE_ p4, _PARAM_5_TYPE_ p5, _PARAM_6_TYPE_ p6, _PARAM_7_TYPE_ p7) const \ + { \ + if (std::shared_ptr subject = m_subject.lock()) \ + { \ + return subject->_METHOD_NAME_(p1, p2, p3, p4, p5, p6, p7); \ + } \ + return _DEFAULT_VALUE_; \ + } DEF_GETTER_1(getNodeIdForFileNode, const FilePath&, Id, 0) DEF_GETTER_1(getNodeIdForNameHierarchy, const NameHierarchy&, Id, 0) @@ -79,13 +108,15 @@ DEF_GETTER_1(getNodeIdToParentFileMap, const std::vector&, NodeIdToParentFil DEF_GETTER_1(getNodeTypeForNodeWithId, Id, NodeType, NodeType(NodeType::NODE_SYMBOL)) DEF_GETTER_1(getEdgeById, Id, StorageEdge, StorageEdge()) DEF_GETTER_2(getFullTextSearchLocations, const std::wstring &, bool, std::shared_ptr, std::make_shared()) -DEF_GETTER_2(getAutocompletionMatches, const std::wstring &, NodeTypeSet, std::vector, std::vector()) +DEF_GETTER_3(getAutocompletionMatches, const std::wstring &, NodeTypeSet, bool, std::vector, std::vector()) DEF_GETTER_1(getSearchMatchesForTokenIds, const std::vector&, std::vector, std::vector()) DEF_GETTER_0(getGraphForAll, std::shared_ptr, std::make_shared()) DEF_GETTER_1(getGraphForNodeTypes, NodeTypeSet, std::shared_ptr, std::make_shared()) DEF_GETTER_3(getGraphForActiveTokenIds, const std::vector&, const std::vector&, bool*, std::shared_ptr, std::make_shared()) DEF_GETTER_1(getGraphForChildrenOfNodeId, Id, std::shared_ptr, std::make_shared()) -DEF_GETTER_4(getGraphForTrail, Id, Id, Edge::TypeMask, size_t, std::shared_ptr, std::make_shared()) +DEF_GETTER_7(getGraphForTrail, Id, Id, NodeType::TypeMask, Edge::TypeMask, bool, size_t, bool, std::shared_ptr, std::make_shared()) +DEF_GETTER_0(getAvailableNodeTypes, NodeType::TypeMask, 0); +DEF_GETTER_0(getAvailableEdgeTypes, Edge::TypeMask, 0); DEF_GETTER_2(getActiveTokenIdsForId, Id, Id*, std::vector, {}) DEF_GETTER_1(getNodeIdsForLocationIds, const std::vector&, std::vector, {}) DEF_GETTER_1(getSourceLocationsForTokenIds, const std::vector&, std::shared_ptr, std::make_shared()) diff --git a/src/lib/data/storage/StorageAccessProxy.h b/src/lib/data/storage/StorageAccessProxy.h index 84e4213e..7d838de4 100644 --- a/src/lib/data/storage/StorageAccessProxy.h +++ b/src/lib/data/storage/StorageAccessProxy.h @@ -28,7 +28,7 @@ public: std::shared_ptr getFullTextSearchLocations( const std::wstring& searchTerm, bool caseSensitive) const override; - std::vector getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes) const override; + std::vector getAutocompletionMatches(const std::wstring& query, NodeTypeSet acceptedNodeTypes, bool acceptCommands) const override; std::vector getSearchMatchesForTokenIds(const std::vector& tokenIds) const override; std::shared_ptr getGraphForAll() const override; @@ -36,7 +36,10 @@ public: std::shared_ptr getGraphForActiveTokenIds( const std::vector& tokenIds, const std::vector& expandedNodeIds, bool* isActiveNamespace = nullptr) const override; std::shared_ptr getGraphForChildrenOfNodeId(Id nodeId) const override; - std::shared_ptr getGraphForTrail(Id originId, Id targetId, Edge::TypeMask trailType, size_t depth) const override; + std::shared_ptr getGraphForTrail(Id originId, Id targetId, NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes, bool nodeNonIndexed, size_t depth, bool directed) const override; + + NodeType::TypeMask getAvailableNodeTypes() const override; + Edge::TypeMask getAvailableEdgeTypes() const override; std::vector getActiveTokenIdsForId(Id tokenId, Id* declarationId) const override; std::vector getNodeIdsForLocationIds(const std::vector& locationIds) const override; diff --git a/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp b/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp index a4283cde..de073318 100644 --- a/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp +++ b/src/lib/data/storage/sqlite/SqliteIndexStorage.cpp @@ -758,6 +758,46 @@ StorageNode SqliteIndexStorage::getNodeBySerializedName(const std::wstring& seri return StorageNode(); } +std::vector SqliteIndexStorage::getAvailableNodeTypes() const +{ + CppSQLite3Query q = executeQuery("SELECT DISTINCT type FROM node;"); + + std::vector types; + + while (!q.eof()) + { + const int type = q.getIntField(0, -1); + if (type != -1) + { + types.push_back(type); + } + + q.nextRow(); + } + + return types; +} + +std::vector SqliteIndexStorage::getAvailableEdgeTypes() const +{ + CppSQLite3Query q = executeQuery("SELECT DISTINCT type FROM edge;"); + + std::vector types; + + while (!q.eof()) + { + const int type = q.getIntField(0, -1); + if (type != -1) + { + types.push_back(type); + } + + q.nextRow(); + } + + return types; +} + StorageFile SqliteIndexStorage::getFileByPath(const std::wstring& filePath) const { return doGetFirst("WHERE file.path == '" + utility::encodeToUtf8(filePath) + "'"); diff --git a/src/lib/data/storage/sqlite/SqliteIndexStorage.h b/src/lib/data/storage/sqlite/SqliteIndexStorage.h index 8b5c7053..681cb1d5 100644 --- a/src/lib/data/storage/sqlite/SqliteIndexStorage.h +++ b/src/lib/data/storage/sqlite/SqliteIndexStorage.h @@ -98,6 +98,9 @@ public: StorageNode getNodeById(Id id) const; StorageNode getNodeBySerializedName(const std::wstring& serializedName) const; + std::vector getAvailableNodeTypes() const; + std::vector getAvailableEdgeTypes() const; + StorageFile getFileByPath(const std::wstring& filePath) const; std::vector getFilesByPaths(const std::vector& filePaths) const; diff --git a/src/lib/utility/messaging/type/MessageActivateTrail.h b/src/lib/utility/messaging/type/MessageActivateTrail.h index 06bd7db9..2c0eabfc 100644 --- a/src/lib/utility/messaging/type/MessageActivateTrail.h +++ b/src/lib/utility/messaging/type/MessageActivateTrail.h @@ -2,19 +2,48 @@ #define MESSAGE_ACTIVATE_TRAIL_H #include "Message.h" -#include "types.h" +#include "MessageActivateBase.h" +#include "NodeType.h" #include "TabId.h" +#include "types.h" class MessageActivateTrail : public Message + , public MessageActivateBase { public: - MessageActivateTrail(Id originId, Id targetId, Edge::TypeMask trailType, size_t depth, bool horizontalLayout) + MessageActivateTrail( + Id originId, Id targetId, Edge::TypeMask edgeTypes, size_t depth, bool horizontalLayout + ) : originId(originId) , targetId(targetId) - , trailType(trailType) + , nodeTypes(0) + , edgeTypes(edgeTypes) + , nodeNonIndexed(false) , depth(depth) , horizontalLayout(horizontalLayout) + , custom(false) + { + setSchedulerId(TabId::currentTab()); + } + + MessageActivateTrail( + Id originId, + Id targetId, + NodeType::TypeMask nodeTypes, + Edge::TypeMask edgeTypes, + bool nodeNonIndexed, + size_t depth, + bool horizontalLayout + ) + : originId(originId) + , targetId(targetId) + , nodeTypes(nodeTypes) + , edgeTypes(edgeTypes) + , nodeNonIndexed(nodeNonIndexed) + , depth(depth) + , horizontalLayout(horizontalLayout) + , custom(true) { setSchedulerId(TabId::currentTab()); } @@ -24,11 +53,21 @@ public: return "MessageActivateTrail"; } + std::vector getSearchMatches() const override + { + return searchMatches; + } + + std::vector searchMatches; + const Id originId; const Id targetId; - const Edge::TypeMask trailType; + const NodeType::TypeMask nodeTypes; + const Edge::TypeMask edgeTypes; + const bool nodeNonIndexed; const size_t depth; const bool horizontalLayout; + const bool custom; }; #endif // MESSAGE_ACTIVATE_TRAIL_H diff --git a/src/lib/utility/messaging/type/MessageActivateFullTextSearch.h b/src/lib/utility/messaging/type/activation/MessageActivateFullTextSearch.h similarity index 100% rename from src/lib/utility/messaging/type/MessageActivateFullTextSearch.h rename to src/lib/utility/messaging/type/activation/MessageActivateFullTextSearch.h diff --git a/src/lib/utility/messaging/type/custom_trail/MessageCustomTrailShow.h b/src/lib/utility/messaging/type/custom_trail/MessageCustomTrailShow.h new file mode 100644 index 00000000..9b08e1b5 --- /dev/null +++ b/src/lib/utility/messaging/type/custom_trail/MessageCustomTrailShow.h @@ -0,0 +1,20 @@ +#ifndef MESSAGE_CUSTOM_TRAIL_SHOW_H +#define MESSAGE_CUSTOM_TRAIL_SHOW_H + +#include "Message.h" + +class MessageCustomTrailShow + : public Message +{ +public: + MessageCustomTrailShow() + { + } + + static const std::string getStaticType() + { + return "MessageCustomTrailShow"; + } +}; + +#endif // MESSAGE_CUSTOM_TRAIL_SHOW_H diff --git a/src/lib/utility/messaging/type/MessageSearch.h b/src/lib/utility/messaging/type/search/MessageSearch.h similarity index 100% rename from src/lib/utility/messaging/type/MessageSearch.h rename to src/lib/utility/messaging/type/search/MessageSearch.h diff --git a/src/lib/utility/messaging/type/MessageSearchAutocomplete.h b/src/lib/utility/messaging/type/search/MessageSearchAutocomplete.h similarity index 100% rename from src/lib/utility/messaging/type/MessageSearchAutocomplete.h rename to src/lib/utility/messaging/type/search/MessageSearchAutocomplete.h diff --git a/src/lib_gui/CMakeLists.txt b/src/lib_gui/CMakeLists.txt index a9c172db..1762330a 100644 --- a/src/lib_gui/CMakeLists.txt +++ b/src/lib_gui/CMakeLists.txt @@ -246,6 +246,8 @@ add_files( qt/view/QtCodeView.h qt/view/QtCompositeView.cpp qt/view/QtCompositeView.h + qt/view/QtCustomTrailView.cpp + qt/view/QtCustomTrailView.h qt/view/QtDialogView.cpp qt/view/QtDialogView.h qt/view/QtErrorView.cpp diff --git a/src/lib_gui/qt/element/search/QtSearchBar.cpp b/src/lib_gui/qt/element/search/QtSearchBar.cpp index b186f49a..70183780 100644 --- a/src/lib_gui/qt/element/search/QtSearchBar.cpp +++ b/src/lib_gui/qt/element/search/QtSearchBar.cpp @@ -3,10 +3,12 @@ #include #include "MessageActivateAll.h" -#include "ResourcePaths.h" - +#include "MessageActivateFullTextSearch.h" +#include "MessageSearch.h" +#include "MessageSearchAutocomplete.h" #include "QtSearchBarButton.h" #include "QtSmartSearchBox.h" +#include "ResourcePaths.h" QtSearchBar::QtSearchBar() { @@ -33,19 +35,21 @@ QtSearchBar::QtSearchBar() innerLayout->setContentsMargins(12, 3, 5, 2); m_searchBoxContainer->setLayout(innerLayout); - m_searchBox = new QtSmartSearchBox(m_searchBoxContainer); - m_searchBox->setObjectName("search_box"); - m_searchBox->setAttribute(Qt::WA_MacShowFocusRect, 0); // remove blue focus box on Mac + m_searchBox = new QtSmartSearchBox("Search", true, m_searchBoxContainer); m_searchBox->setMinimumWidth(100); m_searchBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); innerLayout->addWidget(m_searchBox); + connect(m_searchBox, &QtSmartSearchBox::autocomplete, this, &QtSearchBar::requestAutocomplete); + connect(m_searchBox, &QtSmartSearchBox::search, this, &QtSearchBar::requestSearch); + connect(m_searchBox, &QtSmartSearchBox::fullTextSearch, this, &QtSearchBar::requestFullTextSearch); + m_searchButton = new QtSearchBarButton(ResourcePaths::getGuiPath().concatenate(L"search_view/images/search.png")); m_searchButton->setObjectName("search_button"); m_searchButton->setToolTip("search"); layout->addWidget(m_searchButton); - connect(m_searchButton, &QPushButton::clicked, m_searchBox, &QtSmartSearchBox::search); + connect(m_searchButton, &QPushButton::clicked, m_searchBox, &QtSmartSearchBox::startSearch); refreshStyle(); } @@ -98,3 +102,18 @@ void QtSearchBar::homeButtonClicked() { MessageActivateAll().dispatch(); } + +void QtSearchBar::requestAutocomplete(const std::wstring& query, NodeTypeSet acceptedNodeTypes) +{ + MessageSearchAutocomplete(query, acceptedNodeTypes).dispatch(); +} + +void QtSearchBar::requestSearch(const std::vector& matches, NodeTypeSet acceptedNodeTypes) +{ + MessageSearch(matches, acceptedNodeTypes).dispatch(); +} + +void QtSearchBar::requestFullTextSearch(const std::wstring& query, bool caseSensitive) +{ + MessageActivateFullTextSearch(query, caseSensitive).dispatch(); +} diff --git a/src/lib_gui/qt/element/search/QtSearchBar.h b/src/lib_gui/qt/element/search/QtSearchBar.h index 9d278a02..d8b9ded3 100644 --- a/src/lib_gui/qt/element/search/QtSearchBar.h +++ b/src/lib_gui/qt/element/search/QtSearchBar.h @@ -36,6 +36,10 @@ public: private slots: void homeButtonClicked(); + void requestAutocomplete(const std::wstring& query, NodeTypeSet acceptedNodeTypes); + void requestSearch(const std::vector& matches, NodeTypeSet acceptedNodeTypes); + void requestFullTextSearch(const std::wstring& query, bool caseSensitive); + private: QWidget* m_searchBoxContainer; // used for correct clipping inside the search box diff --git a/src/lib_gui/qt/element/search/QtSmartSearchBox.cpp b/src/lib_gui/qt/element/search/QtSmartSearchBox.cpp index a6324e04..b9dabb33 100644 --- a/src/lib_gui/qt/element/search/QtSmartSearchBox.cpp +++ b/src/lib_gui/qt/element/search/QtSmartSearchBox.cpp @@ -11,9 +11,6 @@ #include "GraphViewStyle.h" #include "NodeTypeSet.h" #include "ColorScheme.h" -#include "MessageActivateFullTextSearch.h" -#include "MessageSearch.h" -#include "MessageSearchAutocomplete.h" #include "utility.h" #include "utilityString.h" @@ -30,7 +27,7 @@ void QtSearchElement::onChecked(bool) emit wasChecked(this); } -void QtSmartSearchBox::search() +void QtSmartSearchBox::startSearch() { editTextToElement(); @@ -53,7 +50,7 @@ void QtSmartSearchBox::search() else { QString text = QString::fromStdWString(match.name); - if (!text.startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER)) + if (m_supportsFullTextSearch && !text.startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER)) { text = QChar(SearchMatch::FULLTEXT_SEARCH_CHARACTER) + text; } @@ -62,46 +59,28 @@ void QtSmartSearchBox::search() updateElements(); setEditText(text); - fullTextSearch(); + startFullTextSearch(); return; } } } - MessageSearch(utility::toVector(m_matches), getMatchAcceptedNodeTypes()).dispatch(); + emit search(utility::toVector(m_matches), getMatchAcceptedNodeTypes()); } -void QtSmartSearchBox::fullTextSearch() -{ - std::wstring term = text().toStdWString().substr(1); - if (term.empty()) - { - return; - } - - bool caseSensitive = false; - if (term.at(0) == SearchMatch::FULLTEXT_SEARCH_CHARACTER) - { - term = term.substr(1); - if (term.empty()) - { - return; - } - - caseSensitive = true; - } - - MessageActivateFullTextSearch(term, caseSensitive).dispatch(); -} - -QtSmartSearchBox::QtSmartSearchBox(QWidget* parent) +QtSmartSearchBox::QtSmartSearchBox(const QString& placeholder, bool supportsFullTextSearch, QWidget* parent) : QLineEdit(parent) + , m_placeholder(placeholder) + , m_supportsFullTextSearch(supportsFullTextSearch) , m_allowTextChange(false) , m_cursorIndex(0) , m_shiftKeyDown(false) , m_mousePressed(false) , m_ignoreNextMousePress(false) { + setObjectName("search_box"); + setAttribute(Qt::WA_MacShowFocusRect, 0); // remove blue focus box on Mac + m_highlightRect = new QWidget(this); m_highlightRect->setGeometry(0, 0, 0, 0); m_highlightRect->setObjectName("search_box_highlight"); @@ -124,6 +103,11 @@ QCompleter* QtSmartSearchBox::getCompleter() const return m_completer; } +std::vector QtSmartSearchBox::getMatches() const +{ + return utility::toVector(m_matches); +} + void QtSmartSearchBox::setAutocompletionList(const std::vector& autocompletionList) { // Save the cursor position, because after activating the completer the cursor gets set to the end position. @@ -208,8 +192,8 @@ bool QtSmartSearchBox::event(QEvent *event) setEditText(QString::fromStdWString(m_highlightedMatch.getFullName())); requestAutoCompletions(); } + return true; } - return true; } else if (keyEvent->key() == Qt::Key_Escape) { @@ -271,12 +255,12 @@ void QtSmartSearchBox::keyPressEvent(QKeyEvent* event) { if (text().startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER)) { - fullTextSearch(); + startFullTextSearch(); return; } else if (!m_completer->popup()->isVisible()) { - search(); + startSearch(); } } else if (event->key() == Qt::Key_Backspace) @@ -646,7 +630,7 @@ void QtSmartSearchBox::onAutocompletionActivated(const SearchMatch& match) if (!match.name.empty()) { - search(); + startSearch(); } } @@ -1034,7 +1018,7 @@ void QtSmartSearchBox::updatePlaceholder() { if (text().isEmpty() && m_elements.empty()) { - setPlaceholderText("Search"); + setPlaceholderText(m_placeholder); } else { @@ -1052,7 +1036,7 @@ void QtSmartSearchBox::requestAutoCompletions() { if (!text().isEmpty() && !text().startsWith(SearchMatch::FULLTEXT_SEARCH_CHARACTER)) { - MessageSearchAutocomplete(text().toStdWString(), getMatchAcceptedNodeTypes()).dispatch(); + emit autocomplete(text().toStdWString(), getMatchAcceptedNodeTypes()); } else { @@ -1065,6 +1049,34 @@ void QtSmartSearchBox::hideAutoCompletions() m_completer->popup()->hide(); } +void QtSmartSearchBox::startFullTextSearch() +{ + if (!m_supportsFullTextSearch) + { + return; + } + + std::wstring term = text().toStdWString().substr(1); + if (term.empty()) + { + return; + } + + bool caseSensitive = false; + if (term.at(0) == SearchMatch::FULLTEXT_SEARCH_CHARACTER) + { + term = term.substr(1); + if (term.empty()) + { + return; + } + + caseSensitive = true; + } + + emit fullTextSearch(term, caseSensitive); +} + std::deque QtSmartSearchBox::getMatchesForInput(const std::wstring& text) const { std::deque matches; diff --git a/src/lib_gui/qt/element/search/QtSmartSearchBox.h b/src/lib_gui/qt/element/search/QtSmartSearchBox.h index 91484d72..823d0573 100644 --- a/src/lib_gui/qt/element/search/QtSmartSearchBox.h +++ b/src/lib_gui/qt/element/search/QtSmartSearchBox.h @@ -33,15 +33,20 @@ class QtSmartSearchBox { Q_OBJECT +signals: + void autocomplete(const std::wstring& query, NodeTypeSet acceptedNodeTypes); + void search(const std::vector& matches, NodeTypeSet acceptedNodeTypes); + void fullTextSearch(const std::wstring& query, bool caseSensitive); + public slots: - void search(); - void fullTextSearch(); + void startSearch(); public: - QtSmartSearchBox(QWidget* parent); + QtSmartSearchBox(const QString& placeholder, bool supportsFullTextSearch, QWidget* parent = nullptr); virtual ~QtSmartSearchBox(); QCompleter* getCompleter() const; + std::vector getMatches() const; void setAutocompletionList(const std::vector& autocompletionList); void setMatches(const std::vector& matches); @@ -99,11 +104,16 @@ private: void requestAutoCompletions(); void hideAutoCompletions(); + void startFullTextSearch(); + std::deque getMatchesForInput(const std::wstring& text) const; NodeTypeSet getMatchAcceptedNodeTypes() const; bool lastMatchIsNoFilter() const; + const QString m_placeholder; + const bool m_supportsFullTextSearch; + bool m_allowTextChange; QString m_oldText; diff --git a/src/lib_gui/qt/graphics/base/QtGraphicsView.cpp b/src/lib_gui/qt/graphics/base/QtGraphicsView.cpp index 0a466fb5..3987582f 100644 --- a/src/lib_gui/qt/graphics/base/QtGraphicsView.cpp +++ b/src/lib_gui/qt/graphics/base/QtGraphicsView.cpp @@ -120,20 +120,20 @@ QtGraphicsView::QtGraphicsView(QWidget* parent) "", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/zoom_in.png"), "search/button", this); m_zoomInButton->setObjectName("zoom_in_button"); m_zoomInButton->setAutoRepeat(true); - m_zoomInButton->setToolTip("Zoom in (" + modifierName + " + Mousewheel forward)"); + m_zoomInButton->setToolTip("zoom in (" + modifierName + " + Mousewheel forward)"); connect(m_zoomInButton, &QPushButton::pressed, this, &QtGraphicsView::zoomInPressed); m_zoomOutButton = new QtSelfRefreshIconButton( "", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/zoom_out.png"), "search/button", this); m_zoomOutButton->setObjectName("zoom_out_button"); m_zoomOutButton->setAutoRepeat(true); - m_zoomOutButton->setToolTip("Zoom out (" + modifierName + " + Mousewheel back)"); + m_zoomOutButton->setToolTip("zoom out (" + modifierName + " + Mousewheel back)"); connect(m_zoomOutButton, &QPushButton::pressed, this, &QtGraphicsView::zoomOutPressed); m_legendButton = new QtSelfRefreshIconButton( "", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/legend.png"), "search/button", this); m_legendButton->setObjectName("legend_button"); - m_legendButton->setToolTip("Show graph legend"); + m_legendButton->setToolTip("show legend"); connect(m_legendButton, &QPushButton::clicked, this, &QtGraphicsView::legendClicked); } diff --git a/src/lib_gui/qt/view/QtCustomTrailView.cpp b/src/lib_gui/qt/view/QtCustomTrailView.cpp new file mode 100644 index 00000000..e9c44fa7 --- /dev/null +++ b/src/lib_gui/qt/view/QtCustomTrailView.cpp @@ -0,0 +1,644 @@ +#include "QtCustomTrailView.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ColorScheme.h" +#include "MessageActivateTrail.h" +#include "NodeTypeSet.h" +#include "QtSmartSearchBox.h" +#include "ResourcePaths.h" +#include "TabId.h" +#include "utilityQt.h" + +QtCustomTrailView::QtCustomTrailView(ViewLayout*) + : QWidget(nullptr) + , CustomTrailView(nullptr) + , m_controllerProxy(this, TabId::app()) +{ + setWindowTitle("Custom Trail"); + + QGridLayout* mainLayout = new QGridLayout(); + mainLayout->setContentsMargins(0, 0, 0, 0); + mainLayout->setSpacing(0); + setLayout(mainLayout); + + QWidget* panelA1 = new QWidget(); + QWidget* panelA2 = new QWidget(); + QWidget* panelB1 = new QWidget(); + QWidget* panelB2 = new QWidget(); + QWidget* panelC1 = new QWidget(); + QWidget* panelC2 = new QWidget(); + QWidget* panelD = new QWidget(); + + panelA1->setObjectName("panelA"); + panelA2->setObjectName("panelA"); + panelB1->setObjectName("panelB"); + panelB2->setObjectName("panelB"); + panelC1->setObjectName("panelA"); + panelC2->setObjectName("panelA"); + panelD->setObjectName("panelB2"); + + mainLayout->addWidget(panelA1, 0, 0); + mainLayout->addWidget(panelA2, 0, 1); + mainLayout->addWidget(panelB1, 1, 0); + mainLayout->addWidget(panelB2, 1, 1); + mainLayout->addWidget(panelC1, 2, 0); + mainLayout->addWidget(panelC2, 2, 1); + mainLayout->addWidget(panelD, 3, 0, 1, 2); + + // search boxes + { + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(25, 10, 10, 10); + panelA1->setLayout(hLayout); + + m_searchBoxFrom = new QtSmartSearchBox("Start Symbol", false); + m_searchBoxTo = new QtSmartSearchBox("Target Symbol", false); + + hLayout->addWidget(new QLabel("From:")); + hLayout->addWidget(createSearchBox(m_searchBoxFrom)); + hLayout->addSpacing(15); + + QVBoxLayout* optionsLayout = new QVBoxLayout(); + optionsLayout->setContentsMargins(10, 10, 25, 10); + panelA2->setLayout(optionsLayout); + + m_optionTo = new QRadioButton(QString::fromUtf8("\xe2\x86\x92") + " To:"); + m_optionReferenced = new QRadioButton(QString::fromUtf8("\xe2\x86\x92") + " All Referenced"); + m_optionReferencing = new QRadioButton(QString::fromUtf8("\xe2\x86\x90") + " All Referencing"); + + m_optionTo->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac + m_optionReferenced->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac + m_optionReferencing->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac + + m_optionTo->setChecked(true); + + QButtonGroup* options = new QButtonGroup(); + options->addButton(m_optionTo); + options->addButton(m_optionReferenced); + options->addButton(m_optionReferencing); + + QWidget* searchBoxToContainer = createSearchBox(m_searchBoxTo); + QHBoxLayout* toLayout = new QHBoxLayout(); + toLayout->setContentsMargins(0, 0, 0, 0); + toLayout->addWidget(m_optionTo); + toLayout->addWidget(searchBoxToContainer); + + optionsLayout->addLayout(toLayout); + optionsLayout->addWidget(m_optionReferenced); + optionsLayout->addSpacing(7); + optionsLayout->addWidget(m_optionReferencing); + + connect(options, QOverload::of(&QButtonGroup::buttonClicked), + [this, searchBoxToContainer](QAbstractButton* button) + { + searchBoxToContainer->setEnabled(button == m_optionTo); + } + ); + + connect(m_searchBoxFrom, &QtSmartSearchBox::autocomplete, + [this](const std::wstring& query, NodeTypeSet acceptedNodeTypes) + { + m_controllerProxy.executeAsTaskWithArgs(&CustomTrailController::autocomplete, query, true); + } + ); + + connect(m_searchBoxTo, &QtSmartSearchBox::autocomplete, + [this](const std::wstring& query, NodeTypeSet acceptedNodeTypes) + { + m_controllerProxy.executeAsTaskWithArgs(&CustomTrailController::autocomplete, query, false); + } + ); + } + + // depth slider + { + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(25, 10, 10, 10); + panelB1->setLayout(hLayout); + + hLayout->addWidget(new QLabel("Max Depth:")); + + m_slider = new QSlider(Qt::Horizontal); + m_slider->setObjectName("depth_slider"); + m_slider->setToolTip("adjust graph depth"); + m_slider->setMinimum(1); + m_slider->setMaximum(51); + m_slider->setMinimumWidth(150); + m_slider->setValue(INITIAL_GRAPH_DEPTH); + + hLayout->addWidget(m_slider); + + QLabel* valueLabel = new QLabel(QString::number(INITIAL_GRAPH_DEPTH)); + valueLabel->setMinimumWidth(20); + hLayout->addWidget(valueLabel); + + connect(m_slider, &QSlider::valueChanged, + [this, valueLabel](int) + { + if (m_slider->value() == m_slider->maximum()) + { + valueLabel->setText("inf"); + } + else + { + valueLabel->setText(QString::number(m_slider->value())); + } + } + ); + + hLayout->addStretch(); + } + + // layout direction + { + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(10, 10, 25, 10); + hLayout->setSpacing(15); + panelB2->setLayout(hLayout); + + m_horizontalButton = new QRadioButton("Horizontal"); + m_verticalButton = new QRadioButton("Vertical"); + + m_horizontalButton->setChecked(true); + + hLayout->addWidget(new QLabel("Layout Direction:")); + hLayout->addWidget(m_horizontalButton); + hLayout->addWidget(m_verticalButton); + + QButtonGroup* layouts = new QButtonGroup(); + layouts->addButton(m_horizontalButton); + layouts->addButton(m_verticalButton); + + hLayout->addStretch(); + } + + ColorScheme* scheme = ColorScheme::getInstance().get(); + + // node filters + { + std::vector nodeFilters; + std::vector nodeColors; + + std::vector nodeTypes = { + // NodeType::NODE_SYMBOL, + NodeType::NODE_TYPE, + NodeType::NODE_BUILTIN_TYPE, + // NodeType::NODE_MODULE, + // NodeType::NODE_NAMESPACE, + // NodeType::NODE_PACKAGE, + NodeType::NODE_CLASS, + NodeType::NODE_STRUCT, + NodeType::NODE_UNION, + NodeType::NODE_INTERFACE, + NodeType::NODE_TYPEDEF, + NodeType::NODE_TEMPLATE_PARAMETER, + NodeType::NODE_TYPE_PARAMETER, + NodeType::NODE_ENUM, + NodeType::NODE_ENUM_CONSTANT, + NodeType::NODE_GLOBAL_VARIABLE, + NodeType::NODE_FIELD, + NodeType::NODE_FUNCTION, + NodeType::NODE_METHOD, + NodeType::NODE_FILE, + NodeType::NODE_MACRO, + NodeType::NODE_ANNOTATION + }; + + for (NodeType::Type t : nodeTypes) + { + nodeFilters.push_back(QString::fromStdString(NodeType::getReadableTypeString(t))); + nodeColors.push_back(QColor(scheme->getNodeTypeColor(t, "fill", ColorScheme::FOCUS).c_str())); + } + + QVBoxLayout* filterLayout = addFilters("Nodes:", nodeFilters, nodeColors, &m_nodeFilters, 11); + filterLayout->setContentsMargins(25, 10, 25, 10); + panelC1->setLayout(filterLayout); + } + + // edge filters + { + std::vector edgeFilters; + std::vector edgeColors; + + std::vector edgeTypes = { + // Edge::EDGE_UNDEFINED, + Edge::EDGE_TYPE_USAGE, + Edge::EDGE_INHERITANCE, + Edge::EDGE_USAGE, + Edge::EDGE_CALL, + Edge::EDGE_OVERRIDE, + // Edge::EDGE_TEMPLATE_ARGUMENT, // merged with type use + // Edge::EDGE_TEMPLATE_DEFAULT_ARGUMENT, // merged with type use + Edge::EDGE_TEMPLATE_SPECIALIZATION, + // Edge::EDGE_TEMPLATE_MEMBER_SPECIALIZATION, // merged with above + Edge::EDGE_TYPE_ARGUMENT, + Edge::EDGE_INCLUDE, + Edge::EDGE_IMPORT, + // Edge::EDGE_AGGREGATION, + Edge::EDGE_MACRO_USAGE, + Edge::EDGE_ANNOTATION_USAGE + // Edge::EDGE_MEMBER // has separate checkbox + }; + + for (Edge::EdgeType t : edgeTypes) + { + edgeFilters.push_back(QString::fromStdWString(Edge::getReadableTypeString(t))); + edgeColors.push_back(QColor(scheme->getEdgeTypeColor(t, ColorScheme::FOCUS).c_str())); + } + + QVBoxLayout* filterLayout = addFilters("Edges:", edgeFilters, edgeColors, &m_edgeFilters, 5); + filterLayout->setContentsMargins(10, 10, 25, 10); + panelC2->setLayout(filterLayout); + } + + // controls + { + QPushButton* cancelButton = new QPushButton("Cancel"); + QPushButton* searchButton = new QPushButton("Search"); + + cancelButton->setObjectName("button"); + searchButton->setObjectName("button"); + + cancelButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac + searchButton->setAttribute(Qt::WA_LayoutUsesWidgetRect); // fixes layouting on Mac + + m_errorLabel = new QLabel(); + m_errorLabel->setObjectName("error"); + + QHBoxLayout* hLayout = new QHBoxLayout(); + hLayout->setContentsMargins(25, 15, 25, 15); + hLayout->addWidget(cancelButton); + hLayout->addStretch(); + hLayout->addWidget(m_errorLabel); + hLayout->addSpacing(10); + hLayout->addWidget(searchButton); + panelD->setLayout(hLayout); + + connect(cancelButton, &QPushButton::clicked, + [this]() + { + hide(); + } + ); + + connect(searchButton, &QPushButton::clicked, + [this]() + { + if (m_searchBoxFrom->getMatches().size() == 0 || + m_searchBoxFrom->getMatches().front().tokenIds.size() == 0) + { + setError("No 'Start Symbol' symbol found."); + return; + } + + Id startId = m_searchBoxFrom->getMatches().front().tokenIds.front(); + Id endId = 0; + + if (m_optionReferencing->isChecked()) + { + std::swap(startId, endId); + } + else if (m_optionTo->isChecked()) + { + if (m_searchBoxTo->getMatches().size() && m_searchBoxTo->getMatches().front().tokenIds.size()) + { + endId = m_searchBoxTo->getMatches().front().tokenIds.front(); + } + else + { + setError("No 'Target Symbol' symbol found."); + return; + } + } + + NodeType::TypeMask nodeTypes = getCheckedNodeTypes(); + Edge::TypeMask edgeTypes = getCheckedEdgeTypes(); + + if (!nodeTypes) + { + setError("No 'Nodes' selected."); + return; + } + + if (!edgeTypes) + { + setError("No 'Edges' selected."); + return; + } + + MessageActivateTrail message( + startId, + endId, + nodeTypes, + edgeTypes, + m_nodeNonIndexed->isChecked(), + m_slider->value() == m_slider->maximum() ? 0 : m_slider->value(), + m_horizontalButton->isChecked() + ); + + m_controllerProxy.executeAsTaskWithArgs(&CustomTrailController::activateTrail, message); + + setError(""); + + hide(); + } + ); + } +} + +void QtCustomTrailView::createWidgetWrapper() +{ +} + +void QtCustomTrailView::refreshView() +{ + m_onQtThread([this]() + { + updateStyleSheet(); + m_searchBoxFrom->refreshStyle(); + m_searchBoxTo->refreshStyle(); + }); +} + +void QtCustomTrailView::clearView() +{ + m_onQtThread([this]() + { + m_searchBoxFrom->setMatches({}); + m_searchBoxTo->setMatches({}); + }); +} + +void QtCustomTrailView::setAvailableNodeAndEdgeTypes(NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes) +{ + m_onQtThread([this, nodeTypes, edgeTypes]() + { + for (QCheckBox* filter : m_nodeFilters) + { + if (filter == m_nodeNonIndexed) + { + continue; + } + + bool enabled = nodeTypes & NodeType::getTypeForReadableTypeString(filter->text().toStdWString()); + filter->setEnabled(enabled); + filter->setVisible(enabled); + } + + for (QCheckBox* filter : m_edgeFilters) + { + bool enabled = false; + if (filter == m_edgeMember) + { + enabled = edgeTypes & Edge::EDGE_MEMBER; + } + else + { + enabled = edgeTypes & Edge::getTypeForReadableTypeString(filter->text().toStdWString()); + } + + filter->setEnabled(enabled); + filter->setVisible(enabled); + } + }); +} + +void QtCustomTrailView::showView() +{ + m_onQtThread([this]() + { + show(); + raise(); + }); +} + +void QtCustomTrailView::hideView() +{ + m_onQtThread([this]() + { + hide(); + }); +} + +void QtCustomTrailView::showAutocompletions(const std::vector& autocompletions, bool from) +{ + m_onQtThread([this, autocompletions, from]() + { + if (from) + { + m_searchBoxFrom->setAutocompletionList(autocompletions); + } + else + { + m_searchBoxTo->setAutocompletionList(autocompletions); + } + }); +} + +void QtCustomTrailView::keyPressEvent(QKeyEvent* event) +{ + if (event->key() == Qt::Key_Escape) + { + hide(); + return; + } + + QWidget::keyPressEvent(event); +} + +void QtCustomTrailView::updateStyleSheet() +{ + std::string css = utility::getStyleSheet(ResourcePaths::getGuiPath().concatenate(L"search_view/search_view.css")); + css += utility::getStyleSheet(ResourcePaths::getGuiPath().concatenate(L"custom_trail_view/custom_trail_view.css")); + + setStyleSheet(css.c_str()); + + QAbstractItemView* popup = m_searchBoxFrom->getCompleter()->popup(); + if (popup) + { + popup->setStyleSheet(css.c_str()); + } + + popup = m_searchBoxTo->getCompleter()->popup(); + if (popup) + { + popup->setStyleSheet(css.c_str()); + } +} + +QWidget* QtCustomTrailView::createSearchBox(QtSmartSearchBox* searchBox) const +{ + QWidget* searchBoxContainer = new QWidget(); + searchBoxContainer->setObjectName("search_box_container"); + searchBoxContainer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + searchBox->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Maximum); + + QBoxLayout* innerLayout = new QVBoxLayout(); + innerLayout->setContentsMargins(12, 3, 5, 2); + innerLayout->addWidget(searchBox); + searchBoxContainer->setLayout(innerLayout); + + return searchBoxContainer; +} + +QVBoxLayout* QtCustomTrailView::addFilters( + QString name, + const std::vector& filters, + const std::vector& colors, + std::vector* checkBoxes, + size_t filtersInFirstColumn) +{ + QVBoxLayout* vLayout = new QVBoxLayout(); + vLayout->addWidget(new QLabel(name)); + + QHBoxLayout* mainLayout = new QHBoxLayout(); + vLayout->addLayout(mainLayout); + + QVBoxLayout* filterALayout = new QVBoxLayout(); + QVBoxLayout* filterBLayout = new QVBoxLayout(); + + mainLayout->addLayout(filterALayout); + mainLayout->addLayout(filterBLayout); + + QPixmap pixmap(QString::fromStdString(ResourcePaths::getGuiPath().concatenate(L"custom_trail_view/images/circle.png").str())); + for (size_t i = 0; i < filters.size(); i++) + { + QCheckBox* checkBox = new QCheckBox(filters[i]); + checkBox->setChecked(true); + checkBox->setIcon(QIcon(utility::colorizePixmap(pixmap, colors[i]))); + checkBoxes->push_back(checkBox); + + if (i < filtersInFirstColumn) + { + filterALayout->addWidget(checkBox); + } + else + { + filterBLayout->addWidget(checkBox); + } + } + + if (checkBoxes == &m_nodeFilters) + { + m_nodeNonIndexed = new QCheckBox("non-indexed"); + m_nodeNonIndexed->setChecked(true); + checkBoxes->push_back(m_nodeNonIndexed); + + filterBLayout->addSpacing(7); + filterBLayout->addWidget(m_nodeNonIndexed); + } + else if (checkBoxes == &m_edgeFilters) + { + m_edgeMember = new QCheckBox("member"); + m_edgeMember->setChecked(true); + checkBoxes->push_back(m_edgeMember); + + filterBLayout->addSpacing(5); + filterBLayout->addWidget(m_edgeMember); + } + + filterALayout->addStretch(); + filterBLayout->addStretch(); + + vLayout->addLayout(addCheckButtons(*checkBoxes)); + vLayout->addStretch(); + + return vLayout; +} + +QHBoxLayout* QtCustomTrailView::addCheckButtons(const std::vector& checkBoxes) const +{ + QHBoxLayout* buttonLayout = new QHBoxLayout(); + + QPushButton* checkButton = new QPushButton("Check All"); + QPushButton* uncheckButton = new QPushButton("Uncheck All"); + + checkButton->setObjectName("button_small"); + uncheckButton->setObjectName("button_small"); + + buttonLayout->addWidget(checkButton); + buttonLayout->addWidget(uncheckButton); + buttonLayout->addStretch(); + + connect(checkButton, &QPushButton::clicked, + [&checkBoxes]() + { + for (QCheckBox* box : checkBoxes) + { + if (box->isEnabled()) + { + box->setChecked(true); + } + } + } + ); + + connect(uncheckButton, &QPushButton::clicked, + [&checkBoxes]() + { + for (QCheckBox* box : checkBoxes) + { + box->setChecked(false); + } + } + ); + + return buttonLayout; +} + +NodeType::TypeMask QtCustomTrailView::getCheckedNodeTypes() const +{ + NodeType::TypeMask nodeTypes = 0; + for (const QCheckBox* filter : m_nodeFilters) + { + if (filter->isEnabled() && filter->isChecked() && filter != m_nodeNonIndexed) + { + nodeTypes |= NodeType::getTypeForReadableTypeString(filter->text().toStdWString()); + } + } + return nodeTypes; +} + +Edge::TypeMask QtCustomTrailView::getCheckedEdgeTypes() const +{ + Edge::TypeMask edgeTypes = 0; + for (const QCheckBox* filter : m_edgeFilters) + { + if (filter->isEnabled() && filter->isChecked() && filter != m_edgeMember) + { + Edge::EdgeType type = Edge::getTypeForReadableTypeString(filter->text().toStdWString()); + + edgeTypes |= type; + + if (type == Edge::EDGE_TYPE_USAGE) + { + edgeTypes |= Edge::EDGE_TEMPLATE_ARGUMENT | Edge::EDGE_TEMPLATE_DEFAULT_ARGUMENT; + } + else if (type == Edge::EDGE_TEMPLATE_SPECIALIZATION) + { + edgeTypes |= Edge::EDGE_TEMPLATE_MEMBER_SPECIALIZATION; + } + } + } + + if (m_edgeMember->isChecked()) + { + edgeTypes |= Edge::EDGE_MEMBER; + } + + return edgeTypes; +} + +void QtCustomTrailView::setError(const QString& error) +{ + m_errorLabel->setText(error); +} diff --git a/src/lib_gui/qt/view/QtCustomTrailView.h b/src/lib_gui/qt/view/QtCustomTrailView.h new file mode 100644 index 00000000..c9f1083c --- /dev/null +++ b/src/lib_gui/qt/view/QtCustomTrailView.h @@ -0,0 +1,86 @@ +#ifndef QT_CUSTOM_TRAIL_VIEW_H +#define QT_CUSTOM_TRAIL_VIEW_H + +#include + +#include "ControllerProxy.h" +#include "CustomTrailController.h" +#include "CustomTrailView.h" +#include "QtThreadedFunctor.h" + +class QCheckBox; +class QHBoxLayout; +class QLabel; +class QSlider; +class QRadioButton; +class QtSmartSearchBox; +class QVBoxLayout; + +class QtCustomTrailView + : public QWidget + , public CustomTrailView +{ +public: + QtCustomTrailView(ViewLayout*); + + // View implementation + void createWidgetWrapper() override; + void refreshView() override; + + // TrailView implementation + void clearView() override; + void setAvailableNodeAndEdgeTypes(NodeType::TypeMask nodeTypes, Edge::TypeMask edgeTypes) override; + + void showView() override; + void hideView() override; + void showAutocompletions(const std::vector& autocompletions, bool from) override; + +protected: + void keyPressEvent(QKeyEvent* event) override; + +private: + void updateStyleSheet(); + + QWidget* createSearchBox(QtSmartSearchBox* searchBox) const; + QVBoxLayout* addFilters( + QString name, + const std::vector& filters, + const std::vector& colors, + std::vector* checkBoxes, + size_t filtersInFirstColumn); + QHBoxLayout* addCheckButtons(const std::vector& checkBoxes) const; + + NodeType::TypeMask getCheckedNodeTypes() const; + Edge::TypeMask getCheckedEdgeTypes() const; + + void setError(const QString& error); + + static const size_t INITIAL_GRAPH_DEPTH = 5; + static const size_t FILTERS_PER_COLUMN = 10; + + QtThreadedLambdaFunctor m_onQtThread; + + ControllerProxy m_controllerProxy; + + QtSmartSearchBox* m_searchBoxFrom; + QtSmartSearchBox* m_searchBoxTo; + + QRadioButton* m_optionReferenced; + QRadioButton* m_optionReferencing; + QRadioButton* m_optionTo; + + QSlider* m_slider; + + QRadioButton* m_horizontalButton; + QRadioButton* m_verticalButton; + + std::vector m_nodeFilters; + std::vector m_edgeFilters; + + QCheckBox* m_nodeNonIndexed; + QCheckBox* m_edgeMember; + + QLabel* m_errorLabel; +}; + +#endif // QT_CUSTOM_TRAIL_VIEW_H diff --git a/src/lib_gui/qt/view/QtGraphView.cpp b/src/lib_gui/qt/view/QtGraphView.cpp index f33403aa..1660a00d 100644 --- a/src/lib_gui/qt/view/QtGraphView.cpp +++ b/src/lib_gui/qt/view/QtGraphView.cpp @@ -18,6 +18,7 @@ #include "DummyNode.h" #include "GraphViewStyle.h" #include "MessageActivateTrail.h" +#include "MessageCustomTrailShow.h" #include "MessageDeactivateEdge.h" #include "MessageRefreshUI.h" #include "MessageScrollGraph.h" @@ -81,7 +82,7 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout) m_expandButton = new QtSelfRefreshIconButton( "", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/graph.png"), "search/button"); m_expandButton->setObjectName("expand_button"); - m_expandButton->setToolTip("show depth graph controls"); + m_expandButton->setToolTip("show trail controls"); m_expandButton->setIconSize(QSize(16, 16)); m_expandButton->setGeometry(0, 0, 26, 26); connect(m_expandButton, &QPushButton::clicked, this, &QtGraphView::clickedExpand); @@ -96,10 +97,17 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout) m_collapseButton = new QtSelfRefreshIconButton( "", ResourcePaths::getGuiPath().concatenate(L"graph_view/images/graph_arrow.png"), "search/button", ui); m_collapseButton->setObjectName("collapse_button"); - m_collapseButton->setToolTip("hide depth graph controls"); + m_collapseButton->setToolTip("hide trail controls"); m_collapseButton->setIconSize(QSize(16, 16)); connect(m_collapseButton, &QPushButton::clicked, this, &QtGraphView::clickedCollapse); + m_customTrailButton = new QtSelfRefreshIconButton("", FilePath(), "search/button", ui); + m_customTrailButton->setObjectName("trail_button"); + m_customTrailButton->setIconSize(QSize(16, 16)); + m_customTrailButton->setToolTip("custom trail"); + m_customTrailButton->setIconPath(ResourcePaths::getGuiPath().concatenate(L"graph_view/images/graph_custom.png")); + connect(m_customTrailButton, &QPushButton::clicked, this, &QtGraphView::clickedCustomTrail); + m_forwardTrailButton = new QtSelfRefreshIconButton("", FilePath(), "search/button", ui); m_forwardTrailButton->setObjectName("trail_button"); m_forwardTrailButton->setIconSize(QSize(16, 16)); @@ -112,12 +120,12 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout) m_trailDepthLabel = new QLabel(ui); m_trailDepthLabel->setObjectName("depth_label"); - m_trailDepthLabel->setToolTip("adjust graph depth"); + m_trailDepthLabel->setToolTip("adjust trail depth"); m_trailDepthLabel->setAlignment(Qt::AlignCenter); m_trailDepthSlider = new QSlider(Qt::Vertical, ui); m_trailDepthSlider->setObjectName("depth_slider"); - m_trailDepthSlider->setToolTip("adjust graph depth"); + m_trailDepthSlider->setToolTip("adjust trail depth"); m_trailDepthSlider->setMinimum(1); m_trailDepthSlider->setMaximum(26); m_trailDepthSlider->setValue(5); @@ -125,14 +133,15 @@ QtGraphView::QtGraphView(ViewLayout* viewLayout) connect(m_trailDepthSlider, &QSlider::sliderReleased, this, &QtGraphView::trailDepthUpdated); m_collapseButton->setGeometry(0, 0, 26, 20); - m_backwardTrailButton->setGeometry(0, 22, 26, 26); - m_forwardTrailButton->setGeometry(0, 50, 26, 26); - m_trailDepthLabel->setGeometry(0, 78, 26, 26); - m_trailDepthSlider->setGeometry(0, 104, 26, 100); + m_customTrailButton->setGeometry(0, 22, 26, 26); + m_backwardTrailButton->setGeometry(0, 50, 26, 26); + m_forwardTrailButton->setGeometry(0, 78, 26, 26); + m_trailDepthLabel->setGeometry(0, 106, 26, 26); + m_trailDepthSlider->setGeometry(0, 132, 26, 100); } m_trailWidget = new QWidget(widget); - m_trailWidget->setGeometry(8, 8, 26, 210); + m_trailWidget->setGeometry(8, 8, 26, 238); m_trailWidget->setLayout(stack); if (ApplicationSettings::getInstance()->getGraphControlsVisible()) @@ -720,6 +729,11 @@ void QtGraphView::clickedExpand() ApplicationSettings::getInstance()->save(); } +void QtGraphView::clickedCustomTrail() +{ + MessageCustomTrailShow().dispatch(); +} + void QtGraphView::clickedBackwardTrail() { activateTrail(false); @@ -768,22 +782,22 @@ MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward) QtGraphNodeData* node = dynamic_cast(m_oldActiveNode); if (node) { - Edge::TypeMask trailType; + Edge::TypeMask edgeTypes; bool horizontalLayout = true; if (node->getData()->getType().isInheritable()) { - trailType = Edge::EDGE_INHERITANCE | Edge::EDGE_TEMPLATE_SPECIALIZATION; + edgeTypes = Edge::EDGE_INHERITANCE | Edge::EDGE_TEMPLATE_SPECIALIZATION; horizontalLayout = false; } else if (node->getData()->getType().isCallable()) { - trailType = Edge::EDGE_CALL | Edge::EDGE_OVERRIDE; + edgeTypes = Edge::EDGE_CALL | Edge::EDGE_OVERRIDE; horizontalLayout = true; } else if (node->getData()->getType().isFile()) { - trailType = Edge::EDGE_INCLUDE; + edgeTypes = Edge::EDGE_INCLUDE; horizontalLayout = true; } else @@ -801,7 +815,7 @@ MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward) depth = 0; } - return MessageActivateTrail(originId, targetId, trailType, depth, horizontalLayout); + return MessageActivateTrail(originId, targetId, edgeTypes, depth, horizontalLayout); } return message; @@ -810,7 +824,7 @@ MessageActivateTrail QtGraphView::getMessageActivateTrail(bool forward) void QtGraphView::activateTrail(bool forward) { MessageActivateTrail message = getMessageActivateTrail(forward); - if (message.trailType) + if (message.edgeTypes) { message.dispatch(); } @@ -820,20 +834,20 @@ void QtGraphView::updateTrailButtons() { MessageActivateTrail message = getMessageActivateTrail(false); - m_backwardTrailButton->setEnabled(message.trailType); - m_forwardTrailButton->setEnabled(message.trailType); - m_trailDepthLabel->setEnabled(message.trailType); - m_trailDepthSlider->setEnabled(message.trailType); + m_backwardTrailButton->setEnabled(message.edgeTypes); + m_forwardTrailButton->setEnabled(message.edgeTypes); + m_trailDepthLabel->setEnabled(message.edgeTypes); + m_trailDepthSlider->setEnabled(message.edgeTypes); std::wstring backwardImagePath = L"graph_left.png"; std::wstring forwardImagePath = L"graph_right.png"; - if (message.trailType & Edge::EDGE_CALL) + if (message.edgeTypes & Edge::EDGE_CALL) { m_backwardTrailButton->setToolTip("show caller graph"); m_forwardTrailButton->setToolTip("show callee graph"); } - else if (message.trailType & Edge::EDGE_INHERITANCE) + else if (message.edgeTypes & Edge::EDGE_INHERITANCE) { m_backwardTrailButton->setToolTip("show base hierarchy"); m_forwardTrailButton->setToolTip("show derived hierarchy"); @@ -841,15 +855,15 @@ void QtGraphView::updateTrailButtons() backwardImagePath = L"graph_up.png"; forwardImagePath = L"graph_down.png"; } - else if (message.trailType & Edge::EDGE_INCLUDE) + else if (message.edgeTypes & Edge::EDGE_INCLUDE) { m_backwardTrailButton->setToolTip("show including files hierarchy"); m_forwardTrailButton->setToolTip("show included files hierarchy"); } else { - m_backwardTrailButton->setToolTip("no depth graph available for active symbol"); - m_forwardTrailButton->setToolTip("no depth graph available for active symbol"); + m_backwardTrailButton->setToolTip("no trail for active symbol"); + m_forwardTrailButton->setToolTip("no trail for active symbol"); } m_forwardTrailButton->setIconPath(ResourcePaths::getGuiPath().concatenate(L"graph_view/images/" + forwardImagePath)); diff --git a/src/lib_gui/qt/view/QtGraphView.h b/src/lib_gui/qt/view/QtGraphView.h index 6957ae2a..11755c14 100644 --- a/src/lib_gui/qt/view/QtGraphView.h +++ b/src/lib_gui/qt/view/QtGraphView.h @@ -81,6 +81,7 @@ private slots: void clickedCollapse(); void clickedExpand(); + void clickedCustomTrail(); void clickedBackwardTrail(); void clickedForwardTrail(); @@ -152,6 +153,7 @@ private: QWidget* m_trailWidget; QtSelfRefreshIconButton* m_expandButton; QtSelfRefreshIconButton* m_collapseButton; + QtSelfRefreshIconButton* m_customTrailButton; QtSelfRefreshIconButton* m_forwardTrailButton; QtSelfRefreshIconButton* m_backwardTrailButton; QSlider* m_trailDepthSlider; diff --git a/src/lib_gui/qt/view/QtViewFactory.cpp b/src/lib_gui/qt/view/QtViewFactory.cpp index dde53c97..efff1ec5 100644 --- a/src/lib_gui/qt/view/QtViewFactory.cpp +++ b/src/lib_gui/qt/view/QtViewFactory.cpp @@ -5,6 +5,7 @@ #include "QtBookmarkView.h" #include "QtCodeView.h" #include "QtCompositeView.h" +#include "QtCustomTrailView.h" #include "QtDialogView.h" #include "QtErrorView.h" #include "QtGraphView.h" @@ -51,6 +52,11 @@ std::shared_ptr QtViewFactory::createCodeView(ViewLayout* viewLayout) return View::createAndAddToLayout(viewLayout); } +std::shared_ptr QtViewFactory::createCustomTrailView() const +{ + return View::create(nullptr); +} + std::shared_ptr QtViewFactory::createErrorView(ViewLayout* viewLayout) const { return View::createAndAddToLayout(viewLayout); diff --git a/src/lib_gui/qt/view/QtViewFactory.h b/src/lib_gui/qt/view/QtViewFactory.h index 192a73fa..b309e1d2 100644 --- a/src/lib_gui/qt/view/QtViewFactory.h +++ b/src/lib_gui/qt/view/QtViewFactory.h @@ -18,6 +18,7 @@ public: std::shared_ptr createBookmarkButtonsView(ViewLayout* viewLayout) const override; std::shared_ptr createBookmarkView(ViewLayout* viewLayout) const override; std::shared_ptr createCodeView(ViewLayout* viewLayout) const override; + std::shared_ptr createCustomTrailView() const override; std::shared_ptr createErrorView(ViewLayout* viewLayout) const override; std::shared_ptr createGraphView(ViewLayout* viewLayout) const override; std::shared_ptr createRefreshView(ViewLayout* viewLayout) const override; diff --git a/src/lib_gui/qt/window/QtMainWindow.cpp b/src/lib_gui/qt/window/QtMainWindow.cpp index 1ddc9cfb..aa28d57e 100644 --- a/src/lib_gui/qt/window/QtMainWindow.cpp +++ b/src/lib_gui/qt/window/QtMainWindow.cpp @@ -40,6 +40,7 @@ #include "MessageBookmarkBrowse.h" #include "MessageBookmarkCreate.h" #include "MessageCodeReference.h" +#include "MessageCustomTrailShow.h" #include "MessageFind.h" #include "MessageIndexingShowDialog.h" #include "MessageLoadProject.h" @@ -755,6 +756,11 @@ void QtMainWindow::codeLocalReferenceNext() MessageCodeReference(MessageCodeReference::REFERENCE_NEXT, true).dispatch(); } +void QtMainWindow::customTrail() +{ + MessageCustomTrailShow().dispatch(); +} + void QtMainWindow::overview() { MessageActivateAll().dispatch(); @@ -963,6 +969,10 @@ void QtMainWindow::setupEditMenu() menu->addSeparator(); + menu->addAction(tr("Custom Trail..."), this, &QtMainWindow::customTrail, QKeySequence(Qt::CTRL + Qt::Key_L)); + + menu->addSeparator(); + menu->addAction(tr("&To overview"), this, &QtMainWindow::overview, QKeySequence::MoveToStartOfDocument); menu->addSeparator(); diff --git a/src/lib_gui/qt/window/QtMainWindow.h b/src/lib_gui/qt/window/QtMainWindow.h index feb230ff..052ecc28 100644 --- a/src/lib_gui/qt/window/QtMainWindow.h +++ b/src/lib_gui/qt/window/QtMainWindow.h @@ -137,6 +137,7 @@ public slots: void codeReferenceNext(); void codeLocalReferencePrevious(); void codeLocalReferenceNext(); + void customTrail(); void overview(); void closeWindow(); diff --git a/testing/graph_view/data/custom_trail_files/custom.h b/testing/graph_view/data/custom_trail_files/custom.h new file mode 100644 index 00000000..ffeca3b3 --- /dev/null +++ b/testing/graph_view/data/custom_trail_files/custom.h @@ -0,0 +1,56 @@ +void func() +{ + int m; +} + +void func2() +{ + func(); +} + +class CustomA +{ +public: + void method() + { + func(); + } + + int i; +}; + +class CustomB +{ +public: + void method() + { + a.method(); + } + + CustomA a; +}; + +class CustomC +{ +public: + int method() + { + func2(); + bool a; + char c; + } + + CustomA a; +}; + +class CustomD +{ +public: + void method() + { + b.method(); + } + + CustomB b; + CustomC c; +}; diff --git a/testing/graph_view/data/custom_trail_tests.cpp b/testing/graph_view/data/custom_trail_tests.cpp new file mode 100644 index 00000000..9cce341b --- /dev/null +++ b/testing/graph_view/data/custom_trail_tests.cpp @@ -0,0 +1,85 @@ + +#define CUSTOM_TRAIL_TESTS + +#include "custom_trail_files/custom.h" + +// TEST: open and close dialog +// START ---------------------------------------------------------------------- + +// ACTION 1: Click on custom trail button in graph +// RESULT 1: dialog opens on top of window + +// ACTION 2: Close with click on Cancel +// RESULT 2: dialog closes + +// ACTION 3: Use custom trail shortcut Ctrl + L +// RESULT 3: dialog opens on top of window + +// ACTION 4: click on main window +// ACTION 5: open again +// RESULT 5: dialog opens on top of window + +// ACTION 6: press ESC +// RESULT 6: dialog closes + +// END ------------------------------------------------------------------------ + + +// TEST: from -> to +// START ---------------------------------------------------------------------- + +// ACTION 1: search from "CustomD" +// ACTION 2: search to int +// ACTION 3: select infinite depth +// ACTION 4: select horizontal layout +// ACTION 5: select all node and edge types +// Action 6: click search + +// RESULTS 6: +// - Graph shows multiple paths +// - Graph contains: CustomD -> CustomB, CustomC -> CustomA -> func -> int + +// Action 7: Repeat search with: +// - select vertical layout +// - uncheck node field + +// RESULTS 7: +// - Graph one call chain to func +// - Graph goes top to bottom +// - Graph contains: CustomD -> CustomB -> CustomA, func2 -> func -> int + +// END ------------------------------------------------------------------------ + + +// TEST: from -> all referenced +// START ---------------------------------------------------------------------- + +// ACTION 1: search from "CustomD::c" +// ACTION 2: select all referenced +// ACTION 3: select depth 3 +// ACTION 4: select horizontal layout +// ACTION 5: select all node and edge types +// Action 6: click search + +// RESULTS 6: +// - Graph shows multiple paths +// - Graph contains: CustomD -> CustomC -> int, char, bool, func2, CustomA + +// END ------------------------------------------------------------------------ + + +// TEST: from <- all referencing +// START ---------------------------------------------------------------------- + +// ACTION 1: search from "func" +// ACTION 2: select all referencing +// ACTION 3: select depth infinite +// ACTION 4: select horizontal layout +// ACTION 5: select all node and edge types +// Action 6: click search + +// RESULTS 6: +// - Graph shows multiple paths +// - Graph contains: CustomB, CustomC -> CustomA, func2 -> func + +// END ------------------------------------------------------------------------ diff --git a/testing/graph_view/graph_view_tests.srctrlprj b/testing/graph_view/graph_view_tests.srctrlprj index 8b33b7ae..3ae411e5 100644 --- a/testing/graph_view/graph_view_tests.srctrlprj +++ b/testing/graph_view/graph_view_tests.srctrlprj @@ -7,6 +7,7 @@ Test Suites:\n\n [::\tmOVERVIEW_TESTS\ts\tp]\n [::\tmLAYOUT_TESTS\ts\tp]\n [::\tmDEPTH_GRAPH_TESTS\ts\tp]\n +[::\tmCUSTOM_TRAIL_TESTS\ts\tp]\n [::\tmGROUPING_TESTS\ts\tp]\n @@ -37,5 +38,5 @@ Test Suites:\n\n C++ Source Group - 7 + 8