/**\file provides routines for user interactions with a GTK GUI for each field. * Copyright (C) 2011 Joseph Pingenot This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include "qsolog_field_autoline.h" #define GERROR_DOMAIN_QSOLOG_FIELD_AUTOLINE 1024 static void free_string_callback_data(gpointer data, GClosure* closure) { free(data); } /*Signal handlers for the widgets*/ void band_combobox_changed(GtkComboBox* cb, gpointer user_data) { fprintf(stderr, "band changed from %d", *(enum hamdat_band*)user_data); *((enum hamdat_band*)user_data) = gtk_combo_box_get_active(cb) + 1; fprintf(stderr, "to %d\n", *(enum hamdat_band*)user_data); } void mode_combobox_changed(GtkComboBox* cb, gpointer user_data) { fprintf(stderr, "mode changed from %d", *(enum hamdat_band*)user_data); *((enum hamdat_mode*)user_data) = gtk_combo_box_get_active(cb) + 1; fprintf(stderr, "to %d\n", *(enum hamdat_band*)user_data); } void continent_combobox_changed(GtkComboBox* cb, gpointer user_data) { hamdat_assign_continent_abbreviation((char*)user_data, gtk_combo_box_get_active(cb)); } struct string_callback_datum { char** s; }; void string_entry_lost_focus(GtkWidget* w, gpointer user_data) { struct string_callback_datum* scd = (struct string_callback_datum*)user_data; fprintf(stderr, "string changed from %s", *(scd->s)); //if(*(scd->s) != NULL) g_free(*(scd->s)); *(scd->s) = g_strdup(gtk_entry_get_text(GTK_ENTRY(w))); fprintf(stderr, "to %s\n", *(scd->s)); } void long_int_changed(GtkSpinButton* sb, gpointer user_data) { *((long int*)user_data) = gtk_adjustment_get_value(gtk_spin_button_get_adjustment(sb)); } void int_changed(GtkSpinButton* sb, gpointer user_data) { *((int*)user_data) = gtk_adjustment_get_value(gtk_spin_button_get_adjustment(sb)); } void short_unsigned_changed(GtkSpinButton* sb, gpointer user_data) { *((short unsigned*)user_data) = gtk_adjustment_get_value(gtk_spin_button_get_adjustment(sb)); } void unsigned_changed(GtkSpinButton* sb, gpointer user_data) { *((unsigned*)user_data) = gtk_adjustment_get_value(gtk_spin_button_get_adjustment(sb)); } void long_unsigned_changed(GtkSpinButton* sb, gpointer user_data) { *((long unsigned*)user_data) = gtk_adjustment_get_value(gtk_spin_button_get_adjustment(sb)); } void double_changed(GtkSpinButton* sb, gpointer user_data) { *((double*)user_data) = gtk_adjustment_get_value(gtk_spin_button_get_adjustment(sb)); } struct date_data { GtkAdjustment *day_adjustment; long int date; gboolean localtime; }; static const int days_in_months[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; void month_changed(GtkComboBox *cb, gpointer user_data) { struct date_data *md= (struct date_data*) user_data; struct tm stm; if(md->localtime) { localtime_r(&(md->date), &stm); }else{ gmtime_r(&(md->date), &stm); } stm.tm_mon = gtk_combo_box_get_active(cb); gtk_adjustment_set_upper(md->day_adjustment, days_in_months[stm.tm_mon]); md->date = mktime(&stm); } enum date_piece { SECOND, MINUTE, HOUR, DAY, YEAR }; void date_adjustment_changed(GtkAdjustment *adj, gpointer user_data) { struct date_data *md= (struct date_data*) user_data; struct tm stm; if(md->localtime) { localtime_r(&(md->date), &stm); }else{ gmtime_r(&(md->date), &stm); } enum date_piece dp; g_object_get(G_OBJECT(adj), "user-data", &dp, NULL); switch(dp){ case SECOND: stm.tm_sec = gtk_adjustment_get_value(adj); break; case MINUTE: stm.tm_min = gtk_adjustment_get_value(adj); break; case HOUR: stm.tm_hour = gtk_adjustment_get_value(adj); break; case DAY: stm.tm_mday = gtk_adjustment_get_value(adj); break; case YEAR: stm.tm_year = gtk_adjustment_get_value(adj) - 1900; break; } md->date = mktime(&stm); } static void free_date_data(gpointer data, GClosure* closure) { free(data); } static GtkWidget* get_widget_for_band(enum hamdat_band* band, gboolean horizontal, GError **err) { if((*band < 1) || (*band >= HAMDAT_END_OF_BANDS)) { *err = g_error_new(GERROR_DOMAIN_QSOLOG_FIELD_AUTOLINE, 10, "qsolog_field_autoline.c::get_widget_for_band: invalid band given (%d; <1 or >= %d)\n", *band, HAMDAT_END_OF_BANDS); return NULL; } GtkListStore* ls = gtk_list_store_new(1, G_TYPE_STRING); gtk_list_store_clear(ls); for(int i=1; i= HAMDAT_END_OF_MODES)) { *err = g_error_new(GERROR_DOMAIN_QSOLOG_FIELD_AUTOLINE, 10, "qsolog_field_autoline.c::get_widget_for_mode: invalid mode given (%d; <1 or >= %d)\n", *mode, HAMDAT_END_OF_MODES); return NULL; } GtkListStore* ls = gtk_list_store_new(1, G_TYPE_STRING); gtk_list_store_clear(ls); for(int i=1; i= HAMDAT_END_OF_CONTINENTS)) { *err = g_error_new(GERROR_DOMAIN_QSOLOG_FIELD_AUTOLINE, 10, "qsolog_field_autoline.c::get_widget_for_continent: invalid continent given (%d; <0 or >= %d)\n", continent, HAMDAT_END_OF_CONTINENTS); return NULL; } GtkListStore* ls = gtk_list_store_new(1, G_TYPE_STRING); gtk_list_store_clear(ls); for(int i=0; is = s; GtkWidget* entry = gtk_entry_new(); gtk_entry_set_text(GTK_ENTRY(entry), (*s != NULL)?*s:""); g_signal_connect_data(entry, "focus-out-event", G_CALLBACK(string_entry_lost_focus), scd, free_string_callback_data, G_CONNECT_AFTER); return entry; } static GtkWidget* get_widget_for_long_int(long int* val, gboolean horizontal, const long int* range, GError **err) { long int min, max, step, page; if(range != NULL) { min = range[0]; max = range[1]; step = range[2]; page = range[3]; }else{ min = LONG_MIN; max=LONG_MAX; step = 10; page = 1000; } /*\todo needs updated for longs on 64bit systems*/ GtkObject* adj = gtk_adjustment_new(*val, min, max, step, page, 0); GtkWidget* sb = gtk_spin_button_new(GTK_ADJUSTMENT(adj), step, 0); g_signal_connect(sb, "value-changed", G_CALLBACK(long_int_changed), val); return sb; } static GtkWidget* get_widget_for_short_unsigned(short unsigned* val, gboolean horizontal, const short unsigned* range, GError **err) { short unsigned min, max, step, page; if(range != NULL) { min = range[0]; max = range[1]; step = range[2]; page = range[3]; }else{ min = 0; max=USHRT_MAX; step = 10; page = 1000; } /*\todo needs updated for longs on 64bit systems*/ GtkObject* adj = gtk_adjustment_new(*val, min, max, step, page, 0); GtkWidget* sb = gtk_spin_button_new(GTK_ADJUSTMENT(adj), step, 0); g_signal_connect(sb, "value-changed", G_CALLBACK(short_unsigned_changed), val); return sb; } static GtkWidget* get_widget_for_double(double* val, gboolean horizontal, const double* range, GError **err) { double min, max, step, page; if(range != NULL) { min = range[0]; max = range[1]; step = range[2]; page = range[3]; }else{ min = DBL_MIN; max=DBL_MAX; step = 10; page = 1000; } /*\todo needs updated for longs on 64bit systems*/ GtkObject* adj = gtk_adjustment_new(*val, min, max, step, page, 0); GtkWidget* sb = gtk_spin_button_new(GTK_ADJUSTMENT(adj), step, 0); g_signal_connect(sb, "value-changed", G_CALLBACK(double_changed), val); return sb; } static GtkWidget* get_widget_for_unsigned(unsigned* val, gboolean horizontal, const unsigned* range, GError **err) { unsigned min, max, step, page; if(range != NULL) { min = range[0]; max = range[1]; step = range[2]; page = range[3]; }else{ min = 0; max=UINT_MAX; step = 10; page = 1000; } /*\todo needs updated for longs on 64bit systems*/ GtkObject* adj = gtk_adjustment_new(*val, min, max, step, page, 0); GtkWidget* sb = gtk_spin_button_new(GTK_ADJUSTMENT(adj), step, 0); g_signal_connect(sb, "value-changed", G_CALLBACK(unsigned_changed), val); return sb; } static GtkWidget* get_widget_for_long_unsigned(long unsigned* val, gboolean horizontal, const long unsigned* range, GError **err) { long unsigned min, max, step, page; if(range != NULL) { min = range[0]; max = range[1]; step = range[2]; page = range[3]; }else{ min = 0; max=ULONG_MAX; step = 10; page = 1000; } /*\todo needs updated for longs on 64bit systems*/ GtkObject* adj = gtk_adjustment_new(*val, min, max, step, page, 0); GtkWidget* sb = gtk_spin_button_new(GTK_ADJUSTMENT(adj), step, 0); g_signal_connect(sb, "value-changed", G_CALLBACK(long_unsigned_changed), val); return sb; } static GtkWidget* get_widget_for_db_index(long int* val, gboolean horizontal, const long int* range, GError **err) { GString *s = g_string_new_len("", 64); g_string_printf(s, "%ld", *val); /*This is informative, not interactive, so we're just gonna use a GtkLabel*/ GtkWidget* w = gtk_label_new(s->str); g_string_free(s, TRUE); return w; } static GtkWidget* get_widget_for_date(long int* val, gboolean horizontal, const long int* range, gboolean localtime, GError **err) { struct tm stm; if(localtime) { localtime_r(val, &stm); }else{ gmtime_r(val, &stm); } GtkWidget* box; if(horizontal) { box = gtk_hbox_new(FALSE, 5); }else{ box = gtk_vbox_new(FALSE, 5); } struct date_data *dd = (struct date_data*)malloc(sizeof(struct date_data)); dd->date = *val; dd->localtime = localtime; GtkAdjustment *adj = (GtkAdjustment*)gtk_adjustment_new(stm.tm_sec, 0, 60, 1, 15, 0); g_object_set(G_OBJECT(adj), "user-data", SECOND, NULL); g_signal_connect_data(adj, "changed", G_CALLBACK(date_adjustment_changed), dd, free_date_data, G_CONNECT_AFTER); GtkWidget *sb = gtk_spin_button_new(adj, 1, 2); gtk_box_pack_end(GTK_BOX(box), sb, FALSE, FALSE, 2); adj= (GtkAdjustment*)gtk_adjustment_new(stm.tm_min, 0, 59, 1, 15, 0); g_object_set(G_OBJECT(adj), "user-data", MINUTE, NULL); g_signal_connect(adj, "changed", G_CALLBACK(date_adjustment_changed), dd); sb = gtk_spin_button_new(adj, 1, 2); gtk_box_pack_end(GTK_BOX(box), sb, FALSE, FALSE, 2); adj= (GtkAdjustment*)gtk_adjustment_new(stm.tm_hour, 0, 23, 1, 4, 0); g_object_set(G_OBJECT(adj), "user-data", HOUR, NULL); g_signal_connect(adj, "changed", G_CALLBACK(date_adjustment_changed), dd); sb = gtk_spin_button_new(adj, 1, 2); gtk_box_pack_end(GTK_BOX(box), sb, FALSE, FALSE, 2); adj= (GtkAdjustment*)gtk_adjustment_new(stm.tm_mday, 0, days_in_months[stm.tm_mon], 1, 7, 0); dd->day_adjustment = adj; g_object_set(G_OBJECT(adj), "user-data", DAY, NULL); g_signal_connect(adj, "changed", G_CALLBACK(date_adjustment_changed), dd); sb = gtk_spin_button_new(adj, 1, 2); gtk_box_pack_end(GTK_BOX(box), sb, FALSE, FALSE, 2); GtkListStore* ls = gtk_list_store_new(1, G_TYPE_STRING); gtk_list_store_clear(ls); for(int i=0; i<12; ++i) { GtkTreeIter it; gtk_list_store_append(ls, &it); struct tm tmp_tm; tmp_tm.tm_mon = i; char buff[4096]; /**\note valgrind complains here, but we don't care about fields other tm_mon, so it's OK.*/ strftime(buff, 4096, "%B", &tmp_tm); gtk_list_store_set(ls, &it, 0, buff, -1); } GtkCellRenderer* render_cell_text = gtk_cell_renderer_text_new(); GtkWidget* month_combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ls)); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(month_combobox), render_cell_text, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(month_combobox), render_cell_text, "text", 0, NULL); g_signal_connect(month_combobox, "changed", G_CALLBACK(month_changed), dd); gtk_combo_box_set_active(GTK_COMBO_BOX(month_combobox), stm.tm_mon); gtk_box_pack_end(GTK_BOX(box), month_combobox, FALSE, FALSE, 2); adj= (GtkAdjustment*)gtk_adjustment_new(1900 + stm.tm_year, 1878, 2371, 1, 10, 0); g_object_set(G_OBJECT(adj), "user-data", YEAR, NULL); g_signal_connect(adj, "changed", G_CALLBACK(date_adjustment_changed), dd); sb = gtk_spin_button_new(adj, 1, 0); gtk_box_pack_end(GTK_BOX(box), sb, FALSE, FALSE, 2); return box; } GtkWidget* qsolog_field_autoline_get_widget_for_field(enum logentry_field field, struct hamdat_logentry *le, gboolean horizontal, gboolean localtime, GError **err) { if((field < 0) || (field >= HAMDAT_LOGENTRY_FIELD_END_OF_LOGENTRY_COLUMNS)) { g_error_new(GERROR_DOMAIN_QSOLOG_FIELD_AUTOLINE, 1, "qsolog_field_autoline_get_widget_for_field: ERROR: field (%d) < 0 or >= HAMDAT_LOGENTRY_FIELD_END_OF_LOGENTRY_COLUMNS (%d)\n", field, HAMDAT_LOGENTRY_FIELD_END_OF_LOGENTRY_COLUMNS); return NULL; } /*Calculate the location*/ char* member_address = ((char*)le) + logentry_fields[field].offset; switch(logentry_fields[field].type) { case HAMDAT_LOGENTRY_FIELD_TYPE_LONG_INT: return get_widget_for_long_int((long int*)member_address, horizontal, (const long int*)logentry_fields[field].range, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_STRING: return get_widget_for_string((char**)member_address, horizontal, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_ENUM_BAND: return get_widget_for_band((enum hamdat_band*)member_address, horizontal, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_DOUBLE: return get_widget_for_double((double*)member_address, horizontal, (const double*)logentry_fields[field].range, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_SHORT_UNSIGNED: return get_widget_for_short_unsigned((short unsigned*)member_address, horizontal, (const short unsigned*)logentry_fields[field].range, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_ENUM_MODE: return get_widget_for_mode((enum hamdat_mode*)member_address, horizontal, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_2CHAR: return get_widget_for_2char(member_address, horizontal, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_UNSIGNED: return get_widget_for_unsigned((unsigned*)member_address, horizontal, (const unsigned*)logentry_fields[field].range, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_LONG_UNSIGNED: return get_widget_for_long_unsigned((long unsigned*)member_address, horizontal, (const long unsigned*)logentry_fields[field].range, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_DATE: return get_widget_for_date((long int*)member_address, horizontal, (const long int*)logentry_fields[field].range, localtime, err); break; case HAMDAT_LOGENTRY_FIELD_TYPE_DB_INDEX: return get_widget_for_db_index((long int*)member_address, horizontal, (const long int*)logentry_fields[field].range, err); break; } /*If we reach this point, we didn't have a handler for this type.*/ *err = g_error_new(GERROR_DOMAIN_QSOLOG_FIELD_AUTOLINE, 100, "qsolog_field_autoline.c::qsolog_field_autoline_get_widget_for_field: unknown field %d\n", field); return NULL; }