##// END OF EJS Templates
Merge branch 'feature/CataloguePart7' into develop
trabillard -
r1383:4dfca649988f merge
parent child
Show More
@@ -0,0 +1,19
1 #ifndef SCIQLOP_FILTERINGACTION_H
2 #define SCIQLOP_FILTERINGACTION_H
3
4 #include <Common/spimpl.h>
5 #include <QWidgetAction>
6
7 /// A LineEdit inside an action which is able to filter other actions
8 class FilteringAction : public QWidgetAction {
9 public:
10 FilteringAction(QWidget *parent);
11
12 void addActionToFilter(QAction *action);
13
14 private:
15 class FilteringActionPrivate;
16 spimpl::unique_impl_ptr<FilteringActionPrivate> impl;
17 };
18
19 #endif // SCIQLOP_FILTERINGACTION_H
@@ -0,0 +1,27
1 #include "Actions/FilteringAction.h"
2
3 #include <QLineEdit>
4
5 struct FilteringAction::FilteringActionPrivate {
6 QLineEdit *m_FilterLineEdit;
7 QVector<QAction *> m_FilteredActions;
8 };
9
10 FilteringAction::FilteringAction(QWidget *parent)
11 : QWidgetAction(parent), impl{spimpl::make_unique_impl<FilteringActionPrivate>()}
12 {
13 impl->m_FilterLineEdit = new QLineEdit(parent);
14 setDefaultWidget(impl->m_FilterLineEdit);
15
16 connect(impl->m_FilterLineEdit, &QLineEdit::textEdited, [this](auto text) {
17 for (auto action : impl->m_FilteredActions) {
18 auto match = action->text().contains(text, Qt::CaseInsensitive);
19 action->setVisible(match);
20 }
21 });
22 }
23
24 void FilteringAction::addActionToFilter(QAction *action)
25 {
26 impl->m_FilteredActions << action;
27 }
@@ -1,27 +1,35
1 1 #ifndef SCIQLOP_ACTIONSGUICONTROLLER_H
2 2 #define SCIQLOP_ACTIONSGUICONTROLLER_H
3 3
4 4 #include <Actions/SelectionZoneAction.h>
5 5 #include <Common/spimpl.h>
6 6
7 7 #include <memory>
8 8
9 9 class ActionsGuiController {
10 10 public:
11 11 ActionsGuiController();
12 12
13 13 std::shared_ptr<SelectionZoneAction>
14 14 addSectionZoneAction(const QString &name, SelectionZoneAction::ExecuteFunction function);
15 15
16 16 std::shared_ptr<SelectionZoneAction>
17 17 addSectionZoneAction(const QStringList &subMenuList, const QString &name,
18 18 SelectionZoneAction::ExecuteFunction function);
19 19
20 20 QVector<std::shared_ptr<SelectionZoneAction> > selectionZoneActions() const;
21 21
22 void removeAction(const std::shared_ptr<SelectionZoneAction> &action);
23
24 /// Sets a flag to say that the specified menu can be filtered, usually via a FilteringAction
25 void addFilterForMenu(const QStringList &menuPath);
26
27 /// Returns true if the menu can be filtered
28 bool isMenuFiltered(const QStringList &menuPath) const;
29
22 30 private:
23 31 class ActionsGuiControllerPrivate;
24 32 spimpl::unique_impl_ptr<ActionsGuiControllerPrivate> impl;
25 33 };
26 34
27 35 #endif // SCIQLOP_ACTIONSGUICONTROLLER_H
@@ -1,75 +1,82
1 1 #ifndef SCIQLOP_SELECTIONZONEACTION_H
2 2 #define SCIQLOP_SELECTIONZONEACTION_H
3 3
4 4 #include <Common/spimpl.h>
5 5
6 6 #include <QLoggingCategory>
7 7 #include <QObject>
8 8
9 9 #include <functional>
10 10
11 11 class VisualizationSelectionZoneItem;
12 12
13 13 Q_DECLARE_LOGGING_CATEGORY(LOG_SelectionZoneAction)
14 14
15 15 /**
16 16 * @brief The SelectionZoneAction class represents an action on a selection zone in the
17 17 * visualization.
18 18 *
19 19 * The action is a function that will be executed when the slot execute() is called.
20 20 */
21 21 class SelectionZoneAction : public QObject {
22 22
23 23 Q_OBJECT
24 24
25 25 public:
26 26 /// Signature of the function associated to the action
27 27 using ExecuteFunction
28 28 = std::function<void(const QVector<VisualizationSelectionZoneItem *> &item)>;
29 29
30 30 using EnableFunction
31 31 = std::function<bool(const QVector<VisualizationSelectionZoneItem *> &item)>;
32 32
33 33 /**
34 34 * @param name the name of the action, displayed to the user
35 35 * @param fun the function that will be called when the action is executed
36 36 * @sa execute()
37 37 */
38 38 explicit SelectionZoneAction(const QString &name, ExecuteFunction fun);
39 39
40 40 /**
41 41 * @param name the name of the action, displayed to the user
42 42 * @param subMenusList the list of sub menus where the action should be inserted
43 43 * @param fun the function that will be called when the action is executed
44 44 * @sa execute()
45 45 */
46 46 explicit SelectionZoneAction(const QStringList &subMenuList, const QString &name,
47 47 ExecuteFunction fun);
48 48
49 49 /// Sets the function which determine if the action should be enabled or disabled
50 50 void setEnableFunction(EnableFunction fun);
51 51
52 52 /// Sets the shortcut displayed by the action.
53 53 /// Note: The shortcut is only displayed and not active because it is not permanently stored
54 54 void setDisplayedShortcut(const QKeySequence &shortcut);
55 55 QKeySequence displayedShortcut() const;
56 56
57 57 /// The name of the action
58 58 QString name() const noexcept;
59 59
60 60 /// The path in the sub menus, if any
61 61 QStringList subMenuList() const noexcept;
62 62
63 /// Sets if filtering the action is allowed via a FilteringAction
64 void setAllowedFiltering(bool value);
65
66 /// Returns true if filtering the action is allowed via a FilteringAction. By default it is
67 /// allowed.
68 bool isFilteringAllowed() const;
69
63 70 public slots:
64 71 /// Executes the action
65 72 void execute(const QVector<VisualizationSelectionZoneItem *> &item);
66 73
67 74 /// Returns true if the action is enabled
68 75 bool isEnabled(const QVector<VisualizationSelectionZoneItem *> &item);
69 76
70 77 private:
71 78 class SelectionZoneActionPrivate;
72 79 spimpl::unique_impl_ptr<SelectionZoneActionPrivate> impl;
73 80 };
74 81
75 82 #endif // SCIQLOP_SELECTIONZONEACTION_H
@@ -1,19 +1,20
1 1 #ifndef SCIQLOP_CATALOGUEACTIONMANAGER_H
2 2 #define SCIQLOP_CATALOGUEACTIONMANAGER_H
3 3
4 4 #include <Common/spimpl.h>
5 5
6 6 class CatalogueExplorer;
7 7
8 8 class CatalogueActionManager {
9 9 public:
10 10 CatalogueActionManager(CatalogueExplorer *catalogueExplorer);
11 11
12 12 void installSelectionZoneActions();
13 void refreshCreateInCatalogueAction();
13 14
14 15 private:
15 16 class CatalogueActionManagerPrivate;
16 17 spimpl::unique_impl_ptr<CatalogueActionManagerPrivate> impl;
17 18 };
18 19
19 20 #endif // SCIQLOP_CATALOGUEACTIONMANAGER_H
@@ -1,56 +1,57
1 1 #ifndef SCIQLOP_CATALOGUESIDEBARWIDGET_H
2 2 #define SCIQLOP_CATALOGUESIDEBARWIDGET_H
3 3
4 4 #include <Common/spimpl.h>
5 5 #include <QLoggingCategory>
6 6 #include <QTreeWidgetItem>
7 7 #include <QWidget>
8 8
9 9 class CatalogueAbstractTreeItem;
10 10 class DBCatalogue;
11 11
12 12 namespace Ui {
13 13 class CatalogueSideBarWidget;
14 14 }
15 15
16 16 Q_DECLARE_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget)
17 17
18 18 class CatalogueSideBarWidget : public QWidget {
19 19 Q_OBJECT
20 20
21 21 signals:
22 22 void catalogueSelected(const QVector<std::shared_ptr<DBCatalogue> > &catalogues);
23 23 void databaseSelected(const QStringList &databases);
24 24 void allEventsSelected();
25 25 void trashSelected();
26 26 void selectionCleared();
27 27 void catalogueSaved(const std::shared_ptr<DBCatalogue> &catalogue);
28 void catalogueListChanged();
28 29
29 30 public:
30 31 explicit CatalogueSideBarWidget(QWidget *parent = 0);
31 32 virtual ~CatalogueSideBarWidget();
32 33
33 34 CatalogueAbstractTreeItem *addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
34 35 const QString &repository);
35 36 void setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue, bool hasChanges);
36 37
37 38 QVector<std::shared_ptr<DBCatalogue> > getCatalogues(const QString &repository) const;
38 39
39 40 // QWidget interface
40 41 protected:
41 42 void keyPressEvent(QKeyEvent *event);
42 43
43 44 private slots:
44 45 void emitSelection();
45 46
46 47 private:
47 48 Ui::CatalogueSideBarWidget *ui;
48 49
49 50 class CatalogueSideBarWidgetPrivate;
50 51 spimpl::unique_impl_ptr<CatalogueSideBarWidgetPrivate> impl;
51 52
52 53 private slots:
53 54 void onContextMenuRequested(const QPoint &pos);
54 55 };
55 56
56 57 #endif // SCIQLOP_CATALOGUESIDEBARWIDGET_H
@@ -1,28 +1,29
1 1 #ifndef SCIQLOP_CATALOGUETREEITEM_H
2 2 #define SCIQLOP_CATALOGUETREEITEM_H
3 3
4 4 #include <Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.h>
5 5 #include <Common/spimpl.h>
6 6
7 7 class DBCatalogue;
8 8
9 9
10 10 class CatalogueTreeItem : public CatalogueAbstractTreeItem {
11 11 public:
12 12 CatalogueTreeItem(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon, int type);
13 13
14 14 QVariant data(int column, int role) const override;
15 15 bool setData(int column, int role, const QVariant &value) override;
16 16 Qt::ItemFlags flags(int column) const override;
17 17 bool canDropMimeData(const QMimeData *data, Qt::DropAction action) override;
18 18 bool dropMimeData(const QMimeData *data, Qt::DropAction action) override;
19 19
20 20 /// Returns the catalogue represented by the item
21 21 std::shared_ptr<DBCatalogue> catalogue() const;
22 void replaceCatalogue(const std::shared_ptr<DBCatalogue> &catalogue);
22 23
23 24 private:
24 25 class CatalogueTreeItemPrivate;
25 26 spimpl::unique_impl_ptr<CatalogueTreeItemPrivate> impl;
26 27 };
27 28
28 29 #endif // SCIQLOP_CATALOGUETREEITEM_H
@@ -1,148 +1,147
1 1 qxorm_dep = dependency('QxOrm', required : true, fallback:['QxOrm','qxorm_dep'])
2 2 catalogueapi_dep = dependency('CatalogueAPI', required : true, fallback:['CatalogueAPI','CatalogueAPI_dep'])
3 3
4 4 gui_moc_headers = [
5 5 'include/DataSource/DataSourceWidget.h',
6 6 'include/Settings/SqpSettingsDialog.h',
7 7 'include/Settings/SqpSettingsGeneralWidget.h',
8 8 'include/SidePane/SqpSidePane.h',
9 9 'include/SqpApplication.h',
10 10 'include/DragAndDrop/DragDropScroller.h',
11 11 'include/DragAndDrop/DragDropTabSwitcher.h',
12 12 'include/TimeWidget/TimeWidget.h',
13 13 'include/Variable/VariableInspectorWidget.h',
14 14 'include/Variable/RenameVariableDialog.h',
15 15 'include/Visualization/qcustomplot.h',
16 16 'include/Visualization/VisualizationGraphWidget.h',
17 17 'include/Visualization/VisualizationTabWidget.h',
18 18 'include/Visualization/VisualizationWidget.h',
19 19 'include/Visualization/VisualizationZoneWidget.h',
20 20 'include/Visualization/VisualizationDragDropContainer.h',
21 21 'include/Visualization/VisualizationDragWidget.h',
22 22 'include/Visualization/ColorScaleEditor.h',
23 23 'include/Visualization/VisualizationSelectionZoneItem.h',
24 24 'include/Actions/SelectionZoneAction.h',
25 25 'include/Visualization/VisualizationMultiZoneSelectionDialog.h',
26 26 'include/Catalogue/CatalogueExplorer.h',
27 27 'include/Catalogue/CatalogueEventsWidget.h',
28 28 'include/Catalogue/CatalogueSideBarWidget.h',
29 29 'include/Catalogue/CatalogueInspectorWidget.h',
30 30 'include/Catalogue/CatalogueEventsModel.h',
31 'include/Catalogue/CreateEventDialog.h',
32 'include/Catalogue/CatalogueTreeModel.h'
31 'include/Catalogue/CatalogueTreeModel.h',
32 'include/Actions/FilteringAction.h'
33 33 ]
34 34
35 35 gui_ui_files = [
36 36 'ui/DataSource/DataSourceWidget.ui',
37 37 'ui/Settings/SqpSettingsDialog.ui',
38 38 'ui/Settings/SqpSettingsGeneralWidget.ui',
39 39 'ui/SidePane/SqpSidePane.ui',
40 40 'ui/TimeWidget/TimeWidget.ui',
41 41 'ui/Variable/VariableInspectorWidget.ui',
42 42 'ui/Variable/RenameVariableDialog.ui',
43 43 'ui/Variable/VariableMenuHeaderWidget.ui',
44 44 'ui/Visualization/VisualizationGraphWidget.ui',
45 45 'ui/Visualization/VisualizationTabWidget.ui',
46 46 'ui/Visualization/VisualizationWidget.ui',
47 47 'ui/Visualization/VisualizationZoneWidget.ui',
48 48 'ui/Visualization/ColorScaleEditor.ui',
49 49 'ui/Visualization/VisualizationMultiZoneSelectionDialog.ui',
50 50 'ui/Catalogue/CatalogueExplorer.ui',
51 51 'ui/Catalogue/CatalogueEventsWidget.ui',
52 52 'ui/Catalogue/CatalogueSideBarWidget.ui',
53 'ui/Catalogue/CatalogueInspectorWidget.ui',
54 'ui/Catalogue/CreateEventDialog.ui'
53 'ui/Catalogue/CatalogueInspectorWidget.ui'
55 54 ]
56 55
57 56 gui_qresources = ['resources/sqpguiresources.qrc']
58 57
59 58 rcc_gen = generator(rcc,
60 59 output : 'qrc_@BASENAME@.cpp',
61 60 arguments : [
62 61 '--output',
63 62 '@OUTPUT@',
64 63 '@INPUT@',
65 64 '@EXTRA_ARGS@'])
66 65
67 66 rcc_files = rcc_gen.process(gui_qresources, extra_args : ['-name', 'sqpguiresources'])
68 67
69 68 gui_moc_files = qt5.preprocess(moc_headers : gui_moc_headers,
70 69 ui_files : gui_ui_files)
71 70
72 71 gui_sources = [
73 72 'src/SqpApplication.cpp',
74 73 'src/DragAndDrop/DragDropGuiController.cpp',
75 74 'src/DragAndDrop/DragDropScroller.cpp',
76 75 'src/DragAndDrop/DragDropTabSwitcher.cpp',
77 76 'src/Common/ColorUtils.cpp',
78 77 'src/Common/VisualizationDef.cpp',
79 78 'src/DataSource/DataSourceTreeWidgetItem.cpp',
80 79 'src/DataSource/DataSourceTreeWidgetHelper.cpp',
81 80 'src/DataSource/DataSourceWidget.cpp',
82 81 'src/DataSource/DataSourceTreeWidget.cpp',
83 82 'src/Settings/SqpSettingsDialog.cpp',
84 83 'src/Settings/SqpSettingsGeneralWidget.cpp',
85 84 'src/SidePane/SqpSidePane.cpp',
86 85 'src/TimeWidget/TimeWidget.cpp',
87 86 'src/Variable/VariableInspectorWidget.cpp',
88 87 'src/Variable/VariableInspectorTableView.cpp',
89 88 'src/Variable/VariableMenuHeaderWidget.cpp',
90 89 'src/Variable/RenameVariableDialog.cpp',
91 90 'src/Visualization/VisualizationGraphHelper.cpp',
92 91 'src/Visualization/VisualizationGraphRenderingDelegate.cpp',
93 92 'src/Visualization/VisualizationGraphWidget.cpp',
94 93 'src/Visualization/VisualizationTabWidget.cpp',
95 94 'src/Visualization/VisualizationWidget.cpp',
96 95 'src/Visualization/VisualizationZoneWidget.cpp',
97 96 'src/Visualization/qcustomplot.cpp',
98 97 'src/Visualization/QCustomPlotSynchronizer.cpp',
99 98 'src/Visualization/operations/FindVariableOperation.cpp',
100 99 'src/Visualization/operations/GenerateVariableMenuOperation.cpp',
101 100 'src/Visualization/operations/MenuBuilder.cpp',
102 101 'src/Visualization/operations/RemoveVariableOperation.cpp',
103 102 'src/Visualization/operations/RescaleAxeOperation.cpp',
104 103 'src/Visualization/VisualizationDragDropContainer.cpp',
105 104 'src/Visualization/VisualizationDragWidget.cpp',
106 105 'src/Visualization/AxisRenderingUtils.cpp',
107 106 'src/Visualization/PlottablesRenderingUtils.cpp',
108 107 'src/Visualization/MacScrollBarStyle.cpp',
109 108 'src/Visualization/VisualizationCursorItem.cpp',
110 109 'src/Visualization/ColorScaleEditor.cpp',
111 110 'src/Visualization/SqpColorScale.cpp',
112 111 'src/Visualization/QCPColorMapIterator.cpp',
113 112 'src/Visualization/VisualizationSelectionZoneItem.cpp',
114 113 'src/Visualization/VisualizationSelectionZoneManager.cpp',
115 114 'src/Actions/SelectionZoneAction.cpp',
116 115 'src/Actions/ActionsGuiController.cpp',
116 'src/Actions/FilteringAction.cpp',
117 117 'src/Visualization/VisualizationActionManager.cpp',
118 118 'src/Visualization/VisualizationMultiZoneSelectionDialog.cpp',
119 119 'src/Catalogue/CatalogueExplorer.cpp',
120 120 'src/Catalogue/CatalogueEventsWidget.cpp',
121 121 'src/Catalogue/CatalogueSideBarWidget.cpp',
122 122 'src/Catalogue/CatalogueInspectorWidget.cpp',
123 123 'src/Catalogue/CatalogueTreeItems/CatalogueAbstractTreeItem.cpp',
124 124 'src/Catalogue/CatalogueTreeItems/CatalogueTreeItem.cpp',
125 125 'src/Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.cpp',
126 126 'src/Catalogue/CatalogueEventsModel.cpp',
127 127 'src/Catalogue/CatalogueExplorerHelper.cpp',
128 128 'src/Catalogue/CatalogueActionManager.cpp',
129 'src/Catalogue/CreateEventDialog.cpp',
130 129 'src/Catalogue/CatalogueTreeModel.cpp'
131 130 ]
132 131
133 132 gui_inc = include_directories(['include'])
134 133
135 134 sciqlop_gui_lib = library('sciqlopgui',
136 135 gui_sources,
137 136 gui_moc_files,
138 137 rcc_files,
139 138 include_directories : [gui_inc],
140 139 dependencies : [ qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep],
141 140 install : true
142 141 )
143 142
144 143 sciqlop_gui = declare_dependency(link_with : sciqlop_gui_lib,
145 144 include_directories : gui_inc,
146 145 dependencies : [qt5printsupport, qt5gui, qt5widgets, qt5svg, sciqlop_core, catalogueapi_dep])
147 146
148 147
@@ -1,36 +1,52
1 1 #include "Actions/ActionsGuiController.h"
2 2
3 3 struct ActionsGuiController::ActionsGuiControllerPrivate {
4 4
5 5 QVector<std::shared_ptr<SelectionZoneAction> > m_SelectionZoneActions;
6 QSet<QStringList> m_FilteredMenu;
6 7 };
7 8
8 9 ActionsGuiController::ActionsGuiController()
9 10 : impl{spimpl::make_unique_impl<ActionsGuiControllerPrivate>()}
10 11 {
11 12 }
12 13
13 14 std::shared_ptr<SelectionZoneAction>
14 15 ActionsGuiController::addSectionZoneAction(const QString &name,
15 16 SelectionZoneAction::ExecuteFunction function)
16 17 {
17 18 auto action = std::make_shared<SelectionZoneAction>(name, function);
18 19 impl->m_SelectionZoneActions.push_back(action);
19 20
20 21 return action;
21 22 }
22 23
23 24 std::shared_ptr<SelectionZoneAction>
24 25 ActionsGuiController::addSectionZoneAction(const QStringList &subMenuList, const QString &name,
25 26 SelectionZoneAction::ExecuteFunction function)
26 27 {
27 28 auto action = std::make_shared<SelectionZoneAction>(subMenuList, name, function);
28 29 impl->m_SelectionZoneActions.push_back(action);
29 30
30 31 return action;
31 32 }
32 33
33 34 QVector<std::shared_ptr<SelectionZoneAction> > ActionsGuiController::selectionZoneActions() const
34 35 {
35 36 return impl->m_SelectionZoneActions;
36 37 }
38
39 void ActionsGuiController::removeAction(const std::shared_ptr<SelectionZoneAction> &action)
40 {
41 impl->m_SelectionZoneActions.removeAll(action);
42 }
43
44 void ActionsGuiController::addFilterForMenu(const QStringList &menuPath)
45 {
46 impl->m_FilteredMenu.insert(menuPath);
47 }
48
49 bool ActionsGuiController::isMenuFiltered(const QStringList &menuPath) const
50 {
51 return impl->m_FilteredMenu.contains(menuPath);
52 }
@@ -1,66 +1,77
1 1 #include <Actions/SelectionZoneAction.h>
2 2 #include <Visualization/VisualizationSelectionZoneItem.h>
3 3
4 4 Q_LOGGING_CATEGORY(LOG_SelectionZoneAction, "SelectionZoneAction")
5 5
6 6 struct SelectionZoneAction::SelectionZoneActionPrivate {
7 7 explicit SelectionZoneActionPrivate(const QString &name, const QStringList &subMenuList,
8 8 SelectionZoneAction::ExecuteFunction fun)
9 9 : m_Name{name}, m_SubMenuList{subMenuList}, m_Fun{std::move(fun)}
10 10 {
11 11 }
12 12
13 13 QString m_Name;
14 14 QStringList m_SubMenuList;
15 15 QKeySequence m_DisplayedShortcut;
16 16 SelectionZoneAction::ExecuteFunction m_Fun;
17 17 SelectionZoneAction::EnableFunction m_EnableFun = [](auto zones) { return true; };
18 bool m_FilteringAllowed = true;
18 19 };
19 20
20 21 SelectionZoneAction::SelectionZoneAction(const QString &name, ExecuteFunction fun)
21 22 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, QStringList{},
22 23 std::move(fun))}
23 24 {
24 25 }
25 26
26 27 SelectionZoneAction::SelectionZoneAction(const QStringList &subMenuList, const QString &name,
27 28 SelectionZoneAction::ExecuteFunction fun)
28 29 : impl{spimpl::make_unique_impl<SelectionZoneActionPrivate>(name, subMenuList,
29 30 std::move(fun))}
30 31 {
31 32 }
32 33
33 34 void SelectionZoneAction::setEnableFunction(EnableFunction fun)
34 35 {
35 36 impl->m_EnableFun = std::move(fun);
36 37 }
37 38
38 39 void SelectionZoneAction::setDisplayedShortcut(const QKeySequence &shortcut)
39 40 {
40 41 impl->m_DisplayedShortcut = shortcut;
41 42 }
42 43
43 44 QKeySequence SelectionZoneAction::displayedShortcut() const
44 45 {
45 46 return impl->m_DisplayedShortcut;
46 47 }
47 48
48 49 QString SelectionZoneAction::name() const noexcept
49 50 {
50 51 return impl->m_Name;
51 52 }
52 53
53 54 QStringList SelectionZoneAction::subMenuList() const noexcept
54 55 {
55 56 return impl->m_SubMenuList;
56 57 }
57 58
59 void SelectionZoneAction::setAllowedFiltering(bool value)
60 {
61 impl->m_FilteringAllowed = value;
62 }
63
64 bool SelectionZoneAction::isFilteringAllowed() const
65 {
66 return impl->m_FilteringAllowed;
67 }
68
58 69 void SelectionZoneAction::execute(const QVector<VisualizationSelectionZoneItem *> &item)
59 70 {
60 71 impl->m_Fun(item);
61 72 }
62 73
63 74 bool SelectionZoneAction::isEnabled(const QVector<VisualizationSelectionZoneItem *> &item)
64 75 {
65 76 return impl->m_EnableFun(item);
66 77 }
@@ -1,145 +1,174
1 1 #include "Catalogue/CatalogueActionManager.h"
2 2
3 3 #include <Actions/ActionsGuiController.h>
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <DataSource/DataSourceItem.h>
6 6 #include <SqpApplication.h>
7 7 #include <Variable/Variable.h>
8 8 #include <Visualization/VisualizationGraphWidget.h>
9 9 #include <Visualization/VisualizationSelectionZoneItem.h>
10 10
11 11 #include <Catalogue/CatalogueEventsWidget.h>
12 12 #include <Catalogue/CatalogueExplorer.h>
13 13 #include <Catalogue/CatalogueSideBarWidget.h>
14 #include <Catalogue/CreateEventDialog.h>
15 14
16 15 #include <CatalogueDao.h>
17 16 #include <DBCatalogue.h>
18 17 #include <DBEvent.h>
19 18 #include <DBEventProduct.h>
20 19
21 20 #include <QBoxLayout>
22 21 #include <QComboBox>
23 22 #include <QDialog>
24 23 #include <QDialogButtonBox>
25 24 #include <QLineEdit>
26 25 #include <memory>
27 26
27 const auto CATALOGUE_MENU_NAME = QObject::tr("Catalogues");
28 const auto CATALOGUE_CREATE_EVENT_MENU_NAME = QObject::tr("New Event...");
29
30 const auto DEFAULT_EVENT_NAME = QObject::tr("Event");
31 const auto DEFAULT_CATALOGUE_NAME = QObject::tr("Catalogue");
32
28 33 struct CatalogueActionManager::CatalogueActionManagerPrivate {
29 34
30 35 CatalogueExplorer *m_CatalogueExplorer = nullptr;
36 QVector<std::shared_ptr<SelectionZoneAction> > m_CreateInCatalogueActions;
31 37
32 38 CatalogueActionManagerPrivate(CatalogueExplorer *catalogueExplorer)
33 39 : m_CatalogueExplorer(catalogueExplorer)
34 40 {
35 41 }
36 42
37 43 void createEventFromZones(const QString &eventName,
38 44 const QVector<VisualizationSelectionZoneItem *> &zones,
39 45 const std::shared_ptr<DBCatalogue> &catalogue = nullptr)
40 46 {
41 47 auto event = std::make_shared<DBEvent>();
42 48 event->setName(eventName);
43 49
44 50 std::list<DBEventProduct> productList;
45 51 for (auto zone : zones) {
46 52 auto graph = zone->parentGraphWidget();
47 53 for (auto var : graph->variables()) {
48 54 auto eventProduct = std::make_shared<DBEventProduct>();
49 55 eventProduct->setEvent(*event);
50 56
51 57 auto productId
52 58 = var->metadata().value(DataSourceItem::ID_DATA_KEY, "UnknownID").toString();
53 59
54 60 auto zoneRange = zone->range();
55 61 eventProduct->setTStart(zoneRange.m_TStart);
56 62 eventProduct->setTEnd(zoneRange.m_TEnd);
57 63
58 64 eventProduct->setProductId(productId);
59 65
60 66 productList.push_back(*eventProduct);
61 67 }
62 68 }
63 69
64 70 event->setEventProducts(productList);
65 71
66 72 sqpApp->catalogueController().addEvent(event);
67 73
68 74
69 75 if (catalogue) {
70 76 catalogue->addEvent(event->getUniqId());
71 77 sqpApp->catalogueController().updateCatalogue(catalogue);
72 78 m_CatalogueExplorer->sideBarWidget().setCatalogueChanges(catalogue, true);
73 79 if (m_CatalogueExplorer->eventsWidget().displayedCatalogues().contains(catalogue)) {
74 80 m_CatalogueExplorer->eventsWidget().addEvent(event);
75 81 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
76 82 }
77 83 }
78 84 else if (m_CatalogueExplorer->eventsWidget().isAllEventsDisplayed()) {
79 85 m_CatalogueExplorer->eventsWidget().addEvent(event);
80 86 m_CatalogueExplorer->eventsWidget().setEventChanges(event, true);
81 87 }
82 88 }
89
90 SelectionZoneAction::EnableFunction createEventEnableFuntion() const
91 {
92 return [](auto zones) {
93
94 // Checks that all variables in the zones doesn't refer to the same product
95 QSet<QString> usedDatasource;
96 for (auto zone : zones) {
97 auto graph = zone->parentGraphWidget();
98 auto variables = graph->variables();
99
100 for (auto var : variables) {
101 auto datasourceId
102 = var->metadata().value(DataSourceItem::ID_DATA_KEY).toString();
103 if (!usedDatasource.contains(datasourceId)) {
104 usedDatasource.insert(datasourceId);
105 }
106 else {
107 return false;
108 }
109 }
110 }
111
112 return true;
113 };
114 }
83 115 };
84 116
85 117 CatalogueActionManager::CatalogueActionManager(CatalogueExplorer *catalogueExplorer)
86 118 : impl{spimpl::make_unique_impl<CatalogueActionManagerPrivate>(catalogueExplorer)}
87 119 {
88 120 }
89 121
90 122 void CatalogueActionManager::installSelectionZoneActions()
91 123 {
92 124 auto &actionController = sqpApp->actionsGuiController();
93 125
94 auto createEventEnableFuntion = [](auto zones) {
95
96 // Checks that all variables in the zones doesn't refer to the same product
97 QSet<QString> usedDatasource;
98 for (auto zone : zones) {
99 auto graph = zone->parentGraphWidget();
100 auto variables = graph->variables();
126 auto createEventAction = actionController.addSectionZoneAction(
127 {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}, QObject::tr("Without Catalogue"),
128 [this](auto zones) { impl->createEventFromZones(DEFAULT_EVENT_NAME, zones); });
129 createEventAction->setEnableFunction(impl->createEventEnableFuntion());
130 createEventAction->setAllowedFiltering(false);
131
132 auto createEventInNewCatalogueAction = actionController.addSectionZoneAction(
133 {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME}, QObject::tr("In New Catalogue"),
134 [this](auto zones) {
135
136 auto newCatalogue = std::make_shared<DBCatalogue>();
137 newCatalogue->setName(DEFAULT_CATALOGUE_NAME);
138 sqpApp->catalogueController().addCatalogue(newCatalogue);
139 impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(newCatalogue,
140 REPOSITORY_DEFAULT);
141
142 impl->createEventFromZones(DEFAULT_EVENT_NAME, zones, newCatalogue);
143 });
144 createEventInNewCatalogueAction->setEnableFunction(impl->createEventEnableFuntion());
145 createEventInNewCatalogueAction->setAllowedFiltering(false);
101 146
102 for (auto var : variables) {
103 auto datasourceId = var->metadata().value(DataSourceItem::ID_DATA_KEY).toString();
104 if (!usedDatasource.contains(datasourceId)) {
105 usedDatasource.insert(datasourceId);
106 }
107 else {
108 return false;
109 }
110 }
111 }
147 refreshCreateInCatalogueAction();
112 148
113 return true;
114 };
149 actionController.addFilterForMenu({CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME});
150 }
115 151
116 auto createEventAction = actionController.addSectionZoneAction(
117 {QObject::tr("Catalogues")}, QObject::tr("New Event..."), [this](auto zones) {
118 CreateEventDialog dialog(
119 impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT));
120 dialog.hideCatalogueChoice();
121 if (dialog.exec() == QDialog::Accepted) {
122 impl->createEventFromZones(dialog.eventName(), zones);
123 }
124 });
125 createEventAction->setEnableFunction(createEventEnableFuntion);
126
127 auto createEventInCatalogueAction = actionController.addSectionZoneAction(
128 {QObject::tr("Catalogues")}, QObject::tr("New Event in Catalogue..."), [this](auto zones) {
129 CreateEventDialog dialog(
130 impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT));
131 if (dialog.exec() == QDialog::Accepted) {
132 auto selectedCatalogue = dialog.selectedCatalogue();
133 if (!selectedCatalogue) {
134 selectedCatalogue = std::make_shared<DBCatalogue>();
135 selectedCatalogue->setName(dialog.catalogueName());
136 sqpApp->catalogueController().addCatalogue(selectedCatalogue);
137 impl->m_CatalogueExplorer->sideBarWidget().addCatalogue(selectedCatalogue,
138 REPOSITORY_DEFAULT);
139 }
152 void CatalogueActionManager::refreshCreateInCatalogueAction()
153 {
154 auto &actionController = sqpApp->actionsGuiController();
140 155
141 impl->createEventFromZones(dialog.eventName(), zones, selectedCatalogue);
142 }
143 });
144 createEventInCatalogueAction->setEnableFunction(createEventEnableFuntion);
156 for (auto action : impl->m_CreateInCatalogueActions) {
157 actionController.removeAction(action);
158 }
159 impl->m_CreateInCatalogueActions.clear();
160
161 auto allCatalogues
162 = impl->m_CatalogueExplorer->sideBarWidget().getCatalogues(REPOSITORY_DEFAULT);
163
164 for (auto catalogue : allCatalogues) {
165 auto catalogueName = catalogue->getName();
166 auto createEventInCatalogueAction = actionController.addSectionZoneAction(
167 {CATALOGUE_MENU_NAME, CATALOGUE_CREATE_EVENT_MENU_NAME},
168 QObject::tr("In \"").append(catalogueName).append("\""), [this, catalogue](auto zones) {
169 impl->createEventFromZones(DEFAULT_EVENT_NAME, zones, catalogue);
170 });
171 createEventInCatalogueAction->setEnableFunction(impl->createEventEnableFuntion());
172 impl->m_CreateInCatalogueActions << createEventInCatalogueAction;
173 }
145 174 }
@@ -1,629 +1,617
1 1 #include "Catalogue/CatalogueEventsWidget.h"
2 2 #include "ui_CatalogueEventsWidget.h"
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Catalogue/CatalogueEventsModel.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <CatalogueDao.h>
8 8 #include <DBCatalogue.h>
9 9 #include <DBEventProduct.h>
10 10 #include <DataSource/DataSourceController.h>
11 11 #include <DataSource/DataSourceItem.h>
12 12 #include <SqpApplication.h>
13 13 #include <Variable/Variable.h>
14 14 #include <Variable/VariableController.h>
15 15 #include <Visualization/VisualizationGraphWidget.h>
16 16 #include <Visualization/VisualizationTabWidget.h>
17 17 #include <Visualization/VisualizationWidget.h>
18 18 #include <Visualization/VisualizationZoneWidget.h>
19 19
20 #include <QActionGroup>
20 21 #include <QDialog>
21 22 #include <QDialogButtonBox>
22 23 #include <QKeyEvent>
23 24 #include <QListWidget>
25 #include <QMenu>
24 26 #include <QMessageBox>
25 27
26 28 Q_LOGGING_CATEGORY(LOG_CatalogueEventsWidget, "CatalogueEventsWidget")
27 29
28 30 /// Percentage added to the range of a event when it is displayed
29 31 const auto EVENT_RANGE_MARGE = 30; // in %
30 32
33 const QString NEW_ZONE_TEXT = QStringLiteral("New Zone");
34
31 35 struct CatalogueEventsWidget::CatalogueEventsWidgetPrivate {
32 36
33 37 CatalogueEventsModel *m_Model = nullptr;
34 38 QStringList m_ZonesForTimeMode;
35 39 QString m_ZoneForGraphMode;
36 40 QVector<std::shared_ptr<DBCatalogue> > m_DisplayedCatalogues;
37 41 bool m_AllEventDisplayed = false;
38 42 QVector<VisualizationGraphWidget *> m_CustomGraphs;
39 43
40 44 VisualizationWidget *m_VisualizationWidget = nullptr;
41 45
42 46 void setEvents(const QVector<std::shared_ptr<DBEvent> > &events, CatalogueEventsWidget *widget)
43 47 {
44 48 widget->ui->treeView->setSortingEnabled(false);
45 49 m_Model->setSourceCatalogues(m_DisplayedCatalogues);
46 50 m_Model->setEvents(events);
47 51 widget->ui->treeView->setSortingEnabled(true);
48 52
49 53 for (auto event : events) {
50 54 if (sqpApp->catalogueController().eventHasChanges(event)) {
51 55 auto index = m_Model->indexOf(event);
52 56 widget->setEventChanges(event, true);
53 57 }
54 58 }
55 59 }
56 60
57 61 void addEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
58 62 {
59 63 treeView->setSortingEnabled(false);
60 64 m_Model->addEvent(event);
61 65 treeView->setSortingEnabled(true);
62 66 }
63 67
64 68 void removeEvent(const std::shared_ptr<DBEvent> &event, QTreeView *treeView)
65 69 {
66 70 treeView->setSortingEnabled(false);
67 71 m_Model->removeEvent(event);
68 72 treeView->setSortingEnabled(true);
69 73 }
70 74
71 75 QStringList getAvailableVisualizationZoneList() const
72 76 {
73 77 if (m_VisualizationWidget) {
74 78 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
75 79 return tab->availableZoneWidgets();
76 80 }
77 81 }
78 82
79 83 return QStringList{};
80 84 }
81 85
82 86 QStringList selectZone(QWidget *parent, const QStringList &selectedZones,
83 bool allowMultiSelection, const QPoint &location)
87 bool allowMultiSelection, bool addNewZoneOption, const QPoint &location)
84 88 {
85 89 auto availableZones = getAvailableVisualizationZoneList();
86 if (availableZones.isEmpty()) {
90 if (!addNewZoneOption && availableZones.isEmpty()) {
87 91 return QStringList{};
88 92 }
89 93
90 QDialog d(parent, Qt::Tool);
91 d.setWindowTitle("Choose a zone");
92 auto layout = new QVBoxLayout{&d};
93 layout->setContentsMargins(0, 0, 0, 0);
94 auto listWidget = new QListWidget{&d};
95 layout->addWidget(listWidget);
94 QActionGroup actionGroup{parent};
95 actionGroup.setExclusive(!allowMultiSelection);
96 96
97 QSet<QListWidgetItem *> checkedItems;
98 for (auto zone : availableZones) {
99 auto item = new QListWidgetItem{zone};
100 item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
101 if (selectedZones.contains(zone)) {
102 item->setCheckState(Qt::Checked);
103 checkedItems << item;
104 }
105 else {
106 item->setCheckState(Qt::Unchecked);
107 }
97 QVector<QAction *> zoneActions;
98
99 QMenu selectionMenu{parent};
108 100
109 listWidget->addItem(item);
101 if (addNewZoneOption) {
102 availableZones.prepend(NEW_ZONE_TEXT);
110 103 }
111 104
112 auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok, &d};
113 layout->addWidget(buttonBox);
114
115 QObject::connect(buttonBox, &QDialogButtonBox::accepted, &d, &QDialog::accept);
116 QObject::connect(buttonBox, &QDialogButtonBox::rejected, &d, &QDialog::reject);
117
118 QObject::connect(listWidget, &QListWidget::itemChanged,
119 [&checkedItems, allowMultiSelection, listWidget](auto item) {
120 if (item->checkState() == Qt::Checked) {
121 if (!allowMultiSelection) {
122 for (auto checkedItem : checkedItems) {
123 listWidget->blockSignals(true);
124 checkedItem->setCheckState(Qt::Unchecked);
125 listWidget->blockSignals(false);
126 }
127
128 checkedItems.clear();
129 }
130 checkedItems << item;
131 }
132 else {
133 checkedItems.remove(item);
134 }
135 });
105 selectionMenu.addSeparator();
106 for (auto zone : availableZones) {
107 auto zoneAction = selectionMenu.addAction(zone);
108 zoneAction->setCheckable(true);
109 zoneAction->setChecked(selectedZones.contains(zone));
110 actionGroup.addAction(zoneAction);
111 zoneActions << zoneAction;
112 }
113
114 auto resultAction = selectionMenu.exec(QCursor::pos());
136 115
137 116 QStringList result;
138 117
139 d.setMinimumWidth(120);
140 d.resize(d.minimumSizeHint());
141 d.move(location);
142 if (d.exec() == QDialog::Accepted) {
143 for (auto item : checkedItems) {
144 result += item->text();
145 }
118 if (resultAction == nullptr) {
119 result = selectedZones;
146 120 }
147 121 else {
148 result = selectedZones;
122 for (auto zoneAction : zoneActions) {
123 if (zoneAction->isChecked()) {
124 result << zoneAction->text();
125 }
126 }
149 127 }
150 128
151 129 return result;
152 130 }
153 131
154 132 void updateForTimeMode(QTreeView *treeView)
155 133 {
156 134 auto selectedRows = treeView->selectionModel()->selectedRows();
157 135
158 136 if (selectedRows.count() == 1) {
159 137 auto event = m_Model->getEvent(selectedRows.first());
160 138 if (event) {
161 139 if (m_VisualizationWidget) {
162 140 if (auto tab = m_VisualizationWidget->currentTabWidget()) {
163 141
164 142 for (auto zoneName : m_ZonesForTimeMode) {
165 143 if (auto zone = tab->getZoneWithName(zoneName)) {
166 144 SqpRange eventRange;
167 145 eventRange.m_TStart = event->getTStart();
168 146 eventRange.m_TEnd = event->getTEnd();
169 147 zone->setZoneRange(eventRange);
170 148 }
171 149 }
172 150 }
173 151 else {
174 152 qCWarning(LOG_CatalogueEventsWidget())
175 153 << "updateTimeZone: no tab found in the visualization";
176 154 }
177 155 }
178 156 else {
179 157 qCWarning(LOG_CatalogueEventsWidget())
180 158 << "updateTimeZone: visualization widget not found";
181 159 }
182 160 }
183 161 }
184 162 else {
185 163 qCWarning(LOG_CatalogueEventsWidget())
186 164 << "updateTimeZone: not compatible with multiple events selected";
187 165 }
188 166 }
189 167
190 168 QVector<SqpRange> getGraphRanges(const std::shared_ptr<DBEvent> &event)
191 169 {
192 170 // Retrieves the range of each product and the maximum size
193 171 QVector<SqpRange> graphRanges;
194 172 double maxDt = 0;
195 173 for (auto eventProduct : event->getEventProducts()) {
196 174 SqpRange eventRange;
197 175 eventRange.m_TStart = eventProduct.getTStart();
198 176 eventRange.m_TEnd = eventProduct.getTEnd();
199 177 graphRanges << eventRange;
200 178
201 179 auto dt = eventRange.m_TEnd - eventRange.m_TStart;
202 180 if (dt > maxDt) {
203 181 maxDt = dt;
204 182 }
205 183 }
206 184
207 185 // Adds the marge
208 186 maxDt *= (100.0 + EVENT_RANGE_MARGE) / 100.0;
209 187
210 188 // Corrects the graph ranges so that they all have the same size
211 189 QVector<SqpRange> correctedGraphRanges;
212 190 for (auto range : graphRanges) {
213 191 auto dt = range.m_TEnd - range.m_TStart;
214 192 auto diff = qAbs((maxDt - dt) / 2.0);
215 193
216 194 SqpRange correctedRange;
217 195 correctedRange.m_TStart = range.m_TStart - diff;
218 196 correctedRange.m_TEnd = range.m_TEnd + diff;
219 197
220 198 correctedGraphRanges << correctedRange;
221 199 }
222 200
223 201 return correctedGraphRanges;
224 202 }
225 203
226 204 void updateForGraphMode(CatalogueEventsWidget *catalogueEventWidget)
227 205 {
228 206 auto selectedRows = catalogueEventWidget->ui->treeView->selectionModel()->selectedRows();
229 207 if (selectedRows.count() != 1) {
230 208 qCWarning(LOG_CatalogueEventsWidget())
231 209 << "updateGraphMode: not compatible with multiple events selected";
232 210 return;
233 211 }
234 212
235 213 if (!m_VisualizationWidget) {
236 214 qCWarning(LOG_CatalogueEventsWidget())
237 215 << "updateGraphMode: visualization widget not found";
238 216 return;
239 217 }
240 218
241 219 auto event = m_Model->getEvent(selectedRows.first());
242 220 if (!event) {
243 221 // A event product is probably selected
244 222 qCInfo(LOG_CatalogueEventsWidget()) << "updateGraphMode: no events are selected";
245 223 return;
246 224 }
247 225
248 226 auto tab = m_VisualizationWidget->currentTabWidget();
249 227 if (!tab) {
250 228 qCWarning(LOG_CatalogueEventsWidget())
251 229 << "updateGraphMode: no tab found in the visualization";
252 230 return;
253 231 }
254 232
233 auto isNewZone = m_ZoneForGraphMode == NEW_ZONE_TEXT;
255 234 auto zone = tab->getZoneWithName(m_ZoneForGraphMode);
256 if (!zone) {
235 if (!isNewZone && !zone) {
257 236 qCWarning(LOG_CatalogueEventsWidget()) << "updateGraphMode: zone not found";
258 237 return;
259 238 }
260 239
261 240 // Closes the previous graph and delete the asociated variables
262 241 for (auto graph : m_CustomGraphs) {
263 242 graph->close();
264 243 auto variables = graph->variables().toVector();
265 244
266 245 QMetaObject::invokeMethod(&sqpApp->variableController(), "deleteVariables",
267 246 Qt::QueuedConnection,
268 247 Q_ARG(QVector<std::shared_ptr<Variable> >, variables));
269 248 }
270 249 m_CustomGraphs.clear();
271 250
272 251 // Closes the remaining graphs inside the zone
273 zone->closeAllGraphs();
252 if (zone) {
253 zone->closeAllGraphs();
254 }
255
256 // Creates the zone if needed
257 if (isNewZone) {
258 zone = tab->createEmptyZone(0);
259 m_ZoneForGraphMode = zone->name();
260 }
274 261
275 262 // Calculates the range of each graph which will be created
276 263 auto graphRange = getGraphRanges(event);
277 264
278 265 // Loops through the event products and create the graph
279 266 auto itRange = graphRange.cbegin();
280 267 for (auto eventProduct : event->getEventProducts()) {
281 268 auto productId = eventProduct.getProductId();
282 269
283 270 auto range = *itRange;
284 271 ++itRange;
285 272
286 273 SqpRange productRange;
287 274 productRange.m_TStart = eventProduct.getTStart();
288 275 productRange.m_TEnd = eventProduct.getTEnd();
289 276
290 277 auto context = new QObject{catalogueEventWidget};
291 278 QObject::connect(
292 279 &sqpApp->variableController(), &VariableController::variableAdded, context,
293 280 [this, catalogueEventWidget, zone, context, event, range, productRange,
294 281 productId](auto variable) {
295 282
296 283 if (variable->metadata().value(DataSourceItem::ID_DATA_KEY).toString()
297 284 == productId) {
298 285 auto graph = zone->createGraph(variable);
299 286 graph->setAutoRangeOnVariableInitialization(false);
300 287
301 288 auto selectionZone
302 289 = graph->addSelectionZone(event->getName(), productRange);
303 290 emit catalogueEventWidget->selectionZoneAdded(event, productId,
304 291 selectionZone);
305 292 m_CustomGraphs << graph;
306 293
307 294 graph->setGraphRange(range, true);
308 295
309 296 // Removes the graph from the graph list if it is closed manually
310 297 QObject::connect(graph, &VisualizationGraphWidget::destroyed,
311 298 [this, graph]() { m_CustomGraphs.removeAll(graph); });
312 299
313 300 delete context; // removes the connection
314 301 }
315 302 },
316 303 Qt::QueuedConnection);
317 304
318 305 QMetaObject::invokeMethod(&sqpApp->dataSourceController(),
319 306 "requestVariableFromProductIdKey", Qt::QueuedConnection,
320 307 Q_ARG(QString, productId));
321 308 }
322 309 }
323 310
324 311 void getSelectedItems(
325 312 QTreeView *treeView, QVector<std::shared_ptr<DBEvent> > &events,
326 313 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > &eventProducts)
327 314 {
328 315 for (auto rowIndex : treeView->selectionModel()->selectedRows()) {
329 316 auto itemType = m_Model->itemTypeOf(rowIndex);
330 317 if (itemType == CatalogueEventsModel::ItemType::Event) {
331 318 events << m_Model->getEvent(rowIndex);
332 319 }
333 320 else if (itemType == CatalogueEventsModel::ItemType::EventProduct) {
334 321 eventProducts << qMakePair(m_Model->getParentEvent(rowIndex),
335 322 m_Model->getEventProduct(rowIndex));
336 323 }
337 324 }
338 325 }
339 326 };
340 327
341 328 CatalogueEventsWidget::CatalogueEventsWidget(QWidget *parent)
342 329 : QWidget(parent),
343 330 ui(new Ui::CatalogueEventsWidget),
344 331 impl{spimpl::make_unique_impl<CatalogueEventsWidgetPrivate>()}
345 332 {
346 333 ui->setupUi(this);
347 334
348 335 impl->m_Model = new CatalogueEventsModel{this};
349 336 ui->treeView->setModel(impl->m_Model);
350 337
351 338 ui->treeView->setSortingEnabled(true);
352 339 ui->treeView->setDragDropMode(QAbstractItemView::DragDrop);
353 340 ui->treeView->setDragEnabled(true);
354 341
355 342
356 343 connect(ui->btnTime, &QToolButton::clicked, [this](auto checked) {
357 344 if (checked) {
358 345 ui->btnChart->setChecked(false);
359 346 impl->m_ZonesForTimeMode
360 = impl->selectZone(this, impl->m_ZonesForTimeMode, true,
347 = impl->selectZone(this, impl->m_ZonesForTimeMode, true, false,
361 348 this->mapToGlobal(ui->btnTime->frameGeometry().center()));
362 349
363 350 impl->updateForTimeMode(ui->treeView);
364 351 }
365 352 });
366 353
367 354 connect(ui->btnChart, &QToolButton::clicked, [this](auto checked) {
368 355 if (checked) {
369 356 ui->btnTime->setChecked(false);
357
370 358 impl->m_ZoneForGraphMode
371 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false,
359 = impl->selectZone(this, {impl->m_ZoneForGraphMode}, false, true,
372 360 this->mapToGlobal(ui->btnChart->frameGeometry().center()))
373 361 .value(0);
374 362
375 363 impl->updateForGraphMode(this);
376 364 }
377 365 });
378 366
379 367 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
380 368 QVector<std::shared_ptr<DBEvent> > events;
381 369 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
382 370 impl->getSelectedItems(ui->treeView, events, eventProducts);
383 371
384 372 if (!events.isEmpty() && eventProducts.isEmpty()) {
385 373
386 374 auto canRemoveEvent
387 375 = !this->isAllEventsDisplayed()
388 376 || (QMessageBox::warning(
389 377 this, tr("Remove Event(s)"),
390 378 tr("The selected event(s) will be permanently removed "
391 379 "from the repository!\nAre you sure you want to continue?"),
392 380 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
393 381 == QMessageBox::Yes);
394 382
395 383 if (canRemoveEvent) {
396 384 for (auto event : events) {
397 385 if (this->isAllEventsDisplayed()) {
398 386 sqpApp->catalogueController().removeEvent(event);
399 387 impl->removeEvent(event, ui->treeView);
400 388 }
401 389 else {
402 390 QVector<std::shared_ptr<DBCatalogue> > modifiedCatalogues;
403 391 for (auto catalogue : this->displayedCatalogues()) {
404 392 if (catalogue->removeEvent(event->getUniqId())) {
405 393 sqpApp->catalogueController().updateCatalogue(catalogue);
406 394 modifiedCatalogues << catalogue;
407 395 }
408 396 }
409 397 if (!modifiedCatalogues.empty()) {
410 398 emit eventCataloguesModified(modifiedCatalogues);
411 399 }
412 400 }
413 401 impl->m_Model->removeEvent(event);
414 402 }
415 403
416 404
417 405 emit this->eventsRemoved(events);
418 406 }
419 407 }
420 408 });
421 409
422 410 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueEventsWidget::emitSelection);
423 411 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
424 412 &CatalogueEventsWidget::emitSelection);
425 413
426 414 ui->btnRemove->setEnabled(false); // Disabled by default when nothing is selected
427 415 connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
428 416 auto isNotMultiSelection = ui->treeView->selectionModel()->selectedRows().count() <= 1;
429 417 ui->btnChart->setEnabled(isNotMultiSelection);
430 418 ui->btnTime->setEnabled(isNotMultiSelection);
431 419
432 420 if (isNotMultiSelection && ui->btnTime->isChecked()) {
433 421 impl->updateForTimeMode(ui->treeView);
434 422 }
435 423 else if (isNotMultiSelection && ui->btnChart->isChecked()) {
436 424 impl->updateForGraphMode(this);
437 425 }
438 426
439 427 QVector<std::shared_ptr<DBEvent> > events;
440 428 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
441 429 impl->getSelectedItems(ui->treeView, events, eventProducts);
442 430 ui->btnRemove->setEnabled(!events.isEmpty() && eventProducts.isEmpty());
443 431 });
444 432
445 433 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
446 434 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Tags,
447 435 QHeaderView::Stretch);
448 436 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Validation,
449 437 QHeaderView::ResizeToContents);
450 438 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::Name,
451 439 QHeaderView::Interactive);
452 440 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TStart,
453 441 QHeaderView::ResizeToContents);
454 442 ui->treeView->header()->setSectionResizeMode((int)CatalogueEventsModel::Column::TEnd,
455 443 QHeaderView::ResizeToContents);
456 444 ui->treeView->header()->setSortIndicatorShown(true);
457 445
458 446 connect(impl->m_Model, &CatalogueEventsModel::modelSorted, [this]() {
459 447 auto allEvents = impl->m_Model->events();
460 448 for (auto event : allEvents) {
461 449 setEventChanges(event, sqpApp->catalogueController().eventHasChanges(event));
462 450 }
463 451 });
464 452
465 453 populateWithAllEvents();
466 454 }
467 455
468 456 CatalogueEventsWidget::~CatalogueEventsWidget()
469 457 {
470 458 delete ui;
471 459 }
472 460
473 461 void CatalogueEventsWidget::setVisualizationWidget(VisualizationWidget *visualization)
474 462 {
475 463 impl->m_VisualizationWidget = visualization;
476 464 }
477 465
478 466 void CatalogueEventsWidget::addEvent(const std::shared_ptr<DBEvent> &event)
479 467 {
480 468 impl->addEvent(event, ui->treeView);
481 469 }
482 470
483 471 void CatalogueEventsWidget::setEventChanges(const std::shared_ptr<DBEvent> &event, bool hasChanges)
484 472 {
485 473 impl->m_Model->refreshEvent(event);
486 474
487 475 auto eventIndex = impl->m_Model->indexOf(event);
488 476 auto validationIndex
489 477 = eventIndex.sibling(eventIndex.row(), (int)CatalogueEventsModel::Column::Validation);
490 478
491 479 if (validationIndex.isValid()) {
492 480 if (hasChanges) {
493 481 if (ui->treeView->indexWidget(validationIndex) == nullptr) {
494 482 auto widget = CatalogueExplorerHelper::buildValidationWidget(
495 483 ui->treeView,
496 484 [this, event]() {
497 485 sqpApp->catalogueController().saveEvent(event);
498 486 setEventChanges(event, false);
499 487 },
500 488 [this, event]() {
501 489 bool removed = false;
502 490 sqpApp->catalogueController().discardEvent(event, removed);
503 491 if (removed) {
504 492 impl->m_Model->removeEvent(event);
505 493 }
506 494 else {
507 495 setEventChanges(event, false);
508 496 impl->m_Model->refreshEvent(event, true);
509 497 }
510 498 emitSelection();
511 499 });
512 500 ui->treeView->setIndexWidget(validationIndex, widget);
513 501 ui->treeView->header()->resizeSection((int)CatalogueEventsModel::Column::Validation,
514 502 QHeaderView::ResizeToContents);
515 503 }
516 504 }
517 505 else {
518 506 // Note: the widget is destroyed
519 507 ui->treeView->setIndexWidget(validationIndex, nullptr);
520 508 }
521 509 }
522 510 else {
523 511 qCWarning(LOG_CatalogueEventsWidget())
524 512 << "setEventChanges: the event is not displayed in the model.";
525 513 }
526 514 }
527 515
528 516 QVector<std::shared_ptr<DBCatalogue> > CatalogueEventsWidget::displayedCatalogues() const
529 517 {
530 518 return impl->m_DisplayedCatalogues;
531 519 }
532 520
533 521 bool CatalogueEventsWidget::isAllEventsDisplayed() const
534 522 {
535 523 return impl->m_AllEventDisplayed;
536 524 }
537 525
538 526 bool CatalogueEventsWidget::isEventDisplayed(const std::shared_ptr<DBEvent> &event) const
539 527 {
540 528 return impl->m_Model->indexOf(event).isValid();
541 529 }
542 530
543 531 void CatalogueEventsWidget::refreshEvent(const std::shared_ptr<DBEvent> &event)
544 532 {
545 533 impl->m_Model->refreshEvent(event, true);
546 534 }
547 535
548 536 void CatalogueEventsWidget::populateWithCatalogues(
549 537 const QVector<std::shared_ptr<DBCatalogue> > &catalogues)
550 538 {
551 539 impl->m_DisplayedCatalogues = catalogues;
552 540 impl->m_AllEventDisplayed = false;
553 541
554 542 QSet<QUuid> eventIds;
555 543 QVector<std::shared_ptr<DBEvent> > events;
556 544
557 545 for (auto catalogue : catalogues) {
558 546 auto catalogueEvents = sqpApp->catalogueController().retrieveEventsFromCatalogue(catalogue);
559 547 for (auto event : catalogueEvents) {
560 548 if (!eventIds.contains(event->getUniqId())) {
561 549 events << event;
562 550 eventIds.insert(event->getUniqId());
563 551 }
564 552 }
565 553 }
566 554
567 555 impl->setEvents(events, this);
568 556 }
569 557
570 558 void CatalogueEventsWidget::populateWithAllEvents()
571 559 {
572 560 impl->m_DisplayedCatalogues.clear();
573 561 impl->m_AllEventDisplayed = true;
574 562
575 563 auto allEvents = sqpApp->catalogueController().retrieveAllEvents();
576 564
577 565 QVector<std::shared_ptr<DBEvent> > events;
578 566 for (auto event : allEvents) {
579 567 events << event;
580 568 }
581 569
582 570 impl->setEvents(events, this);
583 571 }
584 572
585 573 void CatalogueEventsWidget::clear()
586 574 {
587 575 impl->m_DisplayedCatalogues.clear();
588 576 impl->m_AllEventDisplayed = false;
589 577 impl->setEvents({}, this);
590 578 }
591 579
592 580 void CatalogueEventsWidget::refresh()
593 581 {
594 582 if (isAllEventsDisplayed()) {
595 583 populateWithAllEvents();
596 584 }
597 585 else if (!impl->m_DisplayedCatalogues.isEmpty()) {
598 586 populateWithCatalogues(impl->m_DisplayedCatalogues);
599 587 }
600 588 }
601 589
602 590 void CatalogueEventsWidget::emitSelection()
603 591 {
604 592 QVector<std::shared_ptr<DBEvent> > events;
605 593 QVector<QPair<std::shared_ptr<DBEvent>, std::shared_ptr<DBEventProduct> > > eventProducts;
606 594 impl->getSelectedItems(ui->treeView, events, eventProducts);
607 595
608 596 if (!events.isEmpty() && eventProducts.isEmpty()) {
609 597 emit eventsSelected(events);
610 598 }
611 599 else if (events.isEmpty() && !eventProducts.isEmpty()) {
612 600 emit eventProductsSelected(eventProducts);
613 601 }
614 602 else {
615 603 emit selectionCleared();
616 604 }
617 605 }
618 606
619 607
620 608 void CatalogueEventsWidget::keyPressEvent(QKeyEvent *event)
621 609 {
622 610 switch (event->key()) {
623 611 case Qt::Key_Delete: {
624 612 ui->btnRemove->click();
625 613 }
626 614 default:
627 615 break;
628 616 }
629 617 }
@@ -1,203 +1,207
1 1 #include "Catalogue/CatalogueExplorer.h"
2 2 #include "ui_CatalogueExplorer.h"
3 3
4 4 #include <Catalogue/CatalogueActionManager.h>
5 5 #include <Catalogue/CatalogueController.h>
6 6 #include <SqpApplication.h>
7 7 #include <Visualization/VisualizationGraphWidget.h>
8 8 #include <Visualization/VisualizationSelectionZoneItem.h>
9 9 #include <Visualization/VisualizationWidget.h>
10 10
11 11 #include <DBCatalogue.h>
12 12 #include <DBEvent.h>
13 13 #include <DBEventProduct.h>
14 14
15 15 #include <unordered_map>
16 16
17 17 struct CatalogueExplorer::CatalogueExplorerPrivate {
18 18 CatalogueActionManager m_ActionManager;
19 19 std::unordered_map<std::shared_ptr<DBEvent>, QVector<VisualizationSelectionZoneItem *> >
20 20 m_SelectionZonesPerEvents;
21 21
22 22 QMetaObject::Connection m_Conn;
23 23
24 24 CatalogueExplorerPrivate(CatalogueExplorer *catalogueExplorer)
25 25 : m_ActionManager(catalogueExplorer)
26 26 {
27 27 }
28 28 };
29 29
30 30 CatalogueExplorer::CatalogueExplorer(QWidget *parent)
31 31 : QDialog(parent, Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint),
32 32 ui(new Ui::CatalogueExplorer),
33 33 impl{spimpl::make_unique_impl<CatalogueExplorerPrivate>(this)}
34 34 {
35 35 ui->setupUi(this);
36 36
37 37 impl->m_ActionManager.installSelectionZoneActions();
38 38
39 39 // Updates events and inspector when something is selected in the catalogue widget
40 40 connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSelected, [this](auto catalogues) {
41 41 if (catalogues.count() == 1) {
42 42 ui->inspector->setCatalogue(catalogues.first());
43 43 }
44 44 else {
45 45 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
46 46 }
47 47
48 48 ui->events->populateWithCatalogues(catalogues);
49 49 });
50 50
51 51 connect(ui->catalogues, &CatalogueSideBarWidget::databaseSelected, [this](auto databases) {
52 52 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
53 53 });
54 54
55 55 connect(ui->catalogues, &CatalogueSideBarWidget::trashSelected, [this]() {
56 56 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
57 57 ui->events->clear();
58 58 });
59 59
60 60 connect(ui->catalogues, &CatalogueSideBarWidget::allEventsSelected, [this]() {
61 61 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
62 62 ui->events->populateWithAllEvents();
63 63 });
64 64
65 65 connect(ui->catalogues, &CatalogueSideBarWidget::databaseSelected, [this](auto databaseList) {
66 66 QVector<std::shared_ptr<DBCatalogue> > catalogueList;
67 67 for (auto database : databaseList) {
68 68 catalogueList.append(ui->catalogues->getCatalogues(database));
69 69 }
70 70 ui->events->populateWithCatalogues(catalogueList);
71 71 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
72 72 });
73 73
74 74 connect(ui->catalogues, &CatalogueSideBarWidget::selectionCleared, [this]() {
75 75 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
76 76 ui->events->clear();
77 77 });
78 78
79 79 connect(ui->catalogues, &CatalogueSideBarWidget::catalogueSaved, ui->events,
80 80 &CatalogueEventsWidget::refresh);
81 81
82 connect(ui->catalogues, &CatalogueSideBarWidget::catalogueListChanged,
83 [this]() { impl->m_ActionManager.refreshCreateInCatalogueAction(); });
84
82 85 // Updates the inspectot when something is selected in the events
83 86 connect(ui->events, &CatalogueEventsWidget::eventsSelected, [this](auto events) {
84 87 if (events.count() == 1) {
85 88 ui->inspector->setEvent(events.first());
86 89 }
87 90 else {
88 91 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
89 92 }
90 93 });
91 94
92 95 connect(ui->events, &CatalogueEventsWidget::eventProductsSelected, [this](auto eventProducts) {
93 96 if (eventProducts.count() == 1) {
94 97 ui->inspector->setEventProduct(eventProducts.first().first,
95 98 eventProducts.first().second);
96 99 }
97 100 else {
98 101 ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty);
99 102 }
100 103 });
101 104
102 105 connect(ui->events, &CatalogueEventsWidget::selectionCleared,
103 106 [this]() { ui->inspector->showPage(CatalogueInspectorWidget::Page::Empty); });
104 107
105 108 // Manage Selection Zones associated to events
106 109 connect(ui->events, &CatalogueEventsWidget::selectionZoneAdded,
107 110 [this](auto event, auto productId, auto zone) {
108 111 this->addSelectionZoneItem(event, productId, zone);
109 112 });
110 113
111 114 connect(ui->events, &CatalogueEventsWidget::eventsRemoved, [this](auto events) {
112 115 for (auto event : events) {
113 116 auto associatedSelectionZonesIt = impl->m_SelectionZonesPerEvents.find(event);
114 117 if (associatedSelectionZonesIt != impl->m_SelectionZonesPerEvents.cend()) {
115 118 for (auto selectionZone : associatedSelectionZonesIt->second) {
116 119 auto parentGraph = selectionZone->parentGraphWidget();
117 120 parentGraph->removeSelectionZone(selectionZone);
118 121 }
119 122
120 123 impl->m_SelectionZonesPerEvents.erase(event);
121 124 }
122 125 }
123 126 });
124 127
125 128 // Updates changes from the inspector
126 129 connect(ui->inspector, &CatalogueInspectorWidget::catalogueUpdated, [this](auto catalogue) {
127 130 sqpApp->catalogueController().updateCatalogue(catalogue);
128 131 ui->catalogues->setCatalogueChanges(catalogue, true);
132 impl->m_ActionManager.refreshCreateInCatalogueAction();
129 133 });
130 134
131 135 connect(ui->inspector, &CatalogueInspectorWidget::eventUpdated, [this](auto event) {
132 136 sqpApp->catalogueController().updateEvent(event);
133 137 ui->events->setEventChanges(event, true);
134 138 });
135 139
136 140 connect(ui->inspector, &CatalogueInspectorWidget::eventProductUpdated,
137 141 [this](auto event, auto eventProduct) {
138 142 sqpApp->catalogueController().updateEventProduct(eventProduct);
139 143 ui->events->setEventChanges(event, true);
140 144 });
141 145
142 146 connect(ui->events, &CatalogueEventsWidget::eventCataloguesModified,
143 147 [this](const QVector<std::shared_ptr<DBCatalogue> > &catalogues) {
144 148 for (auto catalogue : catalogues) {
145 149 ui->catalogues->setCatalogueChanges(catalogue, true);
146 150 }
147 151 });
148 152 }
149 153
150 154 CatalogueExplorer::~CatalogueExplorer()
151 155 {
152 156 disconnect(impl->m_Conn);
153 157 delete ui;
154 158 }
155 159
156 160 void CatalogueExplorer::setVisualizationWidget(VisualizationWidget *visualization)
157 161 {
158 162 ui->events->setVisualizationWidget(visualization);
159 163 }
160 164
161 165 CatalogueEventsWidget &CatalogueExplorer::eventsWidget() const
162 166 {
163 167 return *ui->events;
164 168 }
165 169
166 170 CatalogueSideBarWidget &CatalogueExplorer::sideBarWidget() const
167 171 {
168 172 return *ui->catalogues;
169 173 }
170 174
171 175 void CatalogueExplorer::clearSelectionZones()
172 176 {
173 177 impl->m_SelectionZonesPerEvents.clear();
174 178 }
175 179
176 180 void CatalogueExplorer::addSelectionZoneItem(const std::shared_ptr<DBEvent> &event,
177 181 const QString &productId,
178 182 VisualizationSelectionZoneItem *selectionZone)
179 183 {
180 184 impl->m_SelectionZonesPerEvents[event] << selectionZone;
181 185 connect(selectionZone, &VisualizationSelectionZoneItem::rangeEdited,
182 186 [event, productId, this](auto range) {
183 187 auto productList = event->getEventProducts();
184 188 for (auto &product : productList) {
185 189 if (product.getProductId() == productId) {
186 190 product.setTStart(range.m_TStart);
187 191 product.setTEnd(range.m_TEnd);
188 192 }
189 193 }
190 194 event->setEventProducts(productList);
191 195 sqpApp->catalogueController().updateEvent(event);
192 196 ui->events->refreshEvent(event);
193 197 ui->events->setEventChanges(event, true);
194 198 ui->inspector->refresh();
195 199 });
196 200
197 201 impl->m_Conn = connect(selectionZone, &VisualizationSelectionZoneItem::destroyed,
198 202 [event, selectionZone, this]() {
199 203 if (!impl->m_SelectionZonesPerEvents.empty()) {
200 204 impl->m_SelectionZonesPerEvents[event].removeAll(selectionZone);
201 205 }
202 206 });
203 207 }
@@ -1,447 +1,461
1 1 #include "Catalogue/CatalogueSideBarWidget.h"
2 2 #include "ui_CatalogueSideBarWidget.h"
3 3 #include <SqpApplication.h>
4 4
5 5 #include <Catalogue/CatalogueController.h>
6 6 #include <Catalogue/CatalogueExplorerHelper.h>
7 7 #include <Catalogue/CatalogueTreeItems/CatalogueTextTreeItem.h>
8 8 #include <Catalogue/CatalogueTreeItems/CatalogueTreeItem.h>
9 9 #include <Catalogue/CatalogueTreeModel.h>
10 10 #include <CatalogueDao.h>
11 11 #include <Common/MimeTypesDef.h>
12 12 #include <ComparaisonPredicate.h>
13 13 #include <DBCatalogue.h>
14 14
15 15 #include <QKeyEvent>
16 16 #include <QMenu>
17 17 #include <QMessageBox>
18 18 #include <QMimeData>
19 19
20 20 Q_LOGGING_CATEGORY(LOG_CatalogueSideBarWidget, "CatalogueSideBarWidget")
21 21
22 22
23 23 constexpr auto ALL_EVENT_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 1;
24 24 constexpr auto TRASH_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 2;
25 25 constexpr auto CATALOGUE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 3;
26 26 constexpr auto DATABASE_ITEM_TYPE = CatalogueAbstractTreeItem::DEFAULT_TYPE + 4;
27 27
28 const auto DEFAULT_CATALOGUE_NAME = QObject::tr("Catalogue");
29
28 30
29 31 struct CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate {
30 32
31 33 CatalogueTreeModel *m_TreeModel = nullptr;
32 34
33 35 void configureTreeWidget(QTreeView *treeView);
34 36 QModelIndex addDatabaseItem(const QString &name);
35 37 CatalogueAbstractTreeItem *getDatabaseItem(const QString &name);
36 38 CatalogueAbstractTreeItem *addCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue,
37 39 const QModelIndex &databaseIndex);
38 40
39 41 CatalogueTreeItem *getCatalogueItem(const std::shared_ptr<DBCatalogue> &catalogue) const;
40 42 void setHasChanges(bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget);
41 43 bool hasChanges(const QModelIndex &index, QTreeView *treeView);
42 44
43 45 int selectionType(QTreeView *treeView) const
44 46 {
45 47 auto selectedItems = treeView->selectionModel()->selectedRows();
46 48 if (selectedItems.isEmpty()) {
47 49 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
48 50 }
49 51 else {
50 52 auto firstIndex = selectedItems.first();
51 53 auto firstItem = m_TreeModel->item(firstIndex);
52 54 if (!firstItem) {
53 55 Q_ASSERT(false);
54 56 return CatalogueAbstractTreeItem::DEFAULT_TYPE;
55 57 }
56 58 auto selectionType = firstItem->type();
57 59
58 60 for (auto itemIndex : selectedItems) {
59 61 auto item = m_TreeModel->item(itemIndex);
60 62 if (!item || item->type() != selectionType) {
61 63 // Incoherent multi selection
62 64 selectionType = CatalogueAbstractTreeItem::DEFAULT_TYPE;
63 65 break;
64 66 }
65 67 }
66 68
67 69 return selectionType;
68 70 }
69 71 }
70 72
71 73 QVector<std::shared_ptr<DBCatalogue> > selectedCatalogues(QTreeView *treeView) const
72 74 {
73 75 QVector<std::shared_ptr<DBCatalogue> > catalogues;
74 76 auto selectedItems = treeView->selectionModel()->selectedRows();
75 77 for (auto itemIndex : selectedItems) {
76 78 auto item = m_TreeModel->item(itemIndex);
77 79 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
78 80 catalogues.append(static_cast<CatalogueTreeItem *>(item)->catalogue());
79 81 }
80 82 }
81 83
82 84 return catalogues;
83 85 }
84 86
85 87 QStringList selectedRepositories(QTreeView *treeView) const
86 88 {
87 89 QStringList repositories;
88 90 auto selectedItems = treeView->selectionModel()->selectedRows();
89 91 for (auto itemIndex : selectedItems) {
90 92 auto item = m_TreeModel->item(itemIndex);
91 93 if (item && item->type() == DATABASE_ITEM_TYPE) {
92 94 repositories.append(item->text());
93 95 }
94 96 }
95 97
96 98 return repositories;
97 99 }
98 100 };
99 101
100 102 CatalogueSideBarWidget::CatalogueSideBarWidget(QWidget *parent)
101 103 : QWidget(parent),
102 104 ui(new Ui::CatalogueSideBarWidget),
103 105 impl{spimpl::make_unique_impl<CatalogueSideBarWidgetPrivate>()}
104 106 {
105 107 ui->setupUi(this);
106 108
107 109 impl->m_TreeModel = new CatalogueTreeModel(this);
108 110 ui->treeView->setModel(impl->m_TreeModel);
109 111
110 112 impl->configureTreeWidget(ui->treeView);
113 emit catalogueListChanged();
111 114
112 115 ui->treeView->header()->setStretchLastSection(false);
113 116 ui->treeView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
114 117 ui->treeView->header()->setSectionResizeMode((int)CatalogueTreeModel::Column::Name,
115 118 QHeaderView::Stretch);
116 119
117 120 connect(ui->treeView, &QTreeView::clicked, this, &CatalogueSideBarWidget::emitSelection);
118 121 connect(ui->treeView->selectionModel(), &QItemSelectionModel::currentChanged, this,
119 122 &CatalogueSideBarWidget::emitSelection);
120 123
121 124
122 125 connect(ui->btnAdd, &QToolButton::clicked, [this]() {
123 126 auto catalogue = std::make_shared<DBCatalogue>();
124 catalogue->setName(QString("Cat"));
127 catalogue->setName(DEFAULT_CATALOGUE_NAME);
125 128 sqpApp->catalogueController().addCatalogue(catalogue);
126 129 auto item = this->addCatalogue(catalogue, REPOSITORY_DEFAULT);
127 130 this->setCatalogueChanges(catalogue, true);
128 131 ui->treeView->edit(impl->m_TreeModel->indexOf(item));
129 132
130 133 });
131 134
132 135
133 136 connect(impl->m_TreeModel, &CatalogueTreeModel::itemDropped,
134 137 [this](auto index, auto mimeData, auto action) {
135 138 auto item = impl->m_TreeModel->item(index);
139
136 140 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
137 141 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
138 142 this->setCatalogueChanges(catalogue, true);
139 143 }
140 144
141 145 if (action == Qt::MoveAction) {
142 146 /// Display a save button on source catalogues
143 147 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
144 148 mimeData->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
145 149 for (auto catalogue : sourceCatalogues) {
146 150 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
151 catalogueItem->replaceCatalogue(catalogue);
147 152 this->setCatalogueChanges(catalogue, true);
148 153 }
149 154 }
155
156 this->emitSelection();
150 157 }
151 158 });
152 159
153 160 connect(ui->btnRemove, &QToolButton::clicked, [this]() {
154 161 QVector<QPair<std::shared_ptr<DBCatalogue>, CatalogueAbstractTreeItem *> >
155 162 cataloguesToItems;
156 163 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
157 164
158 165 for (auto index : selectedIndexes) {
159 166 auto item = impl->m_TreeModel->item(index);
160 167 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
161 168 auto catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
162 169 cataloguesToItems << qMakePair(catalogue, item);
163 170 }
164 171 }
165 172
166 173 if (!cataloguesToItems.isEmpty()) {
167 174
168 175 if (QMessageBox::warning(this, tr("Remove Catalogue(s)"),
169 176 tr("The selected catalogues(s) will be completly removed "
170 177 "from the repository!\nAre you sure you want to continue?"),
171 178 QMessageBox::Yes | QMessageBox::No, QMessageBox::No)
172 179 == QMessageBox::Yes) {
173 180
174 181 for (auto catalogueToItem : cataloguesToItems) {
175 182 sqpApp->catalogueController().removeCatalogue(catalogueToItem.first);
176 183 impl->m_TreeModel->removeChildItem(
177 184 catalogueToItem.second,
178 185 impl->m_TreeModel->indexOf(catalogueToItem.second->parent()));
179 186 }
180 187 emitSelection();
188 emit catalogueListChanged();
181 189 }
182 190 }
183 191 });
184 192
185 193 connect(impl->m_TreeModel, &CatalogueTreeModel::itemRenamed, [this](auto index) {
186 194 auto selectedIndexes = ui->treeView->selectionModel()->selectedRows();
187 195 if (selectedIndexes.contains(index)) {
188 196 this->emitSelection();
189 197 }
190 198 impl->setHasChanges(true, index, this);
199 emit this->catalogueListChanged();
191 200 });
192 201
193 202 ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);
194 203 connect(ui->treeView, &QTreeView::customContextMenuRequested, this,
195 204 &CatalogueSideBarWidget::onContextMenuRequested);
196 205 }
197 206
198 207 CatalogueSideBarWidget::~CatalogueSideBarWidget()
199 208 {
200 209 delete ui;
201 210 }
202 211
203 212 CatalogueAbstractTreeItem *
204 213 CatalogueSideBarWidget::addCatalogue(const std::shared_ptr<DBCatalogue> &catalogue,
205 214 const QString &repository)
206 215 {
207 216 auto repositoryItem = impl->getDatabaseItem(repository);
208 return impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem));
217 auto catalogueItem
218 = impl->addCatalogueItem(catalogue, impl->m_TreeModel->indexOf(repositoryItem));
219
220 emit catalogueListChanged();
221
222 return catalogueItem;
209 223 }
210 224
211 225 void CatalogueSideBarWidget::setCatalogueChanges(const std::shared_ptr<DBCatalogue> &catalogue,
212 226 bool hasChanges)
213 227 {
214 228 if (auto catalogueItem = impl->getCatalogueItem(catalogue)) {
215 229 auto index = impl->m_TreeModel->indexOf(catalogueItem);
216 230 impl->setHasChanges(hasChanges, index, this);
217 231 // catalogueItem->refresh();
218 232 }
219 233 }
220 234
221 235 QVector<std::shared_ptr<DBCatalogue> >
222 236 CatalogueSideBarWidget::getCatalogues(const QString &repository) const
223 237 {
224 238 QVector<std::shared_ptr<DBCatalogue> > result;
225 239 auto repositoryItem = impl->getDatabaseItem(repository);
226 240 for (auto child : repositoryItem->children()) {
227 241 if (child->type() == CATALOGUE_ITEM_TYPE) {
228 242 auto catalogueItem = static_cast<CatalogueTreeItem *>(child);
229 243 result << catalogueItem->catalogue();
230 244 }
231 245 else {
232 246 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogues: invalid structure";
233 247 }
234 248 }
235 249
236 250 return result;
237 251 }
238 252
239 253 void CatalogueSideBarWidget::emitSelection()
240 254 {
241 255 auto selectionType = impl->selectionType(ui->treeView);
242 256
243 257 switch (selectionType) {
244 258 case CATALOGUE_ITEM_TYPE:
245 259 emit this->catalogueSelected(impl->selectedCatalogues(ui->treeView));
246 260 break;
247 261 case DATABASE_ITEM_TYPE:
248 262 emit this->databaseSelected(impl->selectedRepositories(ui->treeView));
249 263 break;
250 264 case ALL_EVENT_ITEM_TYPE:
251 265 emit this->allEventsSelected();
252 266 break;
253 267 case TRASH_ITEM_TYPE:
254 268 emit this->trashSelected();
255 269 break;
256 270 default:
257 271 emit this->selectionCleared();
258 272 break;
259 273 }
260 274 }
261 275
262 276 void CatalogueSideBarWidget::onContextMenuRequested(const QPoint &pos)
263 277 {
264 278 QMenu menu{this};
265 279
266 280 auto currentIndex = ui->treeView->currentIndex();
267 281 auto currentItem = impl->m_TreeModel->item(currentIndex);
268 282 if (!currentItem) {
269 283 return;
270 284 }
271 285
272 286 switch (currentItem->type()) {
273 287 case CATALOGUE_ITEM_TYPE:
274 288 menu.addAction("Rename", [this, currentIndex]() { ui->treeView->edit(currentIndex); });
275 289 break;
276 290 case DATABASE_ITEM_TYPE:
277 291 break;
278 292 case ALL_EVENT_ITEM_TYPE:
279 293 break;
280 294 case TRASH_ITEM_TYPE:
281 295 menu.addAction("Empty Trash", []() {
282 296 // TODO
283 297 });
284 298 break;
285 299 default:
286 300 break;
287 301 }
288 302
289 303 if (!menu.isEmpty()) {
290 304 menu.exec(ui->treeView->mapToGlobal(pos));
291 305 }
292 306 }
293 307
294 308 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::configureTreeWidget(QTreeView *treeView)
295 309 {
296 310 auto allEventsItem = new CatalogueTextTreeItem{QIcon{":/icones/allEvents.png"}, "All Events",
297 311 ALL_EVENT_ITEM_TYPE};
298 312 auto allEventIndex = m_TreeModel->addTopLevelItem(allEventsItem);
299 313 treeView->setCurrentIndex(allEventIndex);
300 314
301 315 auto trashItem
302 316 = new CatalogueTextTreeItem{QIcon{":/icones/trash.png"}, "Trash", TRASH_ITEM_TYPE};
303 317 m_TreeModel->addTopLevelItem(trashItem);
304 318
305 319 auto separator = new QFrame{treeView};
306 320 separator->setFrameShape(QFrame::HLine);
307 321 auto separatorItem
308 322 = new CatalogueTextTreeItem{QIcon{}, QString{}, CatalogueAbstractTreeItem::DEFAULT_TYPE};
309 323 separatorItem->setEnabled(false);
310 324 auto separatorIndex = m_TreeModel->addTopLevelItem(separatorItem);
311 325 treeView->setIndexWidget(separatorIndex, separator);
312 326
313 327 auto repositories = sqpApp->catalogueController().getRepositories();
314 328 for (auto dbname : repositories) {
315 329 auto dbIndex = addDatabaseItem(dbname);
316 330 auto catalogues = sqpApp->catalogueController().retrieveCatalogues(dbname);
317 331 for (auto catalogue : catalogues) {
318 332 addCatalogueItem(catalogue, dbIndex);
319 333 }
320 334 }
321 335
322 336 treeView->expandAll();
323 337 }
324 338
325 339 QModelIndex
326 340 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addDatabaseItem(const QString &name)
327 341 {
328 342 auto databaseItem
329 343 = new CatalogueTextTreeItem{QIcon{":/icones/database.png"}, {name}, DATABASE_ITEM_TYPE};
330 344 auto databaseIndex = m_TreeModel->addTopLevelItem(databaseItem);
331 345
332 346 return databaseIndex;
333 347 }
334 348
335 349 CatalogueAbstractTreeItem *
336 350 CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getDatabaseItem(const QString &name)
337 351 {
338 352 for (auto item : m_TreeModel->topLevelItems()) {
339 353 if (item->type() == DATABASE_ITEM_TYPE && item->text() == name) {
340 354 return item;
341 355 }
342 356 }
343 357
344 358 return nullptr;
345 359 }
346 360
347 361 CatalogueAbstractTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::addCatalogueItem(
348 362 const std::shared_ptr<DBCatalogue> &catalogue, const QModelIndex &databaseIndex)
349 363 {
350 364 auto catalogueItem
351 365 = new CatalogueTreeItem{catalogue, QIcon{":/icones/catalogue.png"}, CATALOGUE_ITEM_TYPE};
352 366 m_TreeModel->addChildItem(catalogueItem, databaseIndex);
353 367
354 368 return catalogueItem;
355 369 }
356 370
357 371 CatalogueTreeItem *CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::getCatalogueItem(
358 372 const std::shared_ptr<DBCatalogue> &catalogue) const
359 373 {
360 374 for (auto item : m_TreeModel->topLevelItems()) {
361 375 if (item->type() == DATABASE_ITEM_TYPE) {
362 376 for (auto childItem : item->children()) {
363 377 if (childItem->type() == CATALOGUE_ITEM_TYPE) {
364 378 auto catalogueItem = static_cast<CatalogueTreeItem *>(childItem);
365 379 if (catalogueItem->catalogue()->getUniqId() == catalogue->getUniqId()) {
366 380 return catalogueItem;
367 381 }
368 382 }
369 383 else {
370 384 qCWarning(LOG_CatalogueSideBarWidget()) << "getCatalogueItem: Invalid tree "
371 385 "structure. A database item should "
372 386 "only contain catalogues.";
373 387 Q_ASSERT(false);
374 388 }
375 389 }
376 390 }
377 391 }
378 392
379 393 return nullptr;
380 394 }
381 395
382 396 void CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::setHasChanges(
383 397 bool value, const QModelIndex &index, CatalogueSideBarWidget *sideBarWidget)
384 398 {
385 399 std::shared_ptr<DBCatalogue> catalogue = nullptr;
386 400 auto item = m_TreeModel->item(index);
387 401 if (item && item->type() == CATALOGUE_ITEM_TYPE) {
388 402 catalogue = static_cast<CatalogueTreeItem *>(item)->catalogue();
389 403 }
390 404
391 405 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
392 406 if (value) {
393 407 if (!hasChanges(validationIndex, sideBarWidget->ui->treeView)) {
394 408 auto widget = CatalogueExplorerHelper::buildValidationWidget(
395 409 sideBarWidget->ui->treeView,
396 410 [this, validationIndex, sideBarWidget, catalogue]() {
397 411 if (catalogue) {
398 412 sqpApp->catalogueController().saveCatalogue(catalogue);
399 413 emit sideBarWidget->catalogueSaved(catalogue);
400 414 }
401 415 setHasChanges(false, validationIndex, sideBarWidget);
402 416 },
403 417 [this, validationIndex, sideBarWidget, catalogue, item]() {
404 418 if (catalogue) {
405 419 bool removed;
406 420 sqpApp->catalogueController().discardCatalogue(catalogue, removed);
407 421
408 422 if (removed) {
409 423 m_TreeModel->removeChildItem(item,
410 424 m_TreeModel->indexOf(item->parent()));
411 425 }
412 426 else {
413 427 m_TreeModel->refresh(m_TreeModel->indexOf(item));
414 428 setHasChanges(false, validationIndex, sideBarWidget);
415 429 }
416 430 sideBarWidget->emitSelection();
417 431 }
418 432 });
419 433 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, widget);
420 434 sideBarWidget->ui->treeView->header()->resizeSection(
421 435 (int)CatalogueTreeModel::Column::Validation, QHeaderView::ResizeToContents);
422 436 }
423 437 }
424 438 else {
425 439 // Note: the widget is destroyed
426 440 sideBarWidget->ui->treeView->setIndexWidget(validationIndex, nullptr);
427 441 }
428 442 }
429 443
430 444 bool CatalogueSideBarWidget::CatalogueSideBarWidgetPrivate::hasChanges(const QModelIndex &index,
431 445 QTreeView *treeView)
432 446 {
433 447 auto validationIndex = index.sibling(index.row(), (int)CatalogueTreeModel::Column::Validation);
434 448 return treeView->indexWidget(validationIndex) != nullptr;
435 449 }
436 450
437 451
438 452 void CatalogueSideBarWidget::keyPressEvent(QKeyEvent *event)
439 453 {
440 454 switch (event->key()) {
441 455 case Qt::Key_Delete: {
442 456 ui->btnRemove->click();
443 457 }
444 458 default:
445 459 break;
446 460 }
447 461 }
@@ -1,126 +1,131
1 1 #include "Catalogue/CatalogueTreeItems/CatalogueTreeItem.h"
2 2 #include <Catalogue/CatalogueExplorerHelper.h>
3 3
4 4 #include <Catalogue/CatalogueController.h>
5 5 #include <Common/MimeTypesDef.h>
6 6 #include <QIcon>
7 7 #include <QMimeData>
8 8 #include <SqpApplication.h>
9 9
10 10 #include <memory>
11 11
12 12 #include <DBCatalogue.h>
13 13
14 14 struct CatalogueTreeItem::CatalogueTreeItemPrivate {
15 15
16 16 std::shared_ptr<DBCatalogue> m_Catalogue;
17 17 QIcon m_Icon;
18 18
19 19 CatalogueTreeItemPrivate(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon)
20 20 : m_Catalogue(catalogue), m_Icon(icon)
21 21 {
22 22 }
23 23 };
24 24
25 25
26 26 CatalogueTreeItem::CatalogueTreeItem(std::shared_ptr<DBCatalogue> catalogue, const QIcon &icon,
27 27 int type)
28 28 : CatalogueAbstractTreeItem(type),
29 29 impl{spimpl::make_unique_impl<CatalogueTreeItemPrivate>(catalogue, icon)}
30 30 {
31 31 }
32 32
33 33 QVariant CatalogueTreeItem::data(int column, int role) const
34 34 {
35 35 if (column == 0) {
36 36 switch (role) {
37 37 case Qt::EditRole: // fallthrough
38 38 case Qt::DisplayRole:
39 39 return impl->m_Catalogue->getName();
40 40 case Qt::DecorationRole:
41 41 return impl->m_Icon;
42 42 default:
43 43 break;
44 44 }
45 45 }
46 46
47 47 return QVariant();
48 48 }
49 49
50 50 bool CatalogueTreeItem::setData(int column, int role, const QVariant &value)
51 51 {
52 52 bool result = false;
53 53
54 54 if (role == Qt::EditRole && column == 0) {
55 55 auto newName = value.toString();
56 56 if (newName != impl->m_Catalogue->getName()) {
57 57 impl->m_Catalogue->setName(newName);
58 58 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
59 59 result = true;
60 60 }
61 61 }
62 62
63 63 return result;
64 64 }
65 65
66 66 Qt::ItemFlags CatalogueTreeItem::flags(int column) const
67 67 {
68 68 if (column == 0) {
69 69 return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable
70 70 | Qt::ItemIsDropEnabled;
71 71 }
72 72 else {
73 73 return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
74 74 }
75 75 }
76 76
77 77 bool CatalogueTreeItem::canDropMimeData(const QMimeData *data, Qt::DropAction action)
78 78 {
79 79 // Check that the event is not dropped on the same catalogue
80 80 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
81 81 data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
82 82 for (auto catalogue : sourceCatalogues) {
83 83 if (catalogue->getUniqId() == impl->m_Catalogue->getUniqId()) {
84 84 return false;
85 85 }
86 86 }
87 87
88 88 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
89 89 auto canDrop = data->hasFormat(MIME_TYPE_EVENT_LIST);
90 90
91 91 for (auto event : events) {
92 92 canDrop &= (event->getRepository() == impl->m_Catalogue->getRepository());
93 93 }
94 94
95 95 return canDrop;
96 96 }
97 97
98 98 bool CatalogueTreeItem::dropMimeData(const QMimeData *data, Qt::DropAction action)
99 99 {
100 100 Q_ASSERT(canDropMimeData(data, action));
101 101 // Warning: Check that the events aren't already in the catalogue
102 102 // No need to check check for the repository: inter-repository drop is forbidden in
103 103 // canDropMimeData
104 104
105 105 auto events = sqpApp->catalogueController().eventsForMimeData(data->data(MIME_TYPE_EVENT_LIST));
106 106 auto sourceCatalogues = sqpApp->catalogueController().cataloguesForMimeData(
107 107 data->data(MIME_TYPE_SOURCE_CATALOGUE_LIST));
108 108
109 109 for (auto event : events) {
110 110
111 111 if (action == Qt::MoveAction) {
112 112 for (auto catalogue : sourceCatalogues) {
113 113 catalogue->removeEvent(event->getUniqId());
114 114 sqpApp->catalogueController().updateCatalogue(catalogue);
115 115 }
116 116 }
117 117
118 118 impl->m_Catalogue->addEvent(event->getUniqId());
119 119 sqpApp->catalogueController().updateCatalogue(impl->m_Catalogue);
120 120 }
121 121 }
122 122
123 123 std::shared_ptr<DBCatalogue> CatalogueTreeItem::catalogue() const
124 124 {
125 125 return impl->m_Catalogue;
126 126 }
127
128 void CatalogueTreeItem::replaceCatalogue(const std::shared_ptr<DBCatalogue> &catalogue)
129 {
130 impl->m_Catalogue = catalogue;
131 }
@@ -1,1053 +1,1077
1 1 #include "Visualization/VisualizationGraphWidget.h"
2 2 #include "Visualization/IVisualizationWidgetVisitor.h"
3 3 #include "Visualization/VisualizationCursorItem.h"
4 4 #include "Visualization/VisualizationDefs.h"
5 5 #include "Visualization/VisualizationGraphHelper.h"
6 6 #include "Visualization/VisualizationGraphRenderingDelegate.h"
7 7 #include "Visualization/VisualizationMultiZoneSelectionDialog.h"
8 8 #include "Visualization/VisualizationSelectionZoneItem.h"
9 9 #include "Visualization/VisualizationSelectionZoneManager.h"
10 10 #include "Visualization/VisualizationWidget.h"
11 11 #include "Visualization/VisualizationZoneWidget.h"
12 12 #include "ui_VisualizationGraphWidget.h"
13 13
14 14 #include <Actions/ActionsGuiController.h>
15 #include <Actions/FilteringAction.h>
15 16 #include <Common/MimeTypesDef.h>
16 17 #include <Data/ArrayData.h>
17 18 #include <Data/IDataSeries.h>
18 19 #include <Data/SpectrogramSeries.h>
19 20 #include <DragAndDrop/DragDropGuiController.h>
20 21 #include <Settings/SqpSettingsDefs.h>
21 22 #include <SqpApplication.h>
22 23 #include <Time/TimeController.h>
23 24 #include <Variable/Variable.h>
24 25 #include <Variable/VariableController.h>
25 26
26 27 #include <unordered_map>
27 28
28 29 Q_LOGGING_CATEGORY(LOG_VisualizationGraphWidget, "VisualizationGraphWidget")
29 30
30 31 namespace {
31 32
32 33 /// Key pressed to enable drag&drop in all modes
33 34 const auto DRAG_DROP_MODIFIER = Qt::AltModifier;
34 35
35 36 /// Key pressed to enable zoom on horizontal axis
36 37 const auto HORIZONTAL_ZOOM_MODIFIER = Qt::ControlModifier;
37 38
38 39 /// Key pressed to enable zoom on vertical axis
39 40 const auto VERTICAL_ZOOM_MODIFIER = Qt::ShiftModifier;
40 41
41 42 /// Speed of a step of a wheel event for a pan, in percentage of the axis range
42 43 const auto PAN_SPEED = 5;
43 44
44 45 /// Key pressed to enable a calibration pan
45 46 const auto VERTICAL_PAN_MODIFIER = Qt::AltModifier;
46 47
47 48 /// Key pressed to enable multi selection of selection zones
48 49 const auto MULTI_ZONE_SELECTION_MODIFIER = Qt::ControlModifier;
49 50
50 51 /// Minimum size for the zoom box, in percentage of the axis range
51 52 const auto ZOOM_BOX_MIN_SIZE = 0.8;
52 53
53 54 /// Format of the dates appearing in the label of a cursor
54 55 const auto CURSOR_LABELS_DATETIME_FORMAT = QStringLiteral("yyyy/MM/dd\nhh:mm:ss:zzz");
55 56
56 57 } // namespace
57 58
58 59 struct VisualizationGraphWidget::VisualizationGraphWidgetPrivate {
59 60
60 61 explicit VisualizationGraphWidgetPrivate(const QString &name)
61 62 : m_Name{name},
62 63 m_Flags{GraphFlag::EnableAll},
63 64 m_IsCalibration{false},
64 65 m_RenderingDelegate{nullptr}
65 66 {
66 67 }
67 68
68 69 void updateData(PlottablesMap &plottables, std::shared_ptr<Variable> variable,
69 70 const SqpRange &range)
70 71 {
71 72 VisualizationGraphHelper::updateData(plottables, variable, range);
72 73
73 74 // Prevents that data has changed to update rendering
74 75 m_RenderingDelegate->onPlotUpdated();
75 76 }
76 77
77 78 QString m_Name;
78 79 // 1 variable -> n qcpplot
79 80 std::map<std::shared_ptr<Variable>, PlottablesMap> m_VariableToPlotMultiMap;
80 81 GraphFlags m_Flags;
81 82 bool m_IsCalibration;
82 83 /// Delegate used to attach rendering features to the plot
83 84 std::unique_ptr<VisualizationGraphRenderingDelegate> m_RenderingDelegate;
84 85
85 86 QCPItemRect *m_DrawingZoomRect = nullptr;
86 87 QStack<QPair<QCPRange, QCPRange> > m_ZoomStack;
87 88
88 89 std::unique_ptr<VisualizationCursorItem> m_HorizontalCursor = nullptr;
89 90 std::unique_ptr<VisualizationCursorItem> m_VerticalCursor = nullptr;
90 91
91 92 VisualizationSelectionZoneItem *m_DrawingZone = nullptr;
92 93 VisualizationSelectionZoneItem *m_HoveredZone = nullptr;
93 94 QVector<VisualizationSelectionZoneItem *> m_SelectionZones;
94 95
95 96 bool m_HasMovedMouse = false; // Indicates if the mouse moved in a releaseMouse even
96 97
97 98 bool m_VariableAutoRangeOnInit = true;
98 99
99 100 void startDrawingRect(const QPoint &pos, QCustomPlot &plot)
100 101 {
101 102 removeDrawingRect(plot);
102 103
103 104 auto axisPos = posToAxisPos(pos, plot);
104 105
105 106 m_DrawingZoomRect = new QCPItemRect{&plot};
106 107 QPen p;
107 108 p.setWidth(2);
108 109 m_DrawingZoomRect->setPen(p);
109 110
110 111 m_DrawingZoomRect->topLeft->setCoords(axisPos);
111 112 m_DrawingZoomRect->bottomRight->setCoords(axisPos);
112 113 }
113 114
114 115 void removeDrawingRect(QCustomPlot &plot)
115 116 {
116 117 if (m_DrawingZoomRect) {
117 118 plot.removeItem(m_DrawingZoomRect); // the item is deleted by QCustomPlot
118 119 m_DrawingZoomRect = nullptr;
119 120 plot.replot(QCustomPlot::rpQueuedReplot);
120 121 }
121 122 }
122 123
123 124 void startDrawingZone(const QPoint &pos, VisualizationGraphWidget *graph)
124 125 {
125 126 endDrawingZone(graph);
126 127
127 128 auto axisPos = posToAxisPos(pos, graph->plot());
128 129
129 130 m_DrawingZone = new VisualizationSelectionZoneItem{&graph->plot()};
130 131 m_DrawingZone->setRange(axisPos.x(), axisPos.x());
131 132 m_DrawingZone->setEditionEnabled(false);
132 133 }
133 134
134 135 void endDrawingZone(VisualizationGraphWidget *graph)
135 136 {
136 137 if (m_DrawingZone) {
137 138 auto drawingZoneRange = m_DrawingZone->range();
138 139 if (qAbs(drawingZoneRange.m_TEnd - drawingZoneRange.m_TStart) > 0) {
139 140 m_DrawingZone->setEditionEnabled(true);
140 141 addSelectionZone(m_DrawingZone);
141 142 }
142 143 else {
143 144 graph->plot().removeItem(m_DrawingZone); // the item is deleted by QCustomPlot
144 145 }
145 146
146 147 graph->plot().replot(QCustomPlot::rpQueuedReplot);
147 148 m_DrawingZone = nullptr;
148 149 }
149 150 }
150 151
151 152 void setSelectionZonesEditionEnabled(bool value)
152 153 {
153 154 for (auto s : m_SelectionZones) {
154 155 s->setEditionEnabled(value);
155 156 }
156 157 }
157 158
158 159 void addSelectionZone(VisualizationSelectionZoneItem *zone) { m_SelectionZones << zone; }
159 160
160 161 VisualizationSelectionZoneItem *selectionZoneAt(const QPoint &pos,
161 162 const QCustomPlot &plot) const
162 163 {
163 164 VisualizationSelectionZoneItem *selectionZoneItemUnderCursor = nullptr;
164 165 auto minDistanceToZone = -1;
165 166 for (auto zone : m_SelectionZones) {
166 167 auto distanceToZone = zone->selectTest(pos, false);
167 168 if ((minDistanceToZone < 0 || distanceToZone <= minDistanceToZone)
168 169 && distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
169 170 selectionZoneItemUnderCursor = zone;
170 171 }
171 172 }
172 173
173 174 return selectionZoneItemUnderCursor;
174 175 }
175 176
176 177 QVector<VisualizationSelectionZoneItem *> selectionZonesAt(const QPoint &pos,
177 178 const QCustomPlot &plot) const
178 179 {
179 180 QVector<VisualizationSelectionZoneItem *> zones;
180 181 for (auto zone : m_SelectionZones) {
181 182 auto distanceToZone = zone->selectTest(pos, false);
182 183 if (distanceToZone >= 0 && distanceToZone < plot.selectionTolerance()) {
183 184 zones << zone;
184 185 }
185 186 }
186 187
187 188 return zones;
188 189 }
189 190
190 191 void moveSelectionZoneOnTop(VisualizationSelectionZoneItem *zone, QCustomPlot &plot)
191 192 {
192 193 if (!m_SelectionZones.isEmpty() && m_SelectionZones.last() != zone) {
193 194 zone->moveToTop();
194 195 m_SelectionZones.removeAll(zone);
195 196 m_SelectionZones.append(zone);
196 197 }
197 198 }
198 199
199 200 QPointF posToAxisPos(const QPoint &pos, QCustomPlot &plot) const
200 201 {
201 202 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
202 203 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
203 204 return QPointF{axisX->pixelToCoord(pos.x()), axisY->pixelToCoord(pos.y())};
204 205 }
205 206
206 207 bool pointIsInAxisRect(const QPointF &axisPoint, QCustomPlot &plot) const
207 208 {
208 209 auto axisX = plot.axisRect()->axis(QCPAxis::atBottom);
209 210 auto axisY = plot.axisRect()->axis(QCPAxis::atLeft);
210 211 return axisX->range().contains(axisPoint.x()) && axisY->range().contains(axisPoint.y());
211 212 }
212 213 };
213 214
214 215 VisualizationGraphWidget::VisualizationGraphWidget(const QString &name, QWidget *parent)
215 216 : VisualizationDragWidget{parent},
216 217 ui{new Ui::VisualizationGraphWidget},
217 218 impl{spimpl::make_unique_impl<VisualizationGraphWidgetPrivate>(name)}
218 219 {
219 220 ui->setupUi(this);
220 221
221 222 // 'Close' options : widget is deleted when closed
222 223 setAttribute(Qt::WA_DeleteOnClose);
223 224
224 225 // Set qcpplot properties :
225 226 // - zoom is enabled
226 227 // - Mouse wheel on qcpplot is intercepted to determine the zoom orientation
227 228 ui->widget->setInteractions(QCP::iRangeZoom);
228 229 ui->widget->axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
229 230
230 231 // The delegate must be initialized after the ui as it uses the plot
231 232 impl->m_RenderingDelegate = std::make_unique<VisualizationGraphRenderingDelegate>(*this);
232 233
233 234 // Init the cursors
234 235 impl->m_HorizontalCursor = std::make_unique<VisualizationCursorItem>(&plot());
235 236 impl->m_HorizontalCursor->setOrientation(Qt::Horizontal);
236 237 impl->m_VerticalCursor = std::make_unique<VisualizationCursorItem>(&plot());
237 238 impl->m_VerticalCursor->setOrientation(Qt::Vertical);
238 239
239 240 connect(ui->widget, &QCustomPlot::mousePress, this, &VisualizationGraphWidget::onMousePress);
240 241 connect(ui->widget, &QCustomPlot::mouseRelease, this,
241 242 &VisualizationGraphWidget::onMouseRelease);
242 243 connect(ui->widget, &QCustomPlot::mouseMove, this, &VisualizationGraphWidget::onMouseMove);
243 244 connect(ui->widget, &QCustomPlot::mouseWheel, this, &VisualizationGraphWidget::onMouseWheel);
244 245 connect(ui->widget, &QCustomPlot::mouseDoubleClick, this,
245 246 &VisualizationGraphWidget::onMouseDoubleClick);
246 247 connect(ui->widget->xAxis, static_cast<void (QCPAxis::*)(const QCPRange &, const QCPRange &)>(
247 248 &QCPAxis::rangeChanged),
248 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 this, &VisualizationGraphWidget::onRangeChanged, Qt::DirectConnection);
249 250
250 251 // Activates menu when right clicking on the graph
251 252 ui->widget->setContextMenuPolicy(Qt::CustomContextMenu);
252 253 connect(ui->widget, &QCustomPlot::customContextMenuRequested, this,
253 254 &VisualizationGraphWidget::onGraphMenuRequested);
254 255
255 256 connect(this, &VisualizationGraphWidget::requestDataLoading, &sqpApp->variableController(),
256 257 &VariableController::onRequestDataLoading);
257 258
258 259 connect(&sqpApp->variableController(), &VariableController::updateVarDisplaying, this,
259 260 &VisualizationGraphWidget::onUpdateVarDisplaying);
260 261
261 262 // Necessary for all platform since Qt::AA_EnableHighDpiScaling is enable.
262 263 plot().setPlottingHint(QCP::phFastPolylines, true);
263 264 }
264 265
265 266
266 267 VisualizationGraphWidget::~VisualizationGraphWidget()
267 268 {
268 269 delete ui;
269 270 }
270 271
271 272 VisualizationZoneWidget *VisualizationGraphWidget::parentZoneWidget() const noexcept
272 273 {
273 274 auto parent = parentWidget();
274 275 while (parent != nullptr && !qobject_cast<VisualizationZoneWidget *>(parent)) {
275 276 parent = parent->parentWidget();
276 277 }
277 278
278 279 return qobject_cast<VisualizationZoneWidget *>(parent);
279 280 }
280 281
281 282 VisualizationWidget *VisualizationGraphWidget::parentVisualizationWidget() const
282 283 {
283 284 auto parent = parentWidget();
284 285 while (parent != nullptr && !qobject_cast<VisualizationWidget *>(parent)) {
285 286 parent = parent->parentWidget();
286 287 }
287 288
288 289 return qobject_cast<VisualizationWidget *>(parent);
289 290 }
290 291
291 292 void VisualizationGraphWidget::setFlags(GraphFlags flags)
292 293 {
293 294 impl->m_Flags = std::move(flags);
294 295 }
295 296
296 297 void VisualizationGraphWidget::addVariable(std::shared_ptr<Variable> variable, SqpRange range)
297 298 {
298 299 /// Lambda used to set graph's units and range according to the variable passed in parameter
299 300 auto loadRange = [this](std::shared_ptr<Variable> variable, const SqpRange &range) {
300 301 impl->m_RenderingDelegate->setAxesUnits(*variable);
301 302
302 303 this->setFlags(GraphFlag::DisableAll);
303 304 setGraphRange(range);
304 305 this->setFlags(GraphFlag::EnableAll);
305 306 emit requestDataLoading({variable}, range, false);
306 307 };
307 308
308 309 connect(variable.get(), SIGNAL(updated()), this, SLOT(onDataCacheVariableUpdated()));
309 310
310 311 // Calls update of graph's range and units when the data of the variable have been initialized.
311 312 // Note: we use QueuedConnection here as the update event must be called in the UI thread
312 313 connect(variable.get(), &Variable::dataInitialized, this,
313 314 [ varW = std::weak_ptr<Variable>{variable}, range, loadRange, this ]() {
314 315 if (auto var = varW.lock()) {
315 316 // If the variable is the first added in the graph, we load its range
316 317 auto firstVariableInGraph = range == INVALID_RANGE;
317 318 auto loadedRange = graphRange();
318 319 if (impl->m_VariableAutoRangeOnInit) {
319 320 loadedRange = firstVariableInGraph ? var->range() : range;
320 321 }
321 322 loadRange(var, loadedRange);
322 323 setYRange(var);
323 324 }
324 325 },
325 326 Qt::QueuedConnection);
326 327
327 328 // Uses delegate to create the qcpplot components according to the variable
328 329 auto createdPlottables = VisualizationGraphHelper::create(variable, *ui->widget);
329 330
330 331 // Sets graph properties
331 332 impl->m_RenderingDelegate->setGraphProperties(*variable, createdPlottables);
332 333
333 334 impl->m_VariableToPlotMultiMap.insert({variable, std::move(createdPlottables)});
334 335
335 336 // If the variable already has its data loaded, load its units and its range in the graph
336 337 if (variable->dataSeries() != nullptr) {
337 338 loadRange(variable, range);
338 339 }
339 340
340 341 emit variableAdded(variable);
341 342 }
342 343
343 344 void VisualizationGraphWidget::removeVariable(std::shared_ptr<Variable> variable) noexcept
344 345 {
345 346 // Each component associated to the variable :
346 347 // - is removed from qcpplot (which deletes it)
347 348 // - is no longer referenced in the map
348 349 auto variableIt = impl->m_VariableToPlotMultiMap.find(variable);
349 350 if (variableIt != impl->m_VariableToPlotMultiMap.cend()) {
350 351 emit variableAboutToBeRemoved(variable);
351 352
352 353 auto &plottablesMap = variableIt->second;
353 354
354 355 for (auto plottableIt = plottablesMap.cbegin(), plottableEnd = plottablesMap.cend();
355 356 plottableIt != plottableEnd;) {
356 357 ui->widget->removePlottable(plottableIt->second);
357 358 plottableIt = plottablesMap.erase(plottableIt);
358 359 }
359 360
360 361 impl->m_VariableToPlotMultiMap.erase(variableIt);
361 362 }
362 363
363 364 // Updates graph
364 365 ui->widget->replot();
365 366 }
366 367
367 368 QList<std::shared_ptr<Variable> > VisualizationGraphWidget::variables() const
368 369 {
369 370 auto variables = QList<std::shared_ptr<Variable> >{};
370 371 for (auto it = std::cbegin(impl->m_VariableToPlotMultiMap);
371 372 it != std::cend(impl->m_VariableToPlotMultiMap); ++it) {
372 373 variables << it->first;
373 374 }
374 375
375 376 return variables;
376 377 }
377 378
378 379 void VisualizationGraphWidget::setYRange(std::shared_ptr<Variable> variable)
379 380 {
380 381 if (!variable) {
381 382 qCCritical(LOG_VisualizationGraphWidget()) << "Can't set y-axis range: variable is null";
382 383 return;
383 384 }
384 385
385 386 VisualizationGraphHelper::setYAxisRange(variable, *ui->widget);
386 387 }
387 388
388 389 SqpRange VisualizationGraphWidget::graphRange() const noexcept
389 390 {
390 391 auto graphRange = ui->widget->xAxis->range();
391 392 return SqpRange{graphRange.lower, graphRange.upper};
392 393 }
393 394
394 395 void VisualizationGraphWidget::setGraphRange(const SqpRange &range, bool calibration)
395 396 {
396 397 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange START");
397 398
398 399 if (calibration) {
399 400 impl->m_IsCalibration = true;
400 401 }
401 402
402 403 ui->widget->xAxis->setRange(range.m_TStart, range.m_TEnd);
403 404 ui->widget->replot();
404 405
405 406 if (calibration) {
406 407 impl->m_IsCalibration = false;
407 408 }
408 409
409 410 qCDebug(LOG_VisualizationGraphWidget()) << tr("VisualizationGraphWidget::setGraphRange END");
410 411 }
411 412
412 413 void VisualizationGraphWidget::setAutoRangeOnVariableInitialization(bool value)
413 414 {
414 415 impl->m_VariableAutoRangeOnInit = value;
415 416 }
416 417
417 418 QVector<SqpRange> VisualizationGraphWidget::selectionZoneRanges() const
418 419 {
419 420 QVector<SqpRange> ranges;
420 421 for (auto zone : impl->m_SelectionZones) {
421 422 ranges << zone->range();
422 423 }
423 424
424 425 return ranges;
425 426 }
426 427
427 428 void VisualizationGraphWidget::addSelectionZones(const QVector<SqpRange> &ranges)
428 429 {
429 430 for (const auto &range : ranges) {
430 431 // note: ownership is transfered to QCustomPlot
431 432 auto zone = new VisualizationSelectionZoneItem(&plot());
432 433 zone->setRange(range.m_TStart, range.m_TEnd);
433 434 impl->addSelectionZone(zone);
434 435 }
435 436
436 437 plot().replot(QCustomPlot::rpQueuedReplot);
437 438 }
438 439
439 440 VisualizationSelectionZoneItem *VisualizationGraphWidget::addSelectionZone(const QString &name,
440 441 const SqpRange &range)
441 442 {
442 443 // note: ownership is transfered to QCustomPlot
443 444 auto zone = new VisualizationSelectionZoneItem(&plot());
444 445 zone->setName(name);
445 446 zone->setRange(range.m_TStart, range.m_TEnd);
446 447 impl->addSelectionZone(zone);
447 448
448 449 plot().replot(QCustomPlot::rpQueuedReplot);
449 450
450 451 return zone;
451 452 }
452 453
453 454 void VisualizationGraphWidget::removeSelectionZone(VisualizationSelectionZoneItem *selectionZone)
454 455 {
455 456 parentVisualizationWidget()->selectionZoneManager().setSelected(selectionZone, false);
456 457
457 458 if (impl->m_HoveredZone == selectionZone) {
458 459 impl->m_HoveredZone = nullptr;
459 460 setCursor(Qt::ArrowCursor);
460 461 }
461 462
462 463 impl->m_SelectionZones.removeAll(selectionZone);
463 464 plot().removeItem(selectionZone);
464 465 plot().replot(QCustomPlot::rpQueuedReplot);
465 466 }
466 467
467 468 void VisualizationGraphWidget::undoZoom()
468 469 {
469 470 auto zoom = impl->m_ZoomStack.pop();
470 471 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
471 472 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
472 473
473 474 axisX->setRange(zoom.first);
474 475 axisY->setRange(zoom.second);
475 476
476 477 plot().replot(QCustomPlot::rpQueuedReplot);
477 478 }
478 479
479 480 void VisualizationGraphWidget::accept(IVisualizationWidgetVisitor *visitor)
480 481 {
481 482 if (visitor) {
482 483 visitor->visit(this);
483 484 }
484 485 else {
485 486 qCCritical(LOG_VisualizationGraphWidget())
486 487 << tr("Can't visit widget : the visitor is null");
487 488 }
488 489 }
489 490
490 491 bool VisualizationGraphWidget::canDrop(const Variable &variable) const
491 492 {
492 493 auto isSpectrogram = [](const auto &variable) {
493 494 return std::dynamic_pointer_cast<SpectrogramSeries>(variable.dataSeries()) != nullptr;
494 495 };
495 496
496 497 // - A spectrogram series can't be dropped on graph with existing plottables
497 498 // - No data series can be dropped on graph with existing spectrogram series
498 499 return isSpectrogram(variable)
499 500 ? impl->m_VariableToPlotMultiMap.empty()
500 501 : std::none_of(
501 502 impl->m_VariableToPlotMultiMap.cbegin(), impl->m_VariableToPlotMultiMap.cend(),
502 503 [isSpectrogram](const auto &entry) { return isSpectrogram(*entry.first); });
503 504 }
504 505
505 506 bool VisualizationGraphWidget::contains(const Variable &variable) const
506 507 {
507 508 // Finds the variable among the keys of the map
508 509 auto variablePtr = &variable;
509 510 auto findVariable
510 511 = [variablePtr](const auto &entry) { return variablePtr == entry.first.get(); };
511 512
512 513 auto end = impl->m_VariableToPlotMultiMap.cend();
513 514 auto it = std::find_if(impl->m_VariableToPlotMultiMap.cbegin(), end, findVariable);
514 515 return it != end;
515 516 }
516 517
517 518 QString VisualizationGraphWidget::name() const
518 519 {
519 520 return impl->m_Name;
520 521 }
521 522
522 523 QMimeData *VisualizationGraphWidget::mimeData(const QPoint &position) const
523 524 {
524 525 auto mimeData = new QMimeData;
525 526
526 527 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(position, plot());
527 528 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
528 529 && selectionZoneItemUnderCursor) {
529 530 mimeData->setData(MIME_TYPE_TIME_RANGE, TimeController::mimeDataForTimeRange(
530 531 selectionZoneItemUnderCursor->range()));
531 532 mimeData->setData(MIME_TYPE_SELECTION_ZONE, TimeController::mimeDataForTimeRange(
532 533 selectionZoneItemUnderCursor->range()));
533 534 }
534 535 else {
535 536 mimeData->setData(MIME_TYPE_GRAPH, QByteArray{});
536 537
537 538 auto timeRangeData = TimeController::mimeDataForTimeRange(graphRange());
538 539 mimeData->setData(MIME_TYPE_TIME_RANGE, timeRangeData);
539 540 }
540 541
541 542 return mimeData;
542 543 }
543 544
544 545 QPixmap VisualizationGraphWidget::customDragPixmap(const QPoint &dragPosition)
545 546 {
546 547 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(dragPosition, plot());
547 548 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones
548 549 && selectionZoneItemUnderCursor) {
549 550
550 551 auto zoneTopLeft = selectionZoneItemUnderCursor->topLeft->pixelPosition();
551 552 auto zoneBottomRight = selectionZoneItemUnderCursor->bottomRight->pixelPosition();
552 553
553 554 auto zoneSize = QSizeF{qAbs(zoneBottomRight.x() - zoneTopLeft.x()),
554 555 qAbs(zoneBottomRight.y() - zoneTopLeft.y())}
555 556 .toSize();
556 557
557 558 auto pixmap = QPixmap(zoneSize);
558 559 render(&pixmap, QPoint(), QRegion{QRect{zoneTopLeft.toPoint(), zoneSize}});
559 560
560 561 return pixmap;
561 562 }
562 563
563 564 return QPixmap();
564 565 }
565 566
566 567 bool VisualizationGraphWidget::isDragAllowed() const
567 568 {
568 569 return true;
569 570 }
570 571
571 572 void VisualizationGraphWidget::highlightForMerge(bool highlighted)
572 573 {
573 574 if (highlighted) {
574 575 plot().setBackground(QBrush(QColor("#BBD5EE")));
575 576 }
576 577 else {
577 578 plot().setBackground(QBrush(Qt::white));
578 579 }
579 580
580 581 plot().update();
581 582 }
582 583
583 584 void VisualizationGraphWidget::addVerticalCursor(double time)
584 585 {
585 586 impl->m_VerticalCursor->setPosition(time);
586 587 impl->m_VerticalCursor->setVisible(true);
587 588
588 589 auto text
589 590 = DateUtils::dateTime(time).toString(CURSOR_LABELS_DATETIME_FORMAT).replace(' ', '\n');
590 591 impl->m_VerticalCursor->setLabelText(text);
591 592 }
592 593
593 594 void VisualizationGraphWidget::addVerticalCursorAtViewportPosition(double position)
594 595 {
595 596 impl->m_VerticalCursor->setAbsolutePosition(position);
596 597 impl->m_VerticalCursor->setVisible(true);
597 598
598 599 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
599 600 auto text
600 601 = DateUtils::dateTime(axis->pixelToCoord(position)).toString(CURSOR_LABELS_DATETIME_FORMAT);
601 602 impl->m_VerticalCursor->setLabelText(text);
602 603 }
603 604
604 605 void VisualizationGraphWidget::removeVerticalCursor()
605 606 {
606 607 impl->m_VerticalCursor->setVisible(false);
607 608 plot().replot(QCustomPlot::rpQueuedReplot);
608 609 }
609 610
610 611 void VisualizationGraphWidget::addHorizontalCursor(double value)
611 612 {
612 613 impl->m_HorizontalCursor->setPosition(value);
613 614 impl->m_HorizontalCursor->setVisible(true);
614 615 impl->m_HorizontalCursor->setLabelText(QString::number(value));
615 616 }
616 617
617 618 void VisualizationGraphWidget::addHorizontalCursorAtViewportPosition(double position)
618 619 {
619 620 impl->m_HorizontalCursor->setAbsolutePosition(position);
620 621 impl->m_HorizontalCursor->setVisible(true);
621 622
622 623 auto axis = plot().axisRect()->axis(QCPAxis::atLeft);
623 624 impl->m_HorizontalCursor->setLabelText(QString::number(axis->pixelToCoord(position)));
624 625 }
625 626
626 627 void VisualizationGraphWidget::removeHorizontalCursor()
627 628 {
628 629 impl->m_HorizontalCursor->setVisible(false);
629 630 plot().replot(QCustomPlot::rpQueuedReplot);
630 631 }
631 632
632 633 void VisualizationGraphWidget::closeEvent(QCloseEvent *event)
633 634 {
634 635 Q_UNUSED(event);
635 636
637 for (auto i : impl->m_SelectionZones) {
638 parentVisualizationWidget()->selectionZoneManager().setSelected(i, false);
639 }
640
636 641 // Prevents that all variables will be removed from graph when it will be closed
637 642 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
638 643 emit variableAboutToBeRemoved(variableEntry.first);
639 644 }
640 645 }
641 646
642 647 void VisualizationGraphWidget::enterEvent(QEvent *event)
643 648 {
644 649 Q_UNUSED(event);
645 650 impl->m_RenderingDelegate->showGraphOverlay(true);
646 651 }
647 652
648 653 void VisualizationGraphWidget::leaveEvent(QEvent *event)
649 654 {
650 655 Q_UNUSED(event);
651 656 impl->m_RenderingDelegate->showGraphOverlay(false);
652 657
653 658 if (auto parentZone = parentZoneWidget()) {
654 659 parentZone->notifyMouseLeaveGraph(this);
655 660 }
656 661 else {
657 662 qCWarning(LOG_VisualizationGraphWidget()) << "leaveEvent: No parent zone widget";
658 663 }
659 664
660 665 if (impl->m_HoveredZone) {
661 666 impl->m_HoveredZone->setHovered(false);
662 667 impl->m_HoveredZone = nullptr;
663 668 }
664 669 }
665 670
666 671 QCustomPlot &VisualizationGraphWidget::plot() const noexcept
667 672 {
668 673 return *ui->widget;
669 674 }
670 675
671 676 void VisualizationGraphWidget::onGraphMenuRequested(const QPoint &pos) noexcept
672 677 {
673 678 QMenu graphMenu{};
674 679
675 680 // Iterates on variables (unique keys)
676 681 for (auto it = impl->m_VariableToPlotMultiMap.cbegin(),
677 682 end = impl->m_VariableToPlotMultiMap.cend();
678 683 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
679 684 // 'Remove variable' action
680 685 graphMenu.addAction(tr("Remove variable %1").arg(it->first->name()),
681 686 [ this, var = it->first ]() { removeVariable(var); });
682 687 }
683 688
684 689 if (!impl->m_ZoomStack.isEmpty()) {
685 690 if (!graphMenu.isEmpty()) {
686 691 graphMenu.addSeparator();
687 692 }
688 693
689 694 graphMenu.addAction(tr("Undo Zoom"), [this]() { undoZoom(); });
690 695 }
691 696
692 697 // Selection Zone Actions
693 698 auto selectionZoneItem = impl->selectionZoneAt(pos, plot());
694 699 if (selectionZoneItem) {
695 700 auto selectedItems = parentVisualizationWidget()->selectionZoneManager().selectedItems();
696 701 selectedItems.removeAll(selectionZoneItem);
697 702 selectedItems.prepend(selectionZoneItem); // Put the current selection zone first
698 703
699 704 auto zoneActions = sqpApp->actionsGuiController().selectionZoneActions();
700 705 if (!zoneActions.isEmpty() && !graphMenu.isEmpty()) {
701 706 graphMenu.addSeparator();
702 707 }
703 708
704 709 QHash<QString, QMenu *> subMenus;
705 710 QHash<QString, bool> subMenusEnabled;
711 QHash<QString, FilteringAction *> filteredMenu;
706 712
707 713 for (auto zoneAction : zoneActions) {
708 714
709 715 auto isEnabled = zoneAction->isEnabled(selectedItems);
710 716
711 717 auto menu = &graphMenu;
718 QString menuPath;
712 719 for (auto subMenuName : zoneAction->subMenuList()) {
713 if (!subMenus.contains(subMenuName)) {
720 menuPath += '/';
721 menuPath += subMenuName;
722
723 if (!subMenus.contains(menuPath)) {
714 724 menu = menu->addMenu(subMenuName);
715 subMenus[subMenuName] = menu;
716 subMenusEnabled[subMenuName] = isEnabled;
725 subMenus[menuPath] = menu;
726 subMenusEnabled[menuPath] = isEnabled;
717 727 }
718 728 else {
719 menu = subMenus.value(subMenuName);
729 menu = subMenus.value(menuPath);
720 730 if (isEnabled) {
721 731 // The sub menu is enabled if at least one of its actions is enabled
722 subMenusEnabled[subMenuName] = true;
732 subMenusEnabled[menuPath] = true;
723 733 }
724 734 }
725 735 }
726 736
737 FilteringAction *filterAction = nullptr;
738 if (sqpApp->actionsGuiController().isMenuFiltered(zoneAction->subMenuList())) {
739 filterAction = filteredMenu.value(menuPath);
740 if (!filterAction) {
741 filterAction = new FilteringAction{this};
742 filteredMenu[menuPath] = filterAction;
743 menu->addAction(filterAction);
744 }
745 }
746
727 747 auto action = menu->addAction(zoneAction->name());
728 748 action->setEnabled(isEnabled);
729 749 action->setShortcut(zoneAction->displayedShortcut());
730 750 QObject::connect(action, &QAction::triggered,
731 751 [zoneAction, selectedItems]() { zoneAction->execute(selectedItems); });
752
753 if (filterAction && zoneAction->isFilteringAllowed()) {
754 filterAction->addActionToFilter(action);
755 }
732 756 }
733 757
734 758 for (auto it = subMenus.cbegin(); it != subMenus.cend(); ++it) {
735 759 it.value()->setEnabled(subMenusEnabled[it.key()]);
736 760 }
737 761 }
738 762
739 763 if (!graphMenu.isEmpty()) {
740 764 graphMenu.exec(QCursor::pos());
741 765 }
742 766 }
743 767
744 768 void VisualizationGraphWidget::onRangeChanged(const QCPRange &t1, const QCPRange &t2)
745 769 {
746 770 qCDebug(LOG_VisualizationGraphWidget()) << tr("TORM: VisualizationGraphWidget::onRangeChanged")
747 771 << QThread::currentThread()->objectName() << "DoAcqui"
748 772 << impl->m_Flags.testFlag(GraphFlag::EnableAcquisition);
749 773
750 774 auto graphRange = SqpRange{t1.lower, t1.upper};
751 775 auto oldGraphRange = SqpRange{t2.lower, t2.upper};
752 776
753 777 if (impl->m_Flags.testFlag(GraphFlag::EnableAcquisition)) {
754 778 QVector<std::shared_ptr<Variable> > variableUnderGraphVector;
755 779
756 780 for (auto it = impl->m_VariableToPlotMultiMap.begin(),
757 781 end = impl->m_VariableToPlotMultiMap.end();
758 782 it != end; it = impl->m_VariableToPlotMultiMap.upper_bound(it->first)) {
759 783 variableUnderGraphVector.push_back(it->first);
760 784 }
761 785 emit requestDataLoading(std::move(variableUnderGraphVector), graphRange,
762 786 !impl->m_IsCalibration);
763 787 }
764 788
765 789 if (impl->m_Flags.testFlag(GraphFlag::EnableSynchronization) && !impl->m_IsCalibration) {
766 790 qCDebug(LOG_VisualizationGraphWidget())
767 791 << tr("TORM: VisualizationGraphWidget::Synchronize notify !!")
768 792 << QThread::currentThread()->objectName() << graphRange << oldGraphRange;
769 793 emit synchronize(graphRange, oldGraphRange);
770 794 }
771 795
772 796 auto pos = mapFromGlobal(QCursor::pos());
773 797 auto axisPos = impl->posToAxisPos(pos, plot());
774 798 if (auto parentZone = parentZoneWidget()) {
775 799 if (impl->pointIsInAxisRect(axisPos, plot())) {
776 800 parentZone->notifyMouseMoveInGraph(pos, axisPos, this);
777 801 }
778 802 else {
779 803 parentZone->notifyMouseLeaveGraph(this);
780 804 }
781 805 }
782 806 else {
783 807 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
784 808 }
785 809
786 810 // Quits calibration
787 811 impl->m_IsCalibration = false;
788 812 }
789 813
790 814 void VisualizationGraphWidget::onMouseDoubleClick(QMouseEvent *event) noexcept
791 815 {
792 816 impl->m_RenderingDelegate->onMouseDoubleClick(event);
793 817 }
794 818
795 819 void VisualizationGraphWidget::onMouseMove(QMouseEvent *event) noexcept
796 820 {
797 821 // Handles plot rendering when mouse is moving
798 822 impl->m_RenderingDelegate->onMouseMove(event);
799 823
800 824 auto axisPos = impl->posToAxisPos(event->pos(), plot());
801 825
802 826 // Zoom box and zone drawing
803 827 if (impl->m_DrawingZoomRect) {
804 828 impl->m_DrawingZoomRect->bottomRight->setCoords(axisPos);
805 829 }
806 830 else if (impl->m_DrawingZone) {
807 831 impl->m_DrawingZone->setEnd(axisPos.x());
808 832 }
809 833
810 834 // Cursor
811 835 if (auto parentZone = parentZoneWidget()) {
812 836 if (impl->pointIsInAxisRect(axisPos, plot())) {
813 837 parentZone->notifyMouseMoveInGraph(event->pos(), axisPos, this);
814 838 }
815 839 else {
816 840 parentZone->notifyMouseLeaveGraph(this);
817 841 }
818 842 }
819 843 else {
820 844 qCWarning(LOG_VisualizationGraphWidget()) << "onMouseMove: No parent zone widget";
821 845 }
822 846
823 847 // Search for the selection zone under the mouse
824 848 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
825 849 if (selectionZoneItemUnderCursor && !impl->m_DrawingZone
826 850 && sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones) {
827 851
828 852 // Sets the appropriate cursor shape
829 853 auto cursorShape = selectionZoneItemUnderCursor->curshorShapeForPosition(event->pos());
830 854 setCursor(cursorShape);
831 855
832 856 // Manages the hovered zone
833 857 if (selectionZoneItemUnderCursor != impl->m_HoveredZone) {
834 858 if (impl->m_HoveredZone) {
835 859 impl->m_HoveredZone->setHovered(false);
836 860 }
837 861 selectionZoneItemUnderCursor->setHovered(true);
838 862 impl->m_HoveredZone = selectionZoneItemUnderCursor;
839 863 plot().replot(QCustomPlot::rpQueuedReplot);
840 864 }
841 865 }
842 866 else {
843 867 // There is no zone under the mouse or the interaction mode is not "selection zones"
844 868 if (impl->m_HoveredZone) {
845 869 impl->m_HoveredZone->setHovered(false);
846 870 impl->m_HoveredZone = nullptr;
847 871 }
848 872
849 873 setCursor(Qt::ArrowCursor);
850 874 }
851 875
852 876 impl->m_HasMovedMouse = true;
853 877 VisualizationDragWidget::mouseMoveEvent(event);
854 878 }
855 879
856 880 void VisualizationGraphWidget::onMouseWheel(QWheelEvent *event) noexcept
857 881 {
858 882 auto value = event->angleDelta().x() + event->angleDelta().y();
859 883 if (value != 0) {
860 884
861 885 auto direction = value > 0 ? 1.0 : -1.0;
862 886 auto isZoomX = event->modifiers().testFlag(HORIZONTAL_ZOOM_MODIFIER);
863 887 auto isZoomY = event->modifiers().testFlag(VERTICAL_ZOOM_MODIFIER);
864 888 impl->m_IsCalibration = event->modifiers().testFlag(VERTICAL_PAN_MODIFIER);
865 889
866 890 auto zoomOrientations = QFlags<Qt::Orientation>{};
867 891 zoomOrientations.setFlag(Qt::Horizontal, isZoomX);
868 892 zoomOrientations.setFlag(Qt::Vertical, isZoomY);
869 893
870 894 ui->widget->axisRect()->setRangeZoom(zoomOrientations);
871 895
872 896 if (!isZoomX && !isZoomY) {
873 897 auto axis = plot().axisRect()->axis(QCPAxis::atBottom);
874 898 auto diff = direction * (axis->range().size() * (PAN_SPEED / 100.0));
875 899
876 900 axis->setRange(axis->range() + diff);
877 901
878 902 if (plot().noAntialiasingOnDrag()) {
879 903 plot().setNotAntialiasedElements(QCP::aeAll);
880 904 }
881 905
882 906 plot().replot(QCustomPlot::rpQueuedReplot);
883 907 }
884 908 }
885 909 }
886 910
887 911 void VisualizationGraphWidget::onMousePress(QMouseEvent *event) noexcept
888 912 {
889 913 auto isDragDropClick = event->modifiers().testFlag(DRAG_DROP_MODIFIER);
890 914 auto isSelectionZoneMode
891 915 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
892 916 auto isLeftClick = event->buttons().testFlag(Qt::LeftButton);
893 917
894 918 if (!isDragDropClick && isLeftClick) {
895 919 if (sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::ZoomBox) {
896 920 // Starts a zoom box
897 921 impl->startDrawingRect(event->pos(), plot());
898 922 }
899 923 else if (isSelectionZoneMode && impl->m_DrawingZone == nullptr) {
900 924 // Starts a new selection zone
901 925 auto zoneAtPos = impl->selectionZoneAt(event->pos(), plot());
902 926 if (!zoneAtPos) {
903 927 impl->startDrawingZone(event->pos(), this);
904 928 }
905 929 }
906 930 }
907 931
908 932 // Allows mouse panning only in default mode
909 933 plot().setInteraction(QCP::iRangeDrag, sqpApp->plotsInteractionMode()
910 934 == SqpApplication::PlotsInteractionMode::None
911 935 && !isDragDropClick);
912 936
913 937 // Allows zone edition only in selection zone mode without drag&drop
914 938 impl->setSelectionZonesEditionEnabled(isSelectionZoneMode && !isDragDropClick);
915 939
916 940 // Selection / Deselection
917 941 if (isSelectionZoneMode) {
918 942 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
919 943 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
920 944
921 945
922 946 if (selectionZoneItemUnderCursor && !selectionZoneItemUnderCursor->selected()
923 947 && !isMultiSelectionClick) {
924 948 parentVisualizationWidget()->selectionZoneManager().select(
925 949 {selectionZoneItemUnderCursor});
926 950 }
927 951 else if (!selectionZoneItemUnderCursor && !isMultiSelectionClick && isLeftClick) {
928 952 parentVisualizationWidget()->selectionZoneManager().clearSelection();
929 953 }
930 954 else {
931 955 // No selection change
932 956 }
933 957
934 958 if (selectionZoneItemUnderCursor && isLeftClick) {
935 959 selectionZoneItemUnderCursor->setAssociatedEditedZones(
936 960 parentVisualizationWidget()->selectionZoneManager().selectedItems());
937 961 }
938 962 }
939 963
940 964
941 965 impl->m_HasMovedMouse = false;
942 966 VisualizationDragWidget::mousePressEvent(event);
943 967 }
944 968
945 969 void VisualizationGraphWidget::onMouseRelease(QMouseEvent *event) noexcept
946 970 {
947 971 if (impl->m_DrawingZoomRect) {
948 972
949 973 auto axisX = plot().axisRect()->axis(QCPAxis::atBottom);
950 974 auto axisY = plot().axisRect()->axis(QCPAxis::atLeft);
951 975
952 976 auto newAxisXRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().x(),
953 977 impl->m_DrawingZoomRect->bottomRight->coords().x()};
954 978
955 979 auto newAxisYRange = QCPRange{impl->m_DrawingZoomRect->topLeft->coords().y(),
956 980 impl->m_DrawingZoomRect->bottomRight->coords().y()};
957 981
958 982 impl->removeDrawingRect(plot());
959 983
960 984 if (newAxisXRange.size() > axisX->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)
961 985 && newAxisYRange.size() > axisY->range().size() * (ZOOM_BOX_MIN_SIZE / 100.0)) {
962 986 impl->m_ZoomStack.push(qMakePair(axisX->range(), axisY->range()));
963 987 axisX->setRange(newAxisXRange);
964 988 axisY->setRange(newAxisYRange);
965 989
966 990 plot().replot(QCustomPlot::rpQueuedReplot);
967 991 }
968 992 }
969 993
970 994 impl->endDrawingZone(this);
971 995
972 996 // Selection / Deselection
973 997 auto isSelectionZoneMode
974 998 = sqpApp->plotsInteractionMode() == SqpApplication::PlotsInteractionMode::SelectionZones;
975 999 if (isSelectionZoneMode) {
976 1000 auto isMultiSelectionClick = event->modifiers().testFlag(MULTI_ZONE_SELECTION_MODIFIER);
977 1001 auto selectionZoneItemUnderCursor = impl->selectionZoneAt(event->pos(), plot());
978 1002 if (selectionZoneItemUnderCursor && event->button() == Qt::LeftButton
979 1003 && !impl->m_HasMovedMouse) {
980 1004
981 1005 auto zonesUnderCursor = impl->selectionZonesAt(event->pos(), plot());
982 1006 if (zonesUnderCursor.count() > 1) {
983 1007 // There are multiple zones under the mouse.
984 1008 // Performs the selection with a selection dialog.
985 1009 VisualizationMultiZoneSelectionDialog dialog{this};
986 1010 dialog.setZones(zonesUnderCursor);
987 1011 dialog.move(mapToGlobal(event->pos() - QPoint(dialog.width() / 2, 20)));
988 1012 dialog.activateWindow();
989 1013 dialog.raise();
990 1014 if (dialog.exec() == QDialog::Accepted) {
991 1015 auto selection = dialog.selectedZones();
992 1016
993 1017 if (!isMultiSelectionClick) {
994 1018 parentVisualizationWidget()->selectionZoneManager().clearSelection();
995 1019 }
996 1020
997 1021 for (auto it = selection.cbegin(); it != selection.cend(); ++it) {
998 1022 auto zone = it.key();
999 1023 auto isSelected = it.value();
1000 1024 parentVisualizationWidget()->selectionZoneManager().setSelected(zone,
1001 1025 isSelected);
1002 1026
1003 1027 if (isSelected) {
1004 1028 // Puts the zone on top of the stack so it can be moved or resized
1005 1029 impl->moveSelectionZoneOnTop(zone, plot());
1006 1030 }
1007 1031 }
1008 1032 }
1009 1033 }
1010 1034 else {
1011 1035 if (!isMultiSelectionClick) {
1012 1036 parentVisualizationWidget()->selectionZoneManager().select(
1013 1037 {selectionZoneItemUnderCursor});
1014 1038 impl->moveSelectionZoneOnTop(selectionZoneItemUnderCursor, plot());
1015 1039 }
1016 1040 else {
1017 1041 parentVisualizationWidget()->selectionZoneManager().setSelected(
1018 1042 selectionZoneItemUnderCursor, !selectionZoneItemUnderCursor->selected()
1019 1043 || event->button() == Qt::RightButton);
1020 1044 }
1021 1045 }
1022 1046 }
1023 1047 else {
1024 1048 // No selection change
1025 1049 }
1026 1050 }
1027 1051 }
1028 1052
1029 1053 void VisualizationGraphWidget::onDataCacheVariableUpdated()
1030 1054 {
1031 1055 auto graphRange = ui->widget->xAxis->range();
1032 1056 auto dateTime = SqpRange{graphRange.lower, graphRange.upper};
1033 1057
1034 1058 for (auto &variableEntry : impl->m_VariableToPlotMultiMap) {
1035 1059 auto variable = variableEntry.first;
1036 1060 qCDebug(LOG_VisualizationGraphWidget())
1037 1061 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated S" << variable->range();
1038 1062 qCDebug(LOG_VisualizationGraphWidget())
1039 1063 << "TORM: VisualizationGraphWidget::onDataCacheVariableUpdated E" << dateTime;
1040 1064 if (dateTime.contains(variable->range()) || dateTime.intersect(variable->range())) {
1041 1065 impl->updateData(variableEntry.second, variable, variable->range());
1042 1066 }
1043 1067 }
1044 1068 }
1045 1069
1046 1070 void VisualizationGraphWidget::onUpdateVarDisplaying(std::shared_ptr<Variable> variable,
1047 1071 const SqpRange &range)
1048 1072 {
1049 1073 auto it = impl->m_VariableToPlotMultiMap.find(variable);
1050 1074 if (it != impl->m_VariableToPlotMultiMap.end()) {
1051 1075 impl->updateData(it->second, variable, range);
1052 1076 }
1053 1077 }
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now