GncDialog case-study

Chris Shoemaker c.shoemaker at cox.net
Mon Feb 21 17:55:48 EST 2005


As per Derek's request, I've examined what an existing dialog would
look like if it were to use a generic "GncDialog" (new working name)
api.  I selected the New/Edit Commodities dialog because it seems like
a "average" dialog -- it's not super-complicated, but it's not trivial
either.  

I tried to make a fairly minimal set of changes to convert to the
proposed api.  Back-fitting a more abstract api where a more concrete
one was used isn't going to result in the same design as if the dialog
were originally written to the proposed api.  So the patched code is
not as simple as it could be, but I think it shows the gist of things.

The diff is attached and I encourage interested parties to patch
dialog-commodity.c and especially compare patched and unpatched
versions of gnc_ui_new_commodity_dialog() and
gnc_ui_common_commodity_modal().  I'll report on a few of the
observations I made during this exercise.

Code Size:
   Derek seemed especially curious what effect the proposed api would
have on code size.  For this dialog, the code size reduction is about
9%. (from 22Kb to 20Kb) Note: dialog-commodity.c also contains the
code for a differnt "commodity selector" gui, that I'm not counting
here.  IMO, this code-reduction is one of the less-compelling
advantages of the proposed api.
   So, what large chunks of code were actually abstracted away?  Well,
with the proposed api, it's no longer necessary to declare large
"widget lists" like "struct commodity_window", because the GncDialog
acts like a generic widget list.  Also, there's no need to have a
custom event-handling loop for the commodity dialog, since there's a
generic one in GncDialog.
   There are other simplifications that I didn't make for various
reasons.  E.g. 1) Some functions that are no longer needed by this
dialog could be deleted except that they're also used by the commodity
selection gui.  2) This dialog has some code to disable the OK button
when data is invalid.  I left that alone even though it's against the
spirit of using an abstract dialog to provide the interaction model.
   Otherwise, in this case, the move to a higher level of abstraction
didn't reduce code size much.
   (I didn't look into this, but there are also simplifications on the
glade side, too.  E.g. there's no need to connect signals to each
widget to watch for changes, (see below for why not), and there's no
need to specify the GtkDialog, since only the named GtkTable is
loaded.)

libglade:
   Currently, each dialog includes and uses libglade directly.
   The patched dialog no longer has any need to include or use any of
libglade.  IOW, the dialog usage is completely de-coupled from
libglade.  All libglade code is in the GncDialog.  That also means
that, in the future, if we discover that libglade is a performance
bottleneck, we can optimize in just one place - GncDialog - and reap
the benefit for all GncDialog users.

Gtk+:
   Currently, each dialog accesses the dialog widgets using each
widget's (gtk) api directly.  Naturally, the widget type must exactly
match what was specified in the glade file.  For example, where the
glade file uses a (deprecated) GtkCombo, the dialog usage must go:
namespace = gtk_entry_get_text (GTK_ENTRY(GTK_COMBO (combobox)->entry)).

   In the patched version, the call above is:
namespace = gnc_dialog_get_string(d, NAMESPACE);

   In fact, almost (*) all dialog widget access is wrapped by
GncDialog.  Again, GncDialog decouples the dialog usage code from the
specific widget's gtk+ api.  (Actually, this is only true for simple
widget types.  So, e.g. using GtkTreeView would still require direct
use of the gtk+ api, but this dialog only uses simple widgets.)  This
means that the deprecated GtkCombo could be replaced by GtkComboEntry
in the glade file and the gnc_dialog_set_string() would still work
with no changes.  (Currently the GtkCombo populator is shared with the
"commodity selector" gui, so practically, that change would also
require updating that gui.  Furthermore, the new populator would
probably use widget api that GncDialog doesn't wrap, so you'd still
have some direct gtk+ usage.)

(*) "almost all" and not "all" for two reasons: 1) this dialog builds
and installs custom widgets upon loading, and GncDialog doesn't
interfere here, but it doesn't help either.  2) This dialog's glade
file connects some signals directly to the dialog widgets to watch for
changes.  GncDialog _does_ provide help here, offering a single
"changed" signal that can be watched for the dialog, no matter what
widgets it contains.  (I didn't convert to that usage.)


Interface consistency:

   Right now, there's quite a bit of diversity in the layout (like
where the help button is) and interaction models (like what happens to
the buttons when data is valid or invalid) of gnucash's dialogs.

   Using an abstraction like GncDialog would allow these things to be
standardized in one place and used consistently throughout gnucash.


