All pastes #2050735 Raw Edit

Miscellany

public text v1 · immutable
#2050735 ·published 2011-04-26 02:58 UTC
rendered paste body
/**\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 <http://www.gnu.org/licenses/>.
    
*/

#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <float.h>
#include <glib-2.0/glib.h>
#include <hamdat/geography.h>
#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_BANDS; ++i) {
    GtkTreeIter it;
    gtk_list_store_append(ls, &it);
    gtk_list_store_set(ls, &it, 0, hamdat_band_strings[i], -1);
  }
  GtkCellRenderer* render_cell_text = gtk_cell_renderer_text_new();
  GtkWidget* band_combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ls));
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(band_combobox), render_cell_text, TRUE);
  gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(band_combobox), render_cell_text, "text", 0, NULL);
  g_signal_connect(band_combobox, "changed", G_CALLBACK(band_combobox_changed), band);
  gtk_combo_box_set_active(GTK_COMBO_BOX(band_combobox), *band-1);
  return band_combobox;
}
static GtkWidget* get_widget_for_mode(enum hamdat_mode* mode, gboolean horizontal, GError **err) {
  if((*mode < 1) || (*mode >= 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_MODES; ++i) {
    GtkTreeIter it;
    gtk_list_store_append(ls, &it);
    gtk_list_store_set(ls, &it, 0, hamdat_mode_strings[i], -1);
  }
  GtkCellRenderer* render_cell_text = gtk_cell_renderer_text_new();
  GtkWidget* mode_combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ls));
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(mode_combobox), render_cell_text, TRUE);
  gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(mode_combobox), render_cell_text, "text", 0, NULL);
  g_signal_connect(mode_combobox, "changed", G_CALLBACK(mode_combobox_changed), mode);
  gtk_combo_box_set_active(GTK_COMBO_BOX(mode_combobox), *mode-1);
  return mode_combobox;
}
static GtkWidget* get_widget_for_2char(char* val, gboolean horizontal, GError **err) {
  enum hamdat_continent continent = hamdat_get_continent_from_abbreviation(val);
  if((continent < 0) || (continent >= 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; i<HAMDAT_END_OF_CONTINENTS; ++i) {
    GtkTreeIter it;
    gtk_list_store_append(ls, &it);
    gtk_list_store_set(ls, &it, 0, hamdat_continent_full_strings[i], -1);
  }
  GtkCellRenderer* render_cell_text = gtk_cell_renderer_text_new();
  GtkWidget* continent_combobox = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ls));
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(continent_combobox), render_cell_text, TRUE);
  gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(continent_combobox), render_cell_text, "text", 0, NULL);
  g_signal_connect(continent_combobox, "changed", G_CALLBACK(continent_combobox_changed), val);
  gtk_combo_box_set_active(GTK_COMBO_BOX(continent_combobox), continent);
  return continent_combobox;
}
static GtkWidget* get_widget_for_string(char** s, gboolean horizontal, GError **err) {
  struct string_callback_datum *scd = (struct string_callback_datum*)malloc(sizeof(struct string_callback_datum));
  if(scd == NULL) {
    *err = g_error_new(GERROR_DOMAIN_QSOLOG_FIELD_AUTOLINE, 10, "qsolog_field_autoline.c::get_widget_for_string: failed to allocate struct for the callback data");
    return NULL;
  }
  scd->s = 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;
}