Code

Removed libgdl component not used by Inkscape.
[inkscape.git] / src / libgdl / gdl-switcher.c
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;
89     
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)
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;
133 static void
134 button_free (Button *button)
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);
144 static gint
145 gdl_switcher_get_page_id (GtkWidget *widget)
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;
159 static void
160 update_buttons (GdlSwitcher *switcher, int new_selected_id)
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;
183 /* Callbacks.  */
185 static void
186 button_toggled_callback (GtkToggleButton *toggle_button,
187                          GdlSwitcher *switcher)
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     }
215     
216     switcher->priv->in_toggle = FALSE;
218     if (is_active)
219     {
220         gdl_switcher_select_page (switcher, id);
221     }
224 /* Returns -1 if layout didn't happen because a resize request was queued */
225 static int
226 layout_buttons (GdlSwitcher *switcher)
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;
244     
245     last_buttons_height = switcher->priv->buttons_height_request;
246     
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);
257     
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);
273     
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++;
291      
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     }
331     
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     }
342     
343     /* Layout the buttons. */
344     for (i = row_last; i >= 0; i --) {
345         int len, extra_width;
346         
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;
370             
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     }
393     
394     for (i = 0; i <= row_last; i ++)
395         g_slist_free (rows [i]);
396     g_free (rows);
398     return y;
401 static void
402 do_layout (GdlSwitcher *switcher)
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;
415     
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;
421     
422     GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate,
423                      (GTK_WIDGET (switcher), &child_allocation));
426 /* GtkContainer methods.  */
428 static void
429 gdl_switcher_forall (GtkContainer *container, gboolean include_internals,
430                      GtkCallback callback, void *callback_data)
432     GdlSwitcher *switcher =
433         GDL_SWITCHER (container);
434     GSList *p;
435     
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     }
447 static void
448 gdl_switcher_remove (GtkContainer *container, GtkWidget *widget)
450     gint switcher_id;
451     GdlSwitcher *switcher =
452         GDL_SWITCHER (container);
453     GSList *p;
454     
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));
472 /* GtkWidget methods.  */
474 static void
475 gdl_switcher_size_request (GtkWidget *widget, GtkRequisition *requisition)
477     GdlSwitcher *switcher = GDL_SWITCHER (widget);
478     GSList *p;
479     gint button_height = 0;
480     
481     GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
482                      (GTK_WIDGET (switcher), requisition));
484     if (!switcher->priv->show)
485         return;
486     
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     }
498     
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     }
506 static void
507 gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
509     widget->allocation = *allocation;
510     do_layout (GDL_SWITCHER (widget));
513 static gint
514 gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event)
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);
529 static void
530 gdl_switcher_map (GtkWidget *widget)
532     GSList *p;
533     GdlSwitcher *switcher = GDL_SWITCHER (widget);
534     
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));
544 /* GObject methods.  */
546 static void
547 gdl_switcher_set_property  (GObject      *object,
548                             guint         prop_id,
549                             const GValue *value,
550                             GParamSpec   *pspec)
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     }
564 static void
565 gdl_switcher_get_property  (GObject      *object,
566                             guint         prop_id,
567                             GValue       *value,
568                             GParamSpec   *pspec)
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     }
582 static void
583 gdl_switcher_dispose (GObject *object)
585     GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
586     
587 #if HAVE_GNOME
588     GConfClient *gconf_client = gconf_client_get_default ();
589     
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
596     
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));
604 static void
605 gdl_switcher_finalize (GObject *object)
607     GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
609     g_free (priv);
611     GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
614 /* Signal handlers */
616 static void 
617 gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec,
618                         GdlSwitcher *switcher) 
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);
626 static void
627 gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page,
628                              gint page_num, GdlSwitcher *switcher)
630     GtkWidget       *page_widget;
631     GtkWidget       *tablabel;
632     gint             switcher_id;
633     
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);
640 static void
641 gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page,
642                             gint page_num, GdlSwitcher *switcher)
644     gint         switcher_id;
645  
646     switcher_id = gdl_switcher_get_page_id (page);
647     
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);
653 static void
654 gdl_switcher_select_page (GdlSwitcher *switcher, gint id)
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);
682 /* Initialization.  */
684 static void
685 gdl_switcher_class_init (GdlSwitcherClass *klass)
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;
699     
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;
704     
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));
714 static void
715 gdl_switcher_instance_init (GdlSwitcher *switcher)
717     GdlSwitcherPrivate *priv;
719     GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW);
720   
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);
731     
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);
741 GtkWidget *
742 gdl_switcher_new (void)
744     GdlSwitcher *switcher = g_object_new (gdl_switcher_get_type (), NULL);
745     return GTK_WIDGET (switcher);
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)
753     GtkWidget *button_widget;
754     GtkWidget *hbox;
755     GtkWidget *icon_widget;
756     GtkWidget *label_widget;
757     GtkWidget *arrow;
758     GtkTooltips *button_tooltips;
759     
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);
779     
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);
812     
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));
823 static void
824 gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id)
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));
841 static void
842 gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id)
844     update_buttons (switcher, switcher_id);
845     
846     /* Select the notebook page associated with this button */
847     gdl_switcher_select_page (switcher, switcher_id);
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)
856     gint ret_position;
857     gint switcher_id;
858     g_signal_handlers_block_by_func (switcher,
859                                      gdl_switcher_page_added_cb,
860                                      switcher);
861     
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;
876 static void
877 set_switcher_style_internal (GdlSwitcher *switcher,
878                              GdlSwitcherStyle switcher_style )
880     GSList *p;
881     
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     }
891     
892     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
893     
894     if (switcher_style == INTERNAL_MODE (switcher))
895         return;
896     
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     }
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)
964     GdlSwitcher *switcher = data;
965     char *val;
966     int switcher_style;    
967     
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));
982 static void
983 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
985     GConfClient *gconf_client = gconf_client_get_default ();
986     
987     if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
988         switcher->priv->show == FALSE)
989         return;
990     
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     }
1002     
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     }
1021     
1022     g_object_unref (gconf_client);
1024     if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1025         switcher->priv->switcher_style = switcher_style;
1028 #else /* HAVE_GNOME */
1030 static void
1031 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
1033     if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
1034         switcher->priv->show == FALSE)
1035         return;
1036     
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));
1046     
1047     if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1048         switcher->priv->switcher_style = switcher_style;
1051 #endif /* HAVE_GNOME */
1053 static void
1054 gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show)
1056     GSList *p;
1058     if (switcher->priv->show == show)
1059         return;
1060     
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));
1075 static GdlSwitcherStyle
1076 gdl_switcher_get_style (GdlSwitcher *switcher)
1078     if (!switcher->priv->show)
1079         return GDL_SWITCHER_STYLE_TABS;
1080     return switcher->priv->switcher_style;