Summary:

   IMO, some of the benefits of the proposed API, in order of
significance are:
   1) the ability to provide interface consistency for gnucash dialogs
   2) moderate isolation of dialog code from changes to the Gtk+ api
   3) code reduction resulting from not rewritting event-loops, change
detectors, and "widget-lists"


As always, comments are welcome.  Oh, I'm also attaching revised
copies of the proposed api.  Just the header this time, though, and
minus the original comment block.

-chris
-------------- next part --------------
--- src/gnome-utils/dialog-commodity.c	2005-02-20 20:30:36.066054289 -0500
+++ src/gnome-utils/NEWdialog-commodity.c	2005-02-21 16:00:31.575142164 -0500
@@ -41,6 +41,7 @@
 #include "gnc-ui-util.h"
 #include "gnc-ui.h"
 #include "messages.h"
+#include "gnc-dialog.h"
 
 static short module = MOD_GUI;
 
@@ -61,30 +62,32 @@
   int          default_fraction;
 };
 
-struct commodity_window {
-  GtkWidget * dialog;
-  GtkWidget * commodity_info_frame;
-  GtkWidget * fullname_entry;
-  GtkWidget * mnemonic_entry;
-  GtkWidget * namespace_combo;
-  GtkWidget * code_entry;
-  GtkWidget * fraction_spinbutton;
-  GtkWidget * get_quote_check;
-  GtkWidget * source_label;
-  GtkWidget * source_button[SOURCE_MAX];
-  GtkWidget * source_menu[SOURCE_MAX];
-  GtkWidget * quote_tz_label;
-  GtkWidget * quote_tz_menu;
-  GtkWidget * ok_button;
-
-  gnc_commodity *edit_commodity;
-};
-
 typedef struct select_commodity_window SelectCommodityWindow;
 typedef struct commodity_window CommodityWindow;
 
 static gnc_commodity_help_callback help_callback = NULL;
 
+#define COMMODITY_INFO        "commodity_info_frame"
+#define FULLNAME              "fullname_entry"
+#define MNEMONIC              "mnemonic_entry"
+#define NAMESPACE             "namespace_combo"
+#define CODE                  "code_entry"
+#define FRACTION              "fraction_spinbutton"
+#define GET_QUOTE             "get_quote_check"
+#define SOURCE_LABEL          "source_label"
+#define QUOTE_TZ_LABEL        "quote_tz_label"
+#define QUOTE_TZ_MENU         "quote_tz_box"
+#define SINGLE_SOURCE_BUTTON  "single_source_button"
+#define MULTI_SOURCE_BUTTON   "multi_source_button"
+#define UNKNOWN_SOURCE_BUTTON "unknown_source_button"
+#define SINGLE_SOURCE_MENU    "single_source_box"
+#define MULTI_SOURCE_MENU     "multi_source_box"
+#define UNKNOWN_SOURCE_MENU   "unknown_source_box"
+
+static char *SOURCE_BUTTON_LIST[] = {
+    SINGLE_SOURCE_BUTTON, MULTI_SOURCE_BUTTON, UNKNOWN_SOURCE_BUTTON };
+static char *SOURCE_MENU_LIST[] = {
+    SINGLE_SOURCE_MENU, MULTI_SOURCE_MENU, UNKNOWN_SOURCE_MENU };
 
 /* The commodity selection window */
 static SelectCommodityWindow *
@@ -100,7 +103,7 @@
 /* The commodity creation window */
 void gnc_ui_commodity_changed_cb(GtkWidget * dummy, gpointer user_data);
 void gnc_ui_commodity_quote_info_cb(GtkWidget *w, gpointer data);
-gboolean gnc_ui_commodity_dialog_to_object(CommodityWindow * w);
+gboolean gnc_ui_commodity_dialog_to_object(GncDialog *d, gpointer data);
 
 #if 0
 static void gnc_ui_select_commodity_response_cb (GtkDialog * dialog, gint response, gpointer data);
