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