Replaced the tag implementation

The new implementation supports custom tags
which are provided by the user as well as
default tags which are provided by subsurface.
Default tags can be translated and will be written
to XML in their non-localized form.

Signed-off-by: Maximilian Güntner <maximilian.guentner@gmail.com>
This commit is contained in:
Maximilian Güntner 2013-11-02 02:12:42 +01:00
parent 2ef80930ff
commit 6fe8cb6521
7 changed files with 265 additions and 66 deletions

160
dive.c
View file

@ -6,6 +6,8 @@
#include "gettext.h"
#include "dive.h"
struct tag_entry *g_tag_list = NULL;
void add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name)
{
struct event *ev, **p;
@ -189,6 +191,7 @@ struct dive *alloc_dive(void)
if (!dive)
exit(1);
memset(dive, 0, sizeof(*dive));
taglist_init(&(dive->tag_list));
return dive;
}
@ -1813,6 +1816,161 @@ static void join_dive_computers(struct divecomputer *res, struct divecomputer *a
remove_redundant_dc(res, prefer_downloaded);
}
int taglist_get_tagstring(struct tag_entry *tag_list, char *buffer, int len) {
int i = 0;
struct tag_entry *tmp;
tmp = tag_list->next;
memset(buffer, 0, len);
while(tmp != NULL) {
int newlength = strlen(tmp->tag->name);
if (i > 0)
newlength += 2;
if ((i+newlength) < len) {
if (i > 0) {
strcpy(buffer+i, ", ");
strcpy(buffer+i+2, tmp->tag->name);
} else {
strcpy(buffer, tmp->tag->name);
}
} else {
return i;
}
i += newlength;
tmp = tmp->next;
}
return i;
}
struct divetag *taglist_get_tag(struct tag_entry *tag_list, const char *tag)
{
struct tag_entry *tmp;
tmp = tag_list->next;
while(tmp != NULL) {
if (tmp->tag != NULL)
if (strcmp(tmp->tag->name, tag) == 0)
return tmp->tag;
else
tmp = tmp->next;
}
return NULL;
}
static inline void taglist_free_divetag(struct divetag *tag) {
if (tag->name != NULL)
free(tag->name);
if (tag->source != NULL)
free(tag->source);
free(tag);
}
/* Add a tag to the tag_list, keep the list sorted */
static struct divetag *taglist_add_divetag(struct tag_entry *tag_list, struct divetag *tag)
{
struct tag_entry *tmp, *last;
last = tag_list;
tmp = tag_list->next;
while(1) {
if (tmp == NULL || strcmp(tmp->tag->name, tag->name) > 0) {
/* Insert in front of it */
last->next = malloc(sizeof(struct tag_entry));
last->next->next = tmp;
last->next->tag = tag;
return last->next->tag;
} else if (strcmp(tmp->tag->name, tag->name) == 0) {
/* Already in list */
return tmp->tag;
} else {
last = tmp;
tmp = tmp->next;
}
}
}
struct divetag *taglist_add_tag(struct tag_entry *tag_list, const char *tag)
{
struct divetag *ret_tag, *new_tag;
const char *translation;
new_tag = malloc(sizeof(struct divetag));
translation = translate("gettextFromC", tag);
if (strcmp(tag, translation) == 0) {
new_tag->source = NULL;
new_tag->name = malloc(strlen(tag)+1);
memcpy(new_tag->name, tag, strlen(tag)+1);
} else {
new_tag->name = malloc(strlen(translation)+1);
memcpy(new_tag->name, translation, strlen(translation)+1);
new_tag->source = malloc(strlen(tag)+1);
memcpy(new_tag->source, tag, strlen(tag)+1);
}
/* Try to insert new_tag into g_tag_list if we are not operating on it */
if (tag_list != g_tag_list) {
ret_tag = taglist_add_divetag(g_tag_list, new_tag);
/* g_tag_list already contains new_tag, free the duplicate */
if (ret_tag != new_tag)
taglist_free_divetag(new_tag);
ret_tag = taglist_add_divetag(tag_list, ret_tag);
} else {
ret_tag = taglist_add_divetag(tag_list, new_tag);
if (ret_tag != new_tag)
taglist_free_divetag(new_tag);
}
return ret_tag;
}
void taglist_init(struct tag_entry **tag_list) {
*tag_list = malloc(sizeof(struct tag_entry));
(*tag_list)->next = NULL;
(*tag_list)->tag = NULL;
}
/* Clear everything but the first element */
void taglist_clear(struct tag_entry *tag_list) {
struct tag_entry *current_tag_entry, *next;
current_tag_entry = tag_list->next;
while (current_tag_entry != NULL) {
next = current_tag_entry->next;
free(current_tag_entry);
current_tag_entry = next;
}
tag_list->next = NULL;
}
/* Merge src1 and src2, write to *dst */
static void taglist_merge(struct tag_entry *dst, struct tag_entry *src1, struct tag_entry *src2)
{
struct tag_entry *current_tag_entry;
current_tag_entry = src1->next;
while (current_tag_entry != NULL) {
taglist_add_divetag(dst, current_tag_entry->tag);
current_tag_entry = current_tag_entry->next;
}
current_tag_entry = src2->next;
while (current_tag_entry != NULL) {
taglist_add_divetag(dst, current_tag_entry->tag);
current_tag_entry = current_tag_entry->next;
}
}
void taglist_init_global()
{
int i;
const char* default_tags[] = {
QT_TRANSLATE_NOOP("gettextFromC", "boat"), QT_TRANSLATE_NOOP("gettextFromC", "shore"), QT_TRANSLATE_NOOP("gettextFromC", "drift"),
QT_TRANSLATE_NOOP("gettextFromC", "deep"), QT_TRANSLATE_NOOP("gettextFromC", "cavern") , QT_TRANSLATE_NOOP("gettextFromC", "ice"),
QT_TRANSLATE_NOOP("gettextFromC", "wreck"), QT_TRANSLATE_NOOP("gettextFromC", "cave"), QT_TRANSLATE_NOOP("gettextFromC", "altitude"),
QT_TRANSLATE_NOOP("gettextFromC", "pool"), QT_TRANSLATE_NOOP("gettextFromC", "lake"), QT_TRANSLATE_NOOP("gettextFromC", "river"),
QT_TRANSLATE_NOOP("gettextFromC", "night"), QT_TRANSLATE_NOOP("gettextFromC", "fresh"), QT_TRANSLATE_NOOP("gettextFromC", "student"),
QT_TRANSLATE_NOOP("gettextFromC", "instructor"), QT_TRANSLATE_NOOP("gettextFromC", "photo"), QT_TRANSLATE_NOOP("gettextFromC", "video"),
QT_TRANSLATE_NOOP("gettextFromC", "deco")
};
taglist_init(&g_tag_list);
for(i=0; i<sizeof(default_tags)/sizeof(char*); i++)
taglist_add_tag(g_tag_list, default_tags[i]);
}
struct dive *merge_dives(struct dive *a, struct dive *b, int offset, bool prefer_downloaded)
{
struct dive *res = alloc_dive();
@ -1841,7 +1999,7 @@ struct dive *merge_dives(struct dive *a, struct dive *b, int offset, bool prefer
MERGE_MAX(res, a, b, number);
MERGE_NONZERO(res, a, b, cns);
MERGE_NONZERO(res, a, b, visibility);
MERGE_NONZERO(res, a, b, dive_tags);
taglist_merge(res->tag_list, a->tag_list, b->tag_list);
merge_equipment(res, a, b);
merge_airtemps(res, a, b);
if (dl) {

69
dive.h
View file

@ -59,34 +59,6 @@ typedef int bool;
#define SEAWATER_SALINITY 10300
#define FRESHWATER_SALINITY 10000
/* Dive tag definitions */
#define DTAG_INVALID (1 << 0)
#define DTAG_BOAT (1 << 1)
#define DTAG_SHORE (1 << 2)
#define DTAG_DRIFT (1 << 3)
#define DTAG_DEEP (1 << 4)
#define DTAG_CAVERN (1 << 5)
#define DTAG_ICE (1 << 6)
#define DTAG_WRECK (1 << 7)
#define DTAG_CAVE (1 << 8)
#define DTAG_ALTITUDE (1 << 9)
#define DTAG_POOL (1 << 10)
#define DTAG_LAKE (1 << 11)
#define DTAG_RIVER (1 << 12)
#define DTAG_NIGHT (1 << 13)
#define DTAG_FRESH (1 << 14)
#define DTAG_FRESH_NR 14
#define DTAG_STUDENT (1 << 15)
#define DTAG_INSTRUCTOR (1 << 16)
#define DTAG_PHOTO (1 << 17)
#define DTAG_VIDEO (1 << 18)
#define DTAG_DECO (1 << 19)
#define DTAG_NR 20
/* defined in statistics.c */
extern char *dtag_names[DTAG_NR];
extern int dtag_shown[DTAG_NR];
extern int dive_mask;
/*
* Some silly typedefs to make our units very explicit.
*
@ -309,6 +281,45 @@ struct sample {
int po2;
};
struct divetag {
/*
* The name of the divetag. If a translation is available, name contains
* the translated tag
*/
char *name;
/*
* If a translation is available, we write the original tag to source.
* This enables us to write a non-localized tag to the xml file.
*/
char *source;
};
struct tag_entry {
struct divetag *tag;
struct tag_entry *next;
};
/*
* divetags are only stored once, each dive only contains
* a list of tag_entries which then point to the divetags
* in the global g_tag_list
*/
extern struct tag_entry *g_tag_list;
struct divetag *taglist_add_tag(struct tag_entry *tag_list, const char *tag);
/*
* Writes all divetags in tag_list to buffer, limited by the buffer's (len)gth.
* Returns the characters written
*/
int taglist_get_tagstring(struct tag_entry *tag_list, char *buffer, int len);
void taglist_init(struct tag_entry **tag_list);
void taglist_clear(struct tag_entry *tag_list);
void taglist_init_global();
/*
* Events are currently pretty meaningless. This is
* just based on the random data that libdivecomputer
@ -399,7 +410,7 @@ struct dive {
pressure_t surface_pressure;
duration_t duration;
int salinity; // kg per 10000 l
int dive_tags;
struct tag_entry *tag_list;
struct divecomputer dc;
};

View file

@ -735,6 +735,11 @@ void delete_single_dive(int idx)
free((void *)dive->buddy);
if (dive->suit)
free((void *)dive->suit);
if (dive->tag_list) {
taglist_clear(dive->tag_list);
/* Remove head of list */
free((void *)dive->tag_list);
}
free(dive);
}
@ -800,8 +805,6 @@ void select_dive(int idx)
struct dive *dive = get_dive(idx);
if (dive) {
/* never select an invalid dive that isn't displayed */
if (dive->dive_tags & DTAG_INVALID && !prefs.display_invalid_dives)
return;
if (!dive->selected) {
dive->selected = 1;
amount_selected++;

View file

@ -24,6 +24,7 @@ int main(int argc, char **argv)
init_ui(&argc, &argv);
parse_xml_init();
taglist_init_global();
QStringList files;
QStringList importedFiles;

View file

@ -210,26 +210,53 @@ static void divedatetime(char *buffer, void *_when)
}
}
enum ParseState {FINDSTART, FINDEND};
static void divetags(char *buffer, void *_tags)
{
int *tags = _tags;
int i;
for (i = 0; i < DTAG_NR; i++) {
if (strstr(buffer, dtag_names[i])) {
/* stupidly we have 'cave' and 'cavern' */
if (1 << i == DTAG_CAVE) {
char *cave = strstr(buffer, "cave");
while (cave && !strncmp(cave, "cavern", strlen("cavern"))) {
cave++;
cave = strstr(cave, "cave");
struct tag_entry *tags = _tags;
char tag[128];
int i = 0, start = 0, end = 0;
enum ParseState state = FINDEND;
i=0;
while(i < strlen(buffer)) {
if (buffer[i] == ',') {
if (state == FINDSTART) {
/* Detect empty tags */
} else if (state == FINDEND) {
/* Found end of tag */
if (i > 1) {
if(buffer[i-1] != '\\') {
strncpy(tag, buffer+start, end-start+1);
tag[end-start+1] = '\0';
state=FINDSTART;
taglist_add_tag(tags, tag);
}
} else {
state=FINDSTART;
}
if (!cave)
continue;
}
*tags |= (1 << i);
} else if (buffer[i] == ' ') {
/* Handled */
} else {
/* Found start of tag */
if (state == FINDSTART) {
state = FINDEND;
start = i;
} else if (state == FINDEND) {
end=i;
}
}
}
i++;
}
if (state == FINDEND) {
if (end < start)
end = strlen(buffer)-1;
if (strlen(buffer) > 0) {
strncpy(tag, buffer+start, end-start+1);
tag[end-start+1] = '\0';
taglist_add_tag(tags, tag);
}
}
}
enum number_type {
@ -1161,7 +1188,7 @@ static void try_to_fill_dive(struct dive *dive, const char *name, char *buf)
if (MATCH(".number", get_index, &dive->number))
return;
if (MATCH(".tags", divetags, &dive->dive_tags))
if (MATCH(".tags", divetags, dive->tag_list))
return;
if (MATCH(".tripflag", get_tripflag, &dive->tripflag))
return;

View file

@ -392,20 +392,28 @@ static void save_events(FILE *f, struct event *ev)
}
}
static void save_tags(FILE *f, int tags)
static void save_tags(FILE *f, struct tag_entry *tag_list)
{
int i, more = 0;
int more = 0;
struct tag_entry *tmp = tag_list->next;
fprintf(f, " tags='");
for (i = 0; i < DTAG_NR; i++) {
if (tags & (1 << i)) {
/* Only write tag attribute if the list contains at least one item */
if (tmp != NULL) {
fprintf(f, " tags='");
while (tmp != NULL) {
if (more)
fprintf(f, ", ");
fprintf(f, "%s", dtag_names[i]);
/* If the tag has been translated, write the source to the xml file */
if (tmp->tag->source != NULL)
fprintf(f, "%s", tmp->tag->source);
else
fprintf(f, "%s", tmp->tag->name);
tmp = tmp->next;
more = 1;
}
fprintf(f, "'");
}
fprintf(f, "'");
}
static void show_date(FILE *f, timestamp_t when)
@ -468,8 +476,8 @@ void save_dive(FILE *f, struct dive *dive)
fprintf(f, " rating='%d'", dive->rating);
if (dive->visibility)
fprintf(f, " visibility='%d'", dive->visibility);
if (dive->dive_tags)
save_tags(f, dive->dive_tags);
if (dive->tag_list != NULL)
save_tags(f, dive->tag_list);
show_date(f, dive->when);
fprintf(f, " duration='%u:%02u min'>\n",

View file

@ -15,15 +15,6 @@
#include "divelist.h"
#include "statistics.h"
/* mark for translation but don't translate here as these terms are used
* in save-xml.c */
char *dtag_names[DTAG_NR] = {
QT_TRANSLATE_NOOP("gettextFromC","invalid"), QT_TRANSLATE_NOOP("gettextFromC","boat"), QT_TRANSLATE_NOOP("gettextFromC","shore"), QT_TRANSLATE_NOOP("gettextFromC","drift"), QT_TRANSLATE_NOOP("gettextFromC","deep"), QT_TRANSLATE_NOOP("gettextFromC","cavern"),
QT_TRANSLATE_NOOP("gettextFromC","ice"), QT_TRANSLATE_NOOP("gettextFromC","wreck"), QT_TRANSLATE_NOOP("gettextFromC","cave"), QT_TRANSLATE_NOOP("gettextFromC","altitude"), QT_TRANSLATE_NOOP("gettextFromC","pool"), QT_TRANSLATE_NOOP("gettextFromC","lake"),
QT_TRANSLATE_NOOP("gettextFromC","river"), QT_TRANSLATE_NOOP("gettextFromC","night"), QT_TRANSLATE_NOOP("gettextFromC","freshwater"), QT_TRANSLATE_NOOP("gettextFromC","training"), QT_TRANSLATE_NOOP("gettextFromC","teaching"),
QT_TRANSLATE_NOOP("gettextFromC","photo"), QT_TRANSLATE_NOOP("gettextFromC","video"), QT_TRANSLATE_NOOP("gettextFromC","deco")
};
static stats_t stats;
stats_t stats_selection;
stats_t *stats_monthly = NULL;