@@ -273,7 +276,7 @@
 /**
  *  This function is called whenever the user clicks on the "New"
  *  button in the commodity picker.  Its function is pop up a new
- *  dialog alling the user to create a new commodity.
+ *  dialog allowing the user to create a new commodity.
  *
  *  @note This function is an internal helper function for the
  *  Commodity Selection dialog.  It should not be used outside of the
@@ -586,12 +589,19 @@
     return namespace;
 }
 
+const char *
+gnc_ui_commodity_get_namespace(GncDialog *d)
+{
+    char *ns = gnc_dialog_get_string(d, NAMESPACE);
+    return safe_strcmp(namespace, "CURRENCY") ? ns : GNC_COMMODITY_NS_ISO;
+}
+
 /********************************************************************/
 
 void
 gnc_ui_commodity_quote_info_cb (GtkWidget *w, gpointer data)
 {
-  CommodityWindow *cw = data;
+  GncDialog *d = data;
   gboolean get_quote, allow_src, active;
   const char *text;
   gint i;
@@ -599,45 +609,46 @@
   ENTER(" ");
   get_quote = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w));
 
-  text = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(cw->namespace_combo)->entry));
+  text = gnc_dialog_get_string(d, NAMESPACE);
   allow_src = !gnc_commodity_namespace_is_iso(text);
-  gtk_widget_set_sensitive(cw->source_label, get_quote && allow_src);
+  gnc_dialog_set_sensitive(d, SOURCE_LABEL, get_quote && allow_src);
 
   for (i = SOURCE_SINGLE; i < SOURCE_MAX; i++) {
-    if (!cw->source_button[i])
+    if (!SOURCE_BUTTON_LIST[i])
       continue;
-    active =
-      gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cw->source_button[i]));
-    gtk_widget_set_sensitive(cw->source_button[i], get_quote && allow_src);
-    gtk_widget_set_sensitive(cw->source_menu[i], get_quote && allow_src && active);
+    active = gnc_dialog_get_boolean(d, SOURCE_BUTTON_LIST[i]);
+    gnc_dialog_set_sensitive(d, SOURCE_BUTTON_LIST[i], get_quote && allow_src);
+    gnc_dialog_set_sensitive(d, SOURCE_MENU_LIST[i], 
+                             get_quote && allow_src && active);
   }
-  gtk_widget_set_sensitive(cw->quote_tz_label, get_quote);
-  gtk_widget_set_sensitive(cw->quote_tz_menu, get_quote);
+  gnc_dialog_set_sensitive(d, QUOTE_TZ_LABEL, get_quote);
+  gnc_dialog_set_sensitive(d, QUOTE_TZ_MENU, get_quote);
   LEAVE(" ");
 }
 
 void
 gnc_ui_commodity_changed_cb(GtkWidget * dummy, gpointer user_data)
 {
-  CommodityWindow * w = user_data;
+  GncDialog *d = user_data;
   const char * namespace;
   const char * fullname;
   const char * mnemonic;
   gboolean ok;
 
   ENTER("widget=%p, user_data=%p", dummy, user_data);
-  if (GTK_WIDGET_SENSITIVE(w->commodity_info_frame)) {
-    namespace = gnc_ui_namespace_picker_ns (w->namespace_combo);
-    fullname  = gtk_entry_get_text(GTK_ENTRY(w->fullname_entry));
-    mnemonic  = gtk_entry_get_text(GTK_ENTRY(w->mnemonic_entry));
+  if (GTK_WIDGET_SENSITIVE(gnc_dialog_get_widget(d, COMMODITY_INFO))) {
+    namespace = gnc_commodity_get_namespace(d);
+    fullname = gnc_dialog_get_string(d, FULLNAME);
+    mnemonic = gnc_dialog_get_string(d, MNEMONIC);
     DEBUG("namespace=%s, name=%s, mnemonic=%s", namespace, fullname, mnemonic);
     ok = (fullname    && namespace    && mnemonic &&
 	  fullname[0] && namespace[0] && mnemonic[0]);
   } else {
     ok = TRUE;
   }
-  gtk_widget_set_sensitive(w->ok_button, ok);
-  gtk_dialog_set_default_response(GTK_DIALOG(w->dialog), ok ? 0 : 1);
+  gtk_dialog_set_response_sensitive(GTK_DIALOG(d), GTK_RESPONSE_OK, ok);
+  gtk_dialog_set_default_response(
+      GTK_DIALOG(d), ok ? GTK_RESPONSE_OK : GTK_RESPONSE_CANCEL);
   LEAVE("sensitive=%d, default = %d", ok, ok ? 0 : 1);
 }
 
@@ -756,10 +767,10 @@
 
   return omenu;
 }
-
+   
 /** Build the new/edit commodity dialog box
  */
