]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar.c
expand the selection widget to be always as wide as the sidebar
[evince.git] / shell / ev-sidebar.c
1 /* this file is part of evince, a gnome document viewer
2  *
3  *  Copyright (C) 2004 Red Hat, Inc.
4  *
5  *  Author:
6  *    Jonathan Blandford <jrb@alum.mit.edu>
7  *
8  * Evince is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Evince is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <string.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30
31 #include "ev-sidebar.h"
32 #include "ev-document-thumbnails.h"
33 #include "ev-document-links.h"
34 #include "ev-sidebar-links.h"
35 #include "ev-sidebar-thumbnails.h"
36
37 typedef struct
38 {
39         char *id;
40         char *title;
41         GtkWidget *main_widget;
42 } EvSidebarPage;
43
44 enum
45 {
46         PAGE_COLUMN_ID,
47         PAGE_COLUMN_TITLE,
48         PAGE_COLUMN_MENU_ITEM,
49         PAGE_COLUMN_MAIN_WIDGET,
50         PAGE_COLUMN_NOTEBOOK_INDEX,
51         PAGE_COLUMN_NUM_COLS
52 };
53
54 struct _EvSidebarPrivate {
55         GtkWidget *notebook;
56         GtkWidget *menu;
57         GtkWidget *frame;
58         GtkWidget *hbox;
59         GtkWidget *label;
60            
61         GtkTreeModel *page_model;
62 };
63
64 G_DEFINE_TYPE (EvSidebar, ev_sidebar, GTK_TYPE_VBOX)
65
66 #define EV_SIDEBAR_GET_PRIVATE(object) \
67                 (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR, EvSidebarPrivate))
68
69 static void
70 ev_sidebar_destroy (GtkObject *object)
71 {
72         EvSidebar *ev_sidebar = EV_SIDEBAR (object);
73
74         if (ev_sidebar->priv->menu) {
75                 gtk_menu_detach (GTK_MENU (ev_sidebar->priv->menu));
76                 ev_sidebar->priv->menu = NULL;
77         }
78            
79         (* GTK_OBJECT_CLASS (ev_sidebar_parent_class)->destroy) (object);
80 }
81
82 static void
83 ev_sidebar_class_init (EvSidebarClass *ev_sidebar_class)
84 {
85         GObjectClass *g_object_class;
86         GtkWidgetClass *widget_class;
87         GtkObjectClass *gtk_object_klass;
88  
89         g_object_class = G_OBJECT_CLASS (ev_sidebar_class);
90         widget_class = GTK_WIDGET_CLASS (ev_sidebar_class);
91         gtk_object_klass = GTK_OBJECT_CLASS (ev_sidebar_class);
92            
93         g_type_class_add_private (g_object_class, sizeof (EvSidebarPrivate));
94            
95         gtk_object_klass->destroy = ev_sidebar_destroy;
96 }
97
98 static void
99 ev_sidebar_menu_position_under (GtkMenu  *menu,
100                                 int      *x,
101                                 int      *y,
102                                 gboolean *push_in,
103                                 gpointer  user_data)
104 {
105         GtkWidget *widget;
106
107         g_return_if_fail (GTK_IS_BUTTON (user_data));
108         g_return_if_fail (GTK_WIDGET_NO_WINDOW (user_data));
109
110         widget = GTK_WIDGET (user_data);
111            
112         gdk_window_get_origin (widget->window, x, y);
113            
114         *x += widget->allocation.x;
115         *y += widget->allocation.y + widget->allocation.height;
116            
117         *push_in = FALSE;
118 }
119
120 static gboolean
121 ev_sidebar_select_button_press_cb (GtkWidget      *widget,
122                                    GdkEventButton *event,
123                                    gpointer        user_data)
124 {
125         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
126
127         if (event->button == 1) {
128                 gtk_widget_grab_focus (widget);
129                          
130                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
131                 gtk_menu_popup (GTK_MENU (ev_sidebar->priv->menu),
132                                 NULL, NULL, ev_sidebar_menu_position_under, widget,
133                                 event->button, event->time);
134
135                 return TRUE;
136         }
137
138         return FALSE;
139 }
140
141 static gboolean
142 ev_sidebar_select_button_key_press_cb (GtkWidget   *widget,
143                                        GdkEventKey *event,
144                                        gpointer     user_data)
145 {
146         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
147            
148         if (event->keyval == GDK_space ||
149             event->keyval == GDK_KP_Space ||
150             event->keyval == GDK_Return ||
151             event->keyval == GDK_KP_Enter) {
152                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
153                 gtk_menu_popup (GTK_MENU (ev_sidebar->priv->menu),
154                                 NULL, NULL, ev_sidebar_menu_position_under, widget,
155                                 1, event->time);
156                 return TRUE;
157         }
158
159         return FALSE;
160 }
161
162 static void
163 ev_sidebar_close_clicked_cb (GtkWidget *widget,
164                              gpointer   user_data)
165 {
166         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
167
168         gtk_widget_hide (GTK_WIDGET (ev_sidebar));
169 }
170
171 static void
172 ev_sidebar_menu_deactivate_cb (GtkWidget *widget,
173                                gpointer   user_data)
174 {
175         GtkWidget *menu_button;
176
177         menu_button = GTK_WIDGET (user_data);
178            
179         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
180 }
181
182 static void
183 ev_sidebar_menu_detach_cb (GtkWidget *widget,
184                            GtkMenu   *menu)
185 {
186         EvSidebar *ev_sidebar = EV_SIDEBAR (widget);
187            
188         ev_sidebar->priv->menu = NULL;
189 }
190
191 static void
192 ev_sidebar_menu_item_activate_cb (GtkWidget *widget,
193                                   gpointer   user_data)
194 {
195         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
196         GtkTreeIter iter;
197         GtkWidget *menu_item, *item;
198         gchar *title;
199         gboolean valid;
200         gint index;
201
202         menu_item = gtk_menu_get_active (GTK_MENU (ev_sidebar->priv->menu));
203         valid = gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
204            
205         while (valid) {
206                 gtk_tree_model_get (ev_sidebar->priv->page_model,
207                                     &iter,
208                                     PAGE_COLUMN_TITLE, &title, 
209                                     PAGE_COLUMN_MENU_ITEM, &item,
210                                     PAGE_COLUMN_NOTEBOOK_INDEX, &index,
211                                     -1);
212                          
213                 if (item == menu_item) {
214                         gtk_notebook_set_current_page
215                                 (GTK_NOTEBOOK (ev_sidebar->priv->notebook), index);
216                         gtk_label_set_text (GTK_LABEL (ev_sidebar->priv->label), title);
217                         valid = FALSE;
218                 } else {
219                         valid = gtk_tree_model_iter_next (ev_sidebar->priv->page_model, &iter);
220                 }
221         }
222 }
223
224 static void
225 ev_sidebar_init (EvSidebar *ev_sidebar)
226 {
227         GtkWidget *frame;
228         GtkWidget *hbox;
229         GtkWidget *close_button;
230         GtkWidget *select_button;
231         GtkWidget *select_hbox;
232         GtkWidget *arrow;
233         GtkWidget *image;
234
235         ev_sidebar->priv = EV_SIDEBAR_GET_PRIVATE (ev_sidebar);
236
237         /* data model */
238         ev_sidebar->priv->page_model = (GtkTreeModel *)
239                         gtk_list_store_new (PAGE_COLUMN_NUM_COLS,
240                                             G_TYPE_STRING,
241                                             G_TYPE_STRING,
242                                             GTK_TYPE_WIDGET,
243                                             GTK_TYPE_WIDGET,
244                                             G_TYPE_INT);
245
246         /* top option menu */
247         frame = gtk_frame_new (NULL);
248         ev_sidebar->priv->frame = frame;
249         gtk_box_pack_start (GTK_BOX (ev_sidebar), frame, FALSE, FALSE, 0);
250         gtk_widget_show (frame);
251
252         hbox = gtk_hbox_new (FALSE, 0);
253         ev_sidebar->priv->hbox = hbox;
254         gtk_container_add (GTK_CONTAINER (frame), hbox);
255         gtk_widget_show (hbox);
256
257         select_button = gtk_toggle_button_new ();
258         gtk_button_set_relief (GTK_BUTTON (select_button), GTK_RELIEF_NONE);
259         g_signal_connect (select_button, "button_press_event",
260                           G_CALLBACK (ev_sidebar_select_button_press_cb),
261                           ev_sidebar);
262         g_signal_connect (select_button, "key_press_event",
263                           G_CALLBACK (ev_sidebar_select_button_key_press_cb),
264                           ev_sidebar);
265
266         select_hbox = gtk_hbox_new (FALSE, 0);
267
268         ev_sidebar->priv->label = gtk_label_new ("");
269         gtk_box_pack_start (GTK_BOX (select_hbox),
270                             ev_sidebar->priv->label,
271                             FALSE, FALSE, 0);
272         gtk_widget_show (ev_sidebar->priv->label);
273
274         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
275         gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);
276         gtk_widget_show (arrow);
277
278         gtk_container_add (GTK_CONTAINER (select_button), select_hbox);
279         gtk_widget_show (select_hbox);
280
281         gtk_box_pack_start (GTK_BOX (hbox), select_button, TRUE, TRUE, 0);
282         gtk_widget_show (select_button);
283
284         close_button = gtk_button_new ();
285         gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
286         g_signal_connect (close_button, "clicked",
287                           G_CALLBACK (ev_sidebar_close_clicked_cb),
288                           ev_sidebar);
289            
290         image = gtk_image_new_from_stock (GTK_STOCK_CLOSE,
291                                           GTK_ICON_SIZE_SMALL_TOOLBAR);
292         gtk_container_add (GTK_CONTAINER (close_button), image);
293         gtk_widget_show (image);
294    
295         gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
296         gtk_widget_show (close_button);
297    
298         ev_sidebar->priv->menu = gtk_menu_new ();
299         g_signal_connect (ev_sidebar->priv->menu, "deactivate",
300                           G_CALLBACK (ev_sidebar_menu_deactivate_cb),
301                           select_button);
302         gtk_menu_attach_to_widget (GTK_MENU (ev_sidebar->priv->menu),
303                                    GTK_WIDGET (ev_sidebar),
304                                    ev_sidebar_menu_detach_cb);
305         gtk_widget_show (ev_sidebar->priv->menu);
306
307         ev_sidebar->priv->notebook = gtk_notebook_new ();
308         gtk_notebook_set_show_border (GTK_NOTEBOOK (ev_sidebar->priv->notebook), FALSE);
309         gtk_notebook_set_show_tabs (GTK_NOTEBOOK (ev_sidebar->priv->notebook), FALSE);
310         gtk_box_pack_start (GTK_BOX (ev_sidebar), ev_sidebar->priv->notebook,
311                             TRUE, TRUE, 0);
312         gtk_widget_show (ev_sidebar->priv->notebook);
313 }
314
315 /* Public functions */
316
317 GtkWidget *
318 ev_sidebar_new (void)
319 {
320         GtkWidget *ev_sidebar;
321
322         ev_sidebar = g_object_new (EV_TYPE_SIDEBAR, NULL);
323
324         return ev_sidebar;
325 }
326
327 void
328 ev_sidebar_add_page (EvSidebar   *ev_sidebar,
329                      const gchar *page_id,
330                      const gchar *title,
331                      GtkWidget   *main_widget)
332 {
333         GtkTreeIter iter;
334         GtkWidget *menu_item;
335         gchar *label_title;
336         int index;
337            
338         g_return_if_fail (EV_IS_SIDEBAR (ev_sidebar));
339         g_return_if_fail (page_id != NULL);
340         g_return_if_fail (title != NULL);
341         g_return_if_fail (GTK_IS_WIDGET (main_widget));
342            
343         index = gtk_notebook_append_page (GTK_NOTEBOOK (ev_sidebar->priv->notebook),
344                                           main_widget, NULL);
345            
346         menu_item = gtk_image_menu_item_new_with_label (title);
347         g_signal_connect (menu_item, "activate",
348                           G_CALLBACK (ev_sidebar_menu_item_activate_cb),
349                           ev_sidebar);
350         gtk_widget_show (menu_item);
351         gtk_menu_shell_append (GTK_MENU_SHELL (ev_sidebar->priv->menu),
352                                menu_item);
353            
354         gtk_list_store_insert_with_values (GTK_LIST_STORE (ev_sidebar->priv->page_model),
355                                            &iter, 0,
356                                            PAGE_COLUMN_ID, page_id,
357                                            PAGE_COLUMN_TITLE, title,
358                                            PAGE_COLUMN_MENU_ITEM, menu_item,
359                                            PAGE_COLUMN_MAIN_WIDGET, main_widget,
360                                            PAGE_COLUMN_NOTEBOOK_INDEX, index,
361                                            -1);
362            
363         /* Set the first item added as active */
364         gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
365         gtk_tree_model_get (ev_sidebar->priv->page_model,
366                             &iter,
367                             PAGE_COLUMN_TITLE, &label_title,
368                             PAGE_COLUMN_NOTEBOOK_INDEX, &index,
369                             -1);
370
371         gtk_menu_set_active (GTK_MENU (ev_sidebar->priv->menu), index);
372         gtk_label_set_text (GTK_LABEL (ev_sidebar->priv->label), label_title);
373         gtk_notebook_set_current_page (GTK_NOTEBOOK (ev_sidebar->priv->notebook),
374                                        index);
375 }
376
377 void
378 ev_sidebar_set_document (EvSidebar   *sidebar,
379                          EvDocument  *document)
380 {
381         EvSidebarPrivate *priv;
382         GtkTreeIter iter;
383         gboolean result;
384            
385         g_return_if_fail (EV_IS_SIDEBAR (sidebar));
386         g_return_if_fail (EV_IS_DOCUMENT (document));
387            
388         priv = sidebar->priv;
389         
390         /* FIXME: We should prolly make sidebars have an interface.  For now, we        
391          * do this bad hack (TM)        
392          */
393         for (result = gtk_tree_model_get_iter_first (priv->page_model, &iter);
394              result;
395              result = gtk_tree_model_iter_next (priv->page_model, &iter)) {
396                 GtkWidget *widget;
397
398                 gtk_tree_model_get (priv->page_model, &iter,
399                                     PAGE_COLUMN_MAIN_WIDGET, &widget,
400                                     -1);
401                          
402                 if (EV_IS_SIDEBAR_LINKS (widget)
403                     && EV_IS_DOCUMENT_LINKS (document)
404                     && ev_document_links_has_document_links (EV_DOCUMENT_LINKS (document))) {
405                        ev_sidebar_links_set_document
406                                 (EV_SIDEBAR_LINKS (widget), document);
407                 } else if (EV_IS_SIDEBAR_THUMBNAILS (widget) &&
408                            EV_IS_DOCUMENT_THUMBNAILS (document)) {
409                         ev_sidebar_thumbnails_set_document
410                                 (EV_SIDEBAR_THUMBNAILS (widget), document);
411                 }
412         }
413 }