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