-static CommodityWindow *
+static GncDialog *
 gnc_ui_new_commodity_dialog(const char * selected_namespace,
 			    GtkWidget  *parent,
 			    const char * fullname,
@@ -767,74 +778,36 @@
 			    const char * cusip,
 			    int          fraction)
 {
-  CommodityWindow * retval = g_new0(CommodityWindow, 1);
+  GncDialog *d;
   GtkWidget *help_button;
   GtkWidget *box;
   GtkWidget *menu;
-  GladeXML *xml;
   gboolean include_iso;
+  QuoteSourceType type;
 
   ENTER(" ");
-  xml = gnc_glade_xml_new ("commodity.glade", "Commodity Dialog");
+  d = gnc_dialog_new("commodity.glade", "Commodity");
 
-  glade_xml_signal_autoconnect_full( xml,
-                                     gnc_glade_autoconnect_full_func,
-                                     retval );
-
-  retval->dialog = glade_xml_get_widget (xml, "Commodity Dialog");
   if (parent != NULL)
-    gtk_window_set_transient_for (GTK_WINDOW (retval->dialog), GTK_WINDOW (parent));
-  retval->edit_commodity = NULL;
-
-  help_button = glade_xml_get_widget (xml, "help_button");
-  if (!help_callback)
-    gtk_widget_hide (help_button);
-
-
-  /* Get widget pointers */
-  retval->commodity_info_frame = glade_xml_get_widget (xml, "commodity_info_frame");
-  retval->fullname_entry = glade_xml_get_widget (xml, "fullname_entry");
-  retval->mnemonic_entry = glade_xml_get_widget (xml, "mnemonic_entry");
-  retval->namespace_combo = glade_xml_get_widget (xml, "namespace_combo");
-  retval->code_entry = glade_xml_get_widget (xml, "code_entry");
-  retval->fraction_spinbutton = glade_xml_get_widget (xml,
-                                                      "fraction_spinbutton");
-  retval->ok_button = glade_xml_get_widget (xml, "ok_button");
-  retval->get_quote_check = glade_xml_get_widget (xml, "get_quote_check");
-  retval->source_label = glade_xml_get_widget (xml, "source_label");
-  retval->source_button[SOURCE_SINGLE] = glade_xml_get_widget (xml, "single_source_button");
-  retval->source_button[SOURCE_MULTI] = glade_xml_get_widget (xml, "multi_source_button");
-  retval->quote_tz_label = glade_xml_get_widget (xml, "quote_tz_label");
-
+      gtk_window_set_transient_for (GTK_WINDOW(d), GTK_WINDOW (parent));
 
   /* Build custom widgets */
-  box = glade_xml_get_widget (xml, "single_source_box");
-  menu = gnc_ui_source_menu_create(SOURCE_SINGLE);
-  retval->source_menu[SOURCE_SINGLE] = menu;
-  gtk_box_pack_start(GTK_BOX(box), menu, TRUE, TRUE, 0);
-
-  box = glade_xml_get_widget (xml, "multi_source_box");
-  menu = gnc_ui_source_menu_create(SOURCE_MULTI);
-  retval->source_menu[SOURCE_MULTI] = menu;
-  gtk_box_pack_start(GTK_BOX(box), menu, TRUE, TRUE, 0);
-
-  if (gnc_quote_source_num_entries(SOURCE_UNKNOWN)) {
-    retval->source_button[SOURCE_UNKNOWN] =
-      glade_xml_get_widget (xml, "unknown_source_button");
-    box = glade_xml_get_widget (xml, "unknown_source_box");
-    menu = gnc_ui_source_menu_create(SOURCE_UNKNOWN);
-    retval->source_menu[SOURCE_UNKNOWN] = menu;
-    gtk_box_pack_start(GTK_BOX(box), menu, TRUE, TRUE, 0);
+  for (type = SOURCE_SINGLE; type < SOURCE_MAX; type++) {
+      if (gnc_quote_source_num_entries(type) > 0) {
+          box = gnc_dialog_get_widget(d, SOURCE_MENU_LIST[type]);
+          menu = gnc_ui_source_menu_create(type);
+          gtk_box_pack_start(GTK_BOX(box), menu, TRUE, TRUE, 0);
+      }
   }
 
-  box = glade_xml_get_widget (xml, "quote_tz_box");
-  retval->quote_tz_menu = gnc_ui_quote_tz_menu_create();
-  gtk_box_pack_start(GTK_BOX(box), retval->quote_tz_menu, TRUE, TRUE, 0);
+  box = gnc_dialog_get_widget(d, QUOTE_TZ_MENU);
+  menu = gnc_ui_quote_tz_menu_create();
+  gtk_box_pack_start(GTK_BOX(box), menu, TRUE, TRUE, 0);
 
 
   /* Commodity editing is next to nil */
   if (gnc_commodity_namespace_is_iso(selected_namespace)) {
-    gtk_widget_set_sensitive(retval->commodity_info_frame, FALSE);
+    gtk_widget_set_sensitive(gnc_dialog_get_widget(d, COMMODITY_INFO), FALSE);
     include_iso = TRUE;
   } else {
     include_iso = FALSE;
@@ -842,37 +815,29 @@
 
   /* Are price quotes supported */
   if (gnc_quote_source_fq_installed()) {
-    gtk_widget_destroy(glade_xml_get_widget (xml, "finance_quote_warning"));
+    gtk_widget_destroy(gnc_dialog_get_widget(d, "finance_quote_warning"));
   } else {
-    gtk_widget_set_sensitive(glade_xml_get_widget (xml, "price_quote_frame"),
+    gtk_widget_set_sensitive(gnc_dialog_get_widget(d, "price_quote_frame"),
 			     FALSE);
   }
 
-
-#ifdef DRH
-  g_signal_connect (G_OBJECT (retval->dialog), "close",
-		    G_CALLBACK (commodity_close), retval);
-#endif
   /* Fill in any data, top to bottom */
-  
-  gtk_entry_set_text (GTK_ENTRY (retval->fullname_entry), fullname ? fullname : "");
-  gtk_entry_set_text (GTK_ENTRY (retval->mnemonic_entry), mnemonic ? mnemonic : "");
-  gnc_ui_update_namespace_picker(retval->namespace_combo,
+  gnc_dialog_set_string(d, FULLNAME, fullname ? fullname : "");
+  gnc_dialog_set_string(d, MNEMONIC, mnemonic ? mnemonic : "");
+  gnc_ui_update_namespace_picker(gnc_dialog_get_widget(d, NAMESPACE),
 				 selected_namespace,
 				 include_iso ? DIAG_COMM_ALL : DIAG_COMM_NON_CURRENCY);
-  gtk_entry_set_text (GTK_ENTRY (retval->code_entry), cusip ? cusip : "");
+  gnc_dialog_set_string(d, CODE, cusip ? cusip : "");
   if (fraction > 0)
-    gtk_spin_button_set_value (GTK_SPIN_BUTTON (retval->fraction_spinbutton),
-			       fraction);
+    gnc_dialog_set_int(d, FRACTION, fraction);
 
   LEAVE(" ");
-  return retval;
+  return d;
 }
 
 
 static void
-gnc_ui_commodity_update_quote_info(CommodityWindow *win,
-				   gnc_commodity *commodity)
+gnc_ui_commodity_update_quote_info(GncDialog *d, gnc_commodity *commodity)
 {
   gnc_quote_source *source;
   QuoteSourceType type;
@@ -887,13 +852,12 @@
     source = gnc_commodity_get_default_quote_source (commodity);
   quote_tz = gnc_commodity_get_quote_tz (commodity);
 
-  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (win->get_quote_check),
-				has_quote_src);
+  gnc_dialog_set_boolean(d, GET_QUOTE, has_quote_src);
   if (!gnc_commodity_is_iso(commodity)) {
     type = gnc_quote_source_get_type(source);
-    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->source_button[type]), TRUE);
-    gtk_option_menu_set_history (GTK_OPTION_MENU (win->source_menu[type]),
-				 gnc_quote_source_get_index(source));
+    gnc_dialog_set_boolean(d, SOURCE_BUTTON_LIST[type], TRUE);
+    gnc_dialog_set_index(d, SOURCE_MENU_LIST[type],
+                         gnc_quote_source_get_index(source));
   }
 
   if (quote_tz) {
@@ -903,7 +867,8 @@
 	    quote_tz ? quote_tz : "(null)");
     }
   }
