]> www.fi.muni.cz Git - evince.git/blob - shell/ev-sidebar.c
Small rework of sidebar pages handling. See bug #164811 for details
[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-sidebar-page.h"
33 #include "ev-document-thumbnails.h"
34 #include "ev-document-links.h"
35
36 enum
37 {
38         PAGE_COLUMN_TITLE,
39         PAGE_COLUMN_MENU_ITEM,
40         PAGE_COLUMN_MAIN_WIDGET,
41         PAGE_COLUMN_NOTEBOOK_INDEX,
42         PAGE_COLUMN_NUM_COLS
43 };
44
45 struct _EvSidebarPrivate {
46         GtkWidget *notebook;
47         GtkWidget *menu;
48         GtkWidget *frame;
49         GtkWidget *hbox;
50         GtkWidget *label;
51            
52         GtkTreeModel *page_model;
53 };
54
55 G_DEFINE_TYPE (EvSidebar, ev_sidebar, GTK_TYPE_VBOX)
56
57 #define EV_SIDEBAR_GET_PRIVATE(object) \
58                 (G_TYPE_INSTANCE_GET_PRIVATE ((object), EV_TYPE_SIDEBAR, EvSidebarPrivate))
59
60 static void
61 ev_sidebar_destroy (GtkObject *object)
62 {
63         EvSidebar *ev_sidebar = EV_SIDEBAR (object);
64
65         if (ev_sidebar->priv->menu) {
66                 gtk_menu_detach (GTK_MENU (ev_sidebar->priv->menu));
67                 ev_sidebar->priv->menu = NULL;
68         }
69            
70         (* GTK_OBJECT_CLASS (ev_sidebar_parent_class)->destroy) (object);
71 }
72
73 static void
74 ev_sidebar_class_init (EvSidebarClass *ev_sidebar_class)
75 {
76         GObjectClass *g_object_class;
77         GtkWidgetClass *widget_class;
78         GtkObjectClass *gtk_object_klass;
79  
80         g_object_class = G_OBJECT_CLASS (ev_sidebar_class);
81         widget_class = GTK_WIDGET_CLASS (ev_sidebar_class);
82         gtk_object_klass = GTK_OBJECT_CLASS (ev_sidebar_class);
83            
84         g_type_class_add_private (g_object_class, sizeof (EvSidebarPrivate));
85            
86         gtk_object_klass->destroy = ev_sidebar_destroy;
87 }
88
89 static void
90 ev_sidebar_menu_position_under (GtkMenu  *menu,
91                                 int      *x,
92                                 int      *y,
93                                 gboolean *push_in,
94                                 gpointer  user_data)
95 {
96         GtkWidget *widget;
97
98         g_return_if_fail (GTK_IS_BUTTON (user_data));
99         g_return_if_fail (GTK_WIDGET_NO_WINDOW (user_data));
100
101         widget = GTK_WIDGET (user_data);
102            
103         gdk_window_get_origin (widget->window, x, y);
104            
105         *x += widget->allocation.x;
106         *y += widget->allocation.y + widget->allocation.height;
107            
108         *push_in = FALSE;
109 }
110
111 static gboolean
112 ev_sidebar_select_button_press_cb (GtkWidget      *widget,
113                                    GdkEventButton *event,
114                                    gpointer        user_data)
115 {
116         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
117
118         if (event->button == 1) {
119                 gtk_widget_grab_focus (widget);
120                          
121                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
122                 gtk_menu_popup (GTK_MENU (ev_sidebar->priv->menu),
123                                 NULL, NULL, ev_sidebar_menu_position_under, widget,
124                                 event->button, event->time);
125
126                 return TRUE;
127         }
128
129         return FALSE;
130 }
131
132 static gboolean
133 ev_sidebar_select_button_key_press_cb (GtkWidget   *widget,
134                                        GdkEventKey *event,
135                                        gpointer     user_data)
136 {
137         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
138            
139         if (event->keyval == GDK_space ||
140             event->keyval == GDK_KP_Space ||
141             event->keyval == GDK_Return ||
142             event->keyval == GDK_KP_Enter) {
143                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
144                 gtk_menu_popup (GTK_MENU (ev_sidebar->priv->menu),
145                                 NULL, NULL, ev_sidebar_menu_position_under, widget,
146                                 1, event->time);
147                 return TRUE;
148         }
149
150         return FALSE;
151 }
152
153 static void
154 ev_sidebar_close_clicked_cb (GtkWidget *widget,
155                              gpointer   user_data)
156 {
157         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
158
159         gtk_widget_hide (GTK_WIDGET (ev_sidebar));
160 }
161
162 static void
163 ev_sidebar_menu_deactivate_cb (GtkWidget *widget,
164                                gpointer   user_data)
165 {
166         GtkWidget *menu_button;
167
168         menu_button = GTK_WIDGET (user_data);
169            
170         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (menu_button), FALSE);
171 }
172
173 static void
174 ev_sidebar_menu_detach_cb (GtkWidget *widget,
175                            GtkMenu   *menu)
176 {
177         EvSidebar *ev_sidebar = EV_SIDEBAR (widget);
178            
179         ev_sidebar->priv->menu = NULL;
180 }
181
182 static void
183 ev_sidebar_menu_item_activate_cb (GtkWidget *widget,
184                                   gpointer   user_data)
185 {
186         EvSidebar *ev_sidebar = EV_SIDEBAR (user_data);
187         GtkTreeIter iter;
188         GtkWidget *menu_item, *item;
189         gchar *title;
190         gboolean valid;
191         gint index;
192
193         menu_item = gtk_menu_get_active (GTK_MENU (ev_sidebar->priv->menu));
194         valid = gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
195            
196         while (valid) {
197                 gtk_tree_model_get (ev_sidebar->priv->page_model,
198                                     &iter,
199                                     PAGE_COLUMN_TITLE, &title, 
200                                     PAGE_COLUMN_MENU_ITEM, &item,
201                                     PAGE_COLUMN_NOTEBOOK_INDEX, &index,
202                                     -1);
203                          
204                 if (item == menu_item) {
205                         gtk_notebook_set_current_page
206                                 (GTK_NOTEBOOK (ev_sidebar->priv->notebook), index);
207                         gtk_label_set_text (GTK_LABEL (ev_sidebar->priv->label), title);
208                         valid = FALSE;
209                 } else {
210                         valid = gtk_tree_model_iter_next (ev_sidebar->priv->page_model, &iter);
211                 }
212         }
213 }
214
215 static void
216 ev_sidebar_init (EvSidebar *ev_sidebar)
217 {
218         GtkWidget *frame;
219         GtkWidget *hbox;
220         GtkWidget *close_button;
221         GtkWidget *select_button;
222         GtkWidget *select_hbox;
223         GtkWidget *arrow;
224         GtkWidget *image;
225
226         ev_sidebar->priv = EV_SIDEBAR_GET_PRIVATE (ev_sidebar);
227
228         /* data model */
229         ev_sidebar->priv->page_model = (GtkTreeModel *)
230                         gtk_list_store_new (PAGE_COLUMN_NUM_COLS,
231                                             G_TYPE_STRING,
232                                             GTK_TYPE_WIDGET,
233                                             GTK_TYPE_WIDGET,
234                                             G_TYPE_INT);
235
236         /* top option menu */
237         frame = gtk_frame_new (NULL);
238         ev_sidebar->priv->frame = frame;
239         gtk_box_pack_start (GTK_BOX (ev_sidebar), frame, FALSE, FALSE, 0);
240         gtk_widget_show (frame);
241
242         hbox = gtk_hbox_new (FALSE, 0);
243         ev_sidebar->priv->hbox = hbox;
244         gtk_container_add (GTK_CONTAINER (frame), hbox);
245         gtk_widget_show (hbox);
246
247         select_button = gtk_toggle_button_new ();
248         gtk_button_set_relief (GTK_BUTTON (select_button), GTK_RELIEF_NONE);
249         g_signal_connect (select_button, "button_press_event",
250                           G_CALLBACK (ev_sidebar_select_button_press_cb),
251                           ev_sidebar);
252         g_signal_connect (select_button, "key_press_event",
253                           G_CALLBACK (ev_sidebar_select_button_key_press_cb),
254                           ev_sidebar);
255
256         select_hbox = gtk_hbox_new (FALSE, 0);
257
258         ev_sidebar->priv->label = gtk_label_new ("");
259         gtk_box_pack_start (GTK_BOX (select_hbox),
260                             ev_sidebar->priv->label,
261                             FALSE, FALSE, 0);
262         gtk_widget_show (ev_sidebar->priv->label);
263
264         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
265         gtk_box_pack_end (GTK_BOX (select_hbox), arrow, FALSE, FALSE, 0);
266         gtk_widget_show (arrow);
267
268         gtk_container_add (GTK_CONTAINER (select_button), select_hbox);
269         gtk_widget_show (select_hbox);
270
271         gtk_box_pack_start (GTK_BOX (hbox), select_button, TRUE, TRUE, 0);
272         gtk_widget_show (select_button);
273
274         close_button = gtk_button_new ();
275         gtk_button_set_relief (GTK_BUTTON (close_button), GTK_RELIEF_NONE);
276         g_signal_connect (close_button, "clicked",
277                           G_CALLBACK (ev_sidebar_close_clicked_cb),
278                           ev_sidebar);
279            
280         image = gtk_image_new_from_stock (GTK_STOCK_CLOSE,
281                                           GTK_ICON_SIZE_SMALL_TOOLBAR);
282         gtk_container_add (GTK_CONTAINER (close_button), image);
283         gtk_widget_show (image);
284    
285         gtk_box_pack_end (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
286         gtk_widget_show (close_button);
287    
288         ev_sidebar->priv->menu = gtk_menu_new ();
289         g_signal_connect (ev_sidebar->priv->menu, "deactivate",
290                           G_CALLBACK (ev_sidebar_menu_deactivate_cb),
291                           select_button);
292         gtk_menu_attach_to_widget (GTK_MENU (ev_sidebar->priv->menu),
293                                    GTK_WIDGET (ev_sidebar),
294                                    ev_sidebar_menu_detach_cb);
295         gtk_widget_show (ev_sidebar->priv->menu);
296
297         ev_sidebar->priv->notebook = gtk_notebook_new ();
298         gtk_notebook_set_show_border (GTK_NOTEBOOK (ev_sidebar->priv->notebook), FALSE);
299         gtk_notebook_set_show_tabs (GTK_NOTEBOOK (ev_sidebar->priv->notebook), FALSE);
300         gtk_box_pack_start (GTK_BOX (ev_sidebar), ev_sidebar->priv->notebook,
301                             TRUE, TRUE, 0);
302         gtk_widget_show (ev_sidebar->priv->notebook);
303
304         gtk_widget_set_sensitive (GTK_WIDGET (ev_sidebar), FALSE);
305 }
306
307 /* Public functions */
308
309 GtkWidget *
310 ev_sidebar_new (void)
311 {
312         GtkWidget *ev_sidebar;
313
314         ev_sidebar = g_object_new (EV_TYPE_SIDEBAR, NULL);
315
316         return ev_sidebar;
317 }
318
319 void
320 ev_sidebar_add_page (EvSidebar   *ev_sidebar,
321                      GtkWidget   *main_widget)
322 {
323         GtkTreeIter iter;
324         GtkWidget *menu_item;
325         gchar *label_title;
326         const gchar *title;
327         int index;
328            
329         g_return_if_fail (EV_IS_SIDEBAR (ev_sidebar));
330         g_return_if_fail (EV_IS_SIDEBAR_PAGE (main_widget));
331         g_return_if_fail (GTK_IS_WIDGET (main_widget));
332         
333         title = ev_sidebar_page_get_label (EV_SIDEBAR_PAGE (main_widget));
334            
335         index = gtk_notebook_append_page (GTK_NOTEBOOK (ev_sidebar->priv->notebook),
336                                           main_widget, NULL);
337            
338         menu_item = gtk_image_menu_item_new_with_label (title);
339         g_signal_connect (menu_item, "activate",
340                           G_CALLBACK (ev_sidebar_menu_item_activate_cb),
341                           ev_sidebar);
342         gtk_widget_show (menu_item);
343         gtk_menu_shell_append (GTK_MENU_SHELL (ev_sidebar->priv->menu),
344                                menu_item);
345            
346         gtk_list_store_insert_with_values (GTK_LIST_STORE (ev_sidebar->priv->page_model),
347                                            &iter, 0,
348                                            PAGE_COLUMN_TITLE, title,
349                                            PAGE_COLUMN_MENU_ITEM, menu_item,
350                                            PAGE_COLUMN_MAIN_WIDGET, main_widget,
351                                            PAGE_COLUMN_NOTEBOOK_INDEX, index,
352                                            -1);
353            
354         /* Set the first item added as active */
355         gtk_tree_model_get_iter_first (ev_sidebar->priv->page_model, &iter);
356         gtk_tree_model_get (ev_sidebar->priv->page_model,
357                             &iter,
358                             PAGE_COLUMN_TITLE, &label_title,
359                             PAGE_COLUMN_NOTEBOOK_INDEX, &index,
360                             -1);
361
362         gtk_menu_set_active (GTK_MENU (ev_sidebar->priv->menu), index);
363         gtk_label_set_text (GTK_LABEL (ev_sidebar->priv->label), label_title);
364         gtk_notebook_set_current_page (GTK_NOTEBOOK (ev_sidebar->priv->notebook),
365                                        index);
366 }
367
368 void
369 ev_sidebar_set_document (EvSidebar   *sidebar,
370                          EvDocument  *document)
371 {
372         EvSidebarPrivate *priv;
373         GtkTreeIter iter;
374         gboolean valid;
375         gboolean has_pages;
376            
377         g_return_if_fail (EV_IS_SIDEBAR (sidebar));
378         g_return_if_fail (EV_IS_DOCUMENT (document));
379            
380         priv = sidebar->priv;
381         
382         has_pages = FALSE;
383         
384         for (valid = gtk_tree_model_get_iter_first (priv->page_model, &iter);
385              valid;
386              valid = gtk_tree_model_iter_next (priv->page_model, &iter)) {
387                 GtkWidget *widget;
388                 GtkWidget *menu_widget;
389
390                 gtk_tree_model_get (priv->page_model, &iter,
391                                     PAGE_COLUMN_MAIN_WIDGET, &widget,
392                                     PAGE_COLUMN_MENU_ITEM, &menu_widget,
393                                     -1);
394                          
395
396                 if (ev_sidebar_page_support_document (EV_SIDEBAR_PAGE (widget), document)) {
397                                 ev_sidebar_page_set_document (EV_SIDEBAR_PAGE (widget), document);
398                                 has_pages = TRUE;
399                 } else {
400                                 gtk_widget_set_sensitive (menu_widget, FALSE);
401                 }
402         }
403         
404         if (!has_pages) {
405                 gtk_widget_hide (GTK_WIDGET (sidebar));
406         } else {
407                 gtk_widget_set_sensitive (GTK_WIDGET (sidebar), TRUE);
408         }
409 }
410