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.
33 #include <gdk/gdkkeysyms.h>
35 #include <glib/gi18n.h>
37 #include "gdict-sidebar.h"
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
;
60 GtkWidget
*close_button
;
62 GtkWidget
*select_button
;
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
);
79 sidebar_page_new (const gchar
*id
,
85 page
= g_slice_new (SidebarPage
);
87 page
->id
= g_strdup (id
);
88 page
->name
= g_strdup (name
);
91 page
->menu_item
= NULL
;
97 sidebar_page_free (SidebarPage
*page
)
104 g_slice_free (SidebarPage
, page
);
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
);
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
);
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
);
141 gdict_sidebar_menu_position_function (GtkMenu
*menu
,
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
);
158 *y
+= allocation
.y
+ allocation
.height
;
164 gdict_sidebar_select_button_press_cb (GtkWidget
*widget
,
165 GdkEventButton
*event
,
168 GdictSidebar
*sidebar
= GDICT_SIDEBAR (user_data
);
169 GtkAllocation allocation
;
171 if (event
->button
== 1)
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
),
187 gdict_sidebar_menu_position_function
, widget
,
188 event
->button
, event
->time
);
197 gdict_sidebar_select_key_press_cb (GtkWidget
*widget
,
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
),
211 gdict_sidebar_menu_position_function
, widget
,
221 gdict_sidebar_close_clicked_cb (GtkWidget
*widget
,
224 GdictSidebar
*sidebar
= GDICT_SIDEBAR (user_data
);
226 g_signal_emit (sidebar
, sidebar_signals
[CLOSED
], 0);
230 gdict_sidebar_menu_deactivate_cb (GtkWidget
*widget
,
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
);
241 gdict_sidebar_menu_detach_cb (GtkWidget
*widget
,
244 GdictSidebar
*sidebar
= GDICT_SIDEBAR (widget
);
246 sidebar
->priv
->menu
= NULL
;
250 gdict_sidebar_menu_item_activate (GtkWidget
*widget
,
253 GdictSidebar
*sidebar
= GDICT_SIDEBAR (user_data
);
254 GdictSidebarPrivate
*priv
= sidebar
->priv
;
255 GtkWidget
*menu_item
;
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
)
271 gtk_notebook_set_current_page (GTK_NOTEBOOK (priv
->notebook
),
273 gtk_label_set_text (GTK_LABEL (priv
->label
), page
->name
);
275 g_signal_emit (sidebar
, sidebar_signals
[PAGE_CHANGED
], 0);
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
),
294 G_STRUCT_OFFSET (GdictSidebarClass
, page_changed
),
296 g_cclosure_marshal_VOID__VOID
,
298 sidebar_signals
[CLOSED
] =
299 g_signal_new ("closed",
300 G_TYPE_FROM_CLASS (gobject_class
),
302 G_STRUCT_OFFSET (GdictSidebarClass
, closed
),
304 g_cclosure_marshal_VOID__VOID
,
309 gdict_sidebar_init (GdictSidebar
*sidebar
)
311 GdictSidebarPrivate
*priv
;
313 GtkWidget
*select_hbox
;
314 GtkWidget
*select_button
;
315 GtkWidget
*close_button
;
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.
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
);
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
),
340 g_signal_connect (select_button
, "key-press-event",
341 G_CALLBACK (gdict_sidebar_select_key_press_cb
),
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
),
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
),
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
);
395 gdict_sidebar_new (void)
397 return g_object_new (GDICT_TYPE_SIDEBAR
, NULL
);
401 gdict_sidebar_add_page (GdictSidebar
*sidebar
,
402 const gchar
*page_id
,
403 const gchar
*page_name
,
404 GtkWidget
*page_widget
)
406 GdictSidebarPrivate
*priv
;
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...",
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
),
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
,
441 (GDestroyNotify
) g_free
);
442 g_signal_connect (menu_item
, "activate",
443 G_CALLBACK (gdict_sidebar_menu_item_activate
),
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
);
455 gdict_sidebar_remove_page (GdictSidebar
*sidebar
,
456 const gchar
*page_id
)
458 GdictSidebarPrivate
*priv
;
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...",
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
);
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
;
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
);
504 gtk_widget_hide (GTK_WIDGET (sidebar
));
508 gdict_sidebar_view_page (GdictSidebar
*sidebar
,
509 const gchar
*page_id
)
511 GdictSidebarPrivate
*priv
;
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
);
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
);
528 gdict_sidebar_current_page (GdictSidebar
*sidebar
)
530 GdictSidebarPrivate
*priv
;
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
);
547 gdict_sidebar_list_pages (GdictSidebar
*sidebar
,
550 GdictSidebarPrivate
*priv
;
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
);