-  gtk_option_menu_set_history (GTK_OPTION_MENU (win->quote_tz_menu), pos);
+
+  gnc_dialog_set_index(d, QUOTE_TZ_MENU, pos);
   LEAVE(" ");
 }
 
@@ -917,7 +882,7 @@
 			      const char * mnemonic,
 			      int fraction)
 {
-  CommodityWindow * win;
+  GncDialog *d;
   gnc_commodity *retval = NULL;
   gboolean done;
   gint value;
@@ -938,41 +903,23 @@
     }
   }
 
-  win = gnc_ui_new_commodity_dialog(namespace, parent, fullname,
-				    mnemonic, code, fraction);
+  d = gnc_ui_new_commodity_dialog(namespace, parent, fullname,
+                                  mnemonic, code, fraction);
 
   /* Update stock quote info based on existing commodity */
-  gnc_ui_commodity_update_quote_info(win, commodity);
-  win->edit_commodity = commodity;
+  gnc_ui_commodity_update_quote_info(d, commodity);
+  retval = commodity;
 
   /* Update stock quote sensitivities based on check box */
-  gnc_ui_commodity_quote_info_cb(win->get_quote_check, win);
-
-  /* Run the dialog, handling the terminal conditions. */
-  done = FALSE;
-  while (!done) {
-    value = gtk_dialog_run(GTK_DIALOG(win->dialog));
-    switch (value) {
-     case GTK_RESPONSE_OK:
-      DEBUG("case OK");
-      done = gnc_ui_commodity_dialog_to_object(win);
-      retval = win->edit_commodity;
-      break;
-     case GTK_RESPONSE_HELP:
-      DEBUG("case HELP");
-      if (help_callback)
-	help_callback ();
-      break;
-     default:	/* Cancel, Escape, Close, etc. */
-      DEBUG("default: %d", value);
-      retval = NULL;
-      done = TRUE;
-      break;
-    }
-  }
-  gtk_widget_destroy (GTK_WIDGET (win->dialog)); /* Close and destroy */
-  g_free(win);
+  gnc_ui_commodity_quote_info_cb(gnc_dialog_get_widget(d, GET_QUOTE), d);
 
