Updated Spanish translation
[gnome-utils.git] / gnome-dictionary / src / gdict-sidebar.c
blobfbe1b07ff79c5379aff2f65d8996b306f332c68d
1 /* gdict-sidebar.c - sidebar widget
3 * Copyright (C) 2006 Emmanuele Bassi <ebassi@gmail.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Based on the equivalent widget from Evince
20 * by Jonathan Blandford,
21 * Copyright (C) 2004 Red Hat, Inc.
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdarg.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
37 #include "gdict-sidebar.h"
39 typedef struct
41 guint index;
43 gchar *id;
44 gchar *name;
46 GtkWidget *child;
47 GtkWidget *menu_item;
48 } SidebarPage;
50 #define GDICT_SIDEBAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GDICT_TYPE_SIDEBAR, GdictSidebarPrivate))
52 struct _GdictSidebarPrivate
54 GHashTable *pages_by_id;
55 GSList *pages;
57 GtkWidget *hbox;
58 GtkWidget *notebook;
59 GtkWidget *menu;
60 GtkWidget *close_button;
61 GtkWidget *label;
62 GtkWidget *select_button;
65 enum
67 PAGE_CHANGED,
68 CLOSED,
70 LAST_SIGNAL
73 static guint sidebar_signals[LAST_SIGNAL] = { 0 };
74 static GQuark sidebar_page_id_quark = 0;
76 G_DEFINE_TYPE (GdictSidebar, gdict_sidebar, GTK_TYPE_VBOX);
78 SidebarPage *
79 sidebar_page_new (const gchar *id,
80 const gchar *name,
81 GtkWidget *widget)
83 SidebarPage *page;
85 page = g_slice_new (SidebarPage);
87 page->id = g_strdup (id);
88 page->name = g_strdup (name);
89 page->child = widget;
90 page->index = -1;
91 page->menu_item = NULL;
93 return page;
96 void
97 sidebar_page_free (SidebarPage *page)
99 if (G_LIKELY (page))
101 g_free (page->name);
102 g_free (page->id);
104 g_slice_free (SidebarPage, page);
108 static void
109 gdict_sidebar_finalize (GObject *object)
111 GdictSidebar *sidebar = GDICT_SIDEBAR (object);
112 GdictSidebarPrivate *priv = sidebar->priv;
114 if (priv->pages_by_id)
115 g_hash_table_destroy (priv->pages_by_id);
117 if (priv->pages)
119 g_slist_foreach (priv->pages, (GFunc) sidebar_page_free, NULL);
120 g_slist_free (priv->pages);
123 G_OBJECT_CLASS (gdict_sidebar_parent_class)->finalize (object);
126 static void
127 gdict_sidebar_dispose (GObject *object)
129 GdictSidebar *sidebar = GDICT_SIDEBAR (object);
131 if (sidebar->priv->menu)
133 gtk_menu_detach (GTK_MENU (sidebar->priv->menu));
134 sidebar->priv->menu = NULL;
137 G_OBJECT_CLASS (gdict_sidebar_parent_class)->dispose (object);
140 static void
141 gdict_sidebar_menu_position_function (GtkMenu *menu,
142 gint *x,
143 gint *y,
144 gboolean *push_in,
145 gpointer user_data)
147 GtkWidget *widget;
148 GtkAllocation allocation;
150 g_assert (GTK_IS_BUTTON (user_data));
152 widget = GTK_WIDGET (user_data);
154 gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
156 gtk_widget_get_allocation (widget, &allocation);
157 *x += allocation.x;
158 *y += allocation.y + allocation.height;
160 *push_in = FALSE;
163 static gboolean
164 gdict_sidebar_select_button_press_cb (GtkWidget *widget,
165 GdkEventButton *event,
166 gpointer user_data)
168 GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
169 GtkAllocation allocation;
171 if (event->button == 1)
173 GtkRequisition req;
174 gint width;
176 gtk_widget_get_allocation (widget, &allocation);
177 width = allocation.width;
178 gtk_widget_set_size_request (sidebar->priv->menu, -1, -1);
179 gtk_widget_size_request (sidebar->priv->menu, &req);
180 gtk_widget_set_size_request (sidebar->priv->menu,
181 MAX (width, req.width), -1);
182 gtk_widget_grab_focus (widget);
184 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
185 gtk_menu_popup (GTK_MENU (sidebar->priv->menu),
186 NULL, NULL,
187 gdict_sidebar_menu_position_function, widget,
188 event->button, event->time);
190 return TRUE;
193 return FALSE;
196 static gboolean
197 gdict_sidebar_select_key_press_cb (GtkWidget *widget,
198 GdkEventKey *event,
199 gpointer user_data)
201 GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
203 if (event->keyval == GDK_KEY_space ||
204 event->keyval == GDK_KEY_KP_Space ||
205 event->keyval == GDK_KEY_Return ||
206 event->keyval == GDK_KEY_KP_Enter)
208 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
209 gtk_menu_popup (GTK_MENU (sidebar->priv->menu),
210 NULL, NULL,
211 gdict_sidebar_menu_position_function, widget,
212 1, event->time);
214 return TRUE;
217 return FALSE;
220 static void
221 gdict_sidebar_close_clicked_cb (GtkWidget *widget,
222 gpointer user_data)
224 GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
226 g_signal_emit (sidebar, sidebar_signals[CLOSED], 0);
229 static void
230 gdict_sidebar_menu_deactivate_cb (GtkWidget *widget,
231 gpointer user_data)
233 GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
234 GdictSidebarPrivate *priv = sidebar->priv;
235 GtkToggleButton *select_button = GTK_TOGGLE_BUTTON (priv->select_button);
237 gtk_toggle_button_set_active (select_button, FALSE);
240 static void
241 gdict_sidebar_menu_detach_cb (GtkWidget *widget,
242 GtkMenu *menu)
244 GdictSidebar *sidebar = GDICT_SIDEBAR (widget);
246 sidebar->priv->menu = NULL;
249 static void
250 gdict_sidebar_menu_item_activate (GtkWidget *widget,
251 gpointer user_data)
253 GdictSidebar *sidebar = GDICT_SIDEBAR (user_data);
254 GdictSidebarPrivate *priv = sidebar->priv;
255 GtkWidget *menu_item;
256 const gchar *id;
257 SidebarPage *page;
258 gint current_index;
260 menu_item = gtk_menu_get_active (GTK_MENU (priv->menu));
261 id = g_object_get_qdata (G_OBJECT (menu_item), sidebar_page_id_quark);
262 g_assert (id != NULL);
264 page = g_hash_table_lookup (priv->pages_by_id, id);
265 g_assert (page != NULL);
267 current_index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
268 if (current_index == page->index)
269 return;
271 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook),
272 page->index);
273 gtk_label_set_text (GTK_LABEL (priv->label), page->name);
275 g_signal_emit (sidebar, sidebar_signals[PAGE_CHANGED], 0);
278 static void
279 gdict_sidebar_class_init (GdictSidebarClass *klass)
281 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
283 g_type_class_add_private (gobject_class, sizeof (GdictSidebarPrivate));
285 sidebar_page_id_quark = g_quark_from_static_string ("gdict-sidebar-page-id");
287 gobject_class->finalize = gdict_sidebar_finalize;
288 gobject_class->dispose = gdict_sidebar_dispose;
290 sidebar_signals[PAGE_CHANGED] =
291 g_signal_new ("page-changed",
292 G_TYPE_FROM_CLASS (gobject_class),
293 G_SIGNAL_RUN_LAST,
294 G_STRUCT_OFFSET (GdictSidebarClass, page_changed),
295 NULL, NULL,
296 g_cclosure_marshal_VOID__VOID,
297 G_TYPE_NONE, 0);
298 sidebar_signals[CLOSED] =
299 g_signal_new ("closed",
300 G_TYPE_FROM_CLASS (gobject_class),
301 G_SIGNAL_RUN_LAST,
302 G_STRUCT_OFFSET (GdictSidebarClass, closed),
303 NULL, NULL,
304 g_cclosure_marshal_VOID__VOID,
305 G_TYPE_NONE, 0);
308 static void
309 gdict_sidebar_init (GdictSidebar *sidebar)
311 GdictSidebarPrivate *priv;
312 GtkWidget *hbox;
313 GtkWidget *select_hbox;
314 GtkWidget *select_button;
315 GtkWidget *close_button;
316 GtkWidget *arrow;
318 sidebar->priv = priv = GDICT_SIDEBAR_GET_PRIVATE (sidebar);
320 /* we store all the pages inside the list, but we keep
321 * a pointer inside the hash table for faster look up
322 * times; what's inside the table will be destroyed with
323 * the list, so there's no need to supply the destroy
324 * functions for keys and values.
326 priv->pages = NULL;
327 priv->pages_by_id = g_hash_table_new (g_str_hash, g_str_equal);
329 /* top option menu */
330 hbox = gtk_hbox_new (FALSE, 0);
331 gtk_box_pack_start (GTK_BOX (sidebar), hbox, FALSE, FALSE, 0);
332 gtk_widget_show (hbox);
333 priv->hbox = hbox;
335 select_button = gtk_toggle_button_new ();
336 gtk_button_set_relief (GTK_BUTTON (select_button), GTK_RELIEF_NONE);
337 g_signal_connect (select_button, "button-press-event",
338 G_CALLBACK (gdict_sidebar_select_button_press_cb),
339 sidebar);
340 g_signal_connect (select_button, "key-press-event",
341 G_CALLBACK (gdict_sidebar_select_key_press_cb),
342 sidebar);
343 priv->select_button = select_button;
345 select_hbox = gtk_hbox_new (FALSE, 0);
347 priv->label = gtk_label_new (NULL);
348 gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
349 gtk_box_pack_start (GTK_BOX (select_hbox), priv->label, FALSE, FALSE, 0);
350 gtk_widget_show (priv->label);
352 arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
353 gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);
354 gtk_widget_show (arrow);
356 gtk_container_add (GTK_CONTAINER (select_button), select_hbox);
357 gtk_widget_show (select_hbox);
359 gtk_box_pack_start (GTK_BOX (hbox), select_button, TRUE, TRUE, 0);
360 gtk_widget_show (select_button);
362 close_button = gtk_button_new ();
363 gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
364 gtk_button_set_image (GTK_BUTTON (close_button),
365 gtk_image_new_from_stock (GTK_STOCK_CLOSE,
366 GTK_ICON_SIZE_SMALL_TOOLBAR));
367 g_signal_connect (close_button, "clicked",
368 G_CALLBACK (gdict_sidebar_close_clicked_cb),
369 sidebar);
370 gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
371 gtk_widget_show (close_button);
372 priv->close_button = close_button;
374 sidebar->priv->menu = gtk_menu_new ();
375 g_signal_connect (sidebar->priv->menu, "deactivate",
376 G_CALLBACK (gdict_sidebar_menu_deactivate_cb),
377 sidebar);
378 gtk_menu_attach_to_widget (GTK_MENU (sidebar->priv->menu),
379 GTK_WIDGET (sidebar),
380 gdict_sidebar_menu_detach_cb);
381 gtk_widget_show (sidebar->priv->menu);
383 sidebar->priv->notebook = gtk_notebook_new ();
384 gtk_notebook_set_show_border (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE);
385 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (sidebar->priv->notebook), FALSE);
386 gtk_box_pack_start (GTK_BOX (sidebar), sidebar->priv->notebook, TRUE, TRUE, 6);
387 gtk_widget_show (sidebar->priv->notebook);
391 * Public API
394 GtkWidget *
395 gdict_sidebar_new (void)
397 return g_object_new (GDICT_TYPE_SIDEBAR, NULL);
400 void
401 gdict_sidebar_add_page (GdictSidebar *sidebar,
402 const gchar *page_id,
403 const gchar *page_name,
404 GtkWidget *page_widget)
406 GdictSidebarPrivate *priv;
407 SidebarPage *page;
408 GtkWidget *menu_item;
410 g_return_if_fail (GDICT_IS_SIDEBAR (sidebar));
411 g_return_if_fail (page_id != NULL);
412 g_return_if_fail (page_name != NULL);
413 g_return_if_fail (GTK_IS_WIDGET (page_widget));
415 priv = sidebar->priv;
417 if (g_hash_table_lookup (priv->pages_by_id, page_id))
419 g_warning ("Attempting to add a page to the sidebar with "
420 "id `%s', but there already is a page with the "
421 "same id. Aborting...",
422 page_id);
423 return;
426 /* add the page inside the page list */
427 page = sidebar_page_new (page_id, page_name, page_widget);
429 priv->pages = g_slist_append (priv->pages, page);
430 g_hash_table_insert (priv->pages_by_id, page->id, page);
432 page->index = gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook),
433 page_widget,
434 NULL);
436 /* add the menu item for the page */
437 menu_item = gtk_image_menu_item_new_with_label (page_name);
438 g_object_set_qdata_full (G_OBJECT (menu_item),
439 sidebar_page_id_quark,
440 g_strdup (page_id),
441 (GDestroyNotify) g_free);
442 g_signal_connect (menu_item, "activate",
443 G_CALLBACK (gdict_sidebar_menu_item_activate),
444 sidebar);
445 gtk_menu_shell_append (GTK_MENU_SHELL (priv->menu), menu_item);
446 gtk_widget_show (menu_item);
447 page->menu_item = menu_item;
449 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), menu_item);
450 gtk_label_set_text (GTK_LABEL (priv->label), page_name);
451 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index);
454 void
455 gdict_sidebar_remove_page (GdictSidebar *sidebar,
456 const gchar *page_id)
458 GdictSidebarPrivate *priv;
459 SidebarPage *page;
460 GList *children, *l;
462 g_return_if_fail (GDICT_IS_SIDEBAR (sidebar));
463 g_return_if_fail (page_id != NULL);
465 priv = sidebar->priv;
467 if ((page = g_hash_table_lookup (priv->pages_by_id, page_id)) == NULL)
469 g_warning ("Attempting to remove a page from the sidebar with "
470 "id `%s', but there is no page with this id. Aborting...",
471 page_id);
472 return;
475 children = gtk_container_get_children (GTK_CONTAINER (priv->menu));
476 for (l = children; l != NULL; l = l->next)
478 GtkWidget *menu_item = l->data;
480 if (menu_item == page->menu_item)
482 gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
483 break;
486 g_list_free (children);
488 gtk_notebook_remove_page (GTK_NOTEBOOK (priv->notebook), page->index);
490 g_hash_table_remove (priv->pages_by_id, page->id);
491 priv->pages = g_slist_remove (priv->pages, page);
493 sidebar_page_free (page);
495 /* select the first page, if present */
496 page = priv->pages->data;
497 if (page)
499 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), page->menu_item);
500 gtk_label_set_text (GTK_LABEL (priv->label), page->name);
501 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index);
503 else
504 gtk_widget_hide (GTK_WIDGET (sidebar));
507 void
508 gdict_sidebar_view_page (GdictSidebar *sidebar,
509 const gchar *page_id)
511 GdictSidebarPrivate *priv;
512 SidebarPage *page;
514 g_return_if_fail (GDICT_IS_SIDEBAR (sidebar));
515 g_return_if_fail (page_id != NULL);
517 priv = sidebar->priv;
518 page = g_hash_table_lookup (priv->pages_by_id, page_id);
519 if (!page)
520 return;
522 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page->index);
523 gtk_label_set_text (GTK_LABEL (priv->label), page->name);
524 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->menu), page->menu_item);
527 const gchar *
528 gdict_sidebar_current_page (GdictSidebar *sidebar)
530 GdictSidebarPrivate *priv;
531 gint index;
532 SidebarPage *page;
534 g_return_val_if_fail (GDICT_IS_SIDEBAR (sidebar), NULL);
536 priv = sidebar->priv;
538 index = gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->notebook));
539 page = g_slist_nth_data (priv->pages, index);
540 if (page == NULL)
541 return NULL;
543 return page->id;
546 gchar **
547 gdict_sidebar_list_pages (GdictSidebar *sidebar,
548 gsize *length)
550 GdictSidebarPrivate *priv;
551 gchar **retval;
552 gint i;
553 GSList *l;
555 g_return_val_if_fail (GDICT_IS_SIDEBAR (sidebar), NULL);
557 priv = sidebar->priv;
559 retval = g_new (gchar*, g_slist_length (priv->pages) + 1);
560 for (l = priv->pages, i = 0; l; l = l->next, i++)
561 retval[i++] = g_strdup (l->data);
563 retval[i] = NULL;
565 if (length)
566 *length = i;
568 return retval;