]> www.fi.muni.cz Git - evince.git/blob - shell/ev-navigation-action-widget.c
Preliminary history implementation
[evince.git] / shell / ev-navigation-action-widget.c
1 /*
2  *  Copyright (C) 2003, 2004 Marco Pesenti Gritti
3  *  Copyright (C) 2003, 2004 Christian Persch
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
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
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include "ev-navigation-action-widget.h"
22
23 #include <glib/gi18n.h>
24 #include <string.h>
25
26 static void  ev_navigation_action_widget_init       (EvNavigationActionWidget      *action_widget);
27 static void  ev_navigation_action_widget_class_init (EvNavigationActionWidgetClass *action_widget);
28 static void ev_navigation_action_widget_toggled (GtkToggleToolButton *toggle);
29 static gboolean ev_navigation_action_widget_button_press_event (GtkWidget *widget,
30                                                              GdkEventButton    *event);
31
32 G_DEFINE_TYPE (EvNavigationActionWidget, ev_navigation_action_widget, GTK_TYPE_TOGGLE_TOOL_BUTTON)
33
34 enum
35 {
36         SHOW_MENU,
37         LAST_SIGNAL
38 };
39
40 static gint signals[LAST_SIGNAL];
41
42 static void
43 ev_navigation_action_widget_init (EvNavigationActionWidget *action_widget)
44 {
45         return;
46 }
47
48 static void
49 ev_navigation_action_widget_class_init (EvNavigationActionWidgetClass *klass)
50 {
51         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
52         GtkToggleToolButtonClass *toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (klass);
53
54         widget_class->button_press_event = ev_navigation_action_widget_button_press_event;
55         toggle_tool_button_class->toggled = ev_navigation_action_widget_toggled;
56
57
58         signals[SHOW_MENU] =
59                   g_signal_new ("show-menu",
60                                 G_OBJECT_CLASS_TYPE (klass),
61                                 G_SIGNAL_RUN_FIRST,
62                                 G_STRUCT_OFFSET (EvNavigationActionWidgetClass, show_menu),
63                                 NULL, NULL,
64                                 g_cclosure_marshal_VOID__VOID,
65                                 G_TYPE_NONE, 0);
66 }
67
68 static int
69 menu_deactivate_cb (GtkMenuShell      *menu_shell,
70                     EvNavigationActionWidget *widget)
71 {
72          gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (widget), FALSE);
73          return TRUE;
74 }
75                   
76 static void
77 menu_detacher (GtkWidget *widget,
78                GtkMenu   *menu)
79 {
80          EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (widget);
81          g_return_if_fail (button->menu == menu);
82          button->menu = NULL;
83 }
84
85 void
86 ev_navigation_action_widget_set_menu(EvNavigationActionWidget *button, GtkWidget *menu)
87 {
88
89       if (button->menu == GTK_MENU (menu))
90                 return;
91         
92       if (button->menu && GTK_WIDGET_VISIBLE (button->menu))
93                 gtk_menu_shell_deactivate (GTK_MENU_SHELL (button->menu));
94
95       if (button->menu) {
96                 g_signal_handlers_disconnect_by_func (button->menu, 
97                                                 menu_deactivate_cb, 
98                                                 button);
99                 gtk_menu_detach (button->menu);
100        }
101
102        button->menu = GTK_MENU (menu);
103
104        if (button->menu) {
105                 gtk_menu_attach_to_widget (button->menu, GTK_WIDGET (button),
106                                            menu_detacher);
107                 g_signal_connect (button->menu, "deactivate",
108                                   G_CALLBACK (menu_deactivate_cb), button);
109         }
110 }
111
112 static void
113 menu_position_func (GtkMenu           *menu,
114                     int               *x,
115                     int               *y,
116                     gboolean          *push_in,
117                     EvNavigationActionWidget *button)
118 {
119         GtkWidget *widget = GTK_WIDGET (button);
120         GtkRequisition menu_req;
121         GtkTextDirection direction;
122         GdkRectangle monitor;
123         gint monitor_num;
124         GdkScreen *screen;
125
126         gtk_widget_size_request (GTK_WIDGET (button->menu), &menu_req);
127         direction = gtk_widget_get_direction (widget);
128         screen = gtk_widget_get_screen (GTK_WIDGET (menu));
129
130         monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
131         if (monitor_num < 0)
132                 monitor_num = 0;
133         gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
134
135         gdk_window_get_origin (widget->window, x, y);
136         *x += widget->allocation.x;
137         *y += widget->allocation.y;
138
139         if (direction == GTK_TEXT_DIR_LTR)
140                 *x += MAX (widget->allocation.width - menu_req.width, 0);
141         else if (menu_req.width > widget->allocation.width)
142                 *x -= menu_req.width - widget->allocation.width;
143
144         if ((*y + widget->allocation.height + menu_req.height) <= monitor.y + monitor.height)
145                 *y += widget->allocation.height;
146         else if ((*y - menu_req.height) >= monitor.y)
147                 *y -= menu_req.height;
148         else if (monitor.y + monitor.height - (*y + widget->allocation.height) > *y)
149                 *y += widget->allocation.height;
150         else
151                 *y -= menu_req.height; 
152
153         *push_in = FALSE;
154 }
155
156 static void
157 popup_menu_under_arrow (EvNavigationActionWidget *button,
158                         GdkEventButton    *event)
159 {
160         if (!button->menu)
161                 return;
162
163         g_signal_emit (button, signals[SHOW_MENU], 0);
164
165         gtk_menu_popup (button->menu, NULL, NULL, 
166                         (GtkMenuPositionFunc) menu_position_func,
167                          button,
168                          event ? event->button : 0,
169                          event ? event->time : gtk_get_current_event_time ());
170 }
171
172 static void
173 ev_navigation_action_widget_toggled (GtkToggleToolButton *toggle)
174 {
175         EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (toggle);
176         if (!button->menu)
177                 return;
178
179         if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button)) &&
180             !GTK_WIDGET_VISIBLE (button->menu)) {
181                       /* we get here only when the menu is activated by a key
182                        * press, so that we can select the first menu item */
183                       popup_menu_under_arrow (button, NULL);
184                       gtk_menu_shell_select_first (GTK_MENU_SHELL (button->menu), FALSE);
185         }
186 }
187
188 static gboolean
189 ev_navigation_action_widget_button_press_event (GtkWidget *widget,
190                                                 GdkEventButton    *event)
191 {
192         EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (widget);
193         if (event->button == 1) {
194                  popup_menu_under_arrow (button, event);
195                  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button), TRUE);
196                  return TRUE;
197         }
198         return FALSE;
199 }