1 #include <config.h>
2 #include <glib/gi18n-lib.h>
3 #include <gladeui/glade.h>
4 #include "glade-gtk.h"
5 
6 #include "glade-header-bar-editor.h"
7 
8 #define TITLE_DISABLED_MESSAGE _("This property does not apply when a custom title is set")
9 
10 /* Uncomment to enable debug tracing of add/remove/replace children */
11 //#define d(x) x
12 #define d(x)
13 
14 typedef struct
15 {
16   GtkContainer *parent;
17   GtkWidget *custom_title;
18   gboolean include_placeholders;
19   gint count;
20 } ChildrenData;
21 
22 static void
count_children(GtkWidget * widget,gpointer data)23 count_children (GtkWidget *widget, gpointer data)
24 {
25   ChildrenData *cdata = data;
26 
27   if (widget == cdata->custom_title)
28     return;
29 
30   if ((GLADE_IS_PLACEHOLDER (widget) && cdata->include_placeholders) ||
31       glade_widget_get_from_gobject (widget) != NULL)
32     cdata->count++;
33 }
34 
35 static gboolean
glade_gtk_header_bar_verify_size(GObject * object,const GValue * value)36 glade_gtk_header_bar_verify_size (GObject      *object,
37                                   const GValue *value)
38 {
39   gint new_size;
40   ChildrenData data;
41 
42   new_size = g_value_get_int (value);
43 
44   data.parent = GTK_CONTAINER (object);
45   data.custom_title = gtk_header_bar_get_custom_title (GTK_HEADER_BAR (object));
46   data.include_placeholders = FALSE;
47   data.count = 0;
48 
49   gtk_container_forall (data.parent, count_children, &data);
50 
51   return data.count <= new_size;
52 }
53 
54 static gint
glade_gtk_header_bar_get_num_children(GObject * object)55 glade_gtk_header_bar_get_num_children (GObject *object)
56 {
57   ChildrenData data;
58 
59   data.parent = GTK_CONTAINER (object);
60   data.custom_title = gtk_header_bar_get_custom_title (GTK_HEADER_BAR (object));
61   data.include_placeholders = TRUE;
62   data.count = 0;
63 
64   gtk_container_forall (data.parent, count_children, &data);
65 
66   return data.count;
67 }
68 
69 static void
glade_gtk_header_bar_parse_finished(GladeProject * project,GObject * object)70 glade_gtk_header_bar_parse_finished (GladeProject * project,
71                                      GObject * object)
72 {
73   GladeWidget *gbox;
74 
75   gbox = glade_widget_get_from_gobject (object);
76   glade_widget_property_set (gbox, "size", glade_gtk_header_bar_get_num_children (object));
77   glade_widget_property_set (gbox, "use-custom-title", gtk_header_bar_get_custom_title (GTK_HEADER_BAR (object)) != NULL);
78 }
79 
80 void
glade_gtk_header_bar_post_create(GladeWidgetAdaptor * adaptor,GObject * container,GladeCreateReason reason)81 glade_gtk_header_bar_post_create (GladeWidgetAdaptor *adaptor,
82                                   GObject *container,
83                                   GladeCreateReason reason)
84 {
85   GladeWidget *parent = glade_widget_get_from_gobject (container);
86   GladeProject *project = glade_widget_get_project (parent);
87 
88   if (reason == GLADE_CREATE_LOAD)
89     {
90       g_signal_connect (project, "parse-finished",
91                         G_CALLBACK (glade_gtk_header_bar_parse_finished),
92                         container);
93     }
94   else if (reason == GLADE_CREATE_USER)
95     {
96       gtk_header_bar_pack_start (GTK_HEADER_BAR (container), glade_placeholder_new ());
97     }
98 }
99 
100 void
glade_gtk_header_bar_action_activate(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * action_path)101 glade_gtk_header_bar_action_activate (GladeWidgetAdaptor *adaptor,
102                                       GObject * object,
103                                       const gchar *action_path)
104 {
105   if (!strcmp (action_path, "add_slot"))
106     {
107       GladeWidget *parent;
108       GladeProperty *property;
109       gint size;
110 
111       parent = glade_widget_get_from_gobject (object);
112 
113       glade_command_push_group (_("Insert placeholder to %s"), glade_widget_get_name (parent));
114 
115       property = glade_widget_get_property (parent, "size");
116       glade_property_get (property, &size);
117       glade_command_set_property (property, size + 1);
118 
119       glade_command_pop_group ();
120     }
121   else
122     GWA_GET_CLASS (GTK_TYPE_CONTAINER)->action_activate (adaptor, object, action_path);
123 }
124 
125 void
glade_gtk_header_bar_child_action_activate(GladeWidgetAdaptor * adaptor,GObject * container,GObject * object,const gchar * action_path)126 glade_gtk_header_bar_child_action_activate (GladeWidgetAdaptor * adaptor,
127                                             GObject * container,
128                                             GObject * object,
129                                             const gchar * action_path)
130 {
131   if (strcmp (action_path, "remove_slot") == 0)
132     {
133       GladeWidget *parent;
134       GladeProperty *property;
135 
136       parent = glade_widget_get_from_gobject (container);
137       glade_command_push_group (_("Remove placeholder from %s"), glade_widget_get_name (parent));
138 
139       if (g_object_get_data (object, "special-child-type"))
140         {
141           property = glade_widget_get_property (parent, "use-custom-title");
142           glade_command_set_property (property, FALSE);
143         }
144       else
145         {
146           gint size;
147 
148           gtk_container_remove (GTK_CONTAINER (container), GTK_WIDGET (object));
149 
150           property = glade_widget_get_property (parent, "size");
151           glade_property_get (property, &size);
152           glade_command_set_property (property, size - 1);
153         }
154 
155       glade_command_pop_group ();
156     }
157   else
158     GWA_GET_CLASS (GTK_TYPE_CONTAINER)->child_action_activate (adaptor,
159                                                                container,
160                                                                object,
161                                                                action_path);
162 }
163 
164 void
glade_gtk_header_bar_get_property(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * id,GValue * value)165 glade_gtk_header_bar_get_property (GladeWidgetAdaptor * adaptor,
166                                    GObject * object,
167                                    const gchar * id,
168                                    GValue * value)
169 {
170   if (!strcmp (id, "use-custom-title"))
171     {
172       g_value_reset (value);
173       g_value_set_boolean (value, gtk_header_bar_get_custom_title (GTK_HEADER_BAR (object)) != NULL);
174     }
175   else if (!strcmp (id, "size"))
176     {
177       g_value_reset (value);
178       g_value_set_int (value, glade_gtk_header_bar_get_num_children (object));
179     }
180   else
181     GWA_GET_CLASS (GTK_TYPE_CONTAINER)->get_property (adaptor, object, id, value);
182 }
183 
184 static void
glade_gtk_header_bar_set_size(GObject * object,const GValue * value)185 glade_gtk_header_bar_set_size (GObject * object,
186                                const GValue * value)
187 {
188   GList *l, *next, *children;
189   GtkWidget *child;
190   guint new_size, old_size, i;
191 
192   g_return_if_fail (GTK_IS_HEADER_BAR (object));
193 
194   d(g_message ("Setting size to %d", g_value_get_int (value)));
195 
196   if (glade_util_object_is_loading (object))
197     return;
198 
199   children = gtk_container_get_children (GTK_CONTAINER (object));
200   l = children;
201   while (l)
202     {
203       next = l->next;
204       if (l->data == gtk_header_bar_get_custom_title (GTK_HEADER_BAR (object)) ||
205           (!glade_widget_get_from_gobject (l->data) && !GLADE_IS_PLACEHOLDER (l->data)))
206         children = g_list_delete_link (children, l);
207       l = next;
208     }
209 
210   old_size = g_list_length (children);
211   new_size = g_value_get_int (value);
212 
213   if (old_size == new_size)
214     {
215       g_list_free (children);
216       return;
217     }
218 
219   for (i = old_size; i < new_size; i++)
220     {
221       GtkWidget *placeholder = glade_placeholder_new ();
222       gtk_header_bar_pack_start (GTK_HEADER_BAR (object), placeholder);
223     }
224   for (l = g_list_last (children); l && old_size > new_size; l = l->prev)
225     {
226       child = l->data;
227       if (glade_widget_get_from_gobject (child) || !GLADE_IS_PLACEHOLDER (child))
228         continue;
229 
230       gtk_container_remove (GTK_CONTAINER (object), child);
231       old_size--;
232     }
233 
234   g_list_free (children);
235 }
236 
237 void
glade_gtk_header_bar_set_use_custom_title(GObject * object,gboolean use_custom_title)238 glade_gtk_header_bar_set_use_custom_title (GObject *object,
239 					   gboolean use_custom_title)
240 {
241   GladeWidget *gwidget = glade_widget_get_from_gobject (object);
242   GtkWidget *child;
243 
244   if (use_custom_title)
245     {
246       child = gtk_header_bar_get_custom_title (GTK_HEADER_BAR (object));
247       if (!child)
248 	{
249 	  child = glade_placeholder_new ();
250 	  g_object_set_data (G_OBJECT (child), "special-child-type", "title");
251 	}
252     }
253   else
254     child = NULL;
255 
256   gtk_header_bar_set_custom_title (GTK_HEADER_BAR (object), child);
257 
258   if (GLADE_IS_PLACEHOLDER (child))
259     {
260       GList *list, *l;
261 
262       list = glade_placeholder_packing_actions (GLADE_PLACEHOLDER (child));
263       for (l = list; l; l = l->next)
264 	{
265 	  GladeWidgetAction *gwa = l->data;
266 	  if (!strcmp (glade_widget_action_get_class (gwa)->id, "remove_slot"))
267 	    glade_widget_action_set_visible (gwa, FALSE);
268 	}
269     }
270 
271   if (use_custom_title)
272     {
273       glade_widget_property_set_sensitive (gwidget, "title", FALSE, TITLE_DISABLED_MESSAGE);
274       glade_widget_property_set_sensitive (gwidget, "subtitle", FALSE, TITLE_DISABLED_MESSAGE);
275       glade_widget_property_set_sensitive (gwidget, "has-subtitle", FALSE, TITLE_DISABLED_MESSAGE);
276     }
277   else
278     {
279       glade_widget_property_set_sensitive (gwidget, "title", TRUE, NULL);
280       glade_widget_property_set_sensitive (gwidget, "subtitle", TRUE, NULL);
281       glade_widget_property_set_sensitive (gwidget, "has-subtitle", TRUE, NULL);
282     }
283 }
284 
285 void
glade_gtk_header_bar_set_property(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * id,const GValue * value)286 glade_gtk_header_bar_set_property (GladeWidgetAdaptor * adaptor,
287                                    GObject * object,
288                                    const gchar * id,
289                                    const GValue * value)
290 {
291   if (!strcmp (id, "use-custom-title"))
292     glade_gtk_header_bar_set_use_custom_title (object, g_value_get_boolean (value));
293   else if (!strcmp (id, "show-close-button"))
294     {
295       GladeWidget *gwidget = glade_widget_get_from_gobject (object);
296 
297       /* We don't set the property to 'ignore' so that we catch this in the adaptor,
298        * but we also do not apply the property to the runtime object here, thus
299        * avoiding showing the close button which would in turn close glade itself
300        * when clicked.
301        */
302       glade_widget_property_set_sensitive (gwidget, "decoration-layout",
303 					   g_value_get_boolean (value),
304 					   _("The decoration layout does not apply to header bars "
305 					     "which do no show window controls"));
306     }
307   else if (!strcmp (id, "size"))
308     glade_gtk_header_bar_set_size (object, value);
309   else
310     GWA_GET_CLASS (GTK_TYPE_CONTAINER)->set_property (adaptor, object, id, value);
311 }
312 
313 void
glade_gtk_header_bar_add_child(GladeWidgetAdaptor * adaptor,GObject * parent,GObject * child)314 glade_gtk_header_bar_add_child (GladeWidgetAdaptor *adaptor,
315                                 GObject *parent,
316                                 GObject *child)
317 {
318   GladeWidget *gbox, *gchild;
319   gint size;
320   gchar *special_child_type;
321 
322   gchild = glade_widget_get_from_gobject (child);
323   if (gchild)
324     glade_widget_set_pack_action_visible (gchild, "remove_slot", FALSE);
325 
326   special_child_type = g_object_get_data (child, "special-child-type");
327 
328   d(g_message ("Add %s %p (special: %s)",
329 	       GLADE_IS_PLACEHOLDER (child) ? "placeholder" : "child",
330 	       child, special_child_type));
331 
332   if (special_child_type && !strcmp (special_child_type, "title"))
333     {
334       gtk_header_bar_set_custom_title (GTK_HEADER_BAR (parent), GTK_WIDGET (child));
335       return;
336     }
337 
338   GWA_GET_CLASS (GTK_TYPE_CONTAINER)->add (adaptor, parent, child);
339 
340   gbox = glade_widget_get_from_gobject (parent);
341   if (!glade_widget_superuser ())
342     {
343       glade_widget_property_get (gbox, "size", &size);
344       glade_widget_property_set (gbox, "size", size);
345     }
346 }
347 
348 void
glade_gtk_header_bar_remove_child(GladeWidgetAdaptor * adaptor,GObject * object,GObject * child)349 glade_gtk_header_bar_remove_child (GladeWidgetAdaptor * adaptor,
350                                    GObject * object,
351                                    GObject * child)
352 {
353   GladeWidget *gbox;
354   gint size;
355   gchar *special_child_type;
356 
357   special_child_type = g_object_get_data (child, "special-child-type");
358 
359   d(g_message ("Remove %s %p (special: %s)",
360 	       GLADE_IS_PLACEHOLDER (child) ? "placeholder" : "child",
361 	       child, special_child_type));
362 
363   if (special_child_type && !strcmp (special_child_type, "title"))
364     {
365       GtkWidget *replacement = glade_placeholder_new ();
366 
367       g_object_set_data (G_OBJECT (replacement), "special-child-type", "title");
368       gtk_header_bar_set_custom_title (GTK_HEADER_BAR (object), replacement);
369       return;
370     }
371 
372   gtk_container_remove (GTK_CONTAINER (object), GTK_WIDGET (child));
373 
374   /* Synchronize number of placeholders, this should trigger the set_property method with the
375    * correct value (not the arbitrary number of children currently in the headerbar)
376    */
377   gbox = glade_widget_get_from_gobject (object);
378   if (!glade_widget_superuser ())
379     {
380       glade_widget_property_get (gbox, "size", &size);
381       glade_widget_property_set (gbox, "size", size);
382     }
383 }
384 
385 void
glade_gtk_header_bar_replace_child(GladeWidgetAdaptor * adaptor,GObject * container,GObject * current,GObject * new_widget)386 glade_gtk_header_bar_replace_child (GladeWidgetAdaptor * adaptor,
387                                     GObject * container,
388                                     GObject * current,
389                                     GObject * new_widget)
390 {
391   GladeWidget *gbox;
392   gchar *special_child_type;
393   gint size;
394 
395   special_child_type =
396     g_object_get_data (G_OBJECT (current), "special-child-type");
397 
398   d(g_message ("Replace %s %p (special: %s) with %s %p",
399 	       GLADE_IS_PLACEHOLDER (current) ? "placeholder" : "child",
400 	       current, special_child_type,
401 	       GLADE_IS_PLACEHOLDER (new_widget) ? "placeholder" : "child",
402 	       new_widget));
403 
404   if (special_child_type && !strcmp (special_child_type, "title"))
405     {
406       g_object_set_data (G_OBJECT (new_widget), "special-child-type", "title");
407       gtk_header_bar_set_custom_title (GTK_HEADER_BAR (container), GTK_WIDGET (new_widget));
408       return;
409     }
410   else
411     g_object_set_data (G_OBJECT (new_widget), "special-child-type", NULL);
412 
413   GWA_GET_CLASS
414       (GTK_TYPE_CONTAINER)->replace_child (adaptor, container, current, new_widget);
415 
416   gbox = glade_widget_get_from_gobject (container);
417   if (!glade_widget_superuser ())
418     {
419       glade_widget_property_get (gbox, "size", &size);
420       glade_widget_property_set (gbox, "size", size);
421     }
422 }
423 
424 gboolean
glade_gtk_header_bar_verify_property(GladeWidgetAdaptor * adaptor,GObject * object,const gchar * id,const GValue * value)425 glade_gtk_header_bar_verify_property (GladeWidgetAdaptor *adaptor,
426                                       GObject            *object,
427                                       const gchar        *id,
428                                       const GValue       *value)
429 {
430   if (!strcmp (id, "size"))
431     return glade_gtk_header_bar_verify_size (object, value);
432   else if (GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property)
433     return GWA_GET_CLASS (GTK_TYPE_CONTAINER)->verify_property (adaptor, object, id, value);
434 
435   return TRUE;
436 }
437 
438 void
glade_gtk_header_bar_child_set_property(GladeWidgetAdaptor * adaptor,GObject * container,GObject * child,const gchar * property_name,const GValue * value)439 glade_gtk_header_bar_child_set_property (GladeWidgetAdaptor *adaptor,
440                                          GObject *container,
441                                          GObject *child,
442                                          const gchar *property_name,
443                                          const GValue *value)
444 {
445   GladeWidget *gbox;
446   gint size;
447 
448   d(g_message ("Set child prop %s %s\n", g_type_name_from_instance (child), property_name));
449 
450   gtk_container_child_set_property (GTK_CONTAINER (container),
451                                     GTK_WIDGET (child),
452                                     property_name,
453                                     value);
454 
455   gbox = glade_widget_get_from_gobject (container);
456   if (!glade_widget_superuser ())
457     {
458       glade_widget_property_get (gbox, "size", &size);
459       glade_widget_property_set (gbox, "size", size);
460     }
461 }
462 
463 
464 GladeEditable *
glade_gtk_header_bar_create_editable(GladeWidgetAdaptor * adaptor,GladeEditorPageType type)465 glade_gtk_header_bar_create_editable (GladeWidgetAdaptor * adaptor,
466                                       GladeEditorPageType  type)
467 {
468   if (type == GLADE_PAGE_GENERAL)
469     return (GladeEditable *) glade_header_bar_editor_new ();
470   else
471     return GWA_GET_CLASS (GTK_TYPE_CONTAINER)->create_editable (adaptor, type);
472 }
473