nautilus-3.6.3/src/nautilus-toolbar.c

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 }