mirror of
https://github.com/subsurface/subsurface.git
synced 2025-01-31 21:43:23 +00:00
fix copy/paste of dive-site
The copy/pasting of dive-sites was fundamentally broken in at least two ways: 1) The dive-site pointer in struct dive was simply overwritten, which breaks internal consistency. Also, no dive-site changed signals where sent. 2) The copied dive-site was stored as a pointer in a struct dive. Thus, the user could copy a dive, then delete the dive-site and paste. This would lead to a dangling pointer and ultimately crash the application. Fix this by storing the UUID of the dive-site, not a pointer. To do that, don't store a copy of the dive, but collect all the data in a `dive_paste_data` structure. If the dive site has been deleted on paste, do nothing. Send the appropriate signals on pasting. The mobile version had an additional bug: It kept a pointer to the dive to be copied, which might become stale by undo. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
parent
48b4308a7d
commit
152e6966c9
17 changed files with 359 additions and 425 deletions
|
@ -267,9 +267,9 @@ int editDiveGuide(const QStringList &newList, bool currentDiveOnly)
|
||||||
return execute_edit(new EditDiveGuide(newList, currentDiveOnly));
|
return execute_edit(new EditDiveGuide(newList, currentDiveOnly));
|
||||||
}
|
}
|
||||||
|
|
||||||
void pasteDives(const dive *d, dive_components what)
|
void pasteDives(const dive_paste_data &data)
|
||||||
{
|
{
|
||||||
execute(new PasteDives(d, what));
|
execute(new PasteDives(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
void replanDive(dive *d)
|
void replanDive(dive *d)
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
struct divecomputer;
|
struct divecomputer;
|
||||||
struct divelog;
|
struct divelog;
|
||||||
struct dive_components;
|
struct dive_components;
|
||||||
|
struct dive_paste_data;
|
||||||
struct dive_site;
|
struct dive_site;
|
||||||
struct dive_trip;
|
struct dive_trip;
|
||||||
struct event;
|
struct event;
|
||||||
|
@ -95,7 +96,7 @@ int editDiveSiteNew(const QString &newName, bool currentDiveOnly);
|
||||||
int editTags(const QStringList &newList, bool currentDiveOnly);
|
int editTags(const QStringList &newList, bool currentDiveOnly);
|
||||||
int editBuddies(const QStringList &newList, bool currentDiveOnly);
|
int editBuddies(const QStringList &newList, bool currentDiveOnly);
|
||||||
int editDiveGuide(const QStringList &newList, bool currentDiveOnly);
|
int editDiveGuide(const QStringList &newList, bool currentDiveOnly);
|
||||||
void pasteDives(const dive *d, dive_components what);
|
void pasteDives(const dive_paste_data &data);
|
||||||
enum class EditProfileType {
|
enum class EditProfileType {
|
||||||
ADD,
|
ADD,
|
||||||
REMOVE,
|
REMOVE,
|
||||||
|
|
|
@ -613,41 +613,51 @@ QString EditDiveGuide::fieldName() const
|
||||||
return Command::Base::tr("dive guide");
|
return Command::Base::tr("dive guide");
|
||||||
}
|
}
|
||||||
|
|
||||||
PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn)
|
template <typename T>
|
||||||
|
ptrdiff_t comp_ptr(T *v1, T *v2)
|
||||||
{
|
{
|
||||||
if (what.notes)
|
return v1 - v2;
|
||||||
notes = data->notes;
|
}
|
||||||
if (what.diveguide)
|
|
||||||
diveguide = data->diveguide;
|
PasteState::PasteState(dive &d, const dive_paste_data &data, std::vector<dive_site *> &dive_sites_changed) : d(d)
|
||||||
if (what.buddy)
|
{
|
||||||
buddy = data->buddy;
|
notes = data.notes;
|
||||||
if (what.suit)
|
diveguide = data.diveguide;
|
||||||
suit = data->suit;
|
buddy = data.buddy;
|
||||||
if (what.rating)
|
suit = data.suit;
|
||||||
rating = data->rating;
|
rating = data.rating;
|
||||||
if (what.visibility)
|
visibility = data.visibility;
|
||||||
visibility = data->visibility;
|
wavesize = data.wavesize;
|
||||||
if (what.wavesize)
|
current = data.current;
|
||||||
wavesize = data->wavesize;
|
surge = data.surge;
|
||||||
if (what.current)
|
chill = data.chill;
|
||||||
current = data->current;
|
if (data.divesite.has_value()) {
|
||||||
if (what.surge)
|
if (data.divesite) {
|
||||||
surge = data->surge;
|
// In the undo system, we can turn the uuid into a pointer,
|
||||||
if (what.chill)
|
// because everything is serialized.
|
||||||
chill = data->chill;
|
dive_site *ds = divelog.sites.get_by_uuid(*data.divesite);
|
||||||
if (what.divesite)
|
if (ds)
|
||||||
divesite = data->dive_site;
|
divesite = ds;
|
||||||
if (what.tags)
|
} else {
|
||||||
tags = data->tags;
|
divesite = nullptr;
|
||||||
if (what.cylinders) {
|
}
|
||||||
cylinders = data->cylinders;
|
}
|
||||||
|
if (divesite.has_value() && *divesite != d.dive_site) {
|
||||||
|
if (d.dive_site)
|
||||||
|
range_insert_sorted_unique(dive_sites_changed, d.dive_site, comp_ptr<dive_site>); // Use <=> once on C++20
|
||||||
|
if (*divesite)
|
||||||
|
range_insert_sorted_unique(dive_sites_changed, *divesite, comp_ptr<dive_site>); // Use <=> once on C++20
|
||||||
|
}
|
||||||
|
tags = data.tags;
|
||||||
|
if (data.cylinders.has_value()) {
|
||||||
|
cylinders = data.cylinders;
|
||||||
// Paste cylinders is "special":
|
// Paste cylinders is "special":
|
||||||
// 1) For cylinders that exist in the destination dive we keep the gas-mix and pressures.
|
// 1) For cylinders that exist in the destination dive we keep the gas-mix and pressures.
|
||||||
// 2) For cylinders that do not yet exist in the destination dive, we set the pressures to 0, i.e. unset.
|
// 2) For cylinders that do not yet exist in the destination dive, we set the pressures to 0, i.e. unset.
|
||||||
// Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC.
|
// Moreover, for these we set the manually_added flag, because they weren't downloaded from a DC.
|
||||||
for (size_t i = 0; i < d->cylinders.size() && i < cylinders.size(); ++i) {
|
for (size_t i = 0; i < data.cylinders->size() && i < cylinders->size(); ++i) {
|
||||||
const cylinder_t &src = d->cylinders[i];
|
const cylinder_t &src = (*data.cylinders)[i];
|
||||||
cylinder_t &dst = cylinders[i];
|
cylinder_t &dst = (*cylinders)[i];
|
||||||
dst.gasmix = src.gasmix;
|
dst.gasmix = src.gasmix;
|
||||||
dst.start = src.start;
|
dst.start = src.start;
|
||||||
dst.end = src.end;
|
dst.end = src.end;
|
||||||
|
@ -661,8 +671,8 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
|
||||||
dst.bestmix_o2 = src.bestmix_o2;
|
dst.bestmix_o2 = src.bestmix_o2;
|
||||||
dst.bestmix_he = src.bestmix_he;
|
dst.bestmix_he = src.bestmix_he;
|
||||||
}
|
}
|
||||||
for (size_t i = d->cylinders.size(); i < cylinders.size(); ++i) {
|
for (size_t i = data.cylinders->size(); i < cylinders->size(); ++i) {
|
||||||
cylinder_t &cyl = cylinders[i];
|
cylinder_t &cyl = (*cylinders)[i];
|
||||||
cyl.start.mbar = 0;
|
cyl.start.mbar = 0;
|
||||||
cyl.end.mbar = 0;
|
cyl.end.mbar = 0;
|
||||||
cyl.sample_start.mbar = 0;
|
cyl.sample_start.mbar = 0;
|
||||||
|
@ -670,61 +680,62 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI
|
||||||
cyl.manually_added = true;
|
cyl.manually_added = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (what.weights)
|
weightsystems = data.weights;
|
||||||
weightsystems = data->weightsystems;
|
number = data.number;
|
||||||
if (what.number)
|
when = data.when;
|
||||||
number = data->number;
|
|
||||||
if (what.when)
|
|
||||||
when = data->when;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PasteState::~PasteState()
|
PasteState::~PasteState()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void PasteState::swap(dive_components what)
|
void PasteState::swap()
|
||||||
{
|
{
|
||||||
if (what.notes)
|
if (notes.has_value())
|
||||||
std::swap(notes, d->notes);
|
std::swap(*notes, d.notes);
|
||||||
if (what.diveguide)
|
if (diveguide.has_value())
|
||||||
std::swap(diveguide, d->diveguide);
|
std::swap(*diveguide, d.diveguide);
|
||||||
if (what.buddy)
|
if (buddy.has_value())
|
||||||
std::swap(buddy, d->buddy);
|
std::swap(*buddy, d.buddy);
|
||||||
if (what.suit)
|
if (suit.has_value())
|
||||||
std::swap(suit, d->suit);
|
std::swap(*suit, d.suit);
|
||||||
if (what.rating)
|
if (rating.has_value())
|
||||||
std::swap(rating, d->rating);
|
std::swap(*rating, d.rating);
|
||||||
if (what.visibility)
|
if (visibility.has_value())
|
||||||
std::swap(visibility, d->visibility);
|
std::swap(*visibility, d.visibility);
|
||||||
if (what.wavesize)
|
if (wavesize.has_value())
|
||||||
std::swap(wavesize, d->wavesize);
|
std::swap(*wavesize, d.wavesize);
|
||||||
if (what.current)
|
if (current.has_value())
|
||||||
std::swap(current, d->current);
|
std::swap(*current, d.current);
|
||||||
if (what.surge)
|
if (surge.has_value())
|
||||||
std::swap(surge, d->surge);
|
std::swap(*surge, d.surge);
|
||||||
if (what.chill)
|
if (chill.has_value())
|
||||||
std::swap(chill, d->chill);
|
std::swap(*chill, d.chill);
|
||||||
if (what.divesite)
|
if (divesite.has_value() && *divesite != d.dive_site) {
|
||||||
std::swap(divesite, d->dive_site);
|
auto old_ds = unregister_dive_from_dive_site(&d);
|
||||||
if (what.tags)
|
if (*divesite)
|
||||||
std::swap(tags, d->tags);
|
(*divesite)->add_dive(&d);
|
||||||
if (what.cylinders)
|
divesite = old_ds;
|
||||||
std::swap(cylinders, d->cylinders);
|
}
|
||||||
if (what.weights)
|
if (tags.has_value())
|
||||||
std::swap(weightsystems, d->weightsystems);
|
std::swap(*tags, d.tags);
|
||||||
if (what.number)
|
if (cylinders.has_value())
|
||||||
std::swap(number, d->number);
|
std::swap(*cylinders, d.cylinders);
|
||||||
if (what.when)
|
if (weightsystems.has_value())
|
||||||
std::swap(when, d->when);
|
std::swap(*weightsystems, d.weightsystems);
|
||||||
|
if (number.has_value())
|
||||||
|
std::swap(*number, d.number);
|
||||||
|
if (when.has_value())
|
||||||
|
std::swap(*when, d.when);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ***** Paste *****
|
// ***** Paste *****
|
||||||
PasteDives::PasteDives(const dive *data, dive_components whatIn) : what(whatIn)
|
PasteDives::PasteDives(const dive_paste_data &data)
|
||||||
{
|
{
|
||||||
std::vector<dive *> selection = getDiveSelection();
|
std::vector<dive *> selection = getDiveSelection();
|
||||||
dives.reserve(selection.size());
|
dives.reserve(selection.size());
|
||||||
for (dive *d: selection)
|
for (dive *d: selection)
|
||||||
dives.emplace_back(d, data, what);
|
dives.emplace_back(*d, data, dive_sites_changed);
|
||||||
|
|
||||||
setText(QStringLiteral("%1 [%2]").arg(Command::Base::tr("Paste onto %n dive(s)", "", dives.size())).arg(getListOfDives(selection)));
|
setText(QStringLiteral("%1 [%2]").arg(Command::Base::tr("Paste onto %n dive(s)", "", dives.size())).arg(getListOfDives(selection)));
|
||||||
}
|
}
|
||||||
|
@ -739,32 +750,35 @@ void PasteDives::undo()
|
||||||
QVector<dive *> divesToNotify; // Remember dives so that we can send signals later
|
QVector<dive *> divesToNotify; // Remember dives so that we can send signals later
|
||||||
divesToNotify.reserve(dives.size());
|
divesToNotify.reserve(dives.size());
|
||||||
for (PasteState &state: dives) {
|
for (PasteState &state: dives) {
|
||||||
divesToNotify.push_back(state.d);
|
divesToNotify.push_back(&state.d);
|
||||||
state.swap(what);
|
state.swap();
|
||||||
state.d->invalidate_cache(); // Ensure that dive is written in git_save()
|
state.d.invalidate_cache(); // Ensure that dive is written in git_save()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send signals.
|
// Send signals. We use the first data item to determine what changed
|
||||||
DiveField fields(DiveField::NONE);
|
DiveField fields(DiveField::NONE);
|
||||||
fields.notes = what.notes;
|
const PasteState &state = dives[0];
|
||||||
fields.diveguide = what.diveguide;
|
fields.notes = state.notes.has_value();
|
||||||
fields.buddy = what.buddy;
|
fields.diveguide = state.diveguide.has_value();
|
||||||
fields.suit = what.suit;
|
fields.buddy = state.buddy.has_value();
|
||||||
fields.rating = what.rating;
|
fields.suit = state.suit.has_value();
|
||||||
fields.visibility = what.visibility;
|
fields.rating = state.rating.has_value();
|
||||||
fields.wavesize = what.wavesize;
|
fields.visibility = state.visibility.has_value();
|
||||||
fields.current = what.current;
|
fields.wavesize = state.wavesize.has_value();
|
||||||
fields.surge = what.surge;
|
fields.current = state.current.has_value();
|
||||||
fields.chill = what.chill;
|
fields.surge = state.surge.has_value();
|
||||||
fields.divesite = what.divesite;
|
fields.chill = state.chill.has_value();
|
||||||
fields.tags = what.tags;
|
fields.divesite = !dive_sites_changed.empty();
|
||||||
fields.datetime = what.when;
|
fields.tags = state.tags.has_value();
|
||||||
fields.nr = what.number;
|
fields.datetime = state.when.has_value();
|
||||||
|
fields.nr = state.number.has_value();
|
||||||
emit diveListNotifier.divesChanged(divesToNotify, fields);
|
emit diveListNotifier.divesChanged(divesToNotify, fields);
|
||||||
if (what.cylinders)
|
if (state.cylinders.has_value())
|
||||||
emit diveListNotifier.cylindersReset(divesToNotify);
|
emit diveListNotifier.cylindersReset(divesToNotify);
|
||||||
if (what.weights)
|
if (state.weightsystems.has_value())
|
||||||
emit diveListNotifier.weightsystemsReset(divesToNotify);
|
emit diveListNotifier.weightsystemsReset(divesToNotify);
|
||||||
|
for (dive_site *ds: dive_sites_changed)
|
||||||
|
emit diveListNotifier.diveSiteDivesChanged(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redo and undo do the same
|
// Redo and undo do the same
|
||||||
|
|
|
@ -287,34 +287,35 @@ public:
|
||||||
|
|
||||||
// Fields we have to remember to undo paste
|
// Fields we have to remember to undo paste
|
||||||
struct PasteState {
|
struct PasteState {
|
||||||
dive *d;
|
dive &d;
|
||||||
dive_site *divesite;
|
std::optional<dive_site *> divesite;
|
||||||
std::string notes;
|
std::optional<std::string> notes;
|
||||||
std::string diveguide;
|
std::optional<std::string> diveguide;
|
||||||
std::string buddy;
|
std::optional<std::string> buddy;
|
||||||
std::string suit;
|
std::optional<std::string> suit;
|
||||||
int rating;
|
std::optional<int> rating;
|
||||||
int wavesize;
|
std::optional<int> wavesize;
|
||||||
int visibility;
|
std::optional<int> visibility;
|
||||||
int current;
|
std::optional<int> current;
|
||||||
int surge;
|
std::optional<int> surge;
|
||||||
int chill;
|
std::optional<int> chill;
|
||||||
tag_list tags;
|
std::optional<tag_list> tags;
|
||||||
cylinder_table cylinders;
|
std::optional<cylinder_table> cylinders;
|
||||||
weightsystem_table weightsystems;
|
std::optional<weightsystem_table> weightsystems;
|
||||||
int number;
|
std::optional<int> number;
|
||||||
timestamp_t when;
|
std::optional<timestamp_t> when;
|
||||||
|
|
||||||
PasteState(dive *d, const dive *data, dive_components what); // Read data from dive data for dive d
|
PasteState(dive &d, const dive_paste_data &data, std::vector<dive_site *> &changed_dive_sites);
|
||||||
~PasteState();
|
~PasteState();
|
||||||
void swap(dive_components what); // Exchange values here and in dive
|
void swap(); // Exchange values here and in dive
|
||||||
};
|
};
|
||||||
|
|
||||||
class PasteDives : public Base {
|
class PasteDives : public Base {
|
||||||
dive_components what;
|
dive_paste_data data;
|
||||||
std::vector<PasteState> dives;
|
std::vector<PasteState> dives;
|
||||||
|
std::vector<dive_site *> dive_sites_changed;
|
||||||
public:
|
public:
|
||||||
PasteDives(const dive *d, dive_components what);
|
PasteDives(const dive_paste_data &data);
|
||||||
private:
|
private:
|
||||||
void undo() override;
|
void undo() override;
|
||||||
void redo() override;
|
void redo() override;
|
||||||
|
|
|
@ -203,40 +203,6 @@ void copy_dive(const struct dive *s, struct dive *d)
|
||||||
d->invalidate_cache();
|
d->invalidate_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CONDITIONAL_COPY_STRING(_component) \
|
|
||||||
if (what._component) \
|
|
||||||
d->_component = s->_component
|
|
||||||
|
|
||||||
// copy elements, depending on bits in what that are set
|
|
||||||
void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear)
|
|
||||||
{
|
|
||||||
if (clear)
|
|
||||||
d->clear();
|
|
||||||
CONDITIONAL_COPY_STRING(notes);
|
|
||||||
CONDITIONAL_COPY_STRING(diveguide);
|
|
||||||
CONDITIONAL_COPY_STRING(buddy);
|
|
||||||
CONDITIONAL_COPY_STRING(suit);
|
|
||||||
if (what.rating)
|
|
||||||
d->rating = s->rating;
|
|
||||||
if (what.visibility)
|
|
||||||
d->visibility = s->visibility;
|
|
||||||
if (what.divesite) {
|
|
||||||
unregister_dive_from_dive_site(d);
|
|
||||||
s->dive_site->add_dive(d);
|
|
||||||
}
|
|
||||||
if (what.tags)
|
|
||||||
d->tags = s->tags;
|
|
||||||
if (what.cylinders)
|
|
||||||
copy_cylinder_types(s, d);
|
|
||||||
if (what.weights)
|
|
||||||
d->weightsystems = s->weightsystems;
|
|
||||||
if (what.number)
|
|
||||||
d->number = s->number;
|
|
||||||
if (what.when)
|
|
||||||
d->when = s->when;
|
|
||||||
}
|
|
||||||
#undef CONDITIONAL_COPY_STRING
|
|
||||||
|
|
||||||
/* copies all events from the given dive computer before a given time
|
/* copies all events from the given dive computer before a given time
|
||||||
this is used when editing a dive in the planner to preserve the events
|
this is used when editing a dive in the planner to preserve the events
|
||||||
of the old dive */
|
of the old dive */
|
||||||
|
|
39
core/dive.h
39
core/dive.h
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -146,24 +147,25 @@ struct dive_or_trip {
|
||||||
extern void cylinder_renumber(struct dive &dive, int mapping[]);
|
extern void cylinder_renumber(struct dive &dive, int mapping[]);
|
||||||
extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused);
|
extern int same_gasmix_cylinder(const cylinder_t &cyl, int cylid, const struct dive *dive, bool check_unused);
|
||||||
|
|
||||||
/* when selectively copying dive information, which parts should be copied? */
|
/* Data stored when copying a dive */
|
||||||
struct dive_components {
|
struct dive_paste_data {
|
||||||
unsigned int divesite : 1;
|
std::optional<uint32_t> divesite; // We save the uuid not a pointer, because the
|
||||||
unsigned int notes : 1;
|
// user might copy and then delete the dive site.
|
||||||
unsigned int diveguide : 1;
|
std::optional<std::string> notes;
|
||||||
unsigned int buddy : 1;
|
std::optional<std::string> diveguide;
|
||||||
unsigned int suit : 1;
|
std::optional<std::string> buddy;
|
||||||
unsigned int rating : 1;
|
std::optional<std::string> suit;
|
||||||
unsigned int visibility : 1;
|
std::optional<int> rating;
|
||||||
unsigned int wavesize : 1;
|
std::optional<int> visibility;
|
||||||
unsigned int current : 1;
|
std::optional<int> wavesize;
|
||||||
unsigned int surge : 1;
|
std::optional<int> current;
|
||||||
unsigned int chill : 1;
|
std::optional<int> surge;
|
||||||
unsigned int tags : 1;
|
std::optional<int> chill;
|
||||||
unsigned int cylinders : 1;
|
std::optional<tag_list> tags;
|
||||||
unsigned int weights : 1;
|
std::optional<cylinder_table> cylinders;
|
||||||
unsigned int number : 1;
|
std::optional<weightsystem_table> weights;
|
||||||
unsigned int when : 1;
|
std::optional<int> number;
|
||||||
|
std::optional<timestamp_t> when;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern std::unique_ptr<dive> clone_make_first_dc(const struct dive &d, int dc_number);
|
extern std::unique_ptr<dive> clone_make_first_dc(const struct dive &d, int dc_number);
|
||||||
|
@ -179,7 +181,6 @@ struct membuffer;
|
||||||
extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize);
|
extern void save_one_dive_to_mb(struct membuffer *b, const struct dive &dive, bool anonymize);
|
||||||
|
|
||||||
extern void copy_dive(const struct dive *s, struct dive *d);
|
extern void copy_dive(const struct dive *s, struct dive *d);
|
||||||
extern void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_components what, bool clear);
|
|
||||||
|
|
||||||
extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc);
|
extern int legacy_format_o2pressures(const struct dive *dive, const struct divecomputer *dc);
|
||||||
|
|
||||||
|
|
12
core/range.h
12
core/range.h
|
@ -181,13 +181,23 @@ bool range_contains(const Range &v, const Element &item)
|
||||||
|
|
||||||
// Insert into an already sorted range
|
// Insert into an already sorted range
|
||||||
template<typename Range, typename Element, typename Comp>
|
template<typename Range, typename Element, typename Comp>
|
||||||
void range_insert_sorted(Range &v, Element &item, Comp &comp)
|
void range_insert_sorted(Range &v, Element &item, Comp comp)
|
||||||
{
|
{
|
||||||
auto it = std::lower_bound(std::begin(v), std::end(v), item,
|
auto it = std::lower_bound(std::begin(v), std::end(v), item,
|
||||||
[&comp](auto &a, auto &b) { return comp(a, b) < 0; });
|
[&comp](auto &a, auto &b) { return comp(a, b) < 0; });
|
||||||
v.insert(it, std::move(item));
|
v.insert(it, std::move(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert into an already sorted range, but don't add an item twice
|
||||||
|
template<typename Range, typename Element, typename Comp>
|
||||||
|
void range_insert_sorted_unique(Range &v, Element &item, Comp comp)
|
||||||
|
{
|
||||||
|
auto it = std::lower_bound(std::begin(v), std::end(v), item,
|
||||||
|
[&comp](auto &a, auto &b) { return comp(a, b) < 0; });
|
||||||
|
if (it == std::end(v) || comp(item, *it) != 0)
|
||||||
|
v.insert(it, std::move(item));
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Range, typename Element>
|
template<typename Range, typename Element>
|
||||||
void range_remove(Range &v, const Element &item)
|
void range_remove(Range &v, const Element &item)
|
||||||
{
|
{
|
||||||
|
|
|
@ -68,6 +68,8 @@ set(SUBSURFACE_INTERFACE
|
||||||
about.h
|
about.h
|
||||||
configuredivecomputerdialog.cpp
|
configuredivecomputerdialog.cpp
|
||||||
configuredivecomputerdialog.h
|
configuredivecomputerdialog.h
|
||||||
|
divecomponentselection.cpp
|
||||||
|
divecomponentselection.h
|
||||||
divelistview.cpp
|
divelistview.cpp
|
||||||
divelistview.h
|
divelistview.h
|
||||||
divelogexportdialog.cpp
|
divelogexportdialog.cpp
|
||||||
|
|
104
desktop-widgets/divecomponentselection.cpp
Normal file
104
desktop-widgets/divecomponentselection.cpp
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#include "divecomponentselection.h"
|
||||||
|
#include "core/dive.h"
|
||||||
|
#include "core/divesite.h"
|
||||||
|
#include "core/format.h"
|
||||||
|
#include "core/qthelper.h" // for get_dive_date_string()
|
||||||
|
#include "core/selection.h"
|
||||||
|
#include "core/string-format.h"
|
||||||
|
|
||||||
|
#include <QClipboard>
|
||||||
|
#include <QShortcut>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void assign_paste_data(QCheckBox &checkbox, const T &src, std::optional<T> &dest)
|
||||||
|
{
|
||||||
|
if (checkbox.isChecked())
|
||||||
|
dest = src;
|
||||||
|
else
|
||||||
|
dest = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void set_checked(QCheckBox &checkbox, const std::optional<T> &v)
|
||||||
|
{
|
||||||
|
checkbox.setChecked(v.has_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
DiveComponentSelection::DiveComponentSelection(dive_paste_data &data, QWidget *parent) :
|
||||||
|
QDialog(parent), data(data)
|
||||||
|
{
|
||||||
|
ui.setupUi(this);
|
||||||
|
set_checked(*ui.divesite, data.divesite);
|
||||||
|
set_checked(*ui.diveguide, data.diveguide);
|
||||||
|
set_checked(*ui.buddy, data.buddy);
|
||||||
|
set_checked(*ui.rating, data.rating);
|
||||||
|
set_checked(*ui.visibility, data.visibility);
|
||||||
|
set_checked(*ui.notes, data.notes);
|
||||||
|
set_checked(*ui.suit, data.suit);
|
||||||
|
set_checked(*ui.tags, data.tags);
|
||||||
|
set_checked(*ui.cylinders, data.cylinders);
|
||||||
|
set_checked(*ui.weights, data.weights);
|
||||||
|
set_checked(*ui.number, data.number);
|
||||||
|
set_checked(*ui.when, data.when);
|
||||||
|
connect(ui.buttonBox, &QDialogButtonBox::clicked, this, &DiveComponentSelection::buttonClicked);
|
||||||
|
QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this);
|
||||||
|
connect(close, &QShortcut::activated, this, &DiveComponentSelection::close);
|
||||||
|
QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this);
|
||||||
|
connect(quit, &QShortcut::activated, parent, &QWidget::close);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DiveComponentSelection::buttonClicked(QAbstractButton *button)
|
||||||
|
{
|
||||||
|
if (current_dive && ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
|
||||||
|
// Divesite is special, as we store the uuid not the pointer, since the
|
||||||
|
// user may delete the divesite after copying.
|
||||||
|
assign_paste_data(*ui.divesite, current_dive->dive_site ? current_dive->dive_site->uuid : uint32_t(), data.divesite);
|
||||||
|
assign_paste_data(*ui.diveguide, current_dive->diveguide, data.diveguide);
|
||||||
|
assign_paste_data(*ui.buddy, current_dive->buddy, data.buddy);
|
||||||
|
assign_paste_data(*ui.rating, current_dive->rating, data.rating);
|
||||||
|
assign_paste_data(*ui.visibility, current_dive->visibility, data.visibility);
|
||||||
|
assign_paste_data(*ui.notes, current_dive->notes, data.notes);
|
||||||
|
assign_paste_data(*ui.suit, current_dive->suit, data.suit);
|
||||||
|
assign_paste_data(*ui.tags, current_dive->tags, data.tags);
|
||||||
|
assign_paste_data(*ui.cylinders, current_dive->cylinders, data.cylinders);
|
||||||
|
assign_paste_data(*ui.weights, current_dive->weightsystems, data.weights);
|
||||||
|
assign_paste_data(*ui.number, current_dive->number, data.number);
|
||||||
|
assign_paste_data(*ui.when, current_dive->when, data.when);
|
||||||
|
|
||||||
|
std::string text;
|
||||||
|
if (data.divesite && current_dive->dive_site)
|
||||||
|
text += tr("Dive site: ").toStdString() + current_dive->dive_site->name + '\n';
|
||||||
|
if (data.diveguide)
|
||||||
|
text += tr("Dive guide: ").toStdString() + current_dive->diveguide + '\n';
|
||||||
|
if (data.buddy)
|
||||||
|
text += tr("Buddy: ").toStdString() + current_dive->buddy + '\n';
|
||||||
|
if (data.rating)
|
||||||
|
text += tr("Rating: ").toStdString() + std::string(current_dive->rating, '*') + '\n';
|
||||||
|
if (data.visibility)
|
||||||
|
text += tr("Visibility: ").toStdString() + std::string(current_dive->visibility, '*') + '\n';
|
||||||
|
if (data.wavesize)
|
||||||
|
text += tr("Wave size: ").toStdString() + std::string(current_dive->wavesize, '*') + '\n';
|
||||||
|
if (data.current)
|
||||||
|
text += tr("Current: ").toStdString() + std::string(current_dive->current, '*') + '\n';
|
||||||
|
if (data.surge)
|
||||||
|
text += tr("Surge: ").toStdString() + std::string(current_dive->surge, '*') + '\n';
|
||||||
|
if (data.chill)
|
||||||
|
text += tr("Chill: ").toStdString() + std::string(current_dive->chill, '*') + '\n';
|
||||||
|
if (data.notes)
|
||||||
|
text += tr("Notes:\n").toStdString() + current_dive->notes + '\n';
|
||||||
|
if (data.suit)
|
||||||
|
text += tr("Suit: ").toStdString() + current_dive->suit + '\n';
|
||||||
|
if (data.tags)
|
||||||
|
text += tr("Tags: ").toStdString() + taglist_get_tagstring(current_dive->tags) + '\n';
|
||||||
|
if (data.cylinders)
|
||||||
|
text += tr("Cylinders:\n").toStdString() + formatGas(current_dive).toStdString();
|
||||||
|
if (data.weights)
|
||||||
|
text += tr("Weights:\n").toStdString() + formatWeightList(current_dive).toStdString();
|
||||||
|
if (data.number)
|
||||||
|
text += tr("Dive number: ").toStdString() + casprintf_loc("%d", current_dive->number) + '\n';
|
||||||
|
if (data.when)
|
||||||
|
text += tr("Date / time: ").toStdString() + get_dive_date_string(current_dive->when).toStdString() + '\n';
|
||||||
|
QApplication::clipboard()->setText(QString::fromStdString(text));
|
||||||
|
}
|
||||||
|
}
|
22
desktop-widgets/divecomponentselection.h
Normal file
22
desktop-widgets/divecomponentselection.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
#ifndef DIVECOMPONENTSELECTION_H
|
||||||
|
#define DIVECOMPONENTSELECTION_H
|
||||||
|
|
||||||
|
#include "ui_divecomponentselection.h"
|
||||||
|
|
||||||
|
struct dive_paste_data;
|
||||||
|
|
||||||
|
class DiveComponentSelection : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit DiveComponentSelection(dive_paste_data &data, QWidget *parent = nullptr);
|
||||||
|
private
|
||||||
|
slots:
|
||||||
|
void buttonClicked(QAbstractButton *button);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::DiveComponentSelectionDialog ui;
|
||||||
|
dive_paste_data &data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -38,6 +38,7 @@
|
||||||
#include "core/settings/qPrefDisplay.h"
|
#include "core/settings/qPrefDisplay.h"
|
||||||
|
|
||||||
#include "desktop-widgets/about.h"
|
#include "desktop-widgets/about.h"
|
||||||
|
#include "desktop-widgets/divecomponentselection.h"
|
||||||
#include "desktop-widgets/divelistview.h"
|
#include "desktop-widgets/divelistview.h"
|
||||||
#include "desktop-widgets/divelogexportdialog.h"
|
#include "desktop-widgets/divelogexportdialog.h"
|
||||||
#include "desktop-widgets/divelogimportdialog.h"
|
#include "desktop-widgets/divelogimportdialog.h"
|
||||||
|
@ -207,7 +208,6 @@ MainWindow::MainWindow() :
|
||||||
#ifdef NO_USERMANUAL
|
#ifdef NO_USERMANUAL
|
||||||
ui.menuHelp->removeAction(ui.actionUserManual);
|
ui.menuHelp->removeAction(ui.actionUserManual);
|
||||||
#endif
|
#endif
|
||||||
memset(&what, 0, sizeof(what));
|
|
||||||
|
|
||||||
updateManager = new UpdateManager(this);
|
updateManager = new UpdateManager(this);
|
||||||
undoAction = Command::undoAction(this);
|
undoAction = Command::undoAction(this);
|
||||||
|
@ -1424,13 +1424,13 @@ void MainWindow::on_copy_triggered()
|
||||||
{
|
{
|
||||||
// open dialog to select what gets copied
|
// open dialog to select what gets copied
|
||||||
// copy the displayed dive
|
// copy the displayed dive
|
||||||
DiveComponentSelection dialog(this, ©PasteDive, &what);
|
DiveComponentSelection dialog(paste_data, this);
|
||||||
dialog.exec();
|
dialog.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_paste_triggered()
|
void MainWindow::on_paste_triggered()
|
||||||
{
|
{
|
||||||
Command::pasteDives(©PasteDive, what);
|
Command::pasteDives(paste_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_actionFilterTags_triggered()
|
void MainWindow::on_actionFilterTags_triggered()
|
||||||
|
|
|
@ -203,8 +203,7 @@ private:
|
||||||
bool plannerStateClean();
|
bool plannerStateClean();
|
||||||
void setupSocialNetworkMenu();
|
void setupSocialNetworkMenu();
|
||||||
QDialog *findMovedImagesDialog;
|
QDialog *findMovedImagesDialog;
|
||||||
struct dive copyPasteDive;
|
dive_paste_data paste_data;
|
||||||
struct dive_components what;
|
|
||||||
QStringList recentFiles;
|
QStringList recentFiles;
|
||||||
QAction *actionsRecent[NUM_RECENT_FILES];
|
QAction *actionsRecent[NUM_RECENT_FILES];
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QToolTip>
|
#include <QToolTip>
|
||||||
#include <QClipboard>
|
|
||||||
#include <QCompleter>
|
#include <QCompleter>
|
||||||
|
|
||||||
#include "core/file.h"
|
#include "core/file.h"
|
||||||
|
@ -277,93 +276,6 @@ QString URLDialog::url() const
|
||||||
return ui.urlField->toPlainText();
|
return ui.urlField->toPlainText();
|
||||||
}
|
}
|
||||||
|
|
||||||
#define COMPONENT_FROM_UI(_component) what->_component = ui._component->isChecked()
|
|
||||||
#define UI_FROM_COMPONENT(_component) ui._component->setChecked(what->_component)
|
|
||||||
|
|
||||||
DiveComponentSelection::DiveComponentSelection(QWidget *parent, struct dive *target, struct dive_components *_what) : targetDive(target)
|
|
||||||
{
|
|
||||||
ui.setupUi(this);
|
|
||||||
what = _what;
|
|
||||||
UI_FROM_COMPONENT(divesite);
|
|
||||||
UI_FROM_COMPONENT(diveguide);
|
|
||||||
UI_FROM_COMPONENT(buddy);
|
|
||||||
UI_FROM_COMPONENT(rating);
|
|
||||||
UI_FROM_COMPONENT(visibility);
|
|
||||||
UI_FROM_COMPONENT(notes);
|
|
||||||
UI_FROM_COMPONENT(suit);
|
|
||||||
UI_FROM_COMPONENT(tags);
|
|
||||||
UI_FROM_COMPONENT(cylinders);
|
|
||||||
UI_FROM_COMPONENT(weights);
|
|
||||||
UI_FROM_COMPONENT(number);
|
|
||||||
UI_FROM_COMPONENT(when);
|
|
||||||
connect(ui.buttonBox, &QDialogButtonBox::clicked, this, &DiveComponentSelection::buttonClicked);
|
|
||||||
QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_W), this);
|
|
||||||
connect(close, &QShortcut::activated, this, &DiveComponentSelection::close);
|
|
||||||
QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_Q), this);
|
|
||||||
connect(quit, &QShortcut::activated, parent, &QWidget::close);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DiveComponentSelection::buttonClicked(QAbstractButton *button)
|
|
||||||
{
|
|
||||||
if (current_dive && ui.buttonBox->buttonRole(button) == QDialogButtonBox::AcceptRole) {
|
|
||||||
COMPONENT_FROM_UI(divesite);
|
|
||||||
COMPONENT_FROM_UI(diveguide);
|
|
||||||
COMPONENT_FROM_UI(buddy);
|
|
||||||
COMPONENT_FROM_UI(rating);
|
|
||||||
COMPONENT_FROM_UI(visibility);
|
|
||||||
COMPONENT_FROM_UI(notes);
|
|
||||||
COMPONENT_FROM_UI(suit);
|
|
||||||
COMPONENT_FROM_UI(tags);
|
|
||||||
COMPONENT_FROM_UI(cylinders);
|
|
||||||
COMPONENT_FROM_UI(weights);
|
|
||||||
COMPONENT_FROM_UI(number);
|
|
||||||
COMPONENT_FROM_UI(when);
|
|
||||||
selective_copy_dive(current_dive, targetDive, *what, true);
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
QTextStream text;
|
|
||||||
QString cliptext;
|
|
||||||
text.setString(&cliptext);
|
|
||||||
if (what->divesite && current_dive->dive_site)
|
|
||||||
text << tr("Dive site: ") << QString::fromStdString(current_dive->dive_site->name) << "\n";
|
|
||||||
if (what->diveguide)
|
|
||||||
text << tr("Dive guide: ") << QString::fromStdString(current_dive->diveguide) << "\n";
|
|
||||||
if (what->buddy)
|
|
||||||
text << tr("Buddy: ") << QString::fromStdString(current_dive->buddy) << "\n";
|
|
||||||
if (what->rating)
|
|
||||||
text << tr("Rating: ") + QString("*").repeated(current_dive->rating) << "\n";
|
|
||||||
if (what->visibility)
|
|
||||||
text << tr("Visibility: ") + QString("*").repeated(current_dive->visibility) << "\n";
|
|
||||||
if (what->notes)
|
|
||||||
text << tr("Notes:\n") << QString::fromStdString(current_dive->notes) << "\n";
|
|
||||||
if (what->suit)
|
|
||||||
text << tr("Suit: ") << QString::fromStdString(current_dive->suit) << "\n";
|
|
||||||
if (what-> tags) {
|
|
||||||
text << tr("Tags: ");
|
|
||||||
for (const divetag *tag: current_dive->tags)
|
|
||||||
text << tag->name.c_str() << " ";
|
|
||||||
text << "\n";
|
|
||||||
}
|
|
||||||
if (what->cylinders) {
|
|
||||||
text << tr("Cylinders:\n");
|
|
||||||
for (auto [idx, cyl]: enumerated_range(current_dive->cylinders)) {
|
|
||||||
if (current_dive->is_cylinder_used(idx))
|
|
||||||
text << QString::fromStdString(cyl.type.description) << " "
|
|
||||||
<< QString::fromStdString(cyl.gasmix.name()) << "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (what->weights) {
|
|
||||||
text << tr("Weights:\n");
|
|
||||||
for (auto &ws: current_dive->weightsystems)
|
|
||||||
text << QString::fromStdString(ws.description) << ws.weight.grams / 1000 << "kg\n";
|
|
||||||
}
|
|
||||||
if (what->number)
|
|
||||||
text << tr("Dive number: ") << current_dive->number << "\n";
|
|
||||||
if (what->when)
|
|
||||||
text << tr("Date / time: ") << get_dive_date_string(current_dive->when) << "\n";
|
|
||||||
clipboard->setText(cliptext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddFilterPresetDialog::AddFilterPresetDialog(const QString &defaultName, QWidget *parent)
|
AddFilterPresetDialog::AddFilterPresetDialog(const QString &defaultName, QWidget *parent)
|
||||||
{
|
{
|
||||||
ui.setupUi(this);
|
ui.setupUi(this);
|
||||||
|
|
|
@ -7,7 +7,6 @@ class QAbstractButton;
|
||||||
class QNetworkReply;
|
class QNetworkReply;
|
||||||
class FilterModelBase;
|
class FilterModelBase;
|
||||||
struct dive;
|
struct dive;
|
||||||
struct dive_components;
|
|
||||||
|
|
||||||
#include "core/units.h"
|
#include "core/units.h"
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
@ -20,7 +19,6 @@ struct dive_components;
|
||||||
#include "ui_shifttimes.h"
|
#include "ui_shifttimes.h"
|
||||||
#include "ui_shiftimagetimes.h"
|
#include "ui_shiftimagetimes.h"
|
||||||
#include "ui_urldialog.h"
|
#include "ui_urldialog.h"
|
||||||
#include "ui_divecomponentselection.h"
|
|
||||||
#include "ui_listfilter.h"
|
#include "ui_listfilter.h"
|
||||||
#include "ui_addfilterpreset.h"
|
#include "ui_addfilterpreset.h"
|
||||||
|
|
||||||
|
@ -102,20 +100,6 @@ private:
|
||||||
Ui::URLDialog ui;
|
Ui::URLDialog ui;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DiveComponentSelection : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DiveComponentSelection(QWidget *parent, struct dive *target, struct dive_components *_what);
|
|
||||||
private
|
|
||||||
slots:
|
|
||||||
void buttonClicked(QAbstractButton *button);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::DiveComponentSelectionDialog ui;
|
|
||||||
struct dive *targetDive;
|
|
||||||
struct dive_components *what;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AddFilterPresetDialog : public QDialog {
|
class AddFilterPresetDialog : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -38,11 +38,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleDiveSite(false)
|
checked: manager.pasteDiveSite
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleDiveSite(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Notes")
|
text: qsTr("Notes")
|
||||||
|
@ -50,11 +47,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleNotes(false)
|
checked: manager.pasteNotes
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleNotes(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Dive guide")
|
text: qsTr("Dive guide")
|
||||||
|
@ -62,11 +56,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleDiveGuide(false)
|
checked: manager.pasteDiveGuide
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleDiveGuide(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Buddy")
|
text: qsTr("Buddy")
|
||||||
|
@ -74,11 +65,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleBuddy(false)
|
checked: manager.pasteBuddy
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleBuddy(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Suit")
|
text: qsTr("Suit")
|
||||||
|
@ -86,11 +74,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleSuit(false)
|
checked: manager.pasteSuit
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleSuit(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Rating")
|
text: qsTr("Rating")
|
||||||
|
@ -98,11 +83,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleRating(false)
|
checked: manager.pasteRating
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleRating(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Visibility")
|
text: qsTr("Visibility")
|
||||||
|
@ -110,11 +92,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleVisibility(false)
|
checked: manager.pasteVisibility
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleVisibility(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Tags")
|
text: qsTr("Tags")
|
||||||
|
@ -122,11 +101,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleTags(false)
|
checked: manager.pasteTags
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleTags(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Cylinders")
|
text: qsTr("Cylinders")
|
||||||
|
@ -134,11 +110,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleCylinders(false)
|
checked: manager.pasteCylinders
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleCylinders(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Controls.Label {
|
Controls.Label {
|
||||||
text: qsTr("Weights")
|
text: qsTr("Weights")
|
||||||
|
@ -146,11 +119,8 @@ Kirigami.ScrollablePage {
|
||||||
Layout.preferredWidth: gridWidth * 0.75
|
Layout.preferredWidth: gridWidth * 0.75
|
||||||
}
|
}
|
||||||
SsrfSwitch {
|
SsrfSwitch {
|
||||||
checked: manager.toggleWeights(false)
|
checked: manager.pasteWeights
|
||||||
Layout.preferredWidth: gridWidth * 0.25
|
Layout.preferredWidth: gridWidth * 0.25
|
||||||
onClicked: {
|
|
||||||
manager.toggleWeights(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -303,17 +303,18 @@ QMLManager::QMLManager() :
|
||||||
// make sure we know if the current cloud repo has been successfully synced
|
// make sure we know if the current cloud repo has been successfully synced
|
||||||
syncLoadFromCloud();
|
syncLoadFromCloud();
|
||||||
|
|
||||||
memset(&m_copyPasteDive, 0, sizeof(m_copyPasteDive));
|
|
||||||
memset(&what, 0, sizeof(what));
|
|
||||||
|
|
||||||
// Let's set some defaults to be copied so users don't necessarily need
|
// Let's set some defaults to be copied so users don't necessarily need
|
||||||
// to know how to configure this
|
// to know how to configure this
|
||||||
what.diveguide = true;
|
m_pasteDiveSite = false;
|
||||||
what.buddy = true;
|
m_pasteNotes = false;
|
||||||
what.suit = true;
|
m_pasteDiveGuide = true;
|
||||||
what.tags = true;
|
m_pasteBuddy = true;
|
||||||
what.cylinders = true;
|
m_pasteSuit = true;
|
||||||
what.weights = true;
|
m_pasteRating = false;
|
||||||
|
m_pasteVisibility = false;
|
||||||
|
m_pasteTags = true;
|
||||||
|
m_pasteCylinders = true;
|
||||||
|
m_pasteWeights = true;
|
||||||
|
|
||||||
// monitor when dives changed - but only in verbose mode
|
// monitor when dives changed - but only in verbose mode
|
||||||
// careful - changing verbose at runtime isn't enough (of course that could be added if we want it)
|
// careful - changing verbose at runtime isn't enough (of course that could be added if we want it)
|
||||||
|
@ -1607,104 +1608,40 @@ void QMLManager::toggleDiveInvalid(int id)
|
||||||
changesNeedSaving();
|
changesNeedSaving();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QMLManager::toggleDiveSite(bool toggle)
|
template <typename T>
|
||||||
|
static void assign_paste_data(bool copy, const T &src, std::optional<T> &dest)
|
||||||
{
|
{
|
||||||
if (toggle)
|
if (copy)
|
||||||
what.divesite = what.divesite ? false : true;
|
dest = src;
|
||||||
|
else
|
||||||
return what.divesite;
|
dest = {};
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleNotes(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.notes = what.notes ? false : true;
|
|
||||||
|
|
||||||
return what.notes;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleDiveGuide(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.diveguide = what.diveguide ? false : true;
|
|
||||||
|
|
||||||
return what.diveguide;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleBuddy(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.buddy = what.buddy ? false : true;
|
|
||||||
|
|
||||||
return what.buddy;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleSuit(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.suit = what.suit ? false : true;
|
|
||||||
|
|
||||||
return what.suit;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleRating(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.rating = what.rating ? false : true;
|
|
||||||
|
|
||||||
return what.rating;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleVisibility(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.visibility = what.visibility ? false : true;
|
|
||||||
|
|
||||||
return what.visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleTags(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.tags = what.tags ? false : true;
|
|
||||||
|
|
||||||
return what.tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleCylinders(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.cylinders = what.cylinders ? false : true;
|
|
||||||
|
|
||||||
return what.cylinders;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QMLManager::toggleWeights(bool toggle)
|
|
||||||
{
|
|
||||||
if (toggle)
|
|
||||||
what.weights = what.weights ? false : true;
|
|
||||||
|
|
||||||
return what.weights;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMLManager::copyDiveData(int id)
|
void QMLManager::copyDiveData(int id)
|
||||||
{
|
{
|
||||||
m_copyPasteDive = divelog.dives.get_by_uniq_id(id);
|
const dive *d = divelog.dives.get_by_uniq_id(id);
|
||||||
if (!m_copyPasteDive) {
|
if (!d) {
|
||||||
appendTextToLog("trying to copy non-existing dive");
|
appendTextToLog("trying to copy non-existing dive");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assign_paste_data(m_pasteDiveSite, d->dive_site ? d->dive_site->uuid : uint32_t(), paste_data.divesite);
|
||||||
|
assign_paste_data(m_pasteNotes, d->notes, paste_data.notes);
|
||||||
|
assign_paste_data(m_pasteDiveGuide, d->diveguide, paste_data.diveguide);
|
||||||
|
assign_paste_data(m_pasteBuddy, d->buddy, paste_data.buddy);
|
||||||
|
assign_paste_data(m_pasteSuit, d->suit, paste_data.suit);
|
||||||
|
assign_paste_data(m_pasteRating, d->rating, paste_data.rating);
|
||||||
|
assign_paste_data(m_pasteVisibility, d->visibility, paste_data.visibility);
|
||||||
|
assign_paste_data(m_pasteTags, d->tags, paste_data.tags);
|
||||||
|
assign_paste_data(m_pasteCylinders, d->cylinders, paste_data.cylinders);
|
||||||
|
assign_paste_data(m_pasteWeights, d->weightsystems, paste_data.weights);
|
||||||
|
|
||||||
setNotificationText("Copy");
|
setNotificationText("Copy");
|
||||||
}
|
}
|
||||||
|
|
||||||
void QMLManager::pasteDiveData(int id)
|
void QMLManager::pasteDiveData(int id)
|
||||||
{
|
{
|
||||||
if (!m_copyPasteDive) {
|
Command::pasteDives(paste_data);
|
||||||
appendTextToLog("dive to paste is not selected");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Command::pasteDives(m_copyPasteDive, what);
|
|
||||||
changesNeedSaving();
|
changesNeedSaving();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,17 @@ class QMLManager : public QObject {
|
||||||
Q_PROPERTY(QString progressMessage MEMBER m_progressMessage WRITE setProgressMessage NOTIFY progressMessageChanged)
|
Q_PROPERTY(QString progressMessage MEMBER m_progressMessage WRITE setProgressMessage NOTIFY progressMessageChanged)
|
||||||
Q_PROPERTY(bool btEnabled MEMBER m_btEnabled WRITE setBtEnabled NOTIFY btEnabledChanged)
|
Q_PROPERTY(bool btEnabled MEMBER m_btEnabled WRITE setBtEnabled NOTIFY btEnabledChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool pasteDiveSite MEMBER m_pasteDiveSite)
|
||||||
|
Q_PROPERTY(bool pasteNotes MEMBER m_pasteNotes)
|
||||||
|
Q_PROPERTY(bool pasteDiveGuide MEMBER m_pasteDiveGuide)
|
||||||
|
Q_PROPERTY(bool pasteBuddy MEMBER m_pasteBuddy)
|
||||||
|
Q_PROPERTY(bool pasteSuit MEMBER m_pasteSuit)
|
||||||
|
Q_PROPERTY(bool pasteRating MEMBER m_pasteRating)
|
||||||
|
Q_PROPERTY(bool pasteVisibility MEMBER m_pasteVisibility)
|
||||||
|
Q_PROPERTY(bool pasteTags MEMBER m_pasteTags)
|
||||||
|
Q_PROPERTY(bool pasteCylinders MEMBER m_pasteCylinders)
|
||||||
|
Q_PROPERTY(bool pasteWeights MEMBER m_pasteWeights)
|
||||||
|
|
||||||
Q_PROPERTY(QString DC_vendor READ DC_vendor WRITE DC_setVendor)
|
Q_PROPERTY(QString DC_vendor READ DC_vendor WRITE DC_setVendor)
|
||||||
Q_PROPERTY(QString DC_product READ DC_product WRITE DC_setProduct)
|
Q_PROPERTY(QString DC_product READ DC_product WRITE DC_setProduct)
|
||||||
Q_PROPERTY(QString DC_devName READ DC_devName WRITE DC_setDevName)
|
Q_PROPERTY(QString DC_devName READ DC_devName WRITE DC_setDevName)
|
||||||
|
@ -187,16 +198,6 @@ public slots:
|
||||||
void toggleDiveInvalid(int id);
|
void toggleDiveInvalid(int id);
|
||||||
void copyDiveData(int id);
|
void copyDiveData(int id);
|
||||||
void pasteDiveData(int id);
|
void pasteDiveData(int id);
|
||||||
bool toggleDiveSite(bool toggle);
|
|
||||||
bool toggleNotes(bool toggle);
|
|
||||||
bool toggleDiveGuide(bool toggle);
|
|
||||||
bool toggleBuddy(bool toggle);
|
|
||||||
bool toggleSuit(bool toggle);
|
|
||||||
bool toggleRating(bool toggle);
|
|
||||||
bool toggleVisibility(bool toggle);
|
|
||||||
bool toggleTags(bool toggle);
|
|
||||||
bool toggleCylinders(bool toggle);
|
|
||||||
bool toggleWeights(bool toggle);
|
|
||||||
void undo();
|
void undo();
|
||||||
void redo();
|
void redo();
|
||||||
int addDive();
|
int addDive();
|
||||||
|
@ -250,11 +251,21 @@ private:
|
||||||
void updateAllGlobalLists();
|
void updateAllGlobalLists();
|
||||||
void updateHaveLocalChanges(bool status);
|
void updateHaveLocalChanges(bool status);
|
||||||
|
|
||||||
|
bool m_pasteDiveSite;
|
||||||
|
bool m_pasteNotes;
|
||||||
|
bool m_pasteDiveGuide;
|
||||||
|
bool m_pasteBuddy;
|
||||||
|
bool m_pasteSuit;
|
||||||
|
bool m_pasteRating;
|
||||||
|
bool m_pasteVisibility;
|
||||||
|
bool m_pasteTags;
|
||||||
|
bool m_pasteCylinders;
|
||||||
|
bool m_pasteWeights;
|
||||||
|
|
||||||
location_t getGps(QString &gps);
|
location_t getGps(QString &gps);
|
||||||
QString m_pluggedInDeviceName;
|
QString m_pluggedInDeviceName;
|
||||||
bool m_showNonDiveComputers;
|
bool m_showNonDiveComputers;
|
||||||
struct dive *m_copyPasteDive = NULL;
|
struct dive_paste_data paste_data;
|
||||||
struct dive_components what;
|
|
||||||
QAction *undoAction;
|
QAction *undoAction;
|
||||||
|
|
||||||
bool verifyCredentials(QString email, QString password, QString pin);
|
bool verifyCredentials(QString email, QString password, QString pin);
|
||||||
|
|
Loading…
Add table
Reference in a new issue