2 * Copyright (C) 2000 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include "prop-editor.h"
27 get_param_specs (GType type,
31 GObjectClass *class = g_type_class_peek (type);
33 /* We count on the fact we have an instance, or else we'd have
34 * to use g_type_class_ref ();
37 /* Use private interface for now, fix later */
38 *specs = class->property_specs;
39 *n_specs = class->n_property_specs;
45 GObject *alive_object;
50 disconnect_func (gpointer data)
52 DisconnectData *dd = data;
54 g_signal_handler_disconnect (dd->instance, dd->id);
58 signal_removed (gpointer data,
61 DisconnectData *dd = data;
63 g_object_steal_data (dd->alive_object, "alive-object-data");
68 g_object_connect_property (GObject *object,
69 const gchar *prop_name,
72 GObject *alive_object)
75 gchar *with_detail = g_strconcat ("notify::", prop_name, NULL);
78 dd = g_new (DisconnectData, 1);
80 closure = g_cclosure_new (func, data, NULL);
82 g_closure_add_invalidate_notifier (closure, dd, signal_removed);
84 dd->id = g_signal_connect_closure (object, with_detail,
87 dd->instance = object;
88 dd->alive_object = alive_object;
90 g_object_set_data_full (G_OBJECT (alive_object),
106 free_object_property (ObjectProperty *p)
113 connect_controller (GObject *controller,
116 const gchar *prop_name,
121 p = g_new (ObjectProperty, 1);
123 p->prop = g_strdup (prop_name);
125 p->modified_id = g_signal_connect_data (controller, signal, func, p,
126 (GClosureNotify)free_object_property,
128 g_object_set_data (controller, "object-property", p);
132 block_controller (GObject *controller)
134 ObjectProperty *p = g_object_get_data (controller, "object-property");
136 g_signal_handler_block (controller, p->modified_id);
140 unblock_controller (GObject *controller)
142 ObjectProperty *p = g_object_get_data (controller, "object-property");
144 g_signal_handler_unblock (controller, p->modified_id);
148 int_modified (GtkAdjustment *adj, gpointer data)
150 ObjectProperty *p = data;
152 g_object_set (p->obj, p->prop, (int) adj->value, NULL);
156 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
158 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
161 g_value_init (&val, G_TYPE_INT);
162 g_object_get_property (object, pspec->name, &val);
164 if (g_value_get_int (&val) != (int)adj->value)
166 block_controller (G_OBJECT (adj));
167 gtk_adjustment_set_value (adj, g_value_get_int (&val));
168 unblock_controller (G_OBJECT (adj));
171 g_value_unset (&val);
175 float_modified (GtkAdjustment *adj, gpointer data)
177 ObjectProperty *p = data;
179 g_object_set (p->obj, p->prop, (float) adj->value, NULL);
183 float_changed (GObject *object, GParamSpec *pspec, gpointer data)
185 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
188 g_value_init (&val, G_TYPE_FLOAT);
189 g_object_get_property (object, pspec->name, &val);
191 if (g_value_get_float (&val) != (float) adj->value)
193 block_controller (G_OBJECT (adj));
194 gtk_adjustment_set_value (adj, g_value_get_float (&val));
195 unblock_controller (G_OBJECT (adj));
198 g_value_unset (&val);
202 string_modified (GtkEntry *entry, gpointer data)
204 ObjectProperty *p = data;
207 text = gtk_entry_get_text (entry);
209 g_object_set (p->obj, p->prop, text, NULL);
213 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
215 GtkEntry *entry = GTK_ENTRY (data);
220 g_value_init (&val, G_TYPE_STRING);
221 g_object_get_property (object, pspec->name, &val);
223 str = g_value_get_string (&val);
226 text = gtk_entry_get_text (entry);
228 if (strcmp (str, text) != 0)
230 block_controller (G_OBJECT (entry));
231 gtk_entry_set_text (entry, str);
232 unblock_controller (G_OBJECT (entry));
235 g_value_unset (&val);
239 bool_modified (GtkToggleButton *tb, gpointer data)
241 ObjectProperty *p = data;
243 g_object_set (p->obj, p->prop, (int) tb->active, NULL);
247 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
249 GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
252 g_value_init (&val, G_TYPE_BOOLEAN);
253 g_object_get_property (object, pspec->name, &val);
255 if (g_value_get_boolean (&val) != tb->active)
257 block_controller (G_OBJECT (tb));
258 gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
259 unblock_controller (G_OBJECT (tb));
262 gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ?
265 g_value_unset (&val);
270 enum_modified (GtkOptionMenu *om, gpointer data)
272 ObjectProperty *p = data;
277 spec = g_object_class_find_property (G_OBJECT_GET_CLASS (p->obj),
280 eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
282 i = gtk_option_menu_get_history (om);
284 g_object_set (p->obj, p->prop, eclass->values[i].value, NULL);
288 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
290 GtkOptionMenu *om = GTK_OPTION_MENU (data);
295 eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
297 g_value_init (&val, pspec->value_type);
298 g_object_get_property (object, pspec->name, &val);
301 while (i < eclass->n_values)
303 if (eclass->values[i].value == g_value_get_enum (&val))
308 if (gtk_option_menu_get_history (om) != i)
310 block_controller (G_OBJECT (om));
311 gtk_option_menu_set_history (om, i);
312 unblock_controller (G_OBJECT (om));
315 g_value_unset (&val);
320 unichar_get_value (GtkEntry *entry)
322 const gchar *text = gtk_entry_get_text (entry);
325 return g_utf8_get_char (text);
331 unichar_modified (GtkEntry *entry, gpointer data)
333 ObjectProperty *p = data;
334 gunichar val = unichar_get_value (entry);
336 g_object_set (p->obj, p->prop, val, NULL);
340 unichar_changed (GObject *object, GParamSpec *pspec, gpointer data)
342 GtkEntry *entry = GTK_ENTRY (data);
344 gunichar old_val = unichar_get_value (entry);
348 g_object_get (object, pspec->name, &new_val, NULL);
350 if (new_val != old_val)
355 len = g_unichar_to_utf8 (new_val, buf);
359 block_controller (G_OBJECT (entry));
360 gtk_entry_set_text (entry, buf);
361 unblock_controller (G_OBJECT (entry));
366 model_destroy (gpointer data)
368 g_object_steal_data (data, "model-object");
369 gtk_widget_destroy (data);
373 window_destroy (gpointer data)
375 g_object_steal_data (data, "prop-editor-win");
379 create_prop_editor (GObject *object,
386 GtkWidget *prop_edit;
389 GParamSpec **specs = NULL;
394 win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
396 tips = gtk_tooltips_new ();
397 gtk_signal_connect_object (GTK_OBJECT (win), "destroy",
398 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (tips));
400 /* hold a weak ref to the object we're editing */
401 g_object_set_data_full (G_OBJECT (object), "prop-editor-win", win, model_destroy);
402 g_object_set_data_full (G_OBJECT (win), "model-object", object, window_destroy);
404 vbox = gtk_vbox_new (TRUE, 2);
406 sw = gtk_scrolled_window_new (NULL, NULL);
407 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
408 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
410 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
411 gtk_container_add (GTK_CONTAINER (win), sw);
413 get_param_specs (type, &specs, &n_specs);
418 GParamSpec *spec = specs[i];
423 can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
424 (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
426 if ((spec->flags & G_PARAM_READABLE) == 0)
428 /* can't display unreadable properties */
433 switch (G_PARAM_SPEC_TYPE (spec))
435 case G_TYPE_PARAM_INT:
436 hbox = gtk_hbox_new (FALSE, 10);
437 label = gtk_label_new (spec->nick);
438 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
439 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
440 adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
441 G_PARAM_SPEC_INT (spec)->minimum,
442 G_PARAM_SPEC_INT (spec)->maximum,
444 MAX ((G_PARAM_SPEC_INT (spec)->maximum -
445 G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
448 prop_edit = gtk_spin_button_new (adj, 1.0, 0);
449 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
451 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
453 g_object_connect_property (object, spec->name,
454 GTK_SIGNAL_FUNC (int_changed),
455 adj, G_OBJECT (adj));
458 connect_controller (G_OBJECT (adj), "value_changed",
459 object, spec->name, (GtkSignalFunc) int_modified);
462 case G_TYPE_PARAM_FLOAT:
463 hbox = gtk_hbox_new (FALSE, 10);
464 label = gtk_label_new (spec->nick);
465 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
466 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
467 adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value,
468 G_PARAM_SPEC_FLOAT (spec)->minimum,
469 G_PARAM_SPEC_FLOAT (spec)->maximum,
471 MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum -
472 G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1),
475 prop_edit = gtk_spin_button_new (adj, 0.1, 2);
477 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
479 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
481 g_object_connect_property (object, spec->name,
482 GTK_SIGNAL_FUNC (float_changed),
483 adj, G_OBJECT (adj));
486 connect_controller (G_OBJECT (adj), "value_changed",
487 object, spec->name, (GtkSignalFunc) float_modified);
490 case G_TYPE_PARAM_STRING:
491 hbox = gtk_hbox_new (FALSE, 10);
492 label = gtk_label_new (spec->nick);
493 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
494 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
496 prop_edit = gtk_entry_new ();
497 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
499 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
501 g_object_connect_property (object, spec->name,
502 GTK_SIGNAL_FUNC (string_changed),
503 prop_edit, G_OBJECT (prop_edit));
506 connect_controller (G_OBJECT (prop_edit), "changed",
507 object, spec->name, (GtkSignalFunc) string_modified);
510 case G_TYPE_PARAM_BOOLEAN:
511 hbox = gtk_hbox_new (FALSE, 10);
512 label = gtk_label_new (spec->nick);
513 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
514 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
516 prop_edit = gtk_toggle_button_new_with_label ("");
517 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
519 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
521 g_object_connect_property (object, spec->name,
522 GTK_SIGNAL_FUNC (bool_changed),
523 prop_edit, G_OBJECT (prop_edit));
526 connect_controller (G_OBJECT (prop_edit), "toggled",
527 object, spec->name, (GtkSignalFunc) bool_modified);
530 case G_TYPE_PARAM_ENUM:
536 hbox = gtk_hbox_new (FALSE, 10);
537 label = gtk_label_new (spec->nick);
538 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
539 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
541 prop_edit = gtk_option_menu_new ();
543 menu = gtk_menu_new ();
545 eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type));
548 while (i < eclass->n_values)
552 mi = gtk_menu_item_new_with_label (eclass->values[i].value_name);
554 gtk_widget_show (mi);
556 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
561 g_type_class_unref (eclass);
563 gtk_option_menu_set_menu (GTK_OPTION_MENU (prop_edit), menu);
565 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
567 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
569 g_object_connect_property (object, spec->name,
570 GTK_SIGNAL_FUNC (enum_changed),
571 prop_edit, G_OBJECT (prop_edit));
574 connect_controller (G_OBJECT (prop_edit), "changed",
575 object, spec->name, (GtkSignalFunc) enum_modified);
578 case G_TYPE_PARAM_UNICHAR:
579 hbox = gtk_hbox_new (FALSE, 10);
580 label = gtk_label_new (spec->nick);
581 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
582 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
584 prop_edit = gtk_entry_new ();
585 gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1);
586 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
588 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
590 g_object_connect_property (object, spec->name,
591 GTK_SIGNAL_FUNC (unichar_changed),
592 prop_edit, G_OBJECT (prop_edit));
595 connect_controller (G_OBJECT (prop_edit), "changed",
596 object, spec->name, (GtkSignalFunc) unichar_modified);
601 gchar *msg = g_strdup_printf ("%s: don't know how to edit property type %s",
602 spec->nick, g_type_name (G_PARAM_SPEC_TYPE (spec)));
603 hbox = gtk_hbox_new (FALSE, 10);
604 label = gtk_label_new (msg);
606 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
607 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
608 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
615 gtk_widget_set_sensitive (prop_edit, FALSE);
618 gtk_tooltips_set_tip (tips, prop_edit, spec->blurb, NULL);
620 /* set initial value */
621 g_object_notify (object, spec->name);
627 gtk_window_set_default_size (GTK_WINDOW (win), 300, 500);
629 gtk_widget_show_all (win);