mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	core: port tag-list to C++
Also adds a new test, which tests merging of two tag-lists. Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
		
							parent
							
								
									640ecb345b
								
							
						
					
					
						commit
						f18acf6fb9
					
				
					 25 changed files with 195 additions and 227 deletions
				
			
		| 
						 | 
				
			
			@ -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));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -265,26 +265,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
 | 
			
		||||
| 
						 | 
				
			
			@ -304,22 +304,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
 | 
			
		||||
| 
						 | 
				
			
			@ -380,28 +380,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;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -411,36 +411,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
 | 
			
		||||
| 
						 | 
				
			
			@ -450,7 +450,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -474,7 +474,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -630,7 +630,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));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -174,7 +174,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -211,7 +211,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)
 | 
			
		||||
| 
						 | 
				
			
			@ -253,7 +252,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)
 | 
			
		||||
| 
						 | 
				
			
			@ -2349,7 +2348,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<int[]>(std::max(size_t(1), a->cylinders.size()));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
#include "divecomputer.h"
 | 
			
		||||
#include "equipment.h"
 | 
			
		||||
#include "picture.h" // TODO: remove
 | 
			
		||||
#include "tag.h"
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
| 
						 | 
				
			
			@ -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<divecomputer> dcs; // Attn: pointers to divecomputers are not stable!
 | 
			
		||||
	int id = 0; // unique ID for this dive
 | 
			
		||||
	struct picture_table pictures = { };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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());
 | 
			
		||||
	return check(c, dive_tags);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -123,8 +123,8 @@ static std::vector<QString> 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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										125
									
								
								core/tag.cpp
									
										
									
									
									
								
							
							
						
						
									
										125
									
								
								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<divetag> &tag, const char *s)
 | 
			
		||||
				   [](const std::unique_ptr<divetag> &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<divetag>(s, source));
 | 
			
		||||
	}
 | 
			
		||||
	if (it == g_tag_list.end() || (*it)->name != s)
 | 
			
		||||
		it = g_tag_list.insert(it, std::make_unique<divetag>(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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										28
									
								
								core/tag.h
									
										
									
									
									
								
							
							
						
						
									
										28
									
								
								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<const divetag *>;
 | 
			
		||||
 | 
			
		||||
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<std::unique_ptr<divetag>> 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -118,7 +118,7 @@ void TabDiveNotes::divesChanged(const QVector<dive *> &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 *> &, 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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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") {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<std::string> &
 | 
			
		|||
       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()));
 | 
			
		||||
       }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1501,8 +1501,8 @@ struct DiveGuideVariable : public StatsVariableTemplate<StatsVariable::Type::Dis
 | 
			
		|||
struct TagBinner : public StringBinner<TagBinner, StringBin> {
 | 
			
		||||
	std::vector<QString> to_bin_values(const dive *d) const {
 | 
			
		||||
		std::vector<QString> 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<StatsVariable::Type::Discrete>
 | 
			
		|||
		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<const StatsBinner *> binners() const override {
 | 
			
		||||
		return { &tag_binner };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@ private slots:
 | 
			
		|||
	void testGetTagstringMultipleTags();
 | 
			
		||||
	void testGetTagstringWithAnEmptyTag();
 | 
			
		||||
	void testGetTagstringEmptyTagOnly();
 | 
			
		||||
	void testMergeTags();
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue