1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 
3 /*
4  *  Caja
5  *
6  *  Copyright (C) 2004 Red Hat, Inc.
7  *  Copyright (C) 2003 Marco Pesenti Gritti
8  *
9  *  Caja is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public License as
11  *  published by the Free Software Foundation; either version 2 of the
12  *  License, or (at your option) any later version.
13  *
14  *  Caja is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  *  Based on ephy-navigation-action.h from Epiphany
24  *
25  *  Authors: Alexander Larsson <alexl@redhat.com>
26  *           Marco Pesenti Gritti
27  *
28  */
29 
30 #include <config.h>
31 
32 #include <gtk/gtk.h>
33 
34 #include <eel/eel-gtk-extensions.h>
35 
36 #include "caja-navigation-action.h"
37 #include "caja-navigation-window.h"
38 #include "caja-window-private.h"
39 #include "caja-navigation-window-slot.h"
40 
41 struct _CajaNavigationActionPrivate
42 {
43     CajaNavigationWindow *window;
44     CajaNavigationDirection direction;
45     char *arrow_tooltip;
46 };
47 
48 enum
49 {
50     PROP_0,
51     PROP_ARROW_TOOLTIP,
52     PROP_DIRECTION,
53     PROP_WINDOW
54 };
55 
56 G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
57 G_DEFINE_TYPE_WITH_PRIVATE (CajaNavigationAction, caja_navigation_action, GTK_TYPE_ACTION)
58 G_GNUC_END_IGNORE_DEPRECATIONS;
59 
60 static gboolean
should_open_in_new_tab(void)61 should_open_in_new_tab (void)
62 {
63     /* FIXME this is duplicated */
64     GdkEvent *event;
65 
66     event = gtk_get_current_event ();
67     if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE)
68     {
69         return event->button.button == 2;
70     }
71 
72     gdk_event_free (event);
73 
74     return FALSE;
75 }
76 
77 static void
activate_back_or_forward_menu_item(GtkMenuItem * menu_item,CajaNavigationWindow * window,gboolean back)78 activate_back_or_forward_menu_item (GtkMenuItem *menu_item,
79                                     CajaNavigationWindow *window,
80                                     gboolean back)
81 {
82     int index;
83 
84     g_assert (GTK_IS_MENU_ITEM (menu_item));
85     g_assert (CAJA_IS_NAVIGATION_WINDOW (window));
86 
87     index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "user_data"));
88 
89     caja_navigation_window_back_or_forward (window, back, index, should_open_in_new_tab ());
90 }
91 
92 static void
activate_back_menu_item_callback(GtkMenuItem * menu_item,CajaNavigationWindow * window)93 activate_back_menu_item_callback (GtkMenuItem *menu_item, CajaNavigationWindow *window)
94 {
95     activate_back_or_forward_menu_item (menu_item, window, TRUE);
96 }
97 
98 static void
activate_forward_menu_item_callback(GtkMenuItem * menu_item,CajaNavigationWindow * window)99 activate_forward_menu_item_callback (GtkMenuItem *menu_item, CajaNavigationWindow *window)
100 {
101     activate_back_or_forward_menu_item (menu_item, window, FALSE);
102 }
103 
104 static void
fill_menu(CajaNavigationWindow * window,GtkWidget * menu,gboolean back)105 fill_menu (CajaNavigationWindow *window,
106            GtkWidget *menu,
107            gboolean back)
108 {
109     CajaNavigationWindowSlot *slot;
110     int index;
111     GList *list;
112     gboolean list_void;
113     GtkWidget *menu_item = NULL;
114 
115     g_assert (CAJA_IS_NAVIGATION_WINDOW (window));
116 
117     slot = CAJA_NAVIGATION_WINDOW_SLOT (CAJA_WINDOW (window)->details->active_pane->active_slot);
118 
119     list = back ? slot->back_list : slot->forward_list;
120     index = 0;
121     list_void = TRUE;
122 
123     while (list != NULL)
124     {
125         menu_item = caja_bookmark_menu_item_new (CAJA_BOOKMARK (list->data));
126 
127         if (menu_item) {
128             list_void = FALSE;
129             g_object_set_data (G_OBJECT (menu_item), "user_data", GINT_TO_POINTER (index));
130             gtk_widget_show (GTK_WIDGET (menu_item));
131             g_signal_connect_object (menu_item, "activate",
132                                      back
133                                      ? G_CALLBACK (activate_back_menu_item_callback)
134                                      : G_CALLBACK (activate_forward_menu_item_callback),
135                                      window, 0);
136 
137             gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
138         }
139 
140         list = g_list_next (list);
141         ++index;
142     }
143 
144     if (list_void)
145     {
146         gtk_menu_shell_append (GTK_MENU_SHELL (menu),
147                                eel_image_menu_item_new_from_icon ("dialog-error", _("folder removed")));
148         if (back)
149         {
150             caja_navigation_window_slot_clear_back_list (slot);
151             caja_navigation_window_allow_back (window, FALSE);
152         }
153         else
154         {
155             caja_navigation_window_slot_clear_forward_list (slot);
156             caja_navigation_window_allow_forward (window, FALSE);
157         }
158     }
159 }
160 
161 static void
show_menu_callback(GtkMenuToolButton * button,CajaNavigationAction * action)162 show_menu_callback (GtkMenuToolButton *button,
163                     CajaNavigationAction *action)
164 {
165     CajaNavigationActionPrivate *p;
166     CajaNavigationWindow *window;
167     GtkWidget *menu;
168     GList *children;
169     GList *li;
170 
171     p = action->priv;
172     window = action->priv->window;
173 
174     menu = gtk_menu_tool_button_get_menu (button);
175 
176     children = gtk_container_get_children (GTK_CONTAINER (menu));
177     for (li = children; li; li = li->next)
178     {
179         gtk_container_remove (GTK_CONTAINER (menu), li->data);
180     }
181     g_list_free (children);
182 
183     switch (p->direction)
184     {
185     case CAJA_NAVIGATION_DIRECTION_FORWARD:
186         fill_menu (window, menu, FALSE);
187         break;
188     case CAJA_NAVIGATION_DIRECTION_BACK:
189         fill_menu (window, menu, TRUE);
190         break;
191     default:
192         g_assert_not_reached ();
193         break;
194     }
195 }
196 
197 static gboolean
proxy_button_press_event_cb(GtkButton * button,GdkEventButton * event,gpointer user_data)198 proxy_button_press_event_cb (GtkButton *button,
199                              GdkEventButton *event,
200                              gpointer user_data)
201 {
202     if (event->button == 2)
203     {
204         g_signal_emit_by_name (button, "pressed", 0);
205     }
206 
207     return FALSE;
208 }
209 
210 static gboolean
proxy_button_release_event_cb(GtkButton * button,GdkEventButton * event,gpointer user_data)211 proxy_button_release_event_cb (GtkButton *button,
212                                GdkEventButton *event,
213                                gpointer user_data)
214 {
215     if (event->button == 2)
216     {
217         g_signal_emit_by_name (button, "released", 0);
218     }
219 
220     return FALSE;
221 }
222 
223 static void
connect_proxy(GtkAction * action,GtkWidget * proxy)224 connect_proxy (GtkAction *action, GtkWidget *proxy)
225 {
226     if (GTK_IS_MENU_TOOL_BUTTON (proxy))
227     {
228         CajaNavigationAction *naction = CAJA_NAVIGATION_ACTION (action);
229         GtkMenuToolButton *button = GTK_MENU_TOOL_BUTTON (proxy);
230         GtkWidget *menu;
231         GtkWidget *child;
232 
233         /* set an empty menu, so the arrow button becomes sensitive */
234         menu = gtk_menu_new ();
235 
236         gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
237 
238         gtk_menu_tool_button_set_menu (button, menu);
239 
240         gtk_menu_tool_button_set_arrow_tooltip_text (button,
241                 naction->priv->arrow_tooltip);
242 
243         g_signal_connect (proxy, "show-menu",
244                           G_CALLBACK (show_menu_callback), action);
245 
246         /* Make sure that middle click works. Note that there is some code duplication
247          * between here and caja-window-menus.c */
248         child = eel_gtk_menu_tool_button_get_button (button);
249         g_signal_connect (child, "button-press-event", G_CALLBACK (proxy_button_press_event_cb), NULL);
250         g_signal_connect (child, "button-release-event", G_CALLBACK (proxy_button_release_event_cb), NULL);
251     }
252 
253     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
254     (* GTK_ACTION_CLASS (caja_navigation_action_parent_class)->connect_proxy) (action, proxy);
255     G_GNUC_END_IGNORE_DEPRECATIONS;
256 }
257 
258 static void
disconnect_proxy(GtkAction * action,GtkWidget * proxy)259 disconnect_proxy (GtkAction *action, GtkWidget *proxy)
260 {
261     if (GTK_IS_MENU_TOOL_BUTTON (proxy))
262     {
263         GtkWidget *child;
264 
265         g_signal_handlers_disconnect_by_func (proxy, G_CALLBACK (show_menu_callback), action);
266 
267         child = eel_gtk_menu_tool_button_get_button (GTK_MENU_TOOL_BUTTON (proxy));
268         g_signal_handlers_disconnect_by_func (child, G_CALLBACK (proxy_button_press_event_cb), NULL);
269         g_signal_handlers_disconnect_by_func (child, G_CALLBACK (proxy_button_release_event_cb), NULL);
270     }
271 
272     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
273     (* GTK_ACTION_CLASS (caja_navigation_action_parent_class)->disconnect_proxy) (action, proxy);
274     G_GNUC_END_IGNORE_DEPRECATIONS;
275 }
276 
277 static void
caja_navigation_action_finalize(GObject * object)278 caja_navigation_action_finalize (GObject *object)
279 {
280     CajaNavigationAction *action = CAJA_NAVIGATION_ACTION (object);
281 
282     g_free (action->priv->arrow_tooltip);
283 
284     (* G_OBJECT_CLASS (caja_navigation_action_parent_class)->finalize) (object);
285 }
286 
287 static void
caja_navigation_action_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)288 caja_navigation_action_set_property (GObject *object,
289                                      guint prop_id,
290                                      const GValue *value,
291                                      GParamSpec *pspec)
292 {
293     CajaNavigationAction *nav;
294 
295     nav = CAJA_NAVIGATION_ACTION (object);
296 
297     switch (prop_id)
298     {
299     case PROP_ARROW_TOOLTIP:
300         g_free (nav->priv->arrow_tooltip);
301         nav->priv->arrow_tooltip = g_value_dup_string (value);
302         break;
303     case PROP_DIRECTION:
304         nav->priv->direction = g_value_get_int (value);
305         break;
306     case PROP_WINDOW:
307         nav->priv->window = CAJA_NAVIGATION_WINDOW (g_value_get_object (value));
308         break;
309     }
310 }
311 
312 static void
caja_navigation_action_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)313 caja_navigation_action_get_property (GObject *object,
314                                      guint prop_id,
315                                      GValue *value,
316                                      GParamSpec *pspec)
317 {
318     CajaNavigationAction *nav;
319 
320     nav = CAJA_NAVIGATION_ACTION (object);
321 
322     switch (prop_id)
323     {
324     case PROP_ARROW_TOOLTIP:
325         g_value_set_string (value, nav->priv->arrow_tooltip);
326         break;
327     case PROP_DIRECTION:
328         g_value_set_int (value, nav->priv->direction);
329         break;
330     case PROP_WINDOW:
331         g_value_set_object (value, nav->priv->window);
332         break;
333     }
334 }
335 
336 static void
caja_navigation_action_class_init(CajaNavigationActionClass * class)337 caja_navigation_action_class_init (CajaNavigationActionClass *class)
338 {
339     GObjectClass *object_class = G_OBJECT_CLASS (class);
340     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
341     GtkActionClass *action_class = GTK_ACTION_CLASS (class);
342     G_GNUC_END_IGNORE_DEPRECATIONS;
343 
344     object_class->finalize = caja_navigation_action_finalize;
345     object_class->set_property = caja_navigation_action_set_property;
346     object_class->get_property = caja_navigation_action_get_property;
347 
348     action_class->toolbar_item_type = GTK_TYPE_MENU_TOOL_BUTTON;
349     action_class->connect_proxy = connect_proxy;
350     action_class->disconnect_proxy = disconnect_proxy;
351 
352     g_object_class_install_property (object_class,
353                                      PROP_ARROW_TOOLTIP,
354                                      g_param_spec_string ("arrow-tooltip",
355                                              "Arrow Tooltip",
356                                              "Arrow Tooltip",
357                                              NULL,
358                                              G_PARAM_READWRITE));
359     g_object_class_install_property (object_class,
360                                      PROP_DIRECTION,
361                                      g_param_spec_int ("direction",
362                                              "Direction",
363                                              "Direction",
364                                              0,
365                                              G_MAXINT,
366                                              0,
367                                              G_PARAM_READWRITE));
368     g_object_class_install_property (object_class,
369                                      PROP_WINDOW,
370                                      g_param_spec_object ("window",
371                                              "Window",
372                                              "The navigation window",
373                                              G_TYPE_OBJECT,
374                                              G_PARAM_READWRITE));
375 }
376 
377 static void
caja_navigation_action_init(CajaNavigationAction * action)378 caja_navigation_action_init (CajaNavigationAction *action)
379 {
380     action->priv = caja_navigation_action_get_instance_private (action);
381 }
382