1 /*
2  * Copyright (c) 2014 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser 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.
8  *
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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 #include <glib/gi18n-lib.h>
20 
21 #include "prop-editor.h"
22 #include "strv-editor.h"
23 #include "object-tree.h"
24 
25 #include "gtkactionable.h"
26 #include "gtkadjustment.h"
27 #include "gtkapplicationwindow.h"
28 #include "gtkcelllayout.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcolorbutton.h"
31 #include "gtkcolorchooser.h"
32 #include "gtkcolorchooserwidget.h"
33 #include "gtkcombobox.h"
34 #include "gtkfontchooser.h"
35 #include "gtkfontchooserwidget.h"
36 #include "gtkiconview.h"
37 #include "gtklabel.h"
38 #include "gtkpopover.h"
39 #include "gtkradiobutton.h"
40 #include "gtkscrolledwindow.h"
41 #include "gtkspinbutton.h"
42 #include "gtksettingsprivate.h"
43 #include "gtktogglebutton.h"
44 #include "gtkwidgetprivate.h"
45 #include "gtkcssnodeprivate.h"
46 
47 struct _GtkInspectorPropEditorPrivate
48 {
49   GObject *object;
50   gchar *name;
51   gboolean is_child_property;
52   GtkWidget *editor;
53 };
54 
55 enum
56 {
57   PROP_0,
58   PROP_OBJECT,
59   PROP_NAME,
60   PROP_IS_CHILD_PROPERTY
61 };
62 
63 enum
64 {
65   SHOW_OBJECT,
66   N_SIGNALS
67 };
68 
69 static guint signals[N_SIGNALS] = { 0 };
70 
71 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorPropEditor, gtk_inspector_prop_editor, GTK_TYPE_BOX);
72 
73 static gboolean
is_child_property(GParamSpec * pspec)74 is_child_property (GParamSpec *pspec)
75 {
76   return g_param_spec_get_qdata (pspec, g_quark_from_string ("is-child-prop")) != NULL;
77 }
78 
79 static GParamSpec *
mark_child_property(GParamSpec * pspec)80 mark_child_property (GParamSpec *pspec)
81 {
82   if (pspec)
83     g_param_spec_set_qdata (pspec, g_quark_from_string ("is-child-prop"), GINT_TO_POINTER (TRUE));
84   return pspec;
85 }
86 
87 static GParamSpec *
find_property(GtkInspectorPropEditor * editor)88 find_property (GtkInspectorPropEditor *editor)
89 {
90   if (editor->priv->is_child_property)
91     {
92       GtkWidget *widget = GTK_WIDGET (editor->priv->object);
93       GtkWidget *parent = gtk_widget_get_parent (widget);
94 
95       return mark_child_property (gtk_container_class_find_child_property (G_OBJECT_GET_CLASS (parent), editor->priv->name));
96     }
97 
98   return g_object_class_find_property (G_OBJECT_GET_CLASS (editor->priv->object), editor->priv->name);
99 }
100 
101 typedef struct
102 {
103   gpointer instance;
104   GObject *alive_object;
105   gulong id;
106 } DisconnectData;
107 
108 static void
disconnect_func(gpointer data)109 disconnect_func (gpointer data)
110 {
111   DisconnectData *dd = data;
112 
113   g_signal_handler_disconnect (dd->instance, dd->id);
114 }
115 
116 static void
signal_removed(gpointer data,GClosure * closure)117 signal_removed (gpointer  data,
118                 GClosure *closure)
119 {
120   DisconnectData *dd = data;
121 
122   g_object_steal_data (dd->alive_object, "alive-object-data");
123   g_free (dd);
124 }
125 
126 static void
g_object_connect_property(GObject * object,GParamSpec * spec,GCallback func,gpointer data,GObject * alive_object)127 g_object_connect_property (GObject    *object,
128                            GParamSpec *spec,
129                            GCallback   func,
130                            gpointer    data,
131                            GObject    *alive_object)
132 {
133   GClosure *closure;
134   gchar *with_detail;
135   DisconnectData *dd;
136 
137   if (is_child_property (spec))
138     with_detail = g_strconcat ("child-notify::", spec->name, NULL);
139   else
140     with_detail = g_strconcat ("notify::", spec->name, NULL);
141 
142   dd = g_new (DisconnectData, 1);
143 
144   closure = g_cclosure_new (func, data, NULL);
145   g_closure_add_invalidate_notifier (closure, dd, signal_removed);
146   dd->id = g_signal_connect_closure (object, with_detail, closure, FALSE);
147   dd->instance = object;
148   dd->alive_object = alive_object;
149 
150   g_object_set_data_full (G_OBJECT (alive_object), "alive-object-data",
151                           dd, disconnect_func);
152 
153   g_free (with_detail);
154 }
155 
156 static void
block_notify(GObject * editor)157 block_notify (GObject *editor)
158 {
159   DisconnectData *dd = (DisconnectData *)g_object_get_data (editor, "alive-object-data");
160 
161   if (dd)
162     g_signal_handler_block (dd->instance, dd->id);
163 }
164 
165 static void
unblock_notify(GObject * editor)166 unblock_notify (GObject *editor)
167 {
168   DisconnectData *dd = (DisconnectData *)g_object_get_data (editor, "alive-object-data");
169 
170   if (dd)
171     g_signal_handler_unblock (dd->instance, dd->id);
172 }
173 
174 typedef struct
175 {
176   GObject *obj;
177   GParamSpec *spec;
178   gulong modified_id;
179 } ObjectProperty;
180 
181 static void
free_object_property(ObjectProperty * p)182 free_object_property (ObjectProperty *p)
183 {
184   g_free (p);
185 }
186 
187 static void
connect_controller(GObject * controller,const gchar * signal,GObject * model,GParamSpec * spec,GCallback func)188 connect_controller (GObject     *controller,
189                     const gchar *signal,
190                     GObject     *model,
191                     GParamSpec  *spec,
192                     GCallback    func)
193 {
194   ObjectProperty *p;
195 
196   p = g_new (ObjectProperty, 1);
197   p->obj = model;
198   p->spec = spec;
199 
200   p->modified_id = g_signal_connect_data (controller, signal, func, p,
201                                           (GClosureNotify)free_object_property, 0);
202   g_object_set_data (controller, "object-property", p);
203 }
204 
205 static void
block_controller(GObject * controller)206 block_controller (GObject *controller)
207 {
208   ObjectProperty *p = g_object_get_data (controller, "object-property");
209 
210   if (p)
211     g_signal_handler_block (controller, p->modified_id);
212 }
213 
214 static void
unblock_controller(GObject * controller)215 unblock_controller (GObject *controller)
216 {
217   ObjectProperty *p = g_object_get_data (controller, "object-property");
218 
219   if (p)
220     g_signal_handler_unblock (controller, p->modified_id);
221 }
222 
223 static void
get_property_value(GObject * object,GParamSpec * pspec,GValue * value)224 get_property_value (GObject *object, GParamSpec *pspec, GValue *value)
225 {
226   if (is_child_property (pspec))
227     {
228       GtkWidget *widget = GTK_WIDGET (object);
229       GtkWidget *parent = gtk_widget_get_parent (widget);
230 
231       gtk_container_child_get_property (GTK_CONTAINER (parent),
232                                         widget, pspec->name, value);
233     }
234   else
235     g_object_get_property (object, pspec->name, value);
236 }
237 
238 static void
set_property_value(GObject * object,GParamSpec * pspec,GValue * value)239 set_property_value (GObject *object, GParamSpec *pspec, GValue *value)
240 {
241   if (is_child_property (pspec))
242     {
243       GtkWidget *widget = GTK_WIDGET (object);
244       GtkWidget *parent = gtk_widget_get_parent (widget);
245 
246       gtk_container_child_set_property (GTK_CONTAINER (parent),
247                                         widget, pspec->name, value);
248     }
249   else
250     g_object_set_property (object, pspec->name, value);
251 }
252 
253 static void
notify_property(GObject * object,GParamSpec * pspec)254 notify_property (GObject *object, GParamSpec *pspec)
255 {
256   if (is_child_property (pspec))
257     {
258       GtkWidget *widget = GTK_WIDGET (object);
259       GtkWidget *parent = gtk_widget_get_parent (widget);
260 
261       gtk_container_child_notify (GTK_CONTAINER (parent), widget, pspec->name);
262     }
263   else
264     g_object_notify (object, pspec->name);
265 }
266 
267 static void
int_modified(GtkAdjustment * adj,ObjectProperty * p)268 int_modified (GtkAdjustment *adj, ObjectProperty *p)
269 {
270   GValue val = G_VALUE_INIT;
271 
272   g_value_init (&val, G_TYPE_INT);
273   g_value_set_int (&val, (int) gtk_adjustment_get_value (adj));
274   set_property_value (p->obj, p->spec, &val);
275   g_value_unset (&val);
276 }
277 
278 static void
int_changed(GObject * object,GParamSpec * pspec,gpointer data)279 int_changed (GObject *object, GParamSpec *pspec, gpointer data)
280 {
281   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
282   GValue val = G_VALUE_INIT;
283 
284   g_value_init (&val, G_TYPE_INT);
285   get_property_value (object, pspec, &val);
286 
287   if (g_value_get_int (&val) != (int)gtk_adjustment_get_value (adj))
288     {
289       block_controller (G_OBJECT (adj));
290       gtk_adjustment_set_value (adj, g_value_get_int (&val));
291       unblock_controller (G_OBJECT (adj));
292     }
293 
294   g_value_unset (&val);
295 }
296 static void
uint_modified(GtkAdjustment * adj,ObjectProperty * p)297 uint_modified (GtkAdjustment *adj, ObjectProperty *p)
298 {
299   GValue val = G_VALUE_INIT;
300 
301   g_value_init (&val, G_TYPE_UINT);
302   g_value_set_uint (&val, (guint) gtk_adjustment_get_value (adj));
303   set_property_value (p->obj, p->spec, &val);
304   g_value_unset (&val);
305 }
306 
307 static void
uint_changed(GObject * object,GParamSpec * pspec,gpointer data)308 uint_changed (GObject *object, GParamSpec *pspec, gpointer data)
309 {
310   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
311   GValue val = G_VALUE_INIT;
312 
313   g_value_init (&val, G_TYPE_UINT);
314   get_property_value (object, pspec, &val);
315 
316   if (g_value_get_uint (&val) != (guint)gtk_adjustment_get_value (adj))
317     {
318       block_controller (G_OBJECT (adj));
319       gtk_adjustment_set_value (adj, g_value_get_uint (&val));
320       unblock_controller (G_OBJECT (adj));
321     }
322 
323   g_value_unset (&val);
324 }
325 
326 static void
float_modified(GtkAdjustment * adj,ObjectProperty * p)327 float_modified (GtkAdjustment *adj, ObjectProperty *p)
328 {
329   GValue val = G_VALUE_INIT;
330 
331   g_value_init (&val, G_TYPE_FLOAT);
332   g_value_set_float (&val, (float) gtk_adjustment_get_value (adj));
333   set_property_value (p->obj, p->spec, &val);
334   g_value_unset (&val);
335 }
336 
337 static void
float_changed(GObject * object,GParamSpec * pspec,gpointer data)338 float_changed (GObject *object, GParamSpec *pspec, gpointer data)
339 {
340   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
341   GValue val = G_VALUE_INIT;
342 
343   g_value_init (&val, G_TYPE_FLOAT);
344   get_property_value (object, pspec, &val);
345 
346   if (g_value_get_float (&val) != (float) gtk_adjustment_get_value (adj))
347     {
348       block_controller (G_OBJECT (adj));
349       gtk_adjustment_set_value (adj, g_value_get_float (&val));
350       unblock_controller (G_OBJECT (adj));
351     }
352 
353   g_value_unset (&val);
354 }
355 
356 static void
double_modified(GtkAdjustment * adj,ObjectProperty * p)357 double_modified (GtkAdjustment *adj, ObjectProperty *p)
358 {
359   GValue val = G_VALUE_INIT;
360 
361   g_value_init (&val, G_TYPE_DOUBLE);
362   g_value_set_double (&val, gtk_adjustment_get_value (adj));
363   set_property_value (p->obj, p->spec, &val);
364   g_value_unset (&val);
365 }
366 
367 static void
double_changed(GObject * object,GParamSpec * pspec,gpointer data)368 double_changed (GObject *object, GParamSpec *pspec, gpointer data)
369 {
370   GtkAdjustment *adj = GTK_ADJUSTMENT (data);
371   GValue val = G_VALUE_INIT;
372 
373   g_value_init (&val, G_TYPE_DOUBLE);
374   get_property_value (object, pspec, &val);
375 
376   if (g_value_get_double (&val) != gtk_adjustment_get_value (adj))
377     {
378       block_controller (G_OBJECT (adj));
379       gtk_adjustment_set_value (adj, g_value_get_double (&val));
380       unblock_controller (G_OBJECT (adj));
381     }
382 
383   g_value_unset (&val);
384 }
385 
386 static void
string_modified(GtkEntry * entry,ObjectProperty * p)387 string_modified (GtkEntry *entry, ObjectProperty *p)
388 {
389   GValue val = G_VALUE_INIT;
390 
391   g_value_init (&val, G_TYPE_STRING);
392   g_value_set_static_string (&val, gtk_entry_get_text (entry));
393   set_property_value (p->obj, p->spec, &val);
394   g_value_unset (&val);
395 }
396 
397 static void
intern_string_modified(GtkEntry * entry,ObjectProperty * p)398 intern_string_modified (GtkEntry *entry, ObjectProperty *p)
399 {
400   const gchar *s;
401 
402   s = g_intern_string (gtk_entry_get_text (entry));
403   if (g_str_equal (p->spec->name, "id"))
404     gtk_css_node_set_id (GTK_CSS_NODE (p->obj), s);
405   else if (g_str_equal (p->spec->name, "name"))
406     gtk_css_node_set_name (GTK_CSS_NODE (p->obj), s);
407 }
408 
409 static void
string_changed(GObject * object,GParamSpec * pspec,gpointer data)410 string_changed (GObject *object, GParamSpec *pspec, gpointer data)
411 {
412   GtkEntry *entry = GTK_ENTRY (data);
413   GValue val = G_VALUE_INIT;
414   const gchar *str;
415   const gchar *text;
416 
417   g_value_init (&val, G_TYPE_STRING);
418   get_property_value (object, pspec, &val);
419 
420   str = g_value_get_string (&val);
421   if (str == NULL)
422     str = "";
423   text = gtk_entry_get_text (entry);
424   if (g_strcmp0 (str, text) != 0)
425     {
426       block_controller (G_OBJECT (entry));
427       gtk_entry_set_text (entry, str);
428       unblock_controller (G_OBJECT (entry));
429     }
430 
431   g_value_unset (&val);
432 }
433 
434 static void
strv_modified(GtkInspectorStrvEditor * editor,ObjectProperty * p)435 strv_modified (GtkInspectorStrvEditor *editor, ObjectProperty *p)
436 {
437   GValue val = G_VALUE_INIT;
438   gchar **strv;
439 
440   g_value_init (&val, G_TYPE_STRV);
441   strv = gtk_inspector_strv_editor_get_strv (editor);
442   g_value_take_boxed (&val, strv);
443   block_notify (G_OBJECT (editor));
444   set_property_value (p->obj, p->spec, &val);
445   unblock_notify (G_OBJECT (editor));
446   g_value_unset (&val);
447 }
448 
449 static void
strv_changed(GObject * object,GParamSpec * pspec,gpointer data)450 strv_changed (GObject *object, GParamSpec *pspec, gpointer data)
451 {
452   GtkInspectorStrvEditor *editor = data;
453   GValue val = G_VALUE_INIT;
454   gchar **strv;
455 
456   g_value_init (&val, G_TYPE_STRV);
457   get_property_value (object, pspec, &val);
458 
459   strv = g_value_get_boxed (&val);
460   block_controller (G_OBJECT (editor));
461   gtk_inspector_strv_editor_set_strv (editor, strv);
462   unblock_controller (G_OBJECT (editor));
463 
464   g_value_unset (&val);
465 }
466 static void
bool_modified(GtkToggleButton * tb,ObjectProperty * p)467 bool_modified (GtkToggleButton *tb, ObjectProperty *p)
468 {
469   GValue val = G_VALUE_INIT;
470 
471   g_value_init (&val, G_TYPE_BOOLEAN);
472   g_value_set_boolean (&val, gtk_toggle_button_get_active (tb));
473   set_property_value (p->obj, p->spec, &val);
474   g_value_unset (&val);
475 }
476 
477 static void
bool_changed(GObject * object,GParamSpec * pspec,gpointer data)478 bool_changed (GObject *object, GParamSpec *pspec, gpointer data)
479 {
480   GtkToggleButton *tb = GTK_TOGGLE_BUTTON (data);
481   GValue val = G_VALUE_INIT;
482 
483   g_value_init (&val, G_TYPE_BOOLEAN);
484   get_property_value (object, pspec, &val);
485 
486   if (g_value_get_boolean (&val) != gtk_toggle_button_get_active (tb))
487     {
488       block_controller (G_OBJECT (tb));
489       gtk_toggle_button_set_active (tb, g_value_get_boolean (&val));
490       unblock_controller (G_OBJECT (tb));
491     }
492 
493   gtk_button_set_label (GTK_BUTTON (tb),
494                         g_value_get_boolean (&val) ? "TRUE" : "FALSE");
495 
496   g_value_unset (&val);
497 }
498 
499 static void
enum_modified(GtkToggleButton * button,ObjectProperty * p)500 enum_modified (GtkToggleButton *button, ObjectProperty *p)
501 {
502   gint i;
503   GEnumClass *eclass;
504   GValue val = G_VALUE_INIT;
505 
506   if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
507     return;
508 
509   eclass = G_ENUM_CLASS (g_type_class_peek (p->spec->value_type));
510   i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index"));
511 
512   g_value_init (&val, p->spec->value_type);
513   g_value_set_enum (&val, eclass->values[i].value);
514   set_property_value (p->obj, p->spec, &val);
515   g_value_unset (&val);
516 }
517 
518 static void
enum_changed(GObject * object,GParamSpec * pspec,gpointer data)519 enum_changed (GObject *object, GParamSpec *pspec, gpointer data)
520 {
521   GtkWidget *viewport;
522   GtkWidget *box;
523   GList *children, *c;
524   GValue val = G_VALUE_INIT;
525   GEnumClass *eclass;
526   gint i, j;
527 
528   eclass = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
529 
530   g_value_init (&val, pspec->value_type);
531   get_property_value (object, pspec, &val);
532 
533   i = 0;
534   while (i < eclass->n_values)
535     {
536       if (eclass->values[i].value == g_value_get_enum (&val))
537         break;
538       ++i;
539     }
540   g_value_unset (&val);
541 
542   viewport = gtk_bin_get_child (GTK_BIN (data));
543   box = gtk_bin_get_child (GTK_BIN (viewport));
544   children = gtk_container_get_children (GTK_CONTAINER (box));
545 
546   for (c = children; c; c = c->next)
547     block_controller (G_OBJECT (c->data));
548 
549   for (c = children, j = 0; c; c = c->next, j++)
550     {
551       if (j == i)
552         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c->data), TRUE);
553     }
554 
555   for (c = children; c; c = c->next)
556     unblock_controller (G_OBJECT (c->data));
557 }
558 
559 static void
flags_modified(GtkCheckButton * button,ObjectProperty * p)560 flags_modified (GtkCheckButton *button, ObjectProperty *p)
561 {
562   gboolean active;
563   GFlagsClass *fclass;
564   guint flags;
565   gint i;
566   GValue val = G_VALUE_INIT;
567 
568   active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
569   i = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "index"));
570   fclass = G_FLAGS_CLASS (g_type_class_peek (p->spec->value_type));
571 
572   g_value_init (&val, p->spec->value_type);
573   get_property_value (p->obj, p->spec, &val);
574   flags = g_value_get_flags (&val);
575   if (active)
576     flags |= fclass->values[i].value;
577   else
578     flags &= ~fclass->values[i].value;
579   g_value_set_flags (&val, flags);
580   set_property_value (p->obj, p->spec, &val);
581   g_value_unset (&val);
582 }
583 
584 static void
flags_changed(GObject * object,GParamSpec * pspec,gpointer data)585 flags_changed (GObject *object, GParamSpec *pspec, gpointer data)
586 {
587   GList *children, *c;
588   GValue val = G_VALUE_INIT;
589   GFlagsClass *fclass;
590   guint flags;
591   gint i;
592   GtkWidget *viewport;
593   GtkWidget *box;
594 
595   fclass = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type));
596 
597   g_value_init (&val, pspec->value_type);
598   get_property_value (object, pspec, &val);
599   flags = g_value_get_flags (&val);
600   g_value_unset (&val);
601 
602   viewport = gtk_bin_get_child (GTK_BIN (data));
603   box = gtk_bin_get_child (GTK_BIN (viewport));
604   children = gtk_container_get_children (GTK_CONTAINER (box));
605 
606   for (c = children; c; c = c->next)
607     block_controller (G_OBJECT (c->data));
608 
609   for (c = children, i = 0; c; c = c->next, i++)
610     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (c->data),
611                                   (fclass->values[i].value & flags) != 0);
612 
613   for (c = children; c; c = c->next)
614     unblock_controller (G_OBJECT (c->data));
615 
616   g_list_free (children);
617 }
618 
619 static gunichar
unichar_get_value(GtkEntry * entry)620 unichar_get_value (GtkEntry *entry)
621 {
622   const gchar *text = gtk_entry_get_text (entry);
623 
624   if (text[0])
625     return g_utf8_get_char (text);
626   else
627     return 0;
628 }
629 
630 static void
unichar_modified(GtkEntry * entry,ObjectProperty * p)631 unichar_modified (GtkEntry *entry, ObjectProperty *p)
632 {
633   gunichar u = unichar_get_value (entry);
634   GValue val = G_VALUE_INIT;
635 
636   g_value_init (&val, p->spec->value_type);
637   g_value_set_uint (&val, u);
638   set_property_value (p->obj, p->spec, &val);
639   g_value_unset (&val);
640 }
641 static void
unichar_changed(GObject * object,GParamSpec * pspec,gpointer data)642 unichar_changed (GObject *object, GParamSpec *pspec, gpointer data)
643 {
644   GtkEntry *entry = GTK_ENTRY (data);
645   gunichar new_val;
646   gunichar old_val = unichar_get_value (entry);
647   GValue val = G_VALUE_INIT;
648   gchar buf[7];
649   gint len;
650 
651   g_value_init (&val, pspec->value_type);
652   get_property_value (object, pspec, &val);
653   new_val = (gunichar)g_value_get_uint (&val);
654 
655   if (new_val != old_val)
656     {
657       if (!new_val)
658         len = 0;
659       else
660         len = g_unichar_to_utf8 (new_val, buf);
661 
662       buf[len] = '\0';
663 
664       block_controller (G_OBJECT (entry));
665       gtk_entry_set_text (entry, buf);
666       unblock_controller (G_OBJECT (entry));
667     }
668 }
669 
670 static void
pointer_changed(GObject * object,GParamSpec * pspec,gpointer data)671 pointer_changed (GObject *object, GParamSpec *pspec, gpointer data)
672 {
673   GtkLabel *label = GTK_LABEL (data);
674   gchar *str;
675   gpointer ptr;
676 
677   g_object_get (object, pspec->name, &ptr, NULL);
678 
679   str = g_strdup_printf (_("Pointer: %p"), ptr);
680   gtk_label_set_text (label, str);
681   g_free (str);
682 }
683 
684 static gchar *
object_label(GObject * obj,GParamSpec * pspec)685 object_label (GObject *obj, GParamSpec *pspec)
686 {
687   const gchar *name;
688 
689   if (obj)
690     name = g_type_name (G_TYPE_FROM_INSTANCE (obj));
691   else if (pspec)
692     name = g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec));
693   else
694     name = C_("type name", "Unknown");
695   return g_strdup_printf (_("Object: %p (%s)"), obj, name);
696 }
697 
698 static void
object_changed(GObject * object,GParamSpec * pspec,gpointer data)699 object_changed (GObject *object, GParamSpec *pspec, gpointer data)
700 {
701   GtkWidget *label, *button;
702   gchar *str;
703   GObject *obj;
704 
705   GList *children = gtk_container_get_children (GTK_CONTAINER (data));
706   label = GTK_WIDGET (children->data);
707   button = GTK_WIDGET (children->next->data);
708   g_object_get (object, pspec->name, &obj, NULL);
709   g_list_free (children);
710 
711   str = object_label (obj, pspec);
712 
713   gtk_label_set_text (GTK_LABEL (label), str);
714   gtk_widget_set_sensitive (button, G_IS_OBJECT (obj));
715 
716   if (obj)
717     g_object_unref (obj);
718 
719   g_free (str);
720 }
721 
722 static void
object_properties(GtkInspectorPropEditor * editor)723 object_properties (GtkInspectorPropEditor *editor)
724 {
725   GObject *obj;
726 
727   g_object_get (editor->priv->object, editor->priv->name, &obj, NULL);
728   if (G_IS_OBJECT (obj))
729     g_signal_emit (editor, signals[SHOW_OBJECT], 0, obj, editor->priv->name, "properties");
730 }
731 
732 static void
rgba_modified(GtkColorButton * cb,GParamSpec * ignored,ObjectProperty * p)733 rgba_modified (GtkColorButton *cb, GParamSpec *ignored, ObjectProperty *p)
734 {
735   GValue val = G_VALUE_INIT;
736 
737   g_value_init (&val, p->spec->value_type);
738   g_object_get_property (G_OBJECT (cb), "rgba", &val);
739   set_property_value (p->obj, p->spec, &val);
740   g_value_unset (&val);
741 }
742 
743 static void
rgba_changed(GObject * object,GParamSpec * pspec,gpointer data)744 rgba_changed (GObject *object, GParamSpec *pspec, gpointer data)
745 {
746   GtkColorChooser *cb = GTK_COLOR_CHOOSER (data);
747   GValue val = G_VALUE_INIT;
748   GdkRGBA *color;
749   GdkRGBA cb_color;
750 
751   g_value_init (&val, GDK_TYPE_RGBA);
752   get_property_value (object, pspec, &val);
753 
754   color = g_value_get_boxed (&val);
755   gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &cb_color);
756 
757   if (color != NULL && !gdk_rgba_equal (color, &cb_color))
758     {
759       block_controller (G_OBJECT (cb));
760       gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (cb), color);
761       unblock_controller (G_OBJECT (cb));
762     }
763  g_value_unset (&val);
764 }
765 
766 static void
color_modified(GtkColorButton * cb,GParamSpec * ignored,ObjectProperty * p)767 color_modified (GtkColorButton *cb, GParamSpec *ignored, ObjectProperty *p)
768 {
769   GdkRGBA rgba;
770   GdkColor color;
771   GValue val = G_VALUE_INIT;
772 
773   gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (cb), &rgba);
774   color.red = 65535 * rgba.red;
775   color.green = 65535 * rgba.green;
776   color.blue = 65535 * rgba.blue;
777 
778   g_value_init (&val, p->spec->value_type);
779   g_value_set_boxed (&val, &color);
780   set_property_value (p->obj, p->spec, &val);
781   g_value_unset (&val);
782 }
783 
784 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
785 static void
color_changed(GObject * object,GParamSpec * pspec,gpointer data)786 color_changed (GObject *object, GParamSpec *pspec, gpointer data)
787 {
788   GtkColorChooser *cb = GTK_COLOR_CHOOSER (data);
789   GValue val = G_VALUE_INIT;
790   GdkColor *color;
791   GdkRGBA rgba;
792 
793   g_value_init (&val, GDK_TYPE_COLOR);
794   get_property_value (object, pspec, &val);
795   color = g_value_get_boxed (&val);
796   rgba.red = color->red / 65535.0;
797   rgba.green = color->green / 65535.0;
798   rgba.blue = color->blue / 65535.0;
799   rgba.alpha = 1.0;
800 
801   if (g_value_get_boxed (&val))
802     {
803       block_controller (G_OBJECT (cb));
804       gtk_color_chooser_set_rgba (cb, &rgba);
805       unblock_controller (G_OBJECT (cb));
806     }
807 
808   g_value_unset (&val);
809 }
810 G_GNUC_END_IGNORE_DEPRECATIONS
811 
812 static void
font_modified(GtkFontChooser * fb,GParamSpec * pspec,ObjectProperty * p)813 font_modified (GtkFontChooser *fb, GParamSpec *pspec, ObjectProperty *p)
814 {
815   GValue val = G_VALUE_INIT;
816 
817   g_value_init (&val, PANGO_TYPE_FONT_DESCRIPTION);
818   g_object_get_property (G_OBJECT (fb), "font-desc", &val);
819   set_property_value (p->obj, p->spec, &val);
820   g_value_unset (&val);
821 }
822 
823 static void
font_changed(GObject * object,GParamSpec * pspec,gpointer data)824 font_changed (GObject *object, GParamSpec *pspec, gpointer data)
825 {
826   GtkFontChooser *fb = GTK_FONT_CHOOSER (data);
827   GValue val = G_VALUE_INIT;
828   const PangoFontDescription *font_desc;
829   PangoFontDescription *fb_font_desc;
830 
831   g_value_init (&val, PANGO_TYPE_FONT_DESCRIPTION);
832   get_property_value (object, pspec, &val);
833 
834   font_desc = g_value_get_boxed (&val);
835   fb_font_desc = gtk_font_chooser_get_font_desc (fb);
836 
837   if (font_desc == NULL ||
838       (fb_font_desc != NULL &&
839        !pango_font_description_equal (fb_font_desc, font_desc)))
840     {
841       block_controller (G_OBJECT (fb));
842       gtk_font_chooser_set_font_desc (fb, font_desc);
843       unblock_controller (G_OBJECT (fb));
844     }
845 
846   g_value_unset (&val);
847   pango_font_description_free (fb_font_desc);
848 }
849 
850 static GtkWidget *
property_editor(GObject * object,GParamSpec * spec,GtkInspectorPropEditor * editor)851 property_editor (GObject                *object,
852                  GParamSpec             *spec,
853                  GtkInspectorPropEditor *editor)
854 {
855   GtkWidget *prop_edit;
856   GtkAdjustment *adj;
857   gchar *msg;
858   GType type = G_PARAM_SPEC_TYPE (spec);
859 
860   if (type == G_TYPE_PARAM_INT)
861     {
862       adj = gtk_adjustment_new (G_PARAM_SPEC_INT (spec)->default_value,
863                                 G_PARAM_SPEC_INT (spec)->minimum,
864                                 G_PARAM_SPEC_INT (spec)->maximum,
865                                 1,
866                                 MAX ((G_PARAM_SPEC_INT (spec)->maximum - G_PARAM_SPEC_INT (spec)->minimum) / 10, 1),
867                                 0.0);
868 
869       prop_edit = gtk_spin_button_new (adj, 1.0, 0);
870 
871       g_object_connect_property (object, spec, G_CALLBACK (int_changed), adj, G_OBJECT (adj));
872 
873       connect_controller (G_OBJECT (adj), "value_changed",
874                           object, spec, G_CALLBACK (int_modified));
875     }
876   else if (type == G_TYPE_PARAM_UINT)
877     {
878       adj = gtk_adjustment_new (G_PARAM_SPEC_UINT (spec)->default_value,
879                                 G_PARAM_SPEC_UINT (spec)->minimum,
880                                 G_PARAM_SPEC_UINT (spec)->maximum,
881                                 1,
882                                 MAX ((G_PARAM_SPEC_UINT (spec)->maximum - G_PARAM_SPEC_UINT (spec)->minimum) / 10, 1),
883                                 0.0);
884 
885       prop_edit = gtk_spin_button_new (adj, 1.0, 0);
886 
887       g_object_connect_property (object, spec,
888                                  G_CALLBACK (uint_changed),
889                                  adj, G_OBJECT (adj));
890 
891       connect_controller (G_OBJECT (adj), "value_changed",
892                           object, spec, G_CALLBACK (uint_modified));
893     }
894   else if (type == G_TYPE_PARAM_FLOAT)
895     {
896       adj = gtk_adjustment_new (G_PARAM_SPEC_FLOAT (spec)->default_value,
897                                 G_PARAM_SPEC_FLOAT (spec)->minimum,
898                                 G_PARAM_SPEC_FLOAT (spec)->maximum,
899                                 0.1,
900                                 MAX ((G_PARAM_SPEC_FLOAT (spec)->maximum - G_PARAM_SPEC_FLOAT (spec)->minimum) / 10, 0.1),
901                                 0.0);
902 
903       prop_edit = gtk_spin_button_new (adj, 0.1, 2);
904 
905       g_object_connect_property (object, spec,
906                                  G_CALLBACK (float_changed),
907                                  adj, G_OBJECT (adj));
908 
909       connect_controller (G_OBJECT (adj), "value_changed",
910                           object, spec, G_CALLBACK (float_modified));
911     }
912   else if (type == G_TYPE_PARAM_DOUBLE)
913     {
914       adj = gtk_adjustment_new (G_PARAM_SPEC_DOUBLE (spec)->default_value,
915                                 G_PARAM_SPEC_DOUBLE (spec)->minimum,
916                                 G_PARAM_SPEC_DOUBLE (spec)->maximum,
917                                 0.1,
918                                 1.0,
919                                 0.0);
920 
921       prop_edit = gtk_spin_button_new (adj, 0.1, 2);
922 
923       g_object_connect_property (object, spec,
924                                  G_CALLBACK (double_changed),
925                                  adj, G_OBJECT (adj));
926 
927       connect_controller (G_OBJECT (adj), "value_changed",
928                           object, spec, G_CALLBACK (double_modified));
929     }
930   else if (type == G_TYPE_PARAM_STRING)
931     {
932       prop_edit = gtk_entry_new ();
933 
934       g_object_connect_property (object, spec,
935                                  G_CALLBACK (string_changed),
936                                  prop_edit, G_OBJECT (prop_edit));
937 
938       if (GTK_IS_CSS_NODE (object))
939         connect_controller (G_OBJECT (prop_edit), "changed",
940                             object, spec, G_CALLBACK (intern_string_modified));
941       else
942         connect_controller (G_OBJECT (prop_edit), "changed",
943                             object, spec, G_CALLBACK (string_modified));
944     }
945   else if (type == G_TYPE_PARAM_BOOLEAN)
946     {
947       prop_edit = gtk_toggle_button_new_with_label ("");
948 
949       g_object_connect_property (object, spec,
950                                  G_CALLBACK (bool_changed),
951                                  prop_edit, G_OBJECT (prop_edit));
952 
953       connect_controller (G_OBJECT (prop_edit), "toggled",
954                           object, spec, G_CALLBACK (bool_modified));
955     }
956   else if (type == G_TYPE_PARAM_ENUM)
957     {
958       {
959         GtkWidget *box;
960         GEnumClass *eclass;
961         GtkWidget *first;
962         gint j;
963 
964         prop_edit = gtk_scrolled_window_new (NULL, NULL);
965         g_object_set (prop_edit,
966                       "expand", TRUE,
967                       "hscrollbar-policy", GTK_POLICY_NEVER,
968                       "vscrollbar-policy", GTK_POLICY_NEVER,
969                       NULL);
970         box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
971         gtk_widget_show (box);
972         gtk_container_add (GTK_CONTAINER (prop_edit), box);
973 
974         eclass = G_ENUM_CLASS (g_type_class_ref (spec->value_type));
975 
976         j = 0;
977         first = NULL;
978         while (j < eclass->n_values)
979           {
980             GtkWidget *b;
981 
982             b = gtk_radio_button_new_with_label_from_widget ((GtkRadioButton*)first, eclass->values[j].value_name);
983             if (first == NULL)
984               first = b;
985             g_object_set_data (G_OBJECT (b), "index", GINT_TO_POINTER (j));
986             gtk_widget_show (b);
987             gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
988             connect_controller (G_OBJECT (b), "toggled",
989                                 object, spec, G_CALLBACK (enum_modified));
990             ++j;
991           }
992 
993         if (j >= 10)
994           g_object_set (prop_edit, "vscrollbar-policy", GTK_POLICY_AUTOMATIC, NULL);
995 
996         g_type_class_unref (eclass);
997 
998         g_object_connect_property (object, spec,
999                                    G_CALLBACK (enum_changed),
1000                                    prop_edit, G_OBJECT (prop_edit));
1001       }
1002     }
1003   else if (type == G_TYPE_PARAM_FLAGS)
1004     {
1005       {
1006         GtkWidget *box;
1007         GFlagsClass *fclass;
1008         gint j;
1009 
1010         prop_edit = gtk_scrolled_window_new (NULL, NULL);
1011         g_object_set (prop_edit,
1012                       "expand", TRUE,
1013                       "hscrollbar-policy", GTK_POLICY_NEVER,
1014                       "vscrollbar-policy", GTK_POLICY_NEVER,
1015                       NULL);
1016         box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1017         gtk_widget_show (box);
1018         gtk_container_add (GTK_CONTAINER (prop_edit), box);
1019 
1020         fclass = G_FLAGS_CLASS (g_type_class_ref (spec->value_type));
1021 
1022         for (j = 0; j < fclass->n_values; j++)
1023           {
1024             GtkWidget *b;
1025 
1026             b = gtk_check_button_new_with_label (fclass->values[j].value_name);
1027             g_object_set_data (G_OBJECT (b), "index", GINT_TO_POINTER (j));
1028             gtk_widget_show (b);
1029             gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
1030             connect_controller (G_OBJECT (b), "toggled",
1031                                 object, spec, G_CALLBACK (flags_modified));
1032           }
1033 
1034         if (j >= 10)
1035           g_object_set (prop_edit, "vscrollbar-policy", GTK_POLICY_AUTOMATIC, NULL);
1036 
1037         g_type_class_unref (fclass);
1038 
1039         g_object_connect_property (object, spec,
1040                                    G_CALLBACK (flags_changed),
1041                                    prop_edit, G_OBJECT (prop_edit));
1042       }
1043     }
1044   else if (type == G_TYPE_PARAM_UNICHAR)
1045     {
1046       prop_edit = gtk_entry_new ();
1047       gtk_entry_set_max_length (GTK_ENTRY (prop_edit), 1);
1048 
1049       g_object_connect_property (object, spec,
1050                                  G_CALLBACK (unichar_changed),
1051                                  prop_edit, G_OBJECT (prop_edit));
1052 
1053       connect_controller (G_OBJECT (prop_edit), "changed",
1054                           object, spec, G_CALLBACK (unichar_modified));
1055     }
1056   else if (type == G_TYPE_PARAM_POINTER)
1057     {
1058       prop_edit = gtk_label_new ("");
1059 
1060       g_object_connect_property (object, spec,
1061                                  G_CALLBACK (pointer_changed),
1062                                  prop_edit, G_OBJECT (prop_edit));
1063     }
1064   else if (type == G_TYPE_PARAM_OBJECT)
1065     {
1066       GtkWidget *label, *button;
1067 
1068       prop_edit = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 5);
1069 
1070       label = gtk_label_new ("");
1071       button = gtk_button_new_with_label (_("Properties"));
1072       g_signal_connect_swapped (button, "clicked",
1073                                 G_CALLBACK (object_properties),
1074                                 editor);
1075       gtk_container_add (GTK_CONTAINER (prop_edit), label);
1076       gtk_container_add (GTK_CONTAINER (prop_edit), button);
1077       gtk_widget_show (label);
1078       gtk_widget_show (button);
1079 
1080       g_object_connect_property (object, spec,
1081                                  G_CALLBACK (object_changed),
1082                                  prop_edit, G_OBJECT (label));
1083     }
1084   else if (type == G_TYPE_PARAM_BOXED &&
1085            G_PARAM_SPEC_VALUE_TYPE (spec) == GDK_TYPE_RGBA)
1086     {
1087       prop_edit = gtk_color_chooser_widget_new ();
1088       gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (prop_edit), TRUE);
1089 
1090       g_object_connect_property (object, spec,
1091                                  G_CALLBACK (rgba_changed),
1092                                  prop_edit, G_OBJECT (prop_edit));
1093 
1094       connect_controller (G_OBJECT (prop_edit), "notify::rgba",
1095                           object, spec, G_CALLBACK (rgba_modified));
1096     }
1097   else if (type == G_TYPE_PARAM_BOXED &&
1098            G_PARAM_SPEC_VALUE_TYPE (spec) == g_type_from_name ("GdkColor"))
1099     {
1100       prop_edit = gtk_color_chooser_widget_new ();
1101       gtk_color_chooser_set_use_alpha (GTK_COLOR_CHOOSER (prop_edit), FALSE);
1102 
1103       g_object_connect_property (object, spec,
1104                                  G_CALLBACK (color_changed),
1105                                  prop_edit, G_OBJECT (prop_edit));
1106 
1107       connect_controller (G_OBJECT (prop_edit), "notify::rgba",
1108                           object, spec, G_CALLBACK (color_modified));
1109     }
1110   else if (type == G_TYPE_PARAM_BOXED &&
1111            G_PARAM_SPEC_VALUE_TYPE (spec) == PANGO_TYPE_FONT_DESCRIPTION)
1112     {
1113       prop_edit = gtk_font_chooser_widget_new ();
1114 
1115       g_object_connect_property (object, spec,
1116                                  G_CALLBACK (font_changed),
1117                                  prop_edit, G_OBJECT (prop_edit));
1118 
1119       connect_controller (G_OBJECT (prop_edit), "notify::font-desc",
1120                           object, spec, G_CALLBACK (font_modified));
1121     }
1122   else if (type == G_TYPE_PARAM_BOXED &&
1123            G_PARAM_SPEC_VALUE_TYPE (spec) == G_TYPE_STRV)
1124     {
1125       prop_edit = g_object_new (gtk_inspector_strv_editor_get_type (),
1126                                 "visible", TRUE,
1127                                 NULL);
1128 
1129       g_object_connect_property (object, spec,
1130                                  G_CALLBACK (strv_changed),
1131                                  prop_edit, G_OBJECT (prop_edit));
1132 
1133       connect_controller (G_OBJECT (prop_edit), "changed",
1134                           object, spec, G_CALLBACK (strv_modified));
1135 
1136       gtk_widget_set_halign (prop_edit, GTK_ALIGN_START);
1137       gtk_widget_set_valign (prop_edit, GTK_ALIGN_CENTER);
1138     }
1139   else
1140     {
1141       msg = g_strdup_printf (_("Uneditable property type: %s"),
1142                              g_type_name (G_PARAM_SPEC_TYPE (spec)));
1143       prop_edit = gtk_label_new (msg);
1144       g_free (msg);
1145       gtk_widget_set_halign (prop_edit, GTK_ALIGN_START);
1146       gtk_widget_set_valign (prop_edit, GTK_ALIGN_CENTER);
1147     }
1148 
1149   if (g_param_spec_get_blurb (spec))
1150     gtk_widget_set_tooltip_text (prop_edit, g_param_spec_get_blurb (spec));
1151 
1152   notify_property (object, spec);
1153 
1154   return prop_edit;
1155 }
1156 
1157 static void
gtk_inspector_prop_editor_init(GtkInspectorPropEditor * editor)1158 gtk_inspector_prop_editor_init (GtkInspectorPropEditor *editor)
1159 {
1160   editor->priv = gtk_inspector_prop_editor_get_instance_private (editor);
1161   g_object_set (editor,
1162                 "orientation", GTK_ORIENTATION_VERTICAL,
1163                 "spacing", 10,
1164                 "margin", 10,
1165                 NULL);
1166 }
1167 
1168 static GtkTreeModel *
gtk_cell_layout_get_model(GtkCellLayout * layout)1169 gtk_cell_layout_get_model (GtkCellLayout *layout)
1170 {
1171   if (GTK_IS_TREE_VIEW_COLUMN (layout))
1172     return gtk_tree_view_get_model (GTK_TREE_VIEW (gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (layout))));
1173   else if (GTK_IS_ICON_VIEW (layout))
1174     return gtk_icon_view_get_model (GTK_ICON_VIEW (layout));
1175   else if (GTK_IS_COMBO_BOX (layout))
1176     return gtk_combo_box_get_model (GTK_COMBO_BOX (layout));
1177   else
1178     return NULL;
1179 }
1180 
1181 static GtkWidget *
gtk_cell_layout_get_widget(GtkCellLayout * layout)1182 gtk_cell_layout_get_widget (GtkCellLayout *layout)
1183 {
1184   if (GTK_IS_TREE_VIEW_COLUMN (layout))
1185     return gtk_tree_view_column_get_tree_view (GTK_TREE_VIEW_COLUMN (layout));
1186   else if (GTK_IS_WIDGET (layout))
1187     return GTK_WIDGET (layout);
1188   else
1189     return NULL;
1190 }
1191 
1192 static void
model_properties(GtkButton * button,GtkInspectorPropEditor * editor)1193 model_properties (GtkButton              *button,
1194                   GtkInspectorPropEditor *editor)
1195 {
1196   GObject *model;
1197 
1198   model = g_object_get_data (G_OBJECT (button), "model");
1199   g_signal_emit (editor, signals[SHOW_OBJECT], 0, model, "model", "data");
1200 }
1201 
1202 static void
attribute_mapping_changed(GtkComboBox * combo,GtkInspectorPropEditor * editor)1203 attribute_mapping_changed (GtkComboBox            *combo,
1204                            GtkInspectorPropEditor *editor)
1205 {
1206   gint col;
1207   gpointer layout;
1208   GtkCellRenderer *cell;
1209   GtkCellArea *area;
1210 
1211   col = gtk_combo_box_get_active (combo) - 1;
1212   layout = g_object_get_data (editor->priv->object, "gtk-inspector-cell-layout");
1213   if (GTK_IS_CELL_LAYOUT (layout))
1214     {
1215       cell = GTK_CELL_RENDERER (editor->priv->object);
1216       area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (layout));
1217       gtk_cell_area_attribute_disconnect (area, cell, editor->priv->name);
1218       if (col != -1)
1219         gtk_cell_area_attribute_connect (area, cell, editor->priv->name, col);
1220       gtk_widget_set_sensitive (editor->priv->editor, col == -1);
1221       notify_property (editor->priv->object, find_property (editor));
1222       gtk_widget_queue_draw (gtk_cell_layout_get_widget (GTK_CELL_LAYOUT (layout)));
1223     }
1224 }
1225 
1226 static GtkWidget *
attribute_editor(GObject * object,GParamSpec * spec,GtkInspectorPropEditor * editor)1227 attribute_editor (GObject                *object,
1228                   GParamSpec             *spec,
1229                   GtkInspectorPropEditor *editor)
1230 {
1231   gpointer layout;
1232   GtkCellArea *area;
1233   GtkTreeModel *model = NULL;
1234   gint col = -1;
1235   GtkWidget *label;
1236   GtkWidget *button;
1237   GtkWidget *vbox;
1238   GtkWidget *box;
1239   GtkWidget *combo;
1240   gchar *text;
1241   gint i;
1242   gboolean sensitive;
1243   GtkCellRenderer *renderer;
1244   GtkListStore *store;
1245   GtkTreeIter iter;
1246 
1247   layout = g_object_get_data (object, "gtk-inspector-cell-layout");
1248   if (GTK_IS_CELL_LAYOUT (layout))
1249     {
1250       area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (layout));
1251       col = gtk_cell_area_attribute_get_column (area,
1252                                                 GTK_CELL_RENDERER (object),
1253                                                 editor->priv->name);
1254       model = gtk_cell_layout_get_model (GTK_CELL_LAYOUT (layout));
1255     }
1256 
1257   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1258 
1259   label = gtk_label_new (_("Attribute mapping"));
1260   gtk_widget_set_margin_top (label, 10);
1261   gtk_container_add (GTK_CONTAINER (vbox), label);
1262 
1263   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
1264   gtk_container_add (GTK_CONTAINER (box), gtk_label_new (_("Model:")));
1265   text = g_strdup_printf (_("%p (%s)"), model, model ? g_type_name (G_TYPE_FROM_INSTANCE (model)) : "null" );
1266   gtk_container_add (GTK_CONTAINER (box), gtk_label_new (text));
1267   g_free (text);
1268   button = gtk_button_new_with_label (_("Properties"));
1269   g_object_set_data (G_OBJECT (button), "model", model);
1270   g_signal_connect (button, "clicked", G_CALLBACK (model_properties), editor);
1271   gtk_container_add (GTK_CONTAINER (box), button);
1272   gtk_container_add (GTK_CONTAINER (vbox), box);
1273 
1274   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
1275   gtk_container_add (GTK_CONTAINER (box), gtk_label_new (_("Column:")));
1276   store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_BOOLEAN);
1277   combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
1278   renderer = gtk_cell_renderer_text_new ();
1279   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
1280   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
1281                                   "text", 0,
1282                                   "sensitive", 1,
1283                                   NULL);
1284   gtk_list_store_append (store, &iter);
1285   gtk_list_store_set (store, &iter, 0, C_("property name", "None"), 1, TRUE, -1);
1286   for (i = 0; i < gtk_tree_model_get_n_columns (model); i++)
1287     {
1288       text = g_strdup_printf ("%d", i);
1289       sensitive = g_value_type_transformable (gtk_tree_model_get_column_type (model, i),
1290                                               spec->value_type);
1291       gtk_list_store_append (store, &iter);
1292       gtk_list_store_set (store, &iter, 0, text, 1, sensitive, -1);
1293       g_free (text);
1294     }
1295   gtk_combo_box_set_active (GTK_COMBO_BOX (combo), col + 1);
1296   attribute_mapping_changed (GTK_COMBO_BOX (combo), editor);
1297   g_signal_connect (combo, "changed",
1298                     G_CALLBACK (attribute_mapping_changed), editor);
1299   gtk_container_add (GTK_CONTAINER (box), combo);
1300   gtk_container_add (GTK_CONTAINER (vbox), box);
1301   gtk_widget_show_all (vbox);
1302 
1303   return vbox;
1304 }
1305 
1306 static GtkWidget *
action_ancestor(GtkWidget * widget)1307 action_ancestor (GtkWidget *widget)
1308 {
1309   if (GTK_IS_MENU (widget))
1310     return gtk_menu_get_attach_widget (GTK_MENU (widget));
1311   else if (GTK_IS_POPOVER (widget))
1312     return gtk_popover_get_relative_to (GTK_POPOVER (widget));
1313   else
1314     return gtk_widget_get_parent (widget);
1315 }
1316 
1317 static GObject *
find_action_owner(GtkActionable * actionable)1318 find_action_owner (GtkActionable *actionable)
1319 {
1320   GtkWidget *widget = GTK_WIDGET (actionable);
1321   const gchar *full_name;
1322   const gchar *dot;
1323   const gchar *name;
1324   gchar *prefix;
1325   GtkWidget *win;
1326   GActionGroup *group;
1327 
1328   full_name = gtk_actionable_get_action_name (actionable);
1329   if (!full_name)
1330     return NULL;
1331 
1332   dot = strchr (full_name, '.');
1333   prefix = g_strndup (full_name, dot - full_name);
1334   name = dot + 1;
1335 
1336   win = gtk_widget_get_ancestor (widget, GTK_TYPE_APPLICATION_WINDOW);
1337   if (g_strcmp0 (prefix, "win") == 0)
1338     {
1339       if (G_IS_OBJECT (win))
1340         return (GObject *)win;
1341     }
1342   else if (g_strcmp0 (prefix, "app") == 0)
1343     {
1344       if (GTK_IS_WINDOW (win))
1345         return (GObject *)gtk_window_get_application (GTK_WINDOW (win));
1346     }
1347 
1348   while (widget != NULL)
1349     {
1350       group = gtk_widget_get_action_group (widget, prefix);
1351       if (group && g_action_group_has_action (group, name))
1352         return (GObject *)widget;
1353       widget = action_ancestor (widget);
1354     }
1355 
1356   return NULL;
1357 }
1358 
1359 static void
show_action_owner(GtkButton * button,GtkInspectorPropEditor * editor)1360 show_action_owner (GtkButton              *button,
1361                    GtkInspectorPropEditor *editor)
1362 {
1363   GObject *owner;
1364 
1365   owner = g_object_get_data (G_OBJECT (button), "owner");
1366   g_signal_emit (editor, signals[SHOW_OBJECT], 0, owner, NULL, "actions");
1367 }
1368 
1369 static GtkWidget *
action_editor(GObject * object,GtkInspectorPropEditor * editor)1370 action_editor (GObject                *object,
1371                GtkInspectorPropEditor *editor)
1372 {
1373   GtkWidget *vbox;
1374   GtkWidget *label;
1375   GtkWidget *box;
1376   GtkWidget *button;
1377   GObject *owner;
1378   gchar *text;
1379 
1380   owner = find_action_owner (GTK_ACTIONABLE (object));
1381 
1382   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1383   if (owner)
1384     {
1385       label = gtk_label_new (_("Action"));
1386       gtk_widget_set_margin_top (label, 10);
1387       gtk_container_add (GTK_CONTAINER (vbox), label);
1388       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
1389       text = g_strdup_printf (_("Defined at: %p (%s)"),
1390                               owner, g_type_name_from_instance ((GTypeInstance *)owner));
1391       gtk_container_add (GTK_CONTAINER (box), gtk_label_new (text));
1392       g_free (text);
1393       button = gtk_button_new_with_label (_("Properties"));
1394       g_object_set_data (G_OBJECT (button), "owner", owner);
1395       g_signal_connect (button, "clicked",
1396                         G_CALLBACK (show_action_owner), editor);
1397       gtk_container_add (GTK_CONTAINER (box), button);
1398       gtk_container_add (GTK_CONTAINER (vbox), box);
1399       gtk_widget_show_all (vbox);
1400     }
1401 
1402   return vbox;
1403 }
1404 
1405 static void
binding_object_properties(GtkButton * button,GtkInspectorPropEditor * editor)1406 binding_object_properties (GtkButton *button, GtkInspectorPropEditor *editor)
1407 {
1408   GObject *obj;
1409 
1410   obj = (GObject *)g_object_get_data (G_OBJECT (button), "object");
1411   if (G_IS_OBJECT (obj))
1412     g_signal_emit (editor, signals[SHOW_OBJECT], 0, obj, NULL, "properties");
1413 }
1414 
1415 static void
add_binding_info(GtkInspectorPropEditor * editor)1416 add_binding_info (GtkInspectorPropEditor *editor)
1417 {
1418   GObject *object;
1419   const gchar *name;
1420   GHashTable *bindings;
1421   GHashTableIter iter;
1422   GBinding *binding;
1423   GtkWidget *row;
1424   GtkWidget *button;
1425   gchar *str;
1426   GObject *other;
1427   const gchar *property;
1428   const gchar *direction;
1429   const gchar *tip;
1430   GtkWidget *label;
1431 
1432   object = editor->priv->object;
1433   name = editor->priv->name;
1434 
1435   /* Note: this is accessing private GBinding details, so keep it
1436    * in sync with the implementation in GObject
1437    */
1438   bindings = (GHashTable *)g_object_get_data (G_OBJECT (object), "g-binding");
1439   if (!bindings)
1440     return;
1441 
1442   g_hash_table_iter_init (&iter, bindings);
1443   while (g_hash_table_iter_next (&iter, (gpointer*)&binding, NULL))
1444     {
1445       if (g_binding_get_source (binding) == object &&
1446           g_str_equal (g_binding_get_source_property (binding), name))
1447         {
1448           other = g_binding_get_target (binding);
1449           property = g_binding_get_target_property (binding);
1450           if (g_binding_get_flags (binding) & G_BINDING_INVERT_BOOLEAN)
1451             {
1452               direction = "↛";
1453               tip = _("inverted");
1454             }
1455           else
1456             {
1457               direction = "→";
1458               tip = NULL;
1459             }
1460         }
1461       else if (g_binding_get_target (binding) == object &&
1462                g_str_equal (g_binding_get_target_property (binding), name))
1463         {
1464           other = g_binding_get_source (binding);
1465           property = g_binding_get_source_property (binding);
1466           if (g_binding_get_flags (binding) & G_BINDING_INVERT_BOOLEAN)
1467             {
1468               direction = "↚";
1469               tip = _("inverted");
1470             }
1471           else
1472             {
1473               direction = "←";
1474               tip = NULL;
1475             }
1476         }
1477       else
1478         continue;
1479 
1480       if (g_binding_get_flags (binding) & G_BINDING_BIDIRECTIONAL)
1481         {
1482           if (g_binding_get_flags (binding) & G_BINDING_INVERT_BOOLEAN)
1483             {
1484               direction = "↮";
1485               tip = _("bidirectional, inverted");
1486             }
1487           else
1488             {
1489               direction = "↔";
1490               tip = _("bidirectional");
1491             }
1492         }
1493 
1494       row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
1495       gtk_container_add (GTK_CONTAINER (row), gtk_label_new (_("Binding:")));
1496       label = gtk_label_new (direction);
1497       if (tip)
1498         gtk_widget_set_tooltip_text (label, tip);
1499       gtk_container_add (GTK_CONTAINER (row), label);
1500       str = g_strdup_printf ("%p :: %s", other, property);
1501       label = gtk_label_new (str);
1502       gtk_container_add (GTK_CONTAINER (row), label);
1503       g_free (str);
1504       button = gtk_button_new_with_label (_("Properties"));
1505       g_object_set_data (G_OBJECT (button), "object", other);
1506       g_signal_connect (button, "clicked",
1507                         G_CALLBACK (binding_object_properties), editor);
1508       gtk_container_add (GTK_CONTAINER (row), button);
1509 
1510        gtk_widget_show_all (row);
1511        gtk_container_add (GTK_CONTAINER (editor), row);
1512     }
1513 }
1514 
1515 /* Note: Slightly nasty that we have to poke at the
1516  * GSettingsSchemaKey internals here. Keep this in
1517  * sync with the implementation in GIO!
1518  */
1519 struct _GSettingsSchemaKey
1520 {
1521   GSettingsSchema *schema;
1522   const gchar *name;
1523 
1524   guint is_flags : 1;
1525   guint is_enum  : 1;
1526 
1527   const guint32 *strinfo;
1528   gsize strinfo_length;
1529 
1530   const gchar *unparsed;
1531   gchar lc_char;
1532 
1533   const GVariantType *type;
1534   GVariant *minimum, *maximum;
1535   GVariant *default_value;
1536 
1537   gint ref_count;
1538 };
1539 
1540 typedef struct
1541 {
1542   GSettingsSchemaKey key;
1543   GSettings *settings;
1544   GObject *object;
1545 
1546   GSettingsBindGetMapping get_mapping;
1547   GSettingsBindSetMapping set_mapping;
1548   gpointer user_data;
1549   GDestroyNotify destroy;
1550 
1551   guint writable_handler_id;
1552   guint property_handler_id;
1553   const GParamSpec *property;
1554   guint key_handler_id;
1555 
1556   /* prevent recursion */
1557   gboolean running;
1558 } GSettingsBinding;
1559 
1560 static void
add_attribute_info(GtkInspectorPropEditor * editor,GParamSpec * spec)1561 add_attribute_info (GtkInspectorPropEditor *editor,
1562                     GParamSpec             *spec)
1563 {
1564   if (GTK_IS_CELL_RENDERER (editor->priv->object))
1565     gtk_container_add (GTK_CONTAINER (editor),
1566                        attribute_editor (editor->priv->object, spec, editor));
1567 }
1568 
1569 static void
add_actionable_info(GtkInspectorPropEditor * editor)1570 add_actionable_info (GtkInspectorPropEditor *editor)
1571 {
1572   if (GTK_IS_ACTIONABLE (editor->priv->object) &&
1573       g_strcmp0 (editor->priv->name, "action-name") == 0)
1574     gtk_container_add (GTK_CONTAINER (editor),
1575                        action_editor (editor->priv->object, editor));
1576 }
1577 
1578 static void
add_settings_info(GtkInspectorPropEditor * editor)1579 add_settings_info (GtkInspectorPropEditor *editor)
1580 {
1581   gchar *key;
1582   GSettingsBinding *binding;
1583   GObject *object;
1584   const gchar *name;
1585   const gchar *direction;
1586   const gchar *tip;
1587   GtkWidget *row;
1588   GtkWidget *label;
1589   gchar *str;
1590 
1591   object = editor->priv->object;
1592   name = editor->priv->name;
1593 
1594   key = g_strconcat ("gsettingsbinding-", name, NULL);
1595   binding = (GSettingsBinding *)g_object_get_data (object, key);
1596   g_free (key);
1597 
1598   if (!binding)
1599     return;
1600 
1601   if (binding->key_handler_id && binding->property_handler_id)
1602     {
1603       direction = "↔";
1604       tip = _("bidirectional");
1605     }
1606   else if (binding->key_handler_id)
1607     {
1608       direction = "←";
1609       tip = NULL;
1610     }
1611   else if (binding->property_handler_id)
1612     {
1613       direction = "→";
1614       tip = NULL;
1615     }
1616   else
1617     {
1618       direction = "?";
1619       tip = NULL;
1620     }
1621 
1622   row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
1623   gtk_container_add (GTK_CONTAINER (row), gtk_label_new (_("Setting:")));
1624   label = gtk_label_new (direction);
1625   if (tip)
1626     gtk_widget_set_tooltip_text (label, tip);
1627   gtk_container_add (GTK_CONTAINER (row), label);
1628 
1629   str = g_strdup_printf ("%s %s",
1630                          g_settings_schema_get_id (binding->key.schema),
1631                          binding->key.name);
1632   label = gtk_label_new (str);
1633   gtk_container_add (GTK_CONTAINER (row), label);
1634   g_free (str);
1635 
1636   gtk_widget_show_all (row);
1637   gtk_container_add (GTK_CONTAINER (editor), row);
1638 }
1639 
1640 static void
reset_setting(GtkInspectorPropEditor * editor)1641 reset_setting (GtkInspectorPropEditor *editor)
1642 {
1643   gtk_settings_reset_property (GTK_SETTINGS (editor->priv->object),
1644                                editor->priv->name);
1645 }
1646 
1647 static void
add_gtk_settings_info(GtkInspectorPropEditor * editor)1648 add_gtk_settings_info (GtkInspectorPropEditor *editor)
1649 {
1650   GObject *object;
1651   const gchar *name;
1652   GtkWidget *row;
1653   const gchar *source;
1654   GtkWidget *button;
1655 
1656   object = editor->priv->object;
1657   name = editor->priv->name;
1658 
1659   if (!GTK_IS_SETTINGS (object))
1660     return;
1661 
1662   row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
1663   gtk_container_add (GTK_CONTAINER (row), gtk_label_new (_("Source:")));
1664 
1665   button = gtk_button_new_with_label (_("Reset"));
1666   g_signal_connect_swapped (button, "clicked", G_CALLBACK (reset_setting), editor);
1667 
1668   gtk_widget_set_halign (button, GTK_ALIGN_END);
1669   gtk_widget_show (button);
1670   gtk_widget_set_sensitive (button, FALSE);
1671   gtk_box_pack_end (GTK_BOX (row), button, FALSE, FALSE, 0);
1672 
1673   switch (_gtk_settings_get_setting_source (GTK_SETTINGS (object), name))
1674     {
1675     case GTK_SETTINGS_SOURCE_DEFAULT:
1676       source = _("Default");
1677       break;
1678     case GTK_SETTINGS_SOURCE_THEME:
1679       source = _("Theme");
1680       break;
1681     case GTK_SETTINGS_SOURCE_XSETTING:
1682       source = _("XSettings");
1683       break;
1684     case GTK_SETTINGS_SOURCE_APPLICATION:
1685       gtk_widget_set_sensitive (button, TRUE);
1686       source = _("Application");
1687       break;
1688     default:
1689       source = _("Unknown");
1690       break;
1691     }
1692   gtk_container_add (GTK_CONTAINER (row), gtk_label_new (source));
1693 
1694   gtk_widget_show_all (row);
1695   gtk_container_add (GTK_CONTAINER (editor), row);
1696 }
1697 
1698 static void
constructed(GObject * object)1699 constructed (GObject *object)
1700 {
1701   GtkInspectorPropEditor *editor = GTK_INSPECTOR_PROP_EDITOR (object);
1702   GParamSpec *spec;
1703   GtkWidget *label;
1704   gboolean can_modify;
1705 
1706   spec = find_property (editor);
1707 
1708   label = gtk_label_new (g_param_spec_get_nick (spec));
1709   gtk_widget_show (label);
1710   gtk_container_add (GTK_CONTAINER (editor), label);
1711 
1712   can_modify = ((spec->flags & G_PARAM_WRITABLE) != 0 &&
1713                 (spec->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
1714 
1715   if ((spec->flags & G_PARAM_CONSTRUCT_ONLY) != 0)
1716     label = gtk_label_new ("(construct-only)");
1717   else if ((spec->flags & G_PARAM_WRITABLE) == 0)
1718     label = gtk_label_new ("(not writable)");
1719   else
1720     label = NULL;
1721 
1722   if (label)
1723     {
1724       gtk_widget_show (label);
1725       gtk_style_context_add_class (gtk_widget_get_style_context (label), GTK_STYLE_CLASS_DIM_LABEL);
1726       gtk_container_add (GTK_CONTAINER (editor), label);
1727     }
1728 
1729   /* By reaching this, we already know the property is readable.
1730    * Since all we can do for a GObject is dive down into it's properties
1731    * and inspect bindings and such, pretend to be mutable.
1732    */
1733   if (g_type_is_a (spec->value_type, G_TYPE_OBJECT))
1734     can_modify = TRUE;
1735 
1736   if (!can_modify)
1737     return;
1738 
1739   editor->priv->editor = property_editor (editor->priv->object, spec, editor);
1740   gtk_widget_show (editor->priv->editor);
1741   gtk_container_add (GTK_CONTAINER (editor), editor->priv->editor);
1742 
1743   add_attribute_info (editor, spec);
1744   add_actionable_info (editor);
1745   add_binding_info (editor);
1746   add_settings_info (editor);
1747   add_gtk_settings_info (editor);
1748 }
1749 
1750 static void
finalize(GObject * object)1751 finalize (GObject *object)
1752 {
1753   GtkInspectorPropEditor *editor = GTK_INSPECTOR_PROP_EDITOR (object);
1754 
1755   g_free (editor->priv->name);
1756 
1757   G_OBJECT_CLASS (gtk_inspector_prop_editor_parent_class)->finalize (object);
1758 }
1759 
1760 static void
get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)1761 get_property (GObject    *object,
1762               guint       param_id,
1763               GValue     *value,
1764               GParamSpec *pspec)
1765 {
1766   GtkInspectorPropEditor *r = GTK_INSPECTOR_PROP_EDITOR (object);
1767 
1768   switch (param_id)
1769     {
1770     case PROP_OBJECT:
1771       g_value_set_object (value, r->priv->object);
1772       break;
1773 
1774     case PROP_NAME:
1775       g_value_set_string (value, r->priv->name);
1776       break;
1777 
1778     case PROP_IS_CHILD_PROPERTY:
1779       g_value_set_boolean (value, r->priv->is_child_property);
1780       break;
1781 
1782     default:
1783       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1784       break;
1785     }
1786 }
1787 
1788 static void
set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)1789 set_property (GObject      *object,
1790               guint         param_id,
1791               const GValue *value,
1792               GParamSpec   *pspec)
1793 {
1794   GtkInspectorPropEditor *r = GTK_INSPECTOR_PROP_EDITOR (object);
1795 
1796   switch (param_id)
1797     {
1798     case PROP_OBJECT:
1799       r->priv->object = g_value_get_object (value);
1800       break;
1801 
1802     case PROP_NAME:
1803       g_free (r->priv->name);
1804       r->priv->name = g_value_dup_string (value);
1805       break;
1806 
1807     case PROP_IS_CHILD_PROPERTY:
1808       r->priv->is_child_property = g_value_get_boolean (value);
1809       break;
1810 
1811     default:
1812       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
1813       break;
1814     }
1815 }
1816 
1817 static void
gtk_inspector_prop_editor_class_init(GtkInspectorPropEditorClass * klass)1818 gtk_inspector_prop_editor_class_init (GtkInspectorPropEditorClass *klass)
1819 {
1820   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1821 
1822   object_class->constructed = constructed;
1823   object_class->finalize = finalize;
1824   object_class->get_property = get_property;
1825   object_class->set_property = set_property;
1826 
1827   signals[SHOW_OBJECT] =
1828     g_signal_new ("show-object",
1829                   G_TYPE_FROM_CLASS (object_class),
1830                   G_SIGNAL_RUN_LAST,
1831                   G_STRUCT_OFFSET (GtkInspectorPropEditorClass, show_object),
1832                   NULL, NULL, NULL,
1833                   G_TYPE_NONE, 3, G_TYPE_OBJECT, G_TYPE_STRING, G_TYPE_STRING);
1834 
1835   g_object_class_install_property (object_class, PROP_OBJECT,
1836       g_param_spec_object ("object", "Object", "The object owning the property",
1837                            G_TYPE_OBJECT, G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
1838 
1839   g_object_class_install_property (object_class, PROP_NAME,
1840       g_param_spec_string ("name", "Name", "The property name",
1841                            NULL, G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
1842 
1843   g_object_class_install_property (object_class, PROP_IS_CHILD_PROPERTY,
1844       g_param_spec_boolean ("is-child-property", "Child property", "Whether this is a child property",
1845                             FALSE, G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
1846 }
1847 
1848 GtkWidget *
gtk_inspector_prop_editor_new(GObject * object,const gchar * name,gboolean is_child_property)1849 gtk_inspector_prop_editor_new (GObject     *object,
1850                                const gchar *name,
1851                                gboolean     is_child_property)
1852 {
1853   return g_object_new (GTK_TYPE_INSPECTOR_PROP_EDITOR,
1854                        "object", object,
1855                        "name", name,
1856                        "is-child-property", is_child_property,
1857                        NULL);
1858 }
1859 
1860 gboolean
gtk_inspector_prop_editor_should_expand(GtkInspectorPropEditor * editor)1861 gtk_inspector_prop_editor_should_expand (GtkInspectorPropEditor *editor)
1862 {
1863   if (GTK_IS_SCROLLED_WINDOW (editor->priv->editor))
1864     {
1865       GtkPolicyType policy;
1866 
1867       g_object_get (editor->priv->editor, "vscrollbar-policy", &policy, NULL);
1868       if (policy != GTK_POLICY_NEVER)
1869         return TRUE;
1870     }
1871 
1872   return FALSE;
1873 }
1874 
1875 
1876 // vim: set et:
1877