]> www.fi.muni.cz Git - evince.git/blob - shell/ev-navigation-action-widget.c
5a04fd3a9bd6312bb2ba38ec87a9bc0366778fc2
[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                                                                 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_VISIBLE (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         GtkTextDirection direction;
131         GdkRectangle monitor;
132         gint monitor_num;
133         GdkScreen *screen;
134
135         gtk_widget_size_request (GTK_WIDGET (button->menu), &menu_req);
136         direction = gtk_widget_get_direction (widget);
137         screen = gtk_widget_get_screen (GTK_WIDGET (menu));
138
139         monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
140         if (monitor_num < 0)
141                 monitor_num = 0;
142         gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
143
144         gdk_window_get_origin (widget->window, x, y);
145         *x += widget->allocation.x;
146         *y += widget->allocation.y;
147
148         if (direction == GTK_TEXT_DIR_LTR)
149                 *x += MAX (widget->allocation.width - menu_req.width, 0);
150         else if (menu_req.width > widget->allocation.width)
151                 *x -= menu_req.width - widget->allocation.width;
152
153         if ((*y + widget->allocation.height + menu_req.height) <= monitor.y + monitor.height)
154                 *y += widget->allocation.height;
155         else if ((*y - menu_req.height) >= monitor.y)
156                 *y -= menu_req.height;
157         else if (monitor.y + monitor.height - (*y + widget->allocation.height) > *y)
158                 *y += widget->allocation.height;
159         else
160                 *y -= menu_req.height; 
161
162         *push_in = FALSE;
163 }
164
165 static void
166 popup_menu_under_arrow (EvNavigationActionWidget *button,
167                         GdkEventButton    *event)
168 {
169         g_signal_emit (button, signals[SHOW_MENU], 0);
170
171         if (!button->menu)
172                 return;
173
174         gtk_menu_popup (button->menu, NULL, NULL, 
175                         (GtkMenuPositionFunc) menu_position_func,
176                          button,
177                          event ? event->button : 0,
178                          event ? event->time : gtk_get_current_event_time ());
179 }
180
181 static void
182 ev_navigation_action_widget_toggled (GtkToggleToolButton *toggle)
183 {
184         EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (toggle);
185         if (!button->menu)
186                 return;
187
188         if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button)) &&
189             !GTK_WIDGET_VISIBLE (button->menu)) {
190                       /* we get here only when the menu is activated by a key
191                        * press, so that we can select the first menu item */
192                       popup_menu_under_arrow (button, NULL);
193                       gtk_menu_shell_select_first (GTK_MENU_SHELL (button->menu), FALSE);
194         }
195 }
196
197 static gboolean
198 ev_navigation_action_widget_button_press_event (GtkWidget *widget,
199                                                 GdkEventButton    *event,
200                                                 gpointer data)
201 {
202         EvNavigationActionWidget *button = EV_NAVIGATION_ACTION_WIDGET (data);
203
204         if (event->button == 1) {
205                  popup_menu_under_arrow (button, event);
206                  gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button), TRUE);
207                  return TRUE;
208         }
209         return FALSE;
210 }