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 }