Code

Extensions. XAML export improvements.
[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 #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;
88     
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)
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;
132 static void
133 button_free (Button *button)
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);
143 static gint
144 gdl_switcher_get_page_id (GtkWidget *widget)
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;
158 static void
159 update_buttons (GdlSwitcher *switcher, int new_selected_id)
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;
182 /* Callbacks.  */
184 static void
185 button_toggled_callback (GtkToggleButton *toggle_button,
186                          GdlSwitcher *switcher)
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     }
214     
215     switcher->priv->in_toggle = FALSE;
217     if (is_active)
218     {
219         gdl_switcher_select_page (switcher, id);
220     }
223 /* Returns -1 if layout didn't happen because a resize request was queued */
224 static int
225 layout_buttons (GdlSwitcher *switcher)
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;
243     
244     last_buttons_height = switcher->priv->buttons_height_request;
245     
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);
256     
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);
272     
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++;
290      
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     }
330     
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     }
341     
342     /* Layout the buttons. */
343     for (i = row_last; i >= 0; i --) {
344         int len, extra_width;
345         
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;
369             
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     }
392     
393     for (i = 0; i <= row_last; i ++)
394         g_slist_free (rows [i]);
395     g_free (rows);
397     return y;
400 static void
401 do_layout (GdlSwitcher *switcher)
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;
414     
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;
420     
421     GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_allocate,
422                      (GTK_WIDGET (switcher), &child_allocation));
425 /* GtkContainer methods.  */
427 static void
428 gdl_switcher_forall (GtkContainer *container, gboolean include_internals,
429                      GtkCallback callback, void *callback_data)
431     GdlSwitcher *switcher =
432         GDL_SWITCHER (container);
433     GSList *p;
434     
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     }
446 static void
447 gdl_switcher_remove (GtkContainer *container, GtkWidget *widget)
449     gint switcher_id;
450     GdlSwitcher *switcher =
451         GDL_SWITCHER (container);
452     GSList *p;
453     
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));
471 /* GtkWidget methods.  */
473 static void
474 gdl_switcher_size_request (GtkWidget *widget, GtkRequisition *requisition)
476     GdlSwitcher *switcher = GDL_SWITCHER (widget);
477     GSList *p;
478     gint button_height = 0;
479     
480     GDL_CALL_PARENT (GTK_WIDGET_CLASS, size_request,
481                      (GTK_WIDGET (switcher), requisition));
483     if (!switcher->priv->show)
484         return;
485     
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     }
497     
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     }
505 static void
506 gdl_switcher_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
508     widget->allocation = *allocation;
509     do_layout (GDL_SWITCHER (widget));
512 static gint
513 gdl_switcher_expose (GtkWidget *widget, GdkEventExpose *event)
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);
528 static void
529 gdl_switcher_map (GtkWidget *widget)
531     GSList *p;
532     GdlSwitcher *switcher = GDL_SWITCHER (widget);
533     
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));
543 /* GObject methods.  */
545 static void
546 gdl_switcher_set_property  (GObject      *object,
547                             guint         prop_id,
548                             const GValue *value,
549                             GParamSpec   *pspec)
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     }
563 static void
564 gdl_switcher_get_property  (GObject      *object,
565                             guint         prop_id,
566                             GValue       *value,
567                             GParamSpec   *pspec)
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     }
581 static void
582 gdl_switcher_dispose (GObject *object)
584     GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
585     
586 #if HAVE_GNOME
587     GConfClient *gconf_client = gconf_client_get_default ();
588     
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
595     
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));
603 static void
604 gdl_switcher_finalize (GObject *object)
606     GdlSwitcherPrivate *priv = GDL_SWITCHER (object)->priv;
608     g_free (priv);
610     GDL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
613 /* Signal handlers */
615 static void 
616 gdl_switcher_notify_cb (GObject *g_object, GParamSpec *pspec,
617                         GdlSwitcher *switcher) 
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);
625 static void
626 gdl_switcher_switch_page_cb (GtkNotebook *nb, GtkNotebookPage *page,
627                              gint page_num, GdlSwitcher *switcher)
629     GtkWidget       *page_widget;
630     GtkWidget       *tablabel;
631     gint             switcher_id;
632     
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);
639 static void
640 gdl_switcher_page_added_cb (GtkNotebook *nb, GtkWidget *page,
641                             gint page_num, GdlSwitcher *switcher)
643     gint         switcher_id;
644  
645     switcher_id = gdl_switcher_get_page_id (page);
646     
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);
652 static void
653 gdl_switcher_select_page (GdlSwitcher *switcher, gint id)
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);
681 /* Initialization.  */
683 static void
684 gdl_switcher_class_init (GdlSwitcherClass *klass)
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;
698     
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;
703     
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));
713 static void
714 gdl_switcher_instance_init (GdlSwitcher *switcher)
716     GdlSwitcherPrivate *priv;
718     GTK_WIDGET_SET_FLAGS (switcher, GTK_NO_WINDOW);
719   
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);
730     
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);
740 GtkWidget *
741 gdl_switcher_new (void)
743     GdlSwitcher *switcher = g_object_new (gdl_switcher_get_type (), NULL);
744     return GTK_WIDGET (switcher);
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)
752     GtkWidget *button_widget;
753     GtkWidget *hbox;
754     GtkWidget *icon_widget;
755     GtkWidget *label_widget;
756     GtkWidget *arrow;
757     GtkTooltips *button_tooltips;
758     
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);
778     
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);
811     
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));
822 static void
823 gdl_switcher_remove_button (GdlSwitcher *switcher, gint switcher_id)
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));
840 static void
841 gdl_switcher_select_button (GdlSwitcher *switcher, gint switcher_id)
843     update_buttons (switcher, switcher_id);
844     
845     /* Select the notebook page associated with this button */
846     gdl_switcher_select_page (switcher, switcher_id);
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)
855     gint ret_position;
856     gint switcher_id;
857     g_signal_handlers_block_by_func (switcher,
858                                      gdl_switcher_page_added_cb,
859                                      switcher);
860     
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;
875 static void
876 set_switcher_style_internal (GdlSwitcher *switcher,
877                              GdlSwitcherStyle switcher_style )
879     GSList *p;
880     
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     }
890     
891     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (switcher), FALSE);
892     
893     if (switcher_style == INTERNAL_MODE (switcher))
894         return;
895     
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     }
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)
963     GdlSwitcher *switcher = data;
964     char *val;
965     int switcher_style;    
966     
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));
981 static void
982 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
984     GConfClient *gconf_client = gconf_client_get_default ();
985     
986     if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
987         switcher->priv->show == FALSE)
988         return;
989     
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     }
1001     
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     }
1020     
1021     g_object_unref (gconf_client);
1023     if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1024         switcher->priv->switcher_style = switcher_style;
1027 #else /* HAVE_GNOME */
1029 static void
1030 gdl_switcher_set_style (GdlSwitcher *switcher, GdlSwitcherStyle switcher_style)
1032     if (switcher_style == GDL_SWITCHER_STYLE_TABS &&
1033         switcher->priv->show == FALSE)
1034         return;
1035     
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));
1045     
1046     if (switcher_style != GDL_SWITCHER_STYLE_TABS)
1047         switcher->priv->switcher_style = switcher_style;
1050 #endif /* HAVE_GNOME */
1052 static void
1053 gdl_switcher_set_show_buttons (GdlSwitcher *switcher, gboolean show)
1055     GSList *p;
1057     if (switcher->priv->show == show)
1058         return;
1059     
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));
1074 static GdlSwitcherStyle
1075 gdl_switcher_get_style (GdlSwitcher *switcher)
1077     if (!switcher->priv->show)
1078         return GDL_SWITCHER_STYLE_TABS;
1079     return switcher->priv->switcher_style;