[balsa] Identify important icon text
- From: Peter Bloomfield <PeterB src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [balsa] Identify important icon text
- Date: Sun, 25 Oct 2009 01:41:53 +0000 (UTC)
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]