4 * gEDA - GNU Electronic Design Automation
5 * This is a part of gerbv
7 * Copyright (C) 2008 Dan McMahill
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.
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.
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.
27 \brief Dynamic GUI window creation functions
43 #ifdef HAVE_SYS_STAT_H
51 #include "attribute.h"
54 #define dprintf if(DEBUG) printf
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
;
62 static void clear_auto()
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;
68 /* uncheck the "auto" toggle button */
69 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_uncheck_widget
), 0);
71 /* store that we have unchecked the "auto" toggle button */
72 *auto_uncheck_attr
= 0;
74 /* re-enable this bit of code */
75 auto_uncheck_needed
= 1;
79 /* Callback for toggling a boolean attribute */
81 set_flag_cb (GtkToggleButton
* button
, gboolean
* flag
)
85 *flag
= gtk_toggle_button_get_active (button
);
88 * if this is the "auto" button then set/clear the sensitivity of
89 * everything else. Otherwise call the clear_auto() function
91 if (auto_uncheck_widget
== GTK_WIDGET (button
)) {
93 for (i
= 1 ; i
< n_widgets
; i
++) {
94 gtk_widget_set_sensitive (all_widgets
[i
], f
);
101 /* Callback for setting an integer value */
103 intspinner_changed_cb (GtkWidget
* spin_button
, gpointer data
)
107 *ival
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin_button
));
111 /* Callback for setting a floating point value */
113 dblspinner_changed_cb (GtkWidget
* spin_button
, gpointer data
)
117 *dval
= gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin_button
));
121 /* Callback for setting an string value */
123 entry_changed_cb (GtkEntry
* entry
, char **str
)
127 s
= gtk_entry_get_text (entry
);
136 /* Callback for setting an enum value */
138 enum_changed_cb (GtkWidget
* combo_box
, int *val
)
142 active
= gtk_combo_box_get_active (GTK_COMBO_BOX (combo_box
));
148 /* Utility function for building a vbox with a text label */
149 /* Written by Bill Wilson for PCB */
151 ghid_category_vbox (GtkWidget
* box
, const gchar
* category_header
,
153 gint box_pad
, gboolean pack_start
, gboolean bottom_pad
)
155 GtkWidget
*vbox
, *vbox1
, *hbox
, *label
;
158 vbox
= gtk_vbox_new (FALSE
, 0);
160 gtk_box_pack_start (GTK_BOX (box
), vbox
, FALSE
, FALSE
, 0);
162 gtk_box_pack_end (GTK_BOX (box
), vbox
, FALSE
, FALSE
, 0);
166 label
= gtk_label_new (NULL
);
167 s
= g_strconcat ("<span weight=\"bold\">", category_header
,
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
);
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);
184 label
= gtk_label_new ("");
185 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 0);
190 /* Utility function for creating a spin button */
191 /* Written by Bill Wilson for PCB */
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
,
199 GtkWidget
*hbox
= NULL
, *label
, *spin_but
;
205 hbox
= gtk_hbox_new (FALSE
, 0);
206 gtk_box_pack_start (GTK_BOX (box
), hbox
, FALSE
, FALSE
, 2);
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
);
213 *spin_button
= spin_but
;
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
);
219 data
= (gpointer
) spin
;
221 g_signal_connect (G_OBJECT (spin_but
), "value_changed",
222 G_CALLBACK (cb_func
), data
);
225 if (right_align
&& string
)
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);
231 gtk_box_pack_start (GTK_BOX (box
), spin_but
, FALSE
, FALSE
, 2);
232 if (!right_align
&& string
)
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);
241 /* Utility function for creating a check button */
242 /* Written by Bill Wilson for PCB */
244 ghid_check_button_connected (GtkWidget
* box
,
252 gpointer data
, gchar
* string
)
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
);
266 gtk_signal_connect (GTK_OBJECT (b
), "clicked",
267 GTK_SIGNAL_FUNC (cb_func
), data
);
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
278 * Written by Dan McMahill
281 attribute_interface_dialog (gerbv_HID_Attribute
* attrs
,
282 int n_attrs
, gerbv_HID_Attr_Val
* results
,
286 GtkWidget
*dialog
, *main_vbox
, *vbox
, *vbox1
, *hbox
, *entry
;
292 int set_auto_uncheck
= 0;
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.
300 if (all_widgets
!= NULL
)
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
);
309 dprintf ("%s(%p, %d, %p, \"%s\", \"%s\")\n", __FUNCTION__
, attrs
, n_attrs
, results
, title
, descr
);
311 auto_uncheck_needed
= 0;
312 auto_uncheck_widget
= NULL
;
313 auto_uncheck_attr
= NULL
;
315 tips
= gtk_tooltips_new ();
317 dialog
= gtk_dialog_new_with_buttons (title
,
318 GTK_WINDOW (screen
.win
.topLevelWindow
),
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");
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
);
329 vbox
= ghid_category_vbox (main_vbox
, descr
!= NULL
? descr
: "",
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
339 for (j
= 0; j
< n_attrs
; j
++)
341 dprintf ("%s(): Adding attribute #%d\n", __FUNCTION__
, j
);
342 switch (attrs
[j
].type
)
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
);
351 hbox
= gtk_hbox_new (FALSE
, 4);
352 gtk_box_pack_start (GTK_BOX (vbox
), hbox
, FALSE
, FALSE
, 0);
356 * need to pick the "digits" argument based on min/max
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
);
364 gtk_tooltips_set_tip (tips
, widget
, attrs
[j
].help_text
, NULL
);
365 all_widgets
[j
] = widget
;
367 widget
= gtk_label_new (attrs
[j
].name
);
368 gtk_box_pack_start (GTK_BOX (hbox
), widget
, FALSE
, FALSE
, 0);
372 hbox
= gtk_hbox_new (FALSE
, 4);
373 gtk_box_pack_start (GTK_BOX (vbox
), hbox
, FALSE
, FALSE
, 0);
377 * need to pick the "digits" and step size argument more
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,
383 dblspinner_changed_cb
,
384 &(attrs
[j
].default_val
.real_value
), FALSE
, NULL
);
386 gtk_tooltips_set_tip (tips
, widget
, attrs
[j
].help_text
, NULL
);
387 all_widgets
[j
] = widget
;
389 widget
= gtk_label_new (attrs
[j
].name
);
390 gtk_box_pack_start (GTK_BOX (hbox
), widget
, FALSE
, FALSE
, 0);
394 hbox
= gtk_hbox_new (FALSE
, 4);
395 gtk_box_pack_start (GTK_BOX (vbox
), hbox
, FALSE
, FALSE
, 0);
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
;
407 widget
= gtk_label_new (attrs
[j
].name
);
408 gtk_box_pack_start (GTK_BOX (hbox
), widget
, FALSE
, FALSE
, 0);
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
),
418 gtk_tooltips_set_tip (tips
, widget
, attrs
[j
].help_text
, NULL
);
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.
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
);
439 /* if the "auto" button in checked then don't let
440 * anything else be sensitive.
443 if (attrs
[j
].default_val
.int_value
)
446 all_widgets
[j
] = widget
;
451 hbox
= gtk_hbox_new (FALSE
, 4);
452 gtk_box_pack_start (GTK_BOX (vbox
), hbox
, FALSE
, FALSE
, 0);
455 * We have to put the combo_box inside of an event_box in
456 * order for tooltips to work.
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);
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
));
470 * Iterate through each value and add them to the
474 while (attrs
[j
].enumerations
[i
])
476 gtk_combo_box_append_text (GTK_COMBO_BOX (combo
),
477 attrs
[j
].enumerations
[i
]);
480 gtk_combo_box_set_active (GTK_COMBO_BOX (combo
),
481 attrs
[j
].default_val
.int_value
);
482 all_widgets
[j
] = combo
;
484 widget
= gtk_label_new (attrs
[j
].name
);
485 gtk_box_pack_start (GTK_BOX (hbox
), widget
, FALSE
, FALSE
, 0);
489 dprintf ("HID_Mixed\n");
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
));
502 gtk_tooltips_set_tip (tips
, entry
, attrs
[j
].help_text
, NULL
);
503 all_widgets
[j
] = entry
;
507 fprintf (stderr
, "%s: unknown type of HID attribute\n", __FUNCTION__
);
513 gtk_widget_show_all (dialog
);
514 auto_uncheck_needed
= set_auto_uncheck
;
517 * part of the "auto" hack. Make everything sensitive or
518 * insensitive based on the state of the "auto" toggle button (if it
521 for (j
= 1; j
< n_widgets
; j
++) {
522 gtk_widget_set_sensitive (all_widgets
[j
], sen
);
524 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_OK
)
526 /* copy over the results */
527 for (i
= 0; i
< n_attrs
; i
++)
529 results
[i
] = attrs
[i
].default_val
;
530 if (results
[i
].str_value
)
531 results
[i
].str_value
= strdup (results
[i
].str_value
);
538 gtk_widget_destroy (dialog
);