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