attribute.c

Go to the documentation of this file.
00001 /*
00002  *                            COPYRIGHT
00003  *
00004  * gEDA - GNU Electronic Design Automation
00005  * This is a part of gerbv
00006  *
00007  *  Copyright (C) 2008 Dan McMahill
00008  *
00009  *  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  * it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU General Public License for more details.
00019  *
00020  *  You should have received a copy of the GNU General Public License
00021  *  along with this program; if not, write to the Free Software
00022  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023  *
00024  */
00025 
00031 #ifdef HAVE_CONFIG_H
00032 #include "config.h"
00033 #endif
00034 
00035 #ifdef HAVE_STDLIB_H
00036 #include <stdlib.h>
00037 #endif
00038 
00039 #ifdef HAVE_STRING_H
00040 #include <string.h>
00041 #endif
00042 
00043 #ifdef HAVE_SYS_STAT_H
00044 #include <sys/stat.h>
00045 #endif
00046 
00047 #include <gtk/gtk.h>
00048 
00049 #include "gerbv.h"
00050 #include "attribute.h"
00051 #include "main.h"
00052 
00053 #define dprintf if(DEBUG) printf
00054 
00055 static int auto_uncheck_needed = 0;
00056 static GtkWidget * auto_uncheck_widget = NULL;
00057 static int * auto_uncheck_attr = NULL;
00058 static GtkWidget ** all_widgets = NULL;
00059 static int n_widgets;
00060 
00061 static void clear_auto()
00062 {
00063   if( auto_uncheck_needed && auto_uncheck_widget != NULL && auto_uncheck_attr != NULL) {
00064     /* disable this bit of code so we don't enter an endless loop */
00065     auto_uncheck_needed = 0;
00066 
00067     /* uncheck the "auto" toggle button */
00068     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_uncheck_widget), 0);
00069 
00070     /* store that we have unchecked the "auto" toggle button */
00071     *auto_uncheck_attr = 0;
00072 
00073     /* re-enable this bit of code */
00074     auto_uncheck_needed = 1;
00075   }
00076 }
00077 
00078 /* Callback for toggling a boolean attribute */
00079 static void
00080 set_flag_cb (GtkToggleButton * button, gboolean * flag)
00081 {
00082   int i, f;
00083 
00084   *flag = gtk_toggle_button_get_active (button);
00085   
00086   /* 
00087    * if this is the "auto" button then set/clear the sensitivity of
00088    * everything else.  Otherwise call the clear_auto() function 
00089    */
00090   if (auto_uncheck_widget == GTK_WIDGET (button)) {
00091     f = *flag ? 0 : 1;
00092     printf ("flag = %d, f = %d\n", *flag, f);
00093     for (i = 1 ; i < n_widgets ; i++) {
00094       gtk_widget_set_sensitive (all_widgets[i], f);
00095     }
00096   } else {
00097     clear_auto ();
00098   }
00099 }
00100 
00101 /* Callback for setting an integer value */
00102 static void
00103 intspinner_changed_cb (GtkWidget * spin_button, gpointer data)
00104 {
00105   int *ival = data;
00106 
00107   *ival = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin_button));
00108   clear_auto ();
00109 }
00110 
00111 /* Callback for setting a floating point value */
00112 static void
00113 dblspinner_changed_cb (GtkWidget * spin_button, gpointer data)
00114 {
00115   double *dval = data;
00116 
00117   *dval = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin_button));
00118   clear_auto ();
00119 }
00120 
00121 /* Callback for setting an string value */
00122 static void
00123 entry_changed_cb (GtkEntry * entry, char **str)
00124 {
00125   const gchar *s;
00126 
00127   s = gtk_entry_get_text (entry);
00128 
00129   if (*str)
00130     free (*str);
00131   *str = strdup (s);
00132 
00133   clear_auto ();
00134 }
00135 
00136 /* Callback for setting an enum value */
00137 static void
00138 enum_changed_cb (GtkWidget * combo_box, int *val)
00139 {
00140   gint active;
00141 
00142   active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box));
00143   *val = active;
00144 
00145   clear_auto ();
00146 }
00147 
00148 /* Utility function for building a vbox with a text label */
00149 /* Written by Bill Wilson for PCB */
00150 static GtkWidget *
00151 ghid_category_vbox (GtkWidget * box, const gchar * category_header,
00152                   gint header_pad,
00153                   gint box_pad, gboolean pack_start, gboolean bottom_pad)
00154 {
00155   GtkWidget *vbox, *vbox1, *hbox, *label;
00156   gchar *s;
00157 
00158   vbox = gtk_vbox_new (FALSE, 0);
00159   if (pack_start)
00160     gtk_box_pack_start (GTK_BOX (box), vbox, FALSE, FALSE, 0);
00161   else
00162     gtk_box_pack_end (GTK_BOX (box), vbox, FALSE, FALSE, 0);
00163 
00164   if (category_header)
00165     {
00166       label = gtk_label_new (NULL);
00167       s = g_strconcat ("<span weight=\"bold\">", category_header,
00168                      "</span>", NULL);
00169       gtk_label_set_markup (GTK_LABEL (label), s);
00170       gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
00171       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, header_pad);
00172       g_free (s);
00173     }
00174 
00175   hbox = gtk_hbox_new (FALSE, 0);
00176   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
00177   label = gtk_label_new ("     ");
00178   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
00179   vbox1 = gtk_vbox_new (FALSE, box_pad);
00180   gtk_box_pack_start (GTK_BOX (hbox), vbox1, TRUE, TRUE, 0);
00181 
00182   if (bottom_pad)
00183     {
00184       label = gtk_label_new ("");
00185       gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
00186     }
00187   return vbox1;
00188 }
00189 
00190 /* Utility function for creating a spin button */
00191 /* Written by Bill Wilson for PCB */
00192 static void
00193 ghid_spin_button (GtkWidget * box, GtkWidget ** spin_button, gfloat value,
00194                 gfloat low, gfloat high, gfloat step0, gfloat step1,
00195                 gint digits, gint width,
00196                 void (*cb_func) (), gpointer data, gboolean right_align,
00197                 gchar * string)
00198 {
00199   GtkWidget *hbox = NULL, *label, *spin_but;
00200   GtkSpinButton *spin;
00201   GtkAdjustment *adj;
00202 
00203   if (string && box)
00204     {
00205       hbox = gtk_hbox_new (FALSE, 0);
00206       gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 2);
00207       box = hbox;
00208     }
00209   adj = (GtkAdjustment *) gtk_adjustment_new (value,
00210                                          low, high, step0, step1, 0.0);
00211   spin_but = gtk_spin_button_new (adj, 0.5, digits);
00212   if (spin_button)
00213     *spin_button = spin_but;
00214   if (width > 0)
00215     gtk_widget_set_size_request (spin_but, width, -1);
00216   spin = GTK_SPIN_BUTTON (spin_but);
00217   gtk_spin_button_set_numeric (spin, TRUE);
00218   if (data == NULL)
00219     data = (gpointer) spin;
00220   if (cb_func)
00221     g_signal_connect (G_OBJECT (spin_but), "value_changed",
00222                     G_CALLBACK (cb_func), data);
00223   if (box)
00224     {
00225       if (right_align && string)
00226        {
00227          label = gtk_label_new (string);
00228          gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
00229          gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 2);
00230        }
00231       gtk_box_pack_start (GTK_BOX (box), spin_but, FALSE, FALSE, 2);
00232       if (!right_align && string)
00233        {
00234          label = gtk_label_new (string);
00235          gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
00236          gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 2);
00237        }
00238     }
00239 }
00240 
00241 /* Utility function for creating a check button */
00242 /* Written by Bill Wilson for PCB */
00243 static void
00244 ghid_check_button_connected (GtkWidget * box,
00245                           GtkWidget ** button,
00246                           gboolean active,
00247                           gboolean pack_start,
00248                           gboolean expand,
00249                           gboolean fill,
00250                           gint pad,
00251                           void (*cb_func) (),
00252                           gpointer data, gchar * string)
00253 {
00254   GtkWidget *b;
00255 
00256   if (!string)
00257     return;
00258   b = gtk_check_button_new_with_label (string);
00259   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), active);
00260   if (box && pack_start)
00261     gtk_box_pack_start (GTK_BOX (box), b, expand, fill, pad);
00262   else if (box && !pack_start)
00263     gtk_box_pack_end (GTK_BOX (box), b, expand, fill, pad);
00264 
00265   if (cb_func)
00266     gtk_signal_connect (GTK_OBJECT (b), "clicked",
00267                      GTK_SIGNAL_FUNC (cb_func), data);
00268   if (button)
00269     *button = b;
00270 }
00271 
00272 /* 
00273  * The following function is taken almost directly from
00274  * ghid_attribte_dialog() from pcb.  It is a generic attribute editor
00275  * gui where the dialog is built on the fly based on a passed in
00276  * attribute list.
00277  * 
00278  * Written by Dan McMahill
00279  */
00280 int
00281 attribute_interface_dialog (gerbv_HID_Attribute * attrs,
00282                      int n_attrs, gerbv_HID_Attr_Val * results,
00283                      const char * title,
00284                      const char * descr)
00285 {
00286   GtkWidget *dialog, *main_vbox, *vbox, *vbox1, *hbox, *entry;
00287   GtkWidget *combo;
00288   GtkWidget *widget;
00289   int i, j;
00290   GtkTooltips *tips;
00291   int rc = 0;
00292   int set_auto_uncheck = 0;
00293   int sen = TRUE;
00294 
00295   /* 
00296    * Store how many widgets we'll have in our dialog and keep track of
00297    * them.  Be sure to free up our list if one existed already.
00298    */
00299   n_widgets = n_attrs;
00300   if (all_widgets != NULL)
00301     free (all_widgets);
00302 
00303   all_widgets = (GtkWidget **) malloc (n_widgets * sizeof(GtkWidget *));
00304   if (all_widgets == NULL) {
00305     fprintf (stderr, "%s():  malloc failed for an array of size %d\n", __FUNCTION__, n_widgets);
00306     exit (1);
00307   }
00308 
00309   dprintf ("%s(%p, %d, %p, \"%s\", \"%s\")\n", __FUNCTION__, attrs, n_attrs, results, title, descr);
00310 
00311   auto_uncheck_needed = 0;
00312   auto_uncheck_widget = NULL;
00313   auto_uncheck_attr = NULL;
00314 
00315   tips = gtk_tooltips_new ();
00316 
00317   dialog = gtk_dialog_new_with_buttons (title,
00318                                    GTK_WINDOW (screen.win.topLevelWindow),
00319                                    GTK_DIALOG_MODAL
00320                                    | GTK_DIALOG_DESTROY_WITH_PARENT,
00321                                    GTK_STOCK_CANCEL, GTK_RESPONSE_NONE,
00322                                    GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
00323   gtk_window_set_wmclass (GTK_WINDOW (dialog), "gerbv_attribute_editor", "gerbv");
00324 
00325   main_vbox = gtk_vbox_new (FALSE, 6);
00326   gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
00327   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
00328 
00329   vbox = ghid_category_vbox (main_vbox, descr != NULL ? descr : "",
00330                           4, 2, TRUE, TRUE);
00331 
00332   /* 
00333    * Iterate over all the attributes and build up a dialog box
00334    * that lets us control all of the options.  By doing things this
00335    * way, any changes to the attributes or if there is a new list of
00336    * attributes, everything will automatically be reflected in this
00337    * dialog box. 
00338    */
00339   for (j = 0; j < n_attrs; j++)
00340       {
00341          dprintf ("%s():  Adding attribute #%d\n", __FUNCTION__, j);
00342          switch (attrs[j].type)
00343              {
00344              case HID_Label:
00345                 widget = gtk_label_new (attrs[j].name);
00346                 gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
00347                 gtk_tooltips_set_tip (tips, widget, attrs[j].help_text, NULL);
00348                 break;
00349                 
00350              case HID_Integer:
00351                 hbox = gtk_hbox_new (FALSE, 4);
00352                 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
00353                 
00354                 /* 
00355                  * FIXME 
00356                  * need to pick the "digits" argument based on min/max
00357                  * values
00358                  */
00359                 ghid_spin_button (hbox, &widget, attrs[j].default_val.int_value,
00360                                 attrs[j].min_val, attrs[j].max_val, 1.0, 1.0, 0, 0,
00361                                 intspinner_changed_cb,
00362                                 &(attrs[j].default_val.int_value), FALSE, NULL);
00363                 
00364                 gtk_tooltips_set_tip (tips, widget, attrs[j].help_text, NULL);
00365                 all_widgets[j] = widget;
00366                 
00367                 widget = gtk_label_new (attrs[j].name);
00368                 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
00369                 break;
00370                 
00371              case HID_Real:
00372                 hbox = gtk_hbox_new (FALSE, 4);
00373                 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
00374                 
00375                 /* 
00376                  * FIXME 
00377                  * need to pick the "digits" and step size argument more
00378                  * intelligently
00379                  */
00380                 ghid_spin_button (hbox, &widget, attrs[j].default_val.real_value,
00381                                 attrs[j].min_val, attrs[j].max_val, 0.01, 0.01, 3,
00382                                 0, 
00383                                 dblspinner_changed_cb,
00384                                 &(attrs[j].default_val.real_value), FALSE, NULL);
00385                 
00386                 gtk_tooltips_set_tip (tips, widget, attrs[j].help_text, NULL);
00387                 all_widgets[j] = widget;
00388 
00389                 widget = gtk_label_new (attrs[j].name);
00390                 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
00391                 break;
00392                 
00393              case HID_String:
00394                 hbox = gtk_hbox_new (FALSE, 4);
00395                 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
00396                 
00397                 entry = gtk_entry_new ();
00398                 gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
00399                 gtk_entry_set_text (GTK_ENTRY (entry),
00400                                   attrs[j].default_val.str_value);
00401                 gtk_tooltips_set_tip (tips, entry, attrs[j].help_text, NULL);
00402                 g_signal_connect (G_OBJECT (entry), "changed",
00403                                 G_CALLBACK (entry_changed_cb),
00404                                 &(attrs[j].default_val.str_value));
00405                 all_widgets[j] = entry;
00406 
00407                 widget = gtk_label_new (attrs[j].name);
00408                 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
00409                 break;
00410                 
00411              case HID_Boolean:
00412                 /* put this in a check button */
00413                 ghid_check_button_connected (vbox, &widget,
00414                                           attrs[j].default_val.int_value,
00415                                           TRUE, FALSE, FALSE, 0, set_flag_cb,
00416                                           &(attrs[j].default_val.int_value),
00417                                           attrs[j].name);
00418                 gtk_tooltips_set_tip (tips, widget, attrs[j].help_text, NULL);
00419 
00420                 /* 
00421                  * This is an ugly ugly ugly hack....  If this is
00422                  * the first in our list of attributes *and* it has a
00423                  * magic name of "auto" then we'll remember it and
00424                  * all of the other callbacks will cause this button to
00425                  * come unchecked. Among the other nastiness
00426                  * involved here, this dialog is now *required* to
00427                  * be modal since we are using a static variable.
00428                  * To avoid that, we need a new data type that can hold
00429                  * more state information.  Ideally we need a better
00430                  * way to capture dependencies between attributes to
00431                  * allow arbitrary relationships instead of just this
00432                  * one single "magic" one.
00433                  */
00434                 if (j == 0 && strcmp(attrs[j].name, "Autodetect file format") == 0) {
00435                   set_auto_uncheck = 1;
00436                   auto_uncheck_widget = widget;
00437                   auto_uncheck_attr = &(attrs[j].default_val.int_value);
00438 
00439                   /* if the "auto" button in checked then don't let
00440                    * anything else be sensitive.
00441                   */
00442                      
00443                   if (attrs[j].default_val.int_value)
00444                     sen = FALSE;
00445                 }
00446                 all_widgets[j] = widget;
00447 
00448                 break;
00449                 
00450              case HID_Enum:
00451                 hbox = gtk_hbox_new (FALSE, 4);
00452                 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
00453                 
00454                 /* 
00455                  * We have to put the combo_box inside of an event_box in
00456                  * order for tooltips to work.
00457                  */
00458                 widget = gtk_event_box_new ();
00459                 gtk_tooltips_set_tip (tips, widget, attrs[j].help_text, NULL);
00460                 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
00461                 
00462                 combo = gtk_combo_box_new_text ();
00463                 gtk_container_add (GTK_CONTAINER (widget), combo);
00464                 g_signal_connect (G_OBJECT (combo), "changed",
00465                                 G_CALLBACK (enum_changed_cb),
00466                                 &(attrs[j].default_val.int_value));
00467                 
00468 
00469                 /* 
00470                  * Iterate through each value and add them to the
00471                  * combo box
00472                  */
00473                 i = 0;
00474                 while (attrs[j].enumerations[i])
00475                     {
00476                        gtk_combo_box_append_text (GTK_COMBO_BOX (combo),
00477                                                attrs[j].enumerations[i]);
00478                        i++;
00479                     }
00480                 gtk_combo_box_set_active (GTK_COMBO_BOX (combo),
00481                                        attrs[j].default_val.int_value);
00482                 all_widgets[j] = combo;
00483          
00484                 widget = gtk_label_new (attrs[j].name);
00485                 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
00486                 break;
00487 
00488              case HID_Mixed:
00489                 dprintf ("HID_Mixed\n");
00490                 break;
00491 
00492              case HID_Path:
00493                 vbox1 = ghid_category_vbox (vbox, attrs[j].name, 4, 2, TRUE, TRUE);
00494                 entry = gtk_entry_new ();
00495                 gtk_box_pack_start (GTK_BOX (vbox1), entry, FALSE, FALSE, 0);
00496                 gtk_entry_set_text (GTK_ENTRY (entry),
00497                                   attrs[j].default_val.str_value);
00498                 g_signal_connect (G_OBJECT (entry), "changed",
00499                                 G_CALLBACK (entry_changed_cb),
00500                                 &(attrs[j].default_val.str_value));
00501 
00502                 gtk_tooltips_set_tip (tips, entry, attrs[j].help_text, NULL);
00503                 all_widgets[j] = entry;
00504                 break;
00505 
00506              default:
00507                 fprintf (stderr, "%s: unknown type of HID attribute\n", __FUNCTION__);
00508                 break;
00509              }
00510       }
00511 
00512 
00513   gtk_widget_show_all (dialog);
00514   auto_uncheck_needed = set_auto_uncheck;
00515 
00516   /* 
00517    * part of the "auto" hack.  Make everything sensitive or
00518    * insensitive based on the state of the "auto" toggle button (if it
00519    * exists)
00520    */
00521   for (j = 1; j < n_widgets ; j++) {
00522     gtk_widget_set_sensitive (all_widgets[j], sen);
00523   }
00524   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
00525       {
00526          /* copy over the results */
00527          for (i = 0; i < n_attrs; i++)
00528              {
00529                 results[i] = attrs[i].default_val;
00530                 if (results[i].str_value)
00531                     results[i].str_value = strdup (results[i].str_value);
00532              }
00533          rc = 0;
00534       }
00535   else
00536       rc = 1;
00537 
00538   gtk_widget_destroy (dialog);
00539 
00540   return rc;
00541 }
00542 

Generated on Tue Aug 19 00:14:47 2008 for gerbv by  doxygen 1.5.6