+  gnc_dialog_set_cb(
+      d, (GncDialogCallback)gnc_ui_commodity_dialog_to_object, NULL,
+      (GncDialogCallback)help_callback, &retval);
+  
+  gnc_dialog_block_until_close(d);
+  /* retval now points to a valid commodity, or it points to whatever
+     was passed in (possibly NULL), any case, we pass it out */
   LEAVE(" ");
   return retval;
 }
@@ -1041,35 +988,34 @@
  ********************************************************************/
 
 gboolean
-gnc_ui_commodity_dialog_to_object(CommodityWindow * w)
+gnc_ui_commodity_dialog_to_object(GncDialog *d, gpointer userdata)
 {
+  gnc_commodity **commodityp = userdata;
   gnc_quote_source *source;
   QuoteSourceType type;
-  const char * fullname  = gtk_entry_get_text(GTK_ENTRY(w->fullname_entry));
-  const char * namespace = gnc_ui_namespace_picker_ns (w->namespace_combo);
-  const char * mnemonic  = gtk_entry_get_text(GTK_ENTRY(w->mnemonic_entry));
-  const char * code      = gtk_entry_get_text(GTK_ENTRY(w->code_entry));
+  const char * fullname  = gnc_dialog_get_string(d, FULLNAME);
+  const char * namespace = gnc_ui_commodity_get_namespace(d);
+  const char * mnemonic  = gnc_dialog_get_string(d, MNEMONIC);
+  const char * code      = gnc_dialog_get_string(d, CODE);
   QofBook * book = gnc_get_current_book ();
-  int fraction = gtk_spin_button_get_value_as_int
-    (GTK_SPIN_BUTTON(w->fraction_spinbutton));
+  int fraction = gnc_dialog_get_int(d, FRACTION);
   const char *string;
   gnc_commodity * c;
   gint selection;
 
-  ENTER(" ");
+  //ENTER(" ");
   /* Special case currencies */
   if (gnc_commodity_namespace_is_iso (namespace)) {
-    if (w->edit_commodity) {
-      c = w->edit_commodity;
-      gnc_commodity_set_quote_flag (c, gtk_toggle_button_get_active
-				    (GTK_TOGGLE_BUTTON (w->get_quote_check)));
-      selection = gnc_option_menu_get_active (w->quote_tz_menu);
+    if (*commodityp) {
+      c = *commodityp;
+      gnc_commodity_set_quote_flag (c, gnc_dialog_get_boolean(d, GET_QUOTE));
+      selection = gnc_dialog_get_index(d, QUOTE_TZ_MENU);
       string = gnc_timezone_menu_position_to_string(selection);
       gnc_commodity_set_quote_tz(c, string);
       return TRUE;
     }
-    gnc_warning_dialog(w->dialog,
-		       _("You may not create a new national currency."));
+    gnc_warning_dialog(GTK_WIDGET(d), 
+                       _("You may not create a new national currency."));
     return FALSE;
   }
 
@@ -1079,18 +1025,17 @@
     c = gnc_commodity_table_lookup (gnc_get_current_commodities(),
                                     namespace, mnemonic);
 
-    if ((!w->edit_commodity && c) ||
-        (w->edit_commodity && c && (c != w->edit_commodity))) {
-      gnc_warning_dialog (w->dialog, _("That commodity already exists."));
+    if ((*commodityp && c) || (*commodityp && c && (c != *commodityp))) {
+      gnc_warning_dialog (GTK_WIDGET(d), _("That commodity already exists."));
       return FALSE;
     }
 
-    if (!w->edit_commodity) {
+    if (!*commodityp) {
       c = gnc_commodity_new(book, fullname, namespace, mnemonic, code, fraction);
-      w->edit_commodity = c;
+      *commodityp = c;
     }
     else {
-      c = w->edit_commodity;
+      c = *commodityp;
 
       gnc_commodity_table_remove (gnc_get_current_commodities(), c);
 
@@ -1101,18 +1046,19 @@
       gnc_commodity_set_fraction (c, fraction);
     }
 
-    gnc_commodity_set_quote_flag (c, gtk_toggle_button_get_active
-				  (GTK_TOGGLE_BUTTON (w->get_quote_check)));
+    gnc_commodity_set_quote_flag (c, gnc_dialog_get_boolean(d, GET_QUOTE));
 
     for (type = SOURCE_SINGLE; type < SOURCE_MAX; type++) {
-      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->source_button[type])))
+      if (gnc_dialog_get_boolean(d, SOURCE_BUTTON_LIST[type]))
 	break;
     }
-    selection = gnc_option_menu_get_active (w->source_menu[type]);
+    /* NOTE: if code in gnc_option_menu_get_active() isn't equivalent
+       to gtk_option_menu_get_history(), then this might be a bug. */
+    selection = gnc_dialog_get_index(d, SOURCE_MENU_LIST[type]);
     source = gnc_quote_source_lookup_by_ti (type, selection);
     gnc_commodity_set_quote_source(c, source);
 
-    selection = gnc_option_menu_get_active (w->quote_tz_menu);
+    selection = gnc_dialog_get_index (d, QUOTE_TZ_MENU);
     string = gnc_timezone_menu_position_to_string(selection);
     gnc_commodity_set_quote_tz(c, string);
 
@@ -1120,12 +1066,12 @@
     c = gnc_commodity_table_insert(gnc_get_current_commodities(), c);
   }
   else {
-    gnc_warning_dialog(w->dialog,
-		       _("You must enter a non-empty \"Full name\", "
-			 "\"Symbol/abbreviation\",\n"
-			 "and \"Type\" for the commodity."));
+    gnc_warning_dialog(GTK_WIDGET(d), 
+                       _("You must enter a non-empty \"Full name\", "
+                         "\"Symbol/abbreviation\",\n"
+                         "and \"Type\" for the commodity."));
     return FALSE;
   }
