nautilus-notebook.c 17.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 *  Copyright © 2002 Christophe Fergeau
 *  Copyright © 2003, 2004 Marco Pesenti Gritti
 *  Copyright © 2003, 2004, 2005 Christian Persch
 *    (ephy-notebook.c)
 *
 *  Copyright © 2008 Free Software Foundation, Inc.
 *    (nautilus-notebook.c)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
21
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
22 23 24 25 26 27
 *
 */

#include "config.h"

#include "nautilus-notebook.h"
28

29
#include "nautilus-window.h"
30
#include "nautilus-window-slot.h"
31
#include "nautilus-window-slot-dnd.h"
32

33
#include <eel/eel-vfs-extensions.h>
34 35
#include <glib/gi18n.h>
#include <gio/gio.h>
36
#include <gtk/gtk.h>
37 38 39

#define AFTER_ALL_TABS -1

40 41 42 43 44 45 46
static int  nautilus_notebook_insert_page (GtkNotebook *notebook,
                                           GtkWidget   *child,
                                           GtkWidget   *tab_label,
                                           GtkWidget   *menu_label,
                                           int          position);
static void nautilus_notebook_remove (GtkContainer *container,
                                      GtkWidget    *tab_widget);
47 48 49

enum
{
50 51
    TAB_CLOSE_REQUEST,
    LAST_SIGNAL
52 53 54 55
};

static guint signals[LAST_SIGNAL];

56 57 58
struct _NautilusNotebook
{
    GtkNotebook parent_instance;
59 60

    GtkGesture *multi_press_gesture;
61 62
};

63 64
G_DEFINE_TYPE (NautilusNotebook, nautilus_notebook, GTK_TYPE_NOTEBOOK);

65 66 67 68 69 70 71 72 73 74 75 76
static void
nautilus_notebook_dispose (GObject *object)
{
    NautilusNotebook *notebook;

    notebook = NAUTILUS_NOTEBOOK (object);

    g_clear_object (&notebook->multi_press_gesture);

    G_OBJECT_CLASS (nautilus_notebook_parent_class)->dispose (object);
}

77 78 79
static void
nautilus_notebook_class_init (NautilusNotebookClass *klass)
{
80 81 82 83
    GObjectClass *object_class = G_OBJECT_CLASS (klass);
    GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
    GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass);

84 85
    object_class->dispose = nautilus_notebook_dispose;

86 87 88 89 90 91 92 93
    container_class->remove = nautilus_notebook_remove;

    notebook_class->insert_page = nautilus_notebook_insert_page;

    signals[TAB_CLOSE_REQUEST] =
        g_signal_new ("tab-close-request",
                      G_OBJECT_CLASS_TYPE (object_class),
                      G_SIGNAL_RUN_LAST,
94
                      0,
95 96 97 98 99
                      NULL, NULL,
                      g_cclosure_marshal_VOID__OBJECT,
                      G_TYPE_NONE,
                      1,
                      NAUTILUS_TYPE_WINDOW_SLOT);
100 101 102
}

static gint
103 104 105
find_tab_num_at_pos (NautilusNotebook *notebook,
                     gint              abs_x,
                     gint              abs_y)
106
{
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    int page_num = 0;
    GtkNotebook *nb = GTK_NOTEBOOK (notebook);
    GtkWidget *page;
    GtkAllocation allocation;

    while ((page = gtk_notebook_get_nth_page (nb, page_num)))
    {
        GtkWidget *tab;
        gint max_x, max_y;

        tab = gtk_notebook_get_tab_label (nb, page);
        g_return_val_if_fail (tab != NULL, -1);

        if (!gtk_widget_get_mapped (GTK_WIDGET (tab)))
        {
            page_num++;
            continue;
        }

        gtk_widget_get_allocation (tab, &allocation);

128 129
        max_x = allocation.x + allocation.width;
        max_y = allocation.y + allocation.height;
130

131
        if (abs_x <= max_x && abs_y <= max_y)
132 133 134 135 136 137 138
        {
            return page_num;
        }

        page_num++;
    }
    return AFTER_ALL_TABS;
139 140
}

141 142 143 144 145 146
static void
button_press_cb (GtkGestureMultiPress *gesture,
                 gint                  n_press,
                 gdouble               x,
                 gdouble               y,
                 gpointer              user_data)
