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