1 /*
2 * glade-gtk-widget.c - GladeWidgetAdaptor for GtkWidget
3 *
4 * Copyright (C) 2013 Tristan Van Berkom
5 *
6 * Authors:
7 * Tristan Van Berkom <tristan.van.berkom@gmail.com>
8 *
9 * This library is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1 of
12 * the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23 #include <config.h>
24 #include <glib/gi18n-lib.h>
25 #include <gladeui/glade.h>
26 #include <string.h>
27
28 #include "glade-widget-editor.h"
29 #include "glade-string-list.h"
30 #include "glade-accels.h"
31
32 #define GLADE_TAG_A11Y_A11Y "accessibility"
33 #define GLADE_TAG_A11Y_ACTION_NAME "action_name" /* We should make -/_ synonymous */
34 #define GLADE_TAG_A11Y_DESC "description"
35 #define GLADE_TAG_A11Y_TARGET "target"
36 #define GLADE_TAG_A11Y_TYPE "type"
37
38 #define GLADE_TAG_A11Y_INTERNAL_NAME "accessible"
39
40 #define GLADE_TAG_A11Y_RELATION "relation"
41 #define GLADE_TAG_A11Y_ACTION "action"
42 #define GLADE_TAG_A11Y_PROPERTY "property"
43
44 #define GLADE_TAG_STYLE "style"
45 #define GLADE_TAG_CLASS "class"
46
47
48 /* This function does absolutely nothing
49 * (and is for use in overriding some post_create functions
50 * throughout the catalog).
51 */
52 void
empty(GObject * container,GladeCreateReason reason)53 empty (GObject * container, GladeCreateReason reason)
54 {
55 }
56
57 static const gchar *atk_relations_list[] = {
58 "controlled-by",
59 "controller-for",
60 "labelled-by",
61 "label-for",
62 "member-of",
63 "node-child-of",
64 "flows-to",
65 "flows-from",
66 "subwindow-of",
67 "embeds",
68 "embedded-by",
69 "popup-for",
70 "parent-window-of",
71 "described-by",
72 "description-for",
73 NULL
74 };
75
76
77 void
glade_gtk_widget_destroy_object(GladeWidgetAdaptor * adaptor,GObject * object)78 glade_gtk_widget_destroy_object (GladeWidgetAdaptor * adaptor,
79 GObject *object)
80 {
81 gtk_widget_destroy (GTK_WIDGET (object));
82
83 GWA_GET_CLASS (G_TYPE_OBJECT)->destroy_object (adaptor, object);
84 }
85
86 static void
glade_gtk_parse_atk_relation(GladeProperty * property,GladeXmlNode * node)87 glade_gtk_parse_atk_relation (GladeProperty * property, GladeXmlNode * node)
88 {
89 GladeXmlNode *prop;
90 GladePropertyClass *pclass;
91 gchar *type, *target, *id, *tmp;
92 gchar *string = NULL;
93
94 for (prop = glade_xml_node_get_children (node);
95 prop; prop = glade_xml_node_next (prop))
96 {
97 if (!glade_xml_node_verify_silent (prop, GLADE_TAG_A11Y_RELATION))
98 continue;
99
100 if (!(type =
101 glade_xml_get_property_string_required
102 (prop, GLADE_TAG_A11Y_TYPE, NULL)))
103 continue;
104
105 if (!(target =
106 glade_xml_get_property_string_required
107 (prop, GLADE_TAG_A11Y_TARGET, NULL)))
108 {
109 g_free (type);
110 continue;
111 }
112
113 id = glade_util_read_prop_name (type);
114 pclass = glade_property_get_class (property);
115
116 if (!strcmp (id, glade_property_class_id (pclass)))
117 {
118 if (string == NULL)
119 string = g_strdup (target);
120 else
121 {
122 tmp = g_strdup_printf ("%s%s%s", string,
123 GPC_OBJECT_DELIMITER, target);
124 string = (g_free (string), tmp);
125 }
126
127 }
128
129 g_free (id);
130 g_free (type);
131 g_free (target);
132 }
133
134 /* we must synchronize this directly after loading this project
135 * (i.e. lookup the actual objects after they've been parsed and
136 * are present). this is a feature of object and object list properties
137 * that needs a better api.
138 */
139 if (string)
140 {
141 g_object_set_data_full (G_OBJECT (property), "glade-loaded-object",
142 /* 'string' here is already allocated on the heap */
143 string, g_free);
144 }
145 }
146
147 static void
glade_gtk_parse_atk_props(GladeWidget * widget,GladeXmlNode * node)148 glade_gtk_parse_atk_props (GladeWidget * widget, GladeXmlNode * node)
149 {
150 GladeXmlNode *prop;
151 GladeProperty *property;
152 GValue *gvalue;
153 gchar *value, *name, *id, *comment;
154 gint translatable;
155 gboolean is_action;
156
157 for (prop = glade_xml_node_get_children (node);
158 prop; prop = glade_xml_node_next (prop))
159 {
160 if (glade_xml_node_verify_silent (prop, GLADE_TAG_A11Y_PROPERTY))
161 is_action = FALSE;
162 else if (glade_xml_node_verify_silent (prop, GLADE_TAG_A11Y_ACTION))
163 is_action = TRUE;
164 else
165 continue;
166
167 if (!is_action &&
168 !(name = glade_xml_get_property_string_required
169 (prop, GLADE_XML_TAG_NAME, NULL)))
170 continue;
171 else if (is_action &&
172 !(name = glade_xml_get_property_string_required
173 (prop, GLADE_TAG_A11Y_ACTION_NAME, NULL)))
174 continue;
175
176
177 /* Make sure we are working with dashes and
178 * not underscores ...
179 */
180 id = glade_util_read_prop_name (name);
181 g_free (name);
182
183 /* We are namespacing the action properties internally
184 * just incase they clash (all property names must be
185 * unique...)
186 */
187 if (is_action)
188 {
189 name = g_strdup_printf ("atk-%s", id);
190 g_free (id);
191 id = name;
192 }
193
194 if ((property = glade_widget_get_property (widget, id)) != NULL)
195 {
196 /* Complex statement just getting the value here... */
197 if ((!is_action &&
198 !(value = glade_xml_get_content (prop))) ||
199 (is_action &&
200 !(value = glade_xml_get_property_string_required
201 (prop, GLADE_TAG_A11Y_DESC, NULL))))
202 {
203 /* XXX should be a glade_xml_get_content_required()... */
204 g_free (id);
205 continue;
206 }
207
208 /* Set the parsed value on the property ... */
209 gvalue = glade_property_class_make_gvalue_from_string
210 (glade_property_get_class (property), value, glade_widget_get_project (widget));
211 glade_property_set_value (property, gvalue);
212 g_value_unset (gvalue);
213 g_free (gvalue);
214
215 /* Deal with i18n... ... XXX Do i18n context !!! */
216 translatable = glade_xml_get_property_boolean
217 (prop, GLADE_TAG_TRANSLATABLE, FALSE);
218 comment = glade_xml_get_property_string (prop, GLADE_TAG_COMMENT);
219
220 glade_property_i18n_set_translatable (property, translatable);
221 glade_property_i18n_set_comment (property, comment);
222
223 g_free (comment);
224 g_free (value);
225 }
226
227 g_free (id);
228 }
229 }
230
231 static void
glade_gtk_parse_atk_props_gtkbuilder(GladeWidget * widget,GladeXmlNode * node)232 glade_gtk_parse_atk_props_gtkbuilder (GladeWidget * widget, GladeXmlNode * node)
233 {
234 GladeXmlNode *child, *object_node;
235 gchar *internal;
236
237 /* Search for internal "accessible" child and redirect parse from there */
238 for (child = glade_xml_node_get_children (node);
239 child; child = glade_xml_node_next (child))
240 {
241 if (glade_xml_node_verify_silent (child, GLADE_XML_TAG_CHILD))
242 {
243 if ((internal =
244 glade_xml_get_property_string (child,
245 GLADE_XML_TAG_INTERNAL_CHILD)))
246 {
247 if (!strcmp (internal, GLADE_TAG_A11Y_INTERNAL_NAME) &&
248 (object_node =
249 glade_xml_search_child_required (child,
250 GLADE_XML_TAG_WIDGET)))
251 glade_gtk_parse_atk_props (widget, object_node);
252
253 g_free (internal);
254 }
255 }
256 }
257 }
258
259 static void
glade_gtk_widget_read_atk_props(GladeWidget * widget,GladeXmlNode * node)260 glade_gtk_widget_read_atk_props (GladeWidget * widget, GladeXmlNode * node)
261 {
262 GladeXmlNode *atk_node;
263 GladeProperty *property;
264 gint i;
265
266 glade_gtk_parse_atk_props_gtkbuilder (widget, node);
267
268 if ((atk_node = glade_xml_search_child (node, GLADE_TAG_A11Y_A11Y)) != NULL)
269 {
270 /* Properties & actions */
271 glade_gtk_parse_atk_props (widget, atk_node);
272
273 /* Relations */
274 for (i = 0; atk_relations_list[i]; i++)
275 {
276 if ((property =
277 glade_widget_get_property (widget, atk_relations_list[i])))
278 glade_gtk_parse_atk_relation (property, atk_node);
279 else
280 g_warning ("Couldnt find atk relation %s", atk_relations_list[i]);
281 }
282 }
283 }
284
285 static void
glade_gtk_widget_read_style_classes(GladeWidget * widget,GladeXmlNode * node)286 glade_gtk_widget_read_style_classes (GladeWidget * widget, GladeXmlNode * node)
287 {
288 GladeXmlNode *style_node;
289 GladeXmlNode *class_node;
290 GList *string_list = NULL;
291
292 if ((style_node = glade_xml_search_child (node, GLADE_TAG_STYLE)) != NULL)
293 {
294 for (class_node = glade_xml_node_get_children (style_node);
295 class_node; class_node = glade_xml_node_next (class_node))
296 {
297 gchar *name;
298
299 if (!glade_xml_node_verify (class_node, GLADE_TAG_CLASS))
300 continue;
301
302 name = glade_xml_get_property_string (class_node, GLADE_TAG_NAME);
303
304 string_list = glade_string_list_append (string_list, name, NULL, NULL, FALSE, NULL);
305
306 g_free (name);
307 }
308
309 glade_widget_property_set (widget, "glade-style-classes", string_list);
310 glade_string_list_free (string_list);
311 }
312 }
313
314 void
glade_gtk_widget_read_widget(GladeWidgetAdaptor * adaptor,GladeWidget * widget,GladeXmlNode * node)315 glade_gtk_widget_read_widget (GladeWidgetAdaptor * adaptor,
316 GladeWidget * widget, GladeXmlNode * node)
317 {
318 const gchar *tooltip_markup = NULL;
319
320 if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) ||
321 glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE)))
322 return;
323
324 /* First chain up and read in all the normal properties.. */
325 GWA_GET_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);
326
327 /* Read in accelerators */
328 glade_gtk_read_accels (widget, node, TRUE);
329
330 /* Read in atk props */
331 glade_gtk_widget_read_atk_props (widget, node);
332
333 /* Read in the style classes */
334 glade_gtk_widget_read_style_classes (widget, node);
335
336 /* Resolve the virtual tooltip use markup property */
337 glade_widget_property_get (widget, "tooltip-markup", &tooltip_markup);
338 if (tooltip_markup != NULL)
339 glade_widget_property_set (widget, "glade-tooltip-markup", TRUE);
340 }
341
342 static void
glade_gtk_widget_write_atk_property(GladeProperty * property,GladeXmlContext * context,GladeXmlNode * node)343 glade_gtk_widget_write_atk_property (GladeProperty * property,
344 GladeXmlContext * context,
345 GladeXmlNode * node)
346 {
347 gchar *value = glade_property_make_string (property);
348
349 if (value && value[0])
350 {
351 GladePropertyClass *pclass = glade_property_get_class (property);
352 GladeXmlNode *prop_node = glade_xml_node_new (context, GLADE_TAG_A11Y_PROPERTY);
353
354 glade_xml_node_append_child (node, prop_node);
355
356 glade_xml_node_set_property_string (prop_node,
357 GLADE_TAG_NAME, glade_property_class_id (pclass));
358
359 glade_xml_set_content (prop_node, value);
360
361 if (glade_property_i18n_get_translatable (property))
362 glade_xml_node_set_property_string (prop_node,
363 GLADE_TAG_TRANSLATABLE,
364 GLADE_XML_TAG_I18N_TRUE);
365
366 if (glade_property_i18n_get_comment (property))
367 glade_xml_node_set_property_string (prop_node,
368 GLADE_TAG_COMMENT,
369 glade_property_i18n_get_comment (property));
370
371 if (glade_property_i18n_get_context (property))
372 glade_xml_node_set_property_string (prop_node,
373 GLADE_TAG_CONTEXT,
374 glade_property_i18n_get_context (property));
375 }
376
377 g_free (value);
378 }
379
380 static void
glade_gtk_widget_write_atk_properties(GladeWidget * widget,GladeXmlContext * context,GladeXmlNode * node)381 glade_gtk_widget_write_atk_properties (GladeWidget * widget,
382 GladeXmlContext * context,
383 GladeXmlNode * node)
384 {
385 GladeXmlNode *child_node, *object_node;
386 GladeProperty *name_prop, *desc_prop, *role_prop;
387
388 name_prop = glade_widget_get_property (widget, "AtkObject::accessible-name");
389 desc_prop =
390 glade_widget_get_property (widget, "AtkObject::accessible-description");
391 role_prop = glade_widget_get_property (widget, "AtkObject::accessible-role");
392
393 /* Create internal child here if any of these properties are non-null */
394 if (!glade_property_default (name_prop) ||
395 !glade_property_default (desc_prop) ||
396 !glade_property_default (role_prop))
397 {
398 const gchar *widget_name = glade_widget_get_name (widget);
399 gchar *atkname = NULL;
400
401 if (!g_str_has_prefix (widget_name, GLADE_UNNAMED_PREFIX))
402 atkname = g_strdup_printf ("%s-atkobject", widget_name);
403
404 child_node = glade_xml_node_new (context, GLADE_XML_TAG_CHILD);
405 glade_xml_node_append_child (node, child_node);
406
407 glade_xml_node_set_property_string (child_node,
408 GLADE_XML_TAG_INTERNAL_CHILD,
409 GLADE_TAG_A11Y_INTERNAL_NAME);
410
411 object_node = glade_xml_node_new (context, GLADE_XML_TAG_WIDGET);
412 glade_xml_node_append_child (child_node, object_node);
413
414 glade_xml_node_set_property_string (object_node,
415 GLADE_XML_TAG_CLASS, "AtkObject");
416
417 if (atkname)
418 glade_xml_node_set_property_string (object_node,
419 GLADE_XML_TAG_ID, atkname);
420
421 if (!glade_property_default (name_prop))
422 glade_gtk_widget_write_atk_property (name_prop, context, object_node);
423 if (!glade_property_default (desc_prop))
424 glade_gtk_widget_write_atk_property (desc_prop, context, object_node);
425 if (!glade_property_default (role_prop))
426 glade_gtk_widget_write_atk_property (role_prop, context, object_node);
427
428 g_free (atkname);
429 }
430
431 }
432
433 static void
glade_gtk_widget_write_atk_relation(GladeProperty * property,GladeXmlContext * context,GladeXmlNode * node)434 glade_gtk_widget_write_atk_relation (GladeProperty * property,
435 GladeXmlContext * context,
436 GladeXmlNode * node)
437 {
438 GladeXmlNode *prop_node;
439 GladePropertyClass *pclass;
440 gchar *value, **split;
441 gint i;
442
443 if ((value = glade_widget_adaptor_string_from_value
444 (glade_property_class_get_adaptor (glade_property_get_class (property)),
445 glade_property_get_class (property), glade_property_inline_value (property))) != NULL)
446 {
447 if ((split = g_strsplit (value, GPC_OBJECT_DELIMITER, 0)) != NULL)
448 {
449 for (i = 0; split[i] != NULL; i++)
450 {
451 pclass = glade_property_get_class (property);
452
453 prop_node = glade_xml_node_new (context, GLADE_TAG_A11Y_RELATION);
454 glade_xml_node_append_child (node, prop_node);
455
456 glade_xml_node_set_property_string (prop_node,
457 GLADE_TAG_A11Y_TYPE,
458 glade_property_class_id (pclass));
459 glade_xml_node_set_property_string (prop_node,
460 GLADE_TAG_A11Y_TARGET,
461 split[i]);
462 }
463 g_strfreev (split);
464 }
465 }
466 }
467
468 static void
glade_gtk_widget_write_atk_relations(GladeWidget * widget,GladeXmlContext * context,GladeXmlNode * node)469 glade_gtk_widget_write_atk_relations (GladeWidget * widget,
470 GladeXmlContext * context,
471 GladeXmlNode * node)
472 {
473 GladeProperty *property;
474 gint i;
475
476 for (i = 0; atk_relations_list[i]; i++)
477 {
478 if ((property =
479 glade_widget_get_property (widget, atk_relations_list[i])))
480 glade_gtk_widget_write_atk_relation (property, context, node);
481 else
482 g_warning ("Couldnt find atk relation %s on widget %s",
483 atk_relations_list[i], glade_widget_get_name (widget));
484 }
485 }
486
487 static void
glade_gtk_widget_write_atk_action(GladeProperty * property,GladeXmlContext * context,GladeXmlNode * node)488 glade_gtk_widget_write_atk_action (GladeProperty * property,
489 GladeXmlContext * context,
490 GladeXmlNode * node)
491 {
492 gchar *value = glade_property_make_string (property);
493
494 if (value && value[0])
495 {
496 GladePropertyClass *pclass = glade_property_get_class (property);
497 GladeXmlNode *prop_node = glade_xml_node_new (context, GLADE_TAG_A11Y_ACTION);
498 glade_xml_node_append_child (node, prop_node);
499
500 glade_xml_node_set_property_string (prop_node,
501 GLADE_TAG_A11Y_ACTION_NAME,
502 &glade_property_class_id (pclass)[4]);
503 glade_xml_node_set_property_string (prop_node,
504 GLADE_TAG_A11Y_DESC, value);
505 }
506
507 g_free (value);
508 }
509
510 static void
glade_gtk_widget_write_atk_actions(GladeWidget * widget,GladeXmlContext * context,GladeXmlNode * node)511 glade_gtk_widget_write_atk_actions (GladeWidget * widget,
512 GladeXmlContext * context,
513 GladeXmlNode * node)
514 {
515 GladeProperty *property;
516
517 if ((property = glade_widget_get_property (widget, "atk-click")) != NULL)
518 glade_gtk_widget_write_atk_action (property, context, node);
519 if ((property = glade_widget_get_property (widget, "atk-activate")) != NULL)
520 glade_gtk_widget_write_atk_action (property, context, node);
521 if ((property = glade_widget_get_property (widget, "atk-press")) != NULL)
522 glade_gtk_widget_write_atk_action (property, context, node);
523 if ((property = glade_widget_get_property (widget, "atk-release")) != NULL)
524 glade_gtk_widget_write_atk_action (property, context, node);
525 }
526
527 static void
glade_gtk_widget_write_atk_props(GladeWidget * widget,GladeXmlContext * context,GladeXmlNode * node)528 glade_gtk_widget_write_atk_props (GladeWidget * widget,
529 GladeXmlContext * context,
530 GladeXmlNode * node)
531 {
532 GladeXmlNode *atk_node;
533
534 atk_node = glade_xml_node_new (context, GLADE_TAG_A11Y_A11Y);
535
536 glade_gtk_widget_write_atk_relations (widget, context, atk_node);
537 glade_gtk_widget_write_atk_actions (widget, context, atk_node);
538
539 if (!glade_xml_node_get_children (atk_node))
540 glade_xml_node_delete (atk_node);
541 else
542 glade_xml_node_append_child (node, atk_node);
543
544 glade_gtk_widget_write_atk_properties (widget, context, node);
545 }
546
547
548 static void
glade_gtk_widget_write_style_classes(GladeWidget * widget,GladeXmlContext * context,GladeXmlNode * node)549 glade_gtk_widget_write_style_classes (GladeWidget * widget,
550 GladeXmlContext * context,
551 GladeXmlNode * node)
552 {
553 GladeXmlNode *class_node, *style_node;
554 GList *string_list = NULL, *l;
555 GladeString *string;
556
557 if (!glade_widget_property_get (widget, "glade-style-classes", &string_list) || !string_list)
558 return;
559
560 style_node = glade_xml_node_new (context, GLADE_TAG_STYLE);
561
562 for (l = string_list; l; l = l->next)
563 {
564 string = l->data;
565
566 class_node = glade_xml_node_new (context, GLADE_TAG_CLASS);
567 glade_xml_node_append_child (style_node, class_node);
568
569 glade_xml_node_set_property_string (class_node,
570 GLADE_TAG_NAME,
571 string->string);
572 }
573
574 if (!glade_xml_node_get_children (style_node))
575 glade_xml_node_delete (style_node);
576 else
577 glade_xml_node_append_child (node, style_node);
578 }
579
580 void
glade_gtk_widget_write_widget(GladeWidgetAdaptor * adaptor,GladeWidget * widget,GladeXmlContext * context,GladeXmlNode * node)581 glade_gtk_widget_write_widget (GladeWidgetAdaptor * adaptor,
582 GladeWidget * widget,
583 GladeXmlContext * context, GladeXmlNode * node)
584 {
585 GladeProperty *prop;
586
587 if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) ||
588 glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE)))
589 return;
590
591 /* Make sure use-action-appearance and related-action properties are
592 * ordered in a sane way and are only saved if there is an action */
593 prop = glade_widget_get_property (widget, "use-action-appearance");
594 if (prop && glade_property_get_enabled (prop))
595 glade_property_write (prop, context, node);
596
597 prop = glade_widget_get_property (widget, "related-action");
598 if (prop && glade_property_get_enabled (prop))
599 glade_property_write (prop, context, node);
600
601 /* Finally chain up and read in all the normal properties.. */
602 GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);
603 }
604
605 void
glade_gtk_widget_write_widget_after(GladeWidgetAdaptor * adaptor,GladeWidget * widget,GladeXmlContext * context,GladeXmlNode * node)606 glade_gtk_widget_write_widget_after (GladeWidgetAdaptor * adaptor,
607 GladeWidget * widget,
608 GladeXmlContext * context, GladeXmlNode * node)
609 {
610 /* The ATK properties are actually children */
611 glade_gtk_widget_write_atk_props (widget, context, node);
612
613 /* Put the accelerators and style classes after children */
614 glade_gtk_write_accels (widget, context, node, TRUE);
615 glade_gtk_widget_write_style_classes (widget, context, node);
616
617 /* Finally chain up and read in all the normal properties.. */
618 GWA_GET_CLASS (G_TYPE_OBJECT)->write_widget_after (adaptor, widget, context, node);
619 }
620
621 GladeEditorProperty *
glade_gtk_widget_create_eprop(GladeWidgetAdaptor * adaptor,GladePropertyClass * klass,gboolean use_command)622 glade_gtk_widget_create_eprop (GladeWidgetAdaptor * adaptor,
623 GladePropertyClass * klass, gboolean use_command)
624 {
625 GladeEditorProperty *eprop;
626 GParamSpec *pspec;
627
628 pspec = glade_property_class_get_pspec (klass);
629
630 /* chain up.. */
631 if (pspec->value_type == GLADE_TYPE_ACCEL_GLIST)
632 eprop = g_object_new (GLADE_TYPE_EPROP_ACCEL,
633 "property-class", klass,
634 "use-command", use_command, NULL);
635 else if (pspec->value_type == GLADE_TYPE_STRING_LIST)
636 eprop = glade_eprop_string_list_new (klass, use_command, FALSE, FALSE);
637 else
638 eprop = GWA_GET_CLASS
639 (G_TYPE_OBJECT)->create_eprop (adaptor, klass, use_command);
640
641 return eprop;
642 }
643
644 GladeEditable *
glade_gtk_widget_create_editable(GladeWidgetAdaptor * adaptor,GladeEditorPageType type)645 glade_gtk_widget_create_editable (GladeWidgetAdaptor * adaptor,
646 GladeEditorPageType type)
647 {
648 GladeEditable *editable;
649
650 /* Get base editable */
651 if (type == GLADE_PAGE_COMMON)
652 editable = (GladeEditable *)glade_widget_editor_new ();
653 else
654 editable = GWA_GET_CLASS (G_TYPE_OBJECT)->create_editable (adaptor, type);
655
656 return editable;
657 }
658
659 gchar *
glade_gtk_widget_string_from_value(GladeWidgetAdaptor * adaptor,GladePropertyClass * klass,const GValue * value)660 glade_gtk_widget_string_from_value (GladeWidgetAdaptor * adaptor,
661 GladePropertyClass * klass,
662 const GValue * value)
663 {
664 GParamSpec *pspec;
665
666 pspec = glade_property_class_get_pspec (klass);
667
668 if (pspec->value_type == GLADE_TYPE_ACCEL_GLIST)
669 return glade_accels_make_string (g_value_get_boxed (value));
670 else if (pspec->value_type == GLADE_TYPE_STRING_LIST)
671 {
672 GList *list = g_value_get_boxed (value);
673
674 return glade_string_list_to_string (list);
675 }
676 else
677 return GWA_GET_CLASS
678 (G_TYPE_OBJECT)->string_from_value (adaptor, klass, value);
679 }
680
681 static void
widget_parent_changed(GtkWidget * widget,GParamSpec * pspec,GladeWidgetAdaptor * adaptor)682 widget_parent_changed (GtkWidget * widget,
683 GParamSpec * pspec, GladeWidgetAdaptor * adaptor)
684 {
685 GladeWidget *gwidget = glade_widget_get_from_gobject (widget);
686 GladeWidget *parent;
687
688 /* this could get called for a stale instance of an object
689 * being rebuilt for a contruct-only property. */
690 if (!gwidget)
691 return;
692
693 parent = glade_widget_get_parent (gwidget);
694
695 if (parent && !glade_widget_get_internal (parent))
696 glade_widget_set_action_sensitive (gwidget, "remove_parent", TRUE);
697 else
698 glade_widget_set_action_sensitive (gwidget, "remove_parent", FALSE);
699 }
700
701 void
glade_gtk_widget_deep_post_create(GladeWidgetAdaptor * adaptor,GObject * widget,GladeCreateReason reason)702 glade_gtk_widget_deep_post_create (GladeWidgetAdaptor * adaptor,
703 GObject * widget, GladeCreateReason reason)
704 {
705 GladeWidget *gwidget = glade_widget_get_from_gobject (widget);
706
707 /* Work around bug 472555 by resetting the default event mask,
708 * this way only user edits will be saved to the glade file. */
709 if (reason == GLADE_CREATE_USER)
710 glade_widget_property_reset (gwidget, "events");
711
712 glade_widget_set_action_sensitive (gwidget, "remove_parent", FALSE);
713
714 if (GWA_IS_TOPLEVEL (adaptor) || glade_widget_get_internal (gwidget))
715 glade_widget_set_action_sensitive (gwidget, "add_parent", FALSE);
716
717 /* Watch parents/projects and set actions sensitive/insensitive */
718 if (!glade_widget_get_internal (gwidget))
719 g_signal_connect (G_OBJECT (widget), "notify::parent",
720 G_CALLBACK (widget_parent_changed), adaptor);
721
722 if (!glade_widget_adaptor_get_book (adaptor) || !glade_util_have_devhelp ())
723 glade_widget_set_action_visible (gwidget, "read_documentation", FALSE);
724 }
725
726 void
glade_gtk_widget_set_property(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * id,const GValue * value)727 glade_gtk_widget_set_property (GladeWidgetAdaptor * adaptor,
728 GObject * object,
729 const gchar * id, const GValue * value)
730 {
731 /* FIXME: is this still needed with the new gtk+ tooltips? */
732 if (!strcmp (id, "tooltip"))
733 {
734 id = "tooltip-text";
735 }
736
737 /* Setting can-focus in the runtime has been known to cause crashes */
738 if (!strcmp (id, "can-focus"))
739 return;
740
741 if (!strcmp (id, "glade-style-classes"))
742 {
743 GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET (object));
744 GList *l, *styles;
745
746 /* NOTE: we can not use gtk_style_context_list_classes() because we only
747 * want to remove the classes we added not the ones added by the widget
748 */
749 styles = g_object_get_data (object, "glade-style-classes");
750 for (l = styles; l; l = g_list_next (l))
751 {
752 GladeString *style = l->data;
753 gtk_style_context_remove_class (context, style->string);
754 }
755
756 for (l = g_value_get_boxed (value); l; l = g_list_next (l))
757 {
758 GladeString *style = l->data;
759 gtk_style_context_add_class (context, style->string);
760 }
761
762 /* Save the list here so we can use it later on to remove them from the style context */
763 g_object_set_data_full (object, "glade-style-classes",
764 glade_string_list_copy (g_value_get_boxed (value)),
765 (GDestroyNotify) glade_string_list_free);
766 }
767 else
768 GWA_GET_CLASS (G_TYPE_OBJECT)->set_property (adaptor, object, id, value);
769 }
770
771 void
glade_gtk_widget_get_property(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * id,GValue * value)772 glade_gtk_widget_get_property (GladeWidgetAdaptor * adaptor,
773 GObject * object,
774 const gchar * id, GValue * value)
775 {
776 if (!strcmp (id, "tooltip"))
777 {
778 id = "tooltip-text";
779 }
780
781 GWA_GET_CLASS (G_TYPE_OBJECT)->get_property (adaptor, object, id, value);
782 }
783
784
785 static GList *
create_command_property_list(GladeWidget * gnew,GList * saved_props)786 create_command_property_list (GladeWidget * gnew, GList * saved_props)
787 {
788 GList *l, *command_properties = NULL;
789
790 for (l = saved_props; l; l = l->next)
791 {
792 GladeProperty *property = l->data;
793 GladePropertyClass *pclass = glade_property_get_class (property);
794 GladeProperty *orig_prop =
795 glade_widget_get_pack_property (gnew, glade_property_class_id (pclass));
796 GCSetPropData *pdata = g_new0 (GCSetPropData, 1);
797
798 pdata->property = orig_prop;
799 pdata->old_value = g_new0 (GValue, 1);
800 pdata->new_value = g_new0 (GValue, 1);
801
802 glade_property_get_value (orig_prop, pdata->old_value);
803 glade_property_get_value (property, pdata->new_value);
804
805 command_properties = g_list_prepend (command_properties, pdata);
806 }
807 return g_list_reverse (command_properties);
808 }
809
810
811 void
glade_gtk_widget_action_activate(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * action_path)812 glade_gtk_widget_action_activate (GladeWidgetAdaptor * adaptor,
813 GObject * object, const gchar * action_path)
814 {
815 GladeWidget *gwidget = glade_widget_get_from_gobject (object), *gparent;
816 GList this_widget = { 0, }, that_widget = { 0,};
817 GladeProject *project;
818
819 gparent = glade_widget_get_parent (gwidget);
820 project = glade_widget_get_project (gwidget);
821
822 if (strcmp (action_path, "preview") == 0)
823 {
824 glade_project_preview (project,
825 glade_widget_get_from_gobject ((gpointer) object));
826 }
827 else if (strcmp (action_path, "edit_separate") == 0)
828 {
829 GtkWidget *dialog = glade_editor_dialog_for_widget (gwidget);
830 gtk_widget_show_all (dialog);
831 }
832 else if (strcmp (action_path, "remove_parent") == 0)
833 {
834 GladeWidget *new_gparent;
835 GladeProperty *property;
836
837 g_return_if_fail (gparent);
838
839 property = glade_widget_get_parentless_widget_ref (gparent);
840 new_gparent = glade_widget_get_parent (gparent);
841
842 glade_command_push_group (_("Removing parent of %s"), glade_widget_get_name (gwidget));
843
844 /* Remove "this" widget, If the parent we're removing is a parentless
845 * widget reference, the reference will be implicitly broken by the 'cut' command */
846 this_widget.data = gwidget;
847 glade_command_delete (&this_widget);
848
849 /* Delete the parent */
850 that_widget.data = gparent;
851 glade_command_delete (&that_widget);
852
853 /* Add "this" widget to the new parent, if there is no new parent this will re-add
854 * the widget to the project at the toplevel without a parent
855 */
856 glade_command_add (&this_widget, new_gparent, NULL, project, FALSE);
857
858 /* If the parent had a parentless widget reference, undoably add the child
859 * as the new parentless widget reference here */
860 if (property)
861 glade_command_set_property (property, glade_widget_get_object (gwidget));
862
863 glade_command_pop_group ();
864 }
865 else if (strncmp (action_path, "add_parent/", 11) == 0)
866 {
867 const gchar *action = action_path + 11;
868 GType new_type = 0;
869
870 if (strcmp (action, "alignment") == 0)
871 new_type = GTK_TYPE_ALIGNMENT;
872 else if (strcmp (action, "viewport") == 0)
873 new_type = GTK_TYPE_VIEWPORT;
874 else if (strcmp (action, "eventbox") == 0)
875 new_type = GTK_TYPE_EVENT_BOX;
876 else if (strcmp (action, "frame") == 0)
877 new_type = GTK_TYPE_FRAME;
878 else if (strcmp (action, "aspect_frame") == 0)
879 new_type = GTK_TYPE_ASPECT_FRAME;
880 else if (strcmp (action, "scrolled_window") == 0)
881 new_type = GTK_TYPE_SCROLLED_WINDOW;
882 else if (strcmp (action, "expander") == 0)
883 new_type = GTK_TYPE_EXPANDER;
884 else if (strcmp (action, "grid") == 0)
885 new_type = GTK_TYPE_GRID;
886 else if (strcmp (action, "box") == 0)
887 new_type = GTK_TYPE_BOX;
888 else if (strcmp (action, "paned") == 0)
889 new_type = GTK_TYPE_PANED;
890 else if (strcmp (action, "stack") == 0)
891 new_type = GTK_TYPE_STACK;
892
893 if (new_type)
894 {
895 GladeWidgetAdaptor *adaptor =
896 glade_widget_adaptor_get_by_type (new_type);
897 GList *saved_props, *prop_cmds;
898 GladeWidget *gnew_parent;
899 GladeProperty *property;
900
901 glade_command_push_group (_("Adding parent %s for %s"),
902 glade_widget_adaptor_get_title (adaptor),
903 glade_widget_get_name (gwidget));
904
905 /* Record packing properties */
906 saved_props =
907 glade_widget_dup_properties (gwidget, glade_widget_get_packing_properties (gwidget),
908 FALSE, FALSE, FALSE);
909
910 property = glade_widget_get_parentless_widget_ref (gwidget);
911
912 /* Remove "this" widget, If the parent we're removing is a parentless
913 * widget reference, the reference will be implicitly broken by the 'cut' command */
914 this_widget.data = gwidget;
915 glade_command_delete (&this_widget);
916
917 /* Create new widget and put it where the placeholder was */
918 if ((gnew_parent =
919 glade_command_create (adaptor, gparent, NULL, project)) != NULL)
920 {
921 /* Now we created the new parent, if gwidget had a parentless widget reference...
922 * set that reference to the new parent instead */
923 if (property)
924 glade_command_set_property (property, glade_widget_get_object (gnew_parent));
925
926 /* Remove the alignment that we added in the frame's post_create... */
927 if (new_type == GTK_TYPE_FRAME)
928 {
929 GObject *frame = glade_widget_get_object (gnew_parent);
930 GladeWidget *galign =
931 glade_widget_get_from_gobject (gtk_bin_get_child (GTK_BIN (frame)));
932 GList to_delete = { 0, };
933
934 to_delete.data = galign;
935 glade_command_delete (&to_delete);
936 }
937
938 /* Create heavy-duty glade-command properties stuff */
939 prop_cmds =
940 create_command_property_list (gnew_parent, saved_props);
941
942 /* Apply the properties in an undoable way */
943 if (prop_cmds)
944 glade_command_set_properties_list
945 (glade_widget_get_project (gparent), prop_cmds);
946
947 /* Add "this" widget to the new parent */
948 glade_command_add (&this_widget, gnew_parent, NULL, project, FALSE);
949
950 glade_command_pop_group ();
951 }
952 else
953 {
954 glade_command_pop_group ();
955
956 /* Undo delete command
957 * FIXME: this will leave the "Adding parent..." comand in the
958 * redo list, which I think its better than leaving it in the
959 * undo list by using glade_command_add() to add the widget back
960 * to the original parent.
961 * Ideally we need a way to remove a redo item from the project or
962 * simply do not let the user cancel a widget creation!
963 */
964 glade_project_undo (project);
965 }
966
967 g_list_foreach (saved_props, (GFunc) g_object_unref, NULL);
968 g_list_free (saved_props);
969 }
970 }
971 else if (strcmp (action_path, "sizegroup_add") == 0)
972 {
973 /* Ignore dummy */
974 }
975 else if (strcmp (action_path, "clear_properties") == 0)
976 {
977 glade_editor_reset_dialog_run (gtk_widget_get_toplevel (GTK_WIDGET (object)), gwidget);
978 }
979 else if (strcmp (action_path, "read_documentation") == 0)
980 {
981 glade_app_search_docs (glade_widget_adaptor_get_book (adaptor),
982 glade_widget_adaptor_get_name (adaptor),
983 NULL);
984 }
985 else
986 GWA_GET_CLASS (G_TYPE_OBJECT)->action_activate (adaptor,
987 object, action_path);
988 }
989
990 static GList *
list_sizegroups(GladeWidget * gwidget)991 list_sizegroups (GladeWidget * gwidget)
992 {
993 GladeProject *project = glade_widget_get_project (gwidget);
994 GList *groups = NULL;
995 const GList *list;
996
997 for (list = glade_project_get_objects (project); list; list = list->next)
998 {
999 GladeWidget *iter = glade_widget_get_from_gobject (list->data);
1000 if (GTK_IS_SIZE_GROUP (glade_widget_get_object (iter)))
1001 groups = g_list_prepend (groups, iter);
1002 }
1003 return g_list_reverse (groups);
1004 }
1005
1006 static void
glade_gtk_widget_add2group_cb(GtkMenuItem * item,GladeWidget * gwidget)1007 glade_gtk_widget_add2group_cb (GtkMenuItem * item, GladeWidget * gwidget)
1008 {
1009 GladeWidget *group =
1010 g_object_get_data (G_OBJECT (item), "glade-group-widget");
1011 GladeWidgetAdaptor *adaptor =
1012 glade_widget_adaptor_get_by_type (GTK_TYPE_SIZE_GROUP);
1013 GList *widget_list = NULL, *new_list;
1014 GladeProperty *property;
1015 const gchar *current_name;
1016 const gchar *size_group_name = NULL;
1017 gchar *widget_name;
1018
1019 /* Display "(unnamed)" for unnamed size groups */
1020 if (group)
1021 {
1022 size_group_name = glade_widget_get_name (group);
1023 if (g_str_has_prefix (size_group_name, GLADE_UNNAMED_PREFIX))
1024 size_group_name = _("(unnamed)");
1025 }
1026
1027 /* Ensure the widget has a name if it's going to be referred to by a size group */
1028 current_name = glade_widget_get_name (gwidget);
1029 if (g_str_has_prefix (current_name, GLADE_UNNAMED_PREFIX))
1030 widget_name = glade_project_new_widget_name (glade_widget_get_project (gwidget), NULL,
1031 glade_widget_adaptor_get_generic_name (glade_widget_get_adaptor (gwidget)));
1032 else
1033 widget_name = g_strdup (current_name);
1034
1035 if (group)
1036 glade_command_push_group (_("Adding %s to Size Group %s"),
1037 widget_name,
1038 size_group_name);
1039 else
1040 glade_command_push_group (_("Adding %s to a new Size Group"),
1041 widget_name);
1042
1043 glade_command_set_name (gwidget, widget_name);
1044
1045 if (!group)
1046 /* Cant cancel a size group */
1047 group =
1048 glade_command_create (adaptor, NULL, NULL,
1049 glade_widget_get_project (gwidget));
1050
1051 property = glade_widget_get_property (group, "widgets");
1052 glade_property_get (property, &widget_list);
1053 new_list = g_list_copy (widget_list);
1054 if (!g_list_find (widget_list, glade_widget_get_object (gwidget)))
1055 new_list = g_list_append (new_list, glade_widget_get_object (gwidget));
1056 glade_command_set_property (property, new_list);
1057
1058 g_list_free (new_list);
1059 g_free (widget_name);
1060
1061 glade_command_pop_group ();
1062 }
1063
1064
1065 GtkWidget *
glade_gtk_widget_action_submenu(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * action_path)1066 glade_gtk_widget_action_submenu (GladeWidgetAdaptor * adaptor,
1067 GObject * object, const gchar * action_path)
1068 {
1069 GladeWidget *gwidget = glade_widget_get_from_gobject (object);
1070 GList *groups, *list;
1071
1072 if (strcmp (action_path, "sizegroup_add") == 0)
1073 {
1074 GtkWidget *menu = gtk_menu_new ();
1075 GtkWidget *separator, *item;
1076 GladeWidget *group;
1077 const gchar *size_group_name;
1078
1079 if ((groups = list_sizegroups (gwidget)) != NULL)
1080 {
1081 for (list = groups; list; list = list->next)
1082 {
1083 group = list->data;
1084
1085 size_group_name = glade_widget_get_name (group);
1086 if (g_str_has_prefix (size_group_name, GLADE_UNNAMED_PREFIX))
1087 size_group_name = _("(unnamed)");
1088
1089 item = gtk_menu_item_new_with_label (size_group_name);
1090
1091 g_object_set_data (G_OBJECT (item), "glade-group-widget", group);
1092 g_signal_connect (G_OBJECT (item), "activate",
1093 G_CALLBACK (glade_gtk_widget_add2group_cb),
1094 gwidget);
1095
1096 gtk_widget_show (item);
1097 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1098 }
1099 g_list_free (groups);
1100
1101 separator = gtk_menu_item_new ();
1102 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
1103 gtk_widget_show (separator);
1104 }
1105
1106 /* Add trailing new... item */
1107 item = gtk_menu_item_new_with_label (_("New Size Group"));
1108 g_signal_connect (G_OBJECT (item), "activate",
1109 G_CALLBACK (glade_gtk_widget_add2group_cb), gwidget);
1110
1111 gtk_widget_show (item);
1112 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1113
1114 return menu;
1115 }
1116 else if (GWA_GET_CLASS (G_TYPE_OBJECT)->action_submenu)
1117 return GWA_GET_CLASS (G_TYPE_OBJECT)->action_submenu (adaptor,
1118 object, action_path);
1119
1120 return NULL;
1121 }
1122