147
{
148 149 150 151 152
    guint button;
    GdkEventSequence *sequence;
    const GdkEvent *event;
    GtkWidget *widget;
    NautilusNotebook *notebook;
153
    int tab_clicked;
154 155 156 157 158 159 160 161
    GdkModifierType state;

    button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
    sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
    event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
    widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
    notebook = NAUTILUS_NOTEBOOK (widget);
    tab_clicked = find_tab_num_at_pos (notebook, x, y);
162

163
    gdk_event_get_state (event, &state);
164

165
    if (n_press != 1)
166
    {
167 168 169 170 171 172 173
        return;
    }

    if (tab_clicked == -1)
    {
        return;
    }
174

175 176 177
    if (button == GDK_BUTTON_SECONDARY &&
        (state & gtk_accelerator_get_default_mod_mask ()) == 0)
    {
178 179 180
        /* switch to the page the mouse is over, but don't consume the event */
        gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), tab_clicked);
    }
181
    else if (button == GDK_BUTTON_MIDDLE)
182 183 184 185 186 187
    {
        GtkWidget *slot;

        slot = gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), tab_clicked);
        g_signal_emit (notebook, signals[TAB_CLOSE_REQUEST], 0, slot);
    }
188 189 190 191 192
}

static void
nautilus_notebook_init (NautilusNotebook *notebook)
{
193 194 195
    gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
    gtk_notebook_set_show_border (GTK_NOTEBOOK (notebook), FALSE);
    gtk_notebook_set_show_tabs (GTK_NOTEBOOK (notebook), FALSE);
196

197 198 199 200 201 202 203
    notebook->multi_press_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (notebook));

    gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (notebook->multi_press_gesture),
                                                GTK_PHASE_CAPTURE);
    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (notebook->multi_press_gesture), 0);

    g_signal_connect (notebook->multi_press_gesture, "pressed", G_CALLBACK (button_press_cb), NULL);
204 205
}

206 207 208 209
gboolean
nautilus_notebook_contains_slot (NautilusNotebook   *notebook,
                                 NautilusWindowSlot *slot)
{
210 211 212
    GList *children;
    GList *l;
    gboolean found = FALSE;
213

214 215 216 217 218
    children = gtk_container_get_children (GTK_CONTAINER (notebook));
    for (l = children; l != NULL && !found; l = l->next)
    {
        found = l->data == slot;
    }
219

220
    g_list_free (children);
221

222
    return found;
223 224
}

225 226 227 228 229 230 231 232
gboolean
nautilus_notebook_content_area_hit (NautilusNotebook *notebook,
                                    gint              x,
                                    gint              y)
{
    return find_tab_num_at_pos (notebook, x, y) == -1;
}

233
void
234 235
nautilus_notebook_sync_loading (NautilusNotebook   *notebook,
                                NautilusWindowSlot *slot)
236
{
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
    GtkWidget *tab_label, *spinner, *icon;
    gboolean active, allow_stop;

    g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
    g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot));

    tab_label = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook),
                                            GTK_WIDGET (slot));
    g_return_if_fail (GTK_IS_WIDGET (tab_label));

    spinner = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "spinner"));
    icon = GTK_WIDGET (g_object_get_data (G_OBJECT (tab_label), "icon"));
    g_return_if_fail (spinner != NULL && icon != NULL);

    active = FALSE;
    g_object_get (spinner, "active", &active, NULL);
    allow_stop = nautilus_window_slot_get_allow_stop (slot);

    if (active == allow_stop)
    {
        return;
    }

    if (allow_stop)
    {
        gtk_widget_hide (icon);
        gtk_widget_show (spinner);
        gtk_spinner_start (GTK_SPINNER (spinner));
    }
    else
    {
        gtk_spinner_stop (GTK_SPINNER (spinner));
        gtk_widget_hide (spinner);
        gtk_widget_show (icon);
    }
272 273 274
}

void
275 276
nautilus_notebook_sync_tab_label (NautilusNotebook   *notebook,
                                  NautilusWindowSlot *slot)
277
{
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
    GtkWidget *hbox, *label;
    char *location_name;
    GFile *location;
    const gchar *title_name;

    g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
    g_return_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot));

    hbox = gtk_notebook_get_tab_label (GTK_NOTEBOOK (notebook), GTK_WIDGET (slot));
    g_return_if_fail (GTK_IS_WIDGET (hbox));

    label = GTK_WIDGET (g_object_get_data (G_OBJECT (hbox), "label"));
    g_return_if_fail (GTK_IS_WIDGET (label));

    gtk_label_set_text (GTK_LABEL (label), nautilus_window_slot_get_title (slot));
    location = nautilus_window_slot_get_location (slot);

    if (location != NULL)
    {
        /* Set the tooltip on the label's parent (the tab label hbox),
         * so it covers all of the tab label.
         */
        location_name = g_file_get_parse_name (location);
        title_name = nautilus_window_slot_get_title (slot);
302
        if (eel_uri_is_search (location_name))
303 304 305 306 307 308 309 310 311 312 313 314 315
        {
            gtk_widget_set_tooltip_text (gtk_widget_get_parent (label), title_name);
        }
        else
        {
            gtk_widget_set_tooltip_text (gtk_widget_get_parent (label), location_name);
        }
        g_free (location_name);
    }
    else
    {
        gtk_widget_set_tooltip_text (gtk_widget_get_parent (label), NULL);
    }
