aboutsummaryrefslogtreecommitdiffstats
path: root/commands/command_edit.h
blob: 257461c59e994af3813a190c2b2e101008605cca (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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
// SPDX-License-Identifier: GPL-2.0
// Note: this header file is used by the undo-machinery and should not be included elsewhere.

#ifndef COMMAND_EDIT_H
#define COMMAND_EDIT_H

#include "command_base.h"
#include "command.h" // for EditCylinderType
#include "core/subsurface-qt/divelistnotifier.h"

#include <QVector>

// These are commands that edit individual fields of a set of dives.
// The implementation is very OO-style. Out-of-fashion and certainly
// not elegant, but in line with Qt's OO-based design.
// The actual code is in a common base class "Command::EditBase". To
// read and set the fields, the base class calls virtual functions of
// the derived classes.
//
// To deal with different data types, the base class is implemented
// as a template. The template parameter is the type to be read or
// set. Thus, switch-cascades and union trickery can be avoided.

// We put everything in a namespace, so that we can shorten names without polluting the global namespace
namespace Command {

// Base class for commands that have a list of dives.
// This is used for extracting the number of dives and show a
// warning message when multiple dives are edited.
class EditDivesBase : public Base {
protected:
	EditDivesBase(bool currentDiveOnly);
	EditDivesBase(dive *d);
	std::vector<dive *> dives; // Dives to be edited.

	// On undo, we set the selection and current dive at the time of the operation.
	std::vector<dive *> selectedDives;
	struct dive *current;
public:
	int numDives() const;
};

template <typename T>
class EditBase : public EditDivesBase {
protected:
	T value; // Value to be set
	T old; // Previous value

	void undo() override;
	void redo() override;
	bool workToBeDone() override;

public:
	EditBase(T newValue, bool currentDiveOnly);
	EditBase(T newValue, dive *d);

protected:
	// Get and set functions to be overriden by sub-classes.
	virtual void set(struct dive *d, T) const = 0;
	virtual T data(struct dive *d) const = 0;
	virtual QString fieldName() const = 0;	// Name of the field, used to create the undo menu-entry
	virtual DiveField fieldId() const = 0;
};

// The individual Edit-commands define a virtual function that return the field-id.
// For reasons, which I don't fully understand, the C++ makers are strictly opposed
// to "virtual member constants" so we have to define these functions. To make
// things a bit more compact we do this automatically with the following template.
// Of course, we could directly encode the value in the EditBase-template, but
// that would lead to a multiplication of the created code.
template <typename T, DiveField::Flags ID>
class EditTemplate : public EditBase<T> {
private:
	using EditBase<T>::EditBase;	// Use constructor of base class.
	DiveField fieldId() const override final;	// final prevents further overriding - then just don't use this template
};

// Automatically generate getter and setter in the case of simple assignments.
// The third parameter is a pointer to a member of the dive structure.
template <typename T, DiveField::Flags ID, T dive::*PTR>
class EditDefaultSetter : public EditTemplate<T, ID> {
private:
	using EditTemplate<T, ID>::EditTemplate;
	void set(struct dive *d, T) const override final;	// final prevents further overriding - then just don't use this template
	T data(struct dive *d) const override final;	// final prevents further overriding - then just don't use this template
};

// Automatically generate getter and setter in the case for string assignments.
// The third parameter is a pointer to a C-style string in the dive structure.
template <DiveField::Flags ID, char *dive::*PTR>
class EditStringSetter : public EditTemplate<QString, ID> {
private:
	using EditTemplate<QString, ID>::EditTemplate;
	void set(struct dive *d, QString) const override final;	// final prevents further overriding - then just don't use this template
	QString data(struct dive *d) const override final;	// final prevents further overriding - then just don't use this template
};

class EditNotes : public EditStringSetter<DiveField::NOTES, &dive::notes> {
public:
	using EditStringSetter::EditStringSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditSuit : public EditStringSetter<DiveField::SUIT, &dive::suit> {
public:
	using EditStringSetter::EditStringSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditRating : public EditDefaultSetter<int, DiveField::RATING, &dive::rating> {
public:
	using EditDefaultSetter::EditDefaultSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditVisibility : public EditDefaultSetter<int, DiveField::VISIBILITY, &dive::visibility> {
public:
	using EditDefaultSetter::EditDefaultSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditWaveSize : public EditDefaultSetter<int, DiveField::WAVESIZE, &dive::wavesize> {
public:
	using EditDefaultSetter::EditDefaultSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditCurrent : public EditDefaultSetter<int, DiveField::CURRENT, &dive::current> {
public:
	using EditDefaultSetter::EditDefaultSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditSurge : public EditDefaultSetter<int, DiveField::SURGE, &dive::surge> {
public:
	using EditDefaultSetter::EditDefaultSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditChill : public EditDefaultSetter<int, DiveField::CHILL, &dive::chill> {
public:
	using EditDefaultSetter::EditDefaultSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

class EditAirTemp : public EditTemplate<int, DiveField::AIR_TEMP> {
public:
	using EditTemplate::EditTemplate;	// Use constructor of base class.
	void set(struct dive *d, int value) const override;
	int data(struct dive *d) const override;
	QString fieldName() const override;
};

class EditWaterTemp : public EditTemplate<int, DiveField::WATER_TEMP> {
public:
	using EditTemplate::EditTemplate;	// Use constructor of base class.
	void set(struct dive *d, int value) const override;
	int data(struct dive *d) const override;
	QString fieldName() const override;
};

class EditAtmPress : public EditTemplate<int, DiveField::ATM_PRESS> {
public:
	using EditTemplate::EditTemplate;	// Use constructor of base class.
	void set(struct dive *d, int value) const override;
	int data(struct dive *d) const override;
	QString fieldName() const override;
};

class EditWaterTypeUser : public EditTemplate<int, DiveField::SALINITY> {
public:
	using EditTemplate::EditTemplate;	// Use constructor of base class.
	void set(struct dive *d, int value) const override;
	int data(struct dive *d) const override;
	QString fieldName() const override;
};

class EditDuration : public EditTemplate<int, DiveField::DURATION> {
public:
	using EditTemplate::EditTemplate;	// Use constructor of base class.
	void set(struct dive *d, int value) const override;
	int data(struct dive *d) const override;
	QString fieldName() const override;
};

class EditDepth : public EditTemplate<int, DiveField::DEPTH> {
public:
	using EditTemplate::EditTemplate;	// Use constructor of base class.
	void set(struct dive *d, int value) const override;
	int data(struct dive *d) const override;
	QString fieldName() const override;
};

class EditDiveSite : public EditTemplate<struct dive_site *, DiveField::DIVESITE> {
public:
	using EditTemplate::EditTemplate;	// Use constructor of base class.
	void set(struct dive *d, struct dive_site *value) const override;
	struct dive_site *data(struct dive *d) const override;
	QString fieldName() const override;

	// We specialize these so that we can send dive-site changed signals.
	void undo() override;
	void redo() override;
};

// Edit dive site, but add a new dive site first. Reuses the code of EditDiveSite by
// deriving from it and hooks into undo() and redo() to add / remove the dive site.
class EditDiveSiteNew : public EditDiveSite {
public:
	OwningDiveSitePtr diveSiteToAdd;
	struct dive_site *diveSiteToRemove;
	EditDiveSiteNew(const QString &newName, bool currentDiveOnly);
	void undo() override;
	void redo() override;
};

class EditMode : public EditTemplate<int, DiveField::MODE> {
	int index;
public:
	EditMode(int indexIn, int newValue, bool currentDiveOnly);
	void set(struct dive *d, int i) const override;
	int data(struct dive *d) const override;
	QString fieldName() const override;
};

class EditInvalid : public EditDefaultSetter<bool, DiveField::INVALID, &dive::invalid> {
public:
	using EditDefaultSetter::EditDefaultSetter;	// Use constructor of base class.
	QString fieldName() const override;
};

// Fields that work with tag-lists (tags, buddies, divemasters) work differently and therefore
// have their own base class. In this case, it's not a template, as all these lists are base
// on strings.
class EditTagsBase : public EditDivesBase {
	bool workToBeDone() override;

	QStringList newList;	// Temporary until initialized
public:
	EditTagsBase(const QStringList &newList, bool currentDiveOnly);

protected:
	QStringList tagsToAdd;
	QStringList tagsToRemove;
	void undo() override;
	void redo() override;

	// Getters, setters and parsers to be overriden by sub-classes.
	virtual QStringList data(struct dive *d) const = 0;
	virtual void set(struct dive *d, const QStringList &v) const = 0;
	virtual QString fieldName() const = 0;	// Name of the field, used to create the undo menu-entry
	virtual DiveField fieldId() const = 0;
};

// See comments for EditTemplate
template <DiveField::Flags ID>
class EditTagsTemplate : public EditTagsBase {
private:
	using EditTagsBase::EditTagsBase;		// Use constructor of base class.
	DiveField fieldId() const override final;	// final prevents further overriding - then just don't use this template
};

class EditTags : public EditTagsTemplate<DiveField::TAGS> {
public:
	using EditTagsTemplate::EditTagsTemplate;	// Use constructor of base class.
	QStringList data(struct dive *d) const override;
	void set(struct dive *d, const QStringList &v) const override;
	QString fieldName() const override;
};

class EditBuddies : public EditTagsTemplate<DiveField::BUDDY> {
public:
	using EditTagsTemplate::EditTagsTemplate;	// Use constructor of base class.
	QStringList data(struct dive *d) const override;
	void set(struct dive *d, const QStringList &v) const override;
	QString fieldName() const override;
};

class EditDiveMaster : public EditTagsTemplate<DiveField::DIVEMASTER> {
public:
	using EditTagsTemplate::EditTagsTemplate;	// Use constructor of base class.
	QStringList data(struct dive *d) const override;
	void set(struct dive *d, const QStringList &v) const override;
	QString fieldName() const override;
};

// Fields we have to remember to undo paste
struct PasteState {
	dive *d;
	dive_site *divesite;
	QString notes;
	QString divemaster;
	QString buddy;
	QString suit;
	int rating;
	int wavesize;
	int visibility;
	int current;
	int surge;
	int chill;
	tag_entry *tags;
	struct cylinder_table cylinders;
	struct weightsystem_table weightsystems;
	int number;
	timestamp_t when;

	PasteState(dive *d, const dive *data, dive_components what); // Read data from dive data for dive d
	~PasteState();
	void swap(dive_components what); // Exchange values here and in dive
};

class PasteDives : public Base {
	dive_components what;
	std::vector<PasteState> dives;
public:
	PasteDives(const dive *d, dive_components what);
private:
	void undo() override;
	void redo() override;
	bool workToBeDone() override;
};

class ReplanDive : public Base {
	dive *d;

	// Exchange these data with current dive
	timestamp_t when;
	depth_t maxdepth, meandepth;
	struct cylinder_table cylinders;
	struct divecomputer dc;
	char *notes;
	pressure_t surface_pressure;
	duration_t duration;
	int salinity;
public:
	// Dive computer(s) and cylinders(s) of the source dive will be reset!
	// If edit_profile is true, the text will be changed from "replan dive" to "edit profile".
	ReplanDive(dive *source, bool edit_profile);
	~ReplanDive();
private:
	void undo() override;
	void redo() override;
	bool workToBeDone() override;
};

class AddWeight : public EditDivesBase {
public:
	AddWeight(bool currentDiveOnly);
private:
	void undo() override;
	void redo() override;
	bool workToBeDone() override;
};

class EditWeightBase : public EditDivesBase {
protected:
	EditWeightBase(int index, bool currentDiveOnly);
	~EditWeightBase();

	weightsystem_t ws;
	std::vector<int> indices; // An index for each dive in the dives vector.
	bool workToBeDone() override;
};

class RemoveWeight : public EditWeightBase {
public:
	RemoveWeight(int index, bool currentDiveOnly);
private:
	void undo() override;
	void redo() override;
};

class EditWeight : public EditWeightBase {
public:
	EditWeight(int index, weightsystem_t ws, bool currentDiveOnly); // Clones ws
	~EditWeight();
private:
	weightsystem_t new_ws;
	void undo() override;
	void redo() override;
};

class AddCylinder : public EditDivesBase {
public:
	AddCylinder(bool currentDiveOnly);
	~AddCylinder();
private:
	cylinder_t cyl;
	void undo() override;
	void redo() override;
	bool workToBeDone() override;
};

class EditCylinderBase : public EditDivesBase {
protected:
	EditCylinderBase(int index, bool currentDiveOnly, bool nonProtectedOnly, int sameCylinderFlags);
	~EditCylinderBase();

	std::vector<cylinder_t> cyl;
	std::vector<int> indexes; // An index for each dive in the dives vector.
	bool workToBeDone() override;
};

class RemoveCylinder : public EditCylinderBase {
public:
	RemoveCylinder(int index, bool currentDiveOnly);
private:
	void undo() override;
	void redo() override;
};

// Instead of implementing an undo command for every single field in a cylinder,
// we only have one and pass an edit "type". We either edit the type, pressure
// or gasmix fields. This has mostly historical reasons rooted in the way the
// CylindersModel code works. The model works for undo and also in the planner
// without undo. Having a single undo-command simplifies the code there.
class EditCylinder : public EditCylinderBase {
public:
	EditCylinder(int index, cylinder_t cyl, EditCylinderType type, bool currentDiveOnly); // Clones cylinder
private:
	EditCylinderType type;
	void undo() override;
	void redo() override;
};

#ifdef SUBSURFACE_MOBILE
// Edit a full dive. This is used on mobile where we don't have per-field granularity.
// It may add or edit a dive site.
class EditDive : public Base {
public:
	EditDive(dive *oldDive, dive *newDive, dive_site *createDs, dive_site *editDs, location_t dsLocation); // Takes ownership of newDive
private:
	dive *oldDive; // Dive that is going to be overwritten
	OwningDivePtr newDive; // New data
	dive_site *newDiveSite;
	int changedFields;

	dive_site *siteToRemove;
	OwningDiveSitePtr siteToAdd;

	dive_site *siteToEdit;
	location_t dsLocation;

	void undo() override;
	void redo() override;
	bool workToBeDone() override;

	void exchangeDives();
	void editDs();
};

#endif // SUBSURFACE_MOBILE

} // namespace Command
#endif