No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /*
4 * Nautilus
5 *
6 * Copyright (C) 2011, Red Hat, Inc.
7 *
8 * Nautilus is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
12 *
13 * Nautilus is distributed in the hope that it will be useful,
14 * but 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., 675 Mass Ave, Cambridge, MA 02139, USA.
21 *
22 * Author: Cosimo Cecchi <cosimoc@redhat.com>
23 *
24 */
25
26 #include <config.h>
27
28 #include "nautilus-toolbar.h"
29
30 #include "nautilus-location-entry.h"
31 #include "nautilus-pathbar.h"
32 #include "nautilus-actions.h"
33
34 #include <libnautilus-private/nautilus-global-preferences.h>
35 #include <libnautilus-private/nautilus-ui-utilities.h>
36
37 #include <math.h>
38
39 typedef enum {
40 NAUTILUS_NAVIGATION_DIRECTION_NONE,
41 NAUTILUS_NAVIGATION_DIRECTION_BACK,
42 NAUTILUS_NAVIGATION_DIRECTION_FORWARD
43 } NautilusNavigationDirection;
44
45 struct _NautilusToolbarPriv {
46 GtkWidget *toolbar;
47 NautilusWindow *window;
48
49 GtkWidget *path_bar;
50 GtkWidget *location_entry;
51
52 GtkToolItem *back_forward;
53
54 gboolean show_location_entry;
55
56 guint popup_timeout_id;
57 };
58
59 enum {
60 PROP_WINDOW = 1,
61 PROP_SHOW_LOCATION_ENTRY,
62 NUM_PROPERTIES
63 };
64
65 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
66
67 G_DEFINE_TYPE (NautilusToolbar, nautilus_toolbar, GTK_TYPE_BOX);
68
69 static void unschedule_menu_popup_timeout (NautilusToolbar *self);
70
71 static void
72 toolbar_update_appearance (NautilusToolbar *self)
73 {
74 gboolean show_location_entry;
75
76 show_location_entry = self->priv->show_location_entry ||
77 g_settings_get_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY);
78
79 gtk_widget_set_visible (self->priv->location_entry,
80 show_location_entry);
81 gtk_widget_set_visible (self->priv->path_bar,
82 !show_location_entry);
83 }
84
85 static gint
86 get_icon_margin (NautilusToolbar *self)
87 {
88 GtkIconSize toolbar_size;
89 gint toolbar_size_px, menu_size_px;
90
91 toolbar_size = gtk_toolbar_get_icon_size (GTK_TOOLBAR (self->priv->toolbar));
92
93 gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &menu_size_px, NULL);
94 gtk_icon_size_lookup (toolbar_size, &toolbar_size_px, NULL);
95
96 return (gint) floor ((toolbar_size_px - menu_size_px) / 2.0);
97 }
98
99 static GtkWidget *
100 toolbar_create_toolbutton (NautilusToolbar *self,
101 gboolean create_menu,
102 gboolean create_toggle,
103 const gchar *name)
104 {
105 GtkWidget *button, *image;
106 GtkActionGroup *action_group;
107 GtkAction *action;
108
109 action_group = nautilus_window_get_main_action_group (self->priv->window);
110
111 if (create_menu) {
112 button = gtk_menu_button_new ();
113 } else if (create_toggle) {
114 button = gtk_toggle_button_new ();
115 } else {
116 button = gtk_button_new ();
117 }
118
119 image = gtk_image_new ();
120 g_object_set (image, "margin", get_icon_margin (self), NULL);
121
122 gtk_button_set_image (GTK_BUTTON (button), image);
123
124 if (create_menu) {
125 gtk_image_set_from_icon_name (GTK_IMAGE (image), name,
126 GTK_ICON_SIZE_MENU);
127 } else {
128 action = gtk_action_group_get_action (action_group, name);
129 gtk_activatable_set_related_action (GTK_ACTIVATABLE (button), action);
130 gtk_button_set_label (GTK_BUTTON (button), NULL);
131 gtk_widget_set_tooltip_text (button, gtk_action_get_tooltip (action));
132 }
133
134 return button;
135 }
136
137 static void
138 activate_back_or_forward_menu_item (GtkMenuItem *menu_item,
139 NautilusWindow *window,
140 gboolean back)
141 {
142 int index;
143
144 g_assert (GTK_IS_MENU_ITEM (menu_item));
145
146 index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "user_data"));
147
148 nautilus_window_back_or_forward (window, back, index, nautilus_event_get_window_open_flags ());
149 }
150
151 static void
152 activate_back_menu_item_callback (GtkMenuItem *menu_item,
153 NautilusWindow *window)
154 {
155 activate_back_or_forward_menu_item (menu_item, window, TRUE);
156 }
157
158 static void
159 activate_forward_menu_item_callback (GtkMenuItem *menu_item, NautilusWindow *window)
160 {
161 activate_back_or_forward_menu_item (menu_item, window, FALSE);
162 }
163
164 static void
165 fill_menu (NautilusWindow *window,
166 GtkWidget *menu,
167 gboolean back)
168 {
169 NautilusWindowSlot *slot;
170 GtkWidget *menu_item;
171 int index;
172 GList *list;
173
174 slot = nautilus_window_get_active_slot (window);
175
176 list = back ? slot->back_list : slot->forward_list;
177 index = 0;
178 while (list != NULL) {
179 menu_item = nautilus_bookmark_menu_item_new (NAUTILUS_BOOKMARK (list->data));
180 g_object_set_data (G_OBJECT (menu_item), "user_data", GINT_TO_POINTER (index));
181 gtk_widget_show (GTK_WIDGET (menu_item));
182 g_signal_connect_object (menu_item, "activate",
183 back
184 ? G_CALLBACK (activate_back_menu_item_callback)
185 : G_CALLBACK (activate_forward_menu_item_callback),
186 window, 0);
187
188 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
189 list = g_list_next (list);
190 ++index;
191 }
192 }
193
194 static void
195 show_menu (NautilusToolbar *self,
196 NautilusNavigationDirection direction,
197 guint button,
198 guint32 event_time)
199 {
200 NautilusWindow *window;
201 GtkWidget *menu;
202
203 window = self->priv->window;
204 menu = gtk_menu_new ();
205
206 switch (direction) {
207 case NAUTILUS_NAVIGATION_DIRECTION_FORWARD:
208 fill_menu (window, menu, FALSE);
209 break;
210 case NAUTILUS_NAVIGATION_DIRECTION_BACK:
211 fill_menu (window, menu, TRUE);
212 break;
213 default:
214 g_assert_not_reached ();
215 break;
216 }
217
218 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
219 button, event_time);
220 }
221
222 #define MENU_POPUP_TIMEOUT 1200
223
224 typedef struct {
225 NautilusToolbar *self;
226 NautilusNavigationDirection direction;
227 } ScheduleMenuData;
228
229 static void
230 schedule_menu_data_free (ScheduleMenuData *data)
231 {
232 g_slice_free (ScheduleMenuData, data);
233 }
234
235 static gboolean
236 popup_menu_timeout_cb (gpointer user_data)
237 {
238 ScheduleMenuData *data = user_data;
239
240 show_menu (data->self, data->direction,
241 1, gtk_get_current_event_time ());
242
243 return FALSE;
244 }
245
246 static void
247 unschedule_menu_popup_timeout (NautilusToolbar *self)
248 {
249 if (self->priv->popup_timeout_id != 0) {
250 g_source_remove (self->priv->popup_timeout_id);
251 self->priv->popup_timeout_id = 0;
252 }
253 }
254
255 static void
256 schedule_menu_popup_timeout (NautilusToolbar *self,
257 NautilusNavigationDirection direction)
258 {
259 ScheduleMenuData *data;
260
261 /* unschedule any previous timeouts */
262 unschedule_menu_popup_timeout (self);
263
264 data = g_slice_new0 (ScheduleMenuData);
265 data->self = self;
266 data->direction = direction;
267
268 self->priv->popup_timeout_id =
269 g_timeout_add_full (G_PRIORITY_DEFAULT, MENU_POPUP_TIMEOUT,
270 popup_menu_timeout_cb, data,
271 (GDestroyNotify) schedule_menu_data_free);
272 }
273
274 static gboolean
275 tool_button_press_cb (GtkButton *button,
276 GdkEventButton *event,
277 gpointer user_data)
278 {
279 NautilusToolbar *self = user_data;
280 NautilusNavigationDirection direction;
281
282 direction = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button),
283 "nav-direction"));
284
285 if (event->button == 3) {
286 /* right click */
287 show_menu (self, direction, event->button, event->time);
288 return TRUE;
289 }
290
291 if (event->button == 1) {
292 schedule_menu_popup_timeout (self, direction);
293 }
294
295 return FALSE;
296 }
297
298 static gboolean
299 tool_button_release_cb (GtkButton *button,
300 GdkEventButton *event,
301 gpointer user_data)
302 {
303 NautilusToolbar *self = user_data;
304
305 unschedule_menu_popup_timeout (self);
306
307 return FALSE;
308 }
309
310 static void
311 navigation_button_setup_menu (NautilusToolbar *self,
312 GtkWidget *button,
313 NautilusNavigationDirection direction)
314 {
315 g_object_set_data (G_OBJECT (button), "nav-direction", GUINT_TO_POINTER (direction));
316
317 g_signal_connect (button, "button-press-event",
318 G_CALLBACK (tool_button_press_cb), self);
319 g_signal_connect (button, "button-release-event",
320 G_CALLBACK (tool_button_release_cb), self);
321 }
322
323 static void
324 nautilus_toolbar_constructed (GObject *obj)
325 {
326 NautilusToolbar *self = NAUTILUS_TOOLBAR (obj);
327 GtkWidget *hbox, *toolbar;
328 GtkStyleContext *context;
329 GtkWidget *tool_button;
330 GtkWidget *menu;
331 GtkWidget *box;
332 GtkToolItem *back_forward;
333 GtkToolItem *tool_item;
334 GtkUIManager *ui_manager;
335
336 G_OBJECT_CLASS (nautilus_toolbar_parent_class)->constructed (obj);
337
338 ui_manager = nautilus_window_get_ui_manager (self->priv->window);
339
340 gtk_style_context_set_junction_sides (gtk_widget_get_style_context (GTK_WIDGET (self)),
341 GTK_JUNCTION_BOTTOM);
342
343 toolbar = gtk_toolbar_new ();
344 self->priv->toolbar = toolbar;
345
346 gtk_box_pack_start (GTK_BOX (self), self->priv->toolbar, TRUE, TRUE, 0);
347 gtk_widget_show_all (self->priv->toolbar);
348
349 context = gtk_widget_get_style_context (toolbar);
350 /* Set the MENUBAR style class so it's possible to drag the app
351 * using the toolbar. */
352 gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUBAR);
353
354 /* Back and Forward */
355 back_forward = gtk_tool_item_new ();
356 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
357
358 /* Back */
359 tool_button = toolbar_create_toolbutton (self, FALSE, FALSE, NAUTILUS_ACTION_BACK);
360 navigation_button_setup_menu (self, tool_button, NAUTILUS_NAVIGATION_DIRECTION_BACK);
361 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (tool_button));
362
363 /* Forward */
364 tool_button = toolbar_create_toolbutton (self, FALSE, FALSE, NAUTILUS_ACTION_FORWARD);
365 navigation_button_setup_menu (self, tool_button, NAUTILUS_NAVIGATION_DIRECTION_FORWARD);
366 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (tool_button));
367
368 gtk_style_context_add_class (gtk_widget_get_style_context (box),
369 GTK_STYLE_CLASS_RAISED);
370 gtk_style_context_add_class (gtk_widget_get_style_context (box),
371 GTK_STYLE_CLASS_LINKED);
372
373 gtk_container_add (GTK_CONTAINER (back_forward), box);
374 gtk_container_add (GTK_CONTAINER (self->priv->toolbar), GTK_WIDGET (back_forward));
375
376 gtk_widget_show_all (GTK_WIDGET (back_forward));
377 gtk_widget_set_margin_right (GTK_WIDGET (back_forward), 12);
378
379 /* regular path bar */
380 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
381 gtk_widget_show (hbox);
382
383 self->priv->path_bar = g_object_new (NAUTILUS_TYPE_PATH_BAR, NULL);
384 gtk_box_pack_start (GTK_BOX (hbox), self->priv->path_bar, TRUE, TRUE, 0);
385
386 /* entry-like location bar */
387 self->priv->location_entry = nautilus_location_entry_new ();
388 gtk_box_pack_start (GTK_BOX (hbox), self->priv->location_entry, TRUE, TRUE, 0);
389
390 tool_item = gtk_tool_item_new ();
391 gtk_tool_item_set_expand (tool_item, TRUE);
392 gtk_container_add (GTK_CONTAINER (tool_item), hbox);
393 gtk_container_add (GTK_CONTAINER (self->priv->toolbar), GTK_WIDGET (tool_item));
394 gtk_widget_show (GTK_WIDGET (tool_item));
395
396 /* search */
397 tool_item = gtk_tool_item_new ();
398 tool_button = toolbar_create_toolbutton (self, FALSE, TRUE, NAUTILUS_ACTION_SEARCH);
399 gtk_container_add (GTK_CONTAINER (tool_item), GTK_WIDGET (tool_button));
400 gtk_container_add (GTK_CONTAINER (self->priv->toolbar), GTK_WIDGET (tool_item));
401 gtk_widget_show_all (GTK_WIDGET (tool_item));
402 gtk_widget_set_margin_left (GTK_WIDGET (tool_item), 12);
403
404 /* View buttons */
405 tool_item = gtk_tool_item_new ();
406 box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
407
408 tool_button = toolbar_create_toolbutton (self, FALSE, TRUE, NAUTILUS_ACTION_VIEW_LIST);
409 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (tool_button));
410 tool_button = toolbar_create_toolbutton (self, FALSE, TRUE, NAUTILUS_ACTION_VIEW_GRID);
411 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (tool_button));
412 tool_button = toolbar_create_toolbutton (self, TRUE, FALSE, "go-down-symbolic");
413 gtk_container_add (GTK_CONTAINER (box), GTK_WIDGET (tool_button));
414 menu = gtk_ui_manager_get_widget (ui_manager, "/ViewMenu");
415 gtk_menu_button_set_popup (GTK_MENU_BUTTON (tool_button), menu);
416
417 gtk_style_context_add_class (gtk_widget_get_style_context (box),
418 GTK_STYLE_CLASS_RAISED);
419 gtk_style_context_add_class (gtk_widget_get_style_context (box),
420 GTK_STYLE_CLASS_LINKED);
421
422 gtk_container_add (GTK_CONTAINER (tool_item), box);
423 gtk_container_add (GTK_CONTAINER (self->priv->toolbar), GTK_WIDGET (tool_item));
424 gtk_widget_show_all (GTK_WIDGET (tool_item));
425 gtk_widget_set_margin_left (GTK_WIDGET (tool_item), 12);
426
427 /* Action Menu */
428 tool_item = gtk_tool_item_new ();
429 tool_button = toolbar_create_toolbutton (self, TRUE, FALSE, "emblem-system-symbolic");
430 menu = gtk_ui_manager_get_widget (ui_manager, "/ActionMenu");
431 gtk_widget_set_halign (menu, GTK_ALIGN_END);
432 gtk_menu_button_set_popup (GTK_MENU_BUTTON (tool_button), menu);
433 gtk_actionable_set_action_name (GTK_ACTIONABLE (tool_button), "win.gear-menu");
434
435 gtk_container_add (GTK_CONTAINER (tool_item), tool_button);
436 gtk_container_add (GTK_CONTAINER (toolbar), GTK_WIDGET (tool_item));
437 gtk_widget_show_all (GTK_WIDGET (tool_item));
438 gtk_widget_set_margin_left (GTK_WIDGET (tool_item), 12);
439
440 g_signal_connect_swapped (nautilus_preferences,
441 "changed::" NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY,
442 G_CALLBACK (toolbar_update_appearance), self);
443
444 toolbar_update_appearance (self);
445 }
446
447 static void
448 nautilus_toolbar_init (NautilusToolbar *self)
449 {
450 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NAUTILUS_TYPE_TOOLBAR,
451 NautilusToolbarPriv);
452 }
453
454 static void
455 nautilus_toolbar_get_property (GObject *object,
456 guint property_id,
457 GValue *value,
458 GParamSpec *pspec)
459 {
460 NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
461
462 switch (property_id) {
463 case PROP_SHOW_LOCATION_ENTRY:
464 g_value_set_boolean (value, self->priv->show_location_entry);
465 break;
466 default:
467 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
468 break;
469 }
470 }
471
472 static void
473 nautilus_toolbar_set_property (GObject *object,
474 guint property_id,
475 const GValue *value,
476 GParamSpec *pspec)
477 {
478 NautilusToolbar *self = NAUTILUS_TOOLBAR (object);
479
480 switch (property_id) {
481 case PROP_WINDOW:
482 self->priv->window = g_value_get_object (value);
483 break;
484 case PROP_SHOW_LOCATION_ENTRY:
485 nautilus_toolbar_set_show_location_entry (self, g_value_get_boolean (value));
486 break;
487 default:
488 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
489 break;
490 }
491 }
492
493 static void
494 nautilus_toolbar_dispose (GObject *obj)
495 {
496 NautilusToolbar *self = NAUTILUS_TOOLBAR (obj);
497
498 g_signal_handlers_disconnect_by_func (nautilus_preferences,
499 toolbar_update_appearance, self);
500 unschedule_menu_popup_timeout (self);
501
502 G_OBJECT_CLASS (nautilus_toolbar_parent_class)->dispose (obj);
503 }
504
505 static void
506 nautilus_toolbar_class_init (NautilusToolbarClass *klass)
507 {
508 GObjectClass *oclass;
509
510 oclass = G_OBJECT_CLASS (klass);
511 oclass->get_property = nautilus_toolbar_get_property;
512 oclass->set_property = nautilus_toolbar_set_property;
513 oclass->constructed = nautilus_toolbar_constructed;
514 oclass->dispose = nautilus_toolbar_dispose;
515
516 properties[PROP_WINDOW] =
517 g_param_spec_object ("window",
518 "The NautilusWindow",
519 "The NautilusWindow this toolbar is part of",
520 NAUTILUS_TYPE_WINDOW,
521 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
522 G_PARAM_STATIC_STRINGS);
523 properties[PROP_SHOW_LOCATION_ENTRY] =
524 g_param_spec_boolean ("show-location-entry",
525 "Whether to show the location entry",
526 "Whether to show the location entry instead of the pathbar",
527 FALSE,
528 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
529
530 g_type_class_add_private (klass, sizeof (NautilusToolbarClass));
531 g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
532 }
533
534 GtkWidget *
535 nautilus_toolbar_new (NautilusWindow *window)
536 {
537 return g_object_new (NAUTILUS_TYPE_TOOLBAR,
538 "window", window,
539 "orientation", GTK_ORIENTATION_VERTICAL,
540 NULL);
541 }
542
543 GtkWidget *
544 nautilus_toolbar_get_path_bar (NautilusToolbar *self)
545 {
546 return self->priv->path_bar;
547 }
548
549 GtkWidget *
550 nautilus_toolbar_get_location_entry (NautilusToolbar *self)
551 {
552 return self->priv->location_entry;
553 }
554
555 void
556 nautilus_toolbar_set_show_location_entry (NautilusToolbar *self,
557 gboolean show_location_entry)
558 {
559 if (show_location_entry != self->priv->show_location_entry) {
560 self->priv->show_location_entry = show_location_entry;
561 toolbar_update_appearance (self);
562
563 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_LOCATION_ENTRY]);
564 }
565 }