316 317 318
}

static void
319 320
close_button_clicked_cb (GtkWidget          *widget,
                         NautilusWindowSlot *slot)
321
{
322
    GtkWidget *notebook;
323

324 325 326 327 328
    notebook = gtk_widget_get_ancestor (GTK_WIDGET (slot), NAUTILUS_TYPE_NOTEBOOK);
    if (notebook != NULL)
    {
        g_signal_emit (notebook, signals[TAB_CLOSE_REQUEST], 0, slot);
    }
329 330 331
}

static GtkWidget *
332
build_tab_label (NautilusNotebook   *notebook,
333
                 NautilusWindowSlot *slot)
334
{
335
    GtkWidget *box;
336 337 338 339 340
    GtkWidget *label;
    GtkWidget *close_button;
    GtkWidget *image;
    GtkWidget *spinner;
    GtkWidget *icon;
341

342
    /* When porting to Gtk+4, use GtkCenterBox instead */
343 344 345
    box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
    gtk_widget_show (box);

346
    /* Spinner to be shown as load feedback */
347
    spinner = gtk_spinner_new ();
348
    gtk_box_pack_start (GTK_BOX (box), spinner, FALSE, FALSE, 0);
349

350
    /* Dummy icon to allocate space for spinner */
351
    icon = gtk_image_new ();
352
    gtk_box_pack_start (GTK_BOX (box), icon, FALSE, FALSE, 0);
353 354
    /* don't show the icon */

355
    /* Tab title */
356 357 358
    label = gtk_label_new (NULL);
    gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
    gtk_label_set_single_line_mode (GTK_LABEL (label), TRUE);
359
    gtk_label_set_width_chars (GTK_LABEL (label), 6);
360
    gtk_box_set_center_widget (GTK_BOX (box), label);
361 362
    gtk_widget_show (label);

363
    /* Tab close button */
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    close_button = gtk_button_new ();
    gtk_button_set_relief (GTK_BUTTON (close_button),
                           GTK_RELIEF_NONE);
    /* don't allow focus on the close button */
    gtk_widget_set_focus_on_click (close_button, FALSE);

    gtk_widget_set_name (close_button, "nautilus-tab-close-button");

    image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU);
    gtk_widget_set_tooltip_text (close_button, _("Close tab"));
    g_signal_connect_object (close_button, "clicked",
                             G_CALLBACK (close_button_clicked_cb), slot, 0);

    gtk_container_add (GTK_CONTAINER (close_button), image);
    gtk_widget_show (image);

380
    gtk_box_pack_end (GTK_BOX (box), close_button, FALSE, FALSE, 0);
381 382 383 384 385 386 387 388 389 390 391
    gtk_widget_show (close_button);

    g_object_set_data (G_OBJECT (box), "nautilus-notebook-tab", GINT_TO_POINTER (1));
    nautilus_drag_slot_proxy_init (box, NULL, slot);

    g_object_set_data (G_OBJECT (box), "label", label);
    g_object_set_data (G_OBJECT (box), "spinner", spinner);
    g_object_set_data (G_OBJECT (box), "icon", icon);
    g_object_set_data (G_OBJECT (box), "close-button", close_button);

    return box;
392 393 394 395
}

static int
nautilus_notebook_insert_page (GtkNotebook *gnotebook,
396 397 398 399
                               GtkWidget   *tab_widget,
                               GtkWidget   *tab_label,
                               GtkWidget   *menu_label,
                               int          position)
400
{
401
    g_assert (GTK_IS_WIDGET (tab_widget));
402

403 404 405 406 407
    position = GTK_NOTEBOOK_CLASS (nautilus_notebook_parent_class)->insert_page (gnotebook,
                                                                                 tab_widget,
                                                                                 tab_label,
                                                                                 menu_label,
                                                                                 position);
408

409 410 411 412
    gtk_notebook_set_show_tabs (gnotebook,
                                gtk_notebook_get_n_pages (gnotebook) > 1);
    gtk_notebook_set_tab_reorderable (gnotebook, tab_widget, TRUE);
    gtk_notebook_set_tab_detachable (gnotebook, tab_widget, TRUE);
413

414
    return position;
415 416 417
}

int
418 419 420 421
nautilus_notebook_add_tab (NautilusNotebook   *notebook,
                           NautilusWindowSlot *slot,
                           int                 position,
                           gboolean            jump_to)
