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 (GObject *object,
31 /* Use private interface for now, fix later */
32 *specs = G_OBJECT_GET_CLASS (object)->property_specs;
33 *n_specs = G_OBJECT_GET_CLASS (object)->n_property_specs;
39 GObject *alive_object;
44 disconnect_func (gpointer data)
46 DisconnectData *dd = data;
48 g_signal_handler_disconnect (dd->instance, dd->id);
52 signal_removed (gpointer data,
55 DisconnectData *dd = data;
57 g_object_steal_data (dd->alive_object, "alive-object-data");
62 g_object_connect_property (GObject *object,
63 const gchar *prop_name,
66 GObject *alive_object)
69 gchar *with_detail = g_strconcat ("notify::", prop_name, NULL);
72 dd = g_new (DisconnectData, 1);
74 closure = g_cclosure_new (func, data, NULL);
76 g_closure_add_invalidate_notifier (closure, dd, signal_removed);
78 dd->id = g_signal_connect_closure (object, with_detail,
81 dd->instance = object;
82 dd->alive_object = alive_object;
84 g_object_set_data_full (G_OBJECT (alive_object),
99 free_object_property (ObjectProperty *p)
106 connect_controller (GObject *controller,
109 const gchar *prop_name,
114 p = g_new (ObjectProperty, 1);
116 p->prop = g_strdup (prop_name);
118 g_signal_connect_data (controller, signal, func, p,
119 (GClosureNotify)free_object_property,
124 int_modified (GtkAdjustment *adj, gpointer data)
126 ObjectProperty *p = data;
128 g_object_set (p->obj, p->prop, (int) adj->value, NULL);
132 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
134 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
137 g_value_init (&val, G_TYPE_INT);
138 g_object_get_property (object, pspec->name, &val);
140 if (g_value_get_int (&val) != (int)adj->value)
141 gtk_adjustment_set_value (adj, g_value_get_int (&val));
143 g_value_unset (&val);
147 float_modified (GtkAdjustment *adj, gpointer data)
149 ObjectProperty *p = data;
151 g_object_set (p->obj, p->prop, (float) adj->value, NULL);
155 float_changed (GObject *object, GParamSpec *pspec, gpointer data)
157 GtkAdjustment *adj = GTK_ADJUSTMENT (data);
160 g_value_init (&val, G_TYPE_FLOAT);
161 g_object_get_property (object, pspec->name, &val);
163 if (g_value_get_float (&val) != (float) adj->value)
164 gtk_adjustment_set_value (adj, g_value_get_float (&val));
166 g_value_unset (&val);
170 string_modified (GtkEntry *entry, gpointer data)
172 ObjectProperty *p = data;
175 text = gtk_entry_get_text (entry);
177 g_object_set (p->obj, p->prop, text, NULL);
181 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
183 GtkEntry *entry = GTK_ENTRY (data);
188 g_value_init (&val, G_TYPE_STRING);
189 g_object_get_property (object, pspec->name, &val);
191 str = g_value_get_string (&val);
194 text = gtk_entry_get_text (entry);
196 if (strcmp (str, text) != 0)
197 gtk_entry_set_text (entry, str);
199 g_value_unset (&val);
203 bool_modified (GtkToggleButton *tb, gpointer data)
205 ObjectProperty *p = data;
207 g_object_set (p->obj, p->prop, (int) tb->active, NULL);
211 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
213 GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
216 g_value_init (&val, G_TYPE_BOOLEAN);
217 g_object_get_property (object, pspec->name, &val);
219 if (g_value_get_boolean (&val) != tb->active)
220 gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
222 gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ?
225 g_value_unset (&val);
230 enum_modified (GtkOptionMenu *om, gpointer data)
232 ObjectProperty *p = data;
237 spec = g_object_class_find_property (G_OBJECT_GET_CLASS (p->obj),
240 eclass = G_ENUM_CLASS (g_type_class_peek (spec->value_type));
242 i = gtk_option_menu_get_history (om);
244 g_object_set (p->obj, p->prop, eclass->values[i].value, NULL);
248 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
250 GtkOptionMenu *om = GTK_OPTION_MENU (data);
255 eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
257 g_value_init (&val, pspec->value_type);
258 g_object_get_property (object, pspec->name, &val);
261 while (i < eclass->n_values)
263 if (eclass->values[i].value == g_value_get_enum (&val))
268 if (gtk_option_menu_get_history (om) != i)
269 gtk_option_menu_set_history (om, i);
271 g_value_unset (&val);
276 unichar_get_value (GtkEntry *entry)
278 const gchar *text = gtk_entry_get_text (entry);
281 return g_utf8_get_char (text);
287 unichar_modified (GtkEntry *entry, gpointer data)
289 ObjectProperty *p = data;
290 gunichar val = unichar_get_value (entry);
292 g_object_set (p->obj, p->prop, val, NULL);
296 unichar_changed (GObject *object, GParamSpec *pspec, gpointer data)
298 GtkEntry *entry = GTK_ENTRY (data);
300 gunichar old_val = unichar_get_value (entry);
304 g_object_get (object, pspec->name, &new_val, NULL);
306 if (new_val != old_val)
311 len = g_unichar_to_utf8 (new_val, buf);
315 gtk_entry_set_text (entry, buf);
320 model_destroy (gpointer data)
322 g_object_steal_data (data, "model-object");
323 gtk_widget_destroy (data);
327 window_destroy (gpointer data)
329 g_object_steal_data (data, "prop-editor-win");
333 create_prop_editor (GObject *object)
339 GtkWidget *prop_edit;
342 GParamSpec **specs = NULL;
347 win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
349 tips = gtk_tooltips_new ();
350 gtk_signal_connect_object (GTK_OBJECT (win), "destroy",
351 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (tips));
353 /* hold a weak ref to the object we're editing */
354 g_object_set_data_full (G_OBJECT (object), "prop-editor-win", win, model_destroy);
355 g_object_set_data_full (G_OBJECT (win), "model-object", object, window_destroy);
357 vbox = gtk_vbox_new (TRUE, 2);
359 sw = gtk_scrolled_window_new (NULL, NULL);
360 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
361 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
363 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
364 gtk_container_add (GTK_CONTAINER (win), sw);
366 get_param_specs (object, &specs, &n_specs);
371 GParamSpec *spec = specs[i];
376 can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
377 (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
379 if ((spec->flags & G_PARAM_READABLE) == 0)
381 /* can't display unreadable properties */
386 switch (G_PARAM_SPEC_TYPE (spec))
388 case G_TYPE_PARAM_INT:
389 hbox = gtk_hbox_new (FALSE, 10);
390 label = gtk_label_new (spec->nick);
391 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
392 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
393 adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
394 G_PARAM_SPEC_INT (spec)->minimum,
395 G_PARAM_SPEC_INT (spec)->maximum,
397 MAX ((G_PARAM_SPEC_INT (spec)->maximum -
398 G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
401 prop_edit = gtk_spin_button_new (adj, 1.0, 0);
402 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
404 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
406 g_object_connect_property (object, spec->name,
407 GTK_SIGNAL_FUNC (int_changed),
408 adj, G_OBJECT (adj));
411 connect_controller (G_OBJECT (adj), "value_changed",
412 object, spec->name, (GtkSignalFunc) int_modified);
415 case G_TYPE_PARAM_FLOAT:
416 hbox = gtk_hbox_new (FALSE, 10);
417 label = gtk_label_new (spec->nick);
418 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
419 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
420 adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value,
421 G_PARAM_SPEC_FLOAT (spec)->minimum,
422 G_PARAM_SPEC_FLOAT (spec)->maximum,
424 MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum -
425 G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1),
428 prop_edit = gtk_spin_button_new (adj, 0.1, 2);
430 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
432 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
434 g_object_connect_property (object, spec->name,
435 GTK_SIGNAL_FUNC (float_changed),
436 adj, G_OBJECT (adj));
439 connect_controller (G_OBJECT (adj), "value_changed",
440 object, spec->name, (GtkSignalFunc) float_modified);
443 case G_TYPE_PARAM_STRING:
444 hbox = gtk_hbox_new (FALSE, 10);
445 label = gtk_label_new (spec->nick);
446 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
447 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
449 prop_edit = gtk_entry_new ();
450 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
452 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
454 g_object_connect_property (object, spec->name,
455 GTK_SIGNAL_FUNC (string_changed),
456 prop_edit, G_OBJECT (prop_edit));
459 connect_controller (G_OBJECT (prop_edit), "changed",
460 object, spec->name, (GtkSignalFunc) string_modified);
463 case G_TYPE_PARAM_BOOLEAN:
464 hbox = gtk_hbox_new (FALSE, 10);
465 label = gtk_label_new (spec->nick);
466 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
467 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
469 prop_edit = gtk_toggle_button_new_with_label ("");
470 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
472 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
474 g_object_connect_property (object, spec->name,
475 GTK_SIGNAL_FUNC (bool_changed),
476 prop_edit, G_OBJECT (prop_edit));
479 connect_controller (G_OBJECT (prop_edit), "toggled",
480 object, spec->name, (GtkSignalFunc) bool_modified);
483 case G_TYPE_PARAM_ENUM:
489 hbox = gtk_hbox_new (FALSE, 10);
490 label = gtk_label_new (spec->nick);
491 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
492 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
494 prop_edit = gtk_option_menu_new ();
496 menu = gtk_menu_new ();
498 eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type));
501 while (i < eclass->n_values)
505 mi = gtk_menu_item_new_with_label (eclass->values[i].value_name);
507 gtk_widget_show (mi);
509 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
514 g_type_class_unref (eclass);
516 gtk_option_menu_set_menu (GTK_OPTION_MENU (prop_edit), menu);
518 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
520 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
522 g_object_connect_property (object, spec->name,
523 GTK_SIGNAL_FUNC (enum_changed),
524 prop_edit, G_OBJECT (prop_edit));
527 connect_controller (G_OBJECT (prop_edit), "changed",
528 object, spec->name, (GtkSignalFunc) enum_modified);
531 case G_TYPE_PARAM_UNICHAR:
532 hbox = gtk_hbox_new (FALSE, 10);
533 label = gtk_label_new (spec->nick);
534 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
535 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
537 prop_edit = gtk_entry_new ();
538 gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1);
539 gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
541 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
543 g_object_connect_property (object, spec->name,
544 GTK_SIGNAL_FUNC (unichar_changed),
545 prop_edit, G_OBJECT (prop_edit));
548 connect_controller (G_OBJECT (prop_edit), "changed",
549 object, spec->name, (GtkSignalFunc) unichar_modified);
554 gchar *msg = g_strdup_printf ("%s: don't know how to edit property type %s",
555 spec->nick, g_type_name (G_PARAM_SPEC_TYPE (spec)));
556 hbox = gtk_hbox_new (FALSE, 10);
557 label = gtk_label_new (msg);
559 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
560 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
561 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
568 gtk_widget_set_sensitive (prop_edit, FALSE);
571 gtk_tooltips_set_tip (tips, prop_edit, spec->blurb, NULL);
573 /* set initial value */
574 g_object_notify (object, spec->name);
580 gtk_window_set_default_size (GTK_WINDOW (win), 300, 500);
582 gtk_widget_show_all (win);