]> Pileus Git - ~andy/gtk/blob - tests/testtreeview.c
ce0f3ccb00fc8bfa0a5fba26b936378f29224900
[~andy/gtk] / tests / testtreeview.c
1
2 #include <gtk/gtk.h>
3 #include <string.h>
4
5
6 static void
7 get_param_specs (GObject *object,
8                  GParamSpec ***specs,
9                  gint         *n_specs)
10 {
11   /* Use private interface for now, fix later */
12   *specs = G_OBJECT_GET_CLASS (object)->property_specs;
13   *n_specs = G_OBJECT_GET_CLASS (object)->n_property_specs;
14 }
15
16 static void
17 g_object_connect_property (GObject *object,
18                            const gchar *prop_name,
19                            GtkSignalFunc func,
20                            gpointer data)
21 {
22   gchar *with_detail = g_strconcat ("notify::", prop_name, NULL);
23   
24   g_signal_connect_data (object, with_detail,
25                          func, data,
26                          NULL, FALSE, FALSE);
27
28   g_free (with_detail);
29 }
30
31 typedef struct 
32 {
33   GObject *obj;
34   gchar *prop;
35 } ObjectProperty;
36
37 static void
38 free_object_property (ObjectProperty *p)
39 {
40   g_free (p->prop);
41   g_free (p);
42 }
43
44 static void
45 connect_controller (GObject *controller,
46                     const gchar *signal,
47                     GObject *model,
48                     const gchar *prop_name,
49                     GtkSignalFunc func)
50 {
51   ObjectProperty *p;
52
53   p = g_new (ObjectProperty, 1);
54   p->obj = model;
55   p->prop = g_strdup (prop_name);
56
57   g_signal_connect_data (controller, signal, func, p,
58                          (GDestroyNotify)free_object_property,
59                          FALSE, FALSE);
60 }
61
62 static void
63 int_modified (GtkAdjustment *adj, gpointer data)
64 {
65   ObjectProperty *p = data;
66
67   g_object_set (p->obj, p->prop, (int) adj->value, NULL);
68 }
69
70 static void
71 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
72 {
73   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
74   GValue val = { 0, };  
75
76   g_value_init (&val, G_TYPE_INT);
77   g_object_get_property (object, pspec->name, &val);
78
79   if (g_value_get_int (&val) != (int)adj->value)
80     gtk_adjustment_set_value (adj, g_value_get_int (&val));
81
82   g_value_unset (&val);
83 }
84
85
86 static void
87 string_modified (GtkEntry *entry, gpointer data)
88 {
89   ObjectProperty *p = data;
90   gchar *text;
91
92   text = gtk_entry_get_text (entry);
93
94   g_object_set (p->obj, p->prop, text, NULL);
95 }
96
97 static void
98 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
99 {
100   GtkEntry *entry = GTK_ENTRY (data);
101   GValue val = { 0, };  
102   gchar *str;
103   gchar *text;
104   
105   g_value_init (&val, G_TYPE_STRING);
106   g_object_get_property (object, pspec->name, &val);
107
108   str = g_value_get_string (&val);
109   if (str == NULL)
110     str = "";
111   text = gtk_entry_get_text (entry);
112
113   if (strcmp (str, text) != 0)
114     gtk_entry_set_text (entry, str);
115   
116   g_value_unset (&val);
117 }
118
119 static void
120 bool_modified (GtkToggleButton *tb, gpointer data)
121 {
122   ObjectProperty *p = data;
123
124   g_object_set (p->obj, p->prop, (int) tb->active, NULL);
125 }
126
127 static void
128 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
129 {
130   GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
131   GValue val = { 0, };  
132   
133   g_value_init (&val, G_TYPE_BOOLEAN);
134   g_object_get_property (object, pspec->name, &val);
135
136   if (g_value_get_boolean (&val) != tb->active)
137     gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
138
139   gtk_label_set_text (GTK_LABEL (GTK_BIN (tb)->child), g_value_get_boolean (&val) ?
140                       "TRUE" : "FALSE");
141   
142   g_value_unset (&val);
143 }
144
145 static GtkWidget*
146 create_prop_editor (GObject *object)
147 {
148   GtkWidget *win;
149   GtkWidget *vbox;
150   GtkWidget *hbox;
151   GtkWidget *label;
152   GtkWidget *prop_edit;
153   GtkWidget *sw;
154   gint n_specs = 0;
155   GParamSpec **specs = NULL;
156   gint i;
157   GtkAdjustment *adj;
158   
159   win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
160
161   /* hold a strong ref to the object we're editing */
162   g_object_ref (G_OBJECT (object));
163   g_object_set_data_full (G_OBJECT (win), "model-object",
164                           object, (GDestroyNotify)g_object_unref);
165   
166   vbox = gtk_vbox_new (TRUE, 2);
167
168   sw = gtk_scrolled_window_new (NULL, NULL);
169   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
170                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
171   
172   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
173   gtk_container_add (GTK_CONTAINER (win), sw);
174   
175   get_param_specs (object, &specs, &n_specs);
176   
177   i = 0;
178   while (i < n_specs)
179     {
180       GParamSpec *spec = specs[i];
181       gboolean can_modify;
182       
183       prop_edit = NULL;
184
185       can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
186                     (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
187       
188       if ((spec->flags & G_PARAM_READABLE) == 0)
189         {
190           /* can't display unreadable properties */
191           ++i;
192           continue;
193         }
194       
195       switch (spec->value_type)
196         {
197         case G_TYPE_INT:
198           hbox = gtk_hbox_new (FALSE, 10);
199           label = gtk_label_new (spec->nick);
200           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
201           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
202           adj = GTK_ADJUSTMENT (gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
203                                                     G_PARAM_SPEC_INT (spec)->minimum,
204                                                     G_PARAM_SPEC_INT (spec)->maximum,
205                                                     1,
206                                                     MAX ((G_PARAM_SPEC_INT (spec)->maximum -
207                                                           G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
208                                                     0.0));
209
210           prop_edit = gtk_spin_button_new (adj, 1.0, 0);
211           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
212           
213           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
214
215           g_object_connect_property (object, spec->name,
216                                      GTK_SIGNAL_FUNC (int_changed),
217                                      adj);
218
219           if (can_modify)
220             connect_controller (G_OBJECT (adj), "value_changed",
221                                 object, spec->name, (GtkSignalFunc) int_modified);
222           break;
223
224         case G_TYPE_STRING:
225           hbox = gtk_hbox_new (FALSE, 10);
226           label = gtk_label_new (spec->nick);
227           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
228           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
229
230           prop_edit = gtk_entry_new ();
231           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
232           
233           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
234
235           g_object_connect_property (object, spec->name,
236                                      GTK_SIGNAL_FUNC (string_changed),
237                                      prop_edit);
238
239           if (can_modify)
240             connect_controller (G_OBJECT (prop_edit), "changed",
241                                 object, spec->name, (GtkSignalFunc) string_modified);
242           break;
243
244         case G_TYPE_BOOLEAN:
245           hbox = gtk_hbox_new (FALSE, 10);
246           label = gtk_label_new (spec->nick);
247           gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
248           gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
249
250           prop_edit = gtk_toggle_button_new_with_label ("");
251           gtk_box_pack_end (GTK_BOX (hbox), prop_edit, FALSE, FALSE, 0);
252           
253           gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); 
254
255           g_object_connect_property (object, spec->name,
256                                      GTK_SIGNAL_FUNC (bool_changed),
257                                      prop_edit);
258
259           if (can_modify)
260             connect_controller (G_OBJECT (prop_edit), "toggled",
261                                 object, spec->name, (GtkSignalFunc) bool_modified);
262           break;
263
264         default:
265           {
266             gchar *msg = g_strdup_printf ("%s: don't know how to edit type %s",
267                                           spec->nick, g_type_name (spec->value_type));
268             hbox = gtk_hbox_new (FALSE, 10);
269             label = gtk_label_new (msg);            
270             g_free (msg);
271             gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
272             gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
273             gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
274           }
275           break;
276         }
277
278       if (prop_edit)
279         {
280           if (!can_modify)
281             gtk_widget_set_sensitive (prop_edit, FALSE);
282           
283           /* set initial value */
284           g_object_notify (object, spec->name);
285         }
286       
287       ++i;
288     }
289
290   gtk_window_set_default_size (GTK_WINDOW (win), 300, 500);
291   
292   gtk_widget_show_all (win);
293
294   return win;
295 }
296
297 int
298 main (int    argc,
299       char **argv)
300 {
301   GtkWidget *window;
302
303   gtk_init (&argc, &argv);
304
305   /* I didn't write the tree test yet, just the property editor to use
306    * inside the tree test ;-)
307    */
308   window = create_prop_editor (G_OBJECT (gtk_text_tag_new ("foo")));
309
310   gtk_main ();
311
312   return 0;
313 }