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 #include <libgnome/gnome-gconf.h>
46 #endif
48 static void gdl_switcher_set_property (GObject *object,
49 guint prop_id,
50 const GValue *value,
51 GParamSpec *pspec);
52 static void gdl_switcher_get_property (GObject *object,
53 guint prop_id,
54 GValue *value,
55 GParamSpec *pspec);
57 static void gdl_switcher_add_button (GdlSwitcher *switcher,
58 const gchar *label,
59 const gchar *tooltips,
60 const gchar *stock_id,
61 const GdkPixbuf *pixbuf_icon,
62 gint switcher_id);
63 static void gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id);
64 static void gdl_switcher_select_page (GdlSwitcher *switcher, gint switcher_id);
65 static void gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id);
66 static void gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show);
67 static void gdl_switcher_set_style (GdlSwitcher *switcher,
68 GdlSwitcherStyle switcher_style);
69 static GdlSwitcherStyle gdl_switcher_get_style (GdlSwitcher *switcher);
71 enum {
72 PROP_0,
73 PROP_SWITCHER_STYLE
74 };
76 typedef struct {
77 GtkWidget *button_widget;
78 GtkWidget *label;
79 GtkWidget *icon;
80 GtkWidget *arrow;
81 GtkWidget *hbox;
82 GtkTooltips *tooltips;
83 int id;
84 } Button;
86 struct _GdlSwitcherPrivate {
87 GdlSwitcherStyle switcher_style;
88 GdlSwitcherStyle toolbar_style;
90 gboolean show;
91 GSList *buttons;
93 guint style_changed_id;
94 gint buttons_height_request;
95 gboolean in_toggle;
96 };
98 GDL_CLASS_BOILERPLATE (GdlSwitcher, gdl_switcher, GtkNotebook, GTK_TYPE_NOTEBOOK)
100 #define INTERNAL_MODE(switcher) (switcher->priv->switcher_style == \
101 GDL_SWITCHER_STYLE_TOOLBAR ? switcher->priv->toolbar_style : \
102 switcher->priv->switcher_style)
104 #define H_PADDING 2
105 #define V_PADDING 2
107 /* Utility functions. */
109 static Button *
110 button_new (GtkWidget *button_widget, GtkWidget *label, GtkWidget *icon,
111 GtkTooltips *tooltips, GtkWidget *arrow, GtkWidget *hbox, int id)
112 {
113 Button *button = g_new (Button, 1);
115 button->button_widget = button_widget;
116 button->label = label;
117 button->icon = icon;
118 button->arrow = arrow;
119 button->hbox = hbox;
120 button->tooltips = tooltips;
121 button->id = id;
123 g_object_ref (button_widget);
124 g_object_ref (label);
125 g_object_ref (icon);
126 g_object_ref (arrow);
127 g_object_ref (hbox);
128 g_object_ref (tooltips);
130 return button;
131 }
133 static void
134 button_free (Button *button)
135 {
136 g_object_unref (button->button_widget);
137 g_object_unref (button->label);
138 g_object_unref (button->icon);
139 g_object_unref (button->hbox);
140 g_object_unref (button->tooltips);
141 g_free (button);
142 }
144 static gint
145 gdl_switcher_get_page_id (GtkWidget *widget)
146 {
147 static gint switcher_id_count = 0;
148 gint switcher_id;
149 switcher_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
150 "__switcher_id"));
151 if (switcher_id <= 0) {
152 switcher_id = ++switcher_id_count;
153 g_object_set_data (G_OBJECT (widget), "__switcher_id",
154 GINT_TO_POINTER (switcher_id));
155 }
156 return switcher_id;
157 }
159 static void
160 update_buttons (GdlSwitcher *switcher, int new_selected_id)
161 {
162 GSList *p;
164 switcher->priv->in_toggle = TRUE;
166 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
167 Button *button = p->data;
169 if (button->id == new_selected_id) {
170 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
171 (button->button_widget), TRUE);
172 gtk_widget_set_sensitive (button->arrow, TRUE);
173 } else {
174 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
175 (button->button_widget), FALSE);
176 gtk_widget_set_sensitive (button->arrow, FALSE);
177 }
178 }
180 switcher->priv->in_toggle = FALSE;
181 }
183 /* Callbacks. */
185 static void
186 button_toggled_callback (GtkToggleButton *toggle_button,
187 GdlSwitcher *switcher)
188 {
189 int id = 0;
190 gboolean is_active = FALSE;
191 GSList *p;
193 if (switcher->priv->in_toggle)
194 return;
196 switcher->priv->in_toggle = TRUE;
198 if (gtk_toggle_button_get_active (toggle_button))
199 is_active = TRUE;
201 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
202 Button *button = p->data;
204 if (button->button_widget != GTK_WIDGET (toggle_button)) {
205 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
206 (button->button_widget), FALSE);
207 gtk_widget_set_sensitive (button->arrow, FALSE);
208 } else {
209 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
210 (button->button_widget), TRUE);
211 gtk_widget_set_sensitive (button->arrow, TRUE);
212 id = button->id;
213 }
214 }
216 switcher->priv->in_toggle = FALSE;
218 if (is_active)
219 {
220 gdl_switcher_select_page (switcher, id);
221 }
222 }
224 /* Returns -1 if layout didn't happen because a resize request was queued */
225 static int
226 layout_buttons (GdlSwitcher *switcher)
227 {
228 GtkRequisition client_requisition;
229 GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
230 GdlSwitcherStyle switcher_style;
231 gboolean icons_only;
232 int num_btns = g_slist_length (switcher->priv->buttons);
233 int btns_per_row;
234 GSList **rows, *p;
235 Button *button;
236 int row_number;
237 int max_btn_width = 0, max_btn_height = 0;
238 int optimal_layout_width = 0;
239 int row_last;
240 int x, y;
241 int i;
242 int rows_count;
243 int last_buttons_height;
245 last_buttons_height = switcher->priv->buttons_height_request;
247 GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
248 (GTK_WIDGET (switcher), &client_requisition));
250 y = allocation->y + allocation->height - V_PADDING - 1;
252 if (num_btns == 0)
253 return y;
255 switcher_style = INTERNAL_MODE (switcher);
256 icons_only = (switcher_style == GDL_SWITCHER_STYLE_ICON);
258 /* Figure out the max width and height */
259 optimal_layout_width = H_PADDING;
260 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
261 GtkRequisition requisition;
263 button = p->data;
264 gtk_widget_size_request (GTK_WIDGET (button->button_widget),
265 &requisition);
266 optimal_layout_width += requisition.width + H_PADDING;
267 max_btn_height = MAX (max_btn_height, requisition.height);
268 max_btn_width = MAX (max_btn_width, requisition.width);
269 }
271 /* Figure out how many rows and columns we'll use. */
272 btns_per_row = allocation->width / (max_btn_width + H_PADDING);
274 /* If all the buttons could fit in the single row, have it so */
275 if (allocation->width >= optimal_layout_width)
276 {
277 btns_per_row = num_btns;
278 }
279 if (!icons_only) {
280 /* If using text buttons, we want to try to have a
281 * completely filled-in grid, but if we can't, we want
282 * the odd row to have just a single button.
283 */
284 while (num_btns % btns_per_row > 1)
285 btns_per_row--;
286 }
288 rows_count = num_btns / btns_per_row;
289 if (num_btns % btns_per_row != 0)
290 rows_count++;
292 /* Assign buttons to rows */
293 rows = g_new0 (GSList *, rows_count);
295 if (!icons_only && num_btns % btns_per_row != 0) {
296 button = switcher->priv->buttons->data;
297 rows [0] = g_slist_append (rows [0], button->button_widget);
299 p = switcher->priv->buttons->next;
300 row_number = p ? 1 : 0;
301 } else {
302 p = switcher->priv->buttons;
303 row_number = 0;
304 }
306 for (; p != NULL; p = p->next) {
307 button = p->data;
309 if (g_slist_length (rows [row_number]) == btns_per_row)
310 row_number ++;
312 rows [row_number] = g_slist_append (rows [row_number],
313 button->button_widget);
314 }
316 row_last = row_number;
318 /* If there are more than 1 row of buttons, save the current height
319 * requirement for subsequent size requests.
320 */
321 if (row_last > 0)
322 {
323 switcher->priv->buttons_height_request =
324 (row_last + 1) * (max_btn_height + V_PADDING) + 1;
325 } else { /* Otherwize clear it */
326 if (last_buttons_height >= 0) {
328 switcher->priv->buttons_height_request = -1;
329 }
330 }
332 /* If it turns out that we now require smaller height for the buttons
333 * than it was last time, make a resize request to ensure our
334 * size requisition is properly communicated to the parent (otherwise
335 * parent tend to keep assuming the older size).
336 */
337 if (last_buttons_height > switcher->priv->buttons_height_request)
338 {
339 gtk_widget_queue_resize (GTK_WIDGET (switcher));
340 return -1;
341 }
343 /* Layout the buttons. */
344 for (i = row_last; i >= 0; i --) {
345 int len, extra_width;
347 y -= max_btn_height;
349 /* Check for possible size over flow (taking into account client
350 * requisition
351 */
352 if (y < (allocation->y + client_requisition.height)) {
353 /* We have an overflow: Insufficient allocation */
354 if (last_buttons_height < switcher->priv->buttons_height_request) {
355 /* Request for a new resize */
356 gtk_widget_queue_resize (GTK_WIDGET (switcher));
357 return -1;
358 }
359 }
360 x = H_PADDING + allocation->x;
361 len = g_slist_length (rows[i]);
362 if (switcher_style == GDL_SWITCHER_STYLE_TEXT ||
363 switcher_style == GDL_SWITCHER_STYLE_BOTH)
364 extra_width = (allocation->width - (len * max_btn_width )
365 - (len * H_PADDING)) / len;
366 else
367 extra_width = 0;
368 for (p = rows [i]; p != NULL; p = p->next) {
369 GtkAllocation child_allocation;
371 child_allocation.x = x;
372 child_allocation.y = y;
373 if (rows_count == 1 && row_number == 0)
374 {
375 GtkRequisition child_requisition;
376 gtk_widget_size_request (GTK_WIDGET (p->data),
377 &child_requisition);
378 child_allocation.width = child_requisition.width;
379 }
380 else
381 {
382 child_allocation.width = max_btn_width + extra_width;
383 }
384 child_allocation.height = max_btn_height;
386 gtk_widget_size_allocate (GTK_WIDGET (p->data), &child_allocation);
388 x += child_allocation.width + H_PADDING;
389 }
391 y -= V_PADDING;
392 }
394 for (i = 0; i <= row_last; i ++)
395 g_slist_free (rows [i]);
396 g_free (rows);
398 return y;
399 }
401 static void
402 do_layout (GdlSwitcher *switcher)
403 {
404 GtkAllocation *allocation = & GTK_WIDGET (switcher)->allocation;
405 GtkAllocation child_allocation;
406 int y;
408 if (switcher->priv->show) {
409 y = layout_buttons (switcher);
410 if (y < 0) /* Layout did not happen and a resize was requested */
411 return;
412 }
413 else
414 y = allocation->y + allocation->height;
416 /* Place the parent widget. */
417 child_allocation.x = allocation->x;
418 child_allocation.y = allocation->y;
419 child_allocation.width = allocation->width;
420 child_allocation.height = y - allocation->y;
422 GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate,
423 (GTK_WIDGET (switcher), &child_allocation));
424 }
426 /* GtkContainer methods. */
428 static void
429 gdl_switcher_forall (GtkContainer *container, gboolean include_internals,
430 GtkCallback callback, void *callback_data)
431 {
432 GdlSwitcher *switcher =
433 GDL_SWITCHER (container);
434 GSList *p;
436 GDL_CALL_PARENT (GTK_CONTAINER_CLASS, forall,
437 (GTK_CONTAINER (switcher), include_internals,
438 callback, callback_data));
439 if (include_internals) {
440 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
441 GtkWidget *widget = ((Button *) p->data)->button_widget;
442 (* callback) (widget, callback_data);
443 }
444 }
445 }
447 static void
448 gdl_switcher_remove (GtkContainer *container, GtkWidget *widget)
449 {
450 gint switcher_id;
451 GdlSwitcher *switcher =
452 GDL_SWITCHER (container);
453 GSList *p;
455 switcher_id = gdl_switcher_get_page_id (widget);
456 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
457 Button *b = (Button *) p->data;
459 if (b->id == switcher_id) {
460 gtk_widget_unparent (b->button_widget);
461 switcher->priv->buttons =
462 g_slist_remove_link (switcher->priv->buttons, p);
463 button_free (b);
464 gtk_widget_queue_resize (GTK_WIDGET (switcher));
465 break;
466 }
467 }
468 GDL_CALL_PARENT (GTK_CONTAINER_CLASS, remove,
469 (GTK_CONTAINER (switcher), widget));
470 }
472 /* GtkWidget methods. */
474 static void
475 gdl_switcher_size_request (GtkWidget *widget, GtkRequisition *requisition)
476 {
477 GdlSwitcher *switcher = GDL_SWITCHER (widget);
478 GSList *p;
479 gint button_height = 0;
481 GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
482 (GTK_WIDGET (switcher), requisition));
484 if (!switcher->priv->show)
485 return;
487 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
488 gint button_width;
489 Button *button = p->data;
490 GtkRequisition button_requisition;
492 gtk_widget_size_request (button->button_widget, &button_requisition);
493 button_width = button_requisition.width + 2 * H_PADDING;
494 requisition->width = MAX (requisition->width, button_width);
495 button_height = MAX (button_height,
496 button_requisition.height + 2 * V_PADDING);
497 }
499 if (switcher->priv->buttons_height_request > 0) {
500 requisition->height += switcher->priv->buttons_height_request;
501 } else {
502 requisition->height += button_height + V_PADDING;
503 }
504 }
506 static void
507 gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
508 {
509 widget->allocation = *allocation;
510 do_layout (GDL_SWITCHER (widget));
511 }
513 static gint
514 gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event)
515 {
516 GSList *p;
517 GdlSwitcher *switcher = GDL_SWITCHER (widget);
518 if (switcher->priv->show) {
519 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
520 GtkWidget *button = ((Button *) p->data)->button_widget;
521 gtk_container_propagate_expose (GTK_CONTAINER (widget),
522 button, event);
523 }
524 }
525 GDL_CALL_PARENT_WITH_DEFAULT (GTK_WIDGET_CLASS, expose_event,
526 (widget, event), FALSE);
527 }
529 static void
530 gdl_switcher_map (GtkWidget *widget)
531 {
532 GSList *p;
533 GdlSwitcher *switcher = GDL_SWITCHER (widget);
535 if (switcher->priv->show) {
536 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
537 GtkWidget *button = ((Button *) p->data)->button_widget;
538 gtk_widget_map (button);
539 }
540 }
541 GDL_CALL_PARENT (GTK_WIDGET_CLASS, map, (widget));
542 }
544 /* GObject methods. */
546 static void
547 gdl_switcher_set_property (GObject *object,
548 guint prop_id,
549 const GValue *value,
550 GParamSpec *pspec)
551 {
552 GdlSwitcher *switcher = GDL_SWITCHER (object);
554 switch (prop_id) {
555 case PROP_SWITCHER_STYLE:
556 gdl_switcher_set_style (switcher, g_value_get_enum (value));
557 break;
558 default:
559 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560 break;
561 }
562 }
564 static void
565 gdl_switcher_get_property (GObject *object,
566 guint prop_id,
567 GValue *value,
568 GParamSpec *pspec)
569 {
570 GdlSwitcher *switcher = GDL_SWITCHER (object);
572 switch (prop_id) {
573 case PROP_SWITCHER_STYLE:
574 g_value_set_enum (value, gdl_switcher_get_style (switcher));
575 break;
576 default:
577 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
578 break;
579 }
580 }
582 static void
583 gdl_switcher_dispose (GObject *object)
584 {
585 GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
587 #if HAVE_GNOME
588 GConfClient *gconf_client = gconf_client_get_default ();
590 if (priv->style_changed_id) {
591 gconf_client_notify_remove (gconf_client, priv->style_changed_id);
592 priv->style_changed_id = 0;
593 }
594 g_object_unref (gconf_client);
595 #endif
597 g_slist_foreach (priv->buttons, (GFunc) button_free, NULL);
598 g_slist_free (priv->buttons);
599 priv->buttons = NULL;
601 GDL_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
602 }
604 static void
605 gdl_switcher_finalize (GObject *object)
606 {
607 GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
609 g_free (priv);
611 GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
612 }
614 /* Signal handlers */
616 static void
617 gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec,
618 GdlSwitcher *switcher)
619 {
620 gboolean show_tabs;
621 g_return_if_fail (switcher != NULL && GDL_IS_SWITCHER (switcher));
622 show_tabs = gtk_notebook_get_show_tabs (GTK_NOTEBOOK (switcher));
623 gdl_switcher_set_show_buttons (switcher, !show_tabs);
624 }
626 static void
627 gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page,
628 gint page_num, GdlSwitcher *switcher)
629 {
630 GtkWidget *page_widget;
631 GtkWidget *tablabel;
632 gint switcher_id;
634 /* Change switcher button */
635 page_widget = gtk_notebook_get_nth_page (nb, page_num);
636 switcher_id = gdl_switcher_get_page_id (page_widget);
637 gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
638 }
640 static void
641 gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page,
642 gint page_num, GdlSwitcher *switcher)
643 {
644 gint switcher_id;
646 switcher_id = gdl_switcher_get_page_id (page);
648 gdl_switcher_add_button (GDL_SWITCHER (switcher), NULL, NULL, NULL, NULL,
649 switcher_id);
650 gdl_switcher_select_button (GDL_SWITCHER (switcher), switcher_id);
651 }
653 static void
654 gdl_switcher_select_page (GdlSwitcher *switcher, gint id)
655 {
656 GList *children, *node;
657 children = gtk_container_get_children (GTK_CONTAINER (switcher));
658 node = children;
659 while (node)
660 {
661 gint switcher_id;
662 switcher_id = gdl_switcher_get_page_id (GTK_WIDGET (node->data));
663 if (switcher_id == id)
664 {
665 gint page_num;
666 page_num = gtk_notebook_page_num (GTK_NOTEBOOK (switcher),
667 GTK_WIDGET (node->data));
668 g_signal_handlers_block_by_func (switcher,
669 gdl_switcher_switch_page_cb,
670 switcher);
671 gtk_notebook_set_current_page (GTK_NOTEBOOK (switcher), page_num);
672 g_signal_handlers_unblock_by_func (switcher,
673 gdl_switcher_switch_page_cb,
674 switcher);
675 break;
676 }
677 node = g_list_next (node);
678 }
679 g_list_free (children);
680 }
682 /* Initialization. */
684 static void
685 gdl_switcher_class_init (GdlSwitcherClass *klass)
686 {
687 GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
688 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
689 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
690 GObjectClass *object_class = G_OBJECT_CLASS (klass);
692 container_class->forall = gdl_switcher_forall;
693 container_class->remove = gdl_switcher_remove;
695 widget_class->size_request = gdl_switcher_size_request;
696 widget_class->size_allocate = gdl_switcher_size_allocate;
697 widget_class->expose_event = gdl_switcher_expose;
698 widget_class->map = gdl_switcher_map;
700 object_class->dispose = gdl_switcher_dispose;
701 object_class->finalize = gdl_switcher_finalize;
702 object_class->set_property = gdl_switcher_set_property;
703 object_class->get_property = gdl_switcher_get_property;
705 g_object_class_install_property (
706 object_class, PROP_SWITCHER_STYLE,
707 g_param_spec_enum ("switcher-style", _("Switcher Style"),
708 _("Switcher buttons style"),
709 GDL_TYPE_SWITCHER_STYLE,
710 GDL_SWITCHER_STYLE_BOTH,
711 G_PARAM_READWRITE));
712 }
714 static void
715 gdl_switcher_instance_init (GdlSwitcher *switcher)
716 {
717 GdlSwitcherPrivate *priv;
719 GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW);
721 priv = g_new0 (GdlSwitcherPrivate, 1);
722 switcher->priv = priv;
724 priv->show = TRUE;
725 priv->buttons_height_request = -1;
727 gtk_notebook_set_tab_pos (GTK_NOTEBOOK (switcher), GTK_POS_BOTTOM);
728 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
729 gtk_notebook_set_show_border (GTK_NOTEBOOK (switcher), FALSE);
730 gdl_switcher_set_style (switcher, GDL_SWITCHER_STYLE_BOTH);
732 /* notebook signals */
733 g_signal_connect (switcher, "switch-page",
734 G_CALLBACK (gdl_switcher_switch_page_cb), switcher);
735 g_signal_connect (switcher, "page-added",
736 G_CALLBACK (gdl_switcher_page_added_cb), switcher);
737 g_signal_connect (switcher, "notify::show-tabs",
738 G_CALLBACK (gdl_switcher_notify_cb), switcher);
739 }
741 GtkWidget *
742 gdl_switcher_new (void)
743 {
744 GdlSwitcher *switcher = g_object_new (gdl_switcher_get_type (), NULL);
745 return GTK_WIDGET (switcher);
746 }
748 void
749 gdl_switcher_add_button (GdlSwitcher *switcher, const gchar *label,
750 const gchar *tooltips, const gchar *stock_id,
751 const GdkPixbuf *pixbuf_icon, gint switcher_id)
752 {
753 GtkWidget *button_widget;
754 GtkWidget *hbox;
755 GtkWidget *icon_widget;
756 GtkWidget *label_widget;
757 GtkWidget *arrow;
758 GtkTooltips *button_tooltips;
760 button_widget = gtk_toggle_button_new ();
761 if (switcher->priv->show)
762 gtk_widget_show (button_widget);
763 g_signal_connect (button_widget, "toggled",
764 G_CALLBACK (button_toggled_callback),
765 switcher);
766 hbox = gtk_hbox_new (FALSE, 3);
767 gtk_container_set_border_width (GTK_CONTAINER (hbox), 0);
768 gtk_container_add (GTK_CONTAINER (button_widget), hbox);
769 gtk_widget_show (hbox);
771 if (stock_id)
772 icon_widget = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
773 else if (pixbuf_icon)
774 icon_widget = gtk_image_new_from_pixbuf (pixbuf_icon);
775 else
776 icon_widget = gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_BUTTON);
778 gtk_widget_show (icon_widget);
780 if (!label) {
781 gchar *text = g_strdup_printf ("Item %d", switcher_id);
782 label_widget = gtk_label_new (text);
783 g_free (text);
784 } else {
785 label_widget = gtk_label_new (label);
786 }
787 gtk_misc_set_alignment (GTK_MISC (label_widget), 0.0, 0.5);
788 gtk_widget_show (label_widget);
789 button_tooltips = gtk_tooltips_new();
790 gtk_tooltips_set_tip (GTK_TOOLTIPS (button_tooltips), button_widget,
791 tooltips, NULL);
793 switch (INTERNAL_MODE (switcher)) {
794 case GDL_SWITCHER_STYLE_TEXT:
795 gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
796 gtk_tooltips_disable (button_tooltips);
797 break;
798 case GDL_SWITCHER_STYLE_ICON:
799 gtk_box_pack_start (GTK_BOX (hbox), icon_widget, TRUE, TRUE, 0);
800 gtk_tooltips_enable (button_tooltips);
801 break;
802 case GDL_SWITCHER_STYLE_BOTH:
803 default:
804 gtk_box_pack_start (GTK_BOX (hbox), icon_widget, FALSE, TRUE, 0);
805 gtk_box_pack_start (GTK_BOX (hbox), label_widget, TRUE, TRUE, 0);
806 gtk_tooltips_disable (button_tooltips);
807 break;
808 }
809 arrow = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_NONE);
810 gtk_widget_show (arrow);
811 gtk_box_pack_start (GTK_BOX (hbox), arrow, FALSE, FALSE, 0);
813 switcher->priv->buttons =
814 g_slist_append (switcher->priv->buttons,
815 button_new (button_widget, label_widget,
816 icon_widget, button_tooltips,
817 arrow, hbox, switcher_id));
818 gtk_widget_set_parent (button_widget, GTK_WIDGET (switcher));
820 gtk_widget_queue_resize (GTK_WIDGET (switcher));
821 }
823 static void
824 gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id)
825 {
826 GSList *p;
828 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
829 Button *button = p->data;
831 if (button->id == switcher_id)
832 {
833 gtk_container_remove (GTK_CONTAINER (switcher),
834 button->button_widget);
835 break;
836 }
837 }
838 gtk_widget_queue_resize (GTK_WIDGET (switcher));
839 }
841 static void
842 gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id)
843 {
844 update_buttons (switcher, switcher_id);
846 /* Select the notebook page associated with this button */
847 gdl_switcher_select_page (switcher, switcher_id);
848 }
850 gint
851 gdl_switcher_insert_page (GdlSwitcher *switcher, GtkWidget *page,
852 GtkWidget *tab_widget, const gchar *label,
853 const gchar *tooltips, const gchar *stock_id,
854 const GdkPixbuf *pixbuf_icon, gint position)
855 {
856 gint ret_position;
857 gint switcher_id;
858 g_signal_handlers_block_by_func (switcher,
859 gdl_switcher_page_added_cb,
860 switcher);
862 if (!tab_widget) {
863 tab_widget = gtk_label_new (label);
864 gtk_widget_show (tab_widget);
865 }
866 switcher_id = gdl_switcher_get_page_id (page);
867 gdl_switcher_add_button (switcher, label, tooltips, stock_id, pixbuf_icon, switcher_id);
868 ret_position = gtk_notebook_insert_page (GTK_NOTEBOOK (switcher), page,
869 tab_widget, position);
870 g_signal_handlers_unblock_by_func (switcher,
871 gdl_switcher_page_added_cb,
872 switcher);
873 return ret_position;
874 }
876 static void
877 set_switcher_style_internal (GdlSwitcher *switcher,
878 GdlSwitcherStyle switcher_style )
879 {
880 GSList *p;
882 if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
883 switcher->priv->show == FALSE)
884 return;
886 if (switcher_style == GDL_SWITCHER_STYLE_TABS)
887 {
888 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), TRUE);
889 return;
890 }
892 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
894 if (switcher_style == INTERNAL_MODE (switcher))
895 return;
897 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
898 Button *button = p->data;
900 gtk_container_remove (GTK_CONTAINER (button->hbox), button->arrow);
901 switch (switcher_style) {
902 case GDL_SWITCHER_STYLE_TEXT:
903 gtk_container_remove (GTK_CONTAINER (button->hbox), button->icon);
904 if (INTERNAL_MODE (switcher)
905 == GDL_SWITCHER_STYLE_ICON) {
906 gtk_box_pack_start (GTK_BOX (button->hbox), button->label,
907 TRUE, TRUE, 0);
908 gtk_widget_show (button->label);
909 gtk_tooltips_disable (button->tooltips);
910 }
911 break;
912 case GDL_SWITCHER_STYLE_ICON:
913 gtk_container_remove(GTK_CONTAINER (button->hbox), button->label);
914 if (INTERNAL_MODE (switcher)
915 == GDL_SWITCHER_STYLE_TEXT) {
916 gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
917 TRUE, TRUE, 0);
918 gtk_widget_show (button->icon);
919 } else
920 gtk_container_child_set (GTK_CONTAINER (button->hbox),
921 button->icon, "expand", TRUE, NULL);
922 gtk_tooltips_enable (button->tooltips);
923 break;
924 case GDL_SWITCHER_STYLE_BOTH:
925 if (INTERNAL_MODE (switcher)
926 == GDL_SWITCHER_STYLE_TEXT) {
927 gtk_container_remove (GTK_CONTAINER (button->hbox),
928 button->label);
929 gtk_box_pack_start (GTK_BOX (button->hbox), button->icon,
930 FALSE, TRUE, 0);
931 gtk_widget_show (button->icon);
932 } else {
933 gtk_container_child_set (GTK_CONTAINER (button->hbox),
934 button->icon, "expand", FALSE, NULL);
935 }
937 gtk_tooltips_disable (button->tooltips);
938 gtk_box_pack_start (GTK_BOX (button->hbox), button->label, TRUE,
939 TRUE, 0);
940 gtk_widget_show (button->label);
941 break;
942 default:
943 break;
944 }
945 gtk_box_pack_start (GTK_BOX (button->hbox), button->arrow, FALSE,
946 FALSE, 0);
947 }
948 }
950 #if HAVE_GNOME
951 static GConfEnumStringPair toolbar_styles[] = {
952 { GDL_SWITCHER_STYLE_TEXT, "text" },
953 { GDL_SWITCHER_STYLE_ICON, "icons" },
954 { GDL_SWITCHER_STYLE_BOTH, "both" },
955 { GDL_SWITCHER_STYLE_BOTH, "both-horiz" },
956 { GDL_SWITCHER_STYLE_BOTH, "both_horiz" },
957 { -1, NULL }
958 };
960 static void
961 style_changed_notify (GConfClient *gconf, guint id, GConfEntry *entry,
962 void *data)
963 {
964 GdlSwitcher *switcher = data;
965 char *val;
966 int switcher_style;
968 val = gconf_client_get_string (gconf,
969 "/desktop/gnome/interface/toolbar_style",
970 NULL);
971 if (val == NULL || !gconf_string_to_enum (toolbar_styles, val,
972 &switcher_style))
973 switcher_style = GDL_SWITCHER_STYLE_BOTH;
974 g_free(val);
976 set_switcher_style_internal (GDL_SWITCHER (switcher), switcher_style);
977 switcher->priv->toolbar_style = switcher_style;
979 gtk_widget_queue_resize (GTK_WIDGET (switcher));
980 }
982 static void
983 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
984 {
985 GConfClient *gconf_client = gconf_client_get_default ();
987 if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
988 switcher->priv->show == FALSE)
989 return;
991 if (switcher->priv->switcher_style == switcher_style &&
992 switcher->priv->show == TRUE)
993 return;
995 if (switcher->priv->switcher_style == GDL_SWITCHER_STYLE_TOOLBAR) {
996 if (switcher->priv->style_changed_id) {
997 gconf_client_notify_remove (gconf_client,
998 switcher->priv->style_changed_id);
999 switcher->priv->style_changed_id = 0;
1000 }
1001 }
1003 if (switcher_style != GDL_SWITCHER_STYLE_TOOLBAR) {
1004 set_switcher_style_internal (switcher, switcher_style);
1006 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1007 } else {
1008 /* This is a little bit tricky, toolbar style is more
1009 * of a meta-style where the actual style is dictated by
1010 * the gnome toolbar setting, so that is why we have
1011 * the is_toolbar_style bool - it tracks the toolbar
1012 * style while the switcher_style member is the actual look and
1013 * feel */
1014 switcher->priv->style_changed_id =
1015 gconf_client_notify_add (gconf_client,
1016 "/desktop/gnome/interface/toolbar_style",
1017 style_changed_notify, switcher,
1018 NULL, NULL);
1019 style_changed_notify (gconf_client, 0, NULL, switcher);
1020 }
1022 g_object_unref (gconf_client);
1024 if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1025 switcher->priv->switcher_style = switcher_style;
1026 }
1028 #else /* HAVE_GNOME */
1030 static void
1031 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
1032 {
1033 if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
1034 switcher->priv->show == FALSE)
1035 return;
1037 if (switcher->priv->switcher_style == switcher_style &&
1038 switcher->priv->show == TRUE)
1039 return;
1041 set_switcher_style_internal (switcher,
1042 ((switcher_style ==
1043 GDL_SWITCHER_STYLE_TOOLBAR)?
1044 GDL_SWITCHER_STYLE_BOTH : switcher_style));
1045 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1047 if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1048 switcher->priv->switcher_style = switcher_style;
1049 }
1051 #endif /* HAVE_GNOME */
1053 static void
1054 gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show)
1055 {
1056 GSList *p;
1058 if (switcher->priv->show == show)
1059 return;
1061 for (p = switcher->priv->buttons; p != NULL; p = p->next) {
1062 Button *button = p->data;
1064 if (show)
1065 gtk_widget_show (button->button_widget);
1066 else
1067 gtk_widget_hide (button->button_widget);
1068 }
1070 switcher->priv->show = show;
1072 gtk_widget_queue_resize (GTK_WIDGET (switcher));
1073 }
1075 static GdlSwitcherStyle
1076 gdl_switcher_get_style (GdlSwitcher *switcher)
1077 {
1078 if (!switcher->priv->show)
1079 return GDL_SWITCHER_STYLE_TABS;
1080 return switcher->priv->switcher_style;
1081 }