diff --git a/backend-shared/exportfuncs.cpp b/backend-shared/exportfuncs.cpp index 621be5274..9946d3197 100644 --- a/backend-shared/exportfuncs.cpp +++ b/backend-shared/exportfuncs.cpp @@ -187,7 +187,7 @@ void export_TeX(const char *filename, bool selected_only, bool plain, ExportCall dive->maxdepth.mm ? put_format(&buf, "\\def\\%smaximumdepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->maxdepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smaximumdepth{}\n", ssrf); dive->meandepth.mm ? put_format(&buf, "\\def\\%smeandepth{%.1f\\%sdepthunit}\n", ssrf, get_depth_units(dive->meandepth.mm, NULL, &unit), ssrf) : put_format(&buf, "\\def\\%smeandepth{}\n", ssrf); - std::string tags = taglist_get_tagstring(dive->tag_list); + std::string tags = taglist_get_tagstring(dive->tags); put_format(&buf, "\\def\\%stype{%s}\n", ssrf, tags.c_str()); put_format(&buf, "\\def\\%sviz{%s}\n", ssrf, qPrintable(viz)); put_format(&buf, "\\def\\%srating{%s}\n", ssrf, qPrintable(rating)); diff --git a/commands/command_edit.cpp b/commands/command_edit.cpp index 11fe0d9cb..f33cfaa59 100644 --- a/commands/command_edit.cpp +++ b/commands/command_edit.cpp @@ -564,18 +564,17 @@ void EditTagsBase::redo() 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(QString::fromStdString(tag->tag->name)); + for (const divetag *tag: d->tags) + res.push_back(QString::fromStdString(tag->name)); return res; } void EditTags::set(struct dive *d, const QStringList &v) const { - taglist_free(d->tag_list); - d->tag_list = NULL; + d->tags.clear(); for (const QString &tag: v) - taglist_add_tag(&d->tag_list, qPrintable(tag)); - taglist_cleanup(&d->tag_list); + taglist_add_tag(d->tags, tag.toStdString()); + taglist_cleanup(d->tags); } QString EditTags::fieldName() const @@ -627,8 +626,7 @@ static void swapCandQString(QString &q, char *&c) q = std::move(tmp); } -PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn), - tags(nullptr) +PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dIn) { if (what.notes) notes = data->notes; @@ -653,7 +651,7 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI if (what.divesite) divesite = data->dive_site; if (what.tags) - tags = taglist_copy(data->tag_list); + tags = data->tags; if (what.cylinders) { cylinders = data->cylinders; // Paste cylinders is "special": @@ -695,7 +693,6 @@ PasteState::PasteState(dive *dIn, const dive *data, dive_components what) : d(dI PasteState::~PasteState() { - taglist_free(tags); } void PasteState::swap(dive_components what) @@ -723,7 +720,7 @@ void PasteState::swap(dive_components what) if (what.divesite) std::swap(divesite, d->dive_site); if (what.tags) - std::swap(tags, d->tag_list); + std::swap(tags, d->tags); if (what.cylinders) std::swap(cylinders, d->cylinders); if (what.weights) @@ -1397,7 +1394,7 @@ EditDive::EditDive(dive *oldDiveIn, dive *newDiveIn, dive_site *createDs, dive_s changedFields |= DiveField::CHILL; if (!same_string(oldDive->suit, newDive->suit)) changedFields |= DiveField::SUIT; - if (taglist_get_tagstring(oldDive->tag_list) != taglist_get_tagstring(newDive->tag_list)) // This is cheating. Do we have a taglist comparison function? + if (taglist_get_tagstring(oldDive->tags) != taglist_get_tagstring(newDive->tags)) // This is cheating. Do we have a taglist comparison function? changedFields |= DiveField::TAGS; if (oldDive->dcs[0].divemode != newDive->dcs[0].divemode) changedFields |= DiveField::MODE; diff --git a/commands/command_edit.h b/commands/command_edit.h index 0b8535c05..15c9c128d 100644 --- a/commands/command_edit.h +++ b/commands/command_edit.h @@ -299,7 +299,7 @@ struct PasteState { int current; int surge; int chill; - tag_entry *tags; + tag_list tags; cylinder_table cylinders; weightsystem_table weightsystems; int number; diff --git a/core/datatrak.cpp b/core/datatrak.cpp index f28864ad4..6976bbac2 100644 --- a/core/datatrak.cpp +++ b/core/datatrak.cpp @@ -266,26 +266,26 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct * Weather, values table, 0 to 6 * Subsurface don't have this record but we can use tags */ - dt_dive->tag_list = NULL; + dt_dive->tags.clear(); read_bytes(1); switch (tmp_1byte) { case 1: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "clear"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "clear")); break; case 2: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "misty"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "misty")); break; case 3: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fog"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fog")); break; case 4: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "rain"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "rain")); break; case 5: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "storm"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "storm")); break; case 6: - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "snow"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "snow")); break; default: // unknown, do nothing @@ -305,22 +305,22 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_bytes(1); switch (tmp_1byte) { case 1: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "No suit")); + dt_dive->suit = strdup(translate("gettextFromC", "No suit")); break; case 2: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Shorty")); + dt_dive->suit = strdup(translate("gettextFromC", "Shorty")); break; case 3: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Combi")); + dt_dive->suit = strdup(translate("gettextFromC", "Combi")); break; case 4: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Wet suit")); + dt_dive->suit = strdup(translate("gettextFromC", "Wet suit")); break; case 5: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Semidry suit")); + dt_dive->suit = strdup(translate("gettextFromC", "Semidry suit")); break; case 6: - dt_dive->suit = strdup(QT_TRANSLATE_NOOP("gettextFromC", "Dry suit")); + dt_dive->suit = strdup(translate("gettextFromC", "Dry suit")); break; default: // unknown, do nothing @@ -381,28 +381,28 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(1); if (bit_set(tmp_1byte, 2)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "no stop"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "no stop")); if (bit_set(tmp_1byte, 3)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "deco"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "deco")); if (bit_set(tmp_1byte, 4)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "single ascent"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "single ascent")); if (bit_set(tmp_1byte, 5)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "multiple ascent"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "multiple ascent")); if (bit_set(tmp_1byte, 6)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "fresh water"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "fresh water")); if (bit_set(tmp_1byte, 7)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "salt water"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "salt water")); /* * Dive Type 2 - Bit table, use tags again */ read_bytes(1); if (bit_set(tmp_1byte, 0)) { - taglist_add_tag(&dt_dive->tag_list, strdup("nitrox")); + taglist_add_tag(dt_dive->tags, "nitrox"); is_nitrox = 1; } if (bit_set(tmp_1byte, 1)) { - taglist_add_tag(&dt_dive->tag_list, strdup("rebreather")); + taglist_add_tag(dt_dive->tags, "rebreather"); is_SCR = 1; dt_dive->dcs[0].divemode = PSCR; } @@ -412,36 +412,36 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct */ read_bytes(1); if (bit_set(tmp_1byte, 0)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "sight seeing"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "sight seeing")); if (bit_set(tmp_1byte, 1)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "club dive"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "club dive")); if (bit_set(tmp_1byte, 2)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instructor"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instructor")); if (bit_set(tmp_1byte, 3)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "instruction"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "instruction")); if (bit_set(tmp_1byte, 4)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "night"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "night")); if (bit_set(tmp_1byte, 5)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "cave"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "cave")); if (bit_set(tmp_1byte, 6)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "ice"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "ice")); if (bit_set(tmp_1byte, 7)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "search"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "search")); /* * Dive Activity 2 - Bit table, use tags again */ read_bytes(1); if (bit_set(tmp_1byte, 0)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "wreck"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "wreck")); if (bit_set(tmp_1byte, 1)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "river"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "river")); if (bit_set(tmp_1byte, 2)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "drift"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "drift")); if (bit_set(tmp_1byte, 3)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "photo"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "photo")); if (bit_set(tmp_1byte, 4)) - taglist_add_tag(&dt_dive->tag_list, strdup(QT_TRANSLATE_NOOP("gettextFromC", "other"))); + taglist_add_tag(dt_dive->tags, translate("gettextFromC", "other")); /* * Other activities - String 1st byte = long @@ -451,7 +451,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct if (tmp_1byte != 0) { read_string(tmp_string1); snprintf(buffer, sizeof(buffer), "%s: %s\n", - QT_TRANSLATE_NOOP("gettextFromC", "Other activities"), + translate("gettextFromC", "Other activities"), tmp_string1); tmp_notes_str = strdup(buffer); free(tmp_string1); @@ -475,7 +475,7 @@ static char *dt_dive_parser(unsigned char *runner, struct dive *dt_dive, struct read_string(tmp_string1); int len = snprintf(buffer, sizeof(buffer), "%s%s:\n%s", tmp_notes_str ? tmp_notes_str : "", - QT_TRANSLATE_NOOP("gettextFromC", "Datatrak/Wlog notes"), + translate("gettextFromC", "Datatrak/Wlog notes"), tmp_string1); dt_dive->notes = (char *)calloc((len +1), 1); memcpy(dt_dive->notes, buffer, len); @@ -631,7 +631,7 @@ static void wlog_compl_parser(std::string &wl_mem, struct dive *dt_dive, int dco */ tmp = (int) two_bytes_to_int(runner[pos_weight + 1], runner[pos_weight]); if (tmp != 0x7fff) { - weightsystem_t ws = { {tmp * 10}, QT_TRANSLATE_NOOP("gettextFromC", "unknown"), false }; + weightsystem_t ws = { {tmp * 10}, translate("gettextFromC", "unknown"), false }; dt_dive->weightsystems.push_back(std::move(ws)); } diff --git a/core/dive.cpp b/core/dive.cpp index fe23a48c9..59f437be1 100644 --- a/core/dive.cpp +++ b/core/dive.cpp @@ -175,7 +175,7 @@ static void free_dive_structures(struct dive *d) free(d->notes); free(d->suit); /* free tags, additional dive computers, and pictures */ - taglist_free(d->tag_list); + d->tags.clear(); d->cylinders.clear(); d->weightsystems.clear(); clear_picture_table(&d->pictures); @@ -212,7 +212,6 @@ void copy_dive(const struct dive *s, struct dive *d) d->notes = copy_string(s->notes); d->suit = copy_string(s->suit); copy_pictures(&s->pictures, &d->pictures); - d->tag_list = taglist_copy(s->tag_list); } static void copy_dive_onedc(const struct dive *s, const struct divecomputer &sdc, struct dive *d) @@ -254,7 +253,7 @@ void selective_copy_dive(const struct dive *s, struct dive *d, struct dive_compo s->dive_site->add_dive(d); } if (what.tags) - d->tag_list = taglist_copy(s->tag_list); + d->tags = s->tags; if (what.cylinders) copy_cylinder_types(s, d); if (what.weights) @@ -2350,7 +2349,7 @@ struct dive *merge_dives(const struct dive *a, const struct dive *b, int offset, MERGE_NONZERO(res, a, b, surge); MERGE_NONZERO(res, a, b, chill); copy_pictures(a->pictures.nr ? &a->pictures : &b->pictures, &res->pictures); - taglist_merge(&res->tag_list, a->tag_list, b->tag_list); + res->tags = taglist_merge(a->tags, b->tags); /* if we get dives without any gas / cylinder information in an import, make sure * that there is at leatst one entry in the cylinder map for that dive */ auto cylinders_map_a = std::make_unique(std::max(size_t(1), a->cylinders.size())); diff --git a/core/dive.h b/core/dive.h index bca189011..97439fee3 100644 --- a/core/dive.h +++ b/core/dive.h @@ -8,6 +8,7 @@ #include "divecomputer.h" #include "equipment.h" #include "picture.h" // TODO: remove +#include "tag.h" #include #include @@ -46,7 +47,7 @@ struct dive { int salinity = 0; // kg per 10000 l int user_salinity = 0; // water density reflecting a user-specified type - struct tag_entry *tag_list = nullptr; + tag_list tags; std::vector dcs; // Attn: pointers to divecomputers are not stable! int id = 0; // unique ID for this dive struct picture_table pictures = { }; diff --git a/core/filterconstraint.cpp b/core/filterconstraint.cpp index 76b917b1e..ed116d883 100644 --- a/core/filterconstraint.cpp +++ b/core/filterconstraint.cpp @@ -818,8 +818,8 @@ static bool check(const filter_constraint &c, const QStringList &list) static bool has_tags(const filter_constraint &c, const struct dive *d) { QStringList dive_tags; - for (const tag_entry *tag = d->tag_list; tag; tag = tag->next) - dive_tags.push_back(QString::fromStdString(tag->tag->name).trimmed()); + for (const divetag *tag: d->tags) + dive_tags.push_back(QString::fromStdString(tag->name).trimmed()); dive_tags.append(gettextFromC::tr(divemode_text_ui[d->dcs[0].divemode]).trimmed()); return check(c, dive_tags); } diff --git a/core/fulltext.cpp b/core/fulltext.cpp index 70f131628..39fdc763c 100644 --- a/core/fulltext.cpp +++ b/core/fulltext.cpp @@ -123,8 +123,8 @@ static std::vector getWords(const dive *d) tokenize(QString(d->diveguide), res); tokenize(QString(d->buddy), res); tokenize(QString(d->suit), res); - for (const tag_entry *tag = d->tag_list; tag; tag = tag->next) - tokenize(QString::fromStdString(tag->tag->name), res); + for (const divetag *tag: d->tags) + tokenize(QString::fromStdString(tag->name), res); for (auto &cyl: d->cylinders) tokenize(QString::fromStdString(cyl.type.description), res); for (auto &ws: d->weightsystems) diff --git a/core/import-suunto.cpp b/core/import-suunto.cpp index 4396c85dd..22d8956bf 100644 --- a/core/import-suunto.cpp +++ b/core/import-suunto.cpp @@ -155,7 +155,7 @@ static int dm4_tags(void *param, int, char **data, char **) struct parser_state *state = (struct parser_state *)param; if (data[0]) - taglist_add_tag(&state->cur_dive->tag_list, data[0]); + taglist_add_tag(state->cur_dive->tags, data[0]); return 0; } diff --git a/core/load-git.cpp b/core/load-git.cpp index 03672b671..0910adc8e 100644 --- a/core/load-git.cpp +++ b/core/load-git.cpp @@ -261,7 +261,7 @@ static void parse_dive_tags(char *, struct git_parser_state *state) { for (const std::string &tag: state->converted_strings) { if (!tag.empty()) - taglist_add_tag(&state->active_dive->tag_list, tag.c_str()); + taglist_add_tag(state->active_dive->tags, tag.c_str()); } } diff --git a/core/parse-xml.cpp b/core/parse-xml.cpp index accac4978..356c40d84 100644 --- a/core/parse-xml.cpp +++ b/core/parse-xml.cpp @@ -101,7 +101,7 @@ enum ParseState { FINDSTART, FINDEND }; -static void divetags(const char *buffer, struct tag_entry **tags) +static void divetags(const char *buffer, tag_list *tags) { int i = 0, start = 0, end = 0; enum ParseState state = FINDEND; @@ -116,7 +116,7 @@ static void divetags(const char *buffer, struct tag_entry **tags) if (i > 0 && buffer[i - 1] != '\\') { std::string s(buffer + start, i - start); state = FINDSTART; - taglist_add_tag(tags, s.c_str()); + taglist_add_tag(*tags, s.c_str()); } else { state = FINDSTART; } @@ -139,7 +139,7 @@ static void divetags(const char *buffer, struct tag_entry **tags) end = len - 1; if (len > 0) { std::string s(buffer + start, i - start); - taglist_add_tag(tags, buffer + start); + taglist_add_tag(*tags, buffer + start); } } } @@ -1254,7 +1254,7 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf, str return; if (MATCH("number", get_index, &dive->number)) return; - if (MATCH("tags", divetags, &dive->tag_list)) + if (MATCH("tags", divetags, &dive->tags)) return; if (MATCH("tripflag", get_notrip, &dive->notrip)) return; diff --git a/core/save-git.cpp b/core/save-git.cpp index 1e7000c2b..57c3d4294 100644 --- a/core/save-git.cpp +++ b/core/save-git.cpp @@ -104,17 +104,16 @@ static void save_overview(struct membuffer *b, struct dive *dive) show_utf8(b, "notes ", dive->notes, "\n"); } -static void save_tags(struct membuffer *b, struct tag_entry *tags) +static void save_tags(struct membuffer *b, const tag_list &tags) { const char *sep = " "; - if (!tags) + if (tags.empty()) return; put_string(b, "tags"); - while (tags) { - show_utf8(b, sep, tags->tag->source.empty() ? tags->tag->name.c_str() : tags->tag->source.c_str(), ""); + for (const divetag *tag: tags) { + show_utf8(b, sep, tag->source.empty() ? tag->name.c_str() : tag->source.c_str(), ""); sep = ", "; - tags = tags->next; } put_string(b, "\n"); } @@ -449,7 +448,7 @@ static void create_dive_buffer(struct dive *dive, struct membuffer *b) SAVE("airpressure", surface_pressure.mbar); cond_put_format(dive->notrip, b, "notrip\n"); cond_put_format(dive->invalid, b, "invalid\n"); - save_tags(b, dive->tag_list); + save_tags(b, dive->tags); if (dive->dive_site) put_format(b, "divesiteid %08x\n", dive->dive_site->uuid); if (verbose && dive->dive_site) diff --git a/core/save-html.cpp b/core/save-html.cpp index b92869584..8e07509df 100644 --- a/core/save-html.cpp +++ b/core/save-html.cpp @@ -311,18 +311,16 @@ void put_HTML_watertemp(struct membuffer *b, const struct dive *dive, const char static void put_HTML_tags(struct membuffer *b, const struct dive *dive, const char *pre, const char *post) { put_string(b, pre); - struct tag_entry *tag = dive->tag_list; - if (!tag) + if (dive->tags.empty()) put_string(b, "[\"--\""); const char *separator = "["; - while (tag) { + for (const divetag *tag: dive->tags) { put_format(b, "%s\"", separator); separator = ", "; - put_HTML_quoted(b, tag->tag->name.c_str()); + put_HTML_quoted(b, tag->name.c_str()); put_string(b, "\""); - tag = tag->next; } put_string(b, "]"); put_string(b, post); diff --git a/core/save-xml.cpp b/core/save-xml.cpp index 1586e0e69..811d57d35 100644 --- a/core/save-xml.cpp +++ b/core/save-xml.cpp @@ -367,17 +367,16 @@ static void save_events(struct membuffer *b, struct dive *dive, const struct div save_one_event(b, dive, ev); } -static void save_tags(struct membuffer *b, struct tag_entry *entry) +static void save_tags(struct membuffer *b, const tag_list &tags) { - if (entry) { + if (!tags.empty()) { const char *sep = " tags='"; - do { - const struct divetag *tag = entry->tag; + for (const divetag *tag: tags) { put_string(b, sep); /* If the tag has been translated, write the source to the xml file */ quote(b, tag->source.empty() ? tag->name.c_str() : tag->source.c_str(), 1); sep = ", "; - } while ((entry = entry->next) != NULL); + } put_string(b, "'"); } } @@ -509,7 +508,7 @@ void save_one_dive_to_mb(struct membuffer *b, struct dive *dive, bool anonymize) if (dive->maxcns) put_format(b, " cns='%d%%'", dive->maxcns); - save_tags(b, dive->tag_list); + save_tags(b, dive->tags); if (dive->dive_site) put_format(b, " divesiteid='%8x'", dive->dive_site->uuid); if (dive->user_salinity) diff --git a/core/tag.cpp b/core/tag.cpp index 50b588f74..134eda4a4 100644 --- a/core/tag.cpp +++ b/core/tag.cpp @@ -22,124 +22,87 @@ static const char *default_tags[] = { QT_TRANSLATE_NOOP("gettextFromC", "deco") }; -/* copy an element in a list of tags */ -static void copy_tl(struct tag_entry *st, struct tag_entry *dt) +divetag::divetag(std::string name, std::string source) : + name(std::move(name)), source(std::move(source)) { - dt->tag = st->tag; } -static bool tag_seen_before(struct tag_entry *start, struct tag_entry *before) +/* remove duplicates and empty tags */ +void taglist_cleanup(tag_list &list) { - while (start && start != before) { - if (start->tag->name == before->tag->name) - return true; - start = start->next; - } - return false; + // Remove empty tags + list.erase(std::remove_if(list.begin(), list.end(), [](const divetag *tag) { return tag->name.empty(); }), + list.end()); + + // Sort (should be a NOP, because we add in a sorted way, but let's make sure) + std::sort(list.begin(), list.end()); + + // Remove duplicates + list.erase(std::unique(list.begin(), list.end(), + [](const divetag *tag1, const divetag *tag2) { return tag1->name == tag2->name; }), + list.end()); } -/* remove duplicates and empty nodes */ -void taglist_cleanup(struct tag_entry **tag_list) +std::string taglist_get_tagstring(const tag_list &list) { - struct tag_entry **tl = tag_list; - while (*tl) { - /* skip tags that are empty or that we have seen before */ - if ((*tl)->tag->name.empty() || tag_seen_before(*tag_list, *tl)) { - *tl = (*tl)->next; - continue; - } - tl = &(*tl)->next; - } -} - -std::string taglist_get_tagstring(struct tag_entry *tag_list) -{ - bool first_tag = true; std::string res; - for (struct tag_entry *tmp = tag_list; tmp != NULL; tmp = tmp->next) { - if (tmp->tag->name.empty()) + for (const divetag *tag: list) { + if (tag->name.empty()) continue; - if (!first_tag) + if (!res.empty()) res += ", "; - res += tmp->tag->name; - first_tag = false; + res += tag->name; } return res; } /* Add a tag to the tag_list, keep the list sorted */ -static void taglist_add_divetag(struct tag_entry **tag_list, const struct divetag *tag) +static void taglist_add_divetag(tag_list &list, const struct divetag *tag) { - struct tag_entry *next, *entry; - - while ((next = *tag_list) != NULL) { - int cmp = next->tag->name.compare(tag->name); - - /* Already have it? */ - if (!cmp) - return; - /* Is the entry larger? If so, insert here */ - if (cmp > 0) - break; - /* Continue traversing the list */ - tag_list = &next->next; - } - - /* Insert in front of it */ - entry = (tag_entry *)malloc(sizeof(struct tag_entry)); - entry->next = next; - entry->tag = tag; - *tag_list = entry; + // Use binary search to enter at sorted position + auto it = std::lower_bound(list.begin(), list.end(), tag, + [](const struct divetag *tag1, const struct divetag *tag2) + { return tag1->name < tag2->name; }); + // Don't add if it already exists + if (it == list.end() || (*it)->name != tag->name) + list.insert(it, tag); } -static const divetag *register_tag(const char *s, const char *source) +static const divetag *register_tag(std::string s, std::string source) { // binary search auto it = std::lower_bound(g_tag_list.begin(), g_tag_list.end(), s, - [](const std::unique_ptr &tag, const char *s) + [](const std::unique_ptr &tag, const std::string &s) { return tag->name < s; }); - if (it == g_tag_list.end() || (*it)->name != s) { - std::string source_s = empty_string(source) ? std::string() : std::string(source); - it = g_tag_list.insert(it, std::make_unique(s, source)); - } + if (it == g_tag_list.end() || (*it)->name != s) + it = g_tag_list.insert(it, std::make_unique(std::move(s), std::move(source))); return it->get(); } -void taglist_add_tag(struct tag_entry **tag_list, const char *tag) +void taglist_add_tag(tag_list &list, const std::string &tag) { bool is_default_tag = std::find_if(std::begin(default_tags), std::end(default_tags), [&tag] (const char *default_tag) { return tag == default_tag; }); /* Only translate default tags */ /* TODO: Do we really want to translate user-supplied tags if they happen to be known!? */ - const char *translation = is_default_tag ? translate("gettextFromC", tag) : tag; - const char *source = is_default_tag ? tag : nullptr; - const struct divetag *d_tag = register_tag(translation, source); + std::string translation = is_default_tag ? translate("gettextFromC", tag.c_str()) : tag; + std::string source = is_default_tag ? tag : std::string(); + const struct divetag *d_tag = register_tag(std::move(translation), std::move(source)); - taglist_add_divetag(tag_list, d_tag); -} - -void taglist_free(struct tag_entry *entry) -{ - STRUCTURED_LIST_FREE(struct tag_entry, entry, free) -} - -struct tag_entry *taglist_copy(struct tag_entry *s) -{ - struct tag_entry *res; - STRUCTURED_LIST_COPY(struct tag_entry, s, res, copy_tl); - return res; + taglist_add_divetag(list, d_tag); } /* Merge src1 and src2, write to *dst */ -void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2) +tag_list taglist_merge(const tag_list &src1, const tag_list &src2) { - struct tag_entry *entry; + tag_list dst; - for (entry = src1; entry; entry = entry->next) - taglist_add_divetag(dst, entry->tag); - for (entry = src2; entry; entry = entry->next) - taglist_add_divetag(dst, entry->tag); + for (const divetag *t: src1) + taglist_add_divetag(dst, t); + for (const divetag *t: src2) + taglist_add_divetag(dst, t); + return dst; } void taglist_init_global() diff --git a/core/tag.h b/core/tag.h index a4c8fa689..ada4d2700 100644 --- a/core/tag.h +++ b/core/tag.h @@ -19,25 +19,18 @@ struct divetag { * This enables us to write a non-localized tag to the xml file. */ std::string source; - divetag(const char *n, const char *s) : name(n), source(s) - { - } + divetag(std::string name, std::string source); }; -struct tag_entry { - const struct divetag *tag; - struct tag_entry *next; -}; +using tag_list = std::vector; -void taglist_add_tag(struct tag_entry **tag_list, const char *tag); +void taglist_add_tag(tag_list &list, const std::string &tag); /* cleans up a list: removes empty tags and duplicates */ -void taglist_cleanup(struct tag_entry **tag_list); +void taglist_cleanup(tag_list &list); void taglist_init_global(); -void taglist_free(struct tag_entry *tag_list); -struct tag_entry *taglist_copy(struct tag_entry *s); -void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2); +tag_list taglist_merge(const tag_list &src1, const tag_list &src2); /* * divetags are only stored once, each dive only contains @@ -46,14 +39,7 @@ void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_en */ extern std::vector> g_tag_list; -/* - * Writes all divetags form tag_list into internally allocated buffer - * Function returns pointer to allocated buffer - * Buffer contains comma separated list of tags names or null terminated string - */ -extern std::string taglist_get_tagstring(struct tag_entry *tag_list); - -/* Comma separated list of tags names or null terminated string */ -std::string taglist_get_tagstring(struct tag_entry *tag_list); +/* Comma separated list of tags names or empty string */ +std::string taglist_get_tagstring(const tag_list &tags); #endif diff --git a/desktop-widgets/simplewidgets.cpp b/desktop-widgets/simplewidgets.cpp index 4e5c95f7a..581df22ec 100644 --- a/desktop-widgets/simplewidgets.cpp +++ b/desktop-widgets/simplewidgets.cpp @@ -341,11 +341,8 @@ void DiveComponentSelection::buttonClicked(QAbstractButton *button) text << tr("Suit: ") << current_dive->suit << "\n"; if (what-> tags) { text << tr("Tags: "); - tag_entry *entry = current_dive->tag_list; - while (entry) { - text << entry->tag->name.c_str() << " "; - entry = entry->next; - } + for (const divetag *tag: current_dive->tags) + text << tag->name.c_str() << " "; text << "\n"; } if (what->cylinders) { diff --git a/desktop-widgets/tab-widgets/TabDiveNotes.cpp b/desktop-widgets/tab-widgets/TabDiveNotes.cpp index ddd678694..61387ea1a 100644 --- a/desktop-widgets/tab-widgets/TabDiveNotes.cpp +++ b/desktop-widgets/tab-widgets/TabDiveNotes.cpp @@ -118,7 +118,7 @@ void TabDiveNotes::divesChanged(const QVector &dives, DiveField field) if (field.divesite) updateDiveSite(currentDive); if (field.tags) - ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tag_list))); + ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tags))); if (field.buddy) ui.buddy->setText(currentDive->buddy); if (field.diveguide) @@ -253,7 +253,7 @@ void TabDiveNotes::updateData(const std::vector &, dive *currentDive, in // reset labels in case we last displayed trip notes ui.LocationLabel->setText(tr("Location")); ui.NotesLabel->setText(tr("Notes")); - ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tag_list))); + ui.tagWidget->setText(QString::fromStdString(taglist_get_tagstring(currentDive->tags))); bool isManual = is_dc_manually_added_dive(¤tDive->dcs[0]); ui.depth->setVisible(isManual); ui.depthLabel->setVisible(isManual); diff --git a/desktop-widgets/templatelayout.cpp b/desktop-widgets/templatelayout.cpp index 3956db50e..59ab75548 100644 --- a/desktop-widgets/templatelayout.cpp +++ b/desktop-widgets/templatelayout.cpp @@ -552,7 +552,7 @@ QVariant TemplateLayout::getValue(QString list, QString property, const State &s } else if (property == "notes") { return formatNotes(d); } else if (property == "tags") { - return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + return QString::fromStdString(taglist_get_tagstring(d->tags)); } else if (property == "gas") { return formatGas(d); } else if (property == "sac") { diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index b0558968d..c52115f04 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -1324,7 +1324,7 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt } // normalize the tag list we have and the one we get from the UI // try hard to deal with accidental white space issues - QStringList existingTagList = QString::fromStdString(taglist_get_tagstring(d->tag_list)).split(",", SKIP_EMPTY); + QStringList existingTagList = QString::fromStdString(taglist_get_tagstring(d->tags)).split(",", SKIP_EMPTY); QStringList newTagList = tags.split(",", SKIP_EMPTY); QStringList newCleanTagList; for (QString s: newTagList) { @@ -1335,10 +1335,9 @@ void QMLManager::commitChanges(QString diveId, QString number, QString date, QSt existingTagList.sort(); if (newCleanTagList.join(",") != existingTagList.join(",")) { diveChanged = true; - taglist_free(d->tag_list); - d->tag_list = nullptr; + d->tags.clear(); for (QString tag: newCleanTagList) - taglist_add_tag(&d->tag_list, qPrintable(tag)); + taglist_add_tag(d->tags, qPrintable(tag)); } if (d->rating != rating) { diveChanged = true; diff --git a/qt-models/divetripmodel.cpp b/qt-models/divetripmodel.cpp index 267218d71..8c14e1bc8 100644 --- a/qt-models/divetripmodel.cpp +++ b/qt-models/divetripmodel.cpp @@ -294,7 +294,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) case MobileListModel::SumWeightRole: return formatSumWeight(d); case MobileListModel::DiveGuideRole: return QString(d->diveguide); case MobileListModel::BuddyRole: return QString(d->buddy); - case MobileListModel::TagsRole: return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + case MobileListModel::TagsRole: return QString::fromStdString(taglist_get_tagstring(d->tags)); case MobileListModel::NotesRole: return formatNotes(d); case MobileListModel::GpsRole: return formatDiveGPS(d); case MobileListModel::GpsDecimalRole: return format_gps_decimal(d); @@ -347,7 +347,7 @@ QVariant DiveTripModelBase::diveData(const struct dive *d, int column, int role) else return d->maxcns; case TAGS: - return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + return QString::fromStdString(taglist_get_tagstring(d->tags)); case PHOTOS: break; case COUNTRY: @@ -1767,8 +1767,8 @@ bool DiveTripModelList::lessThan(const QModelIndex &i1, const QModelIndex &i2) c case MAXCNS: return lessThanHelper(d1->maxcns - d2->maxcns, row_diff); case TAGS: { - std::string s1 = taglist_get_tagstring(d1->tag_list); - std::string s2 = taglist_get_tagstring(d2->tag_list); + std::string s1 = taglist_get_tagstring(d1->tags); + std::string s2 = taglist_get_tagstring(d2->tags); int diff = strCmp(s1, s2); return lessThanHelper(diff, row_diff); } diff --git a/smtk-import/smartrak.cpp b/smtk-import/smartrak.cpp index a2342f19d..a0f966997 100644 --- a/smtk-import/smartrak.cpp +++ b/smtk-import/smartrak.cpp @@ -691,7 +691,7 @@ static void smtk_parse_relations(MdbHandle *mdb, struct dive *dive, char *dive_i if (str.empty()) continue; if (tag) - taglist_add_tag(&dive->tag_list, str.c_str()); + taglist_add_tag(dive->tags, str); else concat(tmp, ", ", str); if (str.find("SCR") != std::string::npos) @@ -717,7 +717,7 @@ static void smtk_parse_other(struct dive *dive, const std::vector & const std::string &str = list[i]; if (!str.empty()) { if (tag) - taglist_add_tag(&dive->tag_list, str.c_str()); + taglist_add_tag(dive->tags, str); else concat(&dive->notes, "\n", format_string_std("Smartrak %s: %s", data_name, str.c_str())); } diff --git a/stats/statsvariables.cpp b/stats/statsvariables.cpp index 5a752e10e..01a2995fe 100644 --- a/stats/statsvariables.cpp +++ b/stats/statsvariables.cpp @@ -1501,8 +1501,8 @@ struct DiveGuideVariable : public StatsVariableTemplate { std::vector to_bin_values(const dive *d) const { std::vector tags; - for (const tag_entry *tag = d->tag_list; tag; tag = tag->next) - tags.push_back(QString::fromStdString(tag->tag->name).trimmed()); + for (const divetag *tag: d->tags) + tags.push_back(QString::fromStdString(tag->name).trimmed()); return tags; } }; @@ -1513,7 +1513,7 @@ struct TagVariable : public StatsVariableTemplate return StatsTranslations::tr("Tags"); } QString diveCategories(const dive *d) const override { - return QString::fromStdString(taglist_get_tagstring(d->tag_list)); + return QString::fromStdString(taglist_get_tagstring(d->tags)); } std::vector binners() const override { return { &tag_binner }; diff --git a/tests/testtaglist.cpp b/tests/testtaglist.cpp index dabcf351c..b2926853c 100644 --- a/tests/testtaglist.cpp +++ b/tests/testtaglist.cpp @@ -15,29 +15,29 @@ void TestTagList::cleanupTestCase() void TestTagList::testGetTagstringNoTags() { - struct tag_entry *tag_list = NULL; - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + std::string tagstring = taglist_get_tagstring(tags); QVERIFY(tagstring.empty()); } void TestTagList::testGetTagstringSingleTag() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, "A new tag"); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, "A new tag"); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8("A new tag")); } void TestTagList::testGetTagstringMultipleTags() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, "A new tag"); - taglist_add_tag(&tag_list, "A new tag 1"); - taglist_add_tag(&tag_list, "A new tag 2"); - taglist_add_tag(&tag_list, "A new tag 3"); - taglist_add_tag(&tag_list, "A new tag 4"); - taglist_add_tag(&tag_list, "A new tag 5"); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, "A new tag"); + taglist_add_tag(tags, "A new tag 1"); + taglist_add_tag(tags, "A new tag 2"); + taglist_add_tag(tags, "A new tag 3"); + taglist_add_tag(tags, "A new tag 4"); + taglist_add_tag(tags, "A new tag 5"); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8( "A new tag, " @@ -50,11 +50,11 @@ void TestTagList::testGetTagstringMultipleTags() void TestTagList::testGetTagstringWithAnEmptyTag() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, "A new tag"); - taglist_add_tag(&tag_list, "A new tag 1"); - taglist_add_tag(&tag_list, ""); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, "A new tag"); + taglist_add_tag(tags, "A new tag 1"); + taglist_add_tag(tags, ""); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8( "A new tag, " @@ -63,11 +63,40 @@ void TestTagList::testGetTagstringWithAnEmptyTag() void TestTagList::testGetTagstringEmptyTagOnly() { - struct tag_entry *tag_list = NULL; - taglist_add_tag(&tag_list, ""); - std::string tagstring = taglist_get_tagstring(tag_list); + tag_list tags; + taglist_add_tag(tags, ""); + std::string tagstring = taglist_get_tagstring(tags); QCOMPARE(QString::fromStdString(tagstring), QString::fromUtf8("")); } +void TestTagList::testMergeTags() +{ + tag_list tags1, tags2; + taglist_add_tag(tags1, "A new tag"); + taglist_add_tag(tags1, "A new tag 6"); + taglist_add_tag(tags1, "A new tag 1"); + taglist_add_tag(tags1, "A new tag 2"); + taglist_add_tag(tags1, ""); + taglist_add_tag(tags1, "A new tag 2"); + taglist_add_tag(tags1, "A new tag 3"); + taglist_add_tag(tags1, "A new tag"); + taglist_add_tag(tags2, ""); + taglist_add_tag(tags2, "A new tag 1"); + taglist_add_tag(tags2, "A new tag 4"); + taglist_add_tag(tags2, "A new tag 2"); + taglist_add_tag(tags2, "A new tag 5"); + tag_list tags3 = taglist_merge(tags1, tags2); + std::string tagstring = taglist_get_tagstring(tags3); + QCOMPARE(QString::fromStdString(tagstring), + QString::fromUtf8( + "A new tag, " + "A new tag 1, " + "A new tag 2, " + "A new tag 3, " + "A new tag 4, " + "A new tag 5, " + "A new tag 6")); +} + QTEST_GUILESS_MAIN(TestTagList) diff --git a/tests/testtaglist.h b/tests/testtaglist.h index 7e6e94742..4e6ea2f20 100644 --- a/tests/testtaglist.h +++ b/tests/testtaglist.h @@ -15,6 +15,7 @@ private slots: void testGetTagstringMultipleTags(); void testGetTagstringWithAnEmptyTag(); void testGetTagstringEmptyTagOnly(); + void testMergeTags(); }; #endif