-  LEAVE(" ");
+  //LEAVE(" ");
   return TRUE;
 }
-------------- next part --------------
/********************************************************************\
 * Copyright (C) 2005 Chris Shoemaker (c.shoemaker at cox.net)          *
 *                                                                   *
 * This program is free software; you can redistribute it and/or     *
 * modify it under the terms of the GNU General Public License as    *
 * published by the Free Software Foundation; either version 2 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 General Public License for more details.                      *
 *                                                                   *
 * You should have received a copy of the GNU General Public License *
 * along with this program; if not, contact:                         *
 *                                                                   *
 * Free Software Foundation           Voice:  +1-617-542-5942        *
 * 59 Temple Place - Suite 330        Fax:    +1-617-542-2652        *
 * Boston, MA  02111-1307,  USA       gnu at gnu.org                    *
 *                                                                   *
\********************************************************************/


#ifndef GNC_DIALOG_H
#define GNC_DIALOG_H

#include <glade/glade.h>
#include <time.h>

GType gnc_dialog_get_type (void);

/* type macros */
#define GNC_TYPE_DIALOG            (gnc_dialog_get_type ())
#define GNC_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
                                      GNC_TYPE_DIALOG, GncDialog))
#define GNC_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), \
                                      GNC_TYPE_DIALOG, GncDialogClass))
#define GNC_IS_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
                                      GNC_TYPE_DIALOG))
#define GNC_IS_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
                                      GNC_TYPE_DIALOG))
#define GNC_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
                                      GNC_TYPE_DIALOG, GncDialogClass))

typedef struct {
    GtkDialog parent;
} GncDialog;

