summaryrefslogtreecommitdiffstats
path: root/core/subsurface-qt/DiveListNotifier.h
blob: b82c0ac051321bb492a57dfc8ef34b7759a8c2f2 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// 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 <QObject>

// 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;
	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
	};
	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<dive *> &dives);
	void divesDeleted(dive_trip *trip, bool deleteTrip, const QVector<dive *> &dives);
	void divesMovedBetweenTrips(dive_trip *from, dive_trip *to, bool deleteFrom, bool createTo, const QVector<dive *> &dives);
	void divesChanged(const QVector<dive *> &dives, DiveField field);
	void divesTimeChanged(timestamp_t delta, const QVector<dive *> &dives);

	void cylindersReset(const QVector<dive *> &dives);
	void weightsystemsReset(const QVector<dive *> &dives);

	// Trip edited signal
	void tripChanged(dive_trip *trip, TripField field);

	// Selection changes
	void divesSelected(const QVector<dive *> &dives, dive *current);

	// 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 &notifier;
		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 &notifierIn) : 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)
{
}

inline TripField::TripField(int flags) :
	location((flags & LOCATION) != 0),
	notes((flags & NOTES) != 0)
{
}
#endif