summaryrefslogtreecommitdiff
path: root/mate-dictionary/src/gdict-sidebar.c
diff options
context:
space:
mode:
Diffstat (limited to 'mate-dictionary/src/gdict-sidebar.c')
-rw-r--r--mate-dictionary/src/gdict-sidebar.c568
1 files changed, 568 insertions, 0 deletions
diff --git a/mate-dictionary/src/gdict-sidebar.c b/mate-dictionary/src/gdict-sidebar.c
new file mode 100644
index 00000000..c8f51be0
--- /dev/null
+++ b/mate-dictionary/src/gdict-sidebar.c
@@ -0,0 +1,568 @@
+/* gdict-sidebar.c - sidebar widget
+ *
+ * Copyright (C) 2006 Emmanuele Bassi <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ *
+ * Based on the equivalent widget from Evince
+ * by Jonathan Blandford,
+ * Copyright (C) 2004 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <gdk/gdkkeysyms.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include "gdict-sidebar.h"
+
+typedef struct
+{
+ guint index;
+
+ gchar *id;
+ gchar *name;
+
+ GtkWidget *child;
+ GtkWidget *menu_item;
+} SidebarPage;
+
+#define GDICT_SIDEBAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDICT_TYPE_SIDEBAR, GdictSidebarPrivate))
+
+struct _GdictSidebarPrivate
+{
+ GHashTable *pages_by_id;
+ GSList *pages;
+
+ GtkWidget *hbox;
+ GtkWidget *notebook;
+ GtkWidget *menu;
+ GtkWidget *close_button;
+ GtkWidget *label;
+ GtkWidget *select_button;
+};
+
+enum
+{
+ PAGE_CHANGED,
+ CLOSED,
+
+ LAST_SIGNAL
+};
+
+static guint sidebar_signals[LAST_SIGNAL] = { 0 };
+static GQuark sidebar_page_id_quark = 0;
+
+G_DEFINE_TYPE (GdictSidebar, gdict_sidebar, GTK_TYPE_VBOX);
+
+SidebarPage *
+sidebar_page_new (const gchar *id,
+ const gchar *name,
+ GtkWidget *widget)
+{
+ SidebarPage *page;
+
+ page = g_slice_new (SidebarPage);
+
+ page->id = g_strdup (id);
+ page->name = g_strdup (name);
+ page->child = widget;
+ page->index = -1;
+ page->menu_item = NULL;
+
+ return page;
+}
+
+void
+sidebar_page_free (SidebarPage *page)
+{
+ if (G_LIKELY (page))
+ {
+ g_free (page->name);
+ g_free (page->id);
+
+ g_slice_free (SidebarPage, page);
+ }
+}
+
+static void
+gdict_sidebar_finalize (GObject *object)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (object);
+ GdictSidebarPrivate *priv = sidebar->priv;
+
+ if (priv->pages_by_id)
+ g_hash_table_destroy (priv->pages_by_id);
+
+ if (priv->pages)
+ {
+ g_slist_foreach (priv->pages, (GFunc) sidebar_page_free, NULL);
+ g_slist_free (priv->pages);
+ }
+
+ G_OBJECT_CLASS (gdict_sidebar_parent_class)->finalize (object);
+}
+
+static void
+gdict_sidebar_dispose (GObject *object)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (object);
+
+ if (sidebar->priv->menu)
+ {
+ gtk_menu_detach (GTK_MENU (sidebar->priv->menu));
+ sidebar->priv->menu = NULL;
+ }
+
+ G_OBJECT_CLASS (gdict_sidebar_parent_class)->dispose (object);
+}
+
+static void
+gdict_sidebar_menu_position_function (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
+{
+ GtkWidget *widget;
+ GtkAllocation allocation;
+
+ g_assert (GTK_IS_BUTTON (user_data));
+
+ widget = GTK_WIDGET (user_data);
+
+ gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
+
+ gtk_widget_get_allocation (widget, &allocation);
+ *x += allocation.x;
+ *y += allocation.y + allocation.height;
+
+ *push_in = FALSE;
+}
+
+static gboolean
+gdict_sidebar_select_button_press_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
+ GtkAllocation allocation;
+
+ if (event->button == 1)
+ {
+ GtkRequisition req;
+ gint width;
+
+ gtk_widget_get_allocation (widget, &allocation);
+ width = allocation.width;
+ gtk_widget_set_size_request (sidebar->priv->menu, -1, -1);
+ gtk_widget_size_request (sidebar->priv->menu, &req);
+ gtk_widget_set_size_request (sidebar->priv->menu,
+ MAX (width, req.width), -1);
+ gtk_widget_grab_focus (widget);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+ gtk_menu_popup (GTK_MENU (sidebar->priv->menu),
+ NULL, NULL,
+ gdict_sidebar_menu_position_function, widget,
+ event->button, event->time);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gdict_sidebar_select_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
+
+ if (event->keyval == GDK_space ||
+ event->keyval == GDK_KP_Space ||
+ event->keyval == GDK_Return ||
+ event->keyval == GDK_KP_Enter)
+ {
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
+ gtk_menu_popup (GTK_MENU (sidebar->priv->menu),
+ NULL, NULL,
+ gdict_sidebar_menu_position_function, widget,
+ 1, event->time);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+gdict_sidebar_close_clicked_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
+
+ g_signal_emit (sidebar, sidebar_signals[CLOSED], 0);
+}
+
+static void
+gdict_sidebar_menu_deactivate_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
+ GdictSidebarPrivate *priv = sidebar->priv;
+ GtkToggleButton *select_button = GTK_TOGGLE_BUTTON (priv->select_button);
+
+ gtk_toggle_button_set_active (select_button, FALSE);
+}
+
+static void
+gdict_sidebar_menu_detach_cb (GtkWidget *widget,
+ GtkMenu *menu)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (widget);
+
+ sidebar->priv->menu = NULL;
+}
+
+static void
+gdict_sidebar_menu_item_activate (GtkWidget *widget,
+ gpointer user_data)
+{
+ GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
+ GdictSidebarPrivate *priv = sidebar->priv;
+ GtkWidget *menu_item;
+ const gchar *id;
+ SidebarPage *page;
+ gint current_index;
+
+ menu_item = gtk_menu_get_active (GTK_MENU (priv->menu));
+ id = g_object_get_qdata (G_OBJECT (menu_item), sidebar_page_id_quark);
+ g_assert (id != NULL);
+
+ page = g_hash_table_lookup (priv->pages_by_id, id);
+ g_assert (page != NULL);
+
+ current_index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+ if (current_index == page->index)
+ return;
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
+ page->index);
+ gtk_label_set_text (GTK_LABEL (priv->label), page->name);
+
+ g_signal_emit (sidebar, sidebar_signals[PAGE_CHANGED], 0);
+}
+
+static void
+gdict_sidebar_class_init (GdictSidebarClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (gobject_class, sizeof (GdictSidebarPrivate));
+
+ sidebar_page_id_quark = g_quark_from_static_string ("gdict-sidebar-page-id");
+
+ gobject_class->finalize = gdict_sidebar_finalize;
+ gobject_class->dispose = gdict_sidebar_dispose;
+
+ sidebar_signals[PAGE_CHANGED] =
+ g_signal_new ("page-changed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdictSidebarClass, page_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ sidebar_signals[CLOSED] =
+ g_signal_new ("closed",
+ G_TYPE_FROM_CLASS (gobject_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdictSidebarClass, closed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gdict_sidebar_init (GdictSidebar *sidebar)
+{
+ GdictSidebarPrivate *priv;
+ GtkWidget *hbox;
+ GtkWidget *select_hbox;
+ GtkWidget *select_button;
+ GtkWidget *close_button;
+ GtkWidget *arrow;
+
+ sidebar->priv = priv = GDICT_SIDEBAR_GET_PRIVATE (sidebar);
+
+ /* we store all the pages inside the list, but we keep
+ * a pointer inside the hash table for faster look up
+ * times; what's inside the table will be destroyed with
+ * the list, so there's no need to supply the destroy
+ * functions for keys and values.
+ */
+ priv->pages = NULL;
+ priv->pages_by_id = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* top option menu */
+ hbox = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (sidebar), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+ priv->hbox = hbox;
+
+ select_button = gtk_toggle_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (select_button), GTK_RELIEF_NONE);
+ g_signal_connect (select_button, "button-press-event",
+ G_CALLBACK (gdict_sidebar_select_button_press_cb),
+ sidebar);
+ g_signal_connect (select_button, "key-press-event",
+ G_CALLBACK (gdict_sidebar_select_key_press_cb),
+ sidebar);
+ priv->select_button = select_button;
+
+ select_hbox = gtk_hbox_new (FALSE, 0);
+
+ priv->label = gtk_label_new (NULL);
+ gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
+ gtk_box_pack_start (GTK_BOX (select_hbox), priv->label, FALSE, FALSE, 0);
+ gtk_widget_show (priv->label);
+
+ arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+ gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);
+ gtk_widget_show (arrow);
+
+ gtk_container_add (GTK_CONTAINER (select_button), select_hbox);
+ gtk_widget_show (select_hbox);
+
+ gtk_box_pack_start (GTK_BOX (hbox), select_button, TRUE, TRUE, 0);
+ gtk_widget_show (select_button);
+
+ close_button = gtk_button_new ();
+ gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
+ gtk_button_set_image (GTK_BUTTON (close_button),
+ gtk_image_new_from_stock (GTK_STOCK_CLOSE,
+ GTK_ICON_SIZE_SMALL_TOOLBAR));
+ g_signal_connect (close_button, "clicked",
+ G_CALLBACK (gdict_sidebar_close_clicked_cb),
+ sidebar);
+ gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
+ gtk_widget_show (close_button);
+ priv->close_button = close_button;
+
+ sidebar->priv->menu = gtk_menu_new ();
+ g_signal_connect (sidebar->priv->menu, "deactivate",
+ G_CALLBACK (gdict_sidebar_menu_deactivate_cb),
+ sidebar);
+ gtk_menu_attach_to_widget (GTK_MENU (sidebar->priv->menu),
+ GTK_WIDGET (sidebar),
+ gdict_sidebar_menu_detach_cb);
+ gtk_widget_show (sidebar->priv->menu);
+
+ sidebar->priv->notebook = gtk_notebook_new ();
+ gtk_notebook_set_show_border (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE);
+ gtk_notebook_set_show_tabs (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE);
+ gtk_box_pack_start (GTK_BOX (sidebar), sidebar->priv->notebook, TRUE, TRUE, 6);
+ gtk_widget_show (sidebar->priv->notebook);
+}
+
+/*
+ * Public API
+ */
+
+GtkWidget *
+gdict_sidebar_new (void)
+{
+ return g_object_new (GDICT_TYPE_SIDEBAR, NULL);
+}
+
+void
+gdict_sidebar_add_page (GdictSidebar *sidebar,
+ const gchar *page_id,
+ const gchar *page_name,
+ GtkWidget *page_widget)
+{
+ GdictSidebarPrivate *priv;
+ SidebarPage *page;
+ GtkWidget *menu_item;
+
+ g_return_if_fail (GDICT_IS_SIDEBAR (sidebar));
+ g_return_if_fail (page_id != NULL);
+ g_return_if_fail (page_name != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (page_widget));
+
+ priv = sidebar->priv;
+
+ if (g_hash_table_lookup (priv->pages_by_id, page_id))
+ {
+ g_warning ("Attempting to add a page to the sidebar with "
+ "id `%s', but there already is a page with the "
+ "same id. Aborting...",
+ page_id);
+ return;
+ }
+
+ /* add the page inside the page list */
+ page = sidebar_page_new (page_id, page_name, page_widget);
+
+ priv->pages = g_slist_append (priv->pages, page);
+ g_hash_table_insert (priv->pages_by_id, page->id, page);
+
+ page->index = gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook),
+ page_widget,
+ NULL);
+
+ /* add the menu item for the page */
+ menu_item = gtk_image_menu_item_new_with_label (page_name);
+ g_object_set_qdata_full (G_OBJECT (menu_item),
+ sidebar_page_id_quark,
+ g_strdup (page_id),
+ (GDestroyNotify) g_free);
+ g_signal_connect (menu_item, "activate",
+ G_CALLBACK (gdict_sidebar_menu_item_activate),
+ sidebar);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), menu_item);
+ gtk_widget_show (menu_item);
+ page->menu_item = menu_item;
+
+ gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), menu_item);
+ gtk_label_set_text (GTK_LABEL (priv->label), page_name);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index);
+}
+
+void
+gdict_sidebar_remove_page (GdictSidebar *sidebar,
+ const gchar *page_id)
+{
+ GdictSidebarPrivate *priv;
+ SidebarPage *page;
+ GList *children, *l;
+
+ g_return_if_fail (GDICT_IS_SIDEBAR (sidebar));
+ g_return_if_fail (page_id != NULL);
+
+ priv = sidebar->priv;
+
+ if ((page = g_hash_table_lookup (priv->pages_by_id, page_id)) == NULL)
+ {
+ g_warning ("Attempting to remove a page from the sidebar with "
+ "id `%s', but there is no page with this id. Aborting...",
+ page_id);
+ return;
+ }
+
+ children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
+ for (l = children; l != NULL; l = l->next)
+ {
+ GtkWidget *menu_item = l->data;
+
+ if (menu_item == page->menu_item)
+ {
+ gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
+ break;
+ }
+ }
+ g_list_free (children);
+
+ gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), page->index);
+
+ g_hash_table_remove (priv->pages_by_id, page->id);
+ priv->pages = g_slist_remove (priv->pages, page);
+
+ sidebar_page_free (page);
+
+ /* select the first page, if present */
+ page = priv->pages->data;
+ if (page)
+ {
+ gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), page->menu_item);
+ gtk_label_set_text (GTK_LABEL (priv->label), page->name);
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index);
+ }
+ else
+ gtk_widget_hide (GTK_WIDGET (sidebar));
+}
+
+void
+gdict_sidebar_view_page (GdictSidebar *sidebar,
+ const gchar *page_id)
+{
+ GdictSidebarPrivate *priv;
+ SidebarPage *page;
+
+ g_return_if_fail (GDICT_IS_SIDEBAR (sidebar));
+ g_return_if_fail (page_id != NULL);
+
+ priv = sidebar->priv;
+ page = g_hash_table_lookup (priv->pages_by_id, page_id);
+ if (!page)
+ return;
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index);
+ gtk_label_set_text (GTK_LABEL (priv->label), page->name);
+ gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), page->menu_item);
+}
+
+const gchar *
+gdict_sidebar_current_page (GdictSidebar *sidebar)
+{
+ GdictSidebarPrivate *priv;
+ gint index;
+ SidebarPage *page;
+
+ g_return_val_if_fail (GDICT_IS_SIDEBAR (sidebar), NULL);
+
+ priv = sidebar->priv;
+
+ index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
+ page = g_slist_nth_data (priv->pages, index);
+ g_assert (page != NULL);
+
+ return page->id;
+}
+
+gchar **
+gdict_sidebar_list_pages (GdictSidebar *sidebar,
+ gsize *length)
+{
+ GdictSidebarPrivate *priv;
+ gchar **retval;
+ gint i;
+ GSList *l;
+
+ g_return_val_if_fail (GDICT_IS_SIDEBAR (sidebar), NULL);
+
+ priv = sidebar->priv;
+
+ retval = g_new (gchar*, g_slist_length (priv->pages) + 1);
+ for (l = priv->pages, i = 0; l; l = l->next, i++)
+ retval[i++] = g_strdup (l->data);
+
+ retval[i] = NULL;
+
+ if (length)
+ *length = i;
+
+ return retval;
+}