// SPDX-License-Identifier: GPL-2.0 // The DiveListNotifier emits signals when the dive-list changes (dives/trips/divesites created/deleted/moved/edited) #ifndef DIVELISTNOTIFIER_H #define DIVELISTNOTIFIER_H #include "core/dive.h" #include // Dive and trip fields that can be edited. Use bit fields so that we can pass multiple fields at once. // Provides an inlined flag-based constructur because sadly C-style designated initializers are only supported since C++20. struct DiveField { // Note: using int instead of the more natural bool, because gcc produces significantly worse code with // bool. clang, on the other hand, does fine. unsigned int nr : 1; unsigned int datetime : 1; unsigned int depth : 1; unsigned int duration : 1; unsigned int air_temp : 1; unsigned int water_temp : 1; unsigned int atm_press : 1; unsigned int divesite : 1; unsigned int divemaster : 1; unsigned int buddy : 1; unsigned int rating : 1; unsigned int visibility : 1; unsigned int wavesize : 1; unsigned int current : 1; unsigned int surge : 1; unsigned int chill : 1; unsigned int suit : 1; unsigned int tags : 1; unsigned int mode : 1; unsigned int notes : 1; unsigned int salinity : 1; unsigned int invalid : 1; enum Flags { NONE = 0, NR = 1 << 0, DATETIME = 1 << 1, DEPTH = 1 << 2, DURATION = 1 << 3, AIR_TEMP = 1 << 4, WATER_TEMP = 1 << 5, ATM_PRESS = 1 << 6, DIVESITE = 1 << 7, DIVEMASTER = 1 << 8, BUDDY = 1 << 9, RATING = 1 << 10, VISIBILITY = 1 << 11, WAVESIZE = 1 << 12, CURRENT = 1 << 13, SURGE = 1 << 14, CHILL = 1 << 15, SUIT = 1 << 16, TAGS = 1 << 17, MODE = 1 << 18, NOTES = 1 << 19, SALINITY = 1 << 20, INVALID = 1 << 21 }; DiveField(int flags); }; struct TripField { unsigned int location : 1; unsigned int notes : 1; enum Flags { NONE = 0, LOCATION = 1 << 0, NOTES = 1 << 1 }; TripField(int flags); }; class DiveListNotifier : public QObject { Q_OBJECT signals: // Note that there are no signals for trips being added and created // because these events never happen without a dive being added, removed or moved. // The dives are always sorted according to the dives_less_than() function of the core. void divesAdded(dive_trip *trip, bool addTrip, const QVector &dives); void divesDeleted(dive_trip *trip, bool deleteTrip, const QVector &dives); void divesMovedBetweenTrips(dive_trip *from, dive_trip *to, bool deleteFrom, bool createTo, const QVector &dives); void divesChanged(const QVector &dives, DiveField field); void divesTimeChanged(timestamp_t delta, const QVector &dives); void cylindersReset(const QVector &dives); void cylinderAdded(dive *d, int pos); void cylinderRemoved(dive *d, int pos); void cylinderEdited(dive *d, int pos); void weightsystemsReset(const QVector &dives); void weightAdded(dive *d, int pos); void weightRemoved(dive *d, int pos); void weightEdited(dive *d, int pos); // Trip edited signal void tripChanged(dive_trip *trip, TripField field); // Selection changes void divesSelected(const QVector &dives); // Dive site signals. Add and delete events are sent per dive site and // provide an index into the global dive site table. void diveSiteAdded(dive_site *ds, int idx); void diveSiteDeleted(dive_site *ds, int idx); void diveSiteDiveCountChanged(dive_site *ds); void diveSiteChanged(dive_site *ds, int field); // field according to LocationInformationModel void diveSiteDivesChanged(dive_site *ds); // The dives associated with that site changed // Filter-related signals void numShownChanged(); void filterReset(); // This signal is emited every time a command is executed. // This is used to hide an old multi-dives-edited warning message. // This is necessary, so that the user can't click on the "undo" button and undo // an unrelated command. void commandExecuted(); 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 signal that they 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 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); } inline DiveField::DiveField(int flags) : nr((flags & NR) != 0), datetime((flags & DATETIME) != 0), depth((flags & DEPTH) != 0), duration((flags & DURATION) != 0), air_temp((flags & AIR_TEMP) != 0), water_temp((flags & WATER_TEMP) != 0), atm_press((flags & ATM_PRESS) != 0), divesite((flags & DIVESITE) != 0), divemaster((flags & DIVEMASTER) != 0), buddy((flags & BUDDY) != 0), rating((flags & RATING) != 0), visibility((flags & VISIBILITY) != 0), wavesize((flags & WAVESIZE) != 0), current((flags & CURRENT) != 0), surge((flags & SURGE) != 0), chill((flags & CHILL) != 0), suit((flags & SUIT) != 0), tags((flags & TAGS) != 0), mode((flags & MODE) != 0), notes((flags & NOTES) != 0), salinity((flags & SALINITY) != 0), invalid((flags & INVALID) != 0) { } inline TripField::TripField(int flags) : location((flags & LOCATION) != 0), notes((flags & NOTES) != 0) { } #endif