nautilus-3.6.3/src/nautilus-notebook.c

No issues found

  1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
  2 /*
  3  *  Copyright © 2002 Christophe Fergeau
  4  *  Copyright © 2003, 2004 Marco Pesenti Gritti
  5  *  Copyright © 2003, 2004, 2005 Christian Persch
  6  *    (ephy-notebook.c)
  7  *
  8  *  Copyright © 2008 Free Software Foundation, Inc.
  9  *    (nautilus-notebook.c)
 10  *
 11  *  This program is free software; you can redistribute it and/or modify
 12  *  it under the terms of the GNU General Public License as published by
 13  *  the Free Software Foundation; either version 2, or (at your option)
 14  *  any later version.
 15  *
 16  *  This program is distributed in the hope that it will be useful,
 17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 19  *  GNU General Public License for more details.
 20  *
 21  *  You should have received a copy of the GNU General Public License
 22  *  along with this program; if not, write to the Free Software
 23  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 24  *
 25  */
 26 
 27 #include "config.h"
 28 
 29 #include "nautilus-notebook.h"
 30 
 31 #include "nautilus-window.h"
 32 #include "nautilus-window-manage-views.h"
 33 #include "nautilus-window-private.h"
 34 #include "nautilus-window-slot.h"
 35 #include "nautilus-window-slot-dnd.h"
 36 
 37 #include <glib/gi18n.h>
 38 #include <gio/gio.h>
 39 #include <gtk/gtk.h>
 40 
 41 #define AFTER_ALL_TABS -1
 42 #define NOT_IN_APP_WINDOWS -2
 43 
 44 static int  nautilus_notebook_insert_page	 (GtkNotebook *notebook,
 45 					  GtkWidget *child,
 46 					  GtkWidget *tab_label,
 47 					  GtkWidget *menu_label,
 48 					  int position);
 49 static void nautilus_notebook_remove	 (GtkContainer *container,
 50 					  GtkWidget *tab_widget);
 51 
 52 static const GtkTargetEntry url_drag_types[] = 
 53 {
 54 	{ NAUTILUS_ICON_DND_GNOME_ICON_LIST_TYPE, 0, NAUTILUS_ICON_DND_GNOME_ICON_LIST },
 55 	{ NAUTILUS_ICON_DND_URI_LIST_TYPE, 0, NAUTILUS_ICON_DND_URI_LIST },
 56 };
 57 
 58 enum
 59 {
 60 	TAB_CLOSE_REQUEST,
 61 	LAST_SIGNAL
 62 };
 63 
 64 static guint signals[LAST_SIGNAL];
 65 
 66 G_DEFINE_TYPE (NautilusNotebook, nautilus_notebook, GTK_TYPE_NOTEBOOK);
 67 
 68 static void
 69 nautilus_notebook_class_init (NautilusNotebookClass *klass)
 70 {
 71 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 72 	GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 73 	GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);
 74 
 75 	container_class->remove = nautilus_notebook_remove;
 76 
 77 	notebook_class->insert_page = nautilus_notebook_insert_page;
 78 
 79 	signals[TAB_CLOSE_REQUEST] =
 80 		g_signal_new ("tab-close-request",
 81 			      G_OBJECT_CLASS_TYPE (object_class),
 82 			      G_SIGNAL_RUN_LAST,
 83 			      G_STRUCT_OFFSET (NautilusNotebookClass, tab_close_request),
 84 			      NULL, NULL,
 85 			      g_cclosure_marshal_VOID__OBJECT,
 86 			      G_TYPE_NONE,
 87 			      1,
 88 			      NAUTILUS_TYPE_WINDOW_SLOT);
 89 }
 90 
 91 
 92 /* FIXME remove when gtknotebook's func for this becomes public, bug #.... */
 93 static NautilusNotebook *
 94 find_notebook_at_pointer (gint abs_x, gint abs_y)
 95 {
 96 	GdkDeviceManager *manager;
 97 	GdkDevice *pointer;
 98 	GdkWindow *win_at_pointer, *toplevel_win;
 99 	gpointer toplevel = NULL;
100 	gint x, y;
101 
102 	/* FIXME multi-head */
103 	manager = gdk_display_get_device_manager (gdk_display_get_default ());
104 	pointer = gdk_device_manager_get_client_pointer (manager);
105 	win_at_pointer = gdk_device_get_window_at_position (pointer, &x, &y);
106 
107 	if (win_at_pointer == NULL)
108 	{
109 		/* We are outside all windows containing a notebook */
110 		return NULL;
111 	}
112 
113 	toplevel_win = gdk_window_get_toplevel (win_at_pointer);
114 
115 	/* get the GtkWidget which owns the toplevel GdkWindow */
116 	gdk_window_get_user_data (toplevel_win, &toplevel);
117 
118 	/* toplevel should be an NautilusWindow */
119 	if (toplevel != NULL && NAUTILUS_IS_WINDOW (toplevel))
120 	{
121 		return NAUTILUS_NOTEBOOK (NAUTILUS_WINDOW (toplevel)->details->notebook);
122 	}
123 
124 	return NULL;
125 }
126 
127 static gboolean
128 is_in_notebook_window (NautilusNotebook *notebook,
129 		       gint abs_x, gint abs_y)
130 {
131 	NautilusNotebook *nb_at_pointer;
132 
133 	nb_at_pointer = find_notebook_at_pointer (abs_x, abs_y);
134 
135 	return nb_at_pointer == notebook;
136 }
137 
138 static gint
139 find_tab_num_at_pos (NautilusNotebook *notebook, gint abs_x, gint abs_y)
140 {
141 	GtkPositionType tab_pos;
142 	int page_num = 0;
143 	GtkNotebook *nb = GTK_NOTEBOOK (notebook);
144 	GtkWidget *page;
145 	GtkAllocation allocation;
146 
147 	tab_pos = gtk_notebook_get_tab_pos (GTK_NOTEBOOK (notebook));
148 
149 	if (gtk_notebook_get_n_pages (nb) == 0)
150 	{
151 		return AFTER_ALL_TABS;
152 	}
153 
154 	/* For some reason unfullscreen + quick click can
155 	   cause a wrong click event to be reported to the tab */
156 	if (!is_in_notebook_window(notebook, abs_x, abs_y))
157 	{
158 		return NOT_IN_APP_WINDOWS;
159 	}
160 
161 	while ((page = gtk_notebook_get_nth_page (nb, page_num)))
162 	{
163 		GtkWidget *tab;
164 		gint max_x, max_y;
165 		gint x_root, y_root;
166 
167 		tab = gtk_notebook_get_tab_label (nb, page);
168 		g_return_val_if_fail (tab != NULL, -1);
169 
170 		if (!gtk_widget_get_mapped (GTK_WIDGET (tab)))
171 		{
172 			page_num++;
173 			continue;
174 		}
175 
176 		gdk_window_get_origin (gtk_widget_get_window (tab),
177 				       &x_root, &y_root);
178 		gtk_widget_get_allocation (tab, &allocation);
179 
180 		max_x = x_root + allocation.x + allocation.width;
181 		max_y = y_root + allocation.y + allocation.height;
182 
183 		if (((tab_pos == GTK_POS_TOP)
184 		     || (tab_pos == GTK_POS_BOTTOM))
185 		    &&(abs_x<=max_x))
186 		{
187 			return page_num;
188 		}
189 		else if (((tab_pos == GTK_POS_LEFT)
190 			  || (tab_pos == GTK_POS_RIGHT))
191 			 && (abs_y<=max_y))
192 		{
193 			return page_num;
194 		}
195 
196 		page_num++;
197 	}
198 	return AFTER_ALL_TABS;
199 }
200 
201 static gboolean
202 button_press_cb (NautilusNotebook *notebook,
203 		 GdkEventButton *event,
204 		 gpointer data)
205 {
206 	int tab_clicked;
207 
208 	tab_clicked = find_tab_num_at_pos (notebook, event->x_root, event->y_root);
209 
210 	if (event->type == GDK_BUTTON_PRESS &&
211 	    event->button == 3 &&
212 		   (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
213 	{
214 		if (tab_clicked == -1)
215 		{
216 			/* consume event, so that we don't pop up the context menu when
217 			 * the mouse if not over a tab label
218 			 */
219 			return TRUE;
220 		}
221 
222 		/* switch to the page the mouse is over, but don't consume the event */
223 		gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), tab_clicked);
224 	}
225 
226 	return FALSE;
227 }
228 
229 static void
230 nautilus_notebook_init (NautilusNotebook *notebook)
231 {
232 	gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
233 	gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
234 	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
235 
236 	g_signal_connect (notebook, "button-press-event",
237 			  (GCallback)button_press_cb, NULL);
238 
239 	/* Set up drag-and-drop target */
240 	/* TODO this would be used for opening a new tab.
241 	 * It will only work properly as soon as GtkNotebook 
242 	 * supports to find out whether a particular point
243 	 * is on a tab button or not.
244 	 */
245 #if 0
246 	gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
247 			   url_drag_types, G_N_ELEMENTS (url_drag_types),
248 			   GDK_ACTION_LINK);
249 	gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
250 #endif
251 }
252 
253 void
254 nautilus_notebook_sync_loading (NautilusNotebook *notebook,
255 				NautilusWindowSlot *slot)
256 {
257 	GtkWidget *tab_label, *spinner, *icon;
258 	gboolean active;
259 
260 	g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
261 	g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot));
262 
263 	tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), 
264 						GTK_WIDGET (slot));
265 	g_return_if_fail (GTK_IS_WIDGET (tab_label));
266 
267 	spinner = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "spinner"));
268 	icon = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "icon"));
269 	g_return_if_fail (spinner != NULL && icon != NULL);
270 
271 	active = FALSE;
272 	g_object_get (spinner, "active", &active, NULL);
273 	if (active == slot->allow_stop)	{
274 		return;
275 	}
276 
277 	if (slot->allow_stop) {
278 		gtk_widget_hide (icon);
279 		gtk_widget_show (spinner);
280 		gtk_spinner_start (GTK_SPINNER (spinner));
281 	} else {
282 		gtk_spinner_stop (GTK_SPINNER (spinner));
283 		gtk_widget_hide (spinner);
284 		gtk_widget_show (icon);
285 	}
286 }
287 
288 void
289 nautilus_notebook_sync_tab_label (NautilusNotebook *notebook,
290 				  NautilusWindowSlot *slot)
291 {
292 	GtkWidget *hbox, *label;
293 	char *location_name;
294 
295 	g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
296 	g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot));
297 
298 	hbox = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), GTK_WIDGET (slot));
299 	g_return_if_fail (GTK_IS_WIDGET (hbox));
300 
301 	label = GTK_WIDGET (g_object_get_data (G_OBJECT (hbox), "label"));
302 	g_return_if_fail (GTK_IS_WIDGET (label));
303 
304 	gtk_label_set_text (GTK_LABEL (label), slot->title);
305 
306 	if (slot->location != NULL) {
307 		/* Set the tooltip on the label's parent (the tab label hbox),
308 		 * so it covers all of the tab label.
309 		 */
310 		location_name = g_file_get_parse_name (slot->location);
311 		gtk_widget_set_tooltip_text (gtk_widget_get_parent (label), location_name);
312 		g_free (location_name);
313 	} else {
314 		gtk_widget_set_tooltip_text (gtk_widget_get_parent (label), NULL);
315 	}
316 }
317 
318 static void
319 close_button_clicked_cb (GtkWidget *widget,
320 			 NautilusWindowSlot *slot)
321 {
322 	GtkWidget *notebook;
323 
324 	notebook = gtk_widget_get_ancestor (GTK_WIDGET (slot), NAUTILUS_TYPE_NOTEBOOK);
325 	if (notebook != NULL) {
326 		g_signal_emit (notebook, signals[TAB_CLOSE_REQUEST], 0, slot);
327 	}
328 }
329 
330 static GtkWidget *
331 build_tab_label (NautilusNotebook *nb, NautilusWindowSlot *slot)
332 {
333 	GtkWidget *hbox, *label, *close_button, *image, *spinner, *icon;
334 
335 	/* set hbox spacing and label padding (see below) so that there's an
336 	 * equal amount of space around the label */
337 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
338 	gtk_widget_show (hbox);
339 
340 	/* setup load feedback */
341 	spinner = gtk_spinner_new ();
342 	gtk_box_pack_start (GTK_BOX (hbox), spinner, FALSE, FALSE, 0);
343 
344 	/* setup site icon, empty by default */
345 	icon = gtk_image_new ();
346 	gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
347 	/* don't show the icon */
348 
349 	/* setup label */
350 	label = gtk_label_new (NULL);
351 	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
352 	gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE);
353 	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
354 	gtk_misc_set_padding (GTK_MISC (label), 0, 0);
355 	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
356 	gtk_widget_show (label);
357 
358 	/* setup close button */
359 	close_button = gtk_button_new ();
360 	gtk_button_set_relief (GTK_BUTTON (close_button),
361 			       GTK_RELIEF_NONE);
362 	/* don't allow focus on the close button */
363 	gtk_button_set_focus_on_click (GTK_BUTTON (close_button), FALSE);
364 
365 	gtk_widget_set_name (close_button, "nautilus-tab-close-button");
366 
367 	image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU);
368 	gtk_widget_set_tooltip_text (close_button, _("Close tab"));
369 	g_signal_connect_object (close_button, "clicked",
370 				 G_CALLBACK (close_button_clicked_cb), slot, 0);
371 
372 	gtk_container_add (GTK_CONTAINER (close_button), image);
373 	gtk_widget_show (image);
374 
375 	gtk_box_pack_start (GTK_BOX (hbox), close_button, FALSE, FALSE, 0);
376 	gtk_widget_show (close_button);
377 
378 	nautilus_drag_slot_proxy_init (hbox, NULL, slot);
379 
380 	g_object_set_data (G_OBJECT (hbox), "label", label);
381 	g_object_set_data (G_OBJECT (hbox), "spinner", spinner);
382 	g_object_set_data (G_OBJECT (hbox), "icon", icon);
383 	g_object_set_data (G_OBJECT (hbox), "close-button", close_button);
384 
385 	return hbox;
386 }
387 
388 static int
389 nautilus_notebook_insert_page (GtkNotebook *gnotebook,
390 			       GtkWidget *tab_widget,
391 			       GtkWidget *tab_label,
392 			       GtkWidget *menu_label,
393 			       int position)
394 {
395 	g_assert (GTK_IS_WIDGET (tab_widget));
396 
397 	position = GTK_NOTEBOOK_CLASS (nautilus_notebook_parent_class)->insert_page (gnotebook,
398 										     tab_widget,
399 										     tab_label,
400 										     menu_label,
401 										     position);
402 
403 	gtk_notebook_set_show_tabs (gnotebook,
404 				    gtk_notebook_get_n_pages (gnotebook) > 1);
405 	gtk_notebook_set_tab_reorderable (gnotebook, tab_widget, TRUE);
406 	gtk_notebook_set_tab_detachable (gnotebook, tab_widget, TRUE);
407 
408 	return position;
409 }
410 
411 int
412 nautilus_notebook_add_tab (NautilusNotebook *notebook,
413 			   NautilusWindowSlot *slot,
414 			   int position,
415 			   gboolean jump_to)
416 {
417 	GtkNotebook *gnotebook = GTK_NOTEBOOK (notebook);
418 	GtkWidget *tab_label;
419 
420 	g_return_val_if_fail (NAUTILUS_IS_NOTEBOOK (notebook), -1);
421 	g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot), -1);
422 
423 	tab_label = build_tab_label (notebook, slot);
424 
425 	position = gtk_notebook_insert_page (GTK_NOTEBOOK (notebook),
426 					     GTK_WIDGET (slot),
427 					     tab_label,
428 					     position);
429 
430 	gtk_container_child_set (GTK_CONTAINER (notebook),
431 				 GTK_WIDGET (slot),
432 				 "tab-expand", TRUE,
433 				 NULL);
434 
435 	nautilus_notebook_sync_tab_label (notebook, slot);
436 	nautilus_notebook_sync_loading (notebook, slot);
437 
438 	if (jump_to) {
439 		gtk_notebook_set_current_page (gnotebook, position);
440 	}
441 
442 	return position;
443 }
444 
445 static void
446 nautilus_notebook_remove (GtkContainer *container,
447 			  GtkWidget *tab_widget)
448 {
449 	GtkNotebook *gnotebook = GTK_NOTEBOOK (container);
450 	GTK_CONTAINER_CLASS (nautilus_notebook_parent_class)->remove (container, tab_widget);
451 
452 	gtk_notebook_set_show_tabs (gnotebook,
453 				    gtk_notebook_get_n_pages (gnotebook) > 1);
454 
455 }
456 
457 void
458 nautilus_notebook_reorder_current_child_relative (NautilusNotebook *notebook,
459 						  int offset)
460 {
461 	GtkNotebook *gnotebook;
462 	GtkWidget *child;
463 	int page;
464 
465 	g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
466 
467 	if (!nautilus_notebook_can_reorder_current_child_relative (notebook, offset)) {
468 		return;
469 	}
470 
471 	gnotebook = GTK_NOTEBOOK (notebook);
472 
473 	page = gtk_notebook_get_current_page (gnotebook);
474 	child = gtk_notebook_get_nth_page (gnotebook, page);
475 	gtk_notebook_reorder_child (gnotebook, child, page + offset);
476 }
477 
478 static gboolean
479 nautilus_notebook_is_valid_relative_position (NautilusNotebook *notebook,
480 					      int offset)
481 {
482 	GtkNotebook *gnotebook;
483 	int page;
484 	int n_pages;
485 
486 	gnotebook = GTK_NOTEBOOK (notebook);
487 
488 	page = gtk_notebook_get_current_page (gnotebook);
489 	n_pages = gtk_notebook_get_n_pages (gnotebook) - 1;
490 	if (page < 0 ||
491 	    (offset < 0 && page < -offset) ||
492 	    (offset > 0 && page > n_pages - offset)) {
493 		return FALSE;
494 	}
495 
496 	return TRUE;
497 }
498 
499 gboolean
500 nautilus_notebook_can_reorder_current_child_relative (NautilusNotebook *notebook,
501 						      int offset)
502 {
503 	g_return_val_if_fail (NAUTILUS_IS_NOTEBOOK (notebook), FALSE);
504 
505 	return nautilus_notebook_is_valid_relative_position (notebook, offset);
506 }
507 
508 void
509 nautilus_notebook_next_page (NautilusNotebook *notebook)
510 {
511 	gint current_page, n_pages;
512 
513 	g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
514 
515 	current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
516 	n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
517 
518 	if (current_page < n_pages - 1)
519 		gtk_notebook_next_page (GTK_NOTEBOOK (notebook));
520 	else {
521 		gboolean  wrap_around;
522 
523 		g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
524 			      "gtk-keynav-wrap-around", &wrap_around,
525 			      NULL);
526 
527 		if (wrap_around)
528 			gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0);
529 	}
530 }
531 
532 void
533 nautilus_notebook_prev_page (NautilusNotebook *notebook)
534 {
535 	gint current_page;
536 
537 	g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
538 
539 	current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
540 
541 	if (current_page > 0)
542 		gtk_notebook_prev_page (GTK_NOTEBOOK (notebook));
543 	else {
544 		gboolean  wrap_around;
545 
546 		g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
547 			      "gtk-keynav-wrap-around", &wrap_around,
548 			      NULL);
549 
550 		if (wrap_around)
551 			gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), -1);
552 	}
553 }