Ok, this is the ugliest f*&$ing printout I have ever seen in my life, but think of it as a "the concept of printing works" commit, and you'll be able to hold your lunch down and not gouge out your eyeballs with a spoon. Maybe. I'm just doing the cairo display as-is for the printout, which is a seriously bad idea. I need to not try to do colors etc, and instead of having white lines on a black background I just need to make thelines be black on white paper. But that would involve actually changing the current "plot()" routine, which is against the point of the exercise right now. This really is just a demonstration of how to add printing capabilities. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
514 lines
14 KiB
C
514 lines
14 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#include <gconf/gconf-client.h>
|
|
|
|
#include "dive.h"
|
|
#include "divelist.h"
|
|
#include "display.h"
|
|
|
|
GtkWidget *main_window;
|
|
GtkWidget *main_vbox;
|
|
GtkWidget *error_info_bar;
|
|
GtkWidget *error_label;
|
|
int error_count;
|
|
struct DiveList dive_list;
|
|
|
|
GConfClient *gconf;
|
|
struct units output_units;
|
|
|
|
#define GCONF_NAME(x) "/apps/diveclog/" #x
|
|
|
|
static int sortfn(const void *_a, const void *_b)
|
|
{
|
|
const struct dive *a = *(void **)_a;
|
|
const struct dive *b = *(void **)_b;
|
|
|
|
if (a->when < b->when)
|
|
return -1;
|
|
if (a->when > b->when)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This doesn't really report anything at all. We just sort the
|
|
* dives, the GUI does the reporting
|
|
*/
|
|
void report_dives(void)
|
|
{
|
|
int i;
|
|
|
|
qsort(dive_table.dives, dive_table.nr, sizeof(struct dive *), sortfn);
|
|
|
|
for (i = 1; i < dive_table.nr; i++) {
|
|
struct dive **pp = &dive_table.dives[i-1];
|
|
struct dive *prev = pp[0];
|
|
struct dive *dive = pp[1];
|
|
struct dive *merged;
|
|
|
|
if (prev->when + prev->duration.seconds < dive->when)
|
|
continue;
|
|
|
|
merged = try_to_merge(prev, dive);
|
|
if (!merged)
|
|
continue;
|
|
|
|
free(prev);
|
|
free(dive);
|
|
*pp = merged;
|
|
dive_table.nr--;
|
|
memmove(pp+1, pp+2, sizeof(*pp)*(dive_table.nr - i));
|
|
|
|
/* Redo the new 'i'th dive */
|
|
i--;
|
|
}
|
|
}
|
|
|
|
static void parse_argument(const char *arg)
|
|
{
|
|
const char *p = arg+1;
|
|
|
|
do {
|
|
switch (*p) {
|
|
case 'v':
|
|
verbose++;
|
|
continue;
|
|
default:
|
|
fprintf(stderr, "Bad argument '%s'\n", arg);
|
|
exit(1);
|
|
}
|
|
} while (*++p);
|
|
}
|
|
|
|
static void on_destroy(GtkWidget* w, gpointer data)
|
|
{
|
|
gtk_main_quit();
|
|
}
|
|
|
|
static GtkWidget *dive_profile;
|
|
|
|
void update_dive(struct dive *new_dive)
|
|
{
|
|
static struct dive *buffered_dive;
|
|
struct dive *old_dive = buffered_dive;
|
|
|
|
if (old_dive) {
|
|
flush_dive_info_changes(old_dive);
|
|
flush_dive_equipment_changes(old_dive);
|
|
}
|
|
if (new_dive) {
|
|
show_dive_info(new_dive);
|
|
show_dive_equipment(new_dive);
|
|
}
|
|
buffered_dive = new_dive;
|
|
}
|
|
|
|
void repaint_dive(void)
|
|
{
|
|
update_dive(current_dive);
|
|
gtk_widget_queue_draw(dive_profile);
|
|
}
|
|
|
|
static char *existing_filename;
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
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];
|
|
snprintf(buffer, sizeof(buffer), "Failed to open %i files.", error_count);
|
|
gtk_label_set(GTK_LABEL(error_label), buffer);
|
|
}
|
|
}
|
|
|
|
static void file_open(GtkWidget *w, gpointer data)
|
|
{
|
|
GtkWidget *dialog;
|
|
dialog = gtk_file_chooser_dialog_new("Open File",
|
|
GTK_WINDOW(main_window),
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
|
|
NULL);
|
|
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
|
|
|
|
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
|
GSList *filenames;
|
|
char *filename;
|
|
filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
|
|
|
|
GError *error = NULL;
|
|
while(filenames != NULL) {
|
|
filename = (char *)filenames->data;
|
|
parse_xml_file(filename, &error);
|
|
if (error != NULL)
|
|
{
|
|
report_error(error);
|
|
g_error_free(error);
|
|
error = NULL;
|
|
}
|
|
|
|
g_free(filename);
|
|
filenames = g_slist_next(filenames);
|
|
}
|
|
g_slist_free(filenames);
|
|
report_dives();
|
|
dive_list_update_dives(dive_list);
|
|
}
|
|
gtk_widget_destroy(dialog);
|
|
}
|
|
|
|
static void file_save(GtkWidget *w, gpointer data)
|
|
{
|
|
GtkWidget *dialog;
|
|
dialog = gtk_file_chooser_dialog_new("Save File",
|
|
GTK_WINDOW(main_window),
|
|
GTK_FILE_CHOOSER_ACTION_SAVE,
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
|
|
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
|
|
NULL);
|
|
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE);
|
|
if (!existing_filename) {
|
|
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), "Untitled document");
|
|
} else
|
|
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), existing_filename);
|
|
|
|
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
|
char *filename;
|
|
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
|
|
save_dives(filename);
|
|
g_free(filename);
|
|
}
|
|
gtk_widget_destroy(dialog);
|
|
}
|
|
|
|
static void quit(GtkWidget *w, gpointer data)
|
|
{
|
|
gtk_main_quit();
|
|
}
|
|
|
|
static void create_radio(GtkWidget *dialog, const char *name, ...)
|
|
{
|
|
va_list args;
|
|
GtkRadioButton *group = NULL;
|
|
GtkWidget *box, *label;
|
|
|
|
box = gtk_hbox_new(TRUE, 10);
|
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), box);
|
|
|
|
label = gtk_label_new(name);
|
|
gtk_box_pack_start(GTK_BOX(box), label, TRUE, TRUE, 0);
|
|
|
|
va_start(args, name);
|
|
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);
|
|
}
|
|
|
|
#define UNITCALLBACK(name, type, value) \
|
|
static void name(GtkWidget *w, gpointer data) \
|
|
{ \
|
|
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) \
|
|
menu_units.type = value; \
|
|
}
|
|
|
|
static struct units menu_units;
|
|
|
|
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)
|
|
|
|
static void unit_dialog(GtkWidget *w, gpointer data)
|
|
{
|
|
int result;
|
|
GtkWidget *dialog;
|
|
|
|
menu_units = output_units;
|
|
|
|
dialog = gtk_dialog_new_with_buttons("Units",
|
|
GTK_WINDOW(main_window),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
|
NULL);
|
|
|
|
create_radio(dialog, "Depth:",
|
|
"Meter", set_meter, (output_units.length == METERS),
|
|
"Feet", set_feet, (output_units.length == FEET),
|
|
NULL);
|
|
|
|
create_radio(dialog, "Pressure:",
|
|
"Bar", set_bar, (output_units.pressure == BAR),
|
|
"PSI", set_psi, (output_units.pressure == PSI),
|
|
NULL);
|
|
|
|
create_radio(dialog, "Volume:",
|
|
"Liter", set_liter, (output_units.volume == LITER),
|
|
"CuFt", set_cuft, (output_units.volume == CUFT),
|
|
NULL);
|
|
|
|
create_radio(dialog, "Temperature:",
|
|
"Celsius", set_celsius, (output_units.temperature == CELSIUS),
|
|
"Fahrenheit", set_fahrenheit, (output_units.temperature == FAHRENHEIT),
|
|
NULL);
|
|
|
|
gtk_widget_show_all(dialog);
|
|
result = gtk_dialog_run(GTK_DIALOG(dialog));
|
|
if (result == GTK_RESPONSE_ACCEPT) {
|
|
/* Make sure to flush any modified old dive data with old units */
|
|
update_dive(NULL);
|
|
output_units = menu_units;
|
|
update_dive_list_units(&dive_list);
|
|
repaint_dive();
|
|
gconf_client_set_bool(gconf, GCONF_NAME(feet), output_units.length == FEET, NULL);
|
|
gconf_client_set_bool(gconf, GCONF_NAME(psi), output_units.pressure == PSI, NULL);
|
|
gconf_client_set_bool(gconf, GCONF_NAME(cuft), output_units.volume == CUFT, NULL);
|
|
gconf_client_set_bool(gconf, GCONF_NAME(fahrenheit), output_units.temperature == FAHRENHEIT, NULL);
|
|
}
|
|
gtk_widget_destroy(dialog);
|
|
}
|
|
|
|
static void renumber_dives(int nr)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < dive_table.nr; i++) {
|
|
struct dive *dive = dive_table.dives[i];
|
|
dive->number = nr + i;
|
|
}
|
|
}
|
|
|
|
static void renumber_dialog(GtkWidget *w, gpointer data)
|
|
{
|
|
int result;
|
|
GtkWidget *dialog, *frame, *button;
|
|
|
|
dialog = gtk_dialog_new_with_buttons("Renumber",
|
|
GTK_WINDOW(main_window),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
|
|
GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
|
|
NULL);
|
|
|
|
frame = gtk_frame_new("New starting number");
|
|
gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), frame);
|
|
|
|
button = gtk_spin_button_new_with_range(1, 50000, 1);
|
|
gtk_container_add(GTK_CONTAINER(frame), button);
|
|
|
|
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);
|
|
}
|
|
|
|
static GtkActionEntry menu_items[] = {
|
|
{ "FileMenuAction", GTK_STOCK_FILE, "Log", NULL, NULL, NULL},
|
|
{ "OpenFile", GTK_STOCK_OPEN, NULL, "<control>O", NULL, G_CALLBACK(file_open) },
|
|
{ "SaveFile", GTK_STOCK_SAVE, NULL, "<control>S", NULL, G_CALLBACK(file_save) },
|
|
{ "Print", GTK_STOCK_PRINT, NULL, "<control>P", NULL, G_CALLBACK(do_print) },
|
|
{ "Import", NULL, "Import", NULL, NULL, G_CALLBACK(import_dialog) },
|
|
{ "Units", NULL, "Units", NULL, NULL, G_CALLBACK(unit_dialog) },
|
|
{ "Renumber", NULL, "Renumber", NULL, NULL, G_CALLBACK(renumber_dialog) },
|
|
{ "Quit", GTK_STOCK_QUIT, NULL, "<control>Q", NULL, G_CALLBACK(quit) },
|
|
};
|
|
static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);
|
|
|
|
static const gchar* ui_string = " \
|
|
<ui> \
|
|
<menubar name=\"MainMenu\"> \
|
|
<menu name=\"FileMenu\" action=\"FileMenuAction\"> \
|
|
<menuitem name=\"Open\" action=\"OpenFile\" /> \
|
|
<menuitem name=\"Save\" action=\"SaveFile\" /> \
|
|
<menuitem name=\"Print\" action=\"Print\" /> \
|
|
<separator name=\"Separator1\"/> \
|
|
<menuitem name=\"Import\" action=\"Import\" /> \
|
|
<separator name=\"Separator2\"/> \
|
|
<menuitem name=\"Units\" action=\"Units\" /> \
|
|
<menuitem name=\"Renumber\" action=\"Renumber\" /> \
|
|
<separator name=\"Separator3\"/> \
|
|
<menuitem name=\"Quit\" action=\"Quit\" /> \
|
|
</menu> \
|
|
</menubar> \
|
|
</ui> \
|
|
";
|
|
|
|
static GtkWidget *get_menubar_menu(GtkWidget *window)
|
|
{
|
|
GtkActionGroup *action_group = gtk_action_group_new("Menu");
|
|
gtk_action_group_add_actions(action_group, menu_items, nmenu_items, 0);
|
|
|
|
GtkUIManager *ui_manager = gtk_ui_manager_new();
|
|
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;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i;
|
|
GtkWidget *win;
|
|
GtkWidget *paned;
|
|
GtkWidget *info_box;
|
|
GtkWidget *notebook;
|
|
GtkWidget *frame;
|
|
GtkWidget *dive_info;
|
|
GtkWidget *equipment;
|
|
GtkWidget *menubar;
|
|
GtkWidget *vbox;
|
|
|
|
output_units = SI_units;
|
|
parse_xml_init();
|
|
|
|
gtk_init(&argc, &argv);
|
|
|
|
g_type_init();
|
|
gconf = gconf_client_get_default();
|
|
|
|
if (gconf_client_get_bool(gconf, GCONF_NAME(feet), NULL))
|
|
output_units.length = FEET;
|
|
if (gconf_client_get_bool(gconf, GCONF_NAME(psi), NULL))
|
|
output_units.pressure = PSI;
|
|
if (gconf_client_get_bool(gconf, GCONF_NAME(cuft), NULL))
|
|
output_units.volume = CUFT;
|
|
if (gconf_client_get_bool(gconf, GCONF_NAME(fahrenheit), NULL))
|
|
output_units.temperature = FAHRENHEIT;
|
|
|
|
error_info_bar = NULL;
|
|
win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
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;
|
|
|
|
menubar = get_menubar_menu(win);
|
|
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
|
|
|
|
/* HPane for left the dive list, and right the dive info */
|
|
paned = gtk_hpaned_new();
|
|
gtk_box_pack_end(GTK_BOX(vbox), paned, TRUE, TRUE, 0);
|
|
|
|
/* Create the actual divelist */
|
|
dive_list = dive_list_create();
|
|
gtk_paned_add1(GTK_PANED(paned), dive_list.container_widget);
|
|
|
|
/* VBox for dive info, and tabs */
|
|
info_box = gtk_vbox_new(FALSE, 6);
|
|
gtk_paned_add2(GTK_PANED(paned), info_box);
|
|
|
|
/* Frame for minimal dive info */
|
|
frame = dive_info_frame();
|
|
gtk_box_pack_start(GTK_BOX(info_box), frame, FALSE, TRUE, 6);
|
|
|
|
/* Notebook for dive info vs profile vs .. */
|
|
notebook = gtk_notebook_new();
|
|
gtk_box_pack_start(GTK_BOX(info_box), notebook, TRUE, TRUE, 6);
|
|
|
|
/* Frame for dive profile */
|
|
dive_profile = dive_profile_widget();
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_profile, gtk_label_new("Dive Profile"));
|
|
|
|
/* Frame for extended dive info */
|
|
dive_info = extended_dive_info_widget();
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), dive_info, gtk_label_new("Dive Notes"));
|
|
|
|
/* Frame for dive equipment */
|
|
equipment = equipment_widget();
|
|
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), equipment, gtk_label_new("Equipment"));
|
|
|
|
gtk_widget_set_app_paintable(win, TRUE);
|
|
gtk_widget_show_all(win);
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
const char *a = argv[i];
|
|
|
|
if (a[0] == '-') {
|
|
parse_argument(a);
|
|
continue;
|
|
}
|
|
GError *error = NULL;
|
|
parse_xml_file(a, &error);
|
|
|
|
if (error != NULL)
|
|
{
|
|
report_error(error);
|
|
g_error_free(error);
|
|
error = NULL;
|
|
}
|
|
}
|
|
|
|
report_dives();
|
|
dive_list_update_dives(dive_list);
|
|
|
|
gtk_main();
|
|
return 0;
|
|
}
|