diff options
author | Berthold Stoeger <bstoeger@mail.tuwien.ac.at> | 2018-08-01 19:23:43 +0200 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2018-10-11 16:22:27 -0700 |
commit | 23db0ba68df5f785a8a2db5ff5b76bbbee31d69f (patch) | |
tree | 41204dd19c78c7ecf75493be3b121f3773c0d438 /core/subsurface-qt | |
parent | 0d98da52610161ecbafd50afacbff20996756264 (diff) | |
download | subsurface-23db0ba68df5f785a8a2db5ff5b76bbbee31d69f.tar.gz |
Dive list view: replace signal-magic by flag
In DiveListView, we have a very fundamental problem: When
On the one hand, we get informed of user-selection in the
DiveListView::selectionChanged() slot. This has to set the
correct flags in the C-backend.
On the other hand, sometimes we have to set the selection
programatically, e.g. when selecting a trip. This is done
by calling QItemSelectionModel::select().
But: this will *also* call into the above slot, in which
we can't tell whether it was a user interaction or an
internal call. This can lead to either infinite loops or
very inefficient behavior, because the current dive
is set numerous times.
The current code is aware of that and disconnects the
corresponding signal. This is scary, as these signals are
set internally by the model and view. Replace this
by a global "command executing" flag in DiveListNotifier.
The flag is set using a "marker" class, which resets the flag
once it goes out of scope (cf. RAII pattern).
In DiveListView, only process a selection if the flag is not
set. Otherwise simply call the QTreeView base class, to reflect
the new selection in the UI.
To have a common point for notifications of selection changes,
add such a signal to DiveListNotifier. This signal will be
used by the DiveListView as well as the Command-objects.
Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
Diffstat (limited to 'core/subsurface-qt')
-rw-r--r-- | core/subsurface-qt/DiveListNotifier.h | 61 |
1 files changed, 60 insertions, 1 deletions
diff --git a/core/subsurface-qt/DiveListNotifier.h b/core/subsurface-qt/DiveListNotifier.h index 94b0063ea..fe5ea0e22 100644 --- a/core/subsurface-qt/DiveListNotifier.h +++ b/core/subsurface-qt/DiveListNotifier.h @@ -30,10 +30,69 @@ signals: void divesChanged(dive_trip *trip, const QVector<dive *> &dives); void divesMovedBetweenTrips(dive_trip *from, dive_trip *to, bool deleteFrom, bool createTo, const QVector<dive *> &dives); void divesTimeChanged(dive_trip *trip, timestamp_t delta, const QVector<dive *> &dives); + + // This signal is sent if the selection of dives and/or the current dive changed + void selectionChanged(); +public: + // Desktop uses the QTreeView class to present the list of dives. The layout + // of this class gives us a very fundamental problem, as we can not easily + // distinguish between user-initiated changes of the selection and changes + // that are due to actions of the Command-classes. To solve this problem, + // the frontend can use this function to query whether a dive list-modifying + // command is currently executed. If this function returns true, the + // frontend is supposed to not modify the selection. + bool inCommand() const; + + // The following class and function are used by divelist-modifying commands + // to signalize that are in-flight. If the returned object goes out of scope, + // the command-in-flight status is reset to its previous value. Thus, the + // function can be called recursively. + class InCommandMarker { + DiveListNotifier ¬ifier; + bool oldValue; + InCommandMarker(DiveListNotifier &); + friend DiveListNotifier; + public: + ~InCommandMarker(); + }; + + // Usage: + // void doWork() + // { + // auto marker = diveListNotifier.enterCommand(); + // ... do work ... + // } + InCommandMarker enterCommand(); +private: + friend InCommandMarker; + bool commandExecuting; }; -// The DiveListNotifier class has no state and no constructor. +// The DiveListNotifier class has only trivial state. // We can simply define it as a global object. extern DiveListNotifier diveListNotifier; +// InCommandMarker is so trivial that the functions can be inlined. +// TODO: perhaps move this into own header-file. +inline DiveListNotifier::InCommandMarker::InCommandMarker(DiveListNotifier ¬ifierIn) : notifier(notifierIn), + oldValue(notifier.commandExecuting) +{ + notifier.commandExecuting = true; +} + +inline DiveListNotifier::InCommandMarker::~InCommandMarker() +{ + notifier.commandExecuting = oldValue; +} + +inline bool DiveListNotifier::inCommand() const +{ + return commandExecuting; +} + +inline DiveListNotifier::InCommandMarker DiveListNotifier::enterCommand() +{ + return InCommandMarker(*this); +} + #endif |