43768bbdfe0b9f2f7ab134c0e0379c4ea930fcf2
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 program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU General Public
9 * License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
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 */
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
31 #include "gdl-i18n.h"
32 #include "gdl-switcher.h"
33 #include "gdl-tools.h"
34 #include "libgdlmarshal.h"
35 #include "libgdltypebuiltins.h"
37 #include <gtk/gtk.h>
38 #include <gtk/gtkhbox.h>
39 #include <gtk/gtkimage.h>
40 #include <gtk/gtklabel.h>
41 #include <gtk/gtktogglebutton.h>
43 #if HAVE_GNOME
44 #include <gconf/gconf-client.h>
45 #endif
47 static void gdl_switcher_set_property (GObject *object,
48 guint prop_id,
49 const GValue *value,
50 GParamSpec *pspec);
51 static void gdl_switcher_get_property (GObject *object,
52 guint prop_id,
53 GValue *value,
54 GParamSpec *pspec);
56 static void gdl_switcher_add_button (GdlSwitcher *switcher,
57 const gchar *label,
58 const gchar *tooltips,
59 const gchar *stock_id,
60 const GdkPixbuf *pixbuf_icon,
61 gint switcher_id);
62 static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id);
63 static void gdl_switcher_select_page (GdlSwitcher *switcher, gint switcher_id);
64 static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id);
65 static void gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show);
66 static void gdl_switcher_set_style (GdlSwitcher *switcher,
67 GdlSwitcherStyle switcher_style);
68 static GdlSwitcherStyle gdl_switcher_get_style (GdlSwitcher *switcher);
70 enum {
71 PROP_0,
72 PROP_SWITCHER_STYLE
73 };
75 typedef struct {
76 GtkWidget *button_widget;
77 GtkWidget *label;
78 GtkWidget *icon;
79 GtkWidget *arrow;
80 GtkWidget *hbox;
81 GtkTooltips *tooltips;
82 int id;
83 } Button;
85 struct _GdlSwitcherPrivate {
86 GdlSwitcherStyle switcher_style;
87 GdlSwitcherStyle toolbar_style;
89 gboolean show;
90 GSList *buttons;
92 guint style_changed_id;
93 gint buttons_height_request;
94 gboolean in_toggle;
95 };
97 GDL_CLASS_BOILERPLATE (GdlSwitcher, gdl_switcher, GtkNotebook, GTK_TYPE_NOTEBOOK)
99 #define INTERNAL_MODE(switcher) (switcher->priv->switcher_style == \
100 GDL_SWITCHER_STYLE_TOOLBAR ? switcher->priv->toolbar_style : \
101 switcher->priv->switcher_style)
103 #define H_PADDING 2
104 #define V_PADDING 2
106 /* Utility functions. */
108 static Button *
109 button_new (GtkWidget *button_widget, GtkWidget *label, GtkWidget *icon,
110 GtkTooltips *tooltips, GtkWidget *arrow, GtkWidget *hbox, int id)
111 {
112 Button *button = g_new (Button, 1);
114 button->button_widget = button_widget;
115 button->label = label;
116 button->icon = icon;
117 button->arrow = arrow;
118 button->hbox = hbox;
119 button->tooltips = tooltips;
120 button->id = id;
122 g_object_ref (button_widget);
123 g_object_ref (label);
124 g_object_ref (icon);
125 g_object_ref (arrow);
126 g_object_ref (hbox);
127 g_object_ref (tooltips);
129 return button;
130 }
132 static void
133 button_free (Button *button)
134 {
135 g_object_unref (button->button_widget);
136 g_object_unref (button->label);
137 g_object_unref (button->icon);
138 g_object_unref (button->hbox);
139 g_object_unref (button->tooltips);
140 g_free (button);
141 }
143 static gint
144 gdl_switcher_get_page_id (GtkWidget *widget)
145 {
146 static gint switcher_id_count = 0;
147 gint switcher_id;
148 switcher_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
149 "__switcher_id"));
150 if (switcher_id <= 0) {
151 switcher_id = ++switcher_id_count;
152 g_object_set_data (G_OBJECT (widget), "__switcher_id",
153 GINT_TO_POINTER (switcher_id));
154 }
155 return switcher_id;
156 }
158 static void
159 update_buttons (GdlSwitcher *switcher, int new_selected_id)
160 {
161 GSList *p;
163 switcher->priv->in_toggle = TRUE;
165 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
166 Button *button = p->data;
168 if (button->id == new_selected_id) {
169 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
170 (button->button_widget), TRUE);
171 gtk_widget_set_sensitive (button->arrow, TRUE);
172 } else {
173 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
174 (button->button_widget), FALSE);
175 gtk_widget_set_sensitive (button->arrow, FALSE);
176 }
177 }
179 switcher->priv->in_toggle = FALSE;
180 }
182 /* Callbacks. */
184 static void
185 button_toggled_callback (GtkToggleButton *toggle_button,
186 GdlSwitcher *switcher)
187 {
188 int id = 0;
189 gboolean is_active = FALSE;
190 GSList *p;
192 if (switcher->priv->in_toggle)
193 return;
195 switcher->priv->in_toggle = TRUE;
197 if (gtk_toggle_button_get_active (toggle_button))
198 is_active = TRUE;
200 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
201 Button *button = p->data;
203 if (button->button_widget != GTK_WIDGET (toggle_button)) {
204 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
205 (button->button_widget), FALSE);
206 gtk_widget_set_sensitive (button->arrow, FALSE);
207 } else {
208 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
209 (button->button_widget), TRUE);
210 gtk_widget_set_sensitive (button->arrow, TRUE);
211 id = button->id;
212 }
213 }
215 switcher->priv->in_toggle = FALSE;
217 if (is_active)
218 {
219 gdl_switcher_select_page (switcher, id);
220 }
221 }
223 /* Returns -1 if layout didn't happen because a resize request was queued */
224 static int
225 layout_buttons (GdlSwitcher *switcher)
226 {
227 GtkRequisition client_requisition;
228 GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
229 GdlSwitcherStyle switcher_style;
230 gboolean icons_only;
231 int num_btns = g_slist_length (switcher->priv->buttons);
232 int btns_per_row;
233 GSList **rows, *p;
234 Button *button;
235 int row_number;
236 int max_btn_width = 0, max_btn_height = 0;
237 int optimal_layout_width = 0;
238 int row_last;
239 int x, y;
240 int i;
241 int rows_count;
242 int last_buttons_height;
244 last_buttons_height = switcher->priv->buttons_height_request;
246 GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
247 (GTK_WIDGET (switcher), &client_requisition));
249 y = allocation->y + allocation->height - V_PADDING - 1;
251 if (num_btns == 0)
252 return y;
254 switcher_style = INTERNAL_MODE (switcher);
255 icons_only = (switcher_style == GDL_SWITCHER_STYLE_ICON);
257 /* Figure out the max width and height */
258 optimal_layout_width = H_PADDING;
259 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
260 GtkRequisition requisition;
262 button = p->data;
263 gtk_widget_size_request (GTK_WIDGET (button->button_widget),
264 &requisition);
265 optimal_layout_width += requisition.width + H_PADDING;
266 max_btn_height = MAX (max_btn_height, requisition.height);
267 max_btn_width = MAX (max_btn_width, requisition.width);
268 }
270 /* Figure out how many rows and columns we'll use. */
271 btns_per_row = allocation->width / (max_btn_width + H_PADDING);
273 /* If all the buttons could fit in the single row, have it so */
274 if (allocation->width >= optimal_layout_width)
275 {
276 btns_per_row = num_btns;
277 }
278 if (!icons_only) {
279 /* If using text buttons, we want to try to have a
280 * completely filled-in grid, but if we can't, we want
281 * the odd row to have just a single button.
282 */
283 while (num_btns % btns_per_row > 1)
284 btns_per_row--;
285 }
287 rows_count = num_btns / btns_per_row;
288 if (num_btns % btns_per_row != 0)
289 rows_count++;
291 /* Assign buttons to rows */
292 rows = g_new0 (GSList *, rows_count);
294 if (!icons_only && num_btns % btns_per_row != 0) {
295 button = switcher->priv->buttons->data;
296 rows [0] = g_slist_append (rows [0], button->button_widget);
298 p = switcher->priv->buttons->next;
299 row_number = p ? 1 : 0;
300 } else {
301 p = switcher->priv->buttons;
302 row_number = 0;
303 }
305 for (; p != NULL; p = p->next) {
306 button = p->data;
308 if (g_slist_length (rows [row_number]) == btns_per_row)
309 row_number ++;
311 rows [row_number] = g_slist_append (rows [row_number],
312 button->button_widget);
313 }
315 row_last = row_number;
317 /* If there are more than 1 row of buttons, save the current height
318 * requirement for subsequent size requests.
319 */
320 if (row_last > 0)
321 {
322 switcher->priv->buttons_height_request =
323 (row_last + 1) * (max_btn_height + V_PADDING) + 1;
324 } else { /* Otherwize clear it */
325 if (last_buttons_height >= 0) {
327 switcher->priv->buttons_height_request = -1;
328 }
329 }
331 /* If it turns out that we now require smaller height for the buttons
332 * than it was last time, make a resize request to ensure our
333 * size requisition is properly communicated to the parent (otherwise
334 * parent tend to keep assuming the older size).
335 */
336 if (last_buttons_height > switcher->priv->buttons_height_request)
337 {
338 gtk_widget_queue_resize (GTK_WIDGET (switcher));
339 return -1;
340 }
342 /* Layout the buttons. */
343 for (i = row_last; i >= 0; i --) {
344 int len, extra_width;
346 y -= max_btn_height;
348 /* Check for possible size over flow (taking into account client
349 * requisition
350 */
351 if (y < (allocation->y + client_requisition.height)) {
352 /* We have an overflow: Insufficient allocation */
353 if (last_buttons_height < switcher->priv->buttons_height_request) {
354 /* Request for a new resize */
355 gtk_widget_queue_resize (GTK_WIDGET (switcher));
356 return -1;
357 }
358 }
359 x = H_PADDING + allocation->x;
360 len = g_slist_length (rows[i]);
361 if (switcher_style == GDL_SWITCHER_STYLE_TEXT ||
362 switcher_style == GDL_SWITCHER_STYLE_BOTH)
363 extra_width = (allocation->width - (len * max_btn_width )
364 - (len * H_PADDING)) / len;
365 else
366 extra_width = 0;
367 for (p = rows [i]; p != NULL; p = p->next) {
368 GtkAllocation child_allocation;
370 child_allocation.x = x;
371 child_allocation.y = y;
372 if (rows_count == 1 && row_number == 0)
373 {
374 GtkRequisition child_requisition;
375 gtk_widget_size_request (GTK_WIDGET (p->data),
376 &child_requisition);
377 child_allocation.width = child_requisition.width;
378 }
379 else
380 {
381 child_allocation.width = max_btn_width + extra_width;
382 }
383 child_allocation.height = max_btn_height;
385 gtk_widget_size_allocate (GTK_WIDGET (p->data), &child_allocation);
387 x += child_allocation.width + H_PADDING;
388 }
390 y -= V_PADDING;
391 }
393 for (i = 0; i <= row_last; i ++)
394 g_slist_free (rows [i]);
395 g_free (rows);
397 return y;
398 }
400 static void
401 do_layout (GdlSwitcher *switcher)
402 {
403 GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
404 GtkAllocation child_allocation;
405 int y;
407 if (switcher->priv->show) {
408 y = layout_buttons (switcher);
409 if (y < 0) /* Layout did not happen and a resize was requested */
410 return;
411 }
412 else
413 y = allocation->y + allocation->height;
415 /* Place the parent widget. */
416 child_allocation.x = allocation->x;
417 child_allocation.y = allocation->y;
418 child_allocation.width = allocation->width;
419 child_allocation.height = y - allocation->y;
421 GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate,
422 (GTK_WIDGET (switcher), &child_allocation));
423 }
425 /* GtkContainer methods. */
427 static void
428 gdl_switcher_forall (GtkContainer *container, gboolean include_internals,
429 GtkCallback callback, void *callback_data)
430 {
431 GdlSwitcher *switcher =
432 GDL_SWITCHER (container);
433 GSList *p;
435 GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall,
436 (GTK_CONTAINER (switcher), include_internals,
437 callback, callback_data));
438 if (include_internals) {
439 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
440 GtkWidget *widget = ((Button *) p->data)->button_widget;
441 (* callback) (widget, callback_data);
442 }
443 }
444 }
446 static void
447 gdl_switcher_remove (GtkContainer *container, GtkWidget *widget)
448 {
449 gint switcher_id;
450 GdlSwitcher *switcher =
451 GDL_SWITCHER (container);
452 GSList *p;
454 switcher_id = gdl_switcher_get_page_id (widget);
455 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
456 Button *b = (Button *) p->data;
458 if (b->id == switcher_id) {
459 gtk_widget_unparent (b->button_widget);
460 switcher->priv->buttons =
461 g_slist_remove_link (switcher->priv->buttons, p);
462 button_free (b);
463 gtk_widget_queue_resize (GTK_WIDGET (switcher));
464 break;
465 }
466 }
467 GDL_CALL_PARENT (GTK_CONTAINER_CLASS, remove,
468 (GTK_CONTAINER (switcher), widget));
469 }
471 /* GtkWidget methods. */
473 static void
474 gdl_switcher_size_request (GtkWidget *widget, GtkRequisition *requisition)
475 {
476 GdlSwitcher *switcher = GDL_SWITCHER (widget);
477 GSList *p;
478 gint button_height = 0;
480 GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
481 (GTK_WIDGET (switcher), requisition));
483 if (!switcher->priv->show)
484 return;
486 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
487 gint button_width;
488 Button *button = p->data;
489 GtkRequisition button_requisition;
491 gtk_widget_size_request (button->button_widget, &button_requisition);
492 button_width = button_requisition.width + 2 * H_PADDING;
493 requisition->width = MAX (requisition->width, button_width);
494 button_height = MAX (button_height,
495 button_requisition.height + 2 * V_PADDING);
496 }
498 if (switcher->priv->buttons_height_request > 0) {
499 requisition->height += switcher->priv->buttons_height_request;
500 } else {
501 requisition->height += button_height + V_PADDING;
502 }
503 }
505 static void
506 gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
507 {
508 widget->allocation = *allocation;
509 do_layout (GDL_SWITCHER (widget));
510 }
512 static gint
513 gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event)
514 {
515 GSList *p;
516 GdlSwitcher *switcher = GDL_SWITCHER (widget);
517 if (switcher->priv->show) {
518 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
519 GtkWidget *button = ((Button *) p->data)->button_widget;
520 gtk_container_propagate_expose (GTK_CONTAINER (widget),
521 button, event);
522 }
523 }
524 return GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS, expose_event,
525 (widget, event), FALSE);
526 }
528 static void
529 gdl_switcher_map (GtkWidget *widget)
530 {
531 GSList *p;
532 GdlSwitcher *switcher = GDL_SWITCHER (widget);
534 if (switcher->priv->show) {
535 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
536 GtkWidget *button = ((Button *) p->data)->button_widget;
537 gtk_widget_map (button);
538 }
539 }
540 GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
541 }
543 /* GObject methods. */
545 static void
546 gdl_switcher_set_property (GObject *object,
547 guint prop_id,
548 const GValue *value,
549 GParamSpec *pspec)
550 {
551 GdlSwitcher *switcher = GDL_SWITCHER (object);
553 switch (prop_id) {
554 case PROP_SWITCHER_STYLE:
555 gdl_switcher_set_style (switcher, g_value_get_enum (value));
556 break;
557 default:
558 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
559 break;
560 }
561 }
563 static void
564 gdl_switcher_get_property (GObject *object,
565 guint prop_id,
566 GValue *value,
567 GParamSpec *pspec)
568 {
569 GdlSwitcher *switcher = GDL_SWITCHER (object);
571 switch (prop_id) {
572 case PROP_SWITCHER_STYLE:
573 g_value_set_enum (value, gdl_switcher_get_style (switcher));
574 break;
575 default:
576 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
577 break;
578 }
579 }
581 static void
582 gdl_switcher_dispose (GObject *object)
583 {
584 GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
586 #if HAVE_GNOME
587 GConfClient *gconf_client = gconf_client_get_default ();
589 if (priv->style_changed_id) {
590 gconf_client_notify_remove (gconf_client, priv->style_changed_id);
591 priv->style_changed_id = 0;
592 }
593 g_object_unref (gconf_client);
594 #endif
596 g_slist_foreach (priv->buttons, (GFunc) button_free, NULL);
597 g_slist_free (priv->buttons);
598 priv->buttons = NULL;
600 GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
601 }
603 static void
604 gdl_switcher_finalize (GObject *object)
605 {
606 GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
608 g_free (priv);
610 GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
611 }
613 /* Signal handlers */
615 static void
616 gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec,
617 GdlSwitcher *switcher)
618 {
619 gboolean show_tabs;
620 g_return_if_fail (switcher != NULL && GDL_IS_SWITCHER (switcher));
621 show_tabs = gtk_notebook_get_show_tabs (GTK_NOTEBOOK (switcher));
622 gdl_switcher_set_show_buttons (switcher, !show_tabs);
623 }
625 static void
626 gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page,
627 gint page_num, GdlSwitcher *switcher)
628 {
629 GtkWidget *page_widget;
630 GtkWidget *tablabel;
631 gint switcher_id;
633 /* Change switcher button */
634 page_widget = gtk_notebook_get_nth_page (nb, page_num);
635 switcher_id = gdl_switcher_get_page_id (page_widget);
636 gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
637 }
639 static void
640 gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page,
641 gint page_num, GdlSwitcher *switcher)
642 {
643 gint switcher_id;
645 switcher_id = gdl_switcher_get_page_id (page);
647 gdl_switcher_add_button (GDL_SWITCHER (switcher), NULL, NULL, NULL, NULL,
648 switcher_id);
649 gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
650 }
652 static void
653 gdl_switcher_select_page (GdlSwitcher *switcher, gint id)
654 {
655 GList *children, *node;
656 children = gtk_container_get_children (GTK_CONTAINER (switcher));
657 node = children;
658 while (node)
659 {
660 gint switcher_id;
661 switcher_id = gdl_switcher_get_page_id (GTK_WIDGET (node->data));
662 if (switcher_id == id)
663 {
664 gint page_num;
665 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (switcher),
666 GTK_WIDGET (node->data));
667 g_signal_handlers_block_by_func (switcher,
668 gdl_switcher_switch_page_cb,
669 switcher);
670 gtk_notebook_set_current_page (GTK_NOTEBOOK (switcher), page_num);
671 g_signal_handlers_unblock_by_func (switcher,
672 gdl_switcher_switch_page_cb,
673 switcher);
674 break;
675 }
676 node = g_list_next (node);
677 }
678 g_list_free (children);
679 }
681 /* Initialization. */
683 static void
684 gdl_switcher_class_init (GdlSwitcherClass *klass)
685 {
686 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
687 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
688 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
689 GObjectClass *object_class = G_OBJECT_CLASS (klass);
691 container_class->forall = gdl_switcher_forall;
692 container_class->remove = gdl_switcher_remove;
694 widget_class->size_request = gdl_switcher_size_request;
695 widget_class->size_allocate = gdl_switcher_size_allocate;
696 widget_class->expose_event = gdl_switcher_expose;
697 widget_class->map = gdl_switcher_map;
699 object_class->dispose = gdl_switcher_dispose;
700 object_class->finalize = gdl_switcher_finalize;
701 object_class->set_property = gdl_switcher_set_property;
702 object_class->get_property = gdl_switcher_get_property;
704 g_object_class_install_property (
705 object_class, PROP_SWITCHER_STYLE,
706 g_param_spec_enum ("switcher-style", _("Switcher Style"),
707 _("Switcher buttons style"),
708 GDL_TYPE_SWITCHER_STYLE,
709 GDL_SWITCHER_STYLE_BOTH,
710 G_PARAM_READWRITE));
711 }
713 static void
714 gdl_switcher_instance_init (GdlSwitcher *switcher)
715 {
716 GdlSwitcherPrivate *priv;
718 GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW);
720 priv = g_new0 (GdlSwitcherPrivate, 1);
721 switcher->priv = priv;
723 priv->show = TRUE;
724 priv->buttons_height_request = -1;
726 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (switcher), GTK_POS_BOTTOM);
727 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
728 gtk_notebook_set_show_border (GTK_NOTEBOOK (switcher), FALSE);
729 gdl_switcher_set_style (switcher, GDL_SWITCHER_STYLE_BOTH);
731 /* notebook signals */
732 g_signal_connect (switcher, "switch-page",
733 G_CALLBACK (gdl_switcher_switch_page_cb), switcher);
734 g_signal_connect (switcher, "page-added",
735 G_CALLBACK (gdl_switcher_page_added_cb), switcher);
736 g_signal_connect (switcher, "notify::show-tabs",
737 G_CALLBACK (gdl_switcher_notify_cb), switcher);
738 }
740 GtkWidget *
741 gdl_switcher_new (void)
742 {
743 GdlSwitcher *switcher = g_object_new (gdl_switcher_get_type (), NULL);
744 return GTK_WIDGET (switcher);
745 }
747 void
748 gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label,
749 const gchar *tooltips, const gchar *stock_id,
750 const GdkPixbuf *pixbuf_icon, gint switcher_id)
751 {
752 GtkWidget *button_widget;
753 GtkWidget *hbox;
754 GtkWidget *icon_widget;
755 GtkWidget *label_widget;
756 GtkWidget *arrow;
757 GtkTooltips *button_tooltips;
759 button_widget = gtk_toggle_button_new ();
760 if (switcher->priv->show)
761 gtk_widget_show (button_widget);
762 g_signal_connect (button_widget, "toggled",
763 G_CALLBACK (button_toggled_callback),
764 switcher);
765 hbox = gtk_hbox_new (FALSE, 3);
766 gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
767 gtk_container_add (GTK_CONTAINER (button_widget), hbox);
768 gtk_widget_show (hbox);
770 if (stock_id)
771 icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
772 else if (pixbuf_icon)
773 icon_widget = gtk_image_new_from_pixbuf (pixbuf_icon);
774 else
775 icon_widget = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON);
777 gtk_widget_show (icon_widget);
779 if (!label) {
780 gchar *text = g_strdup_printf ("Item %d", switcher_id);
781 label_widget = gtk_label_new (text);
782 g_free (text);
783 } else {
784 label_widget = gtk_label_new (label);
785 }
786 gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
787 gtk_widget_show (label_widget);
788 button_tooltips = gtk_tooltips_new();
789 gtk_tooltips_set_tip (GTK_TOOLTIPS (button_tooltips), button_widget,
790 tooltips, NULL);
792 switch (INTERNAL_MODE (switcher)) {
793 case GDL_SWITCHER_STYLE_TEXT:
794 gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
795 gtk_tooltips_disable (button_tooltips);
796 break;
797 case GDL_SWITCHER_STYLE_ICON:
798 gtk_box_pack_start (GTK_BOX (hbox), icon_widget, TRUE, TRUE, 0);
799 gtk_tooltips_enable (button_tooltips);
800 break;
801 case GDL_SWITCHER_STYLE_BOTH:
802 default:
803 gtk_box_pack_start (GTK_BOX (hbox), icon_widget, FALSE, TRUE, 0);
804 gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
805 gtk_tooltips_disable (button_tooltips);
806 break;
807 }
808 arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_NONE);
809 gtk_widget_show (arrow);
810 gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
812 switcher->priv->buttons =
813 g_slist_append (switcher->priv->buttons,
814 button_new (button_widget, label_widget,
815 icon_widget, button_tooltips,
816 arrow, hbox, switcher_id));
817 gtk_widget_set_parent (button_widget, GTK_WIDGET (switcher));
819 gtk_widget_queue_resize (GTK_WIDGET (switcher));
820 }
822 static void
823 gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id)
824 {
825 GSList *p;
827 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
828 Button *button = p->data;
830 if (button->id == switcher_id)
831 {
832 gtk_container_remove (GTK_CONTAINER (switcher),
833 button->button_widget);
834 break;
835 }
836 }
837 gtk_widget_queue_resize (GTK_WIDGET (switcher));
838 }
840 static void
841 gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id)
842 {
843 update_buttons (switcher, switcher_id);
845 /* Select the notebook page associated with this button */
846 gdl_switcher_select_page (switcher, switcher_id);
847 }
849 gint
850 gdl_switcher_insert_page (GdlSwitcher *switcher, GtkWidget *page,
851 GtkWidget *tab_widget, const gchar *label,
852 const gchar *tooltips, const gchar *stock_id,
853 const GdkPixbuf *pixbuf_icon, gint position)
854 {
855 gint ret_position;
856 gint switcher_id;
857 g_signal_handlers_block_by_func (switcher,
858 gdl_switcher_page_added_cb,
859 switcher);
861 if (!tab_widget) {
862 tab_widget = gtk_label_new (label);
863 gtk_widget_show (tab_widget);
864 }
865 switcher_id = gdl_switcher_get_page_id (page);
866 gdl_switcher_add_button (switcher, label, tooltips, stock_id, pixbuf_icon, switcher_id);
867 ret_position = gtk_notebook_insert_page (GTK_NOTEBOOK (switcher), page,
868 tab_widget, position);
869 g_signal_handlers_unblock_by_func (switcher,
870 gdl_switcher_page_added_cb,
871 switcher);
872 return ret_position;
873 }
875 static void
876 set_switcher_style_internal (GdlSwitcher *switcher,
877 GdlSwitcherStyle switcher_style )
878 {
879 GSList *p;
881 if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
882 switcher->priv->show == FALSE)
883 return;
885 if (switcher_style == GDL_SWITCHER_STYLE_TABS)
886 {
887 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), TRUE);
888 return;
889 }
891 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
893 if (switcher_style == INTERNAL_MODE (switcher))
894 return;
896 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
897 Button *button = p->data;
899 gtk_container_remove (GTK_CONTAINER (button->hbox), button->arrow);
900 switch (switcher_style) {
901 case GDL_SWITCHER_STYLE_TEXT:
902 gtk_container_remove (GTK_CONTAINER (button->hbox), button->icon);
903 if (INTERNAL_MODE (switcher)
904 == GDL_SWITCHER_STYLE_ICON) {
905 gtk_box_pack_start (GTK_BOX (button->hbox), button->label,
906 TRUE, TRUE, 0);
907 gtk_widget_show (button->label);
908 gtk_tooltips_disable (button->tooltips);
909 }
910 break;
911 case GDL_SWITCHER_STYLE_ICON:
912 gtk_container_remove(GTK_CONTAINER (button->hbox), button->label);
913 if (INTERNAL_MODE (switcher)
914 == GDL_SWITCHER_STYLE_TEXT) {
915 gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
916 TRUE, TRUE, 0);
917 gtk_widget_show (button->icon);
918 } else
919 gtk_container_child_set (GTK_CONTAINER (button->hbox),
920 button->icon, "expand", TRUE, NULL);
921 gtk_tooltips_enable (button->tooltips);
922 break;
923 case GDL_SWITCHER_STYLE_BOTH:
924 if (INTERNAL_MODE (switcher)
925 == GDL_SWITCHER_STYLE_TEXT) {
926 gtk_container_remove (GTK_CONTAINER (button->hbox),
927 button->label);
928 gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
929 FALSE, TRUE, 0);
930 gtk_widget_show (button->icon);
931 } else {
932 gtk_container_child_set (GTK_CONTAINER (button->hbox),
933 button->icon, "expand", FALSE, NULL);
934 }
936 gtk_tooltips_disable (button->tooltips);
937 gtk_box_pack_start (GTK_BOX (button->hbox), button->label, TRUE,
938 TRUE, 0);
939 gtk_widget_show (button->label);
940 break;
941 default:
942 break;
943 }
944 gtk_box_pack_start (GTK_BOX (button->hbox), button->arrow, FALSE,
945 FALSE, 0);
946 }
947 }
949 #if HAVE_GNOME
950 static GConfEnumStringPair toolbar_styles[] = {
951 { GDL_SWITCHER_STYLE_TEXT, "text" },
952 { GDL_SWITCHER_STYLE_ICON, "icons" },
953 { GDL_SWITCHER_STYLE_BOTH, "both" },
954 { GDL_SWITCHER_STYLE_BOTH, "both-horiz" },
955 { GDL_SWITCHER_STYLE_BOTH, "both_horiz" },
956 { -1, NULL }
957 };
959 static void
960 style_changed_notify (GConfClient *gconf, guint id, GConfEntry *entry,
961 void *data)
962 {
963 GdlSwitcher *switcher = data;
964 char *val;
965 int switcher_style;
967 val = gconf_client_get_string (gconf,
968 "/desktop/gnome/interface/toolbar_style",
969 NULL);
970 if (val == NULL || !gconf_string_to_enum (toolbar_styles, val,
971 &switcher_style))
972 switcher_style = GDL_SWITCHER_STYLE_BOTH;
973 g_free(val);
975 set_switcher_style_internal (GDL_SWITCHER (switcher), switcher_style);
976 switcher->priv->toolbar_style = switcher_style;
978 gtk_widget_queue_resize (GTK_WIDGET (switcher));
979 }
981 static void
982 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
983 {
984 GConfClient *gconf_client = gconf_client_get_default ();
986 if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
987 switcher->priv->show == FALSE)
988 return;
990 if (switcher->priv->switcher_style == switcher_style &&
991 switcher->priv->show == TRUE)
992 return;
994 if (switcher->priv->switcher_style == GDL_SWITCHER_STYLE_TOOLBAR) {
995 if (switcher->priv->style_changed_id) {
996 gconf_client_notify_remove (gconf_client,
997 switcher->priv->style_changed_id);
998 switcher->priv->style_changed_id = 0;
999 }
1000 }
1002 if (switcher_style != GDL_SWITCHER_STYLE_TOOLBAR) {
1003 set_switcher_style_internal (switcher, switcher_style);
1005 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1006 } else {
1007 /* This is a little bit tricky, toolbar style is more
1008 * of a meta-style where the actual style is dictated by
1009 * the gnome toolbar setting, so that is why we have
1010 * the is_toolbar_style bool - it tracks the toolbar
1011 * style while the switcher_style member is the actual look and
1012 * feel */
1013 switcher->priv->style_changed_id =
1014 gconf_client_notify_add (gconf_client,
1015 "/desktop/gnome/interface/toolbar_style",
1016 style_changed_notify, switcher,
1017 NULL, NULL);
1018 style_changed_notify (gconf_client, 0, NULL, switcher);
1019 }
1021 g_object_unref (gconf_client);
1023 if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1024 switcher->priv->switcher_style = switcher_style;
1025 }
1027 #else /* HAVE_GNOME */
1029 static void
1030 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
1031 {
1032 if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
1033 switcher->priv->show == FALSE)
1034 return;
1036 if (switcher->priv->switcher_style == switcher_style &&
1037 switcher->priv->show == TRUE)
1038 return;
1040 set_switcher_style_internal (switcher,
1041 ((switcher_style ==
1042 GDL_SWITCHER_STYLE_TOOLBAR)?
1043 GDL_SWITCHER_STYLE_BOTH : switcher_style));
1044 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1046 if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1047 switcher->priv->switcher_style = switcher_style;
1048 }
1050 #endif /* HAVE_GNOME */
1052 static void
1053 gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show)
1054 {
1055 GSList *p;
1057 if (switcher->priv->show == show)
1058 return;
1060 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
1061 Button *button = p->data;
1063 if (show)
1064 gtk_widget_show (button->button_widget);
1065 else
1066 gtk_widget_hide (button->button_widget);
1067 }
1069 switcher->priv->show = show;
1071 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1072 }
1074 static GdlSwitcherStyle
1075 gdl_switcher_get_style (GdlSwitcher *switcher)
1076 {
1077 if (!switcher->priv->show)
1078 return GDL_SWITCHER_STYLE_TABS;
1079 return switcher->priv->switcher_style;
1080 }