1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 8 -*- */
2 /* gdl-switcher.c
3 *
4 * Copyright (C) 2003 Ettore Perazzoli,
5 * 2007 Naba Kumar
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 *
20 *
21 * Copied and adapted from ESidebar.[ch] from evolution
22 *
23 * Authors: Ettore Perazzoli <ettore@ximian.com>
24 * Naba Kumar <naba@gnome.org>
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30
31 #include <glib/gi18n-lib.h>
32 #include "gdl-switcher.h"
33 #include "libgdlmarshal.h"
34 #include "libgdltypebuiltins.h"
35
36 #include <gtk/gtk.h>
37
38 /**
39 * SECTION:gdl-switcher
40 * @title: GdlSwitcher
41 * @short_description: An improved notebook widget.
42 * @stability: Unstable
43 * @see_also: #GdlDockNotebook
44 *
45 * A #GdlSwitcher widget is an improved version of the #GtkNotebook. The
46 * tab labels could have different style and could be layouted on several lines.
47 *
48 * It is used by the #GdlDockNotebook widget to dock other widgets.
49 */
50
51
52 static void gdl_switcher_set_property (GObject *object,
53 guint prop_id,
54 const GValue *value,
55 GParamSpec *pspec);
56 static void gdl_switcher_get_property (GObject *object,
57 guint prop_id,
58 GValue *value,
59 GParamSpec *pspec);
60
61 static void gdl_switcher_add_button (GdlSwitcher *switcher,
62 const gchar *label,
63 const gchar *tooltips,
64 const gchar *stock_id,
65 GdkPixbuf *pixbuf_icon,
66 gint switcher_id,
67 GtkWidget *page);
68 /* static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id); */
69 static void gdl_switcher_select_page (GdlSwitcher *switcher, gint switcher_id);
70 static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id);
71 static void gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show);
72 static void gdl_switcher_set_style (GdlSwitcher *switcher,
73 GdlSwitcherStyle switcher_style);
74 static GdlSwitcherStyle gdl_switcher_get_style (GdlSwitcher *switcher);
75 static void gdl_switcher_set_tab_pos (GdlSwitcher *switcher, GtkPositionType pos);
76 static void gdl_switcher_set_tab_reorderable (GdlSwitcher *switcher, gboolean reorderable);
77 static void gdl_switcher_update_lone_button_visibility (GdlSwitcher *switcher);
78
79 enum {
80 PROP_0,
81 PROP_SWITCHER_STYLE,
82 PROP_TAB_POS,
83 PROP_TAB_REORDERABLE
84 };
85
86 typedef struct {
87 GtkWidget *button_widget;
88 GtkWidget *label;
89 GtkWidget *icon;
90 GtkWidget *arrow;
91 GtkWidget *hbox;
92 GtkWidget *page;
93 int id;
94 } Button;
95
96 struct _GdlSwitcherPrivate {
97 GdlSwitcherStyle switcher_style;
98 GdlSwitcherStyle toolbar_style;
99 GtkPositionType tab_pos;
100 gboolean tab_reorderable;
101
102 gboolean show;
103 GSList *buttons;
104
105 guint style_changed_id;
106 gint buttons_height_request;
107 gboolean in_toggle;
108 };
109
110 struct _GdlSwitcherClassPrivate {
111 GtkCssProvider *css;
112 };
113
G_DEFINE_TYPE_WITH_CODE(GdlSwitcher,gdl_switcher,GTK_TYPE_NOTEBOOK,g_type_add_class_private (g_define_type_id,sizeof (GdlSwitcherClassPrivate)))114 G_DEFINE_TYPE_WITH_CODE (GdlSwitcher, gdl_switcher, GTK_TYPE_NOTEBOOK,
115 g_type_add_class_private (g_define_type_id, sizeof (GdlSwitcherClassPrivate)))
116
117 #define INTERNAL_MODE(switcher) (switcher->priv->switcher_style == \
118 GDL_SWITCHER_STYLE_TOOLBAR ? switcher->priv->toolbar_style : \
119 switcher->priv->switcher_style)
120
121 #define H_PADDING 0
122 #define V_PADDING 0
123 #define V_SPACING 2
124
125 /* Utility functions. */
126
127 static void
128 gdl_switcher_long_name_changed (GObject* object,
129 GParamSpec* spec,
130 gpointer user_data)
131 {
132 Button* button = user_data;
133 gchar* label;
134
135 g_object_get (object, "long-name", &label, NULL);
136 gtk_label_set_text (GTK_LABEL (button->label), label);
137 g_free (label);
138 }
139
140 static void
gdl_switcher_stock_id_changed(GObject * object,GParamSpec * spec,gpointer user_data)141 gdl_switcher_stock_id_changed (GObject* object,
142 GParamSpec* spec,
143 gpointer user_data)
144 {
145 Button* button = user_data;
146 gchar* id;
147
148 g_object_get (object, "stock-id", &id, NULL);
149 gtk_image_set_from_stock (GTK_IMAGE(button->icon), id, GTK_ICON_SIZE_MENU);
150 g_free (id);
151 }
152
153 static void
gdl_switcher_visible_changed(GObject * object,GParamSpec * spec,gpointer user_data)154 gdl_switcher_visible_changed (GObject* object,
155 GParamSpec* spec,
156 gpointer user_data)
157 {
158 Button* button = user_data;
159 GdlSwitcher* switcher;
160
161 if (gtk_widget_get_visible (button->page))
162 {
163 gtk_widget_show_all (button->button_widget);
164 }
165 else
166 {
167 gtk_widget_hide (button->button_widget);
168 }
169 switcher = GDL_SWITCHER (gtk_widget_get_parent (button->button_widget));
170 gdl_switcher_update_lone_button_visibility (switcher);
171 }
172
173
174 static Button *
button_new(GtkWidget * button_widget,GtkWidget * label,GtkWidget * icon,GtkWidget * arrow,GtkWidget * hbox,int id,GtkWidget * page)175 button_new (GtkWidget *button_widget, GtkWidget *label, GtkWidget *icon,
176 GtkWidget *arrow, GtkWidget *hbox, int id, GtkWidget *page)
177 {
178 Button *button = g_new (Button, 1);
179
180 button->button_widget = button_widget;
181 button->label = label;
182 button->icon = icon;
183 button->arrow = arrow;
184 button->hbox = hbox;
185 button->id = id;
186 button->page = page;
187
188 g_signal_connect (page, "notify::long-name", G_CALLBACK (gdl_switcher_long_name_changed),
189 button);
190 g_signal_connect (page, "notify::stock-id", G_CALLBACK (gdl_switcher_stock_id_changed),
191 button);
192 g_signal_connect (page, "notify::visible", G_CALLBACK (gdl_switcher_visible_changed),
193 button);
194
195 g_object_ref (button_widget);
196 g_object_ref (label);
197 g_object_ref (icon);
198 g_object_ref (arrow);
199 g_object_ref (hbox);
200
201 return button;
202 }
203
204 static void
button_free(Button * button)205 button_free (Button *button)
206 {
207 g_signal_handlers_disconnect_by_func (button->page,
208 gdl_switcher_long_name_changed,
209 button);
210 g_signal_handlers_disconnect_by_func (button->page,
211 gdl_switcher_stock_id_changed,
212 button);
213 g_signal_handlers_disconnect_by_func (button->page,
214 gdl_switcher_visible_changed,
215 button);
216
217 g_object_unref (button->button_widget);
218 g_object_unref (button->label);
219 g_object_unref (button->icon);
220 g_object_unref (button->hbox);
221 g_free (button);
222 }
223
224 static gint
gdl_switcher_get_page_id(GtkWidget * widget)225 gdl_switcher_get_page_id (GtkWidget *widget)
226 {
227 static gint switcher_id_count = 0;
228 gint switcher_id;
229 switcher_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
230 "__switcher_id"));
231 if (switcher_id <= 0) {
232 switcher_id = ++switcher_id_count;
233 g_object_set_data (G_OBJECT (widget), "__switcher_id",
234 GINT_TO_POINTER (switcher_id));
235 }
236 return switcher_id;
237 }
238
239 /* Hide switcher button if they are not needed (only one visible page)
240 * or show switcher button if there are two visible pages */
241 static void
gdl_switcher_update_lone_button_visibility(GdlSwitcher * switcher)242 gdl_switcher_update_lone_button_visibility (GdlSwitcher *switcher)
243 {
244 GSList *p;
245 GtkWidget *alone = NULL;
246
247 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
248 Button *button = p->data;
249
250 if (gtk_widget_get_visible (button->page))
251 {
252 if (alone == NULL)
253 {
254 alone = button->button_widget;
255 }
256 else
257 {
258 gtk_widget_show (alone);
259 gtk_widget_show (button->button_widget);
260 alone = NULL;
261 break;
262 }
263 }
264 }
265
266 if (alone) gtk_widget_hide (alone);
267 }
268
269 static void
update_buttons(GdlSwitcher * switcher,int new_selected_id)270 update_buttons (GdlSwitcher *switcher, int new_selected_id)
271 {
272 GSList *p;
273
274 switcher->priv->in_toggle = TRUE;
275
276 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
277 Button *button = p->data;
278
279 if (button->id == new_selected_id) {
280 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
281 (button->button_widget), TRUE);
282 gtk_widget_set_sensitive (button->arrow, TRUE);
283 } else {
284 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
285 (button->button_widget), FALSE);
286 gtk_widget_set_sensitive (button->arrow, FALSE);
287 }
288 }
289
290 switcher->priv->in_toggle = FALSE;
291 }
292
293 /* Callbacks. */
294
295 static void
button_toggled_callback(GtkToggleButton * toggle_button,GdlSwitcher * switcher)296 button_toggled_callback (GtkToggleButton *toggle_button,
297 GdlSwitcher *switcher)
298 {
299 int id = 0;
300 gboolean is_active = FALSE;
301 GSList *p;
302
303 if (switcher->priv->in_toggle)
304 return;
305
306 switcher->priv->in_toggle = TRUE;
307
308 if (gtk_toggle_button_get_active (toggle_button))
309 is_active = TRUE;
310
311 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
312 Button *button = p->data;
313
314 if (button->button_widget != GTK_WIDGET (toggle_button)) {
315 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
316 (button->button_widget), FALSE);
317 gtk_widget_set_sensitive (button->arrow, FALSE);
318 } else {
319 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
320 (button->button_widget), TRUE);
321 gtk_widget_set_sensitive (button->arrow, TRUE);
322 id = button->id;
323 }
324 }
325
326 switcher->priv->in_toggle = FALSE;
327
328 if (is_active)
329 {
330 gdl_switcher_select_page (switcher, id);
331 }
332 }
333
334 static gboolean
delayed_resize_switcher(gpointer data)335 delayed_resize_switcher (gpointer data)
336 {
337 gtk_widget_queue_resize (GTK_WIDGET (data));
338 return FALSE;
339 }
340
341 /* Returns -1 if layout didn't happen because a resize request was queued */
342 static int
layout_buttons(GdlSwitcher * switcher,GtkAllocation * allocation)343 layout_buttons (GdlSwitcher *switcher, GtkAllocation* allocation)
344 {
345 gint min_height, nat_height;
346 GdlSwitcherStyle switcher_style;
347 gboolean icons_only;
348 int num_btns;
349 int btns_per_row;
350 GSList **rows, *p;
351 Button *button;
352 int row_number;
353 int max_btn_width = 0, max_btn_height = 0;
354 int optimal_layout_width = 0;
355 int row_last;
356 int x, y;
357 int i;
358 int rows_count;
359 int last_buttons_height;
360
361 last_buttons_height = switcher->priv->buttons_height_request;
362
363 GTK_WIDGET_CLASS (gdl_switcher_parent_class)->get_preferred_height (GTK_WIDGET (switcher), &min_height, &nat_height);
364
365 y = allocation->y + allocation->height - V_PADDING - 1;
366
367 /* Return bottom of the area if there isn't any visible button */
368 for (p = switcher->priv->buttons; p != NULL; p = p->next) if (gtk_widget_get_visible (((Button *)p->data)->button_widget)) break;
369 if (p == NULL)
370 return y;
371
372 switcher_style = INTERNAL_MODE (switcher);
373 icons_only = (switcher_style == GDL_SWITCHER_STYLE_ICON);
374
375 /* Figure out the max width and height taking into account visible buttons */
376 optimal_layout_width = H_PADDING;
377 num_btns = 0;
378 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
379 GtkRequisition requisition;
380
381 button = p->data;
382 if (gtk_widget_get_visible (button->button_widget)) {
383 gtk_widget_get_preferred_size (GTK_WIDGET (button->button_widget),
384 &requisition, NULL);
385 optimal_layout_width += requisition.width + H_PADDING;
386 max_btn_height = MAX (max_btn_height, requisition.height);
387 max_btn_width = MAX (max_btn_width, requisition.width);
388 num_btns++;
389 }
390 }
391
392 /* Figure out how many rows and columns we'll use. */
393 btns_per_row = allocation->width / (max_btn_width + H_PADDING);
394 /* Use at least one column */
395 if (btns_per_row == 0) btns_per_row = 1;
396
397 /* If all the buttons could fit in the single row, have it so */
398 if (allocation->width >= optimal_layout_width)
399 {
400 btns_per_row = num_btns;
401 }
402 if (!icons_only) {
403 /* If using text buttons, we want to try to have a
404 * completely filled-in grid, but if we can't, we want
405 * the odd row to have just a single button.
406 */
407 while (num_btns % btns_per_row > 1)
408 btns_per_row--;
409 }
410
411 rows_count = num_btns / btns_per_row;
412 if (num_btns % btns_per_row != 0)
413 rows_count++;
414
415 /* Assign visible buttons to rows */
416 rows = g_new0 (GSList *, rows_count);
417
418 if (!icons_only && num_btns % btns_per_row != 0) {
419 p = switcher->priv->buttons;
420 for (; p != NULL; p = p->next) if (gtk_widget_get_visible (((Button *)p->data)->button_widget)) break;
421 button = p->data;
422 rows [0] = g_slist_append (rows [0], button->button_widget);
423
424 p = p->next;
425 row_number = p ? 1 : 0;
426 } else {
427 p = switcher->priv->buttons;
428 row_number = 0;
429 }
430
431 for (; p != NULL; p = p->next) {
432 button = p->data;
433
434 if (gtk_widget_get_visible (button->button_widget)) {
435 if (g_slist_length (rows [row_number]) == btns_per_row)
436 row_number ++;
437
438 rows [row_number] = g_slist_append (rows [row_number],
439 button->button_widget);
440 }
441 }
442
443 row_last = row_number;
444
445 /* If there are more than 1 row of buttons, save the current height
446 * requirement for subsequent size requests.
447 */
448 if (row_last > 0)
449 {
450 switcher->priv->buttons_height_request =
451 (row_last + 1) * (max_btn_height + V_PADDING) + 1;
452 } else { /* Otherwize clear it */
453 if (last_buttons_height >= 0) {
454
455 switcher->priv->buttons_height_request = -1;
456 }
457 }
458
459 /* If it turns out that we now require smaller height for the buttons
460 * than it was last time, make a resize request to ensure our
461 * size requisition is properly communicated to the parent (otherwise
462 * parent tend to keep assuming the older size).
463 */
464 if (last_buttons_height > switcher->priv->buttons_height_request)
465 {
466 g_idle_add (delayed_resize_switcher, switcher);
467 return -1;
468 }
469
470 /* Layout the buttons. */
471 for (i = row_last; i >= 0; i --) {
472 int len, extra_width;
473
474 y -= max_btn_height;
475
476 /* Check for possible size over flow (taking into account client
477 * requisition
478 */
479 if (y < (allocation->y + min_height)) {
480 /* We have an overflow: Insufficient allocation */
481 if (last_buttons_height < switcher->priv->buttons_height_request) {
482 /* Request for a new resize */
483 g_idle_add (delayed_resize_switcher, switcher);
484
485 return -1;
486 }
487 }
488 x = H_PADDING + allocation->x;
489 len = g_slist_length (rows[i]);
490 if (switcher_style == GDL_SWITCHER_STYLE_TEXT ||
491 switcher_style == GDL_SWITCHER_STYLE_BOTH)
492 extra_width = (allocation->width - (len * max_btn_width )
493 - (len * H_PADDING)) / len;
494 else
495 extra_width = 0;
496 for (p = rows [i]; p != NULL; p = p->next) {
497 GtkAllocation child_allocation;
498 GtkStyleContext *style_context;
499 GtkJunctionSides junction = 0;
500
501 child_allocation.x = x;
502 child_allocation.y = y;
503 if (rows_count == 1 && row_number == 0)
504 {
505 GtkRequisition child_requisition;
506 gtk_widget_get_preferred_size (GTK_WIDGET (p->data),
507 &child_requisition, NULL);
508 child_allocation.width = child_requisition.width;
509 }
510 else
511 {
512 child_allocation.width = max_btn_width + extra_width;
513 }
514 child_allocation.height = max_btn_height;
515
516 style_context = gtk_widget_get_style_context (GTK_WIDGET (p->data));
517
518 if (row_last) {
519 if (i) {
520 junction |= GTK_JUNCTION_TOP;
521 }
522 if (i != row_last) {
523 junction |= GTK_JUNCTION_BOTTOM;
524 }
525 }
526
527 if (p->next) {
528 junction |= GTK_JUNCTION_RIGHT;
529 }
530
531 if (p != rows [i]) {
532 junction |= GTK_JUNCTION_LEFT;
533 }
534
535 gtk_style_context_set_junction_sides (style_context, junction);
536
537 gtk_widget_size_allocate (GTK_WIDGET (p->data), &child_allocation);
538
539 x += child_allocation.width + H_PADDING;
540 }
541 }
542
543 y -= V_SPACING;
544
545 for (i = 0; i <= row_last; i ++)
546 g_slist_free (rows [i]);
547 g_free (rows);
548
549 return y;
550 }
551
552 static void
do_layout(GdlSwitcher * switcher,GtkAllocation * allocation)553 do_layout (GdlSwitcher *switcher, GtkAllocation* allocation)
554 {
555
556 GtkAllocation child_allocation;
557 int y;
558
559 if (switcher->priv->show) {
560 y = layout_buttons (switcher, allocation);
561 if (y < 0) /* Layout did not happen and a resize was requested */
562 return;
563 }
564 else
565 y = allocation->y + allocation->height;
566
567 /* Place the parent widget. */
568 child_allocation.x = allocation->x;
569 child_allocation.y = allocation->y;
570 child_allocation.width = allocation->width;
571 child_allocation.height = y - allocation->y;
572
573 GTK_WIDGET_CLASS (gdl_switcher_parent_class)->size_allocate (GTK_WIDGET (switcher), &child_allocation);
574 }
575
576 /* GtkContainer methods. */
577
578 static void
gdl_switcher_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,void * callback_data)579 gdl_switcher_forall (GtkContainer *container, gboolean include_internals,
580 GtkCallback callback, void *callback_data)
581 {
582 GdlSwitcher *switcher =
583 GDL_SWITCHER (container);
584 GSList *p;
585
586 GTK_CONTAINER_CLASS (gdl_switcher_parent_class)->forall (GTK_CONTAINER (switcher),
587 include_internals,
588 callback, callback_data);
589 if (include_internals) {
590 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
591 GtkWidget *widget = ((Button *) p->data)->button_widget;
592 (* callback) (widget, callback_data);
593 }
594 }
595 }
596
597 static void
gdl_switcher_remove(GtkContainer * container,GtkWidget * widget)598 gdl_switcher_remove (GtkContainer *container, GtkWidget *widget)
599 {
600 gint switcher_id;
601 GdlSwitcher *switcher =
602 GDL_SWITCHER (container);
603 GSList *p;
604
605 switcher_id = gdl_switcher_get_page_id (widget);
606 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
607 Button *b = (Button *) p->data;
608
609 if (b->id == switcher_id) {
610 gtk_widget_unparent (b->button_widget);
611 switcher->priv->buttons =
612 g_slist_remove_link (switcher->priv->buttons, p);
613 button_free (b);
614 gtk_widget_queue_resize (GTK_WIDGET (switcher));
615 break;
616 }
617 }
618 gdl_switcher_update_lone_button_visibility (switcher);
619 GTK_CONTAINER_CLASS (gdl_switcher_parent_class)->remove (GTK_CONTAINER (switcher), widget);
620 }
621
622 /* GtkWidget methods. */
623
624 static void
gdl_switcher_get_preferred_width(GtkWidget * widget,gint * minimum,gint * natural)625 gdl_switcher_get_preferred_width (GtkWidget *widget, gint *minimum, gint *natural)
626 {
627 GdlSwitcher *switcher = GDL_SWITCHER (widget);
628 GSList *p;
629 gint button_height = 0;
630
631 GTK_WIDGET_CLASS (gdl_switcher_parent_class)->get_preferred_width (GTK_WIDGET (switcher), minimum, natural);
632
633 if (!switcher->priv->show)
634 return;
635
636 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
637 Button *button = p->data;
638
639 if (gtk_widget_get_visible (button->button_widget)) {
640 gint min, nat;
641
642 gtk_widget_get_preferred_width(button->button_widget, &min, &nat);
643 *minimum = MAX (*minimum, min + 2 * H_PADDING);
644 *natural = MAX (*natural, nat + 2 * H_PADDING);
645 }
646 }
647 }
648
649 static void
gdl_switcher_get_preferred_height(GtkWidget * widget,gint * minimum,gint * natural)650 gdl_switcher_get_preferred_height (GtkWidget *widget, gint *minimum, gint *natural)
651 {
652 GdlSwitcher *switcher = GDL_SWITCHER (widget);
653 GSList *p;
654 gint button_min = 0;
655 gint button_nat = 0;
656
657 GTK_WIDGET_CLASS (gdl_switcher_parent_class)->get_preferred_height (GTK_WIDGET (switcher), minimum, natural);
658
659 if (!switcher->priv->show)
660 return;
661
662 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
663 Button *button = p->data;
664
665 if (gtk_widget_get_visible (button->button_widget)) {
666 gint min, nat;
667
668 gtk_widget_get_preferred_height (button->button_widget, &min, &nat);
669 button_min = MAX (button_min, min + 2 * V_PADDING);
670 button_nat = MAX (button_nat, nat + 2 * V_PADDING);
671 }
672 }
673
674 if (switcher->priv->buttons_height_request > 0) {
675 *minimum += switcher->priv->buttons_height_request;
676 *natural += switcher->priv->buttons_height_request;
677 } else {
678 *minimum += button_min + V_PADDING;
679 *natural += button_nat + V_PADDING;
680 }
681 }
682
683 static void
gdl_switcher_size_allocate(GtkWidget * widget,GtkAllocation * allocation)684 gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
685 {
686 do_layout (GDL_SWITCHER (widget), allocation);
687 gtk_widget_set_allocation (widget, allocation);
688 }
689
690 static gint
gdl_switcher_draw(GtkWidget * widget,cairo_t * cr)691 gdl_switcher_draw (GtkWidget *widget, cairo_t *cr)
692 {
693 GSList *p;
694 GdlSwitcher *switcher = GDL_SWITCHER (widget);
695 if (switcher->priv->show) {
696 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
697 GtkWidget *button = ((Button *) p->data)->button_widget;
698 gtk_container_propagate_draw (GTK_CONTAINER (widget),
699 button, cr);
700 }
701 }
702 return GTK_WIDGET_CLASS (gdl_switcher_parent_class)->draw (widget, cr);
703 }
704
705 static void
gdl_switcher_map(GtkWidget * widget)706 gdl_switcher_map (GtkWidget *widget)
707 {
708 GSList *p;
709 GdlSwitcher *switcher = GDL_SWITCHER (widget);
710
711 if (switcher->priv->show) {
712 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
713 GtkWidget *button = ((Button *) p->data)->button_widget;
714 if (gtk_widget_get_visible (button) &&
715 !gtk_widget_get_mapped (button))
716 gtk_widget_map (button);
717 }
718 }
719 GTK_WIDGET_CLASS (gdl_switcher_parent_class)->map (widget);
720 }
721
722 /* GObject methods. */
723
724 static void
gdl_switcher_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)725 gdl_switcher_set_property (GObject *object,
726 guint prop_id,
727 const GValue *value,
728 GParamSpec *pspec)
729 {
730 GdlSwitcher *switcher = GDL_SWITCHER (object);
731
732 switch (prop_id) {
733 case PROP_SWITCHER_STYLE:
734 gdl_switcher_set_style (switcher, g_value_get_enum (value));
735 break;
736 case PROP_TAB_POS:
737 gdl_switcher_set_tab_pos (switcher, g_value_get_enum (value));
738 break;
739 case PROP_TAB_REORDERABLE:
740 gdl_switcher_set_tab_reorderable (switcher, g_value_get_boolean (value));
741 break;
742 default:
743 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
744 break;
745 }
746 }
747
748 static void
gdl_switcher_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)749 gdl_switcher_get_property (GObject *object,
750 guint prop_id,
751 GValue *value,
752 GParamSpec *pspec)
753 {
754 GdlSwitcher *switcher = GDL_SWITCHER (object);
755
756 switch (prop_id) {
757 case PROP_SWITCHER_STYLE:
758 g_value_set_enum (value, gdl_switcher_get_style (switcher));
759 break;
760 case PROP_TAB_POS:
761 g_value_set_enum (value, switcher->priv->tab_pos);
762 break;
763 case PROP_TAB_REORDERABLE:
764 g_value_set_enum (value, switcher->priv->tab_reorderable);
765 break;
766 default:
767 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
768 break;
769 }
770 }
771
772 static void
gdl_switcher_dispose(GObject * object)773 gdl_switcher_dispose (GObject *object)
774 {
775 GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
776
777 #if HAVE_GNOME
778 GConfClient *gconf_client = gconf_client_get_default ();
779
780 if (priv->style_changed_id) {
781 gconf_client_notify_remove (gconf_client, priv->style_changed_id);
782 priv->style_changed_id = 0;
783 }
784 g_object_unref (gconf_client);
785 #endif
786
787 g_slist_free_full (priv->buttons, (GDestroyNotify)button_free);
788 priv->buttons = NULL;
789
790 G_OBJECT_CLASS (gdl_switcher_parent_class)->dispose (object);
791 }
792
793 static void
gdl_switcher_finalize(GObject * object)794 gdl_switcher_finalize (GObject *object)
795 {
796 G_OBJECT_CLASS (gdl_switcher_parent_class)->finalize (object);
797 }
798
799 /* Signal handlers */
800
801 static void
gdl_switcher_notify_cb(GObject * g_object,GParamSpec * pspec,GdlSwitcher * switcher)802 gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec,
803 GdlSwitcher *switcher)
804 {
805 }
806
807 static void
gdl_switcher_switch_page_cb(GtkNotebook * nb,GtkWidget * page_widget,gint page_num,GdlSwitcher * switcher)808 gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkWidget *page_widget,
809 gint page_num, GdlSwitcher *switcher)
810 {
811 gint switcher_id;
812
813 /* Change switcher button */
814 switcher_id = gdl_switcher_get_page_id (page_widget);
815 gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
816 }
817
818 static void
gdl_switcher_page_added_cb(GtkNotebook * nb,GtkWidget * page,gint page_num,GdlSwitcher * switcher)819 gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page,
820 gint page_num, GdlSwitcher *switcher)
821 {
822 gint switcher_id;
823
824 (void)nb;
825 (void)page_num;
826 switcher_id = gdl_switcher_get_page_id (page);
827
828 gdl_switcher_add_button (GDL_SWITCHER (switcher), NULL, NULL, NULL, NULL,
829 switcher_id, page);
830 gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
831 }
832
833 static void
gdl_switcher_select_page(GdlSwitcher * switcher,gint id)834 gdl_switcher_select_page (GdlSwitcher *switcher, gint id)
835 {
836 GList *children, *node;
837 children = gtk_container_get_children (GTK_CONTAINER (switcher));
838 node = children;
839 while (node)
840 {
841 gint switcher_id;
842 switcher_id = gdl_switcher_get_page_id (GTK_WIDGET (node->data));
843 if (switcher_id == id)
844 {
845 gint page_num;
846 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (switcher),
847 GTK_WIDGET (node->data));
848 g_signal_handlers_block_by_func (switcher,
849 gdl_switcher_switch_page_cb,
850 switcher);
851 gtk_notebook_set_current_page (GTK_NOTEBOOK (switcher), page_num);
852 g_signal_handlers_unblock_by_func (switcher,
853 gdl_switcher_switch_page_cb,
854 switcher);
855 break;
856 }
857 node = g_list_next (node);
858 }
859 g_list_free (children);
860 }
861
862 /* Initialization. */
863
864 static void
gdl_switcher_class_init(GdlSwitcherClass * klass)865 gdl_switcher_class_init (GdlSwitcherClass *klass)
866 {
867 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
868 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
869 GObjectClass *object_class = G_OBJECT_CLASS (klass);
870 static const gchar button_style[] =
871 "* {\n"
872 "outline-width : 1px;\n"
873 "padding: 0;\n"
874 "}";
875
876 container_class->forall = gdl_switcher_forall;
877 container_class->remove = gdl_switcher_remove;
878
879 widget_class->get_preferred_width = gdl_switcher_get_preferred_width;
880 widget_class->get_preferred_height = gdl_switcher_get_preferred_height;
881 widget_class->size_allocate = gdl_switcher_size_allocate;
882 widget_class->draw = gdl_switcher_draw;
883 widget_class->map = gdl_switcher_map;
884
885 object_class->dispose = gdl_switcher_dispose;
886 object_class->finalize = gdl_switcher_finalize;
887 object_class->set_property = gdl_switcher_set_property;
888 object_class->get_property = gdl_switcher_get_property;
889
890 g_object_class_install_property (
891 object_class, PROP_SWITCHER_STYLE,
892 g_param_spec_enum ("switcher-style", _("Switcher Style"),
893 _("Switcher buttons style"),
894 GDL_TYPE_SWITCHER_STYLE,
895 GDL_SWITCHER_STYLE_BOTH,
896 G_PARAM_READWRITE));
897
898 g_object_class_install_property (
899 object_class, PROP_TAB_POS,
900 g_param_spec_enum ("tab-pos", _("Tab Position"),
901 _("Which side of the notebook holds the tabs"),
902 GTK_TYPE_POSITION_TYPE,
903 GTK_POS_BOTTOM,
904 G_PARAM_READWRITE));
905
906 g_object_class_install_property (
907 object_class, PROP_TAB_REORDERABLE,
908 g_param_spec_boolean ("tab-reorderable", _("Tab reorderable"),
909 _("Whether the tab is reorderable by user action"),
910 FALSE,
911 G_PARAM_READWRITE));
912
913 g_type_class_add_private (object_class, sizeof (GdlSwitcherPrivate));
914
915 /* set the style */
916 klass->priv = G_TYPE_CLASS_GET_PRIVATE (klass, GDL_TYPE_SWITCHER, GdlSwitcherClassPrivate);
917
918 klass->priv->css = gtk_css_provider_new ();
919 gtk_css_provider_load_from_data (klass->priv->css, button_style, -1, NULL);
920 }
921
922 static void
gdl_switcher_init(GdlSwitcher * switcher)923 gdl_switcher_init (GdlSwitcher *switcher)
924 {
925 GdlSwitcherPrivate *priv;
926
927 gtk_widget_set_has_window (GTK_WIDGET (switcher), FALSE);
928
929 switcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (switcher,
930 GDL_TYPE_SWITCHER,
931 GdlSwitcherPrivate);
932 priv = switcher->priv;
933
934 priv->show = TRUE;
935 priv->buttons_height_request = -1;
936 priv->tab_pos = GTK_POS_BOTTOM;
937 priv->tab_reorderable = FALSE;
938
939 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (switcher), GTK_POS_BOTTOM);
940 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
941 gtk_notebook_set_show_border (GTK_NOTEBOOK (switcher), FALSE);
942 gdl_switcher_set_style (switcher, GDL_SWITCHER_STYLE_BOTH);
943
944 /* notebook signals */
945 g_signal_connect (switcher, "switch-page",
946 G_CALLBACK (gdl_switcher_switch_page_cb), switcher);
947 g_signal_connect (switcher, "page-added",
948 G_CALLBACK (gdl_switcher_page_added_cb), switcher);
949 g_signal_connect (switcher, "notify::show-tabs",
950 G_CALLBACK (gdl_switcher_notify_cb), switcher);
951 }
952
953 /**
954 * gdl_switcher_new:
955 *
956 * Creates a new notebook widget with no pages.
957 *
958 * Returns: The newly created #GdlSwitcher
959 */
960 GtkWidget *
gdl_switcher_new(void)961 gdl_switcher_new (void)
962 {
963 return g_object_new (gdl_switcher_get_type (), NULL);
964 }
965
966 /**
967 * gdl_switcher_add_button:
968 * @switcher: The #GdlSwitcher to which a button will be added
969 * @label: The label for the button
970 * @tooltips: The tooltip for the button
971 * @stock_id: The stock ID for the button
972 * @pixbuf_icon: The pixbuf to use for the button icon
973 * @switcher_id: The ID of the switcher
974 * @page: The notebook page
975 *
976 * Adds a button to a #GdlSwitcher. The button icon is taken preferentially
977 * from the @stock_id parameter. If this is %NULL, then the @pixbuf_icon
978 * parameter is used. Failing that, the %GTK_STOCK_NEW stock icon is used.
979 * The text label for the button is specified using the @label parameter. If
980 * it is %NULL then a default incrementally numbered label is used instead.
981 */
982 static void
gdl_switcher_add_button(GdlSwitcher * switcher,const gchar * label,const gchar * tooltips,const gchar * stock_id,GdkPixbuf * pixbuf_icon,gint switcher_id,GtkWidget * page)983 gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label,
984 const gchar *tooltips, const gchar *stock_id,
985 GdkPixbuf *pixbuf_icon,
986 gint switcher_id, GtkWidget* page)
987 {
988 GtkWidget *event_box;
989 GtkWidget *button_widget;
990 GtkWidget *hbox;
991 GtkWidget *icon_widget;
992 GtkWidget *label_widget;
993 GtkWidget *arrow;
994
995 button_widget = gtk_toggle_button_new ();
996 gtk_button_set_relief (GTK_BUTTON(button_widget), GTK_RELIEF_HALF);
997 if (switcher->priv->show && gtk_widget_get_visible (page))
998 gtk_widget_show (button_widget);
999 g_signal_connect (button_widget, "toggled",
1000 G_CALLBACK (button_toggled_callback),
1001 switcher);
1002 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3);
1003 gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
1004 gtk_container_add (GTK_CONTAINER (button_widget), hbox);
1005 gtk_widget_show (hbox);
1006
1007 if (stock_id) {
1008 icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
1009 } else if (pixbuf_icon) {
1010 icon_widget = gtk_image_new_from_pixbuf (pixbuf_icon);
1011 } else {
1012 icon_widget = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU);
1013 }
1014
1015 gtk_widget_show (icon_widget);
1016
1017 if (!label) {
1018 gchar *text = g_strdup_printf ("Item %d", switcher_id);
1019 label_widget = gtk_label_new (text);
1020 g_free (text);
1021 } else {
1022 label_widget = gtk_label_new (label);
1023 }
1024 gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
1025 gtk_widget_show (label_widget);
1026
1027
1028 gtk_widget_set_tooltip_text (button_widget,
1029 tooltips);
1030
1031 switch (INTERNAL_MODE (switcher)) {
1032 case GDL_SWITCHER_STYLE_TEXT:
1033 gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
1034 break;
1035 case GDL_SWITCHER_STYLE_ICON:
1036 gtk_box_pack_start (GTK_BOX (hbox), icon_widget, TRUE, TRUE, 0);
1037 break;
1038 case GDL_SWITCHER_STYLE_BOTH:
1039 default:
1040 gtk_box_pack_start (GTK_BOX (hbox), icon_widget, FALSE, TRUE, 0);
1041 gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
1042 break;
1043 }
1044 arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_NONE);
1045 gtk_widget_show (arrow);
1046 gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
1047
1048 switcher->priv->buttons =
1049 g_slist_append (switcher->priv->buttons,
1050 button_new (button_widget, label_widget,
1051 icon_widget,
1052 arrow, hbox, switcher_id, page));
1053
1054 gtk_widget_set_parent (button_widget, GTK_WIDGET (switcher));
1055 gdl_switcher_update_lone_button_visibility (switcher);
1056 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1057 }
1058
1059 #if 0
1060 static void
1061 gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id)
1062 {
1063 GSList *p;
1064
1065 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
1066 Button *button = p->data;
1067
1068 if (button->id == switcher_id)
1069 {
1070 gtk_container_remove (GTK_CONTAINER (switcher),
1071 button->button_widget);
1072 break;
1073 }
1074 }
1075 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1076 }
1077 #endif
1078
1079 static void
gdl_switcher_select_button(GdlSwitcher * switcher,gint switcher_id)1080 gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id)
1081 {
1082 update_buttons (switcher, switcher_id);
1083
1084 /* Select the notebook page associated with this button */
1085 gdl_switcher_select_page (switcher, switcher_id);
1086 }
1087
1088
1089 /**
1090 * gdl_switcher_insert_page:
1091 * @switcher: The switcher to which a page will be added
1092 * @page: The page to add to the switcher
1093 * @tab_widget: The to add to the switcher
1094 * @label: The label text for the button
1095 * @tooltips: The tooltip for the button
1096 * @stock_id: The stock ID for the button icon
1097 * @pixbuf_icon: The pixbuf to use for the button icon
1098 * @position: The position at which to create the page
1099 *
1100 * Adds a page to a #GdlSwitcher. A button is created in the switcher, with its
1101 * icon taken preferentially from the @stock_id parameter. If this parameter is
1102 * %NULL, then the @pixbuf_icon parameter is used. Failing that, the
1103 * %GTK_STOCK_NEW stock icon is used. The text label for the button is specified
1104 * using the @label parameter. If it is %NULL then a default incrementally
1105 * numbered label is used instead.
1106 *
1107 * Returns: The index (starting from 0) of the appended page in the notebook, or -1 if function fails
1108 */
1109 gint
gdl_switcher_insert_page(GdlSwitcher * switcher,GtkWidget * page,GtkWidget * tab_widget,const gchar * label,const gchar * tooltips,const gchar * stock_id,GdkPixbuf * pixbuf_icon,gint position)1110 gdl_switcher_insert_page (GdlSwitcher *switcher, GtkWidget *page,
1111 GtkWidget *tab_widget, const gchar *label,
1112 const gchar *tooltips, const gchar *stock_id,
1113 GdkPixbuf *pixbuf_icon, gint position)
1114 {
1115 GtkNotebook *notebook = GTK_NOTEBOOK (switcher);
1116 gint ret_position;
1117 gint switcher_id;
1118 g_signal_handlers_block_by_func (switcher,
1119 gdl_switcher_page_added_cb,
1120 switcher);
1121
1122 if (!tab_widget) {
1123 tab_widget = gtk_label_new (label);
1124 if (gtk_widget_get_visible (page)) gtk_widget_show (tab_widget);
1125 }
1126 switcher_id = gdl_switcher_get_page_id (page);
1127 gdl_switcher_add_button (switcher, label, tooltips, stock_id, pixbuf_icon, switcher_id, page);
1128
1129 ret_position = gtk_notebook_insert_page (notebook, page,
1130 tab_widget, position);
1131 gtk_notebook_set_tab_reorderable (notebook, page,
1132 switcher->priv->tab_reorderable);
1133 g_signal_handlers_unblock_by_func (switcher,
1134 gdl_switcher_page_added_cb,
1135 switcher);
1136
1137 return ret_position;
1138 }
1139
1140 static void
set_switcher_style_toolbar(GdlSwitcher * switcher,GdlSwitcherStyle switcher_style)1141 set_switcher_style_toolbar (GdlSwitcher *switcher,
1142 GdlSwitcherStyle switcher_style)
1143 {
1144 GSList *p;
1145
1146 if (switcher_style == GDL_SWITCHER_STYLE_NONE
1147 || switcher_style == GDL_SWITCHER_STYLE_TABS)
1148 return;
1149
1150 if (switcher_style == GDL_SWITCHER_STYLE_TOOLBAR)
1151 switcher_style = GDL_SWITCHER_STYLE_BOTH;
1152
1153 if (switcher_style == INTERNAL_MODE (switcher))
1154 return;
1155
1156 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
1157
1158 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
1159 Button *button = p->data;
1160
1161 gtk_container_remove (GTK_CONTAINER (button->hbox), button->arrow);
1162
1163 if (gtk_widget_get_parent (button->icon))
1164 gtk_container_remove (GTK_CONTAINER (button->hbox), button->icon);
1165 if (gtk_widget_get_parent (button->label))
1166 gtk_container_remove (GTK_CONTAINER (button->hbox), button->label);
1167
1168 switch (switcher_style) {
1169 case GDL_SWITCHER_STYLE_TEXT:
1170 gtk_box_pack_start (GTK_BOX (button->hbox), button->label,
1171 TRUE, TRUE, 0);
1172 gtk_widget_show (button->label);
1173 break;
1174
1175 case GDL_SWITCHER_STYLE_ICON:
1176 gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
1177 TRUE, TRUE, 0);
1178 gtk_widget_show (button->icon);
1179 break;
1180
1181 case GDL_SWITCHER_STYLE_BOTH:
1182 gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
1183 FALSE, TRUE, 0);
1184 gtk_box_pack_start (GTK_BOX (button->hbox), button->label,
1185 TRUE, TRUE, 0);
1186 gtk_widget_show (button->icon);
1187 gtk_widget_show (button->label);
1188 break;
1189
1190 default:
1191 break;
1192 }
1193
1194 gtk_box_pack_start (GTK_BOX (button->hbox), button->arrow,
1195 FALSE, FALSE, 0);
1196 }
1197
1198 gdl_switcher_set_show_buttons (switcher, TRUE);
1199 }
1200
1201 static void
gdl_switcher_set_style(GdlSwitcher * switcher,GdlSwitcherStyle switcher_style)1202 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
1203 {
1204 if (switcher->priv->switcher_style == switcher_style)
1205 return;
1206
1207 if (switcher_style == GDL_SWITCHER_STYLE_NONE) {
1208 gdl_switcher_set_show_buttons (switcher, FALSE);
1209 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
1210 }
1211 else if (switcher_style == GDL_SWITCHER_STYLE_TABS) {
1212 gdl_switcher_set_show_buttons (switcher, FALSE);
1213 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), TRUE);
1214 }
1215 else
1216 set_switcher_style_toolbar (switcher, switcher_style);
1217
1218 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1219 switcher->priv->switcher_style = switcher_style;
1220 }
1221
1222 static void
gdl_switcher_set_show_buttons(GdlSwitcher * switcher,gboolean show)1223 gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show)
1224 {
1225 GSList *p;
1226
1227 if (switcher->priv->show == show)
1228 return;
1229
1230 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
1231 Button *button = p->data;
1232
1233 if (show && gtk_widget_get_visible (button->page))
1234 gtk_widget_show (button->button_widget);
1235 else
1236 gtk_widget_hide (button->button_widget);
1237 }
1238 gdl_switcher_update_lone_button_visibility (switcher);
1239 switcher->priv->show = show;
1240
1241 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1242 }
1243
1244 static GdlSwitcherStyle
gdl_switcher_get_style(GdlSwitcher * switcher)1245 gdl_switcher_get_style (GdlSwitcher *switcher)
1246 {
1247 if (!switcher->priv->show)
1248 return GDL_SWITCHER_STYLE_TABS;
1249 return switcher->priv->switcher_style;
1250 }
1251
1252 static void
gdl_switcher_set_tab_pos(GdlSwitcher * switcher,GtkPositionType pos)1253 gdl_switcher_set_tab_pos (GdlSwitcher *switcher, GtkPositionType pos)
1254 {
1255 if (switcher->priv->tab_pos == pos)
1256 return;
1257
1258 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (switcher), pos);
1259
1260 switcher->priv->tab_pos = pos;
1261 }
1262
1263 static void
gdl_switcher_set_tab_reorderable(GdlSwitcher * switcher,gboolean reorderable)1264 gdl_switcher_set_tab_reorderable (GdlSwitcher *switcher, gboolean reorderable)
1265 {
1266 GList *children, *l;
1267
1268 if (switcher->priv->tab_reorderable == reorderable)
1269 return;
1270
1271 children = gtk_container_get_children (GTK_CONTAINER (switcher));
1272 for (l = children; l != NULL; l->next) {
1273 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (switcher),
1274 GTK_WIDGET (l->data),
1275 reorderable);
1276 }
1277 g_list_free (children);
1278
1279 switcher->priv->tab_reorderable = reorderable;
1280 }
1281