2011-09-20 19:40:34 +00:00
|
|
|
/* gtk-gui.c */
|
|
|
|
/* gtk UI implementation */
|
|
|
|
/* creates the window and overall layout
|
|
|
|
* divelist, dive info, equipment and printing are handled in their own source files
|
|
|
|
*/
|
2012-10-11 00:42:59 +00:00
|
|
|
#include <libintl.h>
|
|
|
|
#include <glib/gi18n.h>
|
2011-09-20 19:40:34 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
2012-01-03 05:53:33 +00:00
|
|
|
#include <unistd.h>
|
2012-09-09 16:06:44 +00:00
|
|
|
#include <sys/stat.h>
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
#include "dive.h"
|
|
|
|
#include "divelist.h"
|
|
|
|
#include "display.h"
|
|
|
|
#include "display-gtk.h"
|
2012-09-25 14:28:47 +00:00
|
|
|
#include "uemis.h"
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
#include "libdivecomputer.h"
|
|
|
|
|
2011-09-27 23:23:59 +00:00
|
|
|
GtkWidget *main_window;
|
2011-09-20 19:40:34 +00:00
|
|
|
GtkWidget *main_vbox;
|
|
|
|
GtkWidget *error_info_bar;
|
|
|
|
GtkWidget *error_label;
|
2011-12-06 21:00:01 +00:00
|
|
|
GtkWidget *vpane, *hpane;
|
2012-08-19 01:06:32 +00:00
|
|
|
GtkWidget *notebook;
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
int error_count;
|
2012-09-16 03:51:06 +00:00
|
|
|
const char *existing_filename;
|
2011-09-20 19:40:34 +00:00
|
|
|
const char *divelist_font;
|
2012-09-09 16:06:44 +00:00
|
|
|
const char *default_filename;
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-12-10 17:20:57 +00:00
|
|
|
struct preferences prefs = {
|
|
|
|
SI_UNITS,
|
|
|
|
{ TRUE, FALSE, },
|
|
|
|
{ FALSE, FALSE, FALSE, 1.6, 4.0, 13.0},
|
|
|
|
FALSE
|
|
|
|
};
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-12-13 06:26:29 +00:00
|
|
|
struct dcnicknamelist {
|
|
|
|
const char *nickname;
|
2012-12-22 05:00:06 +00:00
|
|
|
const char *model;
|
2012-12-13 06:26:29 +00:00
|
|
|
uint32_t deviceid;
|
|
|
|
struct dcnicknamelist *next;
|
2012-12-31 04:27:01 +00:00
|
|
|
gboolean saved;
|
2012-12-13 06:26:29 +00:00
|
|
|
};
|
|
|
|
static struct dcnicknamelist *nicknamelist;
|
|
|
|
char *nicknamestring;
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
static GtkWidget *dive_profile;
|
2012-06-22 20:37:39 +00:00
|
|
|
static const char *default_dive_computer_vendor;
|
|
|
|
static const char *default_dive_computer_product;
|
2012-05-30 04:54:09 +00:00
|
|
|
static const char *default_dive_computer_device;
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
static gboolean force_download;
|
|
|
|
static gboolean prefer_downloaded;
|
2012-05-02 17:26:34 +00:00
|
|
|
|
2013-01-02 01:29:38 +00:00
|
|
|
GtkActionGroup *action_group;
|
|
|
|
|
2012-12-10 17:20:57 +00:00
|
|
|
struct units *get_output_units()
|
|
|
|
{
|
|
|
|
return &prefs.output_units;
|
|
|
|
}
|
|
|
|
|
2012-06-22 20:37:39 +00:00
|
|
|
static int is_default_dive_computer(const char *vendor, const char *product)
|
2012-05-02 17:26:34 +00:00
|
|
|
{
|
2012-06-22 20:37:39 +00:00
|
|
|
return default_dive_computer_vendor && !strcmp(vendor, default_dive_computer_vendor) &&
|
|
|
|
default_dive_computer_product && !strcmp(product, default_dive_computer_product);
|
2012-05-02 17:26:34 +00:00
|
|
|
}
|
|
|
|
|
2012-10-26 22:52:39 +00:00
|
|
|
int is_default_dive_computer_device(const char *name)
|
2012-05-30 04:54:09 +00:00
|
|
|
{
|
|
|
|
return default_dive_computer_device && !strcmp(name, default_dive_computer_device);
|
|
|
|
}
|
|
|
|
|
2012-06-22 20:37:39 +00:00
|
|
|
static void set_default_dive_computer(const char *vendor, const char *product)
|
2012-05-02 17:26:34 +00:00
|
|
|
{
|
2012-06-22 20:37:39 +00:00
|
|
|
if (!vendor || !*vendor)
|
|
|
|
return;
|
|
|
|
if (!product || !*product)
|
2012-05-02 17:26:34 +00:00
|
|
|
return;
|
2012-06-22 20:37:39 +00:00
|
|
|
if (is_default_dive_computer(vendor, product))
|
2012-05-02 17:26:34 +00:00
|
|
|
return;
|
2012-10-04 20:52:09 +00:00
|
|
|
if (default_dive_computer_vendor)
|
|
|
|
free((void *)default_dive_computer_vendor);
|
|
|
|
if (default_dive_computer_product)
|
|
|
|
free((void *)default_dive_computer_product);
|
2012-12-12 15:23:04 +00:00
|
|
|
default_dive_computer_vendor = strdup(vendor);
|
|
|
|
default_dive_computer_product = strdup(product);
|
2012-06-22 20:37:39 +00:00
|
|
|
subsurface_set_conf("dive_computer_vendor", PREF_STRING, vendor);
|
|
|
|
subsurface_set_conf("dive_computer_product", PREF_STRING, product);
|
2012-05-02 17:26:34 +00:00
|
|
|
}
|
|
|
|
|
2012-05-30 04:54:09 +00:00
|
|
|
static void set_default_dive_computer_device(const char *name)
|
|
|
|
{
|
|
|
|
if (!name || !*name)
|
|
|
|
return;
|
|
|
|
if (is_default_dive_computer_device(name))
|
|
|
|
return;
|
2012-09-22 16:07:50 +00:00
|
|
|
if (default_dive_computer_device)
|
|
|
|
free((void *)default_dive_computer_device);
|
|
|
|
default_dive_computer_device = strdup(name);
|
2012-05-30 04:54:09 +00:00
|
|
|
subsurface_set_conf("dive_computer_device", PREF_STRING, name);
|
|
|
|
}
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
void repaint_dive(void)
|
|
|
|
{
|
|
|
|
update_dive(current_dive);
|
2011-09-27 17:38:07 +00:00
|
|
|
if (dive_profile)
|
|
|
|
gtk_widget_queue_draw(dive_profile);
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
2011-10-11 22:58:38 +00:00
|
|
|
static gboolean need_icon = TRUE;
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
static void on_info_bar_response(GtkWidget *widget, gint response,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
if (response == GTK_RESPONSE_OK)
|
|
|
|
{
|
|
|
|
gtk_widget_destroy(widget);
|
|
|
|
error_info_bar = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void report_error(GError* error)
|
|
|
|
{
|
|
|
|
if (error == NULL)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2012-08-26 21:41:05 +00:00
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
if (error_info_bar == NULL)
|
|
|
|
{
|
|
|
|
error_count = 1;
|
|
|
|
error_info_bar = gtk_info_bar_new_with_buttons(GTK_STOCK_OK,
|
|
|
|
GTK_RESPONSE_OK,
|
|
|
|
NULL);
|
|
|
|
g_signal_connect(error_info_bar, "response", G_CALLBACK(on_info_bar_response), NULL);
|
|
|
|
gtk_info_bar_set_message_type(GTK_INFO_BAR(error_info_bar),
|
|
|
|
GTK_MESSAGE_ERROR);
|
2012-08-26 21:41:05 +00:00
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
error_label = gtk_label_new(error->message);
|
|
|
|
GtkWidget *container = gtk_info_bar_get_content_area(GTK_INFO_BAR(error_info_bar));
|
|
|
|
gtk_container_add(GTK_CONTAINER(container), error_label);
|
2012-08-26 21:41:05 +00:00
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(main_vbox), error_info_bar, FALSE, FALSE, 0);
|
|
|
|
gtk_widget_show_all(main_vbox);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
error_count++;
|
|
|
|
char buffer[256];
|
2012-10-11 00:42:59 +00:00
|
|
|
snprintf(buffer, sizeof(buffer), _("Failed to open %i files."), error_count);
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_label_set(GTK_LABEL(error_label), buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-15 22:22:36 +00:00
|
|
|
static GtkFileFilter *setup_filter(void)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
2012-09-15 22:22:36 +00:00
|
|
|
GtkFileFilter *filter = gtk_file_filter_new();
|
2011-09-26 16:18:23 +00:00
|
|
|
gtk_file_filter_add_pattern(filter, "*.xml");
|
|
|
|
gtk_file_filter_add_pattern(filter, "*.XML");
|
|
|
|
gtk_file_filter_add_mime_type(filter, "text/xml");
|
2012-10-11 00:42:59 +00:00
|
|
|
gtk_file_filter_set_name(filter, _("XML file"));
|
2012-08-26 19:51:18 +00:00
|
|
|
|
2012-09-15 22:22:36 +00:00
|
|
|
return filter;
|
2012-08-26 19:51:18 +00:00
|
|
|
}
|
|
|
|
|
2012-07-31 17:55:41 +00:00
|
|
|
static void file_save_as(GtkWidget *w, gpointer data)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
|
|
|
GtkWidget *dialog;
|
2012-08-18 16:48:15 +00:00
|
|
|
char *filename = NULL;
|
2012-08-26 19:51:18 +00:00
|
|
|
char *current_file;
|
|
|
|
char *current_dir;
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
dialog = gtk_file_chooser_dialog_new(_("Save File As"),
|
2011-09-20 19:40:34 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
|
|
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
|
|
|
|
NULL);
|
2012-07-17 14:49:27 +00:00
|
|
|
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
|
|
|
|
|
2012-09-16 03:51:06 +00:00
|
|
|
if (existing_filename) {
|
|
|
|
current_dir = g_path_get_dirname(existing_filename);
|
|
|
|
current_file = g_path_get_basename(existing_filename);
|
|
|
|
} else {
|
|
|
|
const char *current_default = subsurface_default_filename();
|
|
|
|
current_dir = g_path_get_dirname(current_default);
|
|
|
|
current_file = g_path_get_basename(current_default);
|
|
|
|
}
|
2012-08-26 19:51:18 +00:00
|
|
|
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), current_dir);
|
|
|
|
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), current_file);
|
2012-08-26 21:20:48 +00:00
|
|
|
|
|
|
|
free(current_dir);
|
2012-08-26 22:35:48 +00:00
|
|
|
free(current_file);
|
2012-08-26 21:20:48 +00:00
|
|
|
|
2012-07-17 14:49:27 +00:00
|
|
|
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
|
|
|
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
|
|
|
|
|
|
|
if (filename){
|
|
|
|
save_dives(filename);
|
2012-09-10 19:27:00 +00:00
|
|
|
set_filename(filename, TRUE);
|
2011-09-20 19:40:34 +00:00
|
|
|
g_free(filename);
|
2012-07-17 14:49:27 +00:00
|
|
|
mark_divelist_changed(FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-31 17:55:41 +00:00
|
|
|
static void file_save(GtkWidget *w, gpointer data)
|
|
|
|
{
|
2012-09-09 16:06:44 +00:00
|
|
|
const char *current_default;
|
|
|
|
|
2012-07-31 17:55:41 +00:00
|
|
|
if (!existing_filename)
|
|
|
|
return file_save_as(w, data);
|
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
current_default = subsurface_default_filename();
|
|
|
|
if (strcmp(existing_filename, current_default) == 0) {
|
|
|
|
/* if we are using the default filename the directory
|
|
|
|
* that we are creating the file in may not exist */
|
2012-09-13 18:17:38 +00:00
|
|
|
char *current_def_dir;
|
2012-09-09 16:06:44 +00:00
|
|
|
struct stat sb;
|
|
|
|
|
2012-09-13 18:17:38 +00:00
|
|
|
current_def_dir = g_path_get_dirname(existing_filename);
|
2012-09-09 16:06:44 +00:00
|
|
|
if (stat(current_def_dir, &sb) != 0) {
|
2012-09-11 18:42:47 +00:00
|
|
|
g_mkdir(current_def_dir, S_IRWXU);
|
2012-09-09 16:06:44 +00:00
|
|
|
}
|
2012-09-13 18:17:38 +00:00
|
|
|
free(current_def_dir);
|
2012-09-09 16:06:44 +00:00
|
|
|
}
|
2012-07-31 17:55:41 +00:00
|
|
|
save_dives(existing_filename);
|
2012-08-17 17:57:24 +00:00
|
|
|
mark_divelist_changed(FALSE);
|
2012-07-31 17:55:41 +00:00
|
|
|
}
|
|
|
|
|
2012-07-17 14:09:29 +00:00
|
|
|
static gboolean ask_save_changes()
|
2011-09-21 04:29:09 +00:00
|
|
|
{
|
2011-09-21 17:16:33 +00:00
|
|
|
GtkWidget *dialog, *label, *content;
|
2012-07-17 14:09:29 +00:00
|
|
|
gboolean quit = TRUE;
|
2012-10-11 00:42:59 +00:00
|
|
|
dialog = gtk_dialog_new_with_buttons(_("Save Changes?"),
|
2011-09-21 04:29:09 +00:00
|
|
|
GTK_WINDOW(main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
|
|
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
|
2011-12-13 22:34:42 +00:00
|
|
|
GTK_STOCK_NO, GTK_RESPONSE_NO,
|
2012-07-17 14:09:29 +00:00
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
2011-09-21 04:29:09 +00:00
|
|
|
NULL);
|
2011-09-21 17:16:33 +00:00
|
|
|
content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
2012-07-17 14:05:40 +00:00
|
|
|
|
|
|
|
if (!existing_filename){
|
|
|
|
label = gtk_label_new (
|
2012-10-11 00:42:59 +00:00
|
|
|
_("You have unsaved changes\nWould you like to save those before closing the datafile?"));
|
2012-07-17 14:05:40 +00:00
|
|
|
} else {
|
2012-10-16 16:01:04 +00:00
|
|
|
char *label_text = (char*) malloc(sizeof(char) *
|
|
|
|
(strlen(_("You have unsaved changes to file: %s \nWould you like to save those before closing the datafile?")) +
|
|
|
|
strlen(existing_filename)));
|
2012-07-17 14:05:40 +00:00
|
|
|
sprintf(label_text,
|
2012-10-11 00:42:59 +00:00
|
|
|
_("You have unsaved changes to file: %s \nWould you like to save those before closing the datafile?"),
|
2012-07-17 14:05:40 +00:00
|
|
|
existing_filename);
|
|
|
|
label = gtk_label_new (label_text);
|
2012-09-18 23:51:48 +00:00
|
|
|
free(label_text);
|
2012-07-17 14:05:40 +00:00
|
|
|
}
|
2011-09-21 17:16:33 +00:00
|
|
|
gtk_container_add (GTK_CONTAINER (content), label);
|
|
|
|
gtk_widget_show_all (dialog);
|
|
|
|
gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
2012-08-17 17:57:24 +00:00
|
|
|
gint outcode = gtk_dialog_run(GTK_DIALOG(dialog));
|
2012-07-17 14:09:29 +00:00
|
|
|
if (outcode == GTK_RESPONSE_ACCEPT) {
|
2011-09-21 04:29:09 +00:00
|
|
|
file_save(NULL,NULL);
|
2012-07-17 14:09:29 +00:00
|
|
|
} else if (outcode == GTK_RESPONSE_CANCEL) {
|
|
|
|
quit = FALSE;
|
2011-09-21 04:29:09 +00:00
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
2012-07-17 14:09:29 +00:00
|
|
|
return quit;
|
2011-09-21 04:29:09 +00:00
|
|
|
}
|
|
|
|
|
2012-09-10 21:32:55 +00:00
|
|
|
static void file_close(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
if (unsaved_changes())
|
|
|
|
if (ask_save_changes() == FALSE)
|
|
|
|
return;
|
2012-09-13 17:46:09 +00:00
|
|
|
|
|
|
|
if (existing_filename)
|
2012-09-16 03:51:06 +00:00
|
|
|
free((void *)existing_filename);
|
2012-09-10 21:32:55 +00:00
|
|
|
existing_filename = NULL;
|
|
|
|
|
|
|
|
/* free the dives and trips */
|
2012-11-26 04:06:54 +00:00
|
|
|
while (dive_table.nr)
|
|
|
|
delete_single_dive(0);
|
2012-09-10 21:32:55 +00:00
|
|
|
dive_table.preexisting = 0;
|
2012-09-16 00:49:29 +00:00
|
|
|
mark_divelist_changed(FALSE);
|
2012-09-10 23:07:42 +00:00
|
|
|
|
2012-09-10 21:32:55 +00:00
|
|
|
/* clear the selection and the statistics */
|
|
|
|
selected_dive = 0;
|
|
|
|
process_selected_dives();
|
|
|
|
clear_stats_widgets();
|
|
|
|
|
|
|
|
/* clear the equipment page */
|
|
|
|
clear_equipment_widgets();
|
|
|
|
|
|
|
|
/* redraw the screen */
|
|
|
|
dive_list_update_dives();
|
|
|
|
show_dive_info(NULL);
|
|
|
|
}
|
|
|
|
|
2012-09-15 12:33:25 +00:00
|
|
|
static void file_open(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
GtkWidget *dialog;
|
|
|
|
GtkFileFilter *filter;
|
2012-09-16 03:51:06 +00:00
|
|
|
const char *current_default;
|
2012-09-15 12:33:25 +00:00
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
dialog = gtk_file_chooser_dialog_new(_("Open File"),
|
2012-09-15 12:33:25 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
|
|
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
|
|
|
|
NULL);
|
2012-09-16 03:51:06 +00:00
|
|
|
current_default = subsurface_default_filename();
|
|
|
|
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), current_default);
|
2012-09-15 21:24:34 +00:00
|
|
|
/* when opening the data file we should allow only one file to be chosen */
|
|
|
|
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
|
2012-09-15 12:33:25 +00:00
|
|
|
|
2012-09-15 22:22:36 +00:00
|
|
|
filter = setup_filter();
|
2012-09-15 12:33:25 +00:00
|
|
|
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(dialog), filter);
|
|
|
|
|
|
|
|
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
2012-09-16 09:05:53 +00:00
|
|
|
GSList *fn_glist;
|
2012-09-15 12:33:25 +00:00
|
|
|
char *filename;
|
2012-09-16 09:01:25 +00:00
|
|
|
|
|
|
|
/* first, close the existing file, if any, and forget its name */
|
|
|
|
file_close(w, data);
|
|
|
|
free((void *)existing_filename);
|
|
|
|
existing_filename = NULL;
|
|
|
|
|
2012-09-16 09:05:53 +00:00
|
|
|
/* we know there is only one filename */
|
|
|
|
fn_glist = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
|
2012-09-15 12:33:25 +00:00
|
|
|
|
|
|
|
GError *error = NULL;
|
2012-09-16 09:05:53 +00:00
|
|
|
filename = fn_glist->data;
|
2012-10-09 12:50:16 +00:00
|
|
|
parse_file(filename, &error, TRUE);
|
2012-09-16 09:05:53 +00:00
|
|
|
if (error != NULL)
|
|
|
|
{
|
|
|
|
report_error(error);
|
|
|
|
g_error_free(error);
|
|
|
|
error = NULL;
|
2012-09-15 12:33:25 +00:00
|
|
|
}
|
2012-09-16 09:05:53 +00:00
|
|
|
g_free(filename);
|
2012-09-15 12:33:25 +00:00
|
|
|
g_slist_free(fn_glist);
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
report_dives(FALSE, FALSE);
|
2012-09-15 12:33:25 +00:00
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
|
|
|
}
|
|
|
|
|
2012-12-09 23:19:17 +00:00
|
|
|
gboolean on_delete(GtkWidget* w, gpointer data)
|
2011-09-21 04:29:09 +00:00
|
|
|
{
|
2011-09-21 14:34:00 +00:00
|
|
|
/* Make sure to flush any modified dive data */
|
|
|
|
update_dive(NULL);
|
|
|
|
|
2012-07-17 14:09:29 +00:00
|
|
|
gboolean quit = TRUE;
|
2011-09-21 04:29:09 +00:00
|
|
|
if (unsaved_changes())
|
2012-07-17 14:09:29 +00:00
|
|
|
quit = ask_save_changes();
|
2011-09-21 17:31:03 +00:00
|
|
|
|
2012-07-17 14:09:29 +00:00
|
|
|
if (quit){
|
|
|
|
return FALSE; /* go ahead, kill the program, we're good now */
|
|
|
|
} else {
|
|
|
|
return TRUE; /* We are not leaving */
|
|
|
|
}
|
2011-09-21 17:31:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void on_destroy(GtkWidget* w, gpointer data)
|
|
|
|
{
|
2012-10-01 22:52:43 +00:00
|
|
|
dive_list_destroy();
|
2011-09-21 04:29:09 +00:00
|
|
|
gtk_main_quit();
|
|
|
|
}
|
|
|
|
|
2012-09-12 16:18:56 +00:00
|
|
|
void quit(GtkWidget *w, gpointer data)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
2011-09-21 17:16:33 +00:00
|
|
|
/* Make sure to flush any modified dive data */
|
|
|
|
update_dive(NULL);
|
|
|
|
|
2012-07-17 14:09:29 +00:00
|
|
|
gboolean quit = TRUE;
|
2011-09-21 17:16:33 +00:00
|
|
|
if (unsaved_changes())
|
2012-07-17 14:09:29 +00:00
|
|
|
quit = ask_save_changes();
|
|
|
|
|
|
|
|
if (quit){
|
2012-10-01 22:52:43 +00:00
|
|
|
dive_list_destroy();
|
2012-07-17 14:09:29 +00:00
|
|
|
gtk_main_quit();
|
|
|
|
}
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
2011-10-02 20:05:12 +00:00
|
|
|
GtkTreeViewColumn *tree_view_column(GtkWidget *tree_view, int index, const char *title,
|
2011-12-11 19:40:17 +00:00
|
|
|
data_func_t data_func, unsigned int flags)
|
2011-10-02 20:05:12 +00:00
|
|
|
{
|
|
|
|
GtkCellRenderer *renderer;
|
|
|
|
GtkTreeViewColumn *col;
|
|
|
|
double xalign = 0.0; /* left as default */
|
2011-12-11 19:40:17 +00:00
|
|
|
PangoAlignment align;
|
|
|
|
gboolean visible;
|
|
|
|
|
|
|
|
align = (flags & ALIGN_LEFT) ? PANGO_ALIGN_LEFT :
|
|
|
|
(flags & ALIGN_RIGHT) ? PANGO_ALIGN_RIGHT :
|
|
|
|
PANGO_ALIGN_CENTER;
|
|
|
|
visible = !(flags & INVISIBLE);
|
2011-10-02 20:05:12 +00:00
|
|
|
|
|
|
|
renderer = gtk_cell_renderer_text_new();
|
|
|
|
col = gtk_tree_view_column_new();
|
|
|
|
|
|
|
|
gtk_tree_view_column_set_title(col, title);
|
2011-12-11 19:40:17 +00:00
|
|
|
if (!(flags & UNSORTABLE))
|
|
|
|
gtk_tree_view_column_set_sort_column_id(col, index);
|
2011-10-02 20:05:12 +00:00
|
|
|
gtk_tree_view_column_set_resizable(col, TRUE);
|
|
|
|
gtk_tree_view_column_pack_start(col, renderer, TRUE);
|
|
|
|
if (data_func)
|
|
|
|
gtk_tree_view_column_set_cell_data_func(col, renderer, data_func, (void *)(long)index, NULL);
|
|
|
|
else
|
|
|
|
gtk_tree_view_column_add_attribute(col, renderer, "text", index);
|
|
|
|
gtk_object_set(GTK_OBJECT(renderer), "alignment", align, NULL);
|
|
|
|
switch (align) {
|
|
|
|
case PANGO_ALIGN_LEFT:
|
|
|
|
xalign = 0.0;
|
|
|
|
break;
|
|
|
|
case PANGO_ALIGN_CENTER:
|
|
|
|
xalign = 0.5;
|
|
|
|
break;
|
|
|
|
case PANGO_ALIGN_RIGHT:
|
|
|
|
xalign = 1.0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
gtk_cell_renderer_set_alignment(GTK_CELL_RENDERER(renderer), xalign, 0.5);
|
|
|
|
gtk_tree_view_column_set_visible(col, visible);
|
|
|
|
gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), col);
|
|
|
|
return col;
|
|
|
|
}
|
|
|
|
|
2012-01-05 16:16:08 +00:00
|
|
|
static void create_radio(GtkWidget *vbox, const char *w_name, ...)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
GtkRadioButton *group = NULL;
|
|
|
|
GtkWidget *box, *label;
|
|
|
|
|
|
|
|
box = gtk_hbox_new(TRUE, 10);
|
2011-09-20 21:05:46 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 0);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-01-05 16:16:08 +00:00
|
|
|
label = gtk_label_new(w_name);
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
|
|
|
|
|
2012-01-05 16:16:08 +00:00
|
|
|
va_start(args, w_name);
|
2011-09-20 19:40:34 +00:00
|
|
|
for (;;) {
|
|
|
|
int enabled;
|
|
|
|
const char *name;
|
|
|
|
GtkWidget *button;
|
|
|
|
void *callback_fn;
|
|
|
|
|
|
|
|
name = va_arg(args, char *);
|
|
|
|
if (!name)
|
|
|
|
break;
|
|
|
|
callback_fn = va_arg(args, void *);
|
|
|
|
enabled = va_arg(args, int);
|
|
|
|
|
|
|
|
button = gtk_radio_button_new_with_label_from_widget(group, name);
|
|
|
|
group = GTK_RADIO_BUTTON(button);
|
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
|
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), enabled);
|
|
|
|
g_signal_connect(button, "toggled", G_CALLBACK(callback_fn), NULL);
|
|
|
|
}
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
2012-12-10 18:46:46 +00:00
|
|
|
static void update_screen()
|
|
|
|
{
|
|
|
|
update_dive_list_units();
|
|
|
|
repaint_dive();
|
|
|
|
update_dive_list_col_visibility();
|
|
|
|
}
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
#define UNITCALLBACK(name, type, value) \
|
|
|
|
static void name(GtkWidget *w, gpointer data) \
|
|
|
|
{ \
|
|
|
|
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.output_units.type = value; \
|
2012-12-10 18:46:46 +00:00
|
|
|
update_screen(); \
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UNITCALLBACK(set_meter, length, METERS)
|
|
|
|
UNITCALLBACK(set_feet, length, FEET)
|
|
|
|
UNITCALLBACK(set_bar, pressure, BAR)
|
|
|
|
UNITCALLBACK(set_psi, pressure, PSI)
|
|
|
|
UNITCALLBACK(set_liter, volume, LITER)
|
|
|
|
UNITCALLBACK(set_cuft, volume, CUFT)
|
|
|
|
UNITCALLBACK(set_celsius, temperature, CELSIUS)
|
|
|
|
UNITCALLBACK(set_fahrenheit, temperature, FAHRENHEIT)
|
2011-12-24 03:41:16 +00:00
|
|
|
UNITCALLBACK(set_kg, weight, KG)
|
|
|
|
UNITCALLBACK(set_lbs, weight, LBS)
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-11-10 16:58:42 +00:00
|
|
|
#define OPTIONCALLBACK(name, option) \
|
|
|
|
static void name(GtkWidget *w, gpointer data) \
|
|
|
|
{ \
|
|
|
|
GtkWidget **entry = data; \
|
|
|
|
option = GTK_TOGGLE_BUTTON(w)->active; \
|
2012-12-10 18:46:46 +00:00
|
|
|
update_screen(); \
|
2012-11-10 16:58:42 +00:00
|
|
|
if (entry) \
|
|
|
|
gtk_widget_set_sensitive(*entry, option);\
|
2011-09-27 17:16:40 +00:00
|
|
|
}
|
|
|
|
|
2012-12-10 17:20:57 +00:00
|
|
|
OPTIONCALLBACK(otu_toggle, prefs.visible_cols.otu)
|
2012-12-11 21:09:48 +00:00
|
|
|
OPTIONCALLBACK(maxcns_toggle, prefs.visible_cols.maxcns)
|
2012-12-10 17:20:57 +00:00
|
|
|
OPTIONCALLBACK(sac_toggle, prefs.visible_cols.sac)
|
|
|
|
OPTIONCALLBACK(nitrox_toggle, prefs.visible_cols.nitrox)
|
|
|
|
OPTIONCALLBACK(temperature_toggle, prefs.visible_cols.temperature)
|
|
|
|
OPTIONCALLBACK(totalweight_toggle, prefs.visible_cols.totalweight)
|
|
|
|
OPTIONCALLBACK(suit_toggle, prefs.visible_cols.suit)
|
|
|
|
OPTIONCALLBACK(cylinder_toggle, prefs.visible_cols.cylinder)
|
|
|
|
OPTIONCALLBACK(po2_toggle, prefs.pp_graphs.po2)
|
|
|
|
OPTIONCALLBACK(pn2_toggle, prefs.pp_graphs.pn2)
|
|
|
|
OPTIONCALLBACK(phe_toggle, prefs.pp_graphs.phe)
|
|
|
|
OPTIONCALLBACK(red_ceiling_toggle, prefs.profile_red_ceiling)
|
2013-01-03 06:45:21 +00:00
|
|
|
OPTIONCALLBACK(calc_ceiling_toggle, prefs.profile_calc_ceiling)
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
OPTIONCALLBACK(force_toggle, force_download)
|
|
|
|
OPTIONCALLBACK(prefer_dl_toggle, prefer_downloaded)
|
2011-09-27 17:16:40 +00:00
|
|
|
|
2011-10-25 09:51:16 +00:00
|
|
|
static void event_toggle(GtkWidget *w, gpointer _data)
|
|
|
|
{
|
|
|
|
gboolean *plot_ev = _data;
|
|
|
|
|
|
|
|
*plot_ev = GTK_TOGGLE_BUTTON(w)->active;
|
|
|
|
}
|
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
static void pick_default_file(GtkWidget *w, GtkButton *button)
|
|
|
|
{
|
2012-09-21 19:09:48 +00:00
|
|
|
GtkWidget *fs_dialog, *parent;
|
2012-09-12 20:40:22 +00:00
|
|
|
const char *current_default;
|
2012-09-09 16:06:44 +00:00
|
|
|
char *current_def_file, *current_def_dir;
|
|
|
|
GtkFileFilter *filter;
|
|
|
|
struct stat sb;
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
fs_dialog = gtk_file_chooser_dialog_new(_("Choose Default XML File"),
|
2012-09-09 16:06:44 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
|
|
|
NULL);
|
2012-09-21 19:09:48 +00:00
|
|
|
parent = gtk_widget_get_ancestor(w, GTK_TYPE_DIALOG);
|
|
|
|
gtk_widget_set_sensitive(parent, FALSE);
|
|
|
|
gtk_window_set_transient_for(GTK_WINDOW(fs_dialog), GTK_WINDOW(parent));
|
2012-09-13 17:53:30 +00:00
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
current_default = subsurface_default_filename();
|
2012-09-13 18:17:38 +00:00
|
|
|
current_def_dir = g_path_get_dirname(current_default);
|
|
|
|
current_def_file = g_path_get_basename(current_default);
|
2012-09-12 20:40:22 +00:00
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
/* it's possible that the directory doesn't exist (especially for the default)
|
2012-09-12 20:40:22 +00:00
|
|
|
* For gtk's file select box to make sense we create it */
|
|
|
|
if (stat(current_def_dir, &sb) != 0)
|
|
|
|
g_mkdir(current_def_dir, S_IRWXU);
|
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs_dialog), current_def_dir);
|
|
|
|
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(fs_dialog), current_def_file);
|
|
|
|
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(fs_dialog), FALSE);
|
2012-09-15 22:22:36 +00:00
|
|
|
filter = setup_filter();
|
2012-09-09 16:06:44 +00:00
|
|
|
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs_dialog), filter);
|
|
|
|
gtk_widget_show_all(fs_dialog);
|
|
|
|
if (gtk_dialog_run(GTK_DIALOG(fs_dialog)) == GTK_RESPONSE_ACCEPT) {
|
|
|
|
GSList *list;
|
|
|
|
|
|
|
|
list = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog));
|
2012-09-12 20:40:22 +00:00
|
|
|
if (g_slist_length(list) == 1)
|
|
|
|
gtk_button_set_label(button, list->data);
|
2012-09-09 16:06:44 +00:00
|
|
|
g_slist_free(list);
|
|
|
|
}
|
2012-09-12 20:40:22 +00:00
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
free(current_def_dir);
|
|
|
|
free(current_def_file);
|
2012-09-18 11:24:54 +00:00
|
|
|
free((void *)current_default);
|
2012-09-09 16:06:44 +00:00
|
|
|
gtk_widget_destroy(fs_dialog);
|
2012-09-21 19:09:48 +00:00
|
|
|
|
|
|
|
gtk_widget_set_sensitive(parent, TRUE);
|
2012-09-09 16:06:44 +00:00
|
|
|
}
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
static void preferences_dialog(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
int result;
|
2012-11-06 04:13:05 +00:00
|
|
|
GtkWidget *dialog, *notebook, *font, *frame, *box, *vbox, *button, *xmlfile_button;
|
2012-11-10 16:58:42 +00:00
|
|
|
GtkWidget *entry_po2, *entry_pn2, *entry_phe;
|
2012-09-09 16:06:44 +00:00
|
|
|
const char *current_default, *new_default;
|
2012-12-22 21:34:21 +00:00
|
|
|
char threshold_text[10], utf8_buf[128];
|
2012-12-10 17:20:57 +00:00
|
|
|
struct preferences oldprefs = prefs;
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
dialog = gtk_dialog_new_with_buttons(_("Preferences"),
|
2011-09-20 19:40:34 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
2012-12-10 17:20:57 +00:00
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
2011-09-20 19:40:34 +00:00
|
|
|
NULL);
|
|
|
|
|
2012-11-01 18:11:05 +00:00
|
|
|
/* create the notebook for the preferences and attach it to dialog */
|
|
|
|
notebook = gtk_notebook_new();
|
|
|
|
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook, FALSE, FALSE, 5);
|
|
|
|
|
|
|
|
/* vbox that holds the first notebook page */
|
|
|
|
vbox = gtk_vbox_new(FALSE, 6);
|
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox,
|
|
|
|
gtk_label_new(_("General Settings")));
|
2012-10-11 00:42:59 +00:00
|
|
|
frame = gtk_frame_new(_("Units"));
|
2011-09-24 22:26:37 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
box = gtk_vbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), box);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
create_radio(box, _("Depth:"),
|
2012-12-10 17:20:57 +00:00
|
|
|
_("Meter"), set_meter, (prefs.output_units.length == METERS),
|
|
|
|
_("Feet"), set_feet, (prefs.output_units.length == FEET),
|
2011-09-20 19:40:34 +00:00
|
|
|
NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
create_radio(box, _("Pressure:"),
|
2012-12-10 17:20:57 +00:00
|
|
|
_("Bar"), set_bar, (prefs.output_units.pressure == BAR),
|
|
|
|
_("PSI"), set_psi, (prefs.output_units.pressure == PSI),
|
2011-09-20 19:40:34 +00:00
|
|
|
NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
create_radio(box, _("Volume:"),
|
2012-12-10 17:20:57 +00:00
|
|
|
_("Liter"), set_liter, (prefs.output_units.volume == LITER),
|
|
|
|
_("CuFt"), set_cuft, (prefs.output_units.volume == CUFT),
|
2011-09-20 19:40:34 +00:00
|
|
|
NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
create_radio(box, _("Temperature:"),
|
2012-12-10 17:20:57 +00:00
|
|
|
_("Celsius"), set_celsius, (prefs.output_units.temperature == CELSIUS),
|
|
|
|
_("Fahrenheit"), set_fahrenheit, (prefs.output_units.temperature == FAHRENHEIT),
|
2011-09-20 19:40:34 +00:00
|
|
|
NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
create_radio(box, _("Weight:"),
|
2012-12-10 17:20:57 +00:00
|
|
|
_("kg"), set_kg, (prefs.output_units.weight == KG),
|
|
|
|
_("lbs"), set_lbs, (prefs.output_units.weight == LBS),
|
2011-12-24 03:41:16 +00:00
|
|
|
NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
frame = gtk_frame_new(_("Show Columns"));
|
2012-11-01 18:11:05 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
2011-09-27 17:16:40 +00:00
|
|
|
|
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), box);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("Temp"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.temperature);
|
2011-10-23 20:36:37 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(temperature_toggle), NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("Cyl"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.cylinder);
|
2011-10-23 20:36:37 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(cylinder_toggle), NULL);
|
|
|
|
|
2012-08-17 06:56:03 +00:00
|
|
|
button = gtk_check_button_new_with_label("O" UTF8_SUBSCRIPT_2 "%");
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.nitrox);
|
2011-10-23 20:36:37 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(nitrox_toggle), NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("SAC"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.sac);
|
2011-09-27 17:16:40 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(sac_toggle), NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("Weight"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.totalweight);
|
2012-08-07 18:24:40 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(totalweight_toggle), NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("Suit"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.suit);
|
2012-08-14 23:07:25 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(suit_toggle), NULL);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
frame = gtk_frame_new(_("Divelist Font"));
|
2012-11-01 18:11:05 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
2012-08-22 05:04:24 +00:00
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
font = gtk_font_button_new_with_font(divelist_font);
|
2012-08-22 05:04:24 +00:00
|
|
|
gtk_container_add(GTK_CONTAINER(frame),font);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
frame = gtk_frame_new(_("Misc. Options"));
|
2012-11-01 18:11:05 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
2012-08-22 05:04:24 +00:00
|
|
|
|
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), box);
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
frame = gtk_frame_new(_("Default XML Data File"));
|
2012-09-09 16:06:44 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 5);
|
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), box);
|
|
|
|
current_default = subsurface_default_filename();
|
2012-11-06 04:13:05 +00:00
|
|
|
xmlfile_button = gtk_button_new_with_label(current_default);
|
|
|
|
g_signal_connect(G_OBJECT(xmlfile_button), "clicked",
|
|
|
|
G_CALLBACK(pick_default_file), xmlfile_button);
|
|
|
|
gtk_box_pack_start(GTK_BOX(box), xmlfile_button, FALSE, FALSE, 6);
|
2012-09-09 16:06:44 +00:00
|
|
|
|
2012-11-01 18:11:05 +00:00
|
|
|
/* vbox that holds the second notebook page */
|
|
|
|
vbox = gtk_vbox_new(FALSE, 6);
|
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox,
|
|
|
|
gtk_label_new(_("Tec Settings")));
|
|
|
|
|
|
|
|
frame = gtk_frame_new(_("Show Columns"));
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
|
|
|
|
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), box);
|
|
|
|
|
|
|
|
button = gtk_check_button_new_with_label(_("OTU"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.otu);
|
2012-11-01 18:11:05 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(otu_toggle), NULL);
|
|
|
|
|
2012-12-11 05:18:48 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("maxCNS"));
|
2012-12-11 21:09:48 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.visible_cols.maxcns);
|
2012-12-11 05:18:48 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(maxcns_toggle), NULL);
|
|
|
|
|
2012-12-07 19:36:25 +00:00
|
|
|
frame = gtk_frame_new(_("Profile Settings"));
|
2012-11-01 18:11:05 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
|
|
|
|
2012-11-10 16:58:42 +00:00
|
|
|
vbox = gtk_vbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), vbox);
|
2012-11-01 18:11:05 +00:00
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_container_add(GTK_CONTAINER(vbox), box);
|
2012-11-01 18:11:05 +00:00
|
|
|
|
2012-12-22 21:34:21 +00:00
|
|
|
sprintf(utf8_buf, _("Show pO%s graph"), UTF8_SUBSCRIPT_2);
|
|
|
|
button = gtk_check_button_new_with_label(utf8_buf);
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.po2);
|
2012-09-09 16:06:44 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
2012-11-10 16:58:42 +00:00
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(po2_toggle), &entry_po2);
|
|
|
|
|
2012-12-22 21:34:21 +00:00
|
|
|
sprintf(utf8_buf, _("pO%s threshold"), UTF8_SUBSCRIPT_2);
|
|
|
|
frame = gtk_frame_new(utf8_buf);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6);
|
|
|
|
entry_po2 = gtk_entry_new();
|
|
|
|
gtk_entry_set_max_length(GTK_ENTRY(entry_po2), 4);
|
2012-12-10 17:20:57 +00:00
|
|
|
snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.po2_threshold);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(entry_po2), threshold_text);
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_widget_set_sensitive(entry_po2, prefs.pp_graphs.po2);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_container_add(GTK_CONTAINER(frame), entry_po2);
|
|
|
|
|
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(vbox), box);
|
2012-11-01 18:11:05 +00:00
|
|
|
|
2012-12-22 21:34:21 +00:00
|
|
|
sprintf(utf8_buf, _("Show pN%s graph"), UTF8_SUBSCRIPT_2);
|
|
|
|
button = gtk_check_button_new_with_label(utf8_buf);
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.pn2);
|
2012-11-01 18:11:05 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
2012-11-10 16:58:42 +00:00
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(pn2_toggle), &entry_pn2);
|
|
|
|
|
2012-12-22 21:34:21 +00:00
|
|
|
sprintf(utf8_buf, _("pN%s threshold"), UTF8_SUBSCRIPT_2);
|
|
|
|
frame = gtk_frame_new(utf8_buf);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6);
|
|
|
|
entry_pn2 = gtk_entry_new();
|
|
|
|
gtk_entry_set_max_length(GTK_ENTRY(entry_pn2), 4);
|
2012-12-10 17:20:57 +00:00
|
|
|
snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.pn2_threshold);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(entry_pn2), threshold_text);
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_widget_set_sensitive(entry_pn2, prefs.pp_graphs.pn2);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_container_add(GTK_CONTAINER(frame), entry_pn2);
|
|
|
|
|
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(vbox), box);
|
2012-11-01 18:11:05 +00:00
|
|
|
|
2012-12-07 19:36:25 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("Show pHe graph"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.pp_graphs.phe);
|
2012-11-01 18:11:05 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
2012-11-10 16:58:42 +00:00
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(phe_toggle), &entry_phe);
|
|
|
|
|
|
|
|
frame = gtk_frame_new(_("pHe threshold"));
|
|
|
|
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 6);
|
|
|
|
entry_phe = gtk_entry_new();
|
|
|
|
gtk_entry_set_max_length(GTK_ENTRY(entry_phe), 4);
|
2012-12-10 17:20:57 +00:00
|
|
|
snprintf(threshold_text, sizeof(threshold_text), "%.1f", prefs.pp_graphs.phe_threshold);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_entry_set_text(GTK_ENTRY(entry_phe), threshold_text);
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_widget_set_sensitive(entry_phe, prefs.pp_graphs.phe);
|
2012-11-10 16:58:42 +00:00
|
|
|
gtk_container_add(GTK_CONTAINER(frame), entry_phe);
|
2012-09-09 16:06:44 +00:00
|
|
|
|
2012-12-07 19:36:25 +00:00
|
|
|
box = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(vbox), box);
|
|
|
|
|
2013-01-03 06:45:21 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("Show dc reported ceiling in red"));
|
2012-12-10 17:20:57 +00:00
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_red_ceiling);
|
2012-12-07 19:36:25 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
2012-12-07 20:51:48 +00:00
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(red_ceiling_toggle), NULL);
|
2012-12-07 19:36:25 +00:00
|
|
|
|
2013-01-03 06:45:21 +00:00
|
|
|
button = gtk_check_button_new_with_label(_("Show calculated ceiling"));
|
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), prefs.profile_calc_ceiling);
|
|
|
|
gtk_box_pack_start(GTK_BOX(box), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(calc_ceiling_toggle), NULL);
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_widget_show_all(dialog);
|
|
|
|
result = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
|
|
if (result == GTK_RESPONSE_ACCEPT) {
|
2012-11-10 16:58:42 +00:00
|
|
|
const char *po2_threshold_text, *pn2_threshold_text, *phe_threshold_text;
|
2011-09-20 19:40:34 +00:00
|
|
|
/* Make sure to flush any modified old dive data with old units */
|
|
|
|
update_dive(NULL);
|
|
|
|
|
2012-10-04 20:52:09 +00:00
|
|
|
if (divelist_font)
|
|
|
|
free((void *)divelist_font);
|
2011-09-20 19:40:34 +00:00
|
|
|
divelist_font = strdup(gtk_font_button_get_font_name(GTK_FONT_BUTTON(font)));
|
|
|
|
set_divelist_font(divelist_font);
|
2012-11-10 16:58:42 +00:00
|
|
|
po2_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_po2));
|
2012-12-10 17:20:57 +00:00
|
|
|
sscanf(po2_threshold_text, "%lf", &prefs.pp_graphs.po2_threshold);
|
2012-11-10 16:58:42 +00:00
|
|
|
pn2_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_pn2));
|
2012-12-10 17:20:57 +00:00
|
|
|
sscanf(pn2_threshold_text, "%lf", &prefs.pp_graphs.pn2_threshold);
|
2012-11-10 16:58:42 +00:00
|
|
|
phe_threshold_text = gtk_entry_get_text(GTK_ENTRY(entry_phe));
|
2012-12-10 17:20:57 +00:00
|
|
|
sscanf(phe_threshold_text, "%lf", &prefs.pp_graphs.phe_threshold);
|
2012-12-11 21:09:48 +00:00
|
|
|
|
2012-12-10 18:46:46 +00:00
|
|
|
update_screen();
|
2011-10-29 01:46:53 +00:00
|
|
|
|
2012-12-10 17:20:57 +00:00
|
|
|
subsurface_set_conf("feet", PREF_BOOL, BOOL_TO_PTR(prefs.output_units.length == FEET));
|
|
|
|
subsurface_set_conf("psi", PREF_BOOL, BOOL_TO_PTR(prefs.output_units.pressure == PSI));
|
|
|
|
subsurface_set_conf("cuft", PREF_BOOL, BOOL_TO_PTR(prefs.output_units.volume == CUFT));
|
|
|
|
subsurface_set_conf("fahrenheit", PREF_BOOL, BOOL_TO_PTR(prefs.output_units.temperature == FAHRENHEIT));
|
|
|
|
subsurface_set_conf("lbs", PREF_BOOL, BOOL_TO_PTR(prefs.output_units.weight == LBS));
|
2012-11-01 18:11:05 +00:00
|
|
|
|
2012-12-10 17:20:57 +00:00
|
|
|
subsurface_set_conf("TEMPERATURE", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.temperature));
|
|
|
|
subsurface_set_conf("TOTALWEIGHT", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.totalweight));
|
|
|
|
subsurface_set_conf("SUIT", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.suit));
|
|
|
|
subsurface_set_conf("CYLINDER", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.cylinder));
|
|
|
|
subsurface_set_conf("NITROX", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.nitrox));
|
|
|
|
subsurface_set_conf("SAC", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.sac));
|
|
|
|
subsurface_set_conf("OTU", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.otu));
|
2012-12-11 21:09:48 +00:00
|
|
|
subsurface_set_conf("MAXCNS", PREF_BOOL, BOOL_TO_PTR(prefs.visible_cols.maxcns));
|
2012-11-01 18:11:05 +00:00
|
|
|
|
2011-11-24 06:56:57 +00:00
|
|
|
subsurface_set_conf("divelist_font", PREF_STRING, divelist_font);
|
2012-11-01 18:11:05 +00:00
|
|
|
|
2012-12-10 17:20:57 +00:00
|
|
|
subsurface_set_conf("po2graph", PREF_BOOL, BOOL_TO_PTR(prefs.pp_graphs.po2));
|
|
|
|
subsurface_set_conf("pn2graph", PREF_BOOL, BOOL_TO_PTR(prefs.pp_graphs.pn2));
|
|
|
|
subsurface_set_conf("phegraph", PREF_BOOL, BOOL_TO_PTR(prefs.pp_graphs.phe));
|
2012-11-10 16:58:42 +00:00
|
|
|
subsurface_set_conf("po2threshold", PREF_STRING, po2_threshold_text);
|
|
|
|
subsurface_set_conf("pn2threshold", PREF_STRING, pn2_threshold_text);
|
|
|
|
subsurface_set_conf("phethreshold", PREF_STRING, phe_threshold_text);
|
2012-12-10 17:20:57 +00:00
|
|
|
subsurface_set_conf("redceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_red_ceiling));
|
2013-01-03 06:45:21 +00:00
|
|
|
subsurface_set_conf("calcceiling", PREF_BOOL, BOOL_TO_PTR(prefs.profile_calc_ceiling));
|
2012-11-01 18:11:05 +00:00
|
|
|
|
2012-11-06 04:13:05 +00:00
|
|
|
new_default = strdup(gtk_button_get_label(GTK_BUTTON(xmlfile_button)));
|
2012-09-12 20:40:22 +00:00
|
|
|
|
|
|
|
/* if we opened the default file and are changing its name,
|
|
|
|
* update existing_filename */
|
2012-09-13 17:46:09 +00:00
|
|
|
if (existing_filename) {
|
|
|
|
if (strcmp(current_default, existing_filename) == 0) {
|
2012-09-16 03:51:06 +00:00
|
|
|
free((void *)existing_filename);
|
2012-09-13 17:46:09 +00:00
|
|
|
existing_filename = strdup(new_default);
|
|
|
|
}
|
|
|
|
}
|
2012-09-12 20:40:22 +00:00
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
if (strcmp(current_default, new_default)) {
|
|
|
|
subsurface_set_conf("default_filename", PREF_STRING, new_default);
|
|
|
|
free((void *)default_filename);
|
|
|
|
default_filename = new_default;
|
|
|
|
}
|
2012-05-02 17:03:48 +00:00
|
|
|
|
|
|
|
/* Flush the changes out to the system */
|
|
|
|
subsurface_flush_conf();
|
2012-12-10 17:20:57 +00:00
|
|
|
} else if (result == GTK_RESPONSE_CANCEL) {
|
|
|
|
prefs = oldprefs;
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
2012-10-01 19:22:12 +00:00
|
|
|
free((void *)current_default);
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_widget_destroy(dialog);
|
|
|
|
}
|
|
|
|
|
2011-10-25 09:51:16 +00:00
|
|
|
static void create_toggle(const char* label, int *on, void *_data)
|
|
|
|
{
|
2011-10-25 11:18:31 +00:00
|
|
|
GtkWidget *button, *table = _data;
|
|
|
|
int rows, cols, x, y;
|
|
|
|
static int count;
|
|
|
|
|
|
|
|
if (table == NULL) {
|
|
|
|
/* magic way to reset the number of toggle buttons
|
|
|
|
* that we have already added - call this before you
|
|
|
|
* create the dialog */
|
|
|
|
count = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
g_object_get(G_OBJECT(table), "n-columns", &cols, "n-rows", &rows, NULL);
|
|
|
|
if (count > rows * cols) {
|
|
|
|
gtk_table_resize(GTK_TABLE(table),rows+1,cols);
|
|
|
|
rows++;
|
|
|
|
}
|
|
|
|
x = count % cols;
|
|
|
|
y = count / cols;
|
2011-10-25 09:51:16 +00:00
|
|
|
button = gtk_check_button_new_with_label(label);
|
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), *on);
|
2011-10-25 11:18:31 +00:00
|
|
|
gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1);
|
2011-10-25 09:51:16 +00:00
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(event_toggle), on);
|
2011-10-25 11:18:31 +00:00
|
|
|
count++;
|
2011-10-25 09:51:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void selectevents_dialog(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
int result;
|
2011-10-25 11:18:31 +00:00
|
|
|
GtkWidget *dialog, *frame, *vbox, *table;
|
2011-10-25 09:51:16 +00:00
|
|
|
|
2012-10-25 16:32:23 +00:00
|
|
|
dialog = gtk_dialog_new_with_buttons(_("Select Events"),
|
2011-10-25 09:51:16 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
|
|
|
NULL);
|
2011-10-25 11:18:31 +00:00
|
|
|
/* initialize the function that fills the table */
|
|
|
|
create_toggle(NULL, NULL, NULL);
|
2011-10-25 09:51:16 +00:00
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
frame = gtk_frame_new(_("Enable / Disable Events"));
|
2011-10-25 09:51:16 +00:00
|
|
|
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
|
|
|
|
2011-10-25 11:18:31 +00:00
|
|
|
table = gtk_table_new(1, 4, TRUE);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), table);
|
2011-10-25 09:51:16 +00:00
|
|
|
|
2011-10-25 11:18:31 +00:00
|
|
|
evn_foreach(&create_toggle, table);
|
2011-10-25 09:51:16 +00:00
|
|
|
|
|
|
|
gtk_widget_show_all(dialog);
|
|
|
|
result = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
|
|
if (result == GTK_RESPONSE_ACCEPT) {
|
|
|
|
repaint_dive();
|
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
|
|
|
}
|
|
|
|
|
2012-09-03 04:48:30 +00:00
|
|
|
static void autogroup_cb(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
autogroup = !autogroup;
|
|
|
|
if (! autogroup)
|
|
|
|
remove_autogen_trips();
|
|
|
|
dive_list_update_dives();
|
|
|
|
}
|
|
|
|
|
2013-01-02 01:29:38 +00:00
|
|
|
void set_autogroup(gboolean value)
|
|
|
|
{
|
|
|
|
GtkAction *autogroup_action;
|
|
|
|
|
|
|
|
if (value == autogroup)
|
|
|
|
return;
|
|
|
|
|
|
|
|
autogroup_action = gtk_action_group_get_action(action_group, "Autogroup");
|
|
|
|
gtk_action_activate(autogroup_action);
|
|
|
|
}
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
static void renumber_dialog(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
int result;
|
2011-10-05 15:37:14 +00:00
|
|
|
struct dive *dive;
|
2011-09-24 22:26:37 +00:00
|
|
|
GtkWidget *dialog, *frame, *button, *vbox;
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
dialog = gtk_dialog_new_with_buttons(_("Renumber"),
|
2011-09-20 19:40:34 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
|
|
|
NULL);
|
|
|
|
|
2011-09-24 22:26:37 +00:00
|
|
|
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
|
|
|
|
2012-10-11 00:42:59 +00:00
|
|
|
frame = gtk_frame_new(_("New starting number"));
|
2011-09-24 22:26:37 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
button = gtk_spin_button_new_with_range(1, 50000, 1);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), button);
|
|
|
|
|
2011-10-05 15:37:14 +00:00
|
|
|
/*
|
|
|
|
* Do we have a number for the first dive already? Use that
|
|
|
|
* as the default.
|
|
|
|
*/
|
|
|
|
dive = get_dive(0);
|
|
|
|
if (dive && dive->number)
|
|
|
|
gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), dive->number);
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_widget_show_all(dialog);
|
|
|
|
result = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
|
|
if (result == GTK_RESPONSE_ACCEPT) {
|
|
|
|
int nr = gtk_spin_button_get_value(GTK_SPIN_BUTTON(button));
|
|
|
|
renumber_dives(nr);
|
|
|
|
repaint_dive();
|
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
|
|
|
}
|
|
|
|
|
2011-09-24 22:48:13 +00:00
|
|
|
static void about_dialog(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
const char *logo_property = NULL;
|
|
|
|
GdkPixbuf *logo = NULL;
|
|
|
|
|
2011-10-11 22:58:38 +00:00
|
|
|
if (need_icon) {
|
2011-12-28 23:57:36 +00:00
|
|
|
GtkWidget *image = gtk_image_new_from_file(subsurface_icon_name());
|
2011-10-11 22:58:38 +00:00
|
|
|
|
2012-09-23 14:07:57 +00:00
|
|
|
if (gtk_image_get_storage_type(GTK_IMAGE(image)) == GTK_IMAGE_PIXBUF) {
|
2011-10-11 22:58:38 +00:00
|
|
|
logo = gtk_image_get_pixbuf(GTK_IMAGE(image));
|
|
|
|
logo_property = "logo";
|
|
|
|
}
|
2011-09-24 22:48:13 +00:00
|
|
|
}
|
|
|
|
gtk_show_about_dialog(NULL,
|
2012-10-21 05:39:40 +00:00
|
|
|
"title", _("About Subsurface"),
|
2012-09-22 16:21:44 +00:00
|
|
|
"program-name", "Subsurface",
|
2012-10-11 00:42:59 +00:00
|
|
|
"comments", _("Multi-platform divelog software in C"),
|
2012-10-21 05:39:40 +00:00
|
|
|
"website", "http://subsurface.hohndel.org",
|
|
|
|
"license", "GNU General Public License, version 2\nhttp://www.gnu.org/licenses/old-licenses/gpl-2.0.html",
|
2011-09-26 18:04:50 +00:00
|
|
|
"version", VERSION_STRING,
|
2012-10-11 00:42:59 +00:00
|
|
|
"copyright", _("Linus Torvalds, Dirk Hohndel, and others, 2011, 2012"),
|
2012-10-22 03:59:43 +00:00
|
|
|
/*++GETTEXT the term translator-credits is magic - list the names of the tranlators here */
|
2012-10-21 05:39:40 +00:00
|
|
|
"translator_credits", _("translator-credits"),
|
2011-10-11 22:58:38 +00:00
|
|
|
"logo-icon-name", "subsurface",
|
2011-09-24 22:48:13 +00:00
|
|
|
/* Must be last: */
|
|
|
|
logo_property, logo,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
2011-12-06 21:00:01 +00:00
|
|
|
static void view_list(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
gtk_paned_set_position(GTK_PANED(vpane), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void view_profile(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
gtk_paned_set_position(GTK_PANED(hpane), 0);
|
|
|
|
gtk_paned_set_position(GTK_PANED(vpane), 65535);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void view_info(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
gtk_paned_set_position(GTK_PANED(vpane), 65535);
|
|
|
|
gtk_paned_set_position(GTK_PANED(hpane), 65535);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void view_three(GtkWidget *w, gpointer data)
|
|
|
|
{
|
2012-07-29 09:13:36 +00:00
|
|
|
GtkAllocation alloc;
|
2012-08-19 01:06:32 +00:00
|
|
|
GtkRequisition requisition;
|
|
|
|
|
2012-07-29 09:13:36 +00:00
|
|
|
gtk_widget_get_allocation(hpane, &alloc);
|
|
|
|
gtk_paned_set_position(GTK_PANED(hpane), alloc.width/2);
|
|
|
|
gtk_widget_get_allocation(vpane, &alloc);
|
2012-08-19 01:06:32 +00:00
|
|
|
gtk_widget_size_request(notebook, &requisition);
|
|
|
|
/* pick the requested size for the notebook plus 6 pixels for frame */
|
|
|
|
gtk_paned_set_position(GTK_PANED(vpane), requisition.height + 6);
|
2011-12-06 21:00:01 +00:00
|
|
|
}
|
|
|
|
|
2012-06-11 00:45:36 +00:00
|
|
|
static void toggle_zoom(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
zoomed_plot = (zoomed_plot)?0 : 1;
|
|
|
|
/*Update dive*/
|
|
|
|
repaint_dive();
|
|
|
|
}
|
|
|
|
|
2013-01-01 18:20:22 +00:00
|
|
|
static void prev_dc(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
dc_number--;
|
|
|
|
/* If the dc number underflows, we'll "wrap around" and use the last dc */
|
|
|
|
repaint_dive();
|
|
|
|
}
|
|
|
|
|
Add a "View next dive computer" menu item
This adds the capability to actually view all your dive computers, by
adding a menu item under "Log"->"View"->"Next DC" to show the next dive
computer.
Realistically, if you actually commonly use this, you'd use the
accelerator shortcut. Which right now is Ctrl-C ("C for Computer"),
which is probably a horrible choice.
I really would want to have nice "next/prev dive" accelerators too,
because the cursor keys don't work very well with the gtk focus issues.
Being able to switch between dives would also make the "just the dive
profile, maam" view (ctrl-2) much more useful.
The prev/next dive in the profile view should probably be done with a
keyboard action callback, which also avoids some of the limitations of
accelerators (ie you can make any key do the action). Some gtk person,
please?
Anyway, this commit only does the dive computer choice thing, and only
using the accelerators.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-17 17:37:07 +00:00
|
|
|
static void next_dc(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
dc_number++;
|
|
|
|
/* If the dc number overflows, we'll "wrap around" and zero it */
|
|
|
|
repaint_dive();
|
|
|
|
}
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
static GtkActionEntry menu_items[] = {
|
2012-10-11 00:42:59 +00:00
|
|
|
{ "FileMenuAction", NULL, N_("File"), NULL, NULL, NULL},
|
|
|
|
{ "LogMenuAction", NULL, N_("Log"), NULL, NULL, NULL},
|
|
|
|
{ "ViewMenuAction", NULL, N_("View"), NULL, NULL, NULL},
|
|
|
|
{ "FilterMenuAction", NULL, N_("Filter"), NULL, NULL, NULL},
|
|
|
|
{ "HelpMenuAction", NULL, N_("Help"), NULL, NULL, NULL},
|
2012-10-11 14:08:45 +00:00
|
|
|
{ "NewFile", GTK_STOCK_NEW, N_("New"), CTRLCHAR "N", NULL, G_CALLBACK(file_close) },
|
2012-10-21 07:48:38 +00:00
|
|
|
{ "OpenFile", GTK_STOCK_OPEN, N_("Open..."), CTRLCHAR "O", NULL, G_CALLBACK(file_open) },
|
|
|
|
{ "SaveFile", GTK_STOCK_SAVE, N_("Save..."), CTRLCHAR "S", NULL, G_CALLBACK(file_save) },
|
|
|
|
{ "SaveAsFile", GTK_STOCK_SAVE_AS, N_("Save As..."), SHIFTCHAR CTRLCHAR "S", NULL, G_CALLBACK(file_save_as) },
|
2012-10-11 14:08:45 +00:00
|
|
|
{ "CloseFile", GTK_STOCK_CLOSE, N_("Close"), NULL, NULL, G_CALLBACK(file_close) },
|
2012-10-21 07:48:38 +00:00
|
|
|
{ "Print", GTK_STOCK_PRINT, N_("Print..."), CTRLCHAR "P", NULL, G_CALLBACK(do_print) },
|
|
|
|
{ "ImportFile", GTK_STOCK_GO_BACK, N_("Import XML File(s)..."), CTRLCHAR "I", NULL, G_CALLBACK(import_files) },
|
|
|
|
{ "DownloadLog", GTK_STOCK_GO_DOWN, N_("Download From Dive Computer..."), CTRLCHAR "D", NULL, G_CALLBACK(download_dialog) },
|
|
|
|
{ "AddDive", GTK_STOCK_ADD, N_("Add Dive..."), NULL, NULL, G_CALLBACK(add_dive_cb) },
|
|
|
|
{ "Preferences", GTK_STOCK_PREFERENCES, N_("Preferences..."), PREFERENCE_ACCEL, NULL, G_CALLBACK(preferences_dialog) },
|
|
|
|
{ "Renumber", NULL, N_("Renumber..."), NULL, NULL, G_CALLBACK(renumber_dialog) },
|
2012-10-11 00:42:59 +00:00
|
|
|
{ "YearlyStats", NULL, N_("Yearly Statistics"), NULL, NULL, G_CALLBACK(show_yearly_stats) },
|
2012-10-25 16:32:23 +00:00
|
|
|
{ "SelectEvents", NULL, N_("Select Events..."), NULL, NULL, G_CALLBACK(selectevents_dialog) },
|
2012-10-11 14:08:45 +00:00
|
|
|
{ "Quit", GTK_STOCK_QUIT, N_("Quit"), CTRLCHAR "Q", NULL, G_CALLBACK(quit) },
|
2012-10-21 07:48:38 +00:00
|
|
|
{ "About", GTK_STOCK_ABOUT, N_("About Subsurface"), NULL, NULL, G_CALLBACK(about_dialog) },
|
2012-10-11 00:42:59 +00:00
|
|
|
{ "ViewList", NULL, N_("List"), CTRLCHAR "1", NULL, G_CALLBACK(view_list) },
|
|
|
|
{ "ViewProfile", NULL, N_("Profile"), CTRLCHAR "2", NULL, G_CALLBACK(view_profile) },
|
|
|
|
{ "ViewInfo", NULL, N_("Info"), CTRLCHAR "3", NULL, G_CALLBACK(view_info) },
|
|
|
|
{ "ViewThree", NULL, N_("Three"), CTRLCHAR "4", NULL, G_CALLBACK(view_three) },
|
2013-01-01 18:20:22 +00:00
|
|
|
{ "PrevDC", NULL, N_("Prev DC"), NULL, NULL, G_CALLBACK(prev_dc) },
|
|
|
|
{ "NextDC", NULL, N_("Next DC"), NULL, NULL, G_CALLBACK(next_dc) },
|
2011-09-20 19:40:34 +00:00
|
|
|
};
|
|
|
|
static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
|
|
|
|
|
2012-09-03 04:48:30 +00:00
|
|
|
static GtkToggleActionEntry toggle_items[] = {
|
2012-10-11 00:42:59 +00:00
|
|
|
{ "Autogroup", NULL, N_("Autogroup"), NULL, NULL, G_CALLBACK(autogroup_cb), FALSE },
|
|
|
|
{ "ToggleZoom", NULL, N_("Toggle Zoom"), CTRLCHAR "0", NULL, G_CALLBACK(toggle_zoom), FALSE },
|
2012-09-03 04:48:30 +00:00
|
|
|
};
|
|
|
|
static gint ntoggle_items = sizeof (toggle_items) / sizeof (toggle_items[0]);
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
static const gchar* ui_string = " \
|
|
|
|
<ui> \
|
|
|
|
<menubar name=\"MainMenu\"> \
|
|
|
|
<menu name=\"FileMenu\" action=\"FileMenuAction\"> \
|
2012-09-16 23:23:02 +00:00
|
|
|
<menuitem name=\"New\" action=\"NewFile\" /> \
|
2011-09-20 19:40:34 +00:00
|
|
|
<menuitem name=\"Open\" action=\"OpenFile\" /> \
|
|
|
|
<menuitem name=\"Save\" action=\"SaveFile\" /> \
|
2012-07-17 14:49:27 +00:00
|
|
|
<menuitem name=\"Save As\" action=\"SaveAsFile\" /> \
|
2012-09-10 21:32:55 +00:00
|
|
|
<menuitem name=\"Close\" action=\"CloseFile\" /> \
|
2011-09-20 19:40:34 +00:00
|
|
|
<separator name=\"Separator1\"/> \
|
2012-09-21 22:12:37 +00:00
|
|
|
<menuitem name=\"Import XML File\" action=\"ImportFile\" /> \
|
2012-05-02 19:56:01 +00:00
|
|
|
<separator name=\"Separator2\"/> \
|
2012-09-16 22:29:45 +00:00
|
|
|
<menuitem name=\"Print\" action=\"Print\" /> \
|
|
|
|
<separator name=\"Separator3\"/> \
|
|
|
|
<menuitem name=\"Preferences\" action=\"Preferences\" /> \
|
|
|
|
<separator name=\"Separator4\"/> \
|
2011-09-20 19:40:34 +00:00
|
|
|
<menuitem name=\"Quit\" action=\"Quit\" /> \
|
|
|
|
</menu> \
|
|
|
|
<menu name=\"LogMenu\" action=\"LogMenuAction\"> \
|
2012-09-21 22:12:37 +00:00
|
|
|
<menuitem name=\"Download From Dive Computer\" action=\"DownloadLog\" /> \
|
|
|
|
<separator name=\"Separator1\"/> \
|
2012-06-28 01:09:26 +00:00
|
|
|
<menuitem name=\"Add Dive\" action=\"AddDive\" /> \
|
2012-09-21 22:12:37 +00:00
|
|
|
<separator name=\"Separator2\"/> \
|
2011-09-20 19:40:34 +00:00
|
|
|
<menuitem name=\"Renumber\" action=\"Renumber\" /> \
|
2012-09-03 04:48:30 +00:00
|
|
|
<menuitem name=\"Autogroup\" action=\"Autogroup\" /> \
|
2012-06-11 00:45:36 +00:00
|
|
|
<menuitem name=\"Toggle Zoom\" action=\"ToggleZoom\" /> \
|
2012-09-10 19:17:28 +00:00
|
|
|
<menuitem name=\"YearlyStats\" action=\"YearlyStats\" /> \
|
2011-12-06 21:00:01 +00:00
|
|
|
<menu name=\"View\" action=\"ViewMenuAction\"> \
|
|
|
|
<menuitem name=\"List\" action=\"ViewList\" /> \
|
|
|
|
<menuitem name=\"Profile\" action=\"ViewProfile\" /> \
|
|
|
|
<menuitem name=\"Info\" action=\"ViewInfo\" /> \
|
|
|
|
<menuitem name=\"Paned\" action=\"ViewThree\" /> \
|
2013-01-01 18:20:22 +00:00
|
|
|
<menuitem name=\"PrevDC\" action=\"PrevDC\" /> \
|
Add a "View next dive computer" menu item
This adds the capability to actually view all your dive computers, by
adding a menu item under "Log"->"View"->"Next DC" to show the next dive
computer.
Realistically, if you actually commonly use this, you'd use the
accelerator shortcut. Which right now is Ctrl-C ("C for Computer"),
which is probably a horrible choice.
I really would want to have nice "next/prev dive" accelerators too,
because the cursor keys don't work very well with the gtk focus issues.
Being able to switch between dives would also make the "just the dive
profile, maam" view (ctrl-2) much more useful.
The prev/next dive in the profile view should probably be done with a
keyboard action callback, which also avoids some of the limitations of
accelerators (ie you can make any key do the action). Some gtk person,
please?
Anyway, this commit only does the dive computer choice thing, and only
using the accelerators.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-12-17 17:37:07 +00:00
|
|
|
<menuitem name=\"NextDC\" action=\"NextDC\" /> \
|
2011-12-06 21:00:01 +00:00
|
|
|
</menu> \
|
2011-09-20 19:40:34 +00:00
|
|
|
</menu> \
|
2011-10-25 09:51:16 +00:00
|
|
|
<menu name=\"FilterMenu\" action=\"FilterMenuAction\"> \
|
|
|
|
<menuitem name=\"SelectEvents\" action=\"SelectEvents\" /> \
|
|
|
|
</menu> \
|
2011-09-24 22:48:13 +00:00
|
|
|
<menu name=\"Help\" action=\"HelpMenuAction\"> \
|
|
|
|
<menuitem name=\"About\" action=\"About\" /> \
|
|
|
|
</menu> \
|
2011-09-20 19:40:34 +00:00
|
|
|
</menubar> \
|
|
|
|
</ui> \
|
|
|
|
";
|
|
|
|
|
2012-01-03 04:49:10 +00:00
|
|
|
static GtkWidget *get_menubar_menu(GtkWidget *window, GtkUIManager *ui_manager)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
2013-01-02 01:29:38 +00:00
|
|
|
action_group = gtk_action_group_new("Menu");
|
2012-10-11 00:42:59 +00:00
|
|
|
gtk_action_group_set_translation_domain(action_group, "subsurface");
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
|
2012-09-03 15:01:21 +00:00
|
|
|
toggle_items[0].is_active = autogroup;
|
2012-09-03 04:48:30 +00:00
|
|
|
gtk_action_group_add_toggle_actions(action_group, toggle_items, ntoggle_items, 0);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
gtk_ui_manager_insert_action_group(ui_manager, action_group, 0);
|
|
|
|
GError* error = 0;
|
|
|
|
gtk_ui_manager_add_ui_from_string(GTK_UI_MANAGER(ui_manager), ui_string, -1, &error);
|
|
|
|
|
|
|
|
gtk_window_add_accel_group(GTK_WINDOW(window), gtk_ui_manager_get_accel_group(ui_manager));
|
|
|
|
GtkWidget* menu = gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
|
|
|
|
|
|
|
|
return menu;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void switch_page(GtkNotebook *notebook, gint arg1, gpointer user_data)
|
|
|
|
{
|
|
|
|
repaint_dive();
|
|
|
|
}
|
|
|
|
|
2013-01-01 16:22:46 +00:00
|
|
|
static gboolean on_key_press(GtkWidget *w, GdkEventKey *event, GtkWidget *divelist)
|
|
|
|
{
|
|
|
|
if (event->type != GDK_KEY_PRESS)
|
|
|
|
return FALSE;
|
|
|
|
switch (event->keyval) {
|
2013-01-02 21:01:02 +00:00
|
|
|
case GDK_Up:
|
2013-01-01 16:22:46 +00:00
|
|
|
select_prev_dive();
|
|
|
|
return TRUE;
|
2013-01-02 21:01:02 +00:00
|
|
|
case GDK_Down:
|
2013-01-01 16:22:46 +00:00
|
|
|
select_next_dive();
|
|
|
|
return TRUE;
|
2013-01-02 21:01:02 +00:00
|
|
|
case GDK_Left:
|
2013-01-01 18:20:22 +00:00
|
|
|
prev_dc(NULL, NULL);
|
|
|
|
return TRUE;
|
2013-01-02 21:01:02 +00:00
|
|
|
case GDK_Right:
|
2013-01-01 18:20:22 +00:00
|
|
|
next_dc(NULL, NULL);
|
|
|
|
return TRUE;
|
2013-01-01 16:22:46 +00:00
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2011-11-02 00:09:15 +00:00
|
|
|
void init_ui(int *argcp, char ***argvp)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
|
|
|
GtkWidget *win;
|
2012-01-15 22:29:08 +00:00
|
|
|
GtkWidget *nb_page;
|
2011-09-20 19:40:34 +00:00
|
|
|
GtkWidget *dive_list;
|
|
|
|
GtkWidget *menubar;
|
|
|
|
GtkWidget *vbox;
|
Make the notebook portion (dive notes/equipment/info) a scrollable window
This makes things start up with the wrong size, which is somewhat
annoying, but by doing so avoids a bigger annoyance, namely that the
three panes move around when moving between dives.
In particular, if the initial dive didn't have much of an equipment
list, the initial size allocated for the notebook is fairly small and
determined mainly by the size of the the Dive Notes page. However, when
you then scroll around in the dive list, you might hit a dive with lots
of equipment, and suddenly the panes dividing the different parts of the
subsurface application window will jump around to make room.
That's horribly annoying, and actually makes things like double-clicking
dives in the dive list not work right, because the first click will
select it, and cause the dive to move around (so the second click will
hit a totally different dive).
Now, making the notebook be in a scrollable window means that if the
size of the notebook changes, it might get a scrollbar, but the panes
themselves do not move around.
The initial sizing of that thing being wrong is annoying, though. We
need to figure out a separate solution to that.
[ Side note: currently it uses GTK_POLICY_NEVER for the horizontal
scroll-bar, just to avoid the horizontal size also starting out wrong,
which is *really* nasty. If we can solve the initial size issue, we
should make the horizontal scroll-bar be GTK_POLICY_AUTOMATIC too. ]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-18 21:37:43 +00:00
|
|
|
GtkWidget *scrolled;
|
2011-10-11 22:58:38 +00:00
|
|
|
GdkScreen *screen;
|
|
|
|
GtkIconTheme *icon_theme=NULL;
|
2011-10-04 23:06:27 +00:00
|
|
|
GtkSettings *settings;
|
2012-01-03 04:49:10 +00:00
|
|
|
GtkUIManager *ui_manager;
|
2012-09-25 14:28:47 +00:00
|
|
|
const char *conf_value;
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2011-11-02 00:09:15 +00:00
|
|
|
gtk_init(argcp, argvp);
|
2011-10-04 23:06:27 +00:00
|
|
|
settings = gtk_settings_get_default();
|
2012-09-29 23:13:50 +00:00
|
|
|
gtk_settings_set_long_property(settings, "gtk-tooltip-timeout", 10, "subsurface setting");
|
|
|
|
gtk_settings_set_long_property(settings, "gtk-menu-images", 1, "subsurface setting");
|
|
|
|
gtk_settings_set_long_property(settings, "gtk-button-images", 1, "subsurface setting");
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-10-19 23:31:07 +00:00
|
|
|
/* check if utf8 stars are available as a default OS feature */
|
|
|
|
if (!subsurface_os_feature_available(UTF8_FONT_WITH_STARS)) {
|
|
|
|
star_strings[0] = " ";
|
|
|
|
star_strings[1] = "* ";
|
|
|
|
star_strings[2] = "** ";
|
|
|
|
star_strings[3] = "*** ";
|
|
|
|
star_strings[4] = "**** ";
|
|
|
|
star_strings[5] = "*****";
|
|
|
|
}
|
2011-09-20 19:40:34 +00:00
|
|
|
g_type_init();
|
2011-10-28 05:10:35 +00:00
|
|
|
|
2011-11-24 06:56:57 +00:00
|
|
|
subsurface_open_conf();
|
|
|
|
if (subsurface_get_conf("feet", PREF_BOOL))
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.output_units.length = FEET;
|
2011-11-24 06:56:57 +00:00
|
|
|
if (subsurface_get_conf("psi", PREF_BOOL))
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.output_units.pressure = PSI;
|
2011-11-24 06:56:57 +00:00
|
|
|
if (subsurface_get_conf("cuft", PREF_BOOL))
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.output_units.volume = CUFT;
|
2011-11-24 06:56:57 +00:00
|
|
|
if (subsurface_get_conf("fahrenheit", PREF_BOOL))
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.output_units.temperature = FAHRENHEIT;
|
2011-12-24 03:41:16 +00:00
|
|
|
if (subsurface_get_conf("lbs", PREF_BOOL))
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.output_units.weight = LBS;
|
2011-10-23 20:36:37 +00:00
|
|
|
/* an unset key is FALSE - all these are hidden by default */
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.visible_cols.cylinder = PTR_TO_BOOL(subsurface_get_conf("CYLINDER", PREF_BOOL));
|
|
|
|
prefs.visible_cols.temperature = PTR_TO_BOOL(subsurface_get_conf("TEMPERATURE", PREF_BOOL));
|
|
|
|
prefs.visible_cols.totalweight = PTR_TO_BOOL(subsurface_get_conf("TOTALWEIGHT", PREF_BOOL));
|
|
|
|
prefs.visible_cols.suit = PTR_TO_BOOL(subsurface_get_conf("SUIT", PREF_BOOL));
|
|
|
|
prefs.visible_cols.nitrox = PTR_TO_BOOL(subsurface_get_conf("NITROX", PREF_BOOL));
|
|
|
|
prefs.visible_cols.otu = PTR_TO_BOOL(subsurface_get_conf("OTU", PREF_BOOL));
|
2012-12-11 21:09:48 +00:00
|
|
|
prefs.visible_cols.maxcns = PTR_TO_BOOL(subsurface_get_conf("MAXCNS", PREF_BOOL));
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.visible_cols.sac = PTR_TO_BOOL(subsurface_get_conf("SAC", PREF_BOOL));
|
|
|
|
prefs.pp_graphs.po2 = PTR_TO_BOOL(subsurface_get_conf("po2graph", PREF_BOOL));
|
|
|
|
prefs.pp_graphs.pn2 = PTR_TO_BOOL(subsurface_get_conf("pn2graph", PREF_BOOL));
|
|
|
|
prefs.pp_graphs.phe = PTR_TO_BOOL(subsurface_get_conf("phegraph", PREF_BOOL));
|
2012-11-10 16:58:42 +00:00
|
|
|
conf_value = subsurface_get_conf("po2threshold", PREF_STRING);
|
2012-12-16 22:26:35 +00:00
|
|
|
if (conf_value) {
|
2012-12-10 17:20:57 +00:00
|
|
|
sscanf(conf_value, "%lf", &prefs.pp_graphs.po2_threshold);
|
2012-12-16 22:26:35 +00:00
|
|
|
free((void *)conf_value);
|
|
|
|
}
|
2012-11-10 16:58:42 +00:00
|
|
|
conf_value = subsurface_get_conf("pn2threshold", PREF_STRING);
|
2012-12-16 22:26:35 +00:00
|
|
|
if (conf_value) {
|
2012-12-10 17:20:57 +00:00
|
|
|
sscanf(conf_value, "%lf", &prefs.pp_graphs.pn2_threshold);
|
2012-12-16 22:26:35 +00:00
|
|
|
free((void *)conf_value);
|
|
|
|
}
|
2012-11-10 16:58:42 +00:00
|
|
|
conf_value = subsurface_get_conf("phethreshold", PREF_STRING);
|
2012-12-16 22:26:35 +00:00
|
|
|
if (conf_value) {
|
2012-12-10 17:20:57 +00:00
|
|
|
sscanf(conf_value, "%lf", &prefs.pp_graphs.phe_threshold);
|
2012-12-16 22:26:35 +00:00
|
|
|
free((void *)conf_value);
|
|
|
|
}
|
2012-12-10 17:20:57 +00:00
|
|
|
prefs.profile_red_ceiling = PTR_TO_BOOL(subsurface_get_conf("redceiling", PREF_BOOL));
|
2013-01-03 06:45:21 +00:00
|
|
|
prefs.profile_calc_ceiling = PTR_TO_BOOL(subsurface_get_conf("calcceiling", PREF_BOOL));
|
2011-11-24 06:56:57 +00:00
|
|
|
divelist_font = subsurface_get_conf("divelist_font", PREF_STRING);
|
2013-01-02 01:29:38 +00:00
|
|
|
|
2012-09-09 16:06:44 +00:00
|
|
|
default_filename = subsurface_get_conf("default_filename", PREF_STRING);
|
2012-08-22 05:04:24 +00:00
|
|
|
|
2012-06-22 20:37:39 +00:00
|
|
|
default_dive_computer_vendor = subsurface_get_conf("dive_computer_vendor", PREF_STRING);
|
|
|
|
default_dive_computer_product = subsurface_get_conf("dive_computer_product", PREF_STRING);
|
2012-05-30 04:54:09 +00:00
|
|
|
default_dive_computer_device = subsurface_get_conf("dive_computer_device", PREF_STRING);
|
2012-12-13 06:26:29 +00:00
|
|
|
conf_value = subsurface_get_conf("dc_nicknames", PREF_STRING);
|
|
|
|
nicknamestring = strdup("");
|
|
|
|
if (conf_value) {
|
2012-12-23 04:19:51 +00:00
|
|
|
char *next_token, *nickname, *model, *conf_copy;
|
2012-12-13 06:26:29 +00:00
|
|
|
uint32_t deviceid;
|
|
|
|
int len;
|
2012-12-23 04:19:51 +00:00
|
|
|
next_token = conf_copy = strdup(conf_value);
|
2012-12-13 06:26:29 +00:00
|
|
|
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 = '(';
|
2012-12-22 05:00:06 +00:00
|
|
|
/* the next 8 chars are the deviceid, after that we have the model
|
|
|
|
* and the utf8 nickname (if set) */
|
2012-12-13 06:26:29 +00:00
|
|
|
if (sscanf(next_token, "(%8x,", &deviceid) > 0) {
|
2012-12-22 05:00:06 +00:00
|
|
|
char *namestart, *nameend, *tupleend;
|
2012-12-13 06:26:29 +00:00
|
|
|
namestart = g_utf8_strchr(next_token, len, ',');
|
2012-12-22 05:00:06 +00:00
|
|
|
/* we know that namestart isn't NULL based on the sscanf succeeding */
|
|
|
|
len = strlen(namestart + 1);
|
|
|
|
nameend = g_utf8_strchr(namestart + 1, len, ',');
|
|
|
|
tupleend = g_utf8_strchr(namestart + 1, len, '}');
|
2012-12-23 05:37:13 +00:00
|
|
|
if (!tupleend)
|
2012-12-22 05:00:06 +00:00
|
|
|
/* the config entry is messed up - bail */
|
|
|
|
break;
|
|
|
|
if (!nameend || tupleend < nameend)
|
|
|
|
nameend = tupleend;
|
2012-12-13 06:26:29 +00:00
|
|
|
*nameend = '\0';
|
2012-12-22 05:00:06 +00:00
|
|
|
model = strdup(namestart + 1);
|
|
|
|
if (tupleend == nameend) {
|
|
|
|
/* no nickname */
|
|
|
|
nickname = strdup("");
|
|
|
|
} else {
|
|
|
|
namestart = nameend + 1;
|
|
|
|
len = strlen(namestart);
|
|
|
|
nameend = g_utf8_strchr(namestart, len, '}');
|
|
|
|
if (!nameend)
|
|
|
|
break;
|
|
|
|
*nameend = '\0';
|
|
|
|
nickname = strdup(namestart);
|
|
|
|
}
|
2012-12-26 21:47:54 +00:00
|
|
|
remember_dc(model, deviceid, nickname, FALSE);
|
2012-12-13 06:26:29 +00:00
|
|
|
next_token = nameend + 1;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
free((void *)conf_value);
|
2012-12-23 04:19:51 +00:00
|
|
|
free(conf_copy);
|
2012-12-13 06:26:29 +00:00
|
|
|
}
|
2011-09-20 19:40:34 +00:00
|
|
|
error_info_bar = NULL;
|
|
|
|
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
2011-10-11 22:58:38 +00:00
|
|
|
g_set_application_name ("subsurface");
|
|
|
|
/* Let's check if the subsurface icon has been installed or if
|
|
|
|
* we need to try to load it from the current directory */
|
|
|
|
screen = gdk_screen_get_default();
|
|
|
|
if (screen)
|
|
|
|
icon_theme = gtk_icon_theme_get_for_screen(screen);
|
|
|
|
if (icon_theme) {
|
|
|
|
if (gtk_icon_theme_has_icon(icon_theme, "subsurface")) {
|
|
|
|
need_icon = FALSE;
|
|
|
|
gtk_window_set_default_icon_name ("subsurface");
|
|
|
|
}
|
|
|
|
}
|
2012-01-03 04:15:24 +00:00
|
|
|
if (need_icon) {
|
|
|
|
const char *icon_name = subsurface_icon_name();
|
|
|
|
if (!access(icon_name, R_OK))
|
|
|
|
gtk_window_set_icon_from_file(GTK_WINDOW(win), icon_name, NULL);
|
|
|
|
}
|
2011-09-27 17:38:07 +00:00
|
|
|
g_signal_connect(G_OBJECT(win), "delete-event", G_CALLBACK(on_delete), NULL);
|
2011-09-20 19:40:34 +00:00
|
|
|
g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(on_destroy), NULL);
|
|
|
|
main_window = win;
|
|
|
|
|
|
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
|
|
|
gtk_container_add(GTK_CONTAINER(win), vbox);
|
|
|
|
main_vbox = vbox;
|
|
|
|
|
2012-01-03 04:49:10 +00:00
|
|
|
ui_manager = gtk_ui_manager_new();
|
|
|
|
menubar = get_menubar_menu(win, ui_manager);
|
2012-01-01 21:41:47 +00:00
|
|
|
|
2012-01-03 04:49:10 +00:00
|
|
|
subsurface_ui_setup(settings, menubar, vbox, ui_manager);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2011-11-19 20:54:58 +00:00
|
|
|
vpane = gtk_vpaned_new();
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), vpane, TRUE, TRUE, 3);
|
|
|
|
hpane = gtk_hpaned_new();
|
|
|
|
gtk_paned_add1(GTK_PANED(vpane), hpane);
|
Make the notebook portion (dive notes/equipment/info) a scrollable window
This makes things start up with the wrong size, which is somewhat
annoying, but by doing so avoids a bigger annoyance, namely that the
three panes move around when moving between dives.
In particular, if the initial dive didn't have much of an equipment
list, the initial size allocated for the notebook is fairly small and
determined mainly by the size of the the Dive Notes page. However, when
you then scroll around in the dive list, you might hit a dive with lots
of equipment, and suddenly the panes dividing the different parts of the
subsurface application window will jump around to make room.
That's horribly annoying, and actually makes things like double-clicking
dives in the dive list not work right, because the first click will
select it, and cause the dive to move around (so the second click will
hit a totally different dive).
Now, making the notebook be in a scrollable window means that if the
size of the notebook changes, it might get a scrollbar, but the panes
themselves do not move around.
The initial sizing of that thing being wrong is annoying, though. We
need to figure out a separate solution to that.
[ Side note: currently it uses GTK_POLICY_NEVER for the horizontal
scroll-bar, just to avoid the horizontal size also starting out wrong,
which is *really* nasty. If we can solve the initial size issue, we
should make the horizontal scroll-bar be GTK_POLICY_AUTOMATIC too. ]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-18 21:37:43 +00:00
|
|
|
g_signal_connect_after(G_OBJECT(vbox), "realize", G_CALLBACK(view_three), NULL);
|
2011-11-19 20:54:58 +00:00
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
/* Notebook for dive info vs profile vs .. */
|
|
|
|
notebook = gtk_notebook_new();
|
Make the notebook portion (dive notes/equipment/info) a scrollable window
This makes things start up with the wrong size, which is somewhat
annoying, but by doing so avoids a bigger annoyance, namely that the
three panes move around when moving between dives.
In particular, if the initial dive didn't have much of an equipment
list, the initial size allocated for the notebook is fairly small and
determined mainly by the size of the the Dive Notes page. However, when
you then scroll around in the dive list, you might hit a dive with lots
of equipment, and suddenly the panes dividing the different parts of the
subsurface application window will jump around to make room.
That's horribly annoying, and actually makes things like double-clicking
dives in the dive list not work right, because the first click will
select it, and cause the dive to move around (so the second click will
hit a totally different dive).
Now, making the notebook be in a scrollable window means that if the
size of the notebook changes, it might get a scrollbar, but the panes
themselves do not move around.
The initial sizing of that thing being wrong is annoying, though. We
need to figure out a separate solution to that.
[ Side note: currently it uses GTK_POLICY_NEVER for the horizontal
scroll-bar, just to avoid the horizontal size also starting out wrong,
which is *really* nasty. If we can solve the initial size issue, we
should make the horizontal scroll-bar be GTK_POLICY_AUTOMATIC too. ]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2012-08-18 21:37:43 +00:00
|
|
|
scrolled = gtk_scrolled_window_new(NULL, NULL);
|
|
|
|
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
|
|
|
gtk_paned_add1(GTK_PANED(hpane), scrolled);
|
|
|
|
gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), notebook);
|
2011-09-20 19:40:34 +00:00
|
|
|
g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL);
|
2011-09-27 17:38:07 +00:00
|
|
|
|
|
|
|
/* Create the actual divelist */
|
|
|
|
dive_list = dive_list_create();
|
2011-09-27 23:23:59 +00:00
|
|
|
gtk_widget_set_name(dive_list, "Dive List");
|
2011-11-19 20:54:58 +00:00
|
|
|
gtk_paned_add2(GTK_PANED(vpane), dive_list);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
/* Frame for dive profile */
|
|
|
|
dive_profile = dive_profile_widget();
|
2011-09-27 23:23:59 +00:00
|
|
|
gtk_widget_set_name(dive_profile, "Dive Profile");
|
2011-11-19 20:54:58 +00:00
|
|
|
gtk_paned_add2(GTK_PANED(hpane), dive_profile);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
/* Frame for extended dive info */
|
2012-01-15 22:29:08 +00:00
|
|
|
nb_page = extended_dive_info_widget();
|
2012-10-11 00:42:59 +00:00
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Dive Notes")));
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
/* Frame for dive equipment */
|
2012-08-15 22:21:34 +00:00
|
|
|
nb_page = equipment_widget(W_IDX_PRIMARY);
|
2012-10-11 00:42:59 +00:00
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Equipment")));
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-01-15 22:29:08 +00:00
|
|
|
/* Frame for single dive statistics */
|
|
|
|
nb_page = single_stats_widget();
|
2012-10-11 00:42:59 +00:00
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Dive Info")));
|
2012-01-15 22:29:08 +00:00
|
|
|
|
|
|
|
/* Frame for total dive statistics */
|
|
|
|
nb_page = total_stats_widget();
|
2012-10-11 00:42:59 +00:00
|
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), nb_page, gtk_label_new(_("Stats")));
|
2011-11-02 16:10:57 +00:00
|
|
|
|
2013-01-01 16:22:46 +00:00
|
|
|
/* handle some keys globally (to deal with gtk focus issues) */
|
|
|
|
g_signal_connect (G_OBJECT (win), "key_press_event", G_CALLBACK (on_key_press), dive_list);
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_widget_set_app_paintable(win, TRUE);
|
|
|
|
gtk_widget_show_all(win);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_ui(void)
|
|
|
|
{
|
|
|
|
gtk_main();
|
|
|
|
}
|
|
|
|
|
2012-05-02 17:03:48 +00:00
|
|
|
void exit_ui(void)
|
|
|
|
{
|
|
|
|
subsurface_close_conf();
|
2012-09-12 20:40:22 +00:00
|
|
|
if (default_filename)
|
|
|
|
free((char *)default_filename);
|
2012-09-13 17:46:09 +00:00
|
|
|
if (existing_filename)
|
2012-09-16 03:51:06 +00:00
|
|
|
free((void *)existing_filename);
|
2012-09-22 16:07:50 +00:00
|
|
|
if (default_dive_computer_device)
|
|
|
|
free((void *)default_dive_computer_device);
|
2012-05-02 17:03:48 +00:00
|
|
|
}
|
|
|
|
|
2011-10-04 19:27:55 +00:00
|
|
|
typedef struct {
|
2012-09-10 22:31:01 +00:00
|
|
|
cairo_rectangle_t rect;
|
2011-10-04 19:27:55 +00:00
|
|
|
const char *text;
|
|
|
|
} tooltip_record_t;
|
|
|
|
|
|
|
|
static tooltip_record_t *tooltip_rects;
|
|
|
|
static int tooltips;
|
|
|
|
|
|
|
|
void attach_tooltip(int x, int y, int w, int h, const char *text)
|
|
|
|
{
|
2012-09-10 22:31:01 +00:00
|
|
|
cairo_rectangle_t *rect;
|
2011-10-04 19:27:55 +00:00
|
|
|
tooltip_rects = realloc(tooltip_rects, (tooltips + 1) * sizeof(tooltip_record_t));
|
|
|
|
rect = &tooltip_rects[tooltips].rect;
|
|
|
|
rect->x = x;
|
|
|
|
rect->y = y;
|
|
|
|
rect->width = w;
|
|
|
|
rect->height = h;
|
2012-11-10 10:40:35 +00:00
|
|
|
tooltip_rects[tooltips].text = strdup(text);
|
2011-10-04 19:27:55 +00:00
|
|
|
tooltips++;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define INSIDE_RECT(_r,_x,_y) ((_r.x <= _x) && (_r.x + _r.width >= _x) && \
|
|
|
|
(_r.y <= _y) && (_r.y + _r.height >= _y))
|
|
|
|
|
|
|
|
static gboolean profile_tooltip (GtkWidget *widget, gint x, gint y,
|
2012-11-11 12:20:32 +00:00
|
|
|
gboolean keyboard_mode, GtkTooltip *tooltip, struct graphics_context *gc)
|
2011-10-04 19:27:55 +00:00
|
|
|
{
|
|
|
|
int i;
|
2012-11-11 12:20:32 +00:00
|
|
|
cairo_rectangle_t *drawing_area = &gc->drawing_area;
|
2011-10-04 19:27:55 +00:00
|
|
|
gint tx = x - drawing_area->x; /* get transformed coordinates */
|
|
|
|
gint ty = y - drawing_area->y;
|
2012-11-11 12:20:32 +00:00
|
|
|
gint width, height, time = -1;
|
2012-12-01 21:02:30 +00:00
|
|
|
char buffer[256], plot[256];
|
2012-11-11 12:20:32 +00:00
|
|
|
const char *event = "";
|
|
|
|
|
|
|
|
if (tx < 0 || ty < 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2012-12-10 19:02:33 +00:00
|
|
|
/* don't draw a tooltip if nothing is there */
|
|
|
|
if (gc->pi.nr == 0)
|
|
|
|
return FALSE;
|
|
|
|
|
2012-11-11 12:20:32 +00:00
|
|
|
width = drawing_area->width - 2*drawing_area->x;
|
|
|
|
height = drawing_area->height - 2*drawing_area->y;
|
|
|
|
if (width <= 0 || height <= 0)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (tx > width || ty > height)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
time = (tx * gc->maxtime) / width;
|
2011-10-04 19:27:55 +00:00
|
|
|
|
|
|
|
/* are we over an event marker ? */
|
|
|
|
for (i = 0; i < tooltips; i++) {
|
|
|
|
if (INSIDE_RECT(tooltip_rects[i].rect, tx, ty)) {
|
2012-11-11 12:20:32 +00:00
|
|
|
event = tooltip_rects[i].text;
|
|
|
|
break;
|
2011-10-04 19:27:55 +00:00
|
|
|
}
|
|
|
|
}
|
2012-11-11 12:20:32 +00:00
|
|
|
get_plot_details(gc, time, plot, sizeof(plot));
|
|
|
|
|
2012-11-11 16:12:09 +00:00
|
|
|
snprintf(buffer, sizeof(buffer), "@ %d:%02d%c%s%c%s", time / 60, time % 60,
|
2012-11-11 12:20:32 +00:00
|
|
|
*plot ? '\n' : ' ', plot,
|
|
|
|
*event ? '\n' : ' ', event);
|
|
|
|
gtk_tooltip_set_text(tooltip, buffer);
|
|
|
|
return TRUE;
|
|
|
|
|
2011-10-04 19:27:55 +00:00
|
|
|
}
|
|
|
|
|
2012-11-13 00:44:16 +00:00
|
|
|
static double zoom_factor = 1.0;
|
2012-11-10 16:25:19 +00:00
|
|
|
static int zoom_x = -1, zoom_y = -1;
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
|
|
|
|
{
|
2012-12-16 22:26:36 +00:00
|
|
|
int i = 0;
|
2011-09-20 19:40:34 +00:00
|
|
|
struct dive *dive = current_dive;
|
2012-11-11 12:20:32 +00:00
|
|
|
static struct graphics_context gc = { .printer = 0 };
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2011-10-04 19:14:26 +00:00
|
|
|
/* the drawing area gives TOTAL width * height - x,y is used as the topx/topy offset
|
|
|
|
* so effective drawing area is width-2x * height-2y */
|
2012-11-11 12:20:32 +00:00
|
|
|
gc.drawing_area.width = widget->allocation.width;
|
|
|
|
gc.drawing_area.height = widget->allocation.height;
|
|
|
|
gc.drawing_area.x = MIN(50,gc.drawing_area.width / 20.0);
|
|
|
|
gc.drawing_area.y = MIN(50,gc.drawing_area.height / 20.0);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
gc.cr = gdk_cairo_create(widget->window);
|
2011-10-04 19:27:55 +00:00
|
|
|
g_object_set(widget, "has-tooltip", TRUE, NULL);
|
2012-11-11 12:20:32 +00:00
|
|
|
g_signal_connect(widget, "query-tooltip", G_CALLBACK(profile_tooltip), &gc);
|
2011-11-28 17:17:33 +00:00
|
|
|
init_profile_background(&gc);
|
2011-09-20 19:40:34 +00:00
|
|
|
cairo_paint(gc.cr);
|
|
|
|
|
2012-11-13 00:44:16 +00:00
|
|
|
if (zoom_factor > 1.0) {
|
|
|
|
double n = -(zoom_factor-1);
|
|
|
|
cairo_translate(gc.cr, n*zoom_x, n*zoom_y);
|
|
|
|
cairo_scale(gc.cr, zoom_factor, zoom_factor);
|
2012-11-10 16:25:19 +00:00
|
|
|
}
|
|
|
|
|
2011-10-04 19:27:55 +00:00
|
|
|
if (dive) {
|
|
|
|
if (tooltip_rects) {
|
2012-12-16 22:26:36 +00:00
|
|
|
while (i < tooltips) {
|
|
|
|
if (tooltip_rects[i].text)
|
|
|
|
free((void *)tooltip_rects[i].text);
|
|
|
|
i++;
|
|
|
|
}
|
2011-10-04 19:27:55 +00:00
|
|
|
free(tooltip_rects);
|
|
|
|
tooltip_rects = NULL;
|
|
|
|
}
|
|
|
|
tooltips = 0;
|
2012-11-11 12:20:32 +00:00
|
|
|
plot(&gc, dive, SC_SCREEN);
|
2011-10-04 19:27:55 +00:00
|
|
|
}
|
2011-09-20 19:40:34 +00:00
|
|
|
cairo_destroy(gc.cr);
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2012-11-13 00:44:16 +00:00
|
|
|
static void zoom_event(int x, int y, double inc)
|
|
|
|
{
|
|
|
|
zoom_x = x;
|
|
|
|
zoom_y = y;
|
|
|
|
inc += zoom_factor;
|
|
|
|
if (inc < 1.0)
|
|
|
|
inc = 1.0;
|
|
|
|
else if (inc > 10)
|
|
|
|
inc = 10;
|
|
|
|
zoom_factor = inc;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
|
|
|
|
{
|
|
|
|
switch (event->direction) {
|
|
|
|
case GDK_SCROLL_UP:
|
|
|
|
zoom_event(event->x, event->y, 0.1);
|
|
|
|
break;
|
|
|
|
case GDK_SCROLL_DOWN:
|
|
|
|
zoom_event(event->x, event->y, -0.1);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
gtk_widget_queue_draw(widget);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2012-11-10 16:25:19 +00:00
|
|
|
gboolean clicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
|
|
|
|
{
|
|
|
|
switch (event->button) {
|
2012-11-11 13:45:33 +00:00
|
|
|
case 1:
|
2012-11-10 16:25:19 +00:00
|
|
|
zoom_x = event->x;
|
|
|
|
zoom_y = event->y;
|
2012-11-13 00:44:16 +00:00
|
|
|
zoom_factor = 2.5;
|
2012-11-10 16:25:19 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
gtk_widget_queue_draw(widget);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean released(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
|
|
|
|
{
|
|
|
|
switch (event->button) {
|
2012-11-11 13:45:33 +00:00
|
|
|
case 1:
|
2012-11-10 16:25:19 +00:00
|
|
|
zoom_x = zoom_y = -1;
|
2012-11-13 00:44:16 +00:00
|
|
|
zoom_factor = 1.0;
|
2012-11-10 16:25:19 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
gtk_widget_queue_draw(widget);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean motion(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
|
|
|
|
{
|
|
|
|
if (zoom_x < 0)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
zoom_x = event->x;
|
|
|
|
zoom_y = event->y;
|
|
|
|
gtk_widget_queue_draw(widget);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
GtkWidget *dive_profile_widget(void)
|
|
|
|
{
|
|
|
|
GtkWidget *da;
|
|
|
|
|
|
|
|
da = gtk_drawing_area_new();
|
|
|
|
gtk_widget_set_size_request(da, 350, 250);
|
|
|
|
g_signal_connect(da, "expose_event", G_CALLBACK(expose_event), NULL);
|
2012-11-10 16:25:19 +00:00
|
|
|
g_signal_connect(da, "button-press-event", G_CALLBACK(clicked), NULL);
|
2012-11-13 00:44:16 +00:00
|
|
|
g_signal_connect(da, "scroll-event", G_CALLBACK(scroll_event), NULL);
|
2012-11-10 16:25:19 +00:00
|
|
|
g_signal_connect(da, "button-release-event", G_CALLBACK(released), NULL);
|
|
|
|
g_signal_connect(da, "motion-notify-event", G_CALLBACK(motion), NULL);
|
2012-11-13 00:44:16 +00:00
|
|
|
gtk_widget_add_events(da, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
return da;
|
|
|
|
}
|
|
|
|
|
|
|
|
int process_ui_events(void)
|
|
|
|
{
|
|
|
|
int ret=0;
|
|
|
|
|
|
|
|
while (gtk_events_pending()) {
|
|
|
|
if (gtk_main_iteration_do(0)) {
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-12-15 04:49:40 +00:00
|
|
|
return ret;
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
struct vendor {
|
|
|
|
const char *vendor;
|
|
|
|
struct product *productlist;
|
|
|
|
struct vendor *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct product {
|
|
|
|
const char *product;
|
|
|
|
dc_descriptor_t *descriptor;
|
|
|
|
struct product *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct vendor *dc_list;
|
|
|
|
|
2012-09-25 14:28:47 +00:00
|
|
|
struct mydescriptor {
|
|
|
|
const char *vendor;
|
|
|
|
const char *product;
|
|
|
|
dc_family_t type;
|
|
|
|
unsigned int model;
|
|
|
|
};
|
|
|
|
|
2012-12-07 06:54:16 +00:00
|
|
|
/* create a list of lists and keep the elements sorted */
|
2012-12-06 01:03:58 +00:00
|
|
|
static void add_dc(const char *vendor, const char *product, dc_descriptor_t *descriptor)
|
|
|
|
{
|
|
|
|
struct vendor *dcl = dc_list;
|
|
|
|
struct vendor **dclp = &dc_list;
|
|
|
|
struct product *pl, **plp;
|
|
|
|
|
|
|
|
if (!vendor || !product)
|
|
|
|
return;
|
2012-12-07 06:54:16 +00:00
|
|
|
while (dcl && strcmp(dcl->vendor, vendor) < 0) {
|
2012-12-06 01:03:58 +00:00
|
|
|
dclp = &dcl->next;
|
|
|
|
dcl = dcl->next;
|
|
|
|
}
|
2012-12-07 06:54:16 +00:00
|
|
|
if (!dcl || strcmp(dcl->vendor, vendor)) {
|
|
|
|
dcl = calloc(sizeof(struct vendor), 1);
|
|
|
|
dcl->next = *dclp;
|
|
|
|
*dclp = dcl;
|
2012-12-06 01:03:58 +00:00
|
|
|
dcl->vendor = strdup(vendor);
|
|
|
|
}
|
|
|
|
/* we now have a pointer to the requested vendor */
|
|
|
|
plp = &dcl->productlist;
|
|
|
|
pl = *plp;
|
2012-12-07 06:54:16 +00:00
|
|
|
while (pl && strcmp(pl->product, product) < 0) {
|
2012-12-06 01:03:58 +00:00
|
|
|
plp = &pl->next;
|
|
|
|
pl = pl->next;
|
|
|
|
}
|
2012-12-07 06:54:16 +00:00
|
|
|
if (!pl || strcmp(pl->product, product)) {
|
|
|
|
pl = calloc(sizeof(struct product), 1);
|
|
|
|
pl->next = *plp;
|
|
|
|
*plp = pl;
|
2012-12-06 01:03:58 +00:00
|
|
|
pl->product = strdup(product);
|
|
|
|
}
|
|
|
|
/* one would assume that the vendor / product combinations are unique,
|
|
|
|
* but that is not the case. At the time of this writing, there are two
|
|
|
|
* flavors of the Oceanic OC1 - but looking at the code in libdivecomputer
|
|
|
|
* they are handled exactly the same, so we ignore this issue for now
|
|
|
|
*
|
|
|
|
if (pl->descriptor && memcmp(pl->descriptor, descriptor, sizeof(struct mydescriptor)))
|
|
|
|
printf("duplicate entry with different descriptor for %s - %s\n", vendor, product);
|
|
|
|
else
|
|
|
|
*/
|
|
|
|
pl->descriptor = descriptor;
|
|
|
|
}
|
|
|
|
|
2012-12-07 06:54:16 +00:00
|
|
|
/* fill the vendors and create and fill the respective product stores; return the longest product name
|
|
|
|
* and also the indices of the default vendor / product */
|
|
|
|
static int fill_computer_list(GtkListStore *vendorstore, GtkListStore ***productstore, int *vendor_index, int *product_index)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
2012-12-07 06:54:16 +00:00
|
|
|
int i, j, numvendor, width = 10;
|
2011-09-20 19:40:34 +00:00
|
|
|
GtkTreeIter iter;
|
2012-06-22 20:37:39 +00:00
|
|
|
dc_iterator_t *iterator = NULL;
|
|
|
|
dc_descriptor_t *descriptor = NULL;
|
2012-09-25 14:28:47 +00:00
|
|
|
struct mydescriptor *mydescriptor;
|
2012-12-06 01:03:58 +00:00
|
|
|
struct vendor *dcl;
|
|
|
|
struct product *pl;
|
|
|
|
GtkListStore **pstores;
|
2012-06-22 20:37:39 +00:00
|
|
|
|
|
|
|
dc_descriptor_iterator(&iterator);
|
|
|
|
while (dc_iterator_next (iterator, &descriptor) == DC_STATUS_SUCCESS) {
|
|
|
|
const char *vendor = dc_descriptor_get_vendor(descriptor);
|
|
|
|
const char *product = dc_descriptor_get_product(descriptor);
|
2012-12-06 01:03:58 +00:00
|
|
|
add_dc(vendor, product, descriptor);
|
2012-12-07 06:54:16 +00:00
|
|
|
if (product && strlen(product) > width)
|
|
|
|
width = strlen(product);
|
2012-12-06 01:03:58 +00:00
|
|
|
}
|
|
|
|
dc_iterator_free(iterator);
|
2012-12-07 06:54:16 +00:00
|
|
|
/* and add the Uemis Zurich which we are handling internally
|
|
|
|
THIS IS A HACK as we magically have a data structure here that
|
|
|
|
happens to match a data structure that is internal to libdivecomputer;
|
|
|
|
this WILL BREAK if libdivecomputer changes the dc_descriptor struct...
|
|
|
|
eventually the UEMIS code needs to move into libdivecomputer, I guess */
|
|
|
|
mydescriptor = malloc(sizeof(struct mydescriptor));
|
|
|
|
mydescriptor->vendor = "Uemis";
|
|
|
|
mydescriptor->product = "Zurich";
|
|
|
|
mydescriptor->type = DC_FAMILY_NULL;
|
|
|
|
mydescriptor->model = 0;
|
|
|
|
add_dc("Uemis", "Zurich", (dc_descriptor_t *)mydescriptor);
|
2012-12-06 01:03:58 +00:00
|
|
|
dcl = dc_list;
|
|
|
|
numvendor = 0;
|
|
|
|
while (dcl) {
|
|
|
|
numvendor++;
|
|
|
|
dcl = dcl->next;
|
|
|
|
}
|
2012-12-07 06:54:16 +00:00
|
|
|
/* we need an extra vendor for the empty one */
|
|
|
|
numvendor += 1;
|
2012-12-06 01:03:58 +00:00
|
|
|
dcl = dc_list;
|
|
|
|
i = 0;
|
2012-12-07 06:54:16 +00:00
|
|
|
*vendor_index = *product_index = -1;
|
2012-12-06 01:03:58 +00:00
|
|
|
if (*productstore)
|
|
|
|
free(*productstore);
|
|
|
|
pstores = *productstore = malloc(numvendor * sizeof(GtkListStore *));
|
|
|
|
while (dcl) {
|
|
|
|
gtk_list_store_append(vendorstore, &iter);
|
|
|
|
gtk_list_store_set(vendorstore, &iter,
|
|
|
|
0, dcl->vendor,
|
2011-09-20 19:40:34 +00:00
|
|
|
-1);
|
2012-12-06 01:03:58 +00:00
|
|
|
pl = dcl->productlist;
|
2012-12-12 15:25:28 +00:00
|
|
|
pstores[i + 1] = gtk_list_store_new(1, G_TYPE_POINTER);
|
2012-12-06 01:03:58 +00:00
|
|
|
j = 0;
|
|
|
|
while (pl) {
|
2012-12-12 15:25:28 +00:00
|
|
|
gtk_list_store_append(pstores[i + 1], &iter);
|
|
|
|
gtk_list_store_set(pstores[i + 1], &iter,
|
2012-12-06 01:03:58 +00:00
|
|
|
0, pl->descriptor,
|
|
|
|
-1);
|
|
|
|
if (is_default_dive_computer(dcl->vendor, pl->product)) {
|
2012-12-07 06:54:16 +00:00
|
|
|
*vendor_index = i;
|
2012-12-06 01:03:58 +00:00
|
|
|
*product_index = j;
|
|
|
|
}
|
|
|
|
j++;
|
|
|
|
pl = pl->next;
|
|
|
|
}
|
2012-06-22 20:37:39 +00:00
|
|
|
i++;
|
2012-12-06 01:03:58 +00:00
|
|
|
dcl = dcl->next;
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
2012-12-06 01:03:58 +00:00
|
|
|
/* now add the empty product list in case no vendor is selected */
|
2012-12-12 15:25:28 +00:00
|
|
|
pstores[0] = gtk_list_store_new(1, G_TYPE_POINTER);
|
2012-09-25 14:28:47 +00:00
|
|
|
|
2012-12-07 06:54:16 +00:00
|
|
|
return width;
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
void render_dc_vendor(GtkCellLayout *cell,
|
|
|
|
GtkCellRenderer *renderer,
|
|
|
|
GtkTreeModel *model,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
const char *vendor;
|
|
|
|
|
|
|
|
gtk_tree_model_get(model, iter, 0, &vendor, -1);
|
2012-12-16 22:26:34 +00:00
|
|
|
g_object_set(renderer, "text", vendor, NULL);
|
2012-12-06 01:03:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void render_dc_product(GtkCellLayout *cell,
|
2012-06-22 20:37:39 +00:00
|
|
|
GtkCellRenderer *renderer,
|
|
|
|
GtkTreeModel *model,
|
|
|
|
GtkTreeIter *iter,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
dc_descriptor_t *descriptor = NULL;
|
2012-12-06 01:03:58 +00:00
|
|
|
const char *product;
|
2012-06-22 20:37:39 +00:00
|
|
|
|
|
|
|
gtk_tree_model_get(model, iter, 0, &descriptor, -1);
|
|
|
|
product = dc_descriptor_get_product(descriptor);
|
2012-12-16 22:26:34 +00:00
|
|
|
g_object_set(renderer, "text", product, NULL);
|
2012-06-22 20:37:39 +00:00
|
|
|
}
|
|
|
|
|
2012-09-21 18:32:12 +00:00
|
|
|
static void dive_computer_selector_changed(GtkWidget *combo, gpointer data)
|
|
|
|
{
|
|
|
|
GtkWidget *import, *button;
|
|
|
|
|
|
|
|
import = gtk_widget_get_ancestor(combo, GTK_TYPE_DIALOG);
|
|
|
|
button = gtk_dialog_get_widget_for_response(GTK_DIALOG(import), GTK_RESPONSE_ACCEPT);
|
|
|
|
gtk_widget_set_sensitive(button, TRUE);
|
|
|
|
}
|
2012-06-22 20:37:39 +00:00
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
static GtkListStore **product_model;
|
|
|
|
static void dive_computer_vendor_changed(GtkComboBox *vendorcombo, GtkComboBox *productcombo)
|
|
|
|
{
|
|
|
|
int vendor = gtk_combo_box_get_active(vendorcombo);
|
2012-12-12 15:25:28 +00:00
|
|
|
gtk_combo_box_set_model(productcombo, GTK_TREE_MODEL(product_model[vendor + 1]));
|
2012-12-06 01:03:58 +00:00
|
|
|
gtk_combo_box_set_active(productcombo, -1);
|
|
|
|
}
|
|
|
|
|
2011-09-26 16:19:30 +00:00
|
|
|
static GtkComboBox *dive_computer_selector(GtkWidget *vbox)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
2012-12-06 01:03:58 +00:00
|
|
|
GtkWidget *hbox, *vendor_combo_box, *product_combo_box, *frame;
|
|
|
|
GtkListStore *vendor_model;
|
|
|
|
GtkCellRenderer *vendor_renderer, *product_renderer;
|
2012-12-07 06:54:16 +00:00
|
|
|
int vendor_default_index, product_default_index, width;
|
2011-09-20 19:40:34 +00:00
|
|
|
|
|
|
|
hbox = gtk_hbox_new(FALSE, 6);
|
2011-09-24 22:26:37 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
vendor_model = gtk_list_store_new(1, G_TYPE_POINTER);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-12-07 06:54:16 +00:00
|
|
|
width = fill_computer_list(vendor_model, &product_model, &vendor_default_index, &product_default_index);
|
2012-12-06 01:03:58 +00:00
|
|
|
|
|
|
|
frame = gtk_frame_new(_("Dive computer vendor and product"));
|
2011-09-26 16:19:30 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3);
|
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
hbox = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), hbox);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
vendor_combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(vendor_model));
|
2012-12-12 15:25:28 +00:00
|
|
|
product_combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(product_model[vendor_default_index + 1]));
|
2012-12-06 01:03:58 +00:00
|
|
|
|
|
|
|
g_signal_connect(G_OBJECT(vendor_combo_box), "changed", G_CALLBACK(dive_computer_vendor_changed), product_combo_box);
|
|
|
|
g_signal_connect(G_OBJECT(product_combo_box), "changed", G_CALLBACK(dive_computer_selector_changed), NULL);
|
|
|
|
gtk_box_pack_start(GTK_BOX(hbox), vendor_combo_box, FALSE, FALSE, 3);
|
|
|
|
gtk_box_pack_start(GTK_BOX(hbox), product_combo_box, FALSE, FALSE, 3);
|
|
|
|
|
|
|
|
vendor_renderer = gtk_cell_renderer_text_new();
|
|
|
|
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(vendor_combo_box), vendor_renderer, TRUE);
|
|
|
|
gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(vendor_combo_box), vendor_renderer, render_dc_vendor, NULL, NULL);
|
|
|
|
|
|
|
|
product_renderer = gtk_cell_renderer_text_new();
|
2012-12-07 06:54:16 +00:00
|
|
|
gtk_cell_renderer_set_fixed_size(product_renderer, 10 * width, -1);
|
2012-12-06 01:03:58 +00:00
|
|
|
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(product_combo_box), product_renderer, TRUE);
|
|
|
|
gtk_cell_layout_set_cell_data_func(GTK_CELL_LAYOUT(product_combo_box), product_renderer, render_dc_product, NULL, NULL);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
gtk_combo_box_set_active(GTK_COMBO_BOX(vendor_combo_box), vendor_default_index);
|
|
|
|
gtk_combo_box_set_active(GTK_COMBO_BOX(product_combo_box), product_default_index);
|
2012-05-02 17:26:34 +00:00
|
|
|
|
2012-12-06 01:03:58 +00:00
|
|
|
return GTK_COMBO_BOX(product_combo_box);
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
2012-10-26 22:52:39 +00:00
|
|
|
static GtkComboBox *dc_device_selector(GtkWidget *vbox)
|
2012-05-30 04:54:09 +00:00
|
|
|
{
|
2012-10-26 22:52:39 +00:00
|
|
|
GtkWidget *hbox, *combo_box, *frame;
|
|
|
|
GtkListStore *model;
|
|
|
|
GtkCellRenderer *renderer;
|
|
|
|
int default_index;
|
2011-09-26 16:44:27 +00:00
|
|
|
|
|
|
|
hbox = gtk_hbox_new(FALSE, 6);
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
|
|
|
|
|
2012-10-26 22:52:39 +00:00
|
|
|
model = gtk_list_store_new(1, G_TYPE_STRING);
|
|
|
|
default_index = subsurface_fill_device_list(model);
|
|
|
|
|
|
|
|
frame = gtk_frame_new(_("Device or mount point"));
|
2011-09-26 16:44:27 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, TRUE, 3);
|
|
|
|
|
2012-10-26 22:52:39 +00:00
|
|
|
combo_box = gtk_combo_box_entry_new_with_model(GTK_TREE_MODEL(model), 0);
|
|
|
|
gtk_container_add(GTK_CONTAINER(frame), combo_box);
|
2011-09-26 16:44:27 +00:00
|
|
|
|
2012-10-26 22:52:39 +00:00
|
|
|
renderer = gtk_cell_renderer_text_new();
|
|
|
|
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), renderer, TRUE);
|
|
|
|
|
2012-11-01 21:27:49 +00:00
|
|
|
if (default_index != -1)
|
|
|
|
gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), default_index);
|
|
|
|
else
|
2012-12-06 05:19:30 +00:00
|
|
|
if (default_dive_computer_device)
|
|
|
|
gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(combo_box))),
|
2012-11-01 21:27:49 +00:00
|
|
|
default_dive_computer_device);
|
2012-10-26 22:52:39 +00:00
|
|
|
|
|
|
|
return GTK_COMBO_BOX(combo_box);
|
2011-09-26 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
2012-09-21 23:12:12 +00:00
|
|
|
static void do_import_file(gpointer data, gpointer user_data)
|
2011-10-05 21:12:03 +00:00
|
|
|
{
|
2012-09-21 23:12:12 +00:00
|
|
|
GError *error = NULL;
|
2012-10-09 12:50:16 +00:00
|
|
|
parse_file(data, &error, FALSE);
|
2012-09-21 23:12:12 +00:00
|
|
|
|
|
|
|
if (error != NULL)
|
|
|
|
{
|
|
|
|
report_error(error);
|
|
|
|
g_error_free(error);
|
|
|
|
error = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void import_files(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
GtkWidget *fs_dialog;
|
2012-09-15 22:12:30 +00:00
|
|
|
const char *current_default;
|
|
|
|
char *current_def_dir;
|
2011-10-05 18:36:15 +00:00
|
|
|
GtkFileFilter *filter;
|
2012-09-15 22:12:30 +00:00
|
|
|
struct stat sb;
|
2012-09-21 23:12:12 +00:00
|
|
|
GSList *filenames = NULL;
|
2011-10-05 21:12:03 +00:00
|
|
|
|
2012-11-21 21:53:36 +00:00
|
|
|
remember_tree_state();
|
2012-10-11 00:42:59 +00:00
|
|
|
fs_dialog = gtk_file_chooser_dialog_new(_("Choose XML Files To Import Into Current Data File"),
|
2011-10-05 18:36:15 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
|
|
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
|
|
|
|
NULL);
|
2012-09-15 22:12:30 +00:00
|
|
|
|
|
|
|
/* I'm not sure what the best default path should be... */
|
|
|
|
if (existing_filename) {
|
|
|
|
current_def_dir = g_path_get_dirname(existing_filename);
|
|
|
|
} else {
|
|
|
|
current_default = subsurface_default_filename();
|
|
|
|
current_def_dir = g_path_get_dirname(current_default);
|
|
|
|
free((void *)current_default);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* it's possible that the directory doesn't exist (especially for the default)
|
|
|
|
* For gtk's file select box to make sense we create it */
|
|
|
|
if (stat(current_def_dir, &sb) != 0)
|
|
|
|
g_mkdir(current_def_dir, S_IRWXU);
|
2011-10-05 18:36:15 +00:00
|
|
|
|
2012-09-15 22:12:30 +00:00
|
|
|
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fs_dialog), current_def_dir);
|
|
|
|
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(fs_dialog), TRUE);
|
2012-09-15 22:22:36 +00:00
|
|
|
filter = setup_filter();
|
2012-09-15 22:12:30 +00:00
|
|
|
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fs_dialog), filter);
|
|
|
|
gtk_widget_show_all(fs_dialog);
|
2012-09-21 23:12:12 +00:00
|
|
|
if (gtk_dialog_run(GTK_DIALOG(fs_dialog)) == GTK_RESPONSE_ACCEPT) {
|
|
|
|
/* grab the selected file list, import each file and update the list */
|
|
|
|
filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(fs_dialog));
|
|
|
|
if (filenames) {
|
|
|
|
g_slist_foreach(filenames, do_import_file, NULL);
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
report_dives(TRUE, FALSE);
|
2012-09-21 23:12:12 +00:00
|
|
|
g_slist_free(filenames);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-15 22:12:30 +00:00
|
|
|
free(current_def_dir);
|
|
|
|
gtk_widget_destroy(fs_dialog);
|
2012-11-21 21:53:36 +00:00
|
|
|
restore_tree_state();
|
2011-10-05 18:36:15 +00:00
|
|
|
}
|
|
|
|
|
2012-05-02 20:45:52 +00:00
|
|
|
static GtkWidget *import_dive_computer(device_data_t *data, GtkDialog *dialog)
|
2012-05-02 19:49:03 +00:00
|
|
|
{
|
|
|
|
GError *error;
|
2012-05-02 20:45:52 +00:00
|
|
|
GtkWidget *vbox, *info, *container, *label, *button;
|
2012-05-02 19:49:03 +00:00
|
|
|
|
2012-09-25 14:28:47 +00:00
|
|
|
/* HACK to simply include the Uemis Zurich in the list */
|
|
|
|
if (! strcmp(data->vendor, "Uemis") && ! strcmp(data->product, "Zurich")) {
|
2012-11-29 00:11:19 +00:00
|
|
|
error = uemis_download(data->devname, &data->progress, data->dialog, data->force_download);
|
2012-09-25 14:28:47 +00:00
|
|
|
} else {
|
|
|
|
error = do_import(data);
|
|
|
|
}
|
2012-05-02 19:49:03 +00:00
|
|
|
if (!error)
|
|
|
|
return NULL;
|
|
|
|
|
2012-05-02 20:45:52 +00:00
|
|
|
button = gtk_dialog_get_widget_for_response(dialog, GTK_RESPONSE_ACCEPT);
|
|
|
|
gtk_button_set_use_stock(GTK_BUTTON(button), 0);
|
2012-10-11 00:42:59 +00:00
|
|
|
gtk_button_set_label(GTK_BUTTON(button), _("Retry"));
|
2012-05-02 20:45:52 +00:00
|
|
|
|
|
|
|
vbox = gtk_dialog_get_content_area(dialog);
|
|
|
|
|
2012-05-02 19:49:03 +00:00
|
|
|
info = gtk_info_bar_new();
|
|
|
|
container = gtk_info_bar_get_content_area(GTK_INFO_BAR(info));
|
|
|
|
label = gtk_label_new(error->message);
|
|
|
|
gtk_container_add(GTK_CONTAINER(container), label);
|
2012-05-02 20:45:52 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), info, FALSE, FALSE, 0);
|
2012-05-02 19:49:03 +00:00
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
2012-11-01 22:08:43 +00:00
|
|
|
/* this prevents clicking the [x] button, while the import thread is still running */
|
|
|
|
static void download_dialog_delete(GtkWidget *w, gpointer data)
|
|
|
|
{
|
|
|
|
/* a no-op */
|
|
|
|
}
|
|
|
|
|
2012-09-21 22:47:06 +00:00
|
|
|
void download_dialog(GtkWidget *w, gpointer data)
|
2011-09-20 19:40:34 +00:00
|
|
|
{
|
|
|
|
int result;
|
2012-11-01 21:27:49 +00:00
|
|
|
char *devname, *ns, *ne;
|
2012-09-21 18:32:12 +00:00
|
|
|
GtkWidget *dialog, *button, *hbox, *vbox, *label, *info = NULL;
|
2012-10-26 22:52:39 +00:00
|
|
|
GtkComboBox *computer, *device;
|
2012-09-21 18:32:12 +00:00
|
|
|
GtkTreeIter iter;
|
2011-09-20 19:40:34 +00:00
|
|
|
device_data_t devicedata = {
|
2011-09-26 16:44:27 +00:00
|
|
|
.devname = NULL,
|
2011-09-20 19:40:34 +00:00
|
|
|
};
|
|
|
|
|
2012-11-21 21:53:36 +00:00
|
|
|
remember_tree_state();
|
2012-10-11 00:42:59 +00:00
|
|
|
dialog = gtk_dialog_new_with_buttons(_("Download From Dive Computer"),
|
2011-09-20 19:40:34 +00:00
|
|
|
GTK_WINDOW(main_window),
|
|
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
2012-11-19 22:11:08 +00:00
|
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
2011-09-20 19:40:34 +00:00
|
|
|
NULL);
|
2012-11-01 22:08:43 +00:00
|
|
|
g_signal_connect(dialog, "delete-event", G_CALLBACK(download_dialog_delete), NULL);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
2011-09-26 16:19:30 +00:00
|
|
|
vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
2012-10-11 00:42:59 +00:00
|
|
|
label = gtk_label_new(_(" Please select dive computer and device. "));
|
2011-10-05 18:36:15 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3);
|
2011-09-26 16:19:30 +00:00
|
|
|
computer = dive_computer_selector(vbox);
|
2012-10-26 22:52:39 +00:00
|
|
|
device = dc_device_selector(vbox);
|
2011-09-20 19:40:34 +00:00
|
|
|
hbox = gtk_hbox_new(FALSE, 6);
|
2011-09-24 22:26:37 +00:00
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 3);
|
2011-09-22 23:38:24 +00:00
|
|
|
devicedata.progress.bar = gtk_progress_bar_new();
|
|
|
|
gtk_container_add(GTK_CONTAINER(hbox), devicedata.progress.bar);
|
2011-09-20 19:40:34 +00:00
|
|
|
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
force_download = FALSE;
|
|
|
|
button = gtk_check_button_new_with_label(_("Force download of all dives"));
|
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(force_toggle), NULL);
|
|
|
|
|
|
|
|
prefer_downloaded = FALSE;
|
|
|
|
button = gtk_check_button_new_with_label(_("Always prefer downloaded dive"));
|
|
|
|
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE);
|
|
|
|
gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 6);
|
|
|
|
g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(prefer_dl_toggle), NULL);
|
|
|
|
|
2012-09-21 18:32:12 +00:00
|
|
|
button = gtk_dialog_get_widget_for_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
|
|
|
|
if (!gtk_combo_box_get_active_iter(computer, &iter))
|
|
|
|
gtk_widget_set_sensitive(button, FALSE);
|
|
|
|
|
2012-05-02 19:49:03 +00:00
|
|
|
repeat:
|
2011-09-20 19:40:34 +00:00
|
|
|
gtk_widget_show_all(dialog);
|
|
|
|
result = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
|
|
switch (result) {
|
2012-06-22 20:37:39 +00:00
|
|
|
dc_descriptor_t *descriptor;
|
2011-09-20 19:40:34 +00:00
|
|
|
GtkTreeModel *model;
|
2012-09-15 22:12:30 +00:00
|
|
|
|
2011-09-20 19:40:34 +00:00
|
|
|
case GTK_RESPONSE_ACCEPT:
|
2012-11-01 22:08:43 +00:00
|
|
|
/* once the accept event is triggered the dialog becomes non-modal.
|
|
|
|
* lets re-set that */
|
|
|
|
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
|
2012-05-02 19:49:03 +00:00
|
|
|
if (info)
|
|
|
|
gtk_widget_destroy(info);
|
2012-09-21 22:47:06 +00:00
|
|
|
const char *vendor, *product;
|
|
|
|
|
|
|
|
if (!gtk_combo_box_get_active_iter(computer, &iter))
|
|
|
|
break;
|
|
|
|
|
|
|
|
model = gtk_combo_box_get_model(computer);
|
|
|
|
gtk_tree_model_get(model, &iter,
|
|
|
|
0, &descriptor,
|
|
|
|
-1);
|
|
|
|
|
|
|
|
vendor = dc_descriptor_get_vendor(descriptor);
|
|
|
|
product = dc_descriptor_get_product(descriptor);
|
|
|
|
|
|
|
|
devicedata.descriptor = descriptor;
|
|
|
|
devicedata.vendor = vendor;
|
|
|
|
devicedata.product = product;
|
|
|
|
set_default_dive_computer(vendor, product);
|
2012-11-01 21:27:49 +00:00
|
|
|
|
|
|
|
/* get the device name from the combo box entry and set as default */
|
|
|
|
devname = strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(device)))));
|
|
|
|
set_default_dive_computer_device(devname);
|
|
|
|
/* clear leading and trailing white space from the device name and also
|
|
|
|
* everything after (and including) the first '(' char. */
|
|
|
|
ns = devname;
|
|
|
|
while (*ns == ' ' || *ns == '\t')
|
|
|
|
ns++;
|
|
|
|
ne = ns;
|
|
|
|
while (*ne && *ne != '(')
|
|
|
|
ne++;
|
|
|
|
*ne = '\0';
|
|
|
|
if (ne > ns)
|
|
|
|
while (*(--ne) == ' ' || *ne == '\t')
|
|
|
|
*ne = '\0';
|
|
|
|
devicedata.devname = ns;
|
2012-11-19 22:11:08 +00:00
|
|
|
devicedata.dialog = GTK_DIALOG(dialog);
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
devicedata.force_download = force_download;
|
2012-11-20 00:13:05 +00:00
|
|
|
force_download = FALSE; /* when retrying we don't want to restart */
|
2012-09-21 22:47:06 +00:00
|
|
|
info = import_dive_computer(&devicedata, GTK_DIALOG(dialog));
|
2012-11-01 21:27:49 +00:00
|
|
|
free((void *)devname);
|
2012-09-21 22:47:06 +00:00
|
|
|
if (info)
|
|
|
|
goto repeat;
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
report_dives(TRUE, prefer_downloaded);
|
2011-09-20 19:40:34 +00:00
|
|
|
break;
|
|
|
|
default:
|
2012-09-27 03:37:57 +00:00
|
|
|
/* it's possible that some dives were downloaded */
|
Add special download modes to force updates from the divecomputer
This will hopefully not be something we need often, but if we improve
support for a divecomputer (either in libdivecomputer or in our native
Uemis code or even in the way we handle (and potentially discard) events),
then it is extremely useful to be able to say "re-download things
from the divecomputer and for things that were not edited in Subsurface,
don't try to merge the data (which gives BAD results if for example you
fixed a bug in the depth calculation in libdivecomputer) but instead
simply take the samples, the events and some of the other unedited data
straight from the download".
This commit implements just that - a "force download" checkbox in the
download dialog that makes us reimport all dives from the dive computer,
even the ones we already have, and an "always prefer downloaded dive"
checkbox that then tells Subsurface not to merge but simply to take the
data from the downloaded dive - without overwriting the things we have
already edited in Subsurface (like location, buddy, equipment, etc).
This, as a precaution, refuses to merge dives that don't have identical
start times. So if you have edited the date / time of a dive or if you
have previously merged your dive with a different dive computer (and
therefore modified samples and events) you are out of luck.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2012-11-11 13:29:26 +00:00
|
|
|
report_dives(TRUE, prefer_downloaded);
|
2011-09-20 19:40:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
2012-11-21 21:53:36 +00:00
|
|
|
restore_tree_state();
|
2011-09-20 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void update_progressbar(progressbar_t *progress, double value)
|
|
|
|
{
|
|
|
|
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress->bar), value);
|
|
|
|
}
|
2011-09-21 04:50:26 +00:00
|
|
|
|
2012-05-03 00:40:39 +00:00
|
|
|
void update_progressbar_text(progressbar_t *progress, const char *text)
|
|
|
|
{
|
|
|
|
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress->bar), text);
|
|
|
|
}
|
2011-09-21 04:50:26 +00:00
|
|
|
|
2012-09-10 19:27:00 +00:00
|
|
|
void set_filename(const char *filename, gboolean force)
|
2011-09-21 04:50:26 +00:00
|
|
|
{
|
2012-09-10 19:27:00 +00:00
|
|
|
if (!force && existing_filename)
|
|
|
|
return;
|
2012-09-16 03:51:06 +00:00
|
|
|
free((void *)existing_filename);
|
2012-07-31 17:55:41 +00:00
|
|
|
if (filename)
|
2011-09-21 04:50:26 +00:00
|
|
|
existing_filename = strdup(filename);
|
2012-11-10 14:32:06 +00:00
|
|
|
else
|
|
|
|
existing_filename = NULL;
|
2011-09-21 04:50:26 +00:00
|
|
|
}
|
2012-12-13 06:26:29 +00:00
|
|
|
|
2012-12-26 21:47:54 +00:00
|
|
|
/* just find the entry for this divecomputer */
|
|
|
|
static struct dcnicknamelist *get_dc_nicknameentry(const char *model, int deviceid)
|
2012-12-13 06:26:29 +00:00
|
|
|
{
|
|
|
|
struct dcnicknamelist *known = nicknamelist;
|
2012-12-28 14:36:47 +00:00
|
|
|
|
|
|
|
/* a 0 deviceid doesn't get a nickname - those come from development
|
|
|
|
* versions of Subsurface that didn't store the deviceid in the divecomputer entries */
|
|
|
|
if (!deviceid)
|
|
|
|
return NULL;
|
2012-12-26 21:47:54 +00:00
|
|
|
if (!model)
|
|
|
|
model = "";
|
2012-12-13 06:26:29 +00:00
|
|
|
while (known) {
|
2012-12-26 21:47:54 +00:00
|
|
|
if (!strcmp(known->model, model) && known->deviceid == deviceid)
|
|
|
|
return known;
|
2012-12-13 06:26:29 +00:00
|
|
|
known = known->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-26 21:47:54 +00:00
|
|
|
const char *get_dc_nickname(const char *model, uint32_t deviceid)
|
2012-12-22 05:00:06 +00:00
|
|
|
{
|
2012-12-26 21:47:54 +00:00
|
|
|
struct dcnicknamelist *known = get_dc_nicknameentry(model, deviceid);
|
|
|
|
if (known) {
|
|
|
|
if (known->nickname && *known->nickname)
|
|
|
|
return known->nickname;
|
|
|
|
else
|
|
|
|
return known->model;
|
2012-12-22 05:00:06 +00:00
|
|
|
}
|
2012-12-23 05:37:13 +00:00
|
|
|
return NULL;
|
2012-12-22 05:00:06 +00:00
|
|
|
}
|
|
|
|
|
2012-12-31 04:27:01 +00:00
|
|
|
gboolean dc_was_saved(struct divecomputer *dc)
|
|
|
|
{
|
|
|
|
struct dcnicknamelist *nn_entry = get_dc_nicknameentry(dc->model, dc->deviceid);
|
|
|
|
return nn_entry && nn_entry->saved;
|
|
|
|
}
|
|
|
|
|
|
|
|
void mark_dc_saved(struct divecomputer *dc)
|
|
|
|
{
|
|
|
|
struct dcnicknamelist *nn_entry = get_dc_nicknameentry(dc->model, dc->deviceid);
|
|
|
|
if (nn_entry)
|
|
|
|
nn_entry->saved = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void clear_dc_saved_status()
|
|
|
|
{
|
|
|
|
struct dcnicknamelist *nn_entry = nicknamelist;
|
|
|
|
|
|
|
|
while (nn_entry) {
|
|
|
|
nn_entry->saved = FALSE;
|
|
|
|
nn_entry = nn_entry->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-26 21:47:54 +00:00
|
|
|
/* do we have a DIFFERENT divecomputer of the same model? */
|
|
|
|
static struct dcnicknamelist *get_different_dc_nicknameentry(const char *model, int deviceid)
|
2012-12-23 17:06:23 +00:00
|
|
|
{
|
|
|
|
struct dcnicknamelist *known = nicknamelist;
|
2012-12-28 14:36:47 +00:00
|
|
|
|
|
|
|
/* a 0 deviceid matches any DC of the same model - those come from development
|
|
|
|
* versions of Subsurface that didn't store the deviceid in the divecomputer entries */
|
|
|
|
if (!deviceid)
|
|
|
|
return NULL;
|
2012-12-26 21:47:54 +00:00
|
|
|
if (!model)
|
|
|
|
model = "";
|
2012-12-23 17:06:23 +00:00
|
|
|
while (known) {
|
2012-12-26 21:47:54 +00:00
|
|
|
if (known->model && !strcmp(known->model, model) &&
|
|
|
|
known->deviceid != deviceid)
|
2012-12-23 17:06:23 +00:00
|
|
|
return known;
|
|
|
|
known = known->next;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-13 06:26:29 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2012-12-26 21:47:54 +00:00
|
|
|
void replace_nickname_nicknamestring(const char *model, int deviceid, const char *nickname)
|
2012-12-23 05:37:13 +00:00
|
|
|
{
|
|
|
|
char buf[11];
|
|
|
|
char *entry, *comma1, *comma2, *brace, *new_nn;
|
|
|
|
int len;
|
|
|
|
|
2012-12-26 21:47:54 +00:00
|
|
|
if (!nickname)
|
|
|
|
nickname = "";
|
2012-12-23 05:37:13 +00:00
|
|
|
snprintf(buf, sizeof(buf), "{%08x,", deviceid);
|
|
|
|
entry = strstr(nicknamestring, buf);
|
|
|
|
if (!entry)
|
2012-12-26 21:47:54 +00:00
|
|
|
/* this cannot happen as we know we have an entry for this deviceid */
|
2012-12-23 05:37:13 +00:00
|
|
|
goto bail;
|
|
|
|
len = strlen(entry);
|
|
|
|
comma1 = g_utf8_strchr(entry, len, ',');
|
|
|
|
if (!comma1)
|
|
|
|
goto bail;
|
|
|
|
len = strlen(comma1);
|
|
|
|
comma2 = g_utf8_strchr(comma1, len, ',');
|
|
|
|
brace = g_utf8_strchr(comma1, len, '}');
|
|
|
|
if (!brace)
|
|
|
|
goto bail;
|
|
|
|
if (!comma2 || brace < comma2) {
|
|
|
|
/* didn't have a nickname, so add one */
|
|
|
|
len = strlen(nicknamestring) + strlen(nickname) + 2;
|
|
|
|
*brace = '\0';
|
|
|
|
} else {
|
|
|
|
/* replace the nickname */
|
|
|
|
len = strlen(nicknamestring) + strlen(nickname) - (brace - comma2) + 1;
|
|
|
|
*comma2 = '\0';
|
|
|
|
}
|
|
|
|
new_nn = malloc(len);
|
|
|
|
if (strlen(nickname))
|
2012-12-26 21:47:54 +00:00
|
|
|
snprintf(new_nn, len, "%s,%s}%s", nicknamestring, nickname, brace + 1);
|
2012-12-23 05:37:13 +00:00
|
|
|
else
|
2012-12-26 21:47:54 +00:00
|
|
|
snprintf(new_nn, len, "%s}%s", nicknamestring, brace + 1);
|
2012-12-23 05:37:13 +00:00
|
|
|
free(nicknamestring);
|
|
|
|
nicknamestring = new_nn;
|
|
|
|
return;
|
|
|
|
|
|
|
|
bail:
|
|
|
|
printf("invalid nicknamestring %s (while looking at deviceid %08x\n", nicknamestring, deviceid);
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-01-01 21:27:49 +00:00
|
|
|
void remove_dc(const char *model, uint32_t deviceid, gboolean change_conf)
|
|
|
|
{
|
|
|
|
struct dcnicknamelist *nn_entry, **prevp = &nicknamelist;
|
|
|
|
char pattern[160];
|
|
|
|
char *nnstring, *brace;
|
|
|
|
|
|
|
|
if (!deviceid || !model || !*model)
|
|
|
|
return;
|
|
|
|
nn_entry = get_dc_nicknameentry(model, deviceid);
|
|
|
|
if (!nn_entry)
|
|
|
|
return;
|
|
|
|
while (prevp && *prevp != nn_entry)
|
|
|
|
prevp = &(*prevp)->next;
|
|
|
|
if (!prevp)
|
|
|
|
/* that should be impossible */
|
|
|
|
goto bail;
|
|
|
|
(*prevp) = nn_entry->next;
|
|
|
|
|
|
|
|
/* now remove it from the config string */
|
|
|
|
snprintf(pattern, sizeof(pattern), "{%08x,%s", deviceid, model);
|
|
|
|
nnstring = strstr(nicknamestring, pattern);
|
|
|
|
if (!nnstring)
|
|
|
|
goto bail;
|
|
|
|
brace = strchr(nnstring, '}');
|
|
|
|
if (!brace)
|
|
|
|
goto bail;
|
|
|
|
brace++;
|
|
|
|
memmove(nnstring, brace, strlen(brace) + 1);
|
|
|
|
|
|
|
|
if (change_conf)
|
|
|
|
subsurface_set_conf("dc_nicknames", PREF_STRING, nicknamestring);
|
|
|
|
|
|
|
|
#if defined(NICKNAME_DEBUG)
|
|
|
|
struct dcnicknamelist *nn_entry = nicknamelist;
|
|
|
|
fprintf(debugfile, "nicknames:\n");
|
|
|
|
while (nn_entry) {
|
|
|
|
fprintf(debugfile, "id %8x model %s nickname %s\n", nn_entry->deviceid, nn_entry->model,
|
|
|
|
nn_entry->nickname && *nn_entry->nickname ? nn_entry->nickname : "(none)");
|
|
|
|
nn_entry = nn_entry->next;
|
|
|
|
}
|
|
|
|
fprintf(debugfile, "----------\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bail:
|
|
|
|
free(nn_entry);
|
|
|
|
}
|
|
|
|
|
2012-12-26 21:47:54 +00:00
|
|
|
void remember_dc(const char *model, uint32_t deviceid, const char *nickname, gboolean change_conf)
|
2012-12-13 06:26:29 +00:00
|
|
|
{
|
2012-12-28 14:36:47 +00:00
|
|
|
/* we don't want to record entries with a deviceid of 0; those are from earlier
|
|
|
|
* development versions of Subsurface before we stored the hash in the divecomputer
|
|
|
|
* entries... we don't know which actual divecomputer those entries are from */
|
|
|
|
if (!deviceid)
|
|
|
|
return;
|
2012-12-26 21:47:54 +00:00
|
|
|
if (!nickname)
|
|
|
|
nickname = "";
|
|
|
|
if (!get_dc_nickname(model, deviceid)) {
|
2012-12-16 12:03:17 +00:00
|
|
|
char buffer[160];
|
2012-12-13 06:26:29 +00:00
|
|
|
struct dcnicknamelist *nn_entry = malloc(sizeof(struct dcnicknamelist));
|
|
|
|
nn_entry->deviceid = deviceid;
|
2013-01-02 01:27:33 +00:00
|
|
|
nn_entry->model = strdup(model);
|
2012-12-13 06:26:29 +00:00
|
|
|
/* make sure there are no curly braces or commas in the string and that
|
|
|
|
* it will fit in the buffer */
|
2012-12-22 05:00:06 +00:00
|
|
|
nn_entry->nickname = cleanedup_nickname(nickname, sizeof(buffer) - 13 - strlen(model));
|
2012-12-13 06:26:29 +00:00
|
|
|
nn_entry->next = nicknamelist;
|
|
|
|
nicknamelist = nn_entry;
|
2012-12-22 05:00:06 +00:00
|
|
|
if (*nickname != '\0')
|
|
|
|
snprintf(buffer, sizeof(buffer), "{%08x,%s,%s}", deviceid, model, nn_entry->nickname);
|
|
|
|
else
|
|
|
|
snprintf(buffer, sizeof(buffer), "{%08x,%s}", deviceid, model);
|
2012-12-13 06:26:29 +00:00
|
|
|
nicknamestring = realloc(nicknamestring, strlen(nicknamestring) + strlen(buffer) + 1);
|
|
|
|
strcat(nicknamestring, buffer);
|
2012-12-23 05:37:13 +00:00
|
|
|
} else {
|
|
|
|
/* modify existing entry */
|
2012-12-26 21:47:54 +00:00
|
|
|
struct dcnicknamelist *nn_entry = get_dc_nicknameentry(model, deviceid);
|
2012-12-23 05:37:13 +00:00
|
|
|
if (!nn_entry->model || !*nn_entry->model)
|
|
|
|
nn_entry->model = model;
|
2012-12-28 05:38:23 +00:00
|
|
|
nn_entry->nickname = cleanedup_nickname(nickname, 80);
|
2012-12-26 21:47:54 +00:00
|
|
|
replace_nickname_nicknamestring(model, deviceid, nickname);
|
2012-12-13 06:26:29 +00:00
|
|
|
}
|
2013-01-01 21:27:49 +00:00
|
|
|
if (change_conf)
|
|
|
|
subsurface_set_conf("dc_nicknames", PREF_STRING, nicknamestring);
|
|
|
|
|
2012-12-22 05:00:06 +00:00
|
|
|
#if defined(NICKNAME_DEBUG)
|
|
|
|
struct dcnicknamelist *nn_entry = nicknamelist;
|
|
|
|
fprintf(debugfile, "nicknames:\n");
|
|
|
|
while (nn_entry) {
|
|
|
|
fprintf(debugfile, "id %8x model %s nickname %s\n", nn_entry->deviceid, nn_entry->model,
|
|
|
|
nn_entry->nickname && *nn_entry->nickname ? nn_entry->nickname : "(none)");
|
|
|
|
nn_entry = nn_entry->next;
|
|
|
|
}
|
|
|
|
fprintf(debugfile, "----------\n");
|
|
|
|
#endif
|
2012-12-13 06:26:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void set_dc_nickname(struct dive *dive)
|
|
|
|
{
|
|
|
|
GtkWidget *dialog, *vbox, *entry, *frame, *label;
|
2012-12-22 04:00:20 +00:00
|
|
|
char nickname[160] = "";
|
2012-12-22 05:00:06 +00:00
|
|
|
char dialogtext[1024];
|
2012-12-22 04:00:20 +00:00
|
|
|
const char *name = nickname;
|
2012-12-22 05:00:06 +00:00
|
|
|
struct divecomputer *dc = &dive->dc;
|
2012-12-13 06:26:29 +00:00
|
|
|
|
|
|
|
if (!dive)
|
|
|
|
return;
|
2012-12-22 05:00:06 +00:00
|
|
|
while (dc) {
|
|
|
|
#if NICKNAME_DEBUG & 16
|
|
|
|
fprintf(debugfile, "set_dc_nickname for model %s deviceid %8x\n", dc->model ? : "", dc->deviceid);
|
|
|
|
#endif
|
2012-12-26 21:47:54 +00:00
|
|
|
if (get_dc_nickname(dc->model, dc->deviceid) == NULL) {
|
2012-12-23 17:06:23 +00:00
|
|
|
struct dcnicknamelist *nn_entry = get_different_dc_nicknameentry(dc->model, dc->deviceid);
|
2012-12-23 05:37:13 +00:00
|
|
|
if (!nn_entry) {
|
2012-12-22 05:00:06 +00:00
|
|
|
/* just remember the dive computer without setting a nickname */
|
|
|
|
if (dc->model)
|
2012-12-26 21:47:54 +00:00
|
|
|
remember_dc(dc->model, dc->deviceid, "", TRUE);
|
2012-12-22 05:00:06 +00:00
|
|
|
} 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));
|
|
|
|
snprintf(dialogtext, sizeof(dialogtext),
|
2012-12-23 05:37:13 +00:00
|
|
|
_("You already have a dive computer of this model\n"
|
|
|
|
"named %s\n"
|
2012-12-22 05:00:06 +00:00
|
|
|
"Subsurface can maintain a nickname for this device to "
|
|
|
|
"distinguish it from the existing one. "
|
|
|
|
"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 its name (which may mean that you cannot tell the two "
|
2012-12-23 05:37:13 +00:00
|
|
|
"dive computers apart in the logs)."),
|
|
|
|
nn_entry->nickname && *nn_entry->nickname ? nn_entry->nickname :
|
|
|
|
(nn_entry->model && *nn_entry->model ? nn_entry->model : _("(nothing)")));
|
2012-12-22 05:00:06 +00:00
|
|
|
label = gtk_label_new(dialogtext);
|
|
|
|
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, sizeof(nickname), "%s (%08x)", dc->model, 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(dc->model, gtk_entry_get_text(GTK_ENTRY(entry))))
|
|
|
|
name = cleanedup_nickname(gtk_entry_get_text(GTK_ENTRY(entry)), sizeof(nickname));
|
|
|
|
}
|
|
|
|
gtk_widget_destroy(dialog);
|
2012-12-26 21:47:54 +00:00
|
|
|
remember_dc(dc->model, dc->deviceid, name, TRUE);
|
2012-12-22 05:00:06 +00:00
|
|
|
}
|
2012-12-13 06:26:29 +00:00
|
|
|
}
|
2012-12-22 05:00:06 +00:00
|
|
|
dc = dc->next;
|
2012-12-13 06:26:29 +00:00
|
|
|
}
|
|
|
|
}
|