summaryrefslogtreecommitdiffstats
path: root/desktop-widgets/command_edit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'desktop-widgets/command_edit.cpp')
-rw-r--r--desktop-widgets/command_edit.cpp125
1 files changed, 125 insertions, 0 deletions
diff --git a/desktop-widgets/command_edit.cpp b/desktop-widgets/command_edit.cpp
index c1e00bdf1..c3a47016e 100644
--- a/desktop-widgets/command_edit.cpp
+++ b/desktop-widgets/command_edit.cpp
@@ -325,4 +325,129 @@ DiveField EditMode::fieldId() const
return DiveField::MODE;
}
+// ***** Tag based commands *****
+EditTagsBase::EditTagsBase(const QVector<dive *> &divesIn, const QStringList &newListIn, struct dive *d):
+ dives(divesIn.toStdVector()),
+ newList(newListIn),
+ oldDive(d)
+{
+}
+
+// Two helper functions: returns true if first list contains any tag or
+// misses any tag of second list.
+static bool containsAny(const QStringList &tags1, const QStringList &tags2)
+{
+ return std::any_of(tags2.begin(), tags2.end(), [&tags1](const QString &tag)
+ { return tags1.contains(tag); });
+}
+
+static bool missesAny(const QStringList &tags1, const QStringList &tags2)
+{
+ return std::any_of(tags2.begin(), tags2.end(), [&tags1](const QString &tag)
+ { return !tags1.contains(tag); });
+}
+
+// This is quite hackish: we can't use virtual functions in the constructor and
+// therefore can't initialize the list of dives [the values of the dives are
+// accessed by virtual functions]. Therefore, we (mis)use the fact that workToBeDone()
+// is called exactly once before adding the Command to the system and perform this here.
+// To be more explicit about this, we might think about renaming workToBeDone() to init().
+bool EditTagsBase::workToBeDone()
+{
+ // changing the tags on multiple dives is semantically strange - what's the right thing to do?
+ // here's what I think... add the tags that were added to the displayed dive and remove the tags
+ // that were removed from it
+
+ // Calculate tags to add and tags to remove
+ QStringList oldList = data(oldDive);
+ for (const QString &s: newList) {
+ if (!oldList.contains(s))
+ tagsToAdd.push_back(s);
+ }
+ for (const QString &s: oldList) {
+ if (!newList.contains(s))
+ tagsToRemove.push_back(s);
+ }
+
+ // Now search for all dives that either
+ // - miss a tag to be added
+ // - have a tag to be removed
+ std::vector<dive *> divesNew;
+ divesNew.reserve(dives.size());
+ for (dive *d: dives) {
+ QStringList tags = data(d);
+ if (missesAny(tags, tagsToAdd) || containsAny(tags, tagsToRemove))
+ divesNew.push_back(d);
+ }
+ dives = std::move(divesNew);
+
+ // Create a text for the menu entry. In the case of multiple dives add the number
+ size_t num_dives = dives.size();
+ if (num_dives > 0)
+ //: remove the part in parantheses for %n = 1
+ setText(tr("Edit %1 (%n dive(s))", "", num_dives).arg(fieldName()));
+
+ return num_dives;
+}
+
+void EditTagsBase::undo()
+{
+ if (dives.empty()) {
+ qWarning("Edit command called with empty dives list (shouldn't happen)");
+ return;
+ }
+
+ for (dive *d: dives) {
+ QStringList tags = data(d);
+ for (const QString &tag: tagsToRemove)
+ tags.removeAll(tag);
+ for (const QString &tag: tagsToAdd) {
+ if (!tags.contains(tag))
+ tags.push_back(tag);
+ }
+ invalidate_dive_cache(d); // Ensure that dive is written in git_save()
+ set(d, tags);
+ }
+
+ std::swap(tagsToAdd, tagsToRemove);
+
+ emit diveListNotifier.divesEdited(QVector<dive *>::fromStdVector(dives), fieldId());
+
+ mark_divelist_changed(true);
+}
+
+// Undo and redo do the same as just the stored value is exchanged
+void EditTagsBase::redo()
+{
+ undo();
+}
+
+// ***** Tags *****
+QStringList EditTags::data(struct dive *d) const
+{
+ QStringList res;
+ for (const struct tag_entry *tag = d->tag_list; tag; tag = tag->next)
+ res.push_back(tag->tag->name);
+ return res;
+}
+
+void EditTags::set(struct dive *d, const QStringList &v) const
+{
+ taglist_free(d->tag_list);
+ d->tag_list = NULL;
+ for (const QString &tag: v)
+ taglist_add_tag(&d->tag_list, qPrintable(tag));
+ taglist_cleanup(&d->tag_list);
+}
+
+QString EditTags::fieldName() const
+{
+ return tr("tags");
+}
+
+DiveField EditTags::fieldId() const
+{
+ return DiveField::TAGS;
+}
+
} // namespace Command