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