mirror of
https://github.com/subsurface/subsurface.git
synced 2024-11-30 22:20:21 +00:00
Add the ability to set a nickname for a dive computer
We maintain a list of dive computers that we know about (by deviceid) and their nicknames in our config. If the user downloads dive from a dive computer that we haven't seen before, we give them the option to set a nickname for that dive computer. That nickname is displayed in the profile (and stored in the XML file, assuming it is not the same as the model). This implementation attempts to make sure that it correctly deals with utf8 nicknames. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
parent
aba65736eb
commit
713a4fcff6
6 changed files with 158 additions and 2 deletions
4
dive.h
4
dive.h
|
@ -266,6 +266,7 @@ struct event {
|
||||||
struct divecomputer {
|
struct divecomputer {
|
||||||
timestamp_t when;
|
timestamp_t when;
|
||||||
const char *model;
|
const char *model;
|
||||||
|
const char *nickname;
|
||||||
uint32_t deviceid, diveid;
|
uint32_t deviceid, diveid;
|
||||||
int samples, alloc_samples;
|
int samples, alloc_samples;
|
||||||
struct sample *sample;
|
struct sample *sample;
|
||||||
|
@ -524,6 +525,9 @@ extern int edit_multi_dive_info(struct dive *single_dive);
|
||||||
extern void dive_list_update_dives(void);
|
extern void dive_list_update_dives(void);
|
||||||
extern void flush_divelist(struct dive *dive);
|
extern void flush_divelist(struct dive *dive);
|
||||||
|
|
||||||
|
extern void set_dc_nickname(struct dive *dive);
|
||||||
|
extern void remember_dc(uint32_t deviceid, const char *nickname, gboolean change_conf);
|
||||||
|
|
||||||
#define DIVE_ERROR_PARSE 1
|
#define DIVE_ERROR_PARSE 1
|
||||||
|
|
||||||
const char *weekday(int wday);
|
const char *weekday(int wday);
|
||||||
|
|
137
gtk-gui.c
137
gtk-gui.c
|
@ -39,6 +39,14 @@ struct preferences prefs = {
|
||||||
FALSE
|
FALSE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct dcnicknamelist {
|
||||||
|
const char *nickname;
|
||||||
|
uint32_t deviceid;
|
||||||
|
struct dcnicknamelist *next;
|
||||||
|
};
|
||||||
|
static struct dcnicknamelist *nicknamelist;
|
||||||
|
char *nicknamestring;
|
||||||
|
|
||||||
static GtkWidget *dive_profile;
|
static GtkWidget *dive_profile;
|
||||||
static const char *default_dive_computer_vendor;
|
static const char *default_dive_computer_vendor;
|
||||||
static const char *default_dive_computer_product;
|
static const char *default_dive_computer_product;
|
||||||
|
@ -1178,6 +1186,33 @@ void init_ui(int *argcp, char ***argvp)
|
||||||
default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING);
|
default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING);
|
||||||
default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING);
|
default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING);
|
||||||
default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING);
|
default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING);
|
||||||
|
conf_value = subsurface_get_conf("dc_nicknames", PREF_STRING);
|
||||||
|
nicknamestring = strdup("");
|
||||||
|
if (conf_value) {
|
||||||
|
char *next_token, *nickname;
|
||||||
|
uint32_t deviceid;
|
||||||
|
int len;
|
||||||
|
next_token = strdup(conf_value);
|
||||||
|
len = strlen(next_token);
|
||||||
|
while ((next_token = g_utf8_strchr(next_token, len, '{')) != NULL) {
|
||||||
|
/* replace the '{' so we keep looking in case any test fails */
|
||||||
|
*next_token = '(';
|
||||||
|
/* the next 8 chars are the deviceid, after that we have the utf8 nickname */
|
||||||
|
if (sscanf(next_token, "(%8x,", &deviceid) > 0) {
|
||||||
|
char *namestart, *nameend;
|
||||||
|
namestart = g_utf8_strchr(next_token, len, ',');
|
||||||
|
nameend = g_utf8_strchr(next_token, len, '}');
|
||||||
|
if (!namestart || !nameend)
|
||||||
|
continue;
|
||||||
|
*nameend = '\0';
|
||||||
|
nickname = strdup(namestart + 1);
|
||||||
|
remember_dc(deviceid, nickname, FALSE);
|
||||||
|
next_token = nameend + 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
free((void *)conf_value);
|
||||||
|
free(next_token);
|
||||||
|
}
|
||||||
error_info_bar = NULL;
|
error_info_bar = NULL;
|
||||||
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||||
g_set_application_name ("subsurface");
|
g_set_application_name ("subsurface");
|
||||||
|
@ -1966,3 +2001,105 @@ void set_filename(const char *filename, gboolean force)
|
||||||
else
|
else
|
||||||
existing_filename = NULL;
|
existing_filename = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *get_dc_nickname(uint32_t deviceid)
|
||||||
|
{
|
||||||
|
struct dcnicknamelist *known = nicknamelist;
|
||||||
|
while (known) {
|
||||||
|
if (known->deviceid == deviceid)
|
||||||
|
return known->nickname;
|
||||||
|
known = known->next;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no curly braces or commas, please */
|
||||||
|
static char *cleanedup_nickname(const char *nickname, int len)
|
||||||
|
{
|
||||||
|
char *clean;
|
||||||
|
if (nickname) {
|
||||||
|
char *brace;
|
||||||
|
|
||||||
|
if (!g_utf8_validate(nickname, -1, NULL))
|
||||||
|
return strdup("");
|
||||||
|
brace = clean = strdup(nickname);
|
||||||
|
while (*brace) {
|
||||||
|
if (*brace == '{')
|
||||||
|
*brace = '(';
|
||||||
|
else if (*brace == '}')
|
||||||
|
*brace = ')';
|
||||||
|
else if (*brace == ',')
|
||||||
|
*brace = '.';
|
||||||
|
brace = g_utf8_next_char(brace);
|
||||||
|
if (*brace && g_utf8_next_char(brace) - clean >= len)
|
||||||
|
*brace = '\0';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clean = strdup("");
|
||||||
|
}
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remember_dc(uint32_t deviceid, const char *nickname, gboolean change_conf)
|
||||||
|
{
|
||||||
|
if (!get_dc_nickname(deviceid)) {
|
||||||
|
char buffer[80];
|
||||||
|
struct dcnicknamelist *nn_entry = malloc(sizeof(struct dcnicknamelist));
|
||||||
|
nn_entry->deviceid = deviceid;
|
||||||
|
/* make sure there are no curly braces or commas in the string and that
|
||||||
|
* it will fit in the buffer */
|
||||||
|
nn_entry->nickname = cleanedup_nickname(nickname, sizeof(buffer) - 12);
|
||||||
|
nn_entry->next = nicknamelist;
|
||||||
|
nicknamelist = nn_entry;
|
||||||
|
snprintf(buffer, 80, "{%08x,%s}", deviceid, nn_entry->nickname);
|
||||||
|
nicknamestring = realloc(nicknamestring, strlen(nicknamestring) + strlen(buffer) + 1);
|
||||||
|
strcat(nicknamestring, buffer);
|
||||||
|
if (change_conf)
|
||||||
|
subsurface_set_conf("dc_nicknames", PREF_STRING, nicknamestring);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_dc_nickname(struct dive *dive)
|
||||||
|
{
|
||||||
|
GtkWidget *dialog, *vbox, *entry, *frame, *label;
|
||||||
|
char nickname[68];
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
if (!dive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((name = get_dc_nickname(dive->dc.deviceid)) != NULL) {
|
||||||
|
dive->dc.nickname = strdup(name);
|
||||||
|
} else {
|
||||||
|
dialog = gtk_dialog_new_with_buttons(
|
||||||
|
_("Dive Computer Nickname"),
|
||||||
|
GTK_WINDOW(main_window),
|
||||||
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||||
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
||||||
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
||||||
|
NULL);
|
||||||
|
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
||||||
|
label = gtk_label_new(_("Subsurface can use a nickname for your dive computer.\n"
|
||||||
|
"The default is the model and device ID as shown below.\n"
|
||||||
|
"If you don't want to name this dive computer click "
|
||||||
|
"'Cancel' and Subsurface will simply display its model "
|
||||||
|
"as nickname."));
|
||||||
|
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3);
|
||||||
|
frame = gtk_frame_new(_("Nickname"));
|
||||||
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 3);
|
||||||
|
entry = gtk_entry_new();
|
||||||
|
gtk_container_add(GTK_CONTAINER(frame), entry);
|
||||||
|
gtk_entry_set_max_length(GTK_ENTRY(entry), 68);
|
||||||
|
snprintf(nickname, 69, "%s (%08x)", dive->dc.model, dive->dc.deviceid);
|
||||||
|
gtk_entry_set_text(GTK_ENTRY(entry), nickname);
|
||||||
|
gtk_widget_show_all(dialog);
|
||||||
|
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
||||||
|
if (strcmp(dive->dc.model, gtk_entry_get_text(GTK_ENTRY(entry))))
|
||||||
|
dive->dc.nickname = cleanedup_nickname(gtk_entry_get_text(GTK_ENTRY(entry)),
|
||||||
|
sizeof(nickname));
|
||||||
|
}
|
||||||
|
gtk_widget_destroy(dialog);
|
||||||
|
remember_dc(dive->dc.deviceid, dive->dc.nickname, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
10
main.c
10
main.c
|
@ -124,6 +124,16 @@ void report_dives(gboolean is_imported, gboolean prefer_imported)
|
||||||
int preexisting = dive_table.preexisting;
|
int preexisting = dive_table.preexisting;
|
||||||
struct dive *last;
|
struct dive *last;
|
||||||
|
|
||||||
|
/* set the nickname for the divecomputer for newly downloaded dives */
|
||||||
|
for (i = dive_table.preexisting; i < dive_table.nr; i++)
|
||||||
|
if (dive_table.dives[i]->downloaded) {
|
||||||
|
set_dc_nickname(dive_table.dives[i]);
|
||||||
|
} else {
|
||||||
|
struct divecomputer *dc = &dive_table.dives[i]->dc;
|
||||||
|
if (dc->nickname && *dc->nickname)
|
||||||
|
remember_dc(dc->deviceid, dc->nickname, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/* This does the right thing for -1: NULL */
|
/* This does the right thing for -1: NULL */
|
||||||
last = get_dive(preexisting-1);
|
last = get_dive(preexisting-1);
|
||||||
|
|
||||||
|
|
|
@ -625,6 +625,8 @@ static void try_to_fill_dc(struct divecomputer *dc, const char *name, char *buf)
|
||||||
return;
|
return;
|
||||||
if (MATCH(".model", utf8_string, &dc->model))
|
if (MATCH(".model", utf8_string, &dc->model))
|
||||||
return;
|
return;
|
||||||
|
if (MATCH(".nickname", utf8_string, &dc->nickname))
|
||||||
|
return;
|
||||||
if (MATCH(".deviceid", hex_value, &dc->deviceid))
|
if (MATCH(".deviceid", hex_value, &dc->deviceid))
|
||||||
return;
|
return;
|
||||||
if (MATCH(".diveid", hex_value, &dc->diveid))
|
if (MATCH(".diveid", hex_value, &dc->diveid))
|
||||||
|
|
|
@ -1857,9 +1857,10 @@ void plot(struct graphics_context *gc, struct dive *dive, scale_mode_t scale)
|
||||||
cairo_stroke(gc->cr);
|
cairo_stroke(gc->cr);
|
||||||
|
|
||||||
/* Put the dive computer name in the lower left corner */
|
/* Put the dive computer name in the lower left corner */
|
||||||
if (dc->model) {
|
if (dc->nickname || dc->model) {
|
||||||
static const text_render_options_t computer = {10, TIME_TEXT, LEFT, MIDDLE};
|
static const text_render_options_t computer = {10, TIME_TEXT, LEFT, MIDDLE};
|
||||||
plot_text(gc, &computer, 0, 1, "%s", dc->model);
|
plot_text(gc, &computer, 0, 1, "%s",
|
||||||
|
dc->nickname && *dc->nickname ? dc->nickname : dc->model);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PP_GRAPHS_ENABLED) {
|
if (PP_GRAPHS_ENABLED) {
|
||||||
|
|
|
@ -400,6 +400,8 @@ static void save_dc(FILE *f, struct dive *dive, struct divecomputer *dc)
|
||||||
fprintf(f, " <divecomputer");
|
fprintf(f, " <divecomputer");
|
||||||
if (dc->model)
|
if (dc->model)
|
||||||
show_utf8(f, dc->model, " model='", "'", 1);
|
show_utf8(f, dc->model, " model='", "'", 1);
|
||||||
|
if (dc->nickname && *dc->nickname)
|
||||||
|
show_utf8(f, dc->nickname, " nickname='", "'", 1);
|
||||||
if (dc->deviceid)
|
if (dc->deviceid)
|
||||||
fprintf(f, " deviceid='%08x'", dc->deviceid);
|
fprintf(f, " deviceid='%08x'", dc->deviceid);
|
||||||
if (dc->diveid)
|
if (dc->diveid)
|
||||||
|
|
Loading…
Reference in a new issue