422
{
423 424
    GtkNotebook *gnotebook = GTK_NOTEBOOK (notebook);
    GtkWidget *tab_label;
425

426 427
    g_return_val_if_fail (NAUTILUS_IS_NOTEBOOK (notebook), -1);
    g_return_val_if_fail (NAUTILUS_IS_WINDOW_SLOT (slot), -1);
428

429
    tab_label = build_tab_label (notebook, slot);
430

431 432 433 434
    position = gtk_notebook_insert_page (GTK_NOTEBOOK (notebook),
                                         GTK_WIDGET (slot),
                                         tab_label,
                                         position);
435

436 437 438
    gtk_container_child_set (GTK_CONTAINER (notebook),
                             GTK_WIDGET (slot),
                             "tab-expand", TRUE,
439
                             "detachable", FALSE,
440
                             NULL);
Alexander Larsson's avatar
Alexander Larsson committed
441

442 443
    nautilus_notebook_sync_tab_label (notebook, slot);
    nautilus_notebook_sync_loading (notebook, slot);
444

445 446 447 448
    if (jump_to)
    {
        gtk_notebook_set_current_page (gnotebook, position);
    }
449

450
    return position;
451 452 453 454
}

static void
nautilus_notebook_remove (GtkContainer *container,
455
                          GtkWidget    *tab_widget)
456
{
457 458
    GtkNotebook *gnotebook = GTK_NOTEBOOK (container);
    GTK_CONTAINER_CLASS (nautilus_notebook_parent_class)->remove (container, tab_widget);
459

460 461
    gtk_notebook_set_show_tabs (gnotebook,
                                gtk_notebook_get_n_pages (gnotebook) > 1);
462 463 464 465
}

void
nautilus_notebook_reorder_current_child_relative (NautilusNotebook *notebook,
466
                                                  int               offset)
467
{
468 469 470
    GtkNotebook *gnotebook;
    GtkWidget *child;
    int page;
471

472
    g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
473

474 475 476 477
    if (!nautilus_notebook_can_reorder_current_child_relative (notebook, offset))
    {
        return;
    }
478

479
    gnotebook = GTK_NOTEBOOK (notebook);
480

481 482 483
    page = gtk_notebook_get_current_page (gnotebook);
    child = gtk_notebook_get_nth_page (gnotebook, page);
    gtk_notebook_reorder_child (gnotebook, child, page + offset);
484 485 486 487
}

static gboolean
nautilus_notebook_is_valid_relative_position (NautilusNotebook *notebook,
488
                                              int               offset)
489
{
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
    GtkNotebook *gnotebook;
    int page;
    int n_pages;

    gnotebook = GTK_NOTEBOOK (notebook);

    page = gtk_notebook_get_current_page (gnotebook);
    n_pages = gtk_notebook_get_n_pages (gnotebook) - 1;
    if (page < 0 ||
        (offset < 0 && page < -offset) ||
        (offset > 0 && page > n_pages - offset))
    {
        return FALSE;
    }

    return TRUE;
506 507 508 509
}

gboolean
nautilus_notebook_can_reorder_current_child_relative (NautilusNotebook *notebook,
510
                                                      int               offset)
511
{
512
    g_return_val_if_fail (NAUTILUS_IS_NOTEBOOK (notebook), FALSE);
513

514
    return nautilus_notebook_is_valid_relative_position (notebook, offset);
515 516
}

517 518
void
nautilus_notebook_next_page (NautilusNotebook *notebook)
519
{
520
    gint current_page, n_pages;
521

522
    g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
523

524 525
    current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
    n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
526

527 528 529 530 531 532 533
    if (current_page < n_pages - 1)
    {
        gtk_notebook_next_page (GTK_NOTEBOOK (notebook));
    }
    else
    {
        gboolean wrap_around;
534

535 536 537
        g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
                      "gtk-keynav-wrap-around", &wrap_around,
                      NULL);
538

539 540 541 542 543
        if (wrap_around)
        {
            gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), 0);
        }
    }
544 545
}

546 547 548
void
nautilus_notebook_prev_page (NautilusNotebook *notebook)
{
549
    gint current_page;
550

551
    g_return_if_fail (NAUTILUS_IS_NOTEBOOK (notebook));
552

553
    current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (notebook));
554

555 556 557 558 559 560 561
    if (current_page > 0)
    {
        gtk_notebook_prev_page (GTK_NOTEBOOK (notebook));
    }
    else
    {
        gboolean wrap_around;
562

563 564 565
        g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
                      "gtk-keynav-wrap-around", &wrap_around,
                      NULL);
566

567 568 569 570 571
        if (wrap_around)
        {
            gtk_notebook_set_current_page (GTK_NOTEBOOK (notebook), -1);
        }
    }
572
}