]> www.fi.muni.cz Git - evince.git/blob - shell/ev-navigation-action-widget.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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                                                                 gpointer data);
32
33 G_DEFINE_TYPE (EvNavigationActionWidget, ev_navigation_action_widget, GTK_TYPE_TOGGLE_TOOL_BUTTON)
34
35 enum
36 {
37         SHOW_MENU,
38         LAST_SIGNAL
39 };
40
41 static gint signals[LAST_SIGNAL];
42
43 static void
44 ev_navigation_action_widget_init (EvNavigationActionWidget *action_widget)
45 {
46         GtkWidget *toggle_button;
47         
48         /* It's rather dirty hack but we need a child to connect to
49          * button press event
50          */
51                 
52         toggle_button = gtk_bin_get_child (GTK_BIN (action_widget));
53         
54         g_signal_connect (toggle_button, "button-press-event", 
55                           G_CALLBACK (ev_navigation_action_widget_button_press_event),
56                           action_widget);
57         return;
58 }
59
60 static void
61 ev_navigation_action_widget_class_init (EvNavigationActionWidgetClass *klass)
62 {
63         GtkToggleToolButtonClass *toggle_tool_button_class = GTK_TOGGLE_TOOL_BUTTON_CLASS (klass);
64
65         toggle_tool_button_class->toggled = ev_navigation_action_widget_toggled;
66
67         signals[SHOW_MENU] =
68                   g_signal_new ("show-menu",
69                                 G_OBJECT_CLASS_TYPE (klass),
70                                 G_SIGNAL_RUN_FIRST,
71                                 G_STRUCT_OFFSET (EvNavigationActionWidgetClass, show_menu),
72                                 NULL, NULL,
73                                 g_cclosure_marshal_VOID__VOID,
74                                 G_TYPE_NONE, 0);
75 }
76
77 static int
78 menu_deactivate_cb (GtkMenuShell      *menu_shell,
79                     EvNavigationActionWidget *widget)
80 {
81          gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (widget), FALSE);
82          return TRUE;
83 }
84                   
85 static void
86 menu_detacher (GtkWidget *widget,
87                GtkMenu   *menu)
88 {
89          EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (widget);
90          g_return_if_fail (button->menu == menu);
91          button->menu = NULL;
92 }
93
94 void
95 ev_navigation_action_widget_set_menu(EvNavigationActionWidget *button, GtkWidget *menu)
96 {
97
98       if (button->menu == GTK_MENU (menu))
99                 return;
100         
101       if (button->menu && gtk_widget_get_visible (GTK_WIDGET (button->menu)))
102                 gtk_menu_shell_deactivate (GTK_MENU_SHELL (button->menu));
103
104       if (button->menu) {
105                 g_signal_handlers_disconnect_by_func (button->menu, 
106                                                 menu_deactivate_cb, 
107                                                 button);
108                 gtk_menu_detach (button->menu);
109        }
110
111        button->menu = GTK_MENU (menu);
112
113        if (button->menu) {
114                 gtk_menu_attach_to_widget (button->menu, GTK_WIDGET (button),
115                                            menu_detacher);
116                 g_signal_connect (button->menu, "deactivate",
117                                   G_CALLBACK (menu_deactivate_cb), button);
118         }
119 }
120
121 static void
122 menu_position_func (GtkMenu           *menu,
123                     int               *x,
124                     int               *y,
125                     gboolean          *push_in,
126                     EvNavigationActionWidget *button)
127 {
128         GtkWidget *widget = GTK_WIDGET (button);
129         GtkRequisition menu_req;
130         GtkAllocation  allocation;
131         GtkTextDirection direction;
132         GdkWindow *gdk_window;
133         GdkRectangle monitor;
134         gint monitor_num;
135         GdkScreen *screen;
136
137         gtk_widget_get_preferred_size (GTK_WIDGET (button->menu), &menu_req, NULL);
138         direction = gtk_widget_get_direction (widget);
139         screen = gtk_widget_get_screen (GTK_WIDGET (menu));
140
141         gdk_window = gtk_widget_get_window (widget);
142         monitor_num = gdk_screen_get_monitor_at_window (screen, gdk_window);
143         if (monitor_num < 0)
144                 monitor_num = 0;
145         gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
146
147         gdk_window_get_origin (gdk_window, x, y);
148         gtk_widget_get_allocation (widget, &allocation);
149         *x += allocation.x;
150         *y += allocation.y;
151
152         if (direction == GTK_TEXT_DIR_LTR)
153                 *x += MAX (allocation.width - menu_req.width, 0);
154         else if (menu_req.width > allocation.width)
155                 *x -= menu_req.width - allocation.width;
156
157         if ((*y + allocation.height + menu_req.height) <= monitor.y + monitor.height)
158                 *y += allocation.height;
159         else if ((*y - menu_req.height) >= monitor.y)
160                 *y -= menu_req.height;
161         else if (monitor.y + monitor.height - (*y + allocation.height) > *y)
162                 *y += allocation.height;
163         else
164                 *y -= menu_req.height; 
165
166         *push_in = FALSE;
167 }
168
169 static void
170 popup_menu_under_arrow (EvNavigationActionWidget *button,
171                         GdkEventButton    *event)
172 {
173         g_signal_emit (button, signals[SHOW_MENU], 0);
174
175         if (!button->menu)
176                 return;
177
178         gtk_menu_popup (button->menu, NULL, NULL, 
179                         (GtkMenuPositionFunc) menu_position_func,
180                          button,
181                          event ? event->button : 0,
182                          event ? event->time : gtk_get_current_event_time ());
183 }
184
185 static void
186 ev_navigation_action_widget_toggled (GtkToggleToolButton *toggle)
187 {
188         EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (toggle);
189         if (!button->menu)
190                 return;
191
192         if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button)) &&
193             !gtk_widget_get_visible (GTK_WIDGET (button->menu))) {
194                       /* we get here only when the menu is activated by a key
195                        * press, so that we can select the first menu item */
196                       popup_menu_under_arrow (button, NULL);
197                       gtk_menu_shell_select_first (GTK_MENU_SHELL (button->menu), FALSE);
198         }
199 }
200
201 static gboolean
202 ev_navigation_action_widget_button_press_event (GtkWidget *widget,
203                                                 GdkEventButton    *event,
204                                                 gpointer data)
205 {
206         EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (data);
207
208         if (event->button == 1) {
209                  popup_menu_under_arrow (button, event);
210                  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button), TRUE);
211                  return TRUE;
212         }
213         return FALSE;
214 }