[balsa] Identify important icon text



commit b655de2c24392b3d291ff5fa6c67835366dfb1cc
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Sat Oct 24 21:26:25 2009 -0400

    Identify important icon text

 ChangeLog                |    5 +
 src/toolbar-factory.c~   |  855 ++++++++++++++++++++++++++++++++++++++++++
 src/toolbar-factory.h~   |   96 +++++
 src/toolbar-factory.o    |  Bin 0 -> 79380 bytes
 src/toolbar-prefs.c.orig |  919 ++++++++++++++++++++++++++++++++++++++++++++++
 src/toolbar-prefs.c.rej  |   24 ++
 src/toolbar-prefs.c~     |  919 ++++++++++++++++++++++++++++++++++++++++++++++
 src/toolbar-prefs.o      |  Bin 0 -> 71292 bytes
 8 files changed, 2818 insertions(+), 0 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 2e479af..534cf8f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2009-10-24  Peter Bloomfield
+
+	* src/toolbar-factory.h: new member button_data::is_important.
+	* src/toolbar-factory.c (tm_set_tool_item_label): use it.
+
 2009-10-15  Peter Bloomfield
 
 	GSEAL cleanup
diff --git a/src/toolbar-factory.c~ b/src/toolbar-factory.c~
new file mode 100644
index 0000000..5a091e3
--- /dev/null
+++ b/src/toolbar-factory.c~
@@ -0,0 +1,855 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ * Copyright (C) 1997-2001 Stuart Parmenter and others,
+ *                         See the file AUTHORS for a list.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option) 
+ * any later version.
+ *  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
+ * GNU General Public License for more details.
+ *  
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
+ * 02111-1307, USA.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
+# include "config.h"
+#endif                          /* HAVE_CONFIG_H */
+#include "toolbar-factory.h"
+
+#include <string.h>
+#if HAVE_GNOME
+#include <gconf/gconf-client.h>
+#endif
+
+#include <glib/gi18n.h>
+
+#include "balsa-app.h"
+#include "balsa-icons.h"
+#include "main-window.h"
+#include "message-window.h"
+#include "sendmsg-window.h"
+#include "libbalsa-conf.h"
+
+#include "toolbar-prefs.h"
+
+/* Must be consistent with BalsaToolbarType enum: */
+static const gchar *const balsa_toolbar_names[] =
+    { "MainWindow", "ComposeWindow", "MessageWindow" };
+
+/*
+ * The BalsaToolbarModel class.
+ */
+
+struct BalsaToolbarModel_ {
+    GObject object;
+
+    GHashTable      *legal;
+    GSList          *standard;
+    GSList          *current;
+    BalsaToolbarType type;
+    GtkToolbarStyle  style;
+};
+
+enum {
+    CHANGED,
+    LAST_SIGNAL
+};
+
+static GtkObjectClass* parent_class;
+static guint model_signals[LAST_SIGNAL] = { 0 };
+
+static void
+balsa_toolbar_model_finalize(GObject * object)
+{
+    BalsaToolbarModel *model = BALSA_TOOLBAR_MODEL(object);
+    g_hash_table_destroy(model->legal);
+    G_OBJECT_CLASS(parent_class)->finalize(object);
+}
+
+static void
+balsa_toolbar_model_class_init(BalsaToolbarModelClass* klass)
+{
+    GObjectClass *object_class = (GObjectClass *) klass;
+
+    parent_class = g_type_class_peek_parent(klass);
+
+    model_signals[CHANGED] =
+        g_signal_new("changed", G_TYPE_FROM_CLASS(object_class),
+                     G_SIGNAL_RUN_FIRST,
+                     0,
+                     NULL, NULL,
+                     g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+
+    object_class = G_OBJECT_CLASS(klass);
+    object_class->finalize = balsa_toolbar_model_finalize;
+}
+
+static void
+balsa_toolbar_model_init(BalsaToolbarModel * model)
+{
+    model->legal =
+        g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+}
+
+GType
+balsa_toolbar_model_get_type()
+{
+    static GType balsa_toolbar_model_type = 0;
+
+    if (!balsa_toolbar_model_type) {
+        static const GTypeInfo balsa_toolbar_model_info = {
+            sizeof(BalsaToolbarModelClass),
+            NULL,               /* base_init */
+            NULL,               /* base_finalize */
+            (GClassInitFunc) balsa_toolbar_model_class_init,
+            NULL,               /* class_finalize */
+            NULL,               /* class_data */
+            sizeof(BalsaToolbarModel),
+            0,                  /* n_preallocs */
+            (GInstanceInitFunc) balsa_toolbar_model_init,
+        };
+
+        balsa_toolbar_model_type =
+            g_type_register_static(G_TYPE_OBJECT,
+                                   "BalsaToolbarModel",
+                                   &balsa_toolbar_model_info, 0);
+    }
+    
+    return balsa_toolbar_model_type;
+}
+
+/* End of class boilerplate */
+
+/* The descriptions must be SHORT */
+button_data toolbar_buttons[]={
+    {"",                         N_("Separator"),       FALSE},
+    {GTK_STOCK_QUIT,             N_("Quit"),            FALSE},
+    {BALSA_PIXMAP_RECEIVE,       N_("Check"),           TRUE},
+    {BALSA_PIXMAP_COMPOSE,       N_("Compose"),         FALSE},
+    {BALSA_PIXMAP_CONTINUE,      N_("Continue"),        FALSE},
+    {BALSA_PIXMAP_REPLY,         N_("Reply"),           TRUE},
+    {BALSA_PIXMAP_REPLY_ALL,     N_("Reply\nto all"),   FALSE},
+    {BALSA_PIXMAP_REPLY_GROUP,   N_("Reply\nto group"), FALSE},
+    {BALSA_PIXMAP_FORWARD,       N_("Forward"),         FALSE},
+    {BALSA_PIXMAP_PREVIOUS,      N_("Previous"),        FALSE},
+    {BALSA_PIXMAP_NEXT,          N_("Next"),            FALSE},
+    {BALSA_PIXMAP_NEXT_UNREAD,   N_("Next\nunread"),    TRUE},
+    {BALSA_PIXMAP_NEXT_FLAGGED,  N_("Next\nflagged"),   FALSE},
+    {BALSA_PIXMAP_PREVIOUS_PART, N_("Previous\npart"),  FALSE},
+    {BALSA_PIXMAP_NEXT_PART,     N_("Next\npart"),      FALSE},
+    {GTK_STOCK_DELETE,           N_("Trash /\nDelete"), TRUE},
+    {BALSA_PIXMAP_POSTPONE,      N_("Postpone"),        FALSE},
+    {GTK_STOCK_PRINT,            N_("Print"),           FALSE},
+    {BALSA_PIXMAP_REQUEST_MDN,   N_("Request\nMDN"),    FALSE},
+    {BALSA_PIXMAP_SEND,          N_("Send"),            TRUE},
+    {BALSA_PIXMAP_SEND_RECEIVE,  N_("Exchange"),        FALSE},
+    {BALSA_PIXMAP_ATTACHMENT,    N_("Attach"),          TRUE},
+    {GTK_STOCK_SAVE,             N_("Save"),            FALSE},
+    {BALSA_PIXMAP_IDENTITY,      N_("Identity"),        FALSE},
+    {GTK_STOCK_SPELL_CHECK,      N_("Spelling"),        TRUE},
+    {GTK_STOCK_CLOSE,            N_("Close"),           TRUE},
+    {BALSA_PIXMAP_MARKED_NEW,    N_("Toggle\nnew"),     FALSE},
+    {BALSA_PIXMAP_MARK_ALL,      N_("Mark all"),        FALSE},
+    {BALSA_PIXMAP_SHOW_HEADERS,  N_("All\nheaders"),    FALSE},
+    {GTK_STOCK_CANCEL,           N_("Reset\nFilter"),   FALSE},
+    {BALSA_PIXMAP_SHOW_PREVIEW,  N_("Msg Preview"),     FALSE},
+#ifdef HAVE_GPGME
+    {BALSA_PIXMAP_GPG_SIGN,      N_("Sign"),            FALSE},
+    {BALSA_PIXMAP_GPG_ENCRYPT,   N_("Encrypt"),         FALSE},
+#endif
+    {GTK_STOCK_UNDO,             N_("Undo"),            FALSE},
+    {GTK_STOCK_REDO,             N_("Redo"),            FALSE},
+    {GTK_STOCK_CLEAR,            N_("Expunge"),         FALSE},
+    {GTK_STOCK_REMOVE,           N_("Empty\nTrash"),    FALSE},
+    {GTK_STOCK_EDIT,             N_("Edit"),            FALSE},
+};
+
+const int toolbar_button_count =
+    sizeof(toolbar_buttons) / sizeof(button_data);
+
+/* Public methods. */
+const gchar *
+balsa_toolbar_button_text(gint button)
+{
+    return _(strcmp(toolbar_buttons[button].pixmap_id,
+                    BALSA_PIXMAP_SEND) == 0
+             && balsa_app.always_queue_sent_mail ?
+             N_("Queue") : toolbar_buttons[button].button_text);
+}
+
+const gchar *
+balsa_toolbar_sanitize_id(const gchar *id)
+{
+    gint button = get_toolbar_button_index(id);
+
+    if (button >= 0)
+	return toolbar_buttons[button].pixmap_id;
+    else
+	return NULL;
+}
+
+/* this should go to GTK because it modifies its internal structures. */
+void
+balsa_toolbar_remove_all(GtkWidget * widget)
+{
+    GList *child, *children;
+    
+    children = gtk_container_get_children(GTK_CONTAINER(widget));
+    for (child = children; child; child = child->next)
+        gtk_widget_destroy(child->data);
+    g_list_free(children);
+}
+
+/* Load and save config
+ */
+
+static void
+tm_load_model(BalsaToolbarModel * model)
+{
+    gchar *key;
+    guint j;
+
+    key = g_strconcat("toolbar-", balsa_toolbar_names[model->type], NULL);
+    libbalsa_conf_push_group(key);
+    g_free(key);
+
+    model->style = libbalsa_conf_get_int_with_default("Style=-1", NULL);
+
+    model->current = NULL;
+    for (j = 0;; j++) {
+        gchar *item;
+
+        key = g_strdup_printf("Item%d", j);
+        item = libbalsa_conf_get_string(key);
+        g_free(key);
+
+        if (!item)
+            break;
+
+        model->current = g_slist_prepend(model->current, item);
+    }
+    model->current = g_slist_reverse(model->current);
+
+    libbalsa_conf_pop_group();
+}
+
+static void
+tm_save_model(BalsaToolbarModel * model)
+{
+    gchar *key;
+    guint j;
+    GSList *list;
+
+    key = g_strconcat("toolbar-", balsa_toolbar_names[model->type], NULL);
+    libbalsa_conf_remove_group(key);
+    libbalsa_conf_push_group(key);
+    g_free(key);
+
+    if (model->style != (GtkToolbarStyle) (-1))
+        libbalsa_conf_set_int("Style", model->style);
+
+    for (j = 0, list = model->current;
+         list;
+         j++, list = list->next) {
+        key = g_strdup_printf("Item%d", j);
+        libbalsa_conf_set_string(key, list->data);
+        g_free(key);
+    }
+
+    libbalsa_conf_pop_group();
+}
+
+#if HAVE_GNOME
+/* GConfClientNotifyFunc
+ */
+static void
+tm_gconf_notify(GConfClient * client, guint cnxn_id, GConfEntry * entry,
+                BalsaToolbarModel * model)
+{
+    if (model->style == (GtkToolbarStyle) (-1))
+        balsa_toolbar_model_changed(model);
+}
+#endif /* HAVE_GNOME */
+
+/* Create a BalsaToolbarModel structure.
+ */
+BalsaToolbarModel *
+balsa_toolbar_model_new(BalsaToolbarType type, GSList * standard)
+{
+    BalsaToolbarModel *model =
+        g_object_new(BALSA_TYPE_TOOLBAR_MODEL, NULL);
+#if HAVE_GNOME
+    GConfClient *conf;
+    guint notify_id;
+#endif
+
+    model->type = type;
+    model->standard = standard;
+    tm_load_model(model);
+
+#if HAVE_GNOME
+    conf = gconf_client_get_default();
+    /* We never destroy a model, so we do nothing with the notify-id: */
+    notify_id =
+        gconf_client_notify_add(conf,
+                                "/desktop/gnome/interface/toolbar_style",
+                                (GConfClientNotifyFunc) tm_gconf_notify,
+                                model, NULL, NULL);
+#endif /* HAVE_GNOME */
+
+    return model;
+}
+
+/* balsa_toolbar_model_changed:
+   Update all toolbars derived from the model.
+
+   Called from toolbar-prefs.c after a change has been made to a toolbar
+   layout.
+*/
+void
+balsa_toolbar_model_changed(BalsaToolbarModel * model)
+{
+    g_signal_emit(model, model_signals[CHANGED], 0);
+}
+
+static void
+tm_add_action(BalsaToolbarModel * model, const gchar * stock_id,
+              const gchar * name)
+{
+    /* Check whether we have already seen this icon: */
+    if (stock_id && !g_hash_table_lookup(model->legal, stock_id))
+        g_hash_table_insert(model->legal, (gchar *) stock_id,
+                            (gchar *) name);
+}
+
+void
+balsa_toolbar_model_add_actions(BalsaToolbarModel * model,
+                                const GtkActionEntry * entries,
+                                guint n_entries)
+{
+    guint i;
+
+    for (i = 0; i < n_entries; i++)
+        tm_add_action(model, entries[i].stock_id, entries[i].name);
+}
+
+void
+balsa_toolbar_model_add_toggle_actions(BalsaToolbarModel * model,
+                                       const GtkToggleActionEntry *
+                                       entries, guint n_entries)
+{
+    guint i;
+
+    for (i = 0; i < n_entries; i++)
+        tm_add_action(model, entries[i].stock_id, entries[i].name);
+}
+
+/* Return the legal icons.
+ */
+GHashTable *
+balsa_toolbar_model_get_legal(BalsaToolbarModel * model)
+{
+    return model->legal;
+}
+
+/* Return the current icons.
+ */
+GSList *
+balsa_toolbar_model_get_current(BalsaToolbarModel * model)
+{
+    return model->current ? model->current : model->standard;
+}
+
+gboolean
+balsa_toolbar_model_is_standard(BalsaToolbarModel * model)
+{
+    return model->current == NULL;
+}
+
+/* Add an icon to the list of current icons in a BalsaToolbarModel.
+ */
+void
+balsa_toolbar_model_insert_icon(BalsaToolbarModel * model, gchar * icon,
+                                gint position)
+{
+    const gchar *real_button = balsa_toolbar_sanitize_id(icon);
+
+    if (real_button)
+        model->current =
+            g_slist_insert(model->current, g_strdup(real_button),
+                           position);
+    else
+        g_warning(_("Unknown toolbar icon \"%s\""), icon);
+}
+
+/* Remove all icons from the BalsaToolbarModel.
+ */
+void
+balsa_toolbar_model_clear(BalsaToolbarModel * model)
+{
+    g_slist_foreach(model->current, (GFunc) g_free, NULL);
+    g_slist_free(model->current);
+    model->current = NULL;
+}
+
+/* Create a new instance of a toolbar
+ */
+
+static gboolean
+tm_has_second_line(BalsaToolbarModel * model)
+{
+    GSList *list;
+
+    /* Find out whether any button has 2 lines of text. */
+    for (list = balsa_toolbar_model_get_current(model); list;
+         list = list->next) {
+        const gchar *icon = list->data;
+        gint button = get_toolbar_button_index(icon);
+
+        if (button < 0) {
+            g_warning("button '%s' not found. ABORT!\n", icon);
+            continue;
+        }
+
+        if (strchr(balsa_toolbar_button_text(button), '\n'))
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+static gint
+tm_set_tool_item_label(GtkToolItem * tool_item, const gchar * stock_id,
+                       gboolean make_two_line)
+{
+    gint button = get_toolbar_button_index(stock_id);
+    const gchar *text;
+    gchar *label;
+
+    if (button < 0)
+        return button;
+
+    text = balsa_toolbar_button_text(button);
+    if (balsa_app.toolbar_wrap_button_text) {
+        /* Make sure all buttons have the same number of lines of
+         * text (1 or 2), to keep icons aligned */
+        label = make_two_line && !strchr(text, '\n') ?
+            g_strconcat(text, "\n", NULL) : g_strdup(text);
+    } else {
+        gchar *p = label = g_strdup(text);
+        while ((p = strchr(p, '\n')))
+            *p++ = ' ';
+    }
+
+    gtk_tool_button_set_label(GTK_TOOL_BUTTON(tool_item), label);
+    g_free(label);
+
+    gtk_tool_item_set_is_important(tool_item,
+                                   toolbar_buttons[button].is_important);
+
+    if (strcmp(toolbar_buttons[button].pixmap_id, BALSA_PIXMAP_SEND) == 0
+        && balsa_app.always_queue_sent_mail)
+        gtk_tool_item_set_tooltip_text(tool_item,
+                                       _("Queue this message for sending"));
+
+    return button;
+}
+
+static void
+tm_populate(BalsaToolbarModel * model, GtkUIManager * ui_manager,
+            GArray * merge_ids)
+{
+    gboolean make_two_line = model->style != GTK_TOOLBAR_BOTH_HORIZ
+        && tm_has_second_line(model);
+    GSList *list;
+
+    for (list = balsa_toolbar_model_get_current(model); list;
+         list = list->next) {
+        const gchar *stock_id = list->data;
+        guint merge_id = gtk_ui_manager_new_merge_id(ui_manager);
+
+        g_array_append_val(merge_ids, merge_id);
+
+        if (!*stock_id)
+            gtk_ui_manager_add_ui(ui_manager, merge_id, "/Toolbar",
+                                  NULL, NULL, GTK_UI_MANAGER_SEPARATOR,
+                                  FALSE);
+        else {
+            gchar *path, *name;
+            GtkWidget *tool_item;
+
+            name = g_hash_table_lookup(model->legal, stock_id);
+            if (!name) {
+                g_warning("no name for stock_id \"%s\"", stock_id);
+                continue;
+            }
+            gtk_ui_manager_add_ui(ui_manager, merge_id, "/Toolbar",
+                                  name, name, GTK_UI_MANAGER_AUTO, FALSE);
+            /* Replace the long menu-item label with the short
+             * tool-button label: */
+            path = g_strconcat("/Toolbar/", name, NULL);
+            tool_item = gtk_ui_manager_get_widget(ui_manager, path);
+            g_free(path);
+            tm_set_tool_item_label(GTK_TOOL_ITEM(tool_item), stock_id,
+                                   make_two_line);
+        }
+    }
+}
+
+#define BALSA_TOOLBAR_MERGE_IDS "balsa-toolbar-merge-ids"
+static void
+bt_free_merge_ids(GArray * merge_ids)
+{
+    g_array_free(merge_ids, TRUE);
+}
+
+static const struct {
+    const gchar *text;
+    const gchar *config_name;
+    GtkToolbarStyle style;
+} tm_toolbar_options[] = {
+    {N_("Text Be_low Icons"),           "both",       GTK_TOOLBAR_BOTH},
+    {N_("Priority Text Be_side Icons"), "both-horiz", GTK_TOOLBAR_BOTH_HORIZ},
+    {NULL,                              "both_horiz", GTK_TOOLBAR_BOTH_HORIZ},
+    {N_("_Icons Only"),                 "icons",      GTK_TOOLBAR_ICONS},
+    {N_("_Text Only"),                  "text",       GTK_TOOLBAR_TEXT}
+};
+
+static GtkToolbarStyle
+tm_default_style(void)
+{
+    GtkToolbarStyle default_style = GTK_TOOLBAR_BOTH;
+#if HAVE_GNOME
+    GConfClient *conf;
+    gchar *str;
+
+    /* Get global setting */
+    conf = gconf_client_get_default();
+    str  = gconf_client_get_string(conf,
+                                   "/desktop/gnome/interface/toolbar_style",
+                                   NULL);
+    if (str) {
+        guint i;
+
+        for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++)
+            if (strcmp(tm_toolbar_options[i].config_name, str) == 0) {
+                default_style = tm_toolbar_options[i].style;
+                break;
+            }
+        g_free(str);
+    }
+#endif /* HAVE_GNOME */
+
+    return default_style;
+}
+
+static void
+tm_set_style(GtkWidget * toolbar, BalsaToolbarModel * model)
+{
+    gtk_toolbar_set_style(GTK_TOOLBAR(toolbar),
+                          model->style != (GtkToolbarStyle) (-1) ?
+                          model->style : tm_default_style());
+}
+
+/* Update a real toolbar when the model has changed.
+ */
+static void
+tm_changed_cb(BalsaToolbarModel * model, GtkUIManager * ui_manager)
+{
+    GArray *merge_ids =
+        g_object_get_data(G_OBJECT(ui_manager), BALSA_TOOLBAR_MERGE_IDS);
+    guint i;
+    GtkWidget *toolbar;
+
+    for (i = 0; i < merge_ids->len; i++) {
+        guint merge_id = g_array_index(merge_ids, guint, i);
+        gtk_ui_manager_remove_ui(ui_manager, merge_id);
+    }
+    merge_ids->len = 0;
+
+    tm_populate(model, ui_manager, merge_ids);
+
+    toolbar = gtk_ui_manager_get_widget(ui_manager, "/Toolbar");
+    tm_set_style(toolbar, model);
+
+    tm_save_model(model);
+}
+
+typedef struct {
+    BalsaToolbarModel *model;
+    GtkUIManager      *ui_manager;
+    GtkWidget         *menu;
+} toolbar_info;
+
+static void
+tm_toolbar_weak_notify(toolbar_info * info, GtkWidget * toolbar)
+{
+    g_signal_handlers_disconnect_by_func(info->model, tm_changed_cb,
+                                         info->ui_manager);
+    g_object_unref(info->ui_manager);
+    g_free(info);
+}
+
+#define BALSA_TOOLBAR_STYLE "balsa-toolbar-style"
+static void
+menu_item_toggled_cb(GtkCheckMenuItem * item, toolbar_info * info)
+{
+    if (gtk_check_menu_item_get_active(item)) {
+        info->model->style =
+            GPOINTER_TO_INT(g_object_get_data
+                            (G_OBJECT(item), BALSA_TOOLBAR_STYLE));
+        balsa_toolbar_model_changed(info->model);
+        if (info->menu)
+            gtk_menu_shell_deactivate(GTK_MENU_SHELL(info->menu));
+    }
+}
+
+/* We want to destroy the popup menu after handling the "toggled"
+ * signal; the "deactivate" signal is apparently emitted before
+ * "toggled", so we have to use an idle callback. */
+static gboolean
+tm_popup_idle_cb(GtkWidget *menu)
+{
+    gtk_widget_destroy(menu);
+    return FALSE;
+}
+
+static void
+tm_popup_deactivated_cb(GtkWidget * menu, toolbar_info * info)
+{
+    if (info->menu) {
+        g_idle_add((GSourceFunc) tm_popup_idle_cb, menu);
+        info->menu = NULL;
+    }
+}
+
+static gchar *
+tm_remove_underscore(const gchar * text)
+{
+    gchar *p, *q, *r = g_strdup(text);
+
+    for (p = q = r; *p; p++)
+        if (*p != '_')
+            *q++ = *p;
+    *q = '\0';
+
+    return r;
+}
+
+/* If the menu is popped up in response to a keystroke, center it
+ * immediately below the toolbar.
+ */
+static void
+tm_popup_position_func(GtkMenu * menu, gint * x, gint * y,
+                       gboolean * push_in, gpointer user_data)
+{
+    GtkWidget *toolbar = GTK_WIDGET(user_data);
+    GdkScreen *screen = gtk_widget_get_screen(toolbar);
+    GtkRequisition req;
+    gint monitor_num;
+    GdkRectangle monitor;
+#if GTK_CHECK_VERSION(2, 18, 0)
+    GtkAllocation allocation;
+#endif                          /* GTK_CHECK_VERSION(2, 18, 0) */
+
+    g_return_if_fail(gtk_widget_get_window(toolbar));
+
+    gdk_window_get_origin(gtk_widget_get_window(toolbar), x, y);
+
+    gtk_widget_size_request(GTK_WIDGET(menu), &req);
+
+#if GTK_CHECK_VERSION(2, 18, 0)
+    gtk_widget_get_allocation(toolbar, &allocation);
+    *x += (allocation.width - req.width) / 2;
+    *y += allocation.height;
+#else                           /* GTK_CHECK_VERSION(2, 18, 0) */
+    *x += (toolbar->allocation.width - req.width) / 2;
+    *y += toolbar->allocation.height;
+#endif                          /* GTK_CHECK_VERSION(2, 18, 0) */
+
+    monitor_num = gdk_screen_get_monitor_at_point(screen, *x, *y);
+    gtk_menu_set_monitor(menu, monitor_num);
+    gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor);
+
+    *x = CLAMP(*x, monitor.x,
+               monitor.x + MAX(0, monitor.width - req.width));
+    *y = CLAMP(*y, monitor.y,
+               monitor.y + MAX(0, monitor.height - req.height));
+
+    *push_in = FALSE;
+}
+
+static gboolean
+tm_do_popup_menu(GtkWidget * toolbar, GdkEventButton * event,
+                 toolbar_info * info)
+{
+    GtkWidget *menu;
+    int button, event_time;
+    guint i;
+    GSList *group = NULL;
+    GtkToolbarStyle default_style = tm_default_style();
+
+    if (info->menu)
+        return FALSE;
+
+    info->menu = menu = gtk_menu_new();
+    g_signal_connect(menu, "deactivate",
+                     G_CALLBACK(tm_popup_deactivated_cb), info);
+
+    /* ... add menu items ... */
+    for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
+        GtkWidget *item;
+
+        if (!tm_toolbar_options[i].text)
+            continue;
+
+        item =
+            gtk_radio_menu_item_new_with_mnemonic(group,
+                                                  _(tm_toolbar_options[i].
+                                                    text));
+        group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
+
+        if (tm_toolbar_options[i].style == info->model->style)
+            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item),
+                                           TRUE);
+
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+        g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_STYLE,
+                          GINT_TO_POINTER(tm_toolbar_options[i].style));
+        g_signal_connect(item, "toggled", G_CALLBACK(menu_item_toggled_cb),
+                         info);
+    }
+
+    for (i = 0; i < G_N_ELEMENTS(tm_toolbar_options); i++) {
+
+        if (!tm_toolbar_options[i].text)
+            continue;
+
+        if (tm_toolbar_options[i].style == default_style) {
+            gchar *option_text, *text;
+            GtkWidget *item;
+
+            gtk_menu_shell_append(GTK_MENU_SHELL(menu),
+                                  gtk_separator_menu_item_new());
+
+            option_text =
+                tm_remove_underscore(_(tm_toolbar_options[i].text));
+            text =
+                g_strdup_printf(_("Use Desktop _Default (%s)"),
+                                option_text);
+            g_free(option_text);
+
+            item = gtk_radio_menu_item_new_with_mnemonic(group, text);
+            g_free(text);
+
+            if (info->model->style == (GtkToolbarStyle) (-1))
+                gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM
+                                               (item), TRUE);
+            gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+            g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_STYLE,
+                              GINT_TO_POINTER(-1));
+            g_signal_connect(item, "toggled",
+                             G_CALLBACK(menu_item_toggled_cb), info);
+        }
+    }
+
+#if GTK_CHECK_VERSION(2, 18, 0)
+    if (gtk_widget_is_sensitive(toolbar)) {
+#else                           /* GTK_CHECK_VERSION(2, 18, 0) */
+    if (GTK_WIDGET_IS_SENSITIVE(toolbar)) {
+#endif                          /* GTK_CHECK_VERSION(2, 18, 0) */
+        /* This is a real toolbar, not the template from the
+         * toolbar-prefs dialog. */
+        GtkWidget *item;
+
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu),
+                              gtk_separator_menu_item_new());
+        item =
+            gtk_menu_item_new_with_mnemonic(_("_Customize Toolbars..."));
+        g_signal_connect(item, "activate", G_CALLBACK(customize_dialog_cb),
+                         gtk_widget_get_toplevel(toolbar));
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+        /* Pass the model type to the customize widget, so that it can
+         * show the appropriate notebook page. */
+        g_object_set_data(G_OBJECT(item), BALSA_TOOLBAR_MODEL_TYPE,
+                          GINT_TO_POINTER(info->model->type));
+    }
+
+    gtk_widget_show_all(menu);
+
+    if (event) {
+        button = event->button;
+        event_time = event->time;
+    } else {
+        button = 0;
+        event_time = gtk_get_current_event_time();
+    }
+
+    gtk_menu_attach_to_widget(GTK_MENU(menu), toolbar, NULL);
+    if (button)
+        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, button,
+                       event_time);
+    else
+        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, tm_popup_position_func,
+                       toolbar, button, event_time);
+
+    return TRUE;
+}
+
+static gboolean
+tm_button_press_cb(GtkWidget * toolbar, GdkEventButton * event,
+                   toolbar_info * info)
+{
+    /* Ignore double-clicks and triple-clicks */
+    if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
+        return tm_do_popup_menu(toolbar, event, info);
+
+    return FALSE;
+}
+
+static gboolean
+tm_popup_menu_cb(GtkWidget * toolbar, toolbar_info * info)
+{
+    return tm_do_popup_menu(toolbar, NULL, info);
+}
+
+GtkWidget *balsa_toolbar_new(BalsaToolbarModel * model,
+                             GtkUIManager * ui_manager)
+{
+    GtkWidget *toolbar;
+    toolbar_info *info;
+    GArray *merge_ids = g_array_new(FALSE, FALSE, sizeof(guint));
+
+    g_object_set_data_full(G_OBJECT(ui_manager), BALSA_TOOLBAR_MERGE_IDS,
+                           merge_ids, (GDestroyNotify) bt_free_merge_ids);
+
+    tm_populate(model, ui_manager, merge_ids);
+    g_signal_connect(model, "changed", G_CALLBACK(tm_changed_cb),
+                     ui_manager);
+
+    info = g_new(toolbar_info, 1);
+    info->model = model;
+    info->ui_manager = g_object_ref(ui_manager);
+    info->menu = NULL;
+
+    toolbar = gtk_ui_manager_get_widget(ui_manager, "/Toolbar");
+    tm_set_style(toolbar, model);
+    g_object_weak_ref(G_OBJECT(toolbar),
+                      (GWeakNotify) tm_toolbar_weak_notify, info);
+
+    g_signal_connect(toolbar, "button-press-event",
+                     G_CALLBACK(tm_button_press_cb), info);
+    g_signal_connect(toolbar, "popup-menu", G_CALLBACK(tm_popup_menu_cb),
+                     info);
+
+    return toolbar;
+}
diff --git a/src/toolbar-factory.h~ b/src/toolbar-factory.h~
new file mode 100644
index 0000000..05c5f85
--- /dev/null
+++ b/src/toolbar-factory.h~
@@ -0,0 +1,96 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ * Copyright (C) 1997-2001 Stuart Parmenter and others,
+ *                         See the file AUTHORS for a list.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option) 
+ * any later version.
+ *  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
+ * GNU General Public License for more details.
+ *  
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
+ * 02111-1307, USA.
+ */
+
+#ifndef __TOOLBAR_FACTORY_H__
+#define __TOOLBAR_FACTORY_H__
+
+#include <glib-object.h>
+#include "toolbar-prefs.h"
+
+GType balsa_toolbar_model_get_type(void);
+
+#define BALSA_TYPE_TOOLBAR_MODEL \
+    (balsa_toolbar_model_get_type ())
+#define BALSA_TOOLBAR_MODEL(obj) \
+    (G_TYPE_CHECK_INSTANCE_CAST (obj, BALSA_TYPE_TOOLBAR_MODEL, \
+                                 BalsaToolbarModel))
+#define BALSA_TOOLBAR_MODEL_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_CAST (klass, BALSA_TYPE_TOOLBAR_MODEL, \
+                              BalsaToolbarModelClass))
+#define BALSA_IS_TOOLBAR_MODEL(obj) \
+    (G_TYPE_CHECK_INSTANCE_TYPE (obj, BALSA_TYPE_TOOLBAR_MODEL))
+#define BALSA_IS_TOOLBAR_MODEL_CLASS(klass) \
+    (G_TYPE_CHECK_CLASS_TYPE (klass, BALSA_TYPE_TOOLBAR_MODEL))
+
+typedef struct BalsaToolbarModel_ BalsaToolbarModel;
+typedef struct BalsaToolbarModelClass_ BalsaToolbarModelClass;
+
+struct BalsaToolbarModelClass_ {
+    GObjectClass parent_class;
+};
+typedef void (*BalsaToolbarFunc) (GtkWidget *, gpointer);
+#define BALSA_TOOLBAR_FUNC(f) ((BalsaToolbarFunc) (f))
+
+typedef struct t_button_data {
+    char *pixmap_id;            /* not translatable */
+    char *button_text;          /* translatable */
+} button_data;
+
+extern button_data toolbar_buttons[];
+extern const int toolbar_button_count;
+
+typedef enum {
+    BALSA_TOOLBAR_TYPE_MAIN_WINDOW,
+    BALSA_TOOLBAR_TYPE_COMPOSE_WINDOW,
+    BALSA_TOOLBAR_TYPE_MESSAGE_WINDOW
+} BalsaToolbarType;
+
+void update_all_toolbars(void);
+void balsa_toolbar_remove_all(GtkWidget * toolbar);
+
+/* toolbar code for gtk+-2 */
+const gchar *balsa_toolbar_button_text(gint button);
+const gchar *balsa_toolbar_sanitize_id(const gchar * id);
+
+/* BalsaToolbarModel */
+BalsaToolbarModel *balsa_toolbar_model_new(BalsaToolbarType type,
+                                           GSList * standard);
+void balsa_toolbar_model_add_actions(BalsaToolbarModel * model,
+                                     const GtkActionEntry * entries,
+                                     guint n_entries);
+void balsa_toolbar_model_add_toggle_actions(BalsaToolbarModel * model,
+                                            const GtkToggleActionEntry *
+                                            entries, guint n_entries);
+GHashTable *balsa_toolbar_model_get_legal(BalsaToolbarModel * model);
+GSList *balsa_toolbar_model_get_current(BalsaToolbarModel * model);
+gboolean balsa_toolbar_model_is_standard(BalsaToolbarModel * model);
+void balsa_toolbar_model_insert_icon(BalsaToolbarModel * model,
+                                     gchar * icon, gint position);
+void balsa_toolbar_model_delete_icon(BalsaToolbarModel * model,
+                                     gchar * icon);
+void balsa_toolbar_model_clear(BalsaToolbarModel * model);
+void balsa_toolbar_model_changed(BalsaToolbarModel * model);
+
+/* BalsaToolbar */
+GtkWidget *balsa_toolbar_new(BalsaToolbarModel * model,
+                             GtkUIManager * ui_manager);
+
+#endif
diff --git a/src/toolbar-factory.o b/src/toolbar-factory.o
new file mode 100644
index 0000000..94fd220
Binary files /dev/null and b/src/toolbar-factory.o differ
diff --git a/src/toolbar-prefs.c.orig b/src/toolbar-prefs.c.orig
new file mode 100644
index 0000000..4618b33
--- /dev/null
+++ b/src/toolbar-prefs.c.orig
@@ -0,0 +1,919 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ * Copyright (C) 1997-2002 Stuart Parmenter and others,
+ *                         See the file AUTHORS for a list.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option) 
+ * any later version.
+ *  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
+ * GNU General Public License for more details.
+ *  
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
+ * 02111-1307, USA.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
+# include "config.h"
+#endif                          /* HAVE_CONFIG_H */
+#include "toolbar-prefs.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "balsa-app.h"
+#include "balsa-icons.h"
+#include "main-window.h"
+#include "message-window.h"
+#include "sendmsg-window.h"
+#include "toolbar-factory.h"
+
+#if HAVE_MACOSX_DESKTOP
+#  include "macosx-helpers.h"
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifdef HAVE_GNOME
+#define OLD_BALSA_COMPATIBILITY_TRANSLATION
+#endif
+#ifdef OLD_BALSA_COMPATIBILITY_TRANSLATION
+#include <gnome.h> /* for GNOME_STOCK_* pixmaps */
+#endif
+
+/* Enumeration for GtkTreeModel columns. */
+enum {
+    TP_TEXT_COLUMN,
+    TP_ICON_COLUMN,
+    TP_ITEM_COLUMN,
+    TP_N_COLUMNS
+};
+
+static GtkWidget *customize_widget;
+
+/* Structure associated with each notebook page. */
+typedef struct ToolbarPage_ ToolbarPage;
+struct ToolbarPage_ {
+    BalsaToolbarModel *model;
+    GtkWidget *available;
+    GtkWidget *current;
+    GtkWidget *toolbar;
+    GtkWidget *add_button;
+    GtkWidget *remove_button;
+    GtkWidget *back_button;
+    GtkWidget *forward_button;
+    GtkWidget *standard_button;
+};
+
+/* Callbacks. */
+static void tp_dialog_response_cb(GtkDialog * dialog, gint response,
+                                  gpointer data);
+static void add_button_cb(GtkWidget *, ToolbarPage * page);
+static void remove_button_cb(GtkWidget *, ToolbarPage * page);
+static void back_button_cb(GtkWidget *, ToolbarPage * page);
+static void forward_button_cb(GtkWidget *, ToolbarPage * page);
+static void wrap_toggled_cb(GtkWidget * widget, GtkNotebook * notebook);
+static void available_selection_changed_cb(GtkTreeSelection * selection,
+                                           ToolbarPage * page);
+static void current_selection_changed_cb(GtkTreeSelection * selection,
+                                         ToolbarPage * page);
+static void available_row_activated_cb(GtkTreeView * treeview,
+                                       GtkTreeIter * arg1,
+                                       GtkTreePath * arg2,
+                                       ToolbarPage * page);
+static void current_row_activated_cb(GtkTreeView * treeview,
+                                     GtkTreeIter * arg1,
+                                     GtkTreePath * arg2,
+                                     ToolbarPage * page);
+
+/* Helpers. */
+static GtkWidget *create_toolbar_page(BalsaToolbarModel * model,
+                                      GtkUIManager * ui_manager);
+static GtkWidget *tp_list_new(void);
+static gboolean tp_list_iter_is_first(GtkWidget * list, GtkTreeIter * iter);
+static gboolean tp_list_iter_is_last(GtkWidget * list, GtkTreeIter * iter);
+static void tp_page_refresh_available(ToolbarPage * page);
+static void tp_page_refresh_current(ToolbarPage * page);
+static void tp_page_refresh_preview(ToolbarPage * page);
+static void tp_page_swap_rows(ToolbarPage * page, gboolean forward);
+static void tp_page_add_selected(ToolbarPage * page);
+static void tp_page_remove_selected(ToolbarPage * page);
+static void tp_store_set(GtkListStore * store, GtkTreeIter * iter,
+                         gint item);
+static void replace_nl_with_space(char* str);
+
+/* Public methods. */
+
+/* create the toolbar-customization dialog
+ */
+void
+customize_dialog_cb(GtkWidget * widget, gpointer data)
+{
+    GtkWidget *notebook;
+    GtkWidget *child;
+    GtkWidget *option_frame;
+    GtkWidget *option_box;
+    GtkWidget *wrap_button;
+    GtkWidget *active_window = data;
+    BalsaToolbarModel *model;
+    BalsaToolbarType   type;
+    GtkUIManager * ui_manager;
+
+    /* There can only be one */
+    if (customize_widget) {
+        gtk_window_present(GTK_WINDOW(customize_widget));
+        return;
+    }
+
+    customize_widget =
+        gtk_dialog_new_with_buttons(_("Customize Toolbars"),
+                                    GTK_WINDOW(active_window),
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+                                    GTK_STOCK_HELP,  GTK_RESPONSE_HELP,
+                                    NULL);
+#if HAVE_MACOSX_DESKTOP
+    libbalsa_macosx_menu_for_parent(customize_widget, GTK_WINDOW(active_window));
+#endif
+    g_object_add_weak_pointer(G_OBJECT(customize_widget),
+                              (gpointer) & customize_widget);
+    g_signal_connect(G_OBJECT(customize_widget), "response",
+                     G_CALLBACK(tp_dialog_response_cb), NULL);
+
+    notebook = gtk_notebook_new();
+    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(customize_widget)->vbox),
+                      notebook);
+
+    gtk_window_set_wmclass(GTK_WINDOW(customize_widget), "customize",
+                           "Balsa");
+    gtk_window_set_default_size(GTK_WINDOW(customize_widget), 600, 440);
+
+    /* The order of pages must be consistent with the BalsaToolbarType
+     * enum. */
+    model = balsa_window_get_toolbar_model();
+    ui_manager = balsa_window_ui_manager_new(NULL);
+    child = create_toolbar_page(model, ui_manager);
+    g_object_unref(ui_manager);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
+                             gtk_label_new(_("Main window")));
+
+    model = sendmsg_window_get_toolbar_model();
+    ui_manager = sendmsg_window_ui_manager_new(NULL);
+    child = create_toolbar_page(model, ui_manager);
+    g_object_unref(ui_manager);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
+                             gtk_label_new(_("Compose window")));
+
+    model = message_window_get_toolbar_model();
+    ui_manager = message_window_ui_manager_new(NULL);
+    child = create_toolbar_page(model, ui_manager);
+    g_object_unref(ui_manager);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
+                             gtk_label_new(_("Message window")));
+
+    option_frame = gtk_frame_new(_("Toolbar options"));
+    gtk_container_set_border_width(GTK_CONTAINER(option_frame), 6);
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(customize_widget)->vbox),
+                       option_frame, FALSE, FALSE, 0);
+
+    option_box = gtk_vbox_new(FALSE, 6);
+    gtk_container_set_border_width(GTK_CONTAINER(option_box), 6);
+    gtk_container_add(GTK_CONTAINER(option_frame), option_box);
+    
+    wrap_button =
+        gtk_check_button_new_with_mnemonic(_("_Wrap button labels"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wrap_button),
+                                 balsa_app.toolbar_wrap_button_text);
+    g_signal_connect(G_OBJECT(wrap_button), "toggled",
+                     G_CALLBACK(wrap_toggled_cb), notebook);
+    gtk_box_pack_start(GTK_BOX(option_box), wrap_button, FALSE, FALSE, 0);
+
+    gtk_widget_show_all(customize_widget);
+
+    /* Now that the pages are shown, we can switch to the page
+     * corresponding to the toolbar that the user clicked on. */
+    type =
+        GPOINTER_TO_INT(g_object_get_data
+                        (G_OBJECT(widget), BALSA_TOOLBAR_MODEL_TYPE));
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), type);
+}
+
+/* get_toolbar_button_index:
+   id - button id
+   returns -1 on failure.
+*/
+int
+get_toolbar_button_index(const char *id)
+{
+#ifdef OLD_BALSA_COMPATIBILITY_TRANSLATION
+    static const struct {
+        gchar *new;
+        gchar *old;
+    } button_converter[] = {
+        { BALSA_PIXMAP_ATTACHMENT,   GNOME_STOCK_ATTACH },
+        { BALSA_PIXMAP_COMPOSE,      GNOME_STOCK_MAIL_NEW },
+        { BALSA_PIXMAP_CONTINUE,     GNOME_STOCK_MAIL },
+        { BALSA_PIXMAP_RECEIVE,      GNOME_STOCK_MAIL_RCV },
+        { BALSA_PIXMAP_REPLY,        GNOME_STOCK_MAIL_RPL },
+        { BALSA_PIXMAP_REPLY_ALL,    "reply_to_all" },
+        { BALSA_PIXMAP_REPLY_GROUP,  "reply_to_group" },
+        { BALSA_PIXMAP_FORWARD,      GNOME_STOCK_MAIL_FWD },
+        { BALSA_PIXMAP_NEXT,         GTK_STOCK_GO_FORWARD },
+        { BALSA_PIXMAP_PREVIOUS,     GTK_STOCK_GO_BACK },
+	{ GTK_STOCK_PRINT,           BALSA_OLD_PIXMAP_PRINT },
+	{ GTK_STOCK_SAVE,            BALSA_OLD_PIXMAP_SAVE },
+        { BALSA_PIXMAP_SEND,         GNOME_STOCK_MAIL_SND },
+	{ GTK_STOCK_DELETE,          BALSA_OLD_PIXMAP_TRASH },
+        { BALSA_PIXMAP_TRASH_EMPTY,  "empty_trash" },
+        { BALSA_PIXMAP_NEXT_UNREAD,  "next_unread" },
+        { BALSA_PIXMAP_NEXT_FLAGGED, "next_flagged" },
+        { BALSA_PIXMAP_SHOW_HEADERS, "show_all_headers" },
+        { BALSA_PIXMAP_SHOW_PREVIEW, "show_preview" },
+        { BALSA_PIXMAP_MARKED_NEW,   "flag_unread" },
+        { BALSA_PIXMAP_MARK_ALL,     "mark_all" },
+        { BALSA_PIXMAP_IDENTITY,     "identity" },
+	{ GTK_STOCK_CANCEL,          BALSA_OLD_PIXMAP_CLOSE_MBOX },
+        { NULL, NULL }
+    };
+#endif
+    int i;
+
+    g_return_val_if_fail(id, -1);
+
+    for(i=0; i<toolbar_button_count; i++) {
+	if(!strcmp(id, toolbar_buttons[i].pixmap_id))
+	    return i;
+    }
+#ifdef OLD_BALSA_COMPATIBILITY_TRANSLATION
+    /* you have got a second chance.... */
+    
+    for(i=0; button_converter[i].new; i++) {
+        if(!strcmp(id, button_converter[i].old)) {
+            int j;
+            for(j=0; j<toolbar_button_count; j++) {
+                if(!strcmp(button_converter[i].new,
+                           toolbar_buttons[j].pixmap_id))
+                    return j;
+            }
+            return -1;
+        }
+    }
+#endif
+    return -1;
+}
+
+/* Callbacks. */
+
+#define BALSA_KEY_TOOLBAR_PAGE "balsa-toolbar-page"
+
+/* Callback for the wrap_buttons' "toggled" signal. */
+static void
+wrap_toggled_cb(GtkWidget * widget, GtkNotebook * notebook)
+{
+    gint i;
+    GtkWidget *child;
+    ToolbarPage *page;
+    
+    balsa_app.toolbar_wrap_button_text = GTK_TOGGLE_BUTTON(widget)->active;
+
+    for (i = 0; (child = gtk_notebook_get_nth_page(notebook, i)); i++) {
+        page = g_object_get_data(G_OBJECT(child), BALSA_KEY_TOOLBAR_PAGE);
+        balsa_toolbar_model_changed(page->model);
+    }
+}
+
+/* Button callbacks: each makes the appropriate change to the
+ * page->current GtkTreeView, then refreshes the page's
+ * BalsaToolbarModel and GtkToolbar; add_button_cb and remove_button_cb
+ * also refresh the page's available GtkTreeView.
+ */
+static void
+back_button_cb(GtkWidget * widget, ToolbarPage * page)
+{
+    tp_page_swap_rows(page, FALSE);
+}
+
+static void
+forward_button_cb(GtkWidget * widget, ToolbarPage * page)
+{
+    tp_page_swap_rows(page, TRUE);
+}
+
+static void
+add_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    tp_page_add_selected(page);
+}
+
+static void
+remove_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    tp_page_remove_selected(page);
+}
+
+static void
+standard_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    balsa_toolbar_model_clear(page->model);
+    gtk_widget_set_sensitive(page->standard_button, FALSE);
+    tp_page_refresh_available(page);
+    tp_page_refresh_current(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+static void
+style_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    gboolean handled;
+
+    g_signal_emit_by_name(page->toolbar, "popup-menu", &handled);
+}
+
+/* Callback for the "row-activated" signal for the available list. */
+static void
+available_row_activated_cb(GtkTreeView * treeview, GtkTreeIter * arg1,
+                        GtkTreePath * arg2, ToolbarPage * page)
+{
+    tp_page_add_selected(page);
+}
+
+/* Callback for the selection "changed" signal for the available list. */
+static void
+available_selection_changed_cb(GtkTreeSelection * selection,
+                            ToolbarPage * page)
+{
+    gtk_widget_set_sensitive(page->add_button,
+                             gtk_tree_selection_get_selected(selection,
+                                                             NULL, NULL));
+}
+
+/* Callback for the "row-activated" signal for the current list. */
+static void
+current_row_activated_cb(GtkTreeView * treeview, GtkTreeIter * arg1,
+                      GtkTreePath * arg2, ToolbarPage * page)
+{
+    tp_page_remove_selected(page);
+}
+
+/* Callback for the selection "changed" signal for the destination list. */
+static void
+current_selection_changed_cb(GtkTreeSelection * selection, ToolbarPage * page)
+{
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    gboolean remove = FALSE;
+    gboolean back = FALSE;
+    gboolean forward = FALSE;
+
+    if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+        remove = TRUE;
+        back =
+            !tp_list_iter_is_first(page->current,
+                                   &iter);
+        forward =
+            !tp_list_iter_is_last(page->current,
+                                  &iter);
+    }
+    gtk_widget_set_sensitive(page->remove_button, remove);
+    gtk_widget_set_sensitive(page->back_button, back);
+    gtk_widget_set_sensitive(page->forward_button, forward);
+}
+
+/* Callback for the "response" signal of the dialog. */
+static void
+tp_dialog_response_cb(GtkDialog * dialog, gint response, gpointer data)
+{
+    GError *err = NULL;
+
+    switch (response) {
+    case GTK_RESPONSE_DELETE_EVENT:
+    case GTK_RESPONSE_CLOSE:
+        gtk_widget_destroy(GTK_WIDGET(dialog));
+        break;
+    case GTK_RESPONSE_HELP:
+        gtk_show_uri(NULL, "ghelp:balsa?toolbar-prefs",
+                     gtk_get_current_event_time(), &err);
+        if (err) {
+            balsa_information(LIBBALSA_INFORMATION_WARNING,
+		    _("Error displaying toolbar help: %s\n"),
+		    err->message);
+            g_error_free(err);
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+/* Helpers. */
+
+/* Create a page for the main notebook.
+ */
+static GtkWidget*
+create_toolbar_page(BalsaToolbarModel * model, GtkUIManager * ui_manager)
+{
+    GtkWidget *outer_box;
+    GtkWidget *toolbar_frame, *toolbar_scroll;
+    GtkWidget *toolbar_ctlbox;
+    GtkWidget *lower_ctlbox, *button_box, *move_button_box, *center_button_box;
+    GtkWidget *list_frame, *list_scroll;
+    GtkWidget *destination_frame, *destination_scroll;
+    GtkWidget *style_button;
+    ToolbarPage *page;
+    GtkTreeSelection *selection;
+
+    page = g_new(ToolbarPage, 1);
+    page->model = model;
+
+    /* The "window itself" */
+    outer_box=gtk_vbox_new(FALSE, 0);
+    g_object_set_data_full(G_OBJECT(outer_box), BALSA_KEY_TOOLBAR_PAGE,
+                           page, g_free);
+
+    /* Preview display */
+    toolbar_frame=gtk_frame_new(_("Preview"));
+    gtk_container_set_border_width(GTK_CONTAINER(toolbar_frame), 5);
+    gtk_box_pack_start(GTK_BOX(outer_box), toolbar_frame, FALSE, FALSE, 0);
+
+    toolbar_ctlbox=gtk_vbox_new(FALSE, 5);
+    gtk_container_add(GTK_CONTAINER(toolbar_frame), toolbar_ctlbox);
+    gtk_container_set_border_width(GTK_CONTAINER(toolbar_ctlbox), 5);
+
+    /* The ui-manager has actions but no ui, so we add an empty toolbar. */
+    gtk_ui_manager_add_ui_from_string(ui_manager,
+                                      "<ui>"
+                                      "  <toolbar name='Toolbar'/>"
+                                      "</ui>",
+                                      -1, NULL);
+
+    /* The preview is an actual, fully functional toolbar */
+    page->toolbar = balsa_toolbar_new(model, ui_manager);
+    gtk_widget_set_sensitive(page->toolbar, FALSE);
+
+    /* embedded in a scrolled_window */
+    toolbar_scroll=gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(toolbar_scroll),
+				   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_NEVER);
+
+    gtk_box_pack_start(GTK_BOX(toolbar_ctlbox), toolbar_scroll,
+                       TRUE, TRUE, 0);
+
+    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(toolbar_scroll),
+                                          page->toolbar);
+
+    /* Button box */
+    button_box = gtk_hbutton_box_new();
+    gtk_box_pack_start(GTK_BOX(toolbar_ctlbox), button_box, FALSE, FALSE, 0);
+
+    /* Standard button */
+    page->standard_button =
+        gtk_button_new_with_mnemonic(_("_Restore toolbar to standard buttons"));
+    gtk_container_add(GTK_CONTAINER(button_box), page->standard_button);
+
+    /* Style button */
+    style_button = gtk_button_new_with_mnemonic(_("Toolbar _style..."));
+    gtk_container_add(GTK_CONTAINER(button_box), style_button);
+
+    /* Done with preview */
+	
+    /* Box for lower half of window */
+    lower_ctlbox=gtk_hbox_new(FALSE, 5);
+    gtk_container_set_border_width(GTK_CONTAINER(lower_ctlbox), 5);
+
+    gtk_box_pack_start(GTK_BOX(outer_box), lower_ctlbox, TRUE, TRUE, 0);
+
+    /* A list to show the available items */
+    list_scroll=gtk_scrolled_window_new(NULL, NULL);
+
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(list_scroll),
+				   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+    list_frame=gtk_frame_new(_("Available buttons"));
+    page->available = tp_list_new();
+
+    gtk_box_pack_start(GTK_BOX(lower_ctlbox), list_frame,
+		       TRUE, TRUE, 0);
+    gtk_container_add(GTK_CONTAINER(list_frame), list_scroll);
+    gtk_container_add(GTK_CONTAINER(list_scroll), page->available);
+
+    /* Done with available list */
+
+    /* Another list to show the current tools */
+    destination_scroll=gtk_scrolled_window_new(NULL, NULL);
+
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(destination_scroll),
+				   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	
+    destination_frame=gtk_frame_new(_("Current toolbar"));
+    page->current = tp_list_new();
+
+    /* Done with destination list */
+
+    /* Button box */
+    center_button_box=gtk_vbox_new(TRUE, 0);
+
+    button_box=gtk_vbox_new(FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(lower_ctlbox), center_button_box,
+		       FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(center_button_box), button_box,
+		       FALSE, FALSE, 0);
+
+    page->back_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_UP, _("Up"));
+    gtk_box_pack_start(GTK_BOX(button_box), page->back_button, FALSE, FALSE, 0);
+
+    move_button_box=gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(button_box), move_button_box, FALSE, FALSE, 0);
+
+    page->remove_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_BACK, "-");
+    gtk_box_pack_start(GTK_BOX(move_button_box), page->remove_button,
+                       FALSE, FALSE, 0);
+
+    page->add_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_FORWARD, "+");
+    gtk_box_pack_start(GTK_BOX(move_button_box), page->add_button, FALSE, FALSE, 0);
+
+    page->forward_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_DOWN, _("Down"));
+    gtk_box_pack_start(GTK_BOX(button_box), page->forward_button, FALSE, FALSE, 0);
+
+    /* Pack destination list */
+    gtk_box_pack_start(GTK_BOX(lower_ctlbox), destination_frame, TRUE, TRUE, 0);
+    gtk_container_add(GTK_CONTAINER(destination_frame), destination_scroll);
+    gtk_container_add(GTK_CONTAINER(destination_scroll), page->current);
+
+    /* UI signals */
+    g_signal_connect(G_OBJECT(page->available), "row-activated",
+                     G_CALLBACK(available_row_activated_cb), page);
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
+    g_signal_connect(G_OBJECT(selection), "changed",
+		       G_CALLBACK(available_selection_changed_cb), page);
+
+    g_signal_connect(G_OBJECT(page->current), "row-activated",
+                     G_CALLBACK(current_row_activated_cb), page);
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    g_signal_connect(G_OBJECT(selection), "changed",
+		       G_CALLBACK(current_selection_changed_cb), page);
+
+    g_signal_connect(G_OBJECT(page->add_button), "clicked",
+		       G_CALLBACK(add_button_cb), page);
+    g_signal_connect(G_OBJECT(page->remove_button), "clicked",
+		       G_CALLBACK(remove_button_cb), page);
+    g_signal_connect(G_OBJECT(page->forward_button), "clicked",
+		       G_CALLBACK(forward_button_cb), page);
+    g_signal_connect(G_OBJECT(page->back_button), "clicked",
+		       G_CALLBACK(back_button_cb), page);
+
+    g_signal_connect(G_OBJECT(page->standard_button), "clicked",
+		       G_CALLBACK(standard_button_cb), page);
+    g_signal_connect(G_OBJECT(style_button), "clicked",
+		       G_CALLBACK(style_button_cb), page);
+
+    gtk_widget_set_sensitive(page->add_button, FALSE);
+    gtk_widget_set_sensitive(page->remove_button, FALSE);
+    gtk_widget_set_sensitive(page->back_button, FALSE);
+    gtk_widget_set_sensitive(page->forward_button, FALSE);
+    gtk_widget_set_sensitive(page->standard_button,
+                             !balsa_toolbar_model_is_standard(model));
+
+    tp_page_refresh_available(page);
+    tp_page_refresh_current(page);
+
+    return outer_box;
+}
+
+/* Refresh the page's available GtkTreeView.
+ */
+static void
+tp_page_refresh_available(ToolbarPage * page)
+{
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreePath *path;
+    GHashTable *legal = balsa_toolbar_model_get_legal(page->model);
+    GSList *current = balsa_toolbar_model_get_current(page->model);
+    int item;
+
+    /* save currently selected path, or point path to the first row */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        path = gtk_tree_model_get_path(model, &iter);
+    else {
+        path = gtk_tree_path_new();
+        gtk_tree_path_down(path);
+    }
+    gtk_list_store_clear(GTK_LIST_STORE(model));
+
+    for (item = 0; item < toolbar_button_count; item++) {
+        if (item > 0
+            && (!g_hash_table_lookup(legal,
+                                     toolbar_buttons[item].pixmap_id)
+                || g_slist_find_custom(current,
+                                       toolbar_buttons[item].pixmap_id,
+                                       (GCompareFunc) strcmp)))
+            continue;
+
+        gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+        tp_store_set(GTK_LIST_STORE(model), &iter, item);
+    }
+
+    if (gtk_tree_model_get_iter(model, &iter, path)
+        || gtk_tree_path_prev(path)) {
+        gtk_tree_selection_select_path(selection, path);
+        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->available),
+                                     path, NULL, FALSE, 0, 0);
+    }
+    gtk_tree_path_free(path);
+}
+
+/* Refresh the page's current GtkTreeView.
+ */
+static void
+tp_page_refresh_current(ToolbarPage * page)
+{
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreePath *path;
+    int item;
+    GSList *list;
+
+    /* save currently selected path, or point path to the first row */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        path = gtk_tree_model_get_path(model, &iter);
+    else {
+        path = gtk_tree_path_new();
+        gtk_tree_path_down(path);
+    }
+    gtk_list_store_clear(GTK_LIST_STORE(model));
+
+    for (list = balsa_toolbar_model_get_current(page->model); list;
+         list = g_slist_next(list)) {
+        item = get_toolbar_button_index(list->data);
+        if (item < 0)
+            continue;
+
+        gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+        tp_store_set(GTK_LIST_STORE(model), &iter, item);
+    }
+
+    if (gtk_tree_model_get_iter(model, &iter, path) 
+        || gtk_tree_path_prev(path)) {
+        gtk_tree_selection_select_path(selection, path);
+        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
+                                     path, NULL, FALSE, 0, 0);
+    }
+    gtk_tree_path_free(path);
+}
+
+/* Refresh a page after its destination TreeView has changed.
+ */
+static void
+tp_page_refresh_preview(ToolbarPage * page)
+{
+    GtkTreeModel *model =
+        gtk_tree_view_get_model(GTK_TREE_VIEW(page->current));
+    GtkTreeIter iter;
+    gboolean valid;
+    
+    balsa_toolbar_model_clear(page->model);
+    for (valid = gtk_tree_model_get_iter_first(model, &iter);
+         valid;
+         valid = gtk_tree_model_iter_next(model, &iter)) {
+        gint item;
+
+        gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
+        if (item >= 0 && item < toolbar_button_count)
+            balsa_toolbar_model_insert_icon(page->model, 
+                                            toolbar_buttons[item].pixmap_id,
+                                            -1);
+    }
+}
+
+/* Create a GtkTreeView for an icon list.
+ */
+static GtkWidget *
+tp_list_new(void)
+{
+    GtkListStore *list_store;
+    GtkTreeView *view;
+    GtkCellRenderer *renderer;
+    GtkTreeViewColumn *column;
+
+    list_store = gtk_list_store_new(TP_N_COLUMNS,
+                                    G_TYPE_STRING,   /* TP_TEXT_COLUMN */
+                                    GDK_TYPE_PIXBUF, /* TP_ICON_COLUMN */
+                                    G_TYPE_INT       /* TP_ITEM_COLUMN */
+                                    );
+    view = GTK_TREE_VIEW(gtk_tree_view_new_with_model
+                         (GTK_TREE_MODEL(list_store)));
+    g_object_unref(list_store);
+
+    renderer = gtk_cell_renderer_text_new();
+    column =
+        gtk_tree_view_column_new_with_attributes(NULL, renderer, "text",
+                                                 TP_TEXT_COLUMN, NULL);
+    gtk_tree_view_append_column(view, column);
+
+    renderer = gtk_cell_renderer_pixbuf_new();
+    column =
+        gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf",
+                                                 TP_ICON_COLUMN, NULL);
+    gtk_tree_view_append_column(view, column);
+
+    gtk_tree_view_set_headers_visible(view, FALSE);
+    return GTK_WIDGET(view);
+}
+
+/* Test whether the iter addresses the first row.
+ */
+static gboolean 
+tp_list_iter_is_first(GtkWidget * list, GtkTreeIter * iter)
+{
+    GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
+    GtkTreePath *path = gtk_tree_model_get_path(model, iter);
+    gboolean ret_val = !gtk_tree_path_prev(path);
+    gtk_tree_path_free(path);
+    return ret_val;
+}
+
+/* Test whether the iter addresses the last row.
+ */
+static gboolean 
+tp_list_iter_is_last(GtkWidget * list, GtkTreeIter * iter)
+{
+    GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
+    GtkTreeIter tmp_iter = *iter;
+    return !gtk_tree_model_iter_next(model, &tmp_iter);
+}
+
+/* Swap the currently selected row in page->current
+ * with the next or previous row.
+ */
+static void
+tp_page_swap_rows(ToolbarPage * page, gboolean forward)
+{
+    GtkTreeModel *model =
+        gtk_tree_view_get_model(GTK_TREE_VIEW(page->current));
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    GtkTreeIter iter;
+    GtkTreeIter tmp_iter;
+    GtkTreePath *tmp_path;
+    gint item, tmp_item;
+
+    selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+        return;
+
+    if (forward) {
+        tmp_iter = iter;
+        if (!gtk_tree_model_iter_next(model, &tmp_iter))
+            return;
+        tmp_path = gtk_tree_model_get_path(model, &tmp_iter);
+    } else {
+        tmp_path = gtk_tree_model_get_path(model, &iter);
+        if (!gtk_tree_path_prev(tmp_path)) {
+            gtk_tree_path_free(tmp_path);
+            return;
+        }
+        gtk_tree_model_get_iter(model, &tmp_iter, tmp_path);
+    }
+
+    gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
+    gtk_tree_model_get(model, &tmp_iter, TP_ITEM_COLUMN, &tmp_item, -1);
+    tp_store_set(GTK_LIST_STORE(model), &tmp_iter, item);
+    tp_store_set(GTK_LIST_STORE(model), &iter, tmp_item);
+
+    gtk_tree_selection_select_path(selection, tmp_path);
+    gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
+                                 tmp_path, NULL, FALSE, 0, 0);
+    gtk_tree_path_free(tmp_path);
+
+    gtk_widget_set_sensitive(page->standard_button, TRUE);
+    tp_page_refresh_preview(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+/* Add an item to a GtkTreeView's GtkListStore.
+ */ 
+static void
+tp_store_set(GtkListStore * store, GtkTreeIter * iter, gint item)
+{
+    GdkPixbuf *pixbuf;
+    gchar *text;
+
+    text = g_strdup(balsa_toolbar_button_text(item));
+    replace_nl_with_space(text);
+    pixbuf = (item > 0
+              ? gtk_widget_render_icon(customize_widget,
+                                       toolbar_buttons[item].pixmap_id,
+                                       GTK_ICON_SIZE_LARGE_TOOLBAR,
+                                       "Balsa")
+              : NULL);
+    gtk_list_store_set(store, iter,
+                       TP_TEXT_COLUMN, text,
+                       TP_ICON_COLUMN, pixbuf,
+                       TP_ITEM_COLUMN, item,
+                       -1);
+    g_free(text);
+    if (pixbuf)
+        g_object_unref(pixbuf);
+}
+
+/* Add an item to the current list.
+ */
+static void
+tp_page_add_selected(ToolbarPage * page)
+{
+    gint item = 0;
+    GtkTreeSelection *selection;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreeIter sibling;
+    GtkTreePath *path;
+
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
+    if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+	return;
+    gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
+
+    selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    if (gtk_tree_selection_get_selected(selection, &model, &sibling))
+        gtk_list_store_insert_before(GTK_LIST_STORE(model), &iter, 
+                                     &sibling);
+    else
+        gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+
+    tp_store_set(GTK_LIST_STORE(model), &iter, item);
+	
+    path = gtk_tree_model_get_path(model, &iter);
+    gtk_tree_selection_select_path(selection, path);
+    gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current), path, NULL,
+                                 FALSE, 0, 0);
+    gtk_tree_path_free(path);
+
+    gtk_widget_set_sensitive(page->standard_button, TRUE);
+    tp_page_refresh_preview(page);
+    tp_page_refresh_available(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+/* Remove an item from the page's current list.
+ */
+static void
+tp_page_remove_selected(ToolbarPage * page)
+{
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreePath *path;
+    
+    if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+        return;
+    path = gtk_tree_model_get_path(model, &iter);
+
+    gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+
+    if (gtk_tree_model_get_iter(model, &iter, path) 
+        || gtk_tree_path_prev(path)) {
+        gtk_tree_selection_select_path(selection, path);
+        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
+                                     path, NULL, FALSE, 0, 0);
+    }
+    gtk_tree_path_free(path);
+
+    gtk_widget_set_sensitive(page->standard_button, TRUE);
+    tp_page_refresh_preview(page);
+    tp_page_refresh_available(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+/* Unwrap text.
+ */
+static void
+replace_nl_with_space(char* str)
+{
+    while(*str) {
+	if(*str == '\n')
+	    *str=' ';
+	str++;
+    }
+}
diff --git a/src/toolbar-prefs.c.rej b/src/toolbar-prefs.c.rej
new file mode 100644
index 0000000..e1c264f
--- /dev/null
+++ b/src/toolbar-prefs.c.rej
@@ -0,0 +1,24 @@
+***************
+*** 126,136 ****
+      BalsaToolbarModel *model;
+      BalsaToolbarType   type;
+      GtkUIManager * ui_manager;
+-     GtkWidget *content_area;
+  
+      /* There can only be one */
+      if (customize_widget) {
+-         gtk_window_present(GTK_WINDOW(customize_widget));
+          return;
+      }
+  
+--- 126,135 ----
+      BalsaToolbarModel *model;
+      BalsaToolbarType   type;
+      GtkUIManager * ui_manager;
+  
+      /* There can only be one */
+      if (customize_widget) {
++         gdk_window_raise(customize_widget->window);
+          return;
+      }
+  
diff --git a/src/toolbar-prefs.c~ b/src/toolbar-prefs.c~
new file mode 100644
index 0000000..4618b33
--- /dev/null
+++ b/src/toolbar-prefs.c~
@@ -0,0 +1,919 @@
+/* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
+/* Balsa E-Mail Client
+ * Copyright (C) 1997-2002 Stuart Parmenter and others,
+ *                         See the file AUTHORS for a list.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option) 
+ * any later version.
+ *  
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of 
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
+ * GNU General Public License for more details.
+ *  
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  
+ * 02111-1307, USA.
+ */
+
+#if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
+# include "config.h"
+#endif                          /* HAVE_CONFIG_H */
+#include "toolbar-prefs.h"
+
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "balsa-app.h"
+#include "balsa-icons.h"
+#include "main-window.h"
+#include "message-window.h"
+#include "sendmsg-window.h"
+#include "toolbar-factory.h"
+
+#if HAVE_MACOSX_DESKTOP
+#  include "macosx-helpers.h"
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifdef HAVE_GNOME
+#define OLD_BALSA_COMPATIBILITY_TRANSLATION
+#endif
+#ifdef OLD_BALSA_COMPATIBILITY_TRANSLATION
+#include <gnome.h> /* for GNOME_STOCK_* pixmaps */
+#endif
+
+/* Enumeration for GtkTreeModel columns. */
+enum {
+    TP_TEXT_COLUMN,
+    TP_ICON_COLUMN,
+    TP_ITEM_COLUMN,
+    TP_N_COLUMNS
+};
+
+static GtkWidget *customize_widget;
+
+/* Structure associated with each notebook page. */
+typedef struct ToolbarPage_ ToolbarPage;
+struct ToolbarPage_ {
+    BalsaToolbarModel *model;
+    GtkWidget *available;
+    GtkWidget *current;
+    GtkWidget *toolbar;
+    GtkWidget *add_button;
+    GtkWidget *remove_button;
+    GtkWidget *back_button;
+    GtkWidget *forward_button;
+    GtkWidget *standard_button;
+};
+
+/* Callbacks. */
+static void tp_dialog_response_cb(GtkDialog * dialog, gint response,
+                                  gpointer data);
+static void add_button_cb(GtkWidget *, ToolbarPage * page);
+static void remove_button_cb(GtkWidget *, ToolbarPage * page);
+static void back_button_cb(GtkWidget *, ToolbarPage * page);
+static void forward_button_cb(GtkWidget *, ToolbarPage * page);
+static void wrap_toggled_cb(GtkWidget * widget, GtkNotebook * notebook);
+static void available_selection_changed_cb(GtkTreeSelection * selection,
+                                           ToolbarPage * page);
+static void current_selection_changed_cb(GtkTreeSelection * selection,
+                                         ToolbarPage * page);
+static void available_row_activated_cb(GtkTreeView * treeview,
+                                       GtkTreeIter * arg1,
+                                       GtkTreePath * arg2,
+                                       ToolbarPage * page);
+static void current_row_activated_cb(GtkTreeView * treeview,
+                                     GtkTreeIter * arg1,
+                                     GtkTreePath * arg2,
+                                     ToolbarPage * page);
+
+/* Helpers. */
+static GtkWidget *create_toolbar_page(BalsaToolbarModel * model,
+                                      GtkUIManager * ui_manager);
+static GtkWidget *tp_list_new(void);
+static gboolean tp_list_iter_is_first(GtkWidget * list, GtkTreeIter * iter);
+static gboolean tp_list_iter_is_last(GtkWidget * list, GtkTreeIter * iter);
+static void tp_page_refresh_available(ToolbarPage * page);
+static void tp_page_refresh_current(ToolbarPage * page);
+static void tp_page_refresh_preview(ToolbarPage * page);
+static void tp_page_swap_rows(ToolbarPage * page, gboolean forward);
+static void tp_page_add_selected(ToolbarPage * page);
+static void tp_page_remove_selected(ToolbarPage * page);
+static void tp_store_set(GtkListStore * store, GtkTreeIter * iter,
+                         gint item);
+static void replace_nl_with_space(char* str);
+
+/* Public methods. */
+
+/* create the toolbar-customization dialog
+ */
+void
+customize_dialog_cb(GtkWidget * widget, gpointer data)
+{
+    GtkWidget *notebook;
+    GtkWidget *child;
+    GtkWidget *option_frame;
+    GtkWidget *option_box;
+    GtkWidget *wrap_button;
+    GtkWidget *active_window = data;
+    BalsaToolbarModel *model;
+    BalsaToolbarType   type;
+    GtkUIManager * ui_manager;
+
+    /* There can only be one */
+    if (customize_widget) {
+        gtk_window_present(GTK_WINDOW(customize_widget));
+        return;
+    }
+
+    customize_widget =
+        gtk_dialog_new_with_buttons(_("Customize Toolbars"),
+                                    GTK_WINDOW(active_window),
+                                    GTK_DIALOG_DESTROY_WITH_PARENT,
+                                    GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
+                                    GTK_STOCK_HELP,  GTK_RESPONSE_HELP,
+                                    NULL);
+#if HAVE_MACOSX_DESKTOP
+    libbalsa_macosx_menu_for_parent(customize_widget, GTK_WINDOW(active_window));
+#endif
+    g_object_add_weak_pointer(G_OBJECT(customize_widget),
+                              (gpointer) & customize_widget);
+    g_signal_connect(G_OBJECT(customize_widget), "response",
+                     G_CALLBACK(tp_dialog_response_cb), NULL);
+
+    notebook = gtk_notebook_new();
+    gtk_container_add(GTK_CONTAINER(GTK_DIALOG(customize_widget)->vbox),
+                      notebook);
+
+    gtk_window_set_wmclass(GTK_WINDOW(customize_widget), "customize",
+                           "Balsa");
+    gtk_window_set_default_size(GTK_WINDOW(customize_widget), 600, 440);
+
+    /* The order of pages must be consistent with the BalsaToolbarType
+     * enum. */
+    model = balsa_window_get_toolbar_model();
+    ui_manager = balsa_window_ui_manager_new(NULL);
+    child = create_toolbar_page(model, ui_manager);
+    g_object_unref(ui_manager);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
+                             gtk_label_new(_("Main window")));
+
+    model = sendmsg_window_get_toolbar_model();
+    ui_manager = sendmsg_window_ui_manager_new(NULL);
+    child = create_toolbar_page(model, ui_manager);
+    g_object_unref(ui_manager);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
+                             gtk_label_new(_("Compose window")));
+
+    model = message_window_get_toolbar_model();
+    ui_manager = message_window_ui_manager_new(NULL);
+    child = create_toolbar_page(model, ui_manager);
+    g_object_unref(ui_manager);
+    gtk_notebook_append_page(GTK_NOTEBOOK(notebook), child,
+                             gtk_label_new(_("Message window")));
+
+    option_frame = gtk_frame_new(_("Toolbar options"));
+    gtk_container_set_border_width(GTK_CONTAINER(option_frame), 6);
+    gtk_box_pack_start(GTK_BOX(GTK_DIALOG(customize_widget)->vbox),
+                       option_frame, FALSE, FALSE, 0);
+
+    option_box = gtk_vbox_new(FALSE, 6);
+    gtk_container_set_border_width(GTK_CONTAINER(option_box), 6);
+    gtk_container_add(GTK_CONTAINER(option_frame), option_box);
+    
+    wrap_button =
+        gtk_check_button_new_with_mnemonic(_("_Wrap button labels"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wrap_button),
+                                 balsa_app.toolbar_wrap_button_text);
+    g_signal_connect(G_OBJECT(wrap_button), "toggled",
+                     G_CALLBACK(wrap_toggled_cb), notebook);
+    gtk_box_pack_start(GTK_BOX(option_box), wrap_button, FALSE, FALSE, 0);
+
+    gtk_widget_show_all(customize_widget);
+
+    /* Now that the pages are shown, we can switch to the page
+     * corresponding to the toolbar that the user clicked on. */
+    type =
+        GPOINTER_TO_INT(g_object_get_data
+                        (G_OBJECT(widget), BALSA_TOOLBAR_MODEL_TYPE));
+    gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), type);
+}
+
+/* get_toolbar_button_index:
+   id - button id
+   returns -1 on failure.
+*/
+int
+get_toolbar_button_index(const char *id)
+{
+#ifdef OLD_BALSA_COMPATIBILITY_TRANSLATION
+    static const struct {
+        gchar *new;
+        gchar *old;
+    } button_converter[] = {
+        { BALSA_PIXMAP_ATTACHMENT,   GNOME_STOCK_ATTACH },
+        { BALSA_PIXMAP_COMPOSE,      GNOME_STOCK_MAIL_NEW },
+        { BALSA_PIXMAP_CONTINUE,     GNOME_STOCK_MAIL },
+        { BALSA_PIXMAP_RECEIVE,      GNOME_STOCK_MAIL_RCV },
+        { BALSA_PIXMAP_REPLY,        GNOME_STOCK_MAIL_RPL },
+        { BALSA_PIXMAP_REPLY_ALL,    "reply_to_all" },
+        { BALSA_PIXMAP_REPLY_GROUP,  "reply_to_group" },
+        { BALSA_PIXMAP_FORWARD,      GNOME_STOCK_MAIL_FWD },
+        { BALSA_PIXMAP_NEXT,         GTK_STOCK_GO_FORWARD },
+        { BALSA_PIXMAP_PREVIOUS,     GTK_STOCK_GO_BACK },
+	{ GTK_STOCK_PRINT,           BALSA_OLD_PIXMAP_PRINT },
+	{ GTK_STOCK_SAVE,            BALSA_OLD_PIXMAP_SAVE },
+        { BALSA_PIXMAP_SEND,         GNOME_STOCK_MAIL_SND },
+	{ GTK_STOCK_DELETE,          BALSA_OLD_PIXMAP_TRASH },
+        { BALSA_PIXMAP_TRASH_EMPTY,  "empty_trash" },
+        { BALSA_PIXMAP_NEXT_UNREAD,  "next_unread" },
+        { BALSA_PIXMAP_NEXT_FLAGGED, "next_flagged" },
+        { BALSA_PIXMAP_SHOW_HEADERS, "show_all_headers" },
+        { BALSA_PIXMAP_SHOW_PREVIEW, "show_preview" },
+        { BALSA_PIXMAP_MARKED_NEW,   "flag_unread" },
+        { BALSA_PIXMAP_MARK_ALL,     "mark_all" },
+        { BALSA_PIXMAP_IDENTITY,     "identity" },
+	{ GTK_STOCK_CANCEL,          BALSA_OLD_PIXMAP_CLOSE_MBOX },
+        { NULL, NULL }
+    };
+#endif
+    int i;
+
+    g_return_val_if_fail(id, -1);
+
+    for(i=0; i<toolbar_button_count; i++) {
+	if(!strcmp(id, toolbar_buttons[i].pixmap_id))
+	    return i;
+    }
+#ifdef OLD_BALSA_COMPATIBILITY_TRANSLATION
+    /* you have got a second chance.... */
+    
+    for(i=0; button_converter[i].new; i++) {
+        if(!strcmp(id, button_converter[i].old)) {
+            int j;
+            for(j=0; j<toolbar_button_count; j++) {
+                if(!strcmp(button_converter[i].new,
+                           toolbar_buttons[j].pixmap_id))
+                    return j;
+            }
+            return -1;
+        }
+    }
+#endif
+    return -1;
+}
+
+/* Callbacks. */
+
+#define BALSA_KEY_TOOLBAR_PAGE "balsa-toolbar-page"
+
+/* Callback for the wrap_buttons' "toggled" signal. */
+static void
+wrap_toggled_cb(GtkWidget * widget, GtkNotebook * notebook)
+{
+    gint i;
+    GtkWidget *child;
+    ToolbarPage *page;
+    
+    balsa_app.toolbar_wrap_button_text = GTK_TOGGLE_BUTTON(widget)->active;
+
+    for (i = 0; (child = gtk_notebook_get_nth_page(notebook, i)); i++) {
+        page = g_object_get_data(G_OBJECT(child), BALSA_KEY_TOOLBAR_PAGE);
+        balsa_toolbar_model_changed(page->model);
+    }
+}
+
+/* Button callbacks: each makes the appropriate change to the
+ * page->current GtkTreeView, then refreshes the page's
+ * BalsaToolbarModel and GtkToolbar; add_button_cb and remove_button_cb
+ * also refresh the page's available GtkTreeView.
+ */
+static void
+back_button_cb(GtkWidget * widget, ToolbarPage * page)
+{
+    tp_page_swap_rows(page, FALSE);
+}
+
+static void
+forward_button_cb(GtkWidget * widget, ToolbarPage * page)
+{
+    tp_page_swap_rows(page, TRUE);
+}
+
+static void
+add_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    tp_page_add_selected(page);
+}
+
+static void
+remove_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    tp_page_remove_selected(page);
+}
+
+static void
+standard_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    balsa_toolbar_model_clear(page->model);
+    gtk_widget_set_sensitive(page->standard_button, FALSE);
+    tp_page_refresh_available(page);
+    tp_page_refresh_current(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+static void
+style_button_cb(GtkWidget *widget, ToolbarPage * page)
+{
+    gboolean handled;
+
+    g_signal_emit_by_name(page->toolbar, "popup-menu", &handled);
+}
+
+/* Callback for the "row-activated" signal for the available list. */
+static void
+available_row_activated_cb(GtkTreeView * treeview, GtkTreeIter * arg1,
+                        GtkTreePath * arg2, ToolbarPage * page)
+{
+    tp_page_add_selected(page);
+}
+
+/* Callback for the selection "changed" signal for the available list. */
+static void
+available_selection_changed_cb(GtkTreeSelection * selection,
+                            ToolbarPage * page)
+{
+    gtk_widget_set_sensitive(page->add_button,
+                             gtk_tree_selection_get_selected(selection,
+                                                             NULL, NULL));
+}
+
+/* Callback for the "row-activated" signal for the current list. */
+static void
+current_row_activated_cb(GtkTreeView * treeview, GtkTreeIter * arg1,
+                      GtkTreePath * arg2, ToolbarPage * page)
+{
+    tp_page_remove_selected(page);
+}
+
+/* Callback for the selection "changed" signal for the destination list. */
+static void
+current_selection_changed_cb(GtkTreeSelection * selection, ToolbarPage * page)
+{
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    gboolean remove = FALSE;
+    gboolean back = FALSE;
+    gboolean forward = FALSE;
+
+    if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+        remove = TRUE;
+        back =
+            !tp_list_iter_is_first(page->current,
+                                   &iter);
+        forward =
+            !tp_list_iter_is_last(page->current,
+                                  &iter);
+    }
+    gtk_widget_set_sensitive(page->remove_button, remove);
+    gtk_widget_set_sensitive(page->back_button, back);
+    gtk_widget_set_sensitive(page->forward_button, forward);
+}
+
+/* Callback for the "response" signal of the dialog. */
+static void
+tp_dialog_response_cb(GtkDialog * dialog, gint response, gpointer data)
+{
+    GError *err = NULL;
+
+    switch (response) {
+    case GTK_RESPONSE_DELETE_EVENT:
+    case GTK_RESPONSE_CLOSE:
+        gtk_widget_destroy(GTK_WIDGET(dialog));
+        break;
+    case GTK_RESPONSE_HELP:
+        gtk_show_uri(NULL, "ghelp:balsa?toolbar-prefs",
+                     gtk_get_current_event_time(), &err);
+        if (err) {
+            balsa_information(LIBBALSA_INFORMATION_WARNING,
+		    _("Error displaying toolbar help: %s\n"),
+		    err->message);
+            g_error_free(err);
+        }
+        break;
+    default:
+        break;
+    }
+}
+
+/* Helpers. */
+
+/* Create a page for the main notebook.
+ */
+static GtkWidget*
+create_toolbar_page(BalsaToolbarModel * model, GtkUIManager * ui_manager)
+{
+    GtkWidget *outer_box;
+    GtkWidget *toolbar_frame, *toolbar_scroll;
+    GtkWidget *toolbar_ctlbox;
+    GtkWidget *lower_ctlbox, *button_box, *move_button_box, *center_button_box;
+    GtkWidget *list_frame, *list_scroll;
+    GtkWidget *destination_frame, *destination_scroll;
+    GtkWidget *style_button;
+    ToolbarPage *page;
+    GtkTreeSelection *selection;
+
+    page = g_new(ToolbarPage, 1);
+    page->model = model;
+
+    /* The "window itself" */
+    outer_box=gtk_vbox_new(FALSE, 0);
+    g_object_set_data_full(G_OBJECT(outer_box), BALSA_KEY_TOOLBAR_PAGE,
+                           page, g_free);
+
+    /* Preview display */
+    toolbar_frame=gtk_frame_new(_("Preview"));
+    gtk_container_set_border_width(GTK_CONTAINER(toolbar_frame), 5);
+    gtk_box_pack_start(GTK_BOX(outer_box), toolbar_frame, FALSE, FALSE, 0);
+
+    toolbar_ctlbox=gtk_vbox_new(FALSE, 5);
+    gtk_container_add(GTK_CONTAINER(toolbar_frame), toolbar_ctlbox);
+    gtk_container_set_border_width(GTK_CONTAINER(toolbar_ctlbox), 5);
+
+    /* The ui-manager has actions but no ui, so we add an empty toolbar. */
+    gtk_ui_manager_add_ui_from_string(ui_manager,
+                                      "<ui>"
+                                      "  <toolbar name='Toolbar'/>"
+                                      "</ui>",
+                                      -1, NULL);
+
+    /* The preview is an actual, fully functional toolbar */
+    page->toolbar = balsa_toolbar_new(model, ui_manager);
+    gtk_widget_set_sensitive(page->toolbar, FALSE);
+
+    /* embedded in a scrolled_window */
+    toolbar_scroll=gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(toolbar_scroll),
+				   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_NEVER);
+
+    gtk_box_pack_start(GTK_BOX(toolbar_ctlbox), toolbar_scroll,
+                       TRUE, TRUE, 0);
+
+    gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(toolbar_scroll),
+                                          page->toolbar);
+
+    /* Button box */
+    button_box = gtk_hbutton_box_new();
+    gtk_box_pack_start(GTK_BOX(toolbar_ctlbox), button_box, FALSE, FALSE, 0);
+
+    /* Standard button */
+    page->standard_button =
+        gtk_button_new_with_mnemonic(_("_Restore toolbar to standard buttons"));
+    gtk_container_add(GTK_CONTAINER(button_box), page->standard_button);
+
+    /* Style button */
+    style_button = gtk_button_new_with_mnemonic(_("Toolbar _style..."));
+    gtk_container_add(GTK_CONTAINER(button_box), style_button);
+
+    /* Done with preview */
+	
+    /* Box for lower half of window */
+    lower_ctlbox=gtk_hbox_new(FALSE, 5);
+    gtk_container_set_border_width(GTK_CONTAINER(lower_ctlbox), 5);
+
+    gtk_box_pack_start(GTK_BOX(outer_box), lower_ctlbox, TRUE, TRUE, 0);
+
+    /* A list to show the available items */
+    list_scroll=gtk_scrolled_window_new(NULL, NULL);
+
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(list_scroll),
+				   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+    list_frame=gtk_frame_new(_("Available buttons"));
+    page->available = tp_list_new();
+
+    gtk_box_pack_start(GTK_BOX(lower_ctlbox), list_frame,
+		       TRUE, TRUE, 0);
+    gtk_container_add(GTK_CONTAINER(list_frame), list_scroll);
+    gtk_container_add(GTK_CONTAINER(list_scroll), page->available);
+
+    /* Done with available list */
+
+    /* Another list to show the current tools */
+    destination_scroll=gtk_scrolled_window_new(NULL, NULL);
+
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(destination_scroll),
+				   GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+	
+    destination_frame=gtk_frame_new(_("Current toolbar"));
+    page->current = tp_list_new();
+
+    /* Done with destination list */
+
+    /* Button box */
+    center_button_box=gtk_vbox_new(TRUE, 0);
+
+    button_box=gtk_vbox_new(FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(lower_ctlbox), center_button_box,
+		       FALSE, FALSE, 0);
+
+    gtk_box_pack_start(GTK_BOX(center_button_box), button_box,
+		       FALSE, FALSE, 0);
+
+    page->back_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_UP, _("Up"));
+    gtk_box_pack_start(GTK_BOX(button_box), page->back_button, FALSE, FALSE, 0);
+
+    move_button_box=gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(button_box), move_button_box, FALSE, FALSE, 0);
+
+    page->remove_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_BACK, "-");
+    gtk_box_pack_start(GTK_BOX(move_button_box), page->remove_button,
+                       FALSE, FALSE, 0);
+
+    page->add_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_FORWARD, "+");
+    gtk_box_pack_start(GTK_BOX(move_button_box), page->add_button, FALSE, FALSE, 0);
+
+    page->forward_button =
+        balsa_stock_button_with_label(GTK_STOCK_GO_DOWN, _("Down"));
+    gtk_box_pack_start(GTK_BOX(button_box), page->forward_button, FALSE, FALSE, 0);
+
+    /* Pack destination list */
+    gtk_box_pack_start(GTK_BOX(lower_ctlbox), destination_frame, TRUE, TRUE, 0);
+    gtk_container_add(GTK_CONTAINER(destination_frame), destination_scroll);
+    gtk_container_add(GTK_CONTAINER(destination_scroll), page->current);
+
+    /* UI signals */
+    g_signal_connect(G_OBJECT(page->available), "row-activated",
+                     G_CALLBACK(available_row_activated_cb), page);
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
+    g_signal_connect(G_OBJECT(selection), "changed",
+		       G_CALLBACK(available_selection_changed_cb), page);
+
+    g_signal_connect(G_OBJECT(page->current), "row-activated",
+                     G_CALLBACK(current_row_activated_cb), page);
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    g_signal_connect(G_OBJECT(selection), "changed",
+		       G_CALLBACK(current_selection_changed_cb), page);
+
+    g_signal_connect(G_OBJECT(page->add_button), "clicked",
+		       G_CALLBACK(add_button_cb), page);
+    g_signal_connect(G_OBJECT(page->remove_button), "clicked",
+		       G_CALLBACK(remove_button_cb), page);
+    g_signal_connect(G_OBJECT(page->forward_button), "clicked",
+		       G_CALLBACK(forward_button_cb), page);
+    g_signal_connect(G_OBJECT(page->back_button), "clicked",
+		       G_CALLBACK(back_button_cb), page);
+
+    g_signal_connect(G_OBJECT(page->standard_button), "clicked",
+		       G_CALLBACK(standard_button_cb), page);
+    g_signal_connect(G_OBJECT(style_button), "clicked",
+		       G_CALLBACK(style_button_cb), page);
+
+    gtk_widget_set_sensitive(page->add_button, FALSE);
+    gtk_widget_set_sensitive(page->remove_button, FALSE);
+    gtk_widget_set_sensitive(page->back_button, FALSE);
+    gtk_widget_set_sensitive(page->forward_button, FALSE);
+    gtk_widget_set_sensitive(page->standard_button,
+                             !balsa_toolbar_model_is_standard(model));
+
+    tp_page_refresh_available(page);
+    tp_page_refresh_current(page);
+
+    return outer_box;
+}
+
+/* Refresh the page's available GtkTreeView.
+ */
+static void
+tp_page_refresh_available(ToolbarPage * page)
+{
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreePath *path;
+    GHashTable *legal = balsa_toolbar_model_get_legal(page->model);
+    GSList *current = balsa_toolbar_model_get_current(page->model);
+    int item;
+
+    /* save currently selected path, or point path to the first row */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        path = gtk_tree_model_get_path(model, &iter);
+    else {
+        path = gtk_tree_path_new();
+        gtk_tree_path_down(path);
+    }
+    gtk_list_store_clear(GTK_LIST_STORE(model));
+
+    for (item = 0; item < toolbar_button_count; item++) {
+        if (item > 0
+            && (!g_hash_table_lookup(legal,
+                                     toolbar_buttons[item].pixmap_id)
+                || g_slist_find_custom(current,
+                                       toolbar_buttons[item].pixmap_id,
+                                       (GCompareFunc) strcmp)))
+            continue;
+
+        gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+        tp_store_set(GTK_LIST_STORE(model), &iter, item);
+    }
+
+    if (gtk_tree_model_get_iter(model, &iter, path)
+        || gtk_tree_path_prev(path)) {
+        gtk_tree_selection_select_path(selection, path);
+        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->available),
+                                     path, NULL, FALSE, 0, 0);
+    }
+    gtk_tree_path_free(path);
+}
+
+/* Refresh the page's current GtkTreeView.
+ */
+static void
+tp_page_refresh_current(ToolbarPage * page)
+{
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreePath *path;
+    int item;
+    GSList *list;
+
+    /* save currently selected path, or point path to the first row */
+    if (gtk_tree_selection_get_selected(selection, &model, &iter))
+        path = gtk_tree_model_get_path(model, &iter);
+    else {
+        path = gtk_tree_path_new();
+        gtk_tree_path_down(path);
+    }
+    gtk_list_store_clear(GTK_LIST_STORE(model));
+
+    for (list = balsa_toolbar_model_get_current(page->model); list;
+         list = g_slist_next(list)) {
+        item = get_toolbar_button_index(list->data);
+        if (item < 0)
+            continue;
+
+        gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+        tp_store_set(GTK_LIST_STORE(model), &iter, item);
+    }
+
+    if (gtk_tree_model_get_iter(model, &iter, path) 
+        || gtk_tree_path_prev(path)) {
+        gtk_tree_selection_select_path(selection, path);
+        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
+                                     path, NULL, FALSE, 0, 0);
+    }
+    gtk_tree_path_free(path);
+}
+
+/* Refresh a page after its destination TreeView has changed.
+ */
+static void
+tp_page_refresh_preview(ToolbarPage * page)
+{
+    GtkTreeModel *model =
+        gtk_tree_view_get_model(GTK_TREE_VIEW(page->current));
+    GtkTreeIter iter;
+    gboolean valid;
+    
+    balsa_toolbar_model_clear(page->model);
+    for (valid = gtk_tree_model_get_iter_first(model, &iter);
+         valid;
+         valid = gtk_tree_model_iter_next(model, &iter)) {
+        gint item;
+
+        gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
+        if (item >= 0 && item < toolbar_button_count)
+            balsa_toolbar_model_insert_icon(page->model, 
+                                            toolbar_buttons[item].pixmap_id,
+                                            -1);
+    }
+}
+
+/* Create a GtkTreeView for an icon list.
+ */
+static GtkWidget *
+tp_list_new(void)
+{
+    GtkListStore *list_store;
+    GtkTreeView *view;
+    GtkCellRenderer *renderer;
+    GtkTreeViewColumn *column;
+
+    list_store = gtk_list_store_new(TP_N_COLUMNS,
+                                    G_TYPE_STRING,   /* TP_TEXT_COLUMN */
+                                    GDK_TYPE_PIXBUF, /* TP_ICON_COLUMN */
+                                    G_TYPE_INT       /* TP_ITEM_COLUMN */
+                                    );
+    view = GTK_TREE_VIEW(gtk_tree_view_new_with_model
+                         (GTK_TREE_MODEL(list_store)));
+    g_object_unref(list_store);
+
+    renderer = gtk_cell_renderer_text_new();
+    column =
+        gtk_tree_view_column_new_with_attributes(NULL, renderer, "text",
+                                                 TP_TEXT_COLUMN, NULL);
+    gtk_tree_view_append_column(view, column);
+
+    renderer = gtk_cell_renderer_pixbuf_new();
+    column =
+        gtk_tree_view_column_new_with_attributes(NULL, renderer, "pixbuf",
+                                                 TP_ICON_COLUMN, NULL);
+    gtk_tree_view_append_column(view, column);
+
+    gtk_tree_view_set_headers_visible(view, FALSE);
+    return GTK_WIDGET(view);
+}
+
+/* Test whether the iter addresses the first row.
+ */
+static gboolean 
+tp_list_iter_is_first(GtkWidget * list, GtkTreeIter * iter)
+{
+    GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
+    GtkTreePath *path = gtk_tree_model_get_path(model, iter);
+    gboolean ret_val = !gtk_tree_path_prev(path);
+    gtk_tree_path_free(path);
+    return ret_val;
+}
+
+/* Test whether the iter addresses the last row.
+ */
+static gboolean 
+tp_list_iter_is_last(GtkWidget * list, GtkTreeIter * iter)
+{
+    GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
+    GtkTreeIter tmp_iter = *iter;
+    return !gtk_tree_model_iter_next(model, &tmp_iter);
+}
+
+/* Swap the currently selected row in page->current
+ * with the next or previous row.
+ */
+static void
+tp_page_swap_rows(ToolbarPage * page, gboolean forward)
+{
+    GtkTreeModel *model =
+        gtk_tree_view_get_model(GTK_TREE_VIEW(page->current));
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    GtkTreeIter iter;
+    GtkTreeIter tmp_iter;
+    GtkTreePath *tmp_path;
+    gint item, tmp_item;
+
+    selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+        return;
+
+    if (forward) {
+        tmp_iter = iter;
+        if (!gtk_tree_model_iter_next(model, &tmp_iter))
+            return;
+        tmp_path = gtk_tree_model_get_path(model, &tmp_iter);
+    } else {
+        tmp_path = gtk_tree_model_get_path(model, &iter);
+        if (!gtk_tree_path_prev(tmp_path)) {
+            gtk_tree_path_free(tmp_path);
+            return;
+        }
+        gtk_tree_model_get_iter(model, &tmp_iter, tmp_path);
+    }
+
+    gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
+    gtk_tree_model_get(model, &tmp_iter, TP_ITEM_COLUMN, &tmp_item, -1);
+    tp_store_set(GTK_LIST_STORE(model), &tmp_iter, item);
+    tp_store_set(GTK_LIST_STORE(model), &iter, tmp_item);
+
+    gtk_tree_selection_select_path(selection, tmp_path);
+    gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
+                                 tmp_path, NULL, FALSE, 0, 0);
+    gtk_tree_path_free(tmp_path);
+
+    gtk_widget_set_sensitive(page->standard_button, TRUE);
+    tp_page_refresh_preview(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+/* Add an item to a GtkTreeView's GtkListStore.
+ */ 
+static void
+tp_store_set(GtkListStore * store, GtkTreeIter * iter, gint item)
+{
+    GdkPixbuf *pixbuf;
+    gchar *text;
+
+    text = g_strdup(balsa_toolbar_button_text(item));
+    replace_nl_with_space(text);
+    pixbuf = (item > 0
+              ? gtk_widget_render_icon(customize_widget,
+                                       toolbar_buttons[item].pixmap_id,
+                                       GTK_ICON_SIZE_LARGE_TOOLBAR,
+                                       "Balsa")
+              : NULL);
+    gtk_list_store_set(store, iter,
+                       TP_TEXT_COLUMN, text,
+                       TP_ICON_COLUMN, pixbuf,
+                       TP_ITEM_COLUMN, item,
+                       -1);
+    g_free(text);
+    if (pixbuf)
+        g_object_unref(pixbuf);
+}
+
+/* Add an item to the current list.
+ */
+static void
+tp_page_add_selected(ToolbarPage * page)
+{
+    gint item = 0;
+    GtkTreeSelection *selection;
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreeIter sibling;
+    GtkTreePath *path;
+
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(page->available));
+    if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+	return;
+    gtk_tree_model_get(model, &iter, TP_ITEM_COLUMN, &item, -1);
+
+    selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    if (gtk_tree_selection_get_selected(selection, &model, &sibling))
+        gtk_list_store_insert_before(GTK_LIST_STORE(model), &iter, 
+                                     &sibling);
+    else
+        gtk_list_store_append(GTK_LIST_STORE(model), &iter);
+
+    tp_store_set(GTK_LIST_STORE(model), &iter, item);
+	
+    path = gtk_tree_model_get_path(model, &iter);
+    gtk_tree_selection_select_path(selection, path);
+    gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current), path, NULL,
+                                 FALSE, 0, 0);
+    gtk_tree_path_free(path);
+
+    gtk_widget_set_sensitive(page->standard_button, TRUE);
+    tp_page_refresh_preview(page);
+    tp_page_refresh_available(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+/* Remove an item from the page's current list.
+ */
+static void
+tp_page_remove_selected(ToolbarPage * page)
+{
+    GtkTreeSelection *selection =
+        gtk_tree_view_get_selection(GTK_TREE_VIEW(page->current));
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    GtkTreePath *path;
+    
+    if (!gtk_tree_selection_get_selected(selection, &model, &iter))
+        return;
+    path = gtk_tree_model_get_path(model, &iter);
+
+    gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
+
+    if (gtk_tree_model_get_iter(model, &iter, path) 
+        || gtk_tree_path_prev(path)) {
+        gtk_tree_selection_select_path(selection, path);
+        gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(page->current),
+                                     path, NULL, FALSE, 0, 0);
+    }
+    gtk_tree_path_free(path);
+
+    gtk_widget_set_sensitive(page->standard_button, TRUE);
+    tp_page_refresh_preview(page);
+    tp_page_refresh_available(page);
+    balsa_toolbar_model_changed(page->model);
+}
+
+/* Unwrap text.
+ */
+static void
+replace_nl_with_space(char* str)
+{
+    while(*str) {
+	if(*str == '\n')
+	    *str=' ';
+	str++;
+    }
+}
diff --git a/src/toolbar-prefs.o b/src/toolbar-prefs.o
new file mode 100644
index 0000000..bb5c7b8
Binary files /dev/null and b/src/toolbar-prefs.o differ



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]