typedef struct {
    GtkDialogClass parent;
    void (*changed) (GncDialog *d);
} GncDialogClass;


/**** PROTOTYPES *************************************************/

/* filename is a glade filenames; and root is the name of the root
   widget you want to show. */
GncDialog *gnc_dialog_new(const char *filename,
                          const char *root);


typedef gboolean (*GncDialogCallback) (GncDialog *d, gpointer user_data);

/* The apply callback is optional, but useful dialogs will probably
 * supply one.  The apply callback should return FALSE if any values
 * are invalid.  In that case, the dialog will not close automatically
 * after the user clicks OK, and the changed state will not be marked
 * clean.  If you supply NULL, for the apply callback, no "OK" button
 * will be visible.
 *
 * The close callback is optional.  If you provide a close callback,
 * its return value should be TRUE if you want to proceed with the
 * close.  There's no destroy notifier for user_data, but you can
 * treat the close_cb as one.  So if you must pass this function its
 * own copy of user_data, free it from within close_cb.
 * 
 * The help callback return value is not checked.
 * 
 * Any callback may be NULL, in which case it's not used.  If help_cb
 * is NULL, no help button is shown.
 */
void gnc_dialog_set_cb(GncDialog *d,
                       GncDialogCallback apply_cb,
                       GncDialogCallback close_cb,
                       GncDialogCallback help_cb,
                       gpointer user_data);

/* By default, GncDialog works best in asynchronous environments,
 * where your program execution flow isn't waiting for the dialog to
 * close.  But, if you're using the dialog to do something like fetch
 * a value you want to return on the stack, then you have to block the
 * current thread until the dialog is closed.  Calling this function
 * will do exactly that.
 */
void gnc_dialog_block_until_close(GncDialog *d);

/* This is a catch-all interface to whatever kind of widgets may have
 * been specified in the glade file.  Once you have you widget you can
 * use whatever interface that widget offers to set and get widget
 * state.  You _have_ to use if the widget type isn't supported by the
 * type-specific or type-generic interfaces below.
 */
GtkWidget *gnc_dialog_get_widget(GncDialog *d, const gchar* name);

void gnc_dialog_set_sensitive(GncDialog *d, const gchar* name, gboolean sen);

/* Infers val type from widget type *
*/

/* Type-generic getter/setter: Be careful with these.  They are NOT
 * type safe.  Also, if they prove to be more trouble than they're
 * worth, they'll go away.
 *
 * These functions try to use the widget type to infer the type of
 * data pointed at by val.  They will return FALSE if they are unable
 * to infer value type.  The inferences made are:
 *
 * Widget Type ---> Value Type
 * ===========      ==========
 * GnomeDateEdit     GDate * 
 * GtkSpinButton     gdouble *
 * GtkToggleButton   gboolean *
 * GtkEntry          gchar *
 * GtkLabel          gchar *
 * GtkTextView       GtkTextBuffer *
 * GtkComboBox       gint *
 *
 * WARNING: For the given widget type you must cast the corresponding
 * value type to/from the passed gpointer.  Having mis-matched widget
 * and value types will likely cause a revolt among the electrons.
 *
 */
gboolean gnc_dialog_set(GncDialog *d, const char* name, const gpointer val);
gpointer gnc_dialog_get(GncDialog *d, const char* name);

/* Type-specific getter/setters */
gboolean     gnc_dialog_set_string(GncDialog *d, const char* name,
                               const gchar* val);
const gchar* gnc_dialog_get_string(GncDialog *d, const char* name);

gboolean gnc_dialog_set_double(GncDialog *d, const char* name, gdouble val);
gdouble  gnc_dialog_get_double(GncDialog *d, const char* name);

gboolean gnc_dialog_set_int(GncDialog *d, const char* name, gint val);
gint  gnc_dialog_get_int(GncDialog *d, const char* name);

gboolean gnc_dialog_set_date(GncDialog *d, const char* name, time_t val);
time_t   gnc_dialog_get_date(GncDialog *d, const char* name);

gboolean gnc_dialog_set_index(GncDialog *d, const char* name, gint val);
gint     gnc_dialog_get_index(GncDialog *d, const char* name);

gboolean gnc_dialog_set_boolean(GncDialog *d, const char* name, gboolean val);
gboolean gnc_dialog_get_boolean(GncDialog *d, const char* name);

/* Possible TODO: there are more types that could be added here.

Maybe currency/gnc_commodity *

*/

#endif


More information about the gnucash-devel mailing list