gerbv  2.6A
attribute.c
Go to the documentation of this file.
1 /*
2  * COPYRIGHT
3  *
4  * gEDA - GNU Electronic Design Automation
5  * This is a part of gerbv
6  *
7  * Copyright (C) 2008 Dan McMahill
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38 
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #endif
42 
43 #ifdef HAVE_SYS_STAT_H
44 #include <sys/stat.h>
45 #endif
46 
47 #include <gtk/gtk.h>
48 
49 #include "common.h"
50 #include "gerbv.h"
51 #include "attribute.h"
52 #include "main.h"
53 
54 #define dprintf if(DEBUG) printf
55 
56 static int auto_uncheck_needed = 0;
57 static GtkWidget * auto_uncheck_widget = NULL;
58 static int * auto_uncheck_attr = NULL;
59 static GtkWidget ** all_widgets = NULL;
60 static int n_widgets;
61 
62 static void clear_auto()
63 {
64  if( auto_uncheck_needed && auto_uncheck_widget != NULL && auto_uncheck_attr != NULL) {
65  /* disable this bit of code so we don't enter an endless loop */
66  auto_uncheck_needed = 0;
67 
68  /* uncheck the "auto" toggle button */
69  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_uncheck_widget), 0);
70 
71  /* store that we have unchecked the "auto" toggle button */
72  *auto_uncheck_attr = 0;
73 
74  /* re-enable this bit of code */
75  auto_uncheck_needed = 1;
76  }
77 }
78 
79 /* Callback for toggling a boolean attribute */
80 static void
81 set_flag_cb (GtkToggleButton * button, gboolean * flag)
82 {
83  int i, f;
84 
85  *flag = gtk_toggle_button_get_active (button);
86 
87  /*
88  * if this is the "auto" button then set/clear the sensitivity of
89  * everything else. Otherwise call the clear_auto() function
90  */
91  if (auto_uncheck_widget == GTK_WIDGET (button)) {
92  f = *flag ? 0 : 1;
93  for (i = 1 ; i < n_widgets ; i++) {
94  gtk_widget_set_sensitive (all_widgets[i], f);
95  }
96  } else {
97  clear_auto ();
98  }
99 }
100 
101 /* Callback for setting an integer value */
102 static void
103 intspinner_changed_cb (GtkWidget * spin_button, gpointer data)
104 {
105  int *ival = data;
106 
107  *ival = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin_button));
108  clear_auto ();
109 }
110 
111 /* Callback for setting a floating point value */
112 static void
113 dblspinner_changed_cb (GtkWidget * spin_button, gpointer data)
114 {
115  double *dval = data;
116 
117  *dval = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin_button));
118  clear_auto ();
119 }
120 
121 /* Callback for setting an string value */
122 static void
123 entry_changed_cb (GtkEntry * entry, char **str)
124 {
125  const gchar *s;
126 
127  s = gtk_entry_get_text (entry);
128 
129  if (*str)
130  free (*str);
131  *str = strdup (s);
132 
133  clear_auto ();
134 }
135 
136 /* Callback for setting an enum value */
137 static void
138 enum_changed_cb (GtkWidget * combo_box, int *val)
139 {
140  gint active;
141 
142  active = gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box));
143  *val = active;
144 
145  clear_auto ();
146 }
147 
148 /* Utility function for building a vbox with a text label */
149 /* Written by Bill Wilson for PCB */
150 static GtkWidget *
151 ghid_category_vbox (GtkWidget * box, const gchar * category_header,
152  gint header_pad,
153  gint box_pad, gboolean pack_start, gboolean bottom_pad)
154 {
155  GtkWidget *vbox, *vbox1, *hbox, *label;
156  gchar *s;
157 
158  vbox = gtk_vbox_new (FALSE, 0);
159  if (pack_start)
160  gtk_box_pack_start (GTK_BOX (box), vbox, FALSE, FALSE, 0);
161  else
162  gtk_box_pack_end (GTK_BOX (box), vbox, FALSE, FALSE, 0);
163 
164  if (category_header)
165  {
166  label = gtk_label_new (NULL);
167  s = g_strconcat ("<span weight=\"bold\">", category_header,
168  "</span>", NULL);
169  gtk_label_set_markup (GTK_LABEL (label), s);
170  gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
171  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, header_pad);
172  g_free (s);
173  }
174 
175  hbox = gtk_hbox_new (FALSE, 0);
176  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
177  label = gtk_label_new (" ");
178  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
179  vbox1 = gtk_vbox_new (FALSE, box_pad);
180  gtk_box_pack_start (GTK_BOX (hbox), vbox1, TRUE, TRUE, 0);
181 
182  if (bottom_pad)
183  {
184  label = gtk_label_new ("");
185  gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
186  }
187  return vbox1;
188 }
189 
190 /* Utility function for creating a spin button */
191 /* Written by Bill Wilson for PCB */
192 static void
193 ghid_spin_button (GtkWidget * box, GtkWidget ** spin_button, gfloat value,
194  gfloat low, gfloat high, gfloat step0, gfloat step1,
195  gint digits, gint width,
196  void (*cb_func) (), gpointer data, gboolean right_align,
197  gchar * string)
198 {
199  GtkWidget *hbox = NULL, *label, *spin_but;
200  GtkSpinButton *spin;
201  GtkAdjustment *adj;
202 
203  if (string && box)
204  {
205  hbox = gtk_hbox_new (FALSE, 0);
206  gtk_box_pack_start (GTK_BOX (box), hbox, FALSE, FALSE, 2);
207  box = hbox;
208  }
209  adj = (GtkAdjustment *) gtk_adjustment_new (value,
210  low, high, step0, step1, 0.0);
211  spin_but = gtk_spin_button_new (adj, 0.5, digits);
212  if (spin_button)
213  *spin_button = spin_but;
214  if (width > 0)
215  gtk_widget_set_size_request (spin_but, width, -1);
216  spin = GTK_SPIN_BUTTON (spin_but);
217  gtk_spin_button_set_numeric (spin, TRUE);
218  if (data == NULL)
219  data = (gpointer) spin;
220  if (cb_func)
221  g_signal_connect (G_OBJECT (spin_but), "value_changed",
222  G_CALLBACK (cb_func), data);
223  if (box)
224  {
225  if (right_align && string)
226  {
227  label = gtk_label_new (string);
228  gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
229  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 2);
230  }
231  gtk_box_pack_start (GTK_BOX (box), spin_but, FALSE, FALSE, 2);
232  if (!right_align && string)
233  {
234  label = gtk_label_new (string);
235  gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
236  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 2);
237  }
238  }
239 }
240 
241 /* Utility function for creating a check button */
242 /* Written by Bill Wilson for PCB */
243 static void
244 ghid_check_button_connected (GtkWidget * box,
245  GtkWidget ** button,
246  gboolean active,
247  gboolean pack_start,
248  gboolean expand,
249  gboolean fill,
250  gint pad,
251  void (*cb_func) (),
252  gpointer data, gchar * string)
253 {
254  GtkWidget *b;
255 
256  if (!string)
257  return;
258  b = gtk_check_button_new_with_label (string);
259  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), active);
260  if (box && pack_start)
261  gtk_box_pack_start (GTK_BOX (box), b, expand, fill, pad);
262  else if (box && !pack_start)
263  gtk_box_pack_end (GTK_BOX (box), b, expand, fill, pad);
264 
265  if (cb_func)
266  gtk_signal_connect (GTK_OBJECT (b), "clicked",
267  GTK_SIGNAL_FUNC (cb_func), data);
268  if (button)
269  *button = b;
270 }
271 
272 /*
273  * The following function is taken almost directly from
274  * ghid_attribte_dialog() from pcb. It is a generic attribute editor
275  * gui where the dialog is built on the fly based on a passed in
276  * attribute list.
277  *
278  * Written by Dan McMahill
279  */
280 int
281 attribute_interface_dialog (gerbv_HID_Attribute * attrs,
282  int n_attrs, gerbv_HID_Attr_Val * results,
283  const char * title,
284  const char * descr)
285 {
286  GtkWidget *dialog, *main_vbox, *vbox, *vbox1, *hbox, *entry;
287  GtkWidget *combo;
288  GtkWidget *widget;
289  int i, j;
290  GtkTooltips *tips;
291  int rc = 0;
292  int set_auto_uncheck = 0;
293  int sen = TRUE;
294 
295  /*
296  * Store how many widgets we'll have in our dialog and keep track of
297  * them. Be sure to free up our list if one existed already.
298  */
299  n_widgets = n_attrs;
300  if (all_widgets != NULL)
301  free (all_widgets);
302 
303  all_widgets = (GtkWidget **) malloc (n_widgets * sizeof(GtkWidget *));
304  if (all_widgets == NULL) {
305  fprintf (stderr, _("%s(): malloc failed for an array of size %d\n"), __FUNCTION__, n_widgets);
306  exit (1);
307  }
308 
309  dprintf ("%s(%p, %d, %p, \"%s\", \"%s\")\n", __FUNCTION__, attrs, n_attrs, results, title, descr);
310 
311  auto_uncheck_needed = 0;
312  auto_uncheck_widget = NULL;
313  auto_uncheck_attr = NULL;
314 
315  tips = gtk_tooltips_new ();
316 
317  dialog = gtk_dialog_new_with_buttons (title,
318  GTK_WINDOW (screen.win.topLevelWindow),
319  GTK_DIALOG_MODAL
320  | GTK_DIALOG_DESTROY_WITH_PARENT,
321  GTK_STOCK_CANCEL, GTK_RESPONSE_NONE,
322  GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
323  gtk_window_set_wmclass (GTK_WINDOW (dialog), "gerbv_attribute_editor", _("gerbv"));
324 
325  main_vbox = gtk_vbox_new (FALSE, 6);
326  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 6);
327  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), main_vbox);
328 
329  vbox = ghid_category_vbox (main_vbox, descr != NULL ? descr : "",
330  4, 2, TRUE, TRUE);
331 
332  /*
333  * Iterate over all the attributes and build up a dialog box
334  * that lets us control all of the options. By doing things this
335  * way, any changes to the attributes or if there is a new list of
336  * attributes, everything will automatically be reflected in this
337  * dialog box.
338  */
339  for (j = 0; j < n_attrs; j++)
340  {
341  dprintf (_("%s(): Adding attribute #%d\n"), __FUNCTION__, j);
342  switch (attrs[j].type)
343  {
344  case HID_Label:
345  widget = gtk_label_new (_(attrs[j].name));
346  gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
347  gtk_tooltips_set_tip (tips, widget, _(attrs[j].help_text), NULL);
348  break;
349 
350  case HID_Integer:
351  hbox = gtk_hbox_new (FALSE, 4);
352  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
353 
354  /*
355  * FIXME
356  * need to pick the "digits" argument based on min/max
357  * values
358  */
359  ghid_spin_button (hbox, &widget, attrs[j].default_val.int_value,
360  attrs[j].min_val, attrs[j].max_val, 1.0, 1.0, 0, 0,
361  intspinner_changed_cb,
362  &(attrs[j].default_val.int_value), FALSE, NULL);
363 
364  gtk_tooltips_set_tip (tips, widget, _(attrs[j].help_text), NULL);
365  all_widgets[j] = widget;
366 
367  widget = gtk_label_new (_(attrs[j].name));
368  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
369  break;
370 
371  case HID_Real:
372  hbox = gtk_hbox_new (FALSE, 4);
373  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
374 
375  /*
376  * FIXME
377  * need to pick the "digits" and step size argument more
378  * intelligently
379  */
380  ghid_spin_button (hbox, &widget, attrs[j].default_val.real_value,
381  attrs[j].min_val, attrs[j].max_val, 0.01, 0.01, 3,
382  0,
383  dblspinner_changed_cb,
384  &(attrs[j].default_val.real_value), FALSE, NULL);
385 
386  gtk_tooltips_set_tip (tips, widget, _(attrs[j].help_text), NULL);
387  all_widgets[j] = widget;
388 
389  widget = gtk_label_new (_(attrs[j].name));
390  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
391  break;
392 
393  case HID_String:
394  hbox = gtk_hbox_new (FALSE, 4);
395  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
396 
397  entry = gtk_entry_new ();
398  gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
399  gtk_entry_set_text (GTK_ENTRY (entry),
400  attrs[j].default_val.str_value);
401  gtk_tooltips_set_tip (tips, entry, _(attrs[j].help_text), NULL);
402  g_signal_connect (G_OBJECT (entry), "changed",
403  G_CALLBACK (entry_changed_cb),
404  &(attrs[j].default_val.str_value));
405  all_widgets[j] = entry;
406 
407  widget = gtk_label_new (_(attrs[j].name));
408  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
409  break;
410 
411  case HID_Boolean:
412  /* put this in a check button */
413  ghid_check_button_connected (vbox, &widget,
414  attrs[j].default_val.int_value,
415  TRUE, FALSE, FALSE, 0, set_flag_cb,
416  &(attrs[j].default_val.int_value),
417  _(attrs[j].name));
418  gtk_tooltips_set_tip (tips, widget, _(attrs[j].help_text), NULL);
419 
420  /*
421  * This is an ugly ugly ugly hack.... If this is
422  * the first in our list of attributes *and* it has a
423  * magic name of "autodetect" then we'll remember it and
424  * all of the other callbacks will cause this button to
425  * come unchecked. Among the other nastiness
426  * involved here, this dialog is now *required* to
427  * be modal since we are using a static variable.
428  * To avoid that, we need a new data type that can hold
429  * more state information. Ideally we need a better
430  * way to capture dependencies between attributes to
431  * allow arbitrary relationships instead of just this
432  * one single "magic" one.
433  */
434  if (j == 0 && strcmp(attrs[j].name, "autodetect") == 0) {
435  set_auto_uncheck = 1;
436  auto_uncheck_widget = widget;
437  auto_uncheck_attr = &(attrs[j].default_val.int_value);
438 
439  /* if the "auto" button in checked then don't let
440  * anything else be sensitive.
441  */
442 
443  if (attrs[j].default_val.int_value)
444  sen = FALSE;
445  }
446  all_widgets[j] = widget;
447 
448  break;
449 
450  case HID_Enum:
451  hbox = gtk_hbox_new (FALSE, 4);
452  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
453 
454  /*
455  * We have to put the combo_box inside of an event_box in
456  * order for tooltips to work.
457  */
458  widget = gtk_event_box_new ();
459  gtk_tooltips_set_tip (tips, widget, _(attrs[j].help_text), NULL);
460  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
461 
462  combo = gtk_combo_box_new_text ();
463  gtk_container_add (GTK_CONTAINER (widget), combo);
464  g_signal_connect (G_OBJECT (combo), "changed",
465  G_CALLBACK (enum_changed_cb),
466  &(attrs[j].default_val.int_value));
467 
468 
469  /*
470  * Iterate through each value and add them to the
471  * combo box
472  */
473  i = 0;
474  while (attrs[j].enumerations[i])
475  {
476  gtk_combo_box_append_text (GTK_COMBO_BOX (combo),
477  _(attrs[j].enumerations[i]));
478  i++;
479  }
480  gtk_combo_box_set_active (GTK_COMBO_BOX (combo),
481  attrs[j].default_val.int_value);
482  all_widgets[j] = combo;
483 
484  widget = gtk_label_new (_(attrs[j].name));
485  gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
486  break;
487 
488  case HID_Mixed:
489  dprintf ("HID_Mixed\n");
490  break;
491 
492  case HID_Path:
493  vbox1 = ghid_category_vbox (vbox, _(attrs[j].name), 4, 2, TRUE, TRUE);
494  entry = gtk_entry_new ();
495  gtk_box_pack_start (GTK_BOX (vbox1), entry, FALSE, FALSE, 0);
496  gtk_entry_set_text (GTK_ENTRY (entry),
497  attrs[j].default_val.str_value);
498  g_signal_connect (G_OBJECT (entry), "changed",
499  G_CALLBACK (entry_changed_cb),
500  &(attrs[j].default_val.str_value));
501 
502  gtk_tooltips_set_tip (tips, entry, _(attrs[j].help_text), NULL);
503  all_widgets[j] = entry;
504  break;
505 
506  default:
507  fprintf (stderr, _("%s: unknown type of HID attribute\n"), __FUNCTION__);
508  break;
509  }
510  }
511 
512 
513  gtk_widget_show_all (dialog);
514  auto_uncheck_needed = set_auto_uncheck;
515 
516  /*
517  * part of the "auto" hack. Make everything sensitive or
518  * insensitive based on the state of the "auto" toggle button (if it
519  * exists)
520  */
521  for (j = 1; j < n_widgets ; j++) {
522  gtk_widget_set_sensitive (all_widgets[j], sen);
523  }
524  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
525  {
526  /* copy over the results */
527  for (i = 0; i < n_attrs; i++)
528  {
529  results[i] = attrs[i].default_val;
530  if (results[i].str_value)
531  results[i].str_value = strdup (results[i].str_value);
532  }
533  rc = 0;
534  }
535  else
536  rc = 1;
537 
538  gtk_widget_destroy (dialog);
539 
540  return rc;
541 }
542