1 /* fm-list-view.c - implementation of list view of directory.
2 *
3 * Copyright (C) 2000 Eazel, Inc.
4 * Copyright (C) 2001, 2002 Anders Carlsson <andersca@gnu.org>
5 *
6 * The Gnome Library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * The Gnome Library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with the Gnome Library; see the file COPYING.LIB. If not,
18 * see <http://www.gnu.org/licenses/>.
19 *
20 * Authors: John Sullivan <sullivan@eazel.com>
21 * Anders Carlsson <andersca@gnu.org>
22 * David Emory Watson <dwatson@cs.ucr.edu>
23 */
24
25 #include "nautilus-list-view.h"
26 #include "nautilus-list-view-private.h"
27
28 #include <eel/eel-vfs-extensions.h>
29 #include <gdk/gdk.h>
30 #include <gdk/gdkkeysyms.h>
31 #include <glib/gi18n.h>
32 #include <gtk/gtk.h>
33 #include <libgd/gd.h>
34 #include <string.h>
35
36 #define DEBUG_FLAG NAUTILUS_DEBUG_LIST_VIEW
37 #include "nautilus-debug.h"
38
39 #include "nautilus-clipboard.h"
40 #include "nautilus-column-chooser.h"
41 #include "nautilus-column-utilities.h"
42 #include "nautilus-dnd.h"
43 #include "nautilus-enums.h"
44 #include "nautilus-error-reporting.h"
45 #include "nautilus-file-utilities.h"
46 #include "nautilus-files-view-dnd.h"
47 #include "nautilus-global-preferences.h"
48 #include "nautilus-list-model.h"
49 #include "nautilus-list-view-dnd.h"
50 #include "nautilus-metadata.h"
51 #include "nautilus-search-directory.h"
52 #include "nautilus-tag-manager.h"
53 #include "nautilus-toolbar.h"
54 #include "nautilus-tree-view-drag-dest.h"
55 #include "nautilus-ui-utilities.h"
56 #include "nautilus-view.h"
57 #include "nautilus-tracker-utilities.h"
58
59 struct SelectionForeachData
60 {
61 GList *list;
62 GtkTreeSelection *selection;
63 };
64
65 /*
66 * The row height should be large enough to not clip emblems.
67 * Computing this would be costly, so we just choose a number
68 * that works well with the set of emblems we've designed.
69 */
70 #define LIST_VIEW_MINIMUM_ROW_HEIGHT 28
71
72 /* The star icon itself is 16px, which leaves an empty 16px gutter on each side,
73 * which is necessary to avoid the overlay scrollbar.
74 */
75 #define STAR_COLUMN_WIDTH 48
76
77 /* We wait two seconds after row is collapsed to unload the subdirectory */
78 #define COLLAPSE_TO_UNLOAD_DELAY 2
79
80 static GdkCursor *hand_cursor = NULL;
81
82 static GList *nautilus_list_view_get_selection (NautilusFilesView *view);
83 static GList *nautilus_list_view_get_selection_for_file_transfer (NautilusFilesView *view);
84 static void nautilus_list_view_set_zoom_level (NautilusListView *view,
85 NautilusListZoomLevel new_level);
86 static void nautilus_list_view_scroll_to_file (NautilusListView *view,
87 NautilusFile *file);
88 static void nautilus_list_view_sort_directories_first_changed (NautilusFilesView *view);
89
90 static void apply_columns_settings (NautilusListView *list_view,
91 char **column_order,
92 char **visible_columns);
93 static char **get_visible_columns (NautilusListView *list_view);
94 static char **get_default_visible_columns (NautilusListView *list_view);
95 static char **get_column_order (NautilusListView *list_view);
96 static char **get_default_column_order (NautilusListView *list_view);
97 static void on_clipboard_owner_changed (GtkClipboard *clipboard,
98 GdkEvent *event,
99 gpointer user_data);
100
101
102 G_DEFINE_TYPE (NautilusListView, nautilus_list_view, NAUTILUS_TYPE_FILES_VIEW);
103
104 static const char *default_search_visible_columns[] =
105 {
106 "name", "size", "where", NULL
107 };
108
109 static const char *default_search_columns_order[] =
110 {
111 "name", "size", "where", NULL
112 };
113
114 static const char *default_recent_visible_columns[] =
115 {
116 "name", "where", "recency", NULL
117 };
118
119 static const char *default_recent_columns_order[] =
120 {
121 "name", "where", "recency", NULL
122 };
123
124 static const char *default_trash_visible_columns[] =
125 {
126 "name", "size", "trash_orig_path", "trashed_on", NULL
127 };
128
129 static const char *default_trash_columns_order[] =
130 {
131 "name", "size", "trash_orig_path", "trashed_on", NULL
132 };
133
134 static const gchar *
get_default_sort_order(NautilusFile * file,gboolean * reversed)135 get_default_sort_order (NautilusFile *file,
136 gboolean *reversed)
137 {
138 NautilusFileSortType sort_type;
139
140 /* This array makes the #NautilusFileSortType values correspond to the
141 * respective column attribute.
142 */
143 const char *attributes[] =
144 {
145 "name",
146 "size",
147 "type",
148 "date_modified",
149 "date_accessed",
150 "date_created",
151 "starred",
152 "trashed_on",
153 "search_relevance",
154 "recency",
155 NULL
156 };
157
158 sort_type = nautilus_file_get_default_sort_type (file, reversed);
159
160 return attributes[sort_type];
161 }
162
163 static void
list_selection_changed_callback(GtkTreeSelection * selection,gpointer user_data)164 list_selection_changed_callback (GtkTreeSelection *selection,
165 gpointer user_data)
166 {
167 NautilusFilesView *view;
168
169 view = NAUTILUS_FILES_VIEW (user_data);
170
171 nautilus_files_view_notify_selection_changed (view);
172 }
173
174 static void
preview_selected_items(NautilusListView * view)175 preview_selected_items (NautilusListView *view)
176 {
177 GList *file_list;
178
179 file_list = nautilus_list_view_get_selection (NAUTILUS_FILES_VIEW (view));
180
181 if (file_list != NULL)
182 {
183 nautilus_files_view_preview_files (NAUTILUS_FILES_VIEW (view),
184 file_list, NULL);
185 nautilus_file_list_free (file_list);
186 }
187 }
188
189 static void
activate_selected_items(NautilusListView * view)190 activate_selected_items (NautilusListView *view)
191 {
192 GList *file_list;
193
194 file_list = nautilus_list_view_get_selection (NAUTILUS_FILES_VIEW (view));
195 if (file_list != NULL)
196 {
197 nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (view),
198 file_list,
199 0, TRUE);
200 nautilus_file_list_free (file_list);
201 }
202 }
203
204 static void
activate_selected_items_alternate(NautilusListView * view,NautilusFile * file,gboolean open_in_tab)205 activate_selected_items_alternate (NautilusListView *view,
206 NautilusFile *file,
207 gboolean open_in_tab)
208 {
209 GList *file_list;
210 NautilusWindowOpenFlags flags;
211
212 flags = 0;
213
214 if (open_in_tab)
215 {
216 flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB;
217 flags |= NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE;
218 }
219 else
220 {
221 flags |= NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW;
222 }
223
224 if (file != NULL)
225 {
226 nautilus_file_ref (file);
227 file_list = g_list_prepend (NULL, file);
228 }
229 else
230 {
231 file_list = nautilus_list_view_get_selection (NAUTILUS_FILES_VIEW (view));
232 }
233 nautilus_files_view_activate_files (NAUTILUS_FILES_VIEW (view),
234 file_list,
235 flags,
236 TRUE);
237 nautilus_file_list_free (file_list);
238 }
239
240 static gboolean
button_event_modifies_selection(const GdkEvent * event)241 button_event_modifies_selection (const GdkEvent *event)
242 {
243 GdkModifierType state;
244
245 gdk_event_get_state (event, &state);
246
247 return (state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
248 }
249
250 static int
get_click_policy(void)251 get_click_policy (void)
252 {
253 return g_settings_get_enum (nautilus_preferences,
254 NAUTILUS_PREFERENCES_CLICK_POLICY);
255 }
256
257 static void
nautilus_list_view_did_not_drag(NautilusListView * view,const GdkEvent * event)258 nautilus_list_view_did_not_drag (NautilusListView *view,
259 const GdkEvent *event)
260 {
261 GtkTreeView *tree_view;
262 GtkTreeSelection *selection;
263 gdouble x;
264 gdouble y;
265 GtkTreePath *path;
266 guint button;
267 GdkModifierType state;
268
269 tree_view = view->details->tree_view;
270 selection = gtk_tree_view_get_selection (tree_view);
271
272 if (!gdk_event_get_coords (event, &x, &y))
273 {
274 return;
275 }
276
277 if (!gtk_tree_view_get_path_at_pos (tree_view, x, y, &path, NULL, NULL, NULL))
278 {
279 return;
280 }
281
282 if (!gdk_event_get_button (event, &button))
283 {
284 return;
285 }
286
287 gdk_event_get_state (event, &state);
288
289 if ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE)
290 && ((state & GDK_CONTROL_MASK) != 0 ||
291 (state & GDK_SHIFT_MASK) == 0)
292 && view->details->row_selected_on_button_down)
293 {
294 if (!button_event_modifies_selection (event))
295 {
296 gtk_tree_selection_unselect_all (selection);
297 gtk_tree_selection_select_path (selection, path);
298 }
299 else
300 {
301 gtk_tree_selection_unselect_path (selection, path);
302 }
303 }
304
305 if ((get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
306 && !button_event_modifies_selection (event))
307 {
308 if (button == GDK_BUTTON_PRIMARY)
309 {
310 activate_selected_items (view);
311 }
312 else if (button == GDK_BUTTON_MIDDLE)
313 {
314 activate_selected_items_alternate (view, NULL, TRUE);
315 }
316 }
317 gtk_tree_path_free (path);
318 }
319
320 static gboolean
on_motion_notify(GtkWidget * widget,GdkEvent * event,gpointer callback_data)321 on_motion_notify (GtkWidget *widget,
322 GdkEvent *event,
323 gpointer callback_data)
324 {
325 NautilusListView *view;
326 gdouble x;
327 gdouble y;
328
329 view = NAUTILUS_LIST_VIEW (callback_data);
330
331 /* Remove after switching to GTK+ 4. */
332 if (gdk_event_get_window (event) != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)))
333 {
334 return GDK_EVENT_PROPAGATE;
335 }
336
337 g_assert (gdk_event_get_coords (event, &x, &y));
338
339 if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
340 {
341 GtkTreePath *old_hover_path;
342
343 old_hover_path = view->details->hover_path;
344 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
345 x, y,
346 &view->details->hover_path,
347 NULL, NULL, NULL);
348
349 if ((old_hover_path != NULL) != (view->details->hover_path != NULL))
350 {
351 if (view->details->hover_path != NULL)
352 {
353 gdk_window_set_cursor (gtk_widget_get_window (widget), hand_cursor);
354 }
355 else
356 {
357 gdk_window_set_cursor (gtk_widget_get_window (widget), NULL);
358 }
359 }
360
361 if (old_hover_path != NULL)
362 {
363 gtk_tree_path_free (old_hover_path);
364 }
365 }
366
367 return GDK_EVENT_PROPAGATE;
368 }
369
370 static gboolean
on_leave_notify(GtkWidget * widget,GdkEvent * event,gpointer callback_data)371 on_leave_notify (GtkWidget *widget,
372 GdkEvent *event,
373 gpointer callback_data)
374 {
375 NautilusListView *view;
376
377 view = NAUTILUS_LIST_VIEW (callback_data);
378
379 if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE &&
380 view->details->hover_path != NULL)
381 {
382 gtk_tree_path_free (view->details->hover_path);
383 view->details->hover_path = NULL;
384 }
385
386 return GDK_EVENT_PROPAGATE;
387 }
388
389 static gboolean
on_enter_notify(GtkWidget * widget,GdkEvent * event,gpointer callback_data)390 on_enter_notify (GtkWidget *widget,
391 GdkEvent *event,
392 gpointer callback_data)
393 {
394 NautilusListView *view;
395
396 view = NAUTILUS_LIST_VIEW (callback_data);
397
398 if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
399 {
400 gdouble x;
401 gdouble y;
402
403 if (view->details->hover_path != NULL)
404 {
405 gtk_tree_path_free (view->details->hover_path);
406 }
407
408 g_assert (gdk_event_get_coords (event, &x, &y));
409
410 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
411 x, y,
412 &view->details->hover_path,
413 NULL, NULL, NULL);
414
415 if (view->details->hover_path != NULL)
416 {
417 gdk_window_set_cursor (gtk_widget_get_window (widget), hand_cursor);
418 }
419 }
420
421 return GDK_EVENT_PROPAGATE;
422 }
423
424 static void
row_activated_callback(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,NautilusListView * view)425 row_activated_callback (GtkTreeView *treeview,
426 GtkTreePath *path,
427 GtkTreeViewColumn *column,
428 NautilusListView *view)
429 {
430 activate_selected_items (view);
431 }
432
433 static gboolean
check_starred_status(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)434 check_starred_status (GtkTreeModel *model,
435 GtkTreePath *path,
436 GtkTreeIter *iter,
437 gpointer data)
438 {
439 NautilusFile *file;
440 GList *l;
441 GList *changed_files;
442
443 changed_files = data;
444
445 gtk_tree_model_get (GTK_TREE_MODEL (model),
446 iter,
447 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
448 -1);
449
450 if (!file)
451 {
452 return FALSE;
453 }
454
455 for (l = changed_files; l != NULL; l = l->next)
456 {
457 if (nautilus_file_compare_location (NAUTILUS_FILE (l->data), file) == 0)
458 {
459 gtk_tree_model_row_changed (model, path, iter);
460 }
461 }
462
463 nautilus_file_unref (file);
464
465 return FALSE;
466 }
467
468 static void
on_starred_files_changed(NautilusTagManager * tag_manager,GList * changed_files,gpointer user_data)469 on_starred_files_changed (NautilusTagManager *tag_manager,
470 GList *changed_files,
471 gpointer user_data)
472 {
473 NautilusListView *list_view;
474
475 list_view = NAUTILUS_LIST_VIEW (user_data);
476
477 gtk_tree_model_foreach (GTK_TREE_MODEL (list_view->details->model),
478 check_starred_status,
479 changed_files);
480 }
481
482 static void
on_star_cell_renderer_clicked(GtkTreePath * path,NautilusListView * list_view)483 on_star_cell_renderer_clicked (GtkTreePath *path,
484 NautilusListView *list_view)
485 {
486 NautilusListModel *list_model;
487 NautilusFile *file;
488 g_autofree gchar *uri = NULL;
489 GList *selection;
490
491 list_model = list_view->details->model;
492
493 file = nautilus_list_model_file_for_path (list_model, path);
494
495 if (file == NULL)
496 {
497 /* This row is a label, not a file */
498 return;
499 }
500
501 uri = nautilus_file_get_uri (file);
502 selection = g_list_prepend (NULL, file);
503
504 if (nautilus_tag_manager_file_is_starred (list_view->details->tag_manager, uri))
505 {
506 nautilus_tag_manager_unstar_files (list_view->details->tag_manager,
507 G_OBJECT (list_view),
508 selection,
509 NULL,
510 list_view->details->starred_cancellable);
511 }
512 else
513 {
514 nautilus_tag_manager_star_files (list_view->details->tag_manager,
515 G_OBJECT (list_view),
516 selection,
517 NULL,
518 list_view->details->starred_cancellable);
519 }
520
521 nautilus_file_list_free (selection);
522 }
523
524 static void
on_tree_view_multi_press_gesture_pressed(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,gpointer callback_data)525 on_tree_view_multi_press_gesture_pressed (GtkGestureMultiPress *gesture,
526 gint n_press,
527 gdouble x,
528 gdouble y,
529 gpointer callback_data)
530 {
531 NautilusListView *view;
532 GtkWidget *widget;
533 GtkTreeView *tree_view;
534 g_autoptr (GtkTreePath) path = NULL;
535 GtkTreeViewColumn *column;
536 GtkTreeSelection *selection;
537 GtkWidgetClass *tree_view_class;
538 guint button;
539 gint bin_x;
540 gint bin_y;
541 GdkEventSequence *sequence;
542 const GdkEvent *event;
543 gboolean call_parent, on_expander, show_expanders;
544 gboolean is_simple_click, path_selected;
545 NautilusFile *file;
546 gboolean on_star;
547
548 view = NAUTILUS_LIST_VIEW (callback_data);
549 widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
550 tree_view = GTK_TREE_VIEW (widget);
551 tree_view_class = GTK_WIDGET_GET_CLASS (tree_view);
552 selection = gtk_tree_view_get_selection (tree_view);
553 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
554
555 gtk_tree_view_convert_widget_to_bin_window_coords (tree_view, x, y, &bin_x, &bin_y);
556
557 view->details->last_event_button_x = bin_x;
558 view->details->last_event_button_y = bin_y;
559
560 /* Don't handle extra mouse buttons here */
561 if (button > 5)
562 {
563 return;
564 }
565
566 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
567 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
568
569 /* Remove after switching to GTK+ 4. */
570 if (gdk_event_get_window (event) != gtk_tree_view_get_bin_window (tree_view))
571 {
572 return;
573 }
574
575 nautilus_list_model_set_drag_view
576 (NAUTILUS_LIST_MODEL (gtk_tree_view_get_model (tree_view)),
577 tree_view,
578 bin_x, bin_y);
579
580 /* Ignore double click if we are in single click mode */
581 if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE && n_press >= 2)
582 {
583 return;
584 }
585
586 view->details->ignore_button_release = FALSE;
587 is_simple_click = ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE) && (n_press == 1));
588
589 /* No item at this position */
590 if (!gtk_tree_view_get_path_at_pos (tree_view, bin_x, bin_y,
591 &path, &column, NULL, NULL))
592 {
593 if (is_simple_click)
594 {
595 g_clear_pointer (&view->details->first_click_path, gtk_tree_path_free);
596 }
597
598 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view));
599
600 if (button == GDK_BUTTON_SECONDARY)
601 {
602 nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (view),
603 event);
604 }
605
606 return;
607 }
608
609 call_parent = TRUE;
610 on_expander = FALSE;
611 path_selected = gtk_tree_selection_path_is_selected (selection, path);
612 show_expanders = g_settings_get_boolean (nautilus_list_view_preferences,
613 NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE);
614
615 if (show_expanders)
616 {
617 GdkRectangle cell_area;
618
619 gtk_tree_view_get_cell_area (tree_view, path, column, &cell_area);
620
621 /* We assume that the cell area excludes the expander itself.
622 * Explanatory link for future reference:
623 * https://gitlab.gnome.org/GNOME/nautilus/merge_requests/97#note_58649 */
624
625 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
626 {
627 on_expander = bin_x > (cell_area.x + cell_area.width);
628 }
629 else
630 {
631 on_expander = bin_x < cell_area.x;
632 }
633 }
634
635 /* Keep track of path of last click so double clicks only happen
636 * on the same item */
637 if (is_simple_click)
638 {
639 g_clear_pointer (&view->details->first_click_path, gtk_tree_path_free);
640 view->details->first_click_path = gtk_tree_path_copy (path);
641 }
642
643 on_star = (g_hash_table_lookup (view->details->columns, "starred") == column &&
644 !gtk_tree_view_is_blank_at_pos (tree_view,
645 bin_x,
646 bin_y,
647 NULL,
648 NULL,
649 NULL,
650 NULL));
651
652 if (is_simple_click && on_star)
653 {
654 on_star_cell_renderer_clicked (path, view);
655 }
656 else if (n_press == 2 && !on_star)
657 {
658 /* Double clicking does not trigger a D&D action. */
659 view->details->drag_button = 0;
660
661 /* NOTE: Activation can actually destroy the view if we're switching */
662 if (!on_expander &&
663 view->details->first_click_path &&
664 gtk_tree_path_compare (path, view->details->first_click_path) == 0)
665 {
666 if ((button == GDK_BUTTON_PRIMARY) && button_event_modifies_selection (event))
667 {
668 file = nautilus_list_model_file_for_path (view->details->model, path);
669 if (file != NULL)
670 {
671 activate_selected_items_alternate (view, file, TRUE);
672 nautilus_file_unref (file);
673 }
674 }
675 else if ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_SECONDARY))
676 {
677 activate_selected_items (view);
678 }
679 }
680 else
681 {
682 return;
683 }
684 }
685 else
686 {
687 GdkModifierType state;
688 g_autoptr (GtkTreePath) cursor = NULL;
689 GList *selected_rows = NULL;
690
691 gdk_event_get_state (event, &state);
692
693 if (button == GDK_BUTTON_SECONDARY)
694 {
695 if (path_selected)
696 {
697 /* We're going to filter out some situations where
698 * we can't let the default code run because all
699 * but one row would be would be deselected. We don't
700 * want that; we want the right click menu or single
701 * click to apply to everything that's currently selected.
702 */
703 call_parent = FALSE;
704 }
705 else if ((state & GDK_CONTROL_MASK) != 0)
706 {
707 /* If CTRL is pressed, we don't allow the parent
708 * class to handle it, since GtkTreeView doesn't
709 * do it as intended currently.
710 */
711 call_parent = FALSE;
712 if ((state & GDK_SHIFT_MASK) != 0)
713 {
714 /* This is the CTRL+SHIFT selection mode which
715 * we handleourselves, as the parent class would
716 * otherwise do an unexpected selection.
717 */
718 gtk_tree_view_get_cursor (tree_view, &cursor, NULL);
719 if (cursor != NULL)
720 {
721 gtk_tree_selection_select_range (selection, cursor, path);
722 }
723 else
724 {
725 gtk_tree_selection_select_path (selection, path);
726 }
727 }
728 else
729 {
730 gtk_tree_selection_select_path (selection, path);
731 }
732 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
733
734 /* This unselects everything */
735 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
736
737 /* So select it again */
738 for (GList *l = selected_rows; l != NULL; l = l->next)
739 {
740 gtk_tree_selection_select_path (selection, l->data);
741 }
742 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
743 }
744 else if (on_expander)
745 {
746 /* If the right click happened on an expander, we should
747 * fully change the selection on that row solely.
748 */
749 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
750 }
751 }
752
753 if ((button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE) &&
754 ((state & GDK_CONTROL_MASK) != 0 || (state & GDK_SHIFT_MASK) == 0))
755 {
756 view->details->row_selected_on_button_down = path_selected;
757
758 if (path_selected)
759 {
760 call_parent = on_expander;
761 view->details->ignore_button_release = on_expander;
762 }
763 else if ((state & GDK_CONTROL_MASK) != 0)
764 {
765 call_parent = FALSE;
766 if ((state & GDK_SHIFT_MASK) != 0)
767 {
768 gtk_tree_view_get_cursor (tree_view, &cursor, NULL);
769 if (cursor != NULL)
770 {
771 gtk_tree_selection_select_range (selection, cursor, path);
772 }
773 else
774 {
775 gtk_tree_selection_select_path (selection, path);
776 }
777 }
778 else
779 {
780 gtk_tree_selection_select_path (selection, path);
781 }
782 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
783
784 /* This unselects everything */
785 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
786
787 /* So select it again */
788 for (GList *l = selected_rows; l != NULL; l = l->next)
789 {
790 gtk_tree_selection_select_path (selection, l->data);
791 }
792 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
793 }
794 else
795 {
796 view->details->ignore_button_release = on_expander;
797 }
798 }
799
800 if (is_simple_click && on_expander)
801 {
802 /* Need to let the event propagate down, since propagating up
803 * by chaining up to button_press_event() doesn’t expand the
804 * expander.
805 */
806 return;
807 }
808
809 /* Needed to select an item before popping up a menu. */
810 if (call_parent)
811 {
812 g_signal_handlers_block_by_func (tree_view, row_activated_callback, view);
813 /* GTK+ 4 TODO: replace with event(), at the very least. */
814 tree_view_class->button_press_event (widget, (GdkEventButton *) event);
815 g_signal_handlers_unblock_by_func (tree_view, row_activated_callback, view);
816 }
817 else if (path_selected)
818 {
819 gtk_widget_grab_focus (widget);
820 }
821
822 if (is_simple_click && !on_expander)
823 {
824 view->details->drag_started = FALSE;
825 view->details->drag_button = button;
826 view->details->drag_x = bin_x;
827 view->details->drag_y = bin_y;
828 }
829
830 if (button == GDK_BUTTON_SECONDARY)
831 {
832 nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (view),
833 event);
834 }
835
836 /* Don't open a new tab if we are in single click mode (this would open 2 tabs),
837 * or if CTRL or SHIFT is pressed.
838 */
839 if (button == GDK_BUTTON_MIDDLE &&
840 get_click_policy () != NAUTILUS_CLICK_POLICY_SINGLE &&
841 !button_event_modifies_selection (event))
842 {
843 gtk_tree_selection_unselect_all (selection);
844 gtk_tree_selection_select_path (selection, path);
845
846 activate_selected_items_alternate (view, NULL, TRUE);
847 }
848 }
849
850 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
851 }
852
853 static void
on_tree_view_multi_press_gesture_released(GtkGestureMultiPress * gesture,gint n_press,gdouble x,gdouble y,gpointer callback_data)854 on_tree_view_multi_press_gesture_released (GtkGestureMultiPress *gesture,
855 gint n_press,
856 gdouble x,
857 gdouble y,
858 gpointer callback_data)
859 {
860 NautilusListView *view;
861 guint button;
862
863 view = NAUTILUS_LIST_VIEW (callback_data);
864 button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
865 if (button != view->details->drag_button)
866 {
867 return;
868 }
869
870 view->details->drag_button = 0;
871 if (!view->details->drag_started && !view->details->ignore_button_release)
872 {
873 GdkEventSequence *sequence;
874 const GdkEvent *event;
875
876 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
877 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
878 /* Typically will only happen with GTK+ <= 3.22.30 and <= 3.93.0,
879 * where ::released is emitted after ::cancel, but can’t hurt to guard
880 * against it anyway.
881 */
882 if (event == NULL)
883 {
884 return;
885 }
886
887 nautilus_list_view_did_not_drag (view, event);
888 }
889 }
890
891 static gboolean
key_press_callback(GtkWidget * widget,GdkEvent * event,gpointer callback_data)892 key_press_callback (GtkWidget *widget,
893 GdkEvent *event,
894 gpointer callback_data)
895 {
896 NautilusFilesView *view;
897 GtkTreeView *tree_view;
898 guint keyval;
899 GdkModifierType state;
900
901 view = NAUTILUS_FILES_VIEW (callback_data);
902 tree_view = GTK_TREE_VIEW (widget);
903
904 NAUTILUS_LIST_VIEW (view)->details->last_event_button_x = -1;
905 NAUTILUS_LIST_VIEW (view)->details->last_event_button_y = -1;
906
907 if (G_UNLIKELY (!gdk_event_get_keyval (event, &keyval)))
908 {
909 g_return_val_if_reached (GDK_EVENT_PROPAGATE);
910 }
911 gdk_event_get_state (event, &state);
912
913 if (keyval == GDK_KEY_F10)
914 {
915 if ((state & GDK_CONTROL_MASK) != 0)
916 {
917 nautilus_files_view_pop_up_background_context_menu (view, NULL);
918
919 return GDK_EVENT_STOP;
920 }
921 }
922
923 if (keyval == GDK_KEY_Right)
924 {
925 g_autoptr (GtkTreePath) path = NULL;
926
927 gtk_tree_view_get_cursor (tree_view, &path, NULL);
928
929 if (path != NULL)
930 {
931 gtk_tree_view_expand_row (tree_view, path, FALSE);
932 }
933
934 return GDK_EVENT_STOP;
935 }
936
937 if (keyval == GDK_KEY_Left)
938 {
939 g_autoptr (GtkTreePath) path = NULL;
940
941 gtk_tree_view_get_cursor (tree_view, &path, NULL);
942
943 if (path != NULL && !gtk_tree_view_collapse_row (tree_view, path))
944 {
945 /* if the row is already collapsed or doesn't have any children,
946 * jump to the parent row instead.
947 */
948 if ((gtk_tree_path_get_depth (path) > 1) && gtk_tree_path_up (path))
949 {
950 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
951 }
952 }
953
954 return GDK_EVENT_STOP;
955 }
956
957 if (keyval == GDK_KEY_space)
958 {
959 if ((state & GDK_CONTROL_MASK) != 0)
960 {
961 return GDK_EVENT_PROPAGATE;
962 }
963
964 if (!gtk_widget_has_focus (GTK_WIDGET (NAUTILUS_LIST_VIEW (view)->details->tree_view)))
965 {
966 return GDK_EVENT_PROPAGATE;
967 }
968
969 if ((state & GDK_SHIFT_MASK) != 0)
970 {
971 activate_selected_items_alternate (NAUTILUS_LIST_VIEW (view), NULL, TRUE);
972 }
973 else
974 {
975 preview_selected_items (NAUTILUS_LIST_VIEW (view));
976 }
977
978 return GDK_EVENT_STOP;
979 }
980
981 if (keyval == GDK_KEY_v)
982 {
983 /* Eat Control + v to not enable type ahead */
984 if ((state & GDK_CONTROL_MASK) != 0)
985 {
986 return GDK_EVENT_STOP;
987 }
988 }
989
990 return GDK_EVENT_PROPAGATE;
991 }
992
993 static gboolean
on_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)994 on_event (GtkWidget *widget,
995 GdkEvent *event,
996 gpointer user_data)
997 {
998 GdkEventType event_type;
999
1000 event_type = gdk_event_get_event_type (event);
1001
1002 /* TODO: Replace motion events with motion controllers. */
1003 if (event_type == GDK_MOTION_NOTIFY)
1004 {
1005 return on_motion_notify (widget, event, user_data);
1006 }
1007 else if (event_type == GDK_ENTER_NOTIFY)
1008 {
1009 return on_enter_notify (widget, event, user_data);
1010 }
1011 else if (event_type == GDK_LEAVE_NOTIFY)
1012 {
1013 return on_leave_notify (widget, event, user_data);
1014 }
1015 else if (event_type == GDK_KEY_PRESS)
1016 {
1017 return key_press_callback (widget, event, user_data);
1018 }
1019
1020 return GDK_EVENT_PROPAGATE;
1021 }
1022
1023 static void
subdirectory_done_loading_callback(NautilusDirectory * directory,NautilusListView * view)1024 subdirectory_done_loading_callback (NautilusDirectory *directory,
1025 NautilusListView *view)
1026 {
1027 nautilus_list_model_subdirectory_done_loading (view->details->model, directory);
1028 }
1029
1030 static void
row_expanded_callback(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * path,gpointer callback_data)1031 row_expanded_callback (GtkTreeView *treeview,
1032 GtkTreeIter *iter,
1033 GtkTreePath *path,
1034 gpointer callback_data)
1035 {
1036 NautilusListView *view;
1037 NautilusDirectory *directory;
1038 char *uri;
1039
1040 view = NAUTILUS_LIST_VIEW (callback_data);
1041
1042 if (!nautilus_list_model_load_subdirectory (view->details->model, path, &directory))
1043 {
1044 return;
1045 }
1046
1047 uri = nautilus_directory_get_uri (directory);
1048 DEBUG ("Row expanded callback for URI %s", uri);
1049 g_free (uri);
1050
1051 nautilus_files_view_add_subdirectory (NAUTILUS_FILES_VIEW (view), directory);
1052
1053 if (nautilus_directory_are_all_files_seen (directory))
1054 {
1055 nautilus_list_model_subdirectory_done_loading (view->details->model,
1056 directory);
1057 }
1058 else
1059 {
1060 g_signal_connect_object (directory, "done-loading",
1061 G_CALLBACK (subdirectory_done_loading_callback),
1062 view, 0);
1063 }
1064
1065 nautilus_directory_unref (directory);
1066 }
1067
1068 typedef struct
1069 {
1070 NautilusFile *file;
1071 NautilusDirectory *directory;
1072 NautilusListView *view;
1073 } UnloadDelayData;
1074
1075 static void
unload_delay_data_free(UnloadDelayData * unload_data)1076 unload_delay_data_free (UnloadDelayData *unload_data)
1077 {
1078 if (unload_data->view != NULL)
1079 {
1080 g_object_remove_weak_pointer (G_OBJECT (unload_data->view),
1081 (gpointer *) &unload_data->view);
1082 }
1083
1084 nautilus_directory_unref (unload_data->directory);
1085 nautilus_file_unref (unload_data->file);
1086
1087 g_slice_free (UnloadDelayData, unload_data);
1088 }
1089
1090 static UnloadDelayData *
unload_delay_data_new(NautilusFile * file,NautilusDirectory * parent_directory,NautilusListView * view)1091 unload_delay_data_new (NautilusFile *file,
1092 NautilusDirectory *parent_directory,
1093 NautilusListView *view)
1094 {
1095 UnloadDelayData *unload_data;
1096
1097 unload_data = g_slice_new0 (UnloadDelayData);
1098 unload_data->view = view;
1099 unload_data->file = nautilus_file_ref (file);
1100 unload_data->directory = nautilus_directory_ref (parent_directory);
1101
1102 g_object_add_weak_pointer (G_OBJECT (unload_data->view),
1103 (gpointer *) &unload_data->view);
1104
1105 return unload_data;
1106 }
1107
1108 static gboolean
unload_file_timeout(gpointer data)1109 unload_file_timeout (gpointer data)
1110 {
1111 UnloadDelayData *unload_data = data;
1112 GtkTreeIter iter;
1113 NautilusListModel *model;
1114 GtkTreePath *path;
1115
1116 if (unload_data->view == NULL)
1117 {
1118 goto out;
1119 }
1120
1121 model = unload_data->view->details->model;
1122 if (nautilus_list_model_get_tree_iter_from_file (model,
1123 unload_data->file,
1124 unload_data->directory,
1125 &iter))
1126 {
1127 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1128 if (!gtk_tree_view_row_expanded (unload_data->view->details->tree_view,
1129 path))
1130 {
1131 nautilus_list_model_unload_subdirectory (model, &iter);
1132 }
1133 gtk_tree_path_free (path);
1134 }
1135
1136 out:
1137 unload_delay_data_free (unload_data);
1138 return FALSE;
1139 }
1140
1141 static void
row_collapsed_callback(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * path,gpointer callback_data)1142 row_collapsed_callback (GtkTreeView *treeview,
1143 GtkTreeIter *iter,
1144 GtkTreePath *path,
1145 gpointer callback_data)
1146 {
1147 NautilusListView *view;
1148 NautilusFile *file;
1149 NautilusDirectory *directory;
1150 GtkTreeIter parent;
1151 UnloadDelayData *unload_data;
1152 GtkTreeModel *model;
1153 char *uri;
1154
1155 view = NAUTILUS_LIST_VIEW (callback_data);
1156 model = GTK_TREE_MODEL (view->details->model);
1157
1158 gtk_tree_model_get (model, iter,
1159 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
1160 -1);
1161
1162 uri = nautilus_file_get_uri (file);
1163 DEBUG ("Row collapsed callback for uri %s", uri);
1164 g_free (uri);
1165
1166 directory = NULL;
1167 if (gtk_tree_model_iter_parent (model, &parent, iter))
1168 {
1169 gtk_tree_model_get (model, &parent,
1170 NAUTILUS_LIST_MODEL_SUBDIRECTORY_COLUMN, &directory,
1171 -1);
1172 }
1173
1174 unload_data = unload_delay_data_new (file, directory, view);
1175 g_timeout_add_seconds (COLLAPSE_TO_UNLOAD_DELAY,
1176 unload_file_timeout,
1177 unload_data);
1178
1179 nautilus_file_unref (file);
1180 nautilus_directory_unref (directory);
1181 }
1182
1183 static void
subdirectory_unloaded_callback(NautilusListModel * model,NautilusDirectory * directory,gpointer callback_data)1184 subdirectory_unloaded_callback (NautilusListModel *model,
1185 NautilusDirectory *directory,
1186 gpointer callback_data)
1187 {
1188 NautilusListView *view;
1189
1190 g_return_if_fail (NAUTILUS_IS_LIST_MODEL (model));
1191 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
1192
1193 view = NAUTILUS_LIST_VIEW (callback_data);
1194
1195 g_signal_handlers_disconnect_by_func (directory,
1196 G_CALLBACK (subdirectory_done_loading_callback),
1197 view);
1198 nautilus_files_view_remove_subdirectory (NAUTILUS_FILES_VIEW (view), directory);
1199 }
1200
1201 static gboolean
test_expand_row_callback(GtkTreeView * tree_view,GtkTreeIter * iter,GtkTreePath * path,gboolean user_data)1202 test_expand_row_callback (GtkTreeView *tree_view,
1203 GtkTreeIter *iter,
1204 GtkTreePath *path,
1205 gboolean user_data)
1206 {
1207 return !g_settings_get_boolean (nautilus_list_view_preferences,
1208 NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE);
1209 }
1210
1211 static void
nautilus_list_view_reveal_selection(NautilusFilesView * view)1212 nautilus_list_view_reveal_selection (NautilusFilesView *view)
1213 {
1214 g_autolist (NautilusFile) selection = NULL;
1215
1216 g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
1217
1218 selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1219
1220 /* Make sure at least one of the selected items is scrolled into view */
1221 if (selection != NULL)
1222 {
1223 NautilusListView *list_view;
1224 NautilusFile *file;
1225 GtkTreeIter iter;
1226 GtkTreePath *path;
1227
1228 list_view = NAUTILUS_LIST_VIEW (view);
1229 file = selection->data;
1230 if (nautilus_list_model_get_first_iter_for_file (list_view->details->model, file, &iter))
1231 {
1232 path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter);
1233
1234 gtk_tree_view_scroll_to_cell (list_view->details->tree_view, path, NULL, FALSE, 0.0, 0.0);
1235
1236 gtk_tree_path_free (path);
1237 }
1238 }
1239 }
1240
1241 static gboolean
sort_criterion_changes_due_to_user(GtkTreeView * tree_view)1242 sort_criterion_changes_due_to_user (GtkTreeView *tree_view)
1243 {
1244 GList *columns, *p;
1245 GtkTreeViewColumn *column;
1246 GSignalInvocationHint *ihint;
1247 gboolean ret;
1248
1249 ret = FALSE;
1250
1251 columns = gtk_tree_view_get_columns (tree_view);
1252 for (p = columns; p != NULL; p = p->next)
1253 {
1254 column = p->data;
1255 ihint = g_signal_get_invocation_hint (column);
1256 if (ihint != NULL)
1257 {
1258 ret = TRUE;
1259 break;
1260 }
1261 }
1262 g_list_free (columns);
1263
1264 return ret;
1265 }
1266
1267 static void
sort_column_changed_callback(GtkTreeSortable * sortable,NautilusListView * view)1268 sort_column_changed_callback (GtkTreeSortable *sortable,
1269 NautilusListView *view)
1270 {
1271 NautilusFile *file;
1272 gint sort_column_id, default_sort_column_id;
1273 GtkSortType reversed;
1274 GQuark sort_attr, default_sort_attr;
1275 char *reversed_attr, *default_reversed_attr;
1276 gboolean default_sort_reversed;
1277
1278 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view));
1279
1280 gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &reversed);
1281 sort_attr = nautilus_list_model_get_attribute_from_sort_column_id (view->details->model, sort_column_id);
1282
1283 default_sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (view->details->model,
1284 g_quark_from_string (get_default_sort_order (file, &default_sort_reversed)));
1285 default_sort_attr = nautilus_list_model_get_attribute_from_sort_column_id (view->details->model, default_sort_column_id);
1286 nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
1287 g_quark_to_string (default_sort_attr), g_quark_to_string (sort_attr));
1288
1289 default_reversed_attr = (default_sort_reversed ? "true" : "false");
1290
1291 if (view->details->last_sort_attr != sort_attr &&
1292 sort_criterion_changes_due_to_user (view->details->tree_view))
1293 {
1294 /* at this point, the sort order is always GTK_SORT_ASCENDING, if the sort column ID
1295 * switched. Invert the sort order, if it's the default criterion with a reversed preference,
1296 * or if it makes sense for the attribute (i.e. date). */
1297 if (sort_attr == default_sort_attr)
1298 {
1299 /* use value from preferences */
1300 reversed = g_settings_get_boolean (nautilus_preferences,
1301 NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER);
1302 }
1303 else
1304 {
1305 reversed = nautilus_file_is_date_sort_attribute_q (sort_attr);
1306 }
1307
1308 if (reversed)
1309 {
1310 g_signal_handlers_block_by_func (sortable, sort_column_changed_callback, view);
1311 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->details->model),
1312 sort_column_id,
1313 GTK_SORT_DESCENDING);
1314 g_signal_handlers_unblock_by_func (sortable, sort_column_changed_callback, view);
1315 }
1316 }
1317
1318
1319 reversed_attr = (reversed ? "true" : "false");
1320 nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
1321 default_reversed_attr, reversed_attr);
1322
1323 /* Make sure selected item(s) is visible after sort */
1324 nautilus_list_view_reveal_selection (NAUTILUS_FILES_VIEW (view));
1325
1326 view->details->last_sort_attr = sort_attr;
1327 }
1328
1329 static char *
get_root_uri_callback(NautilusTreeViewDragDest * dest,gpointer user_data)1330 get_root_uri_callback (NautilusTreeViewDragDest *dest,
1331 gpointer user_data)
1332 {
1333 NautilusListView *view;
1334
1335 view = NAUTILUS_LIST_VIEW (user_data);
1336
1337 return nautilus_files_view_get_uri (NAUTILUS_FILES_VIEW (view));
1338 }
1339
1340 static NautilusFile *
get_file_for_path_callback(NautilusTreeViewDragDest * dest,GtkTreePath * path,gpointer user_data)1341 get_file_for_path_callback (NautilusTreeViewDragDest *dest,
1342 GtkTreePath *path,
1343 gpointer user_data)
1344 {
1345 NautilusListView *view;
1346
1347 view = NAUTILUS_LIST_VIEW (user_data);
1348
1349 return nautilus_list_model_file_for_path (view->details->model, path);
1350 }
1351
1352
1353 static void
list_view_handle_uri_list(NautilusTreeViewDragDest * dest,const char * item_uris,const char * target_uri,GdkDragAction action,NautilusListView * view)1354 list_view_handle_uri_list (NautilusTreeViewDragDest *dest,
1355 const char *item_uris,
1356 const char *target_uri,
1357 GdkDragAction action,
1358 NautilusListView *view)
1359 {
1360 nautilus_files_view_handle_uri_list_drop (NAUTILUS_FILES_VIEW (view),
1361 item_uris, target_uri, action);
1362 }
1363
1364 static void
list_view_handle_text(NautilusTreeViewDragDest * dest,const char * text,const char * target_uri,GdkDragAction action,NautilusListView * view)1365 list_view_handle_text (NautilusTreeViewDragDest *dest,
1366 const char *text,
1367 const char *target_uri,
1368 GdkDragAction action,
1369 NautilusListView *view)
1370 {
1371 nautilus_files_view_handle_text_drop (NAUTILUS_FILES_VIEW (view),
1372 text, target_uri, action);
1373 }
1374
1375 static void
list_view_handle_raw(NautilusTreeViewDragDest * dest,const char * raw_data,int length,const char * target_uri,const char * direct_save_uri,GdkDragAction action,NautilusListView * view)1376 list_view_handle_raw (NautilusTreeViewDragDest *dest,
1377 const char *raw_data,
1378 int length,
1379 const char *target_uri,
1380 const char *direct_save_uri,
1381 GdkDragAction action,
1382 NautilusListView *view)
1383 {
1384 nautilus_files_view_handle_raw_drop (NAUTILUS_FILES_VIEW (view),
1385 raw_data, length, target_uri, direct_save_uri,
1386 action);
1387 }
1388
1389 static void
list_view_handle_hover(NautilusTreeViewDragDest * dest,const char * target_uri,NautilusListView * view)1390 list_view_handle_hover (NautilusTreeViewDragDest *dest,
1391 const char *target_uri,
1392 NautilusListView *view)
1393 {
1394 nautilus_files_view_handle_hover (NAUTILUS_FILES_VIEW (view), target_uri);
1395 }
1396
1397 static void
move_copy_items_callback(NautilusTreeViewDragDest * dest,const GList * item_uris,const char * target_uri,guint action,gpointer user_data)1398 move_copy_items_callback (NautilusTreeViewDragDest *dest,
1399 const GList *item_uris,
1400 const char *target_uri,
1401 guint action,
1402 gpointer user_data)
1403 {
1404 NautilusFilesView *view = user_data;
1405
1406 nautilus_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
1407 item_uris);
1408 nautilus_files_view_move_copy_items (view,
1409 item_uris,
1410 target_uri,
1411 action);
1412 }
1413
1414 static void
column_header_menu_toggled(GtkCheckMenuItem * menu_item,NautilusListView * list_view)1415 column_header_menu_toggled (GtkCheckMenuItem *menu_item,
1416 NautilusListView *list_view)
1417 {
1418 NautilusFile *file;
1419 char **visible_columns;
1420 char **column_order;
1421 const char *column;
1422 GList *list = NULL;
1423 GList *l;
1424 int i;
1425
1426 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
1427 visible_columns = get_visible_columns (list_view);
1428 column_order = get_column_order (list_view);
1429 column = g_object_get_data (G_OBJECT (menu_item), "column-name");
1430
1431 for (i = 0; visible_columns[i] != NULL; ++i)
1432 {
1433 list = g_list_prepend (list, visible_columns[i]);
1434 }
1435
1436 if (gtk_check_menu_item_get_active (menu_item))
1437 {
1438 list = g_list_prepend (list, g_strdup (column));
1439 }
1440 else
1441 {
1442 l = g_list_find_custom (list, column, (GCompareFunc) g_strcmp0);
1443 list = g_list_delete_link (list, l);
1444 }
1445
1446 list = g_list_reverse (list);
1447 nautilus_file_set_metadata_list (file,
1448 NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS,
1449 list);
1450
1451 g_free (visible_columns);
1452
1453 visible_columns = g_new0 (char *, g_list_length (list) + 1);
1454 for (i = 0, l = list; l != NULL; ++i, l = l->next)
1455 {
1456 visible_columns[i] = l->data;
1457 }
1458
1459 /* set view values ourselves, as new metadata could not have been
1460 * updated yet.
1461 */
1462 apply_columns_settings (list_view, column_order, visible_columns);
1463
1464 g_list_free (list);
1465 g_strfreev (column_order);
1466 g_strfreev (visible_columns);
1467 }
1468
1469 static void
column_header_menu_use_default(GtkMenuItem * menu_item,NautilusListView * list_view)1470 column_header_menu_use_default (GtkMenuItem *menu_item,
1471 NautilusListView *list_view)
1472 {
1473 NautilusFile *file;
1474 char **default_columns;
1475 char **default_order;
1476
1477 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
1478
1479 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NULL);
1480 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL);
1481
1482 default_columns = get_default_visible_columns (list_view);
1483 default_order = get_default_column_order (list_view);
1484
1485 /* set view values ourselves, as new metadata could not have been
1486 * updated yet.
1487 */
1488 apply_columns_settings (list_view, default_order, default_columns);
1489
1490 g_strfreev (default_columns);
1491 g_strfreev (default_order);
1492 }
1493
1494 static gboolean
on_column_header_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)1495 on_column_header_event (GtkWidget *widget,
1496 GdkEvent *event,
1497 gpointer user_data)
1498 {
1499 NautilusListView *list_view;
1500 guint button;
1501 NautilusFile *file;
1502 char **visible_columns;
1503 char **column_order;
1504 GList *all_columns;
1505 GHashTable *visible_columns_hash;
1506 int i;
1507 GList *l;
1508 GtkWidget *menu;
1509 GtkWidget *menu_item;
1510
1511 list_view = NAUTILUS_LIST_VIEW (user_data);
1512
1513 if (gdk_event_get_event_type (event) != GDK_BUTTON_PRESS)
1514 {
1515 return GDK_EVENT_PROPAGATE;
1516 }
1517
1518 g_assert (gdk_event_get_button (event, &button));
1519
1520 if (button != GDK_BUTTON_SECONDARY)
1521 {
1522 return GDK_EVENT_PROPAGATE;
1523 }
1524
1525 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
1526
1527 visible_columns = get_visible_columns (list_view);
1528 column_order = get_column_order (list_view);
1529
1530 all_columns = nautilus_get_columns_for_file (file);
1531 all_columns = nautilus_sort_columns (all_columns, column_order);
1532
1533 /* hash table to lookup if a given column should be visible */
1534 visible_columns_hash = g_hash_table_new_full (g_str_hash,
1535 g_str_equal,
1536 (GDestroyNotify) g_free,
1537 (GDestroyNotify) g_free);
1538 /* always show name column */
1539 g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name"));
1540 if (visible_columns != NULL)
1541 {
1542 for (i = 0; visible_columns[i] != NULL; ++i)
1543 {
1544 g_hash_table_insert (visible_columns_hash,
1545 g_ascii_strdown (visible_columns[i], -1),
1546 g_ascii_strdown (visible_columns[i], -1));
1547 }
1548 }
1549
1550 menu = gtk_menu_new ();
1551
1552 for (l = all_columns; l != NULL; l = l->next)
1553 {
1554 char *name;
1555 char *label;
1556 char *lowercase;
1557
1558 g_object_get (G_OBJECT (l->data),
1559 "name", &name,
1560 "label", &label,
1561 NULL);
1562 lowercase = g_ascii_strdown (name, -1);
1563
1564 menu_item = gtk_check_menu_item_new_with_label (label);
1565 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1566
1567 g_object_set_data_full (G_OBJECT (menu_item),
1568 "column-name", name, g_free);
1569
1570 /* name is always visible */
1571 if (strcmp (lowercase, "name") == 0)
1572 {
1573 gtk_widget_set_sensitive (menu_item, FALSE);
1574 }
1575
1576 if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL)
1577 {
1578 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
1579 TRUE);
1580 }
1581
1582 g_signal_connect (menu_item,
1583 "toggled",
1584 G_CALLBACK (column_header_menu_toggled),
1585 list_view);
1586
1587 g_free (lowercase);
1588 g_free (label);
1589 }
1590
1591 menu_item = gtk_separator_menu_item_new ();
1592 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1593
1594 menu_item = gtk_menu_item_new_with_label (_("Use Default"));
1595 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1596
1597 g_signal_connect (menu_item,
1598 "activate",
1599 G_CALLBACK (column_header_menu_use_default),
1600 list_view);
1601
1602 gtk_widget_show_all (menu);
1603 gtk_menu_popup_at_pointer (GTK_MENU (menu), event);
1604
1605 g_hash_table_destroy (visible_columns_hash);
1606 nautilus_column_list_free (all_columns);
1607 g_strfreev (column_order);
1608 g_strfreev (visible_columns);
1609
1610 return GDK_EVENT_STOP;
1611 }
1612
1613 static void
apply_columns_settings(NautilusListView * list_view,char ** column_order,char ** visible_columns)1614 apply_columns_settings (NautilusListView *list_view,
1615 char **column_order,
1616 char **visible_columns)
1617 {
1618 GList *all_columns;
1619 NautilusFile *file;
1620 GList *old_view_columns, *view_columns;
1621 GHashTable *visible_columns_hash;
1622 GtkTreeViewColumn *prev_view_column;
1623 GList *l;
1624 int i;
1625
1626 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
1627
1628 /* prepare ordered list of view columns using column_order and visible_columns */
1629 view_columns = NULL;
1630
1631 all_columns = nautilus_get_columns_for_file (file);
1632 all_columns = nautilus_sort_columns (all_columns, column_order);
1633
1634 /* hash table to lookup if a given column should be visible */
1635 visible_columns_hash = g_hash_table_new_full (g_str_hash,
1636 g_str_equal,
1637 (GDestroyNotify) g_free,
1638 (GDestroyNotify) g_free);
1639 /* always show name column */
1640 g_hash_table_insert (visible_columns_hash, g_strdup ("name"), g_strdup ("name"));
1641 if (visible_columns != NULL)
1642 {
1643 for (i = 0; visible_columns[i] != NULL; ++i)
1644 {
1645 g_hash_table_insert (visible_columns_hash,
1646 g_ascii_strdown (visible_columns[i], -1),
1647 g_ascii_strdown (visible_columns[i], -1));
1648 }
1649 }
1650
1651 for (l = all_columns; l != NULL; l = l->next)
1652 {
1653 char *name;
1654 char *lowercase;
1655
1656 g_object_get (G_OBJECT (l->data), "name", &name, NULL);
1657 lowercase = g_ascii_strdown (name, -1);
1658
1659 if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL)
1660 {
1661 GtkTreeViewColumn *view_column;
1662
1663 view_column = g_hash_table_lookup (list_view->details->columns, name);
1664 if (view_column != NULL)
1665 {
1666 view_columns = g_list_prepend (view_columns, view_column);
1667 }
1668 }
1669
1670 g_free (name);
1671 g_free (lowercase);
1672 }
1673
1674 g_hash_table_destroy (visible_columns_hash);
1675 nautilus_column_list_free (all_columns);
1676
1677 view_columns = g_list_reverse (view_columns);
1678
1679 /* hide columns that are not present in the configuration */
1680 old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
1681 for (l = old_view_columns; l != NULL; l = l->next)
1682 {
1683 if (g_list_find (view_columns, l->data) == NULL)
1684 {
1685 gtk_tree_view_column_set_visible (l->data, FALSE);
1686 }
1687 }
1688 g_list_free (old_view_columns);
1689
1690 /* show new columns from the configuration */
1691 for (l = view_columns; l != NULL; l = l->next)
1692 {
1693 gtk_tree_view_column_set_visible (l->data, TRUE);
1694 }
1695
1696 /* place columns in the correct order */
1697 prev_view_column = NULL;
1698 for (l = view_columns; l != NULL; l = l->next)
1699 {
1700 gtk_tree_view_move_column_after (list_view->details->tree_view, l->data, prev_view_column);
1701 prev_view_column = l->data;
1702 }
1703 g_list_free (view_columns);
1704 }
1705
1706 static void
starred_cell_data_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,NautilusListView * view)1707 starred_cell_data_func (GtkTreeViewColumn *column,
1708 GtkCellRenderer *renderer,
1709 GtkTreeModel *model,
1710 GtkTreeIter *iter,
1711 NautilusListView *view)
1712 {
1713 g_autofree gchar *text = NULL;
1714 g_autofree gchar *uri = NULL;
1715 NautilusFile *file;
1716
1717 gtk_tree_model_get (model, iter,
1718 view->details->file_name_column_num, &text,
1719 -1);
1720
1721 gtk_tree_model_get (GTK_TREE_MODEL (model),
1722 iter,
1723 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
1724 -1);
1725
1726 if (file == NULL)
1727 {
1728 /* This row is a label, not a file */
1729 g_object_set (renderer,
1730 "icon-name", NULL,
1731 "mode", GTK_CELL_RENDERER_MODE_INERT,
1732 NULL);
1733 return;
1734 }
1735
1736 uri = nautilus_file_get_uri (file);
1737
1738 if (nautilus_tag_manager_file_is_starred (view->details->tag_manager, uri))
1739 {
1740 g_object_set (renderer,
1741 "icon-name", "starred-symbolic",
1742 NULL);
1743 }
1744 else
1745 {
1746 g_object_set (renderer,
1747 "icon-name", "non-starred-symbolic",
1748 NULL);
1749 }
1750
1751 nautilus_file_unref (file);
1752 }
1753
1754 static void
filename_cell_data_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,NautilusListView * view)1755 filename_cell_data_func (GtkTreeViewColumn *column,
1756 GtkCellRenderer *renderer,
1757 GtkTreeModel *model,
1758 GtkTreeIter *iter,
1759 NautilusListView *view)
1760 {
1761 char *text;
1762 g_autofree gchar *escaped_text = NULL;
1763 g_autofree gchar *escaped_name = NULL;
1764 g_autofree gchar *replaced_text = NULL;
1765 GtkTreePath *path;
1766 PangoUnderline underline;
1767 GString *display_text;
1768 NautilusDirectory *directory;
1769 NautilusQuery *query = NULL;
1770 NautilusFile *file;
1771 const gchar *snippet;
1772
1773 gtk_tree_model_get (model, iter,
1774 view->details->file_name_column_num, &text,
1775 -1);
1776
1777 escaped_name = g_markup_escape_text (text, -1);
1778 display_text = g_string_new (escaped_name);
1779
1780 directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view));
1781
1782 if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
1783 {
1784 query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory));
1785 }
1786
1787 if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
1788 {
1789 path = gtk_tree_model_get_path (model, iter);
1790
1791 if (view->details->hover_path == NULL ||
1792 gtk_tree_path_compare (path, view->details->hover_path))
1793 {
1794 underline = PANGO_UNDERLINE_NONE;
1795 }
1796 else
1797 {
1798 underline = PANGO_UNDERLINE_SINGLE;
1799 }
1800
1801 gtk_tree_path_free (path);
1802 }
1803 else
1804 {
1805 underline = PANGO_UNDERLINE_NONE;
1806 }
1807
1808 if (query &&
1809 nautilus_query_get_search_content (query) == NAUTILUS_QUERY_SEARCH_CONTENT_FULL_TEXT)
1810 {
1811 gtk_tree_model_get (model, iter,
1812 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
1813 -1);
1814
1815 /* Rule out dummy row */
1816 if (file != NULL)
1817 {
1818 snippet = nautilus_file_get_search_fts_snippet (file);
1819 if (snippet)
1820 {
1821 replaced_text = g_regex_replace (view->details->regex,
1822 snippet,
1823 -1,
1824 0,
1825 " ",
1826 G_REGEX_MATCH_NEWLINE_ANY,
1827 NULL);
1828
1829 escaped_text = g_markup_escape_text (replaced_text, -1);
1830
1831 g_string_append_printf (display_text,
1832 " <small><span alpha='50%%'><b>%s</b></span></small>",
1833 escaped_text);
1834 }
1835 }
1836 nautilus_file_unref (file);
1837 }
1838
1839 g_object_set (G_OBJECT (renderer),
1840 "markup", display_text->str,
1841 "underline", underline,
1842 NULL);
1843
1844 g_free (text);
1845 g_string_free (display_text, TRUE);
1846 }
1847
1848 static void
location_cell_data_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,NautilusListView * view,gboolean show_trash_orig)1849 location_cell_data_func (GtkTreeViewColumn *column,
1850 GtkCellRenderer *renderer,
1851 GtkTreeModel *model,
1852 GtkTreeIter *iter,
1853 NautilusListView *view,
1854 gboolean show_trash_orig)
1855 {
1856 NautilusDirectory *directory;
1857 GFile *home_location;
1858 NautilusFile *file;
1859 GFile *dir_location;
1860 GFile *base_location;
1861 gchar *where = NULL;
1862
1863 directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (view));
1864
1865 home_location = g_file_new_for_path (g_get_home_dir ());
1866
1867 gtk_tree_model_get (model, iter,
1868 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
1869 -1);
1870
1871 /* The file might be NULL if we just toggled an expander
1872 * and we're still loading the subdirectory.
1873 */
1874 if (file == NULL)
1875 {
1876 return;
1877 }
1878
1879 if (show_trash_orig && nautilus_file_is_in_trash (file))
1880 {
1881 NautilusFile *orig_file;
1882
1883 orig_file = nautilus_file_get_trash_original_file (file);
1884
1885 if (orig_file != NULL)
1886 {
1887 nautilus_file_unref (file);
1888 file = orig_file;
1889 }
1890 }
1891
1892 if (!nautilus_file_is_in_recent (file))
1893 {
1894 dir_location = nautilus_file_get_parent_location (file);
1895 }
1896 else
1897 {
1898 GFile *activation_location;
1899
1900 activation_location = nautilus_file_get_activation_location (file);
1901 dir_location = g_file_get_parent (activation_location);
1902
1903 g_object_unref (activation_location);
1904 }
1905
1906 if (!NAUTILUS_IS_SEARCH_DIRECTORY (directory))
1907 {
1908 base_location = g_object_ref (home_location);
1909 }
1910 else
1911 {
1912 NautilusQuery *query;
1913 NautilusFile *base;
1914 GFile *location;
1915
1916 query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory));
1917 location = nautilus_query_get_location (query);
1918 base = nautilus_file_get (location);
1919
1920 if (!nautilus_file_is_in_recent (base))
1921 {
1922 base_location = nautilus_file_get_location (base);
1923 }
1924 else
1925 {
1926 base_location = g_object_ref (home_location);
1927 }
1928
1929 nautilus_file_unref (base);
1930 g_object_unref (location);
1931 g_object_unref (query);
1932 }
1933
1934 if (g_file_equal (base_location, dir_location))
1935 {
1936 /* Only occurs when search result is
1937 * a direct child of the base location
1938 */
1939 where = g_strdup ("");
1940 }
1941 else if (g_file_equal (home_location, dir_location))
1942 {
1943 where = g_strdup (_("Home"));
1944 }
1945 else if (g_file_has_prefix (dir_location, base_location))
1946 {
1947 gchar *relative_path;
1948
1949 relative_path = g_file_get_relative_path (base_location, dir_location);
1950 where = g_filename_display_name (relative_path);
1951
1952 g_free (relative_path);
1953 }
1954 else
1955 {
1956 where = g_file_get_path (dir_location);
1957 }
1958
1959 g_object_set (G_OBJECT (renderer),
1960 "text", where,
1961 NULL);
1962
1963 g_free (where);
1964
1965 g_object_unref (base_location);
1966 g_object_unref (dir_location);
1967 nautilus_file_unref (file);
1968 g_object_unref (home_location);
1969 }
1970
1971
1972 static void
where_cell_data_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,NautilusListView * view)1973 where_cell_data_func (GtkTreeViewColumn *column,
1974 GtkCellRenderer *renderer,
1975 GtkTreeModel *model,
1976 GtkTreeIter *iter,
1977 NautilusListView *view)
1978 {
1979 location_cell_data_func (column, renderer, model, iter, view, FALSE);
1980 }
1981
1982 static void
trash_orig_path_cell_data_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,NautilusListView * view)1983 trash_orig_path_cell_data_func (GtkTreeViewColumn *column,
1984 GtkCellRenderer *renderer,
1985 GtkTreeModel *model,
1986 GtkTreeIter *iter,
1987 NautilusListView *view)
1988 {
1989 location_cell_data_func (column, renderer, model, iter, view, TRUE);
1990 }
1991
1992 #define SMALL_ZOOM_ICON_PADDING 0
1993 #define STANDARD_ZOOM_ICON_PADDING 6
1994 #define LARGE_ZOOM_ICON_PADDING 6
1995 #define LARGER_ZOOM_ICON_PADDING 6
1996
1997 static gint
nautilus_list_view_get_icon_padding_for_zoom_level(NautilusListZoomLevel zoom_level)1998 nautilus_list_view_get_icon_padding_for_zoom_level (NautilusListZoomLevel zoom_level)
1999 {
2000 switch (zoom_level)
2001 {
2002 case NAUTILUS_LIST_ZOOM_LEVEL_SMALL:
2003 {
2004 return SMALL_ZOOM_ICON_PADDING;
2005 }
2006
2007 case NAUTILUS_LIST_ZOOM_LEVEL_STANDARD:
2008 {
2009 return STANDARD_ZOOM_ICON_PADDING;
2010 }
2011
2012 case NAUTILUS_LIST_ZOOM_LEVEL_LARGE:
2013 {
2014 return LARGE_ZOOM_ICON_PADDING;
2015 }
2016
2017 case NAUTILUS_LIST_ZOOM_LEVEL_LARGER:
2018 {
2019 return LARGER_ZOOM_ICON_PADDING;
2020 }
2021
2022 default:
2023 {
2024 g_assert_not_reached ();
2025 }
2026 }
2027 }
2028
2029 static void
set_up_pixbuf_size(NautilusListView * view)2030 set_up_pixbuf_size (NautilusListView *view)
2031 {
2032 int icon_size, icon_padding;
2033
2034 /* Make all rows the same size. */
2035 icon_size = nautilus_list_model_get_icon_size_for_zoom_level (view->details->zoom_level);
2036 icon_padding = nautilus_list_view_get_icon_padding_for_zoom_level (view->details->zoom_level);
2037 gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (view->details->pixbuf_cell),
2038 -1, icon_size + 2 * icon_padding);
2039
2040 /* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=641518 */
2041 gtk_tree_view_columns_autosize (view->details->tree_view);
2042 }
2043
2044 static gint
get_icon_scale_callback(NautilusListModel * model,NautilusListView * view)2045 get_icon_scale_callback (NautilusListModel *model,
2046 NautilusListView *view)
2047 {
2048 return gtk_widget_get_scale_factor (GTK_WIDGET (view->details->tree_view));
2049 }
2050
2051 static void
on_longpress_gesture_pressed_event(GtkGestureLongPress * gesture,gdouble x,gdouble y,gpointer user_data)2052 on_longpress_gesture_pressed_event (GtkGestureLongPress *gesture,
2053 gdouble x,
2054 gdouble y,
2055 gpointer user_data)
2056 {
2057 GdkEventSequence *event_sequence;
2058 const GdkEvent *event;
2059 NautilusListView *view = user_data;
2060 g_autolist (NautilusFile) selection = NULL;
2061
2062 event_sequence = gtk_gesture_get_last_updated_sequence (GTK_GESTURE (gesture));
2063 if (event_sequence == NULL)
2064 {
2065 return;
2066 }
2067
2068 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), event_sequence);
2069
2070 selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
2071 if (selection != NULL)
2072 {
2073 nautilus_files_view_pop_up_selection_context_menu (NAUTILUS_FILES_VIEW (view), event);
2074 }
2075 else
2076 {
2077 nautilus_files_view_pop_up_background_context_menu (NAUTILUS_FILES_VIEW (view), event);
2078 }
2079 }
2080
2081 static void
on_tree_view_drag_gesture_drag_begin(GtkGestureDrag * gesture,gdouble start_x,gdouble start_y,gpointer user_data)2082 on_tree_view_drag_gesture_drag_begin (GtkGestureDrag *gesture,
2083 gdouble start_x,
2084 gdouble start_y,
2085 gpointer user_data)
2086 {
2087 nautilus_list_view_dnd_init (NAUTILUS_LIST_VIEW (user_data));
2088 }
2089
2090 static void
on_tree_view_drag_gesture_drag_update(GtkGestureDrag * gesture,gdouble offset_x,gdouble offset_y,gpointer user_data)2091 on_tree_view_drag_gesture_drag_update (GtkGestureDrag *gesture,
2092 gdouble offset_x,
2093 gdouble offset_y,
2094 gpointer user_data)
2095 {
2096 GdkEventSequence *sequence;
2097 const GdkEvent *event;
2098 NautilusListView *list_view;
2099
2100 sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
2101 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
2102 list_view = NAUTILUS_LIST_VIEW (user_data);
2103
2104 nautilus_list_view_dnd_drag_begin (list_view, offset_x, offset_y, event);
2105 }
2106
2107 static void
list_view_use_tree_changed_callback(gpointer callback_data)2108 list_view_use_tree_changed_callback (gpointer callback_data)
2109 {
2110 GtkTreeView *tree_view;
2111
2112 tree_view = GTK_TREE_VIEW (callback_data);
2113
2114 gtk_tree_view_collapse_all (tree_view);
2115 }
2116
2117
2118 static void
create_and_set_up_tree_view(NautilusListView * view)2119 create_and_set_up_tree_view (NautilusListView *view)
2120 {
2121 GtkCellRenderer *cell;
2122 GtkTreeViewColumn *column;
2123 AtkObject *atk_obj;
2124 GList *nautilus_columns;
2125 GList *l;
2126 gchar **default_column_order, **default_visible_columns;
2127 GtkWidget *content_widget;
2128 GtkGesture *longpress_gesture;
2129
2130 content_widget = nautilus_files_view_get_content_widget (NAUTILUS_FILES_VIEW (view));
2131 view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
2132 view->details->columns = g_hash_table_new_full (g_str_hash,
2133 g_str_equal,
2134 (GDestroyNotify) g_free,
2135 NULL);
2136 gtk_tree_view_set_enable_search (view->details->tree_view, FALSE);
2137
2138 view->details->drag_dest =
2139 nautilus_tree_view_drag_dest_new (view->details->tree_view);
2140
2141 /* Stop the tree view from performing select-all actions.
2142 * It is desireable that the action is disabled while directory
2143 * is loading.
2144 */
2145 g_signal_connect (view->details->tree_view, "select-all",
2146 G_CALLBACK (g_signal_stop_emission_by_name), "select-all");
2147
2148 g_signal_connect_object (view->details->drag_dest,
2149 "get-root-uri",
2150 G_CALLBACK (get_root_uri_callback),
2151 view, 0);
2152 g_signal_connect_object (view->details->drag_dest,
2153 "get-file-for-path",
2154 G_CALLBACK (get_file_for_path_callback),
2155 view, 0);
2156 g_signal_connect_object (view->details->drag_dest,
2157 "move-copy-items",
2158 G_CALLBACK (move_copy_items_callback),
2159 view, 0);
2160 g_signal_connect_object (view->details->drag_dest, "handle-uri-list",
2161 G_CALLBACK (list_view_handle_uri_list), view, 0);
2162 g_signal_connect_object (view->details->drag_dest, "handle-text",
2163 G_CALLBACK (list_view_handle_text), view, 0);
2164 g_signal_connect_object (view->details->drag_dest, "handle-raw",
2165 G_CALLBACK (list_view_handle_raw), view, 0);
2166 g_signal_connect_object (view->details->drag_dest, "handle-hover",
2167 G_CALLBACK (list_view_handle_hover), view, 0);
2168
2169 g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view),
2170 "changed",
2171 G_CALLBACK (list_selection_changed_callback), view, 0);
2172
2173 view->details->tree_view_drag_gesture = gtk_gesture_drag_new (GTK_WIDGET (view->details->tree_view));
2174
2175 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (view->details->tree_view_drag_gesture),
2176 GTK_PHASE_CAPTURE);
2177 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (view->details->tree_view_drag_gesture), 0);
2178
2179 g_signal_connect (view->details->tree_view_drag_gesture, "drag-begin",
2180 G_CALLBACK (on_tree_view_drag_gesture_drag_begin), view);
2181 g_signal_connect (view->details->tree_view_drag_gesture, "drag-update",
2182 G_CALLBACK (on_tree_view_drag_gesture_drag_update), view);
2183
2184 view->details->tree_view_multi_press_gesture = gtk_gesture_multi_press_new (GTK_WIDGET (view->details->tree_view));
2185
2186 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (view->details->tree_view_multi_press_gesture),
2187 GTK_PHASE_CAPTURE);
2188 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (view->details->tree_view_multi_press_gesture), 0);
2189
2190 g_signal_connect (view->details->tree_view_multi_press_gesture, "pressed",
2191 G_CALLBACK (on_tree_view_multi_press_gesture_pressed), view);
2192 g_signal_connect (view->details->tree_view_multi_press_gesture, "released",
2193 G_CALLBACK (on_tree_view_multi_press_gesture_released), view);
2194
2195 g_signal_connect_object (view->details->tree_view, "event",
2196 G_CALLBACK (on_event), view, 0);
2197 g_signal_connect_object (view->details->tree_view, "test-expand-row",
2198 G_CALLBACK (test_expand_row_callback), view, 0);
2199 g_signal_connect_object (view->details->tree_view, "row-expanded",
2200 G_CALLBACK (row_expanded_callback), view, 0);
2201 g_signal_connect_object (view->details->tree_view, "row-collapsed",
2202 G_CALLBACK (row_collapsed_callback), view, 0);
2203 g_signal_connect_object (view->details->tree_view, "row-activated",
2204 G_CALLBACK (row_activated_callback), view, 0);
2205
2206 g_signal_connect_object (nautilus_list_view_preferences,
2207 "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE,
2208 G_CALLBACK (list_view_use_tree_changed_callback),
2209 view->details->tree_view,
2210 G_CONNECT_SWAPPED);
2211
2212 view->details->model = g_object_new (NAUTILUS_TYPE_LIST_MODEL, NULL);
2213 gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model));
2214 /* Need the model for the dnd drop icon "accept" change */
2215 nautilus_list_model_set_drag_view (NAUTILUS_LIST_MODEL (view->details->model),
2216 view->details->tree_view, 0, 0);
2217
2218 g_signal_connect_object (view->details->model, "sort-column-changed",
2219 G_CALLBACK (sort_column_changed_callback), view, 0);
2220
2221 g_signal_connect_object (view->details->model, "subdirectory-unloaded",
2222 G_CALLBACK (subdirectory_unloaded_callback), view, 0);
2223
2224 g_signal_connect_object (view->details->model, "get-icon-scale",
2225 G_CALLBACK (get_icon_scale_callback), view, 0);
2226
2227 longpress_gesture = gtk_gesture_long_press_new (GTK_WIDGET (content_widget));
2228 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (longpress_gesture),
2229 GTK_PHASE_CAPTURE);
2230 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (longpress_gesture),
2231 TRUE);
2232 g_signal_connect (longpress_gesture,
2233 "pressed",
2234 (GCallback) on_longpress_gesture_pressed_event,
2235 view);
2236
2237 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view->details->tree_view), GTK_SELECTION_MULTIPLE);
2238
2239 g_settings_bind (nautilus_list_view_preferences, NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE,
2240 view->details->tree_view, "show-expanders",
2241 G_SETTINGS_BIND_DEFAULT);
2242
2243 nautilus_columns = nautilus_get_all_columns ();
2244
2245 for (l = nautilus_columns; l != NULL; l = l->next)
2246 {
2247 NautilusColumn *nautilus_column;
2248 int column_num;
2249 char *name;
2250 char *label;
2251 float xalign;
2252 GtkSortType sort_order;
2253
2254 nautilus_column = NAUTILUS_COLUMN (l->data);
2255
2256 g_object_get (nautilus_column,
2257 "name", &name,
2258 "label", &label,
2259 "xalign", &xalign,
2260 "default-sort-order", &sort_order,
2261 NULL);
2262
2263 column_num = nautilus_list_model_add_column (view->details->model,
2264 nautilus_column);
2265
2266 /* Created the name column specially, because it
2267 * has the icon in it.*/
2268 if (!strcmp (name, "name"))
2269 {
2270 /* Create the file name column */
2271 view->details->file_name_column = gtk_tree_view_column_new ();
2272 gtk_tree_view_append_column (view->details->tree_view,
2273 view->details->file_name_column);
2274 view->details->file_name_column_num = column_num;
2275
2276 g_hash_table_insert (view->details->columns,
2277 g_strdup ("name"),
2278 view->details->file_name_column);
2279
2280 g_signal_connect (gtk_tree_view_column_get_button (view->details->file_name_column),
2281 "event",
2282 G_CALLBACK (on_column_header_event),
2283 view);
2284
2285 gtk_tree_view_set_search_column (view->details->tree_view, column_num);
2286
2287 gtk_tree_view_column_set_sort_column_id (view->details->file_name_column, column_num);
2288 gtk_tree_view_column_set_title (view->details->file_name_column, _("Name"));
2289 gtk_tree_view_column_set_resizable (view->details->file_name_column, TRUE);
2290 gtk_tree_view_column_set_expand (view->details->file_name_column, TRUE);
2291
2292 /* Initial padding */
2293 cell = gtk_cell_renderer_text_new ();
2294 gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE);
2295 g_object_set (cell, "xpad", 6, NULL);
2296 g_settings_bind (nautilus_list_view_preferences, NAUTILUS_PREFERENCES_LIST_VIEW_USE_TREE,
2297 cell, "visible",
2298 G_SETTINGS_BIND_INVERT_BOOLEAN | G_SETTINGS_BIND_GET);
2299
2300 /* File icon */
2301 cell = gtk_cell_renderer_pixbuf_new ();
2302 view->details->pixbuf_cell = (GtkCellRendererPixbuf *) cell;
2303 set_up_pixbuf_size (view);
2304
2305 gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE);
2306 gtk_tree_view_column_set_attributes (view->details->file_name_column,
2307 cell,
2308 "surface", nautilus_list_model_get_column_id_from_zoom_level (view->details->zoom_level),
2309 NULL);
2310
2311 cell = gtk_cell_renderer_text_new ();
2312 view->details->file_name_cell = (GtkCellRendererText *) cell;
2313 g_object_set (cell,
2314 "ellipsize", PANGO_ELLIPSIZE_END,
2315 "single-paragraph-mode", FALSE,
2316 "width-chars", 30,
2317 "xpad", 5,
2318 NULL);
2319
2320 gtk_tree_view_column_pack_start (view->details->file_name_column, cell, TRUE);
2321 gtk_tree_view_column_set_cell_data_func (view->details->file_name_column, cell,
2322 (GtkTreeCellDataFunc) filename_cell_data_func,
2323 view, NULL);
2324 }
2325 else
2326 {
2327 if (g_strcmp0 (name, "starred") == 0)
2328 {
2329 cell = gtk_cell_renderer_pixbuf_new ();
2330 g_object_set (cell,
2331 "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
2332 NULL);
2333
2334 column = gtk_tree_view_column_new_with_attributes ("",
2335 cell,
2336 NULL);
2337 gtk_tree_view_column_set_fixed_width (column, STAR_COLUMN_WIDTH);
2338 }
2339 else
2340 {
2341 /* We need to use libgd */
2342 cell = gd_styled_text_renderer_new ();
2343 /* FIXME: should be just dim-label.
2344 * See https://bugzilla.gnome.org/show_bug.cgi?id=744397
2345 */
2346 gd_styled_text_renderer_add_class (GD_STYLED_TEXT_RENDERER (cell),
2347 "nautilus-list-dim-label");
2348
2349 column = gtk_tree_view_column_new_with_attributes (label,
2350 cell,
2351 "text", column_num,
2352 NULL);
2353 }
2354
2355 gtk_tree_view_column_set_alignment (column, xalign);
2356 g_object_set (cell,
2357 "xalign", xalign,
2358 "xpad", 5,
2359 NULL);
2360 if (!strcmp (name, "permissions"))
2361 {
2362 g_object_set (cell,
2363 "family", "Monospace",
2364 NULL);
2365 }
2366 view->details->cells = g_list_append (view->details->cells,
2367 cell);
2368
2369 gtk_tree_view_append_column (view->details->tree_view, column);
2370 gtk_tree_view_column_set_sort_column_id (column, column_num);
2371 g_hash_table_insert (view->details->columns,
2372 g_strdup (name),
2373 column);
2374
2375 g_signal_connect (gtk_tree_view_column_get_button (column),
2376 "event",
2377 G_CALLBACK (on_column_header_event),
2378 view);
2379
2380 gtk_tree_view_column_set_resizable (column, TRUE);
2381 gtk_tree_view_column_set_sort_order (column, sort_order);
2382
2383 if (!strcmp (name, "where"))
2384 {
2385 gtk_tree_view_column_set_cell_data_func (column, cell,
2386 (GtkTreeCellDataFunc) where_cell_data_func,
2387 view, NULL);
2388 }
2389 else if (!strcmp (name, "trash_orig_path"))
2390 {
2391 gtk_tree_view_column_set_cell_data_func (column, cell,
2392 (GtkTreeCellDataFunc) trash_orig_path_cell_data_func,
2393 view, NULL);
2394 }
2395 else if (!strcmp (name, "starred"))
2396 {
2397 gtk_tree_view_column_set_cell_data_func (column, cell,
2398 (GtkTreeCellDataFunc) starred_cell_data_func,
2399 view, NULL);
2400 }
2401 }
2402 g_free (name);
2403 g_free (label);
2404 }
2405 nautilus_column_list_free (nautilus_columns);
2406
2407 default_visible_columns = g_settings_get_strv (nautilus_list_view_preferences,
2408 NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS);
2409 default_column_order = g_settings_get_strv (nautilus_list_view_preferences,
2410 NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER);
2411
2412 /* Apply the default column order and visible columns, to get it
2413 * right most of the time. The metadata will be checked when a
2414 * folder is loaded */
2415 apply_columns_settings (view,
2416 default_column_order,
2417 default_visible_columns);
2418
2419 gtk_widget_show (GTK_WIDGET (view->details->tree_view));
2420 gtk_container_add (GTK_CONTAINER (content_widget), GTK_WIDGET (view->details->tree_view));
2421
2422 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (view->details->tree_view));
2423 atk_object_set_name (atk_obj, _("List View"));
2424
2425 g_strfreev (default_visible_columns);
2426 g_strfreev (default_column_order);
2427 }
2428
2429 static void
nautilus_list_view_add_files(NautilusFilesView * view,GList * files)2430 nautilus_list_view_add_files (NautilusFilesView *view,
2431 GList *files)
2432 {
2433 NautilusListModel *model;
2434 GList *l;
2435
2436 model = NAUTILUS_LIST_VIEW (view)->details->model;
2437 for (l = files; l != NULL; l = l->next)
2438 {
2439 NautilusFile *parent;
2440 NautilusDirectory *directory;
2441
2442 parent = nautilus_file_get_parent (NAUTILUS_FILE (l->data));
2443 directory = nautilus_directory_get_for_file (parent);
2444 nautilus_list_model_add_file (model, NAUTILUS_FILE (l->data), directory);
2445
2446 nautilus_file_unref (parent);
2447 nautilus_directory_unref (directory);
2448 }
2449 }
2450
2451 static char **
get_default_visible_columns(NautilusListView * list_view)2452 get_default_visible_columns (NautilusListView *list_view)
2453 {
2454 NautilusFile *file;
2455 NautilusDirectory *directory;
2456
2457 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
2458
2459 if (nautilus_file_is_in_trash (file))
2460 {
2461 return g_strdupv ((gchar **) default_trash_visible_columns);
2462 }
2463
2464 if (nautilus_file_is_in_recent (file))
2465 {
2466 return g_strdupv ((gchar **) default_recent_visible_columns);
2467 }
2468
2469 directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (list_view));
2470 if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
2471 {
2472 return g_strdupv ((gchar **) default_search_visible_columns);
2473 }
2474
2475 return g_settings_get_strv (nautilus_list_view_preferences,
2476 NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS);
2477 }
2478
2479 static GList *
get_default_visible_columns_as_list(NautilusListView * list_view)2480 get_default_visible_columns_as_list (NautilusListView *list_view)
2481 {
2482 GList *res = NULL;
2483 gint i = 0;
2484 gchar **array;
2485
2486 array = get_default_visible_columns (list_view);
2487
2488 while (array[i] != NULL)
2489 {
2490 res = g_list_prepend (res, array[i]);
2491 i++;
2492 }
2493
2494 g_free (array);
2495
2496 return res;
2497 }
2498
2499 static char **
get_visible_columns(NautilusListView * list_view)2500 get_visible_columns (NautilusListView *list_view)
2501 {
2502 NautilusFile *file;
2503 g_autoptr (GList) visible_columns = NULL;
2504 g_autoptr (GFile) location = NULL;
2505 GPtrArray *res;
2506 GList *l;
2507 g_autofree gchar *uri = NULL;
2508 gboolean can_star_current_directory;
2509 gboolean is_starred;
2510
2511 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
2512 uri = nautilus_file_get_uri (file);
2513
2514 location = g_file_new_for_uri (uri);
2515 can_star_current_directory = nautilus_tag_manager_can_star_contents (list_view->details->tag_manager,
2516 location);
2517 is_starred = eel_uri_is_starred (uri);
2518
2519 visible_columns = nautilus_file_get_metadata_list (file,
2520 NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS);
2521 if (visible_columns == NULL)
2522 {
2523 visible_columns = get_default_visible_columns_as_list (list_view);
2524 }
2525
2526 res = g_ptr_array_new ();
2527 for (l = visible_columns; l != NULL; l = l->next)
2528 {
2529 if (g_strcmp0 (l->data, "starred") != 0 ||
2530 (g_strcmp0 (l->data, "starred") == 0 && (can_star_current_directory || is_starred)))
2531 {
2532 g_ptr_array_add (res, l->data);
2533 }
2534 }
2535
2536 g_ptr_array_add (res, NULL);
2537
2538 return (char **) g_ptr_array_free (res, FALSE);
2539 }
2540
2541 static char **
get_default_column_order(NautilusListView * list_view)2542 get_default_column_order (NautilusListView *list_view)
2543 {
2544 NautilusFile *file;
2545 NautilusDirectory *directory;
2546
2547 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
2548
2549 if (nautilus_file_is_in_trash (file))
2550 {
2551 return g_strdupv ((gchar **) default_trash_columns_order);
2552 }
2553
2554 if (nautilus_file_is_in_recent (file))
2555 {
2556 return g_strdupv ((gchar **) default_recent_columns_order);
2557 }
2558
2559 directory = nautilus_files_view_get_model (NAUTILUS_FILES_VIEW (list_view));
2560 if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
2561 {
2562 return g_strdupv ((gchar **) default_search_columns_order);
2563 }
2564
2565 return g_settings_get_strv (nautilus_list_view_preferences,
2566 NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER);
2567 }
2568
2569 static char **
get_column_order(NautilusListView * list_view)2570 get_column_order (NautilusListView *list_view)
2571 {
2572 NautilusFile *file;
2573 GList *column_order;
2574
2575 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
2576
2577 column_order = nautilus_file_get_metadata_list
2578 (file,
2579 NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER);
2580
2581 if (column_order)
2582 {
2583 GPtrArray *res;
2584 GList *l;
2585
2586 res = g_ptr_array_new ();
2587 for (l = column_order; l != NULL; l = l->next)
2588 {
2589 g_ptr_array_add (res, l->data);
2590 }
2591 g_ptr_array_add (res, NULL);
2592
2593 g_list_free (column_order);
2594
2595 return (char **) g_ptr_array_free (res, FALSE);
2596 }
2597
2598 return get_default_column_order (list_view);
2599 }
2600
2601 static void
check_allow_sort(NautilusListView * list_view)2602 check_allow_sort (NautilusListView *list_view)
2603 {
2604 GList *column_names;
2605 GList *l;
2606 NautilusFile *file;
2607 GtkTreeViewColumn *column;
2608 gboolean allow_sorting;
2609 int sort_column_id;
2610
2611 column_names = g_hash_table_get_keys (list_view->details->columns);
2612 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
2613 allow_sorting = !(nautilus_file_is_in_recent (file) || nautilus_file_is_in_search (file));
2614
2615 for (l = column_names; l != NULL; l = l->next)
2616 {
2617 column = g_hash_table_lookup (list_view->details->columns, l->data);
2618 if (allow_sorting)
2619 {
2620 sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model,
2621 g_quark_from_string (l->data));
2622 /* Restore its original sorting id. We rely on that the keys of the hashmap
2623 * use the same string than the sort criterias */
2624 gtk_tree_view_column_set_sort_column_id (column, sort_column_id);
2625 }
2626 else
2627 {
2628 /* This disables the header and any sorting capability (like shortcuts),
2629 * but leaving them interactionable so the user can still resize them */
2630 gtk_tree_view_column_set_sort_column_id (column, -1);
2631 }
2632 }
2633
2634 g_list_free (column_names);
2635 }
2636
2637 static void
set_columns_settings_from_metadata_and_preferences(NautilusListView * list_view)2638 set_columns_settings_from_metadata_and_preferences (NautilusListView *list_view)
2639 {
2640 char **column_order;
2641 char **visible_columns;
2642
2643 column_order = get_column_order (list_view);
2644 visible_columns = get_visible_columns (list_view);
2645
2646 apply_columns_settings (list_view, column_order, visible_columns);
2647
2648 g_strfreev (column_order);
2649 g_strfreev (visible_columns);
2650 }
2651
2652 static void
set_sort_order_from_metadata_and_preferences(NautilusListView * list_view)2653 set_sort_order_from_metadata_and_preferences (NautilusListView *list_view)
2654 {
2655 char *sort_attribute;
2656 int sort_column_id;
2657 NautilusFile *file;
2658 gboolean sort_reversed, default_sort_reversed;
2659 const gchar *default_sort_order;
2660
2661 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (list_view));
2662 default_sort_order = get_default_sort_order (file, &default_sort_reversed);
2663 if (!(nautilus_file_is_in_recent (file) || nautilus_file_is_in_search (file)))
2664 {
2665 sort_attribute = nautilus_file_get_metadata (file,
2666 NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
2667 NULL);
2668 sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model,
2669 g_quark_from_string (sort_attribute));
2670 g_free (sort_attribute);
2671
2672 if (sort_column_id == -1)
2673 {
2674 sort_column_id =
2675 nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model,
2676 g_quark_from_string (default_sort_order));
2677 }
2678
2679 sort_reversed = nautilus_file_get_boolean_metadata (file,
2680 NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
2681 default_sort_reversed);
2682 }
2683 else
2684 {
2685 /* Make sure we use the default one and not one that the user used previously
2686 * of the change to not allow sorting on search and recent, or the
2687 * case that the user or some app modified directly the metadata */
2688 sort_column_id = nautilus_list_model_get_sort_column_id_from_attribute (list_view->details->model,
2689 g_quark_from_string (default_sort_order));
2690 sort_reversed = default_sort_reversed;
2691 }
2692 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_view->details->model),
2693 sort_column_id,
2694 sort_reversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
2695 }
2696
2697 static NautilusListZoomLevel
get_default_zoom_level(void)2698 get_default_zoom_level (void)
2699 {
2700 NautilusListZoomLevel default_zoom_level;
2701
2702 default_zoom_level = g_settings_get_enum (nautilus_list_view_preferences,
2703 NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL);
2704
2705 if (default_zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_SMALL
2706 || default_zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_LARGER)
2707 {
2708 default_zoom_level = NAUTILUS_LIST_ZOOM_LEVEL_STANDARD;
2709 }
2710
2711 return default_zoom_level;
2712 }
2713
2714 static void
nautilus_list_view_begin_loading(NautilusFilesView * view)2715 nautilus_list_view_begin_loading (NautilusFilesView *view)
2716 {
2717 NautilusListView *list_view;
2718
2719 list_view = NAUTILUS_LIST_VIEW (view);
2720
2721 nautilus_list_view_sort_directories_first_changed (NAUTILUS_FILES_VIEW (list_view));
2722 set_sort_order_from_metadata_and_preferences (list_view);
2723 set_columns_settings_from_metadata_and_preferences (list_view);
2724 check_allow_sort (list_view);
2725 }
2726
2727 static void
nautilus_list_view_clear(NautilusFilesView * view)2728 nautilus_list_view_clear (NautilusFilesView *view)
2729 {
2730 NautilusListView *list_view;
2731 GtkTreeView *tree_view;
2732 GtkTreeSelection *tree_selection;
2733 GtkTreePath *path;
2734
2735 list_view = NAUTILUS_LIST_VIEW (view);
2736
2737 if (list_view->details->model != NULL)
2738 {
2739 tree_view = list_view->details->tree_view;
2740
2741 /* When the current cursor's row gets deleted, GTK will move the cursor to
2742 * the next row, and when setting the cursor it also selects the new
2743 * cursor's row, thereby triggering selection signals. The new cursor will
2744 * soon be deleted again and the loop repeats.
2745 *
2746 * Since clear() removes all entries, those selections are useless but they
2747 * take up most of the time in clear(). For example, when a search returns
2748 * a large list, exiting from the search view would make nautilus hang.
2749 *
2750 * At the time the code is written simply removing the cursor solves the
2751 * problem, but to be future-proof in case GTK does anything fancy with
2752 * the current selection, we also remove the selection.
2753 *
2754 * Because GTK internally seeking the cursor takes time, only blocking the
2755 * selection signal like everywhere else will not remove that overhead.
2756 */
2757
2758 /* Clear the current selection */
2759 tree_selection = gtk_tree_view_get_selection (tree_view);
2760 gtk_tree_selection_unselect_all (tree_selection);
2761
2762 /* Clear the current cursor */
2763 path = gtk_tree_path_new ();
2764 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
2765 gtk_tree_path_free (path);
2766
2767 nautilus_list_model_clear (list_view->details->model);
2768 }
2769 }
2770
2771 static void
nautilus_list_view_file_changed(NautilusFilesView * view,NautilusFile * file,NautilusDirectory * directory)2772 nautilus_list_view_file_changed (NautilusFilesView *view,
2773 NautilusFile *file,
2774 NautilusDirectory *directory)
2775 {
2776 NautilusListView *listview;
2777
2778 listview = NAUTILUS_LIST_VIEW (view);
2779
2780 nautilus_list_model_file_changed (listview->details->model, file, directory);
2781 }
2782
2783 typedef struct
2784 {
2785 GtkTreePath *path;
2786 gboolean is_common;
2787 gboolean is_root;
2788 } HasCommonParentData;
2789
2790 static void
tree_selection_has_common_parent_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)2791 tree_selection_has_common_parent_foreach_func (GtkTreeModel *model,
2792 GtkTreePath *path,
2793 GtkTreeIter *iter,
2794 gpointer user_data)
2795 {
2796 HasCommonParentData *data;
2797 GtkTreePath *parent_path;
2798 gboolean has_parent;
2799
2800 data = (HasCommonParentData *) user_data;
2801
2802 parent_path = gtk_tree_path_copy (path);
2803 gtk_tree_path_up (parent_path);
2804
2805 has_parent = (gtk_tree_path_get_depth (parent_path) > 0) ? TRUE : FALSE;
2806
2807 if (!has_parent)
2808 {
2809 data->is_root = TRUE;
2810 }
2811
2812 if (data->is_common && !data->is_root)
2813 {
2814 if (data->path == NULL)
2815 {
2816 data->path = gtk_tree_path_copy (parent_path);
2817 }
2818 else if (gtk_tree_path_compare (data->path, parent_path) != 0)
2819 {
2820 data->is_common = FALSE;
2821 }
2822 }
2823
2824 gtk_tree_path_free (parent_path);
2825 }
2826
2827 static void
tree_selection_has_common_parent(GtkTreeSelection * selection,gboolean * is_common,gboolean * is_root)2828 tree_selection_has_common_parent (GtkTreeSelection *selection,
2829 gboolean *is_common,
2830 gboolean *is_root)
2831 {
2832 HasCommonParentData data;
2833
2834 g_assert (is_common != NULL);
2835 g_assert (is_root != NULL);
2836
2837 data.path = NULL;
2838 data.is_common = *is_common = TRUE;
2839 data.is_root = *is_root = FALSE;
2840
2841 gtk_tree_selection_selected_foreach (selection,
2842 tree_selection_has_common_parent_foreach_func,
2843 &data);
2844
2845 *is_common = data.is_common;
2846 *is_root = data.is_root;
2847
2848 if (data.path != NULL)
2849 {
2850 gtk_tree_path_free (data.path);
2851 }
2852 }
2853
2854 static char *
nautilus_list_view_get_backing_uri(NautilusFilesView * view)2855 nautilus_list_view_get_backing_uri (NautilusFilesView *view)
2856 {
2857 NautilusListView *list_view;
2858 NautilusListModel *list_model;
2859 NautilusFile *file;
2860 GtkTreeView *tree_view;
2861 GtkTreeSelection *selection;
2862 GtkTreePath *path;
2863 GList *paths;
2864 guint length;
2865 char *uri;
2866
2867 g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), NULL);
2868
2869 list_view = NAUTILUS_LIST_VIEW (view);
2870 list_model = list_view->details->model;
2871 tree_view = list_view->details->tree_view;
2872
2873 g_assert (list_model);
2874
2875 /* We currently handle three common cases here:
2876 * (a) if the selection contains non-filesystem items (i.e., the
2877 * "(Empty)" label), we return the uri of the parent.
2878 * (b) if the selection consists of exactly one _expanded_ directory, we
2879 * return its URI.
2880 * (c) if the selection consists of either exactly one item which is not
2881 * an expanded directory) or multiple items in the same directory,
2882 * we return the URI of the common parent.
2883 */
2884
2885 uri = NULL;
2886
2887 selection = gtk_tree_view_get_selection (tree_view);
2888 length = gtk_tree_selection_count_selected_rows (selection);
2889
2890 if (length == 1)
2891 {
2892 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
2893 path = (GtkTreePath *) paths->data;
2894
2895 file = nautilus_list_model_file_for_path (list_model, path);
2896 if (file == NULL)
2897 {
2898 /* The selected item is a label, not a file */
2899 gtk_tree_path_up (path);
2900 file = nautilus_list_model_file_for_path (list_model, path);
2901 }
2902
2903 if (file != NULL)
2904 {
2905 if (nautilus_file_is_directory (file) &&
2906 gtk_tree_view_row_expanded (tree_view, path))
2907 {
2908 uri = nautilus_file_get_uri (file);
2909 }
2910 nautilus_file_unref (file);
2911 }
2912
2913 gtk_tree_path_free (path);
2914 g_list_free (paths);
2915 }
2916
2917 if (uri == NULL && length > 0)
2918 {
2919 gboolean is_common, is_root;
2920
2921 /* Check that all the selected items belong to the same
2922 * directory and that directory is not the root directory (which
2923 * is handled by NautilusFilesView::get_backing_directory.) */
2924
2925 tree_selection_has_common_parent (selection, &is_common, &is_root);
2926
2927 if (is_common && !is_root)
2928 {
2929 paths = gtk_tree_selection_get_selected_rows (selection, NULL);
2930 path = (GtkTreePath *) paths->data;
2931
2932 file = nautilus_list_model_file_for_path (list_model, path);
2933 g_assert (file != NULL);
2934 uri = nautilus_file_get_parent_uri (file);
2935 nautilus_file_unref (file);
2936
2937 g_list_free_full (paths, (GDestroyNotify) gtk_tree_path_free);
2938 }
2939 }
2940
2941 if (uri != NULL)
2942 {
2943 return uri;
2944 }
2945
2946 return NAUTILUS_FILES_VIEW_CLASS (nautilus_list_view_parent_class)->get_backing_uri (view);
2947 }
2948
2949 static void
nautilus_list_view_get_selection_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)2950 nautilus_list_view_get_selection_foreach_func (GtkTreeModel *model,
2951 GtkTreePath *path,
2952 GtkTreeIter *iter,
2953 gpointer data)
2954 {
2955 GList **list;
2956 NautilusFile *file;
2957
2958 list = data;
2959
2960 gtk_tree_model_get (model, iter,
2961 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
2962 -1);
2963
2964 if (file != NULL)
2965 {
2966 (*list) = g_list_prepend ((*list), file);
2967 }
2968 }
2969
2970 static GList *
nautilus_list_view_get_selection(NautilusFilesView * view)2971 nautilus_list_view_get_selection (NautilusFilesView *view)
2972 {
2973 GList *list;
2974
2975 list = NULL;
2976
2977 gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view),
2978 nautilus_list_view_get_selection_foreach_func, &list);
2979
2980 return g_list_reverse (list);
2981 }
2982
2983 static void
nautilus_list_view_get_selection_for_file_transfer_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)2984 nautilus_list_view_get_selection_for_file_transfer_foreach_func (GtkTreeModel *model,
2985 GtkTreePath *path,
2986 GtkTreeIter *iter,
2987 gpointer data)
2988 {
2989 NautilusFile *file;
2990 struct SelectionForeachData *selection_data;
2991 GtkTreeIter parent, child;
2992
2993 selection_data = data;
2994
2995 gtk_tree_model_get (model, iter,
2996 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
2997 -1);
2998
2999 if (file != NULL)
3000 {
3001 /* If the parent folder is also selected, don't include this file in the
3002 * file operation, since that would copy it to the toplevel target instead
3003 * of keeping it as a child of the copied folder
3004 */
3005 child = *iter;
3006 while (gtk_tree_model_iter_parent (model, &parent, &child))
3007 {
3008 if (gtk_tree_selection_iter_is_selected (selection_data->selection,
3009 &parent))
3010 {
3011 return;
3012 }
3013 child = parent;
3014 }
3015
3016 nautilus_file_ref (file);
3017 selection_data->list = g_list_prepend (selection_data->list, file);
3018 }
3019 }
3020
3021
3022 static GList *
nautilus_list_view_get_selection_for_file_transfer(NautilusFilesView * view)3023 nautilus_list_view_get_selection_for_file_transfer (NautilusFilesView *view)
3024 {
3025 struct SelectionForeachData selection_data;
3026
3027 selection_data.list = NULL;
3028 selection_data.selection = gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view);
3029
3030 gtk_tree_selection_selected_foreach (selection_data.selection,
3031 nautilus_list_view_get_selection_for_file_transfer_foreach_func, &selection_data);
3032
3033 return g_list_reverse (selection_data.list);
3034 }
3035
3036 static gboolean
nautilus_list_view_is_empty(NautilusFilesView * view)3037 nautilus_list_view_is_empty (NautilusFilesView *view)
3038 {
3039 return nautilus_list_model_is_empty (NAUTILUS_LIST_VIEW (view)->details->model);
3040 }
3041
3042 static void
nautilus_list_view_end_file_changes(NautilusFilesView * view)3043 nautilus_list_view_end_file_changes (NautilusFilesView *view)
3044 {
3045 NautilusListView *list_view;
3046
3047 list_view = NAUTILUS_LIST_VIEW (view);
3048
3049 if (list_view->details->new_selection_path)
3050 {
3051 gtk_tree_view_set_cursor (list_view->details->tree_view,
3052 list_view->details->new_selection_path,
3053 NULL, FALSE);
3054 gtk_tree_path_free (list_view->details->new_selection_path);
3055 list_view->details->new_selection_path = NULL;
3056 }
3057 }
3058
3059 static void
nautilus_list_view_remove_file(NautilusFilesView * view,NautilusFile * file,NautilusDirectory * directory)3060 nautilus_list_view_remove_file (NautilusFilesView *view,
3061 NautilusFile *file,
3062 NautilusDirectory *directory)
3063 {
3064 GtkTreePath *path;
3065 GtkTreePath *file_path;
3066 GtkTreeIter iter;
3067 GtkTreeIter temp_iter;
3068 GtkTreeRowReference *row_reference;
3069 NautilusListView *list_view;
3070 GtkTreeModel *tree_model;
3071 GtkTreeSelection *selection;
3072
3073 path = NULL;
3074 row_reference = NULL;
3075 list_view = NAUTILUS_LIST_VIEW (view);
3076 tree_model = GTK_TREE_MODEL (list_view->details->model);
3077
3078 if (nautilus_list_model_get_tree_iter_from_file (list_view->details->model, file, directory, &iter))
3079 {
3080 selection = gtk_tree_view_get_selection (list_view->details->tree_view);
3081 file_path = gtk_tree_model_get_path (tree_model, &iter);
3082
3083 if (gtk_tree_selection_path_is_selected (selection, file_path))
3084 {
3085 /* get reference for next element in the list view. If the element to be deleted is the
3086 * last one, get reference to previous element. If there is only one element in view
3087 * no need to select anything.
3088 */
3089 temp_iter = iter;
3090
3091 if (gtk_tree_model_iter_next (tree_model, &iter))
3092 {
3093 path = gtk_tree_model_get_path (tree_model, &iter);
3094 row_reference = gtk_tree_row_reference_new (tree_model, path);
3095 }
3096 else
3097 {
3098 path = gtk_tree_model_get_path (tree_model, &temp_iter);
3099 if (gtk_tree_path_prev (path))
3100 {
3101 row_reference = gtk_tree_row_reference_new (tree_model, path);
3102 }
3103 }
3104 gtk_tree_path_free (path);
3105 }
3106
3107 gtk_tree_path_free (file_path);
3108
3109 nautilus_list_model_remove_file (list_view->details->model, file, directory);
3110
3111 if (gtk_tree_row_reference_valid (row_reference))
3112 {
3113 if (list_view->details->new_selection_path)
3114 {
3115 gtk_tree_path_free (list_view->details->new_selection_path);
3116 }
3117 list_view->details->new_selection_path = gtk_tree_row_reference_get_path (row_reference);
3118 }
3119
3120 if (row_reference)
3121 {
3122 gtk_tree_row_reference_free (row_reference);
3123 }
3124 }
3125 }
3126
3127 static void
nautilus_list_view_set_selection(NautilusFilesView * view,GList * selection)3128 nautilus_list_view_set_selection (NautilusFilesView *view,
3129 GList *selection)
3130 {
3131 NautilusListView *list_view;
3132 NautilusListModel *model;
3133 GtkTreeView *tree_view;
3134 GtkTreeSelection *tree_selection;
3135 GList *node;
3136 gboolean cursor_is_set_on_selection = FALSE;
3137 GList *iters, *l;
3138 NautilusFile *file;
3139
3140 list_view = NAUTILUS_LIST_VIEW (view);
3141 model = list_view->details->model;
3142 tree_view = list_view->details->tree_view;
3143 tree_selection = gtk_tree_view_get_selection (tree_view);
3144
3145 g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
3146
3147 gtk_tree_selection_unselect_all (tree_selection);
3148 for (node = selection; node != NULL; node = node->next)
3149 {
3150 file = node->data;
3151 iters = nautilus_list_model_get_all_iters_for_file (model, file);
3152
3153 for (l = iters; l != NULL; l = l->next)
3154 {
3155 if (!cursor_is_set_on_selection)
3156 {
3157 GtkTreePath *path;
3158
3159 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model),
3160 (GtkTreeIter *) l->data);
3161 gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
3162 gtk_tree_path_free (path);
3163
3164 cursor_is_set_on_selection = TRUE;
3165 continue;
3166 }
3167
3168 gtk_tree_selection_select_iter (tree_selection,
3169 (GtkTreeIter *) l->data);
3170 }
3171 g_list_free_full (iters, g_free);
3172 }
3173
3174 g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
3175 nautilus_files_view_notify_selection_changed (view);
3176 }
3177
3178 static void
nautilus_list_view_invert_selection(NautilusFilesView * view)3179 nautilus_list_view_invert_selection (NautilusFilesView *view)
3180 {
3181 NautilusListView *list_view;
3182 GtkTreeSelection *tree_selection;
3183 GList *node;
3184 GList *iters, *l;
3185 NautilusFile *file;
3186 GList *selection = NULL;
3187
3188 list_view = NAUTILUS_LIST_VIEW (view);
3189 tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
3190
3191 g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
3192
3193 gtk_tree_selection_selected_foreach (tree_selection,
3194 nautilus_list_view_get_selection_foreach_func, &selection);
3195
3196 gtk_tree_selection_select_all (tree_selection);
3197
3198 for (node = selection; node != NULL; node = node->next)
3199 {
3200 file = node->data;
3201 iters = nautilus_list_model_get_all_iters_for_file (list_view->details->model, file);
3202
3203 for (l = iters; l != NULL; l = l->next)
3204 {
3205 gtk_tree_selection_unselect_iter (tree_selection,
3206 (GtkTreeIter *) l->data);
3207 }
3208 g_list_free_full (iters, g_free);
3209 }
3210
3211 g_list_free (selection);
3212
3213 g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
3214 nautilus_files_view_notify_selection_changed (view);
3215 }
3216
3217 static void
nautilus_list_view_select_all(NautilusFilesView * view)3218 nautilus_list_view_select_all (NautilusFilesView *view)
3219 {
3220 gtk_tree_selection_select_all (gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view));
3221 }
3222
3223 static void
nautilus_list_view_select_first(NautilusFilesView * view)3224 nautilus_list_view_select_first (NautilusFilesView *view)
3225 {
3226 GtkTreeSelection *selection;
3227 GtkTreeIter iter;
3228
3229 if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (NAUTILUS_LIST_VIEW (view)->details->model), &iter))
3230 {
3231 return;
3232 }
3233 selection = gtk_tree_view_get_selection (NAUTILUS_LIST_VIEW (view)->details->tree_view);
3234 gtk_tree_selection_unselect_all (selection);
3235 gtk_tree_selection_select_iter (selection, &iter);
3236 }
3237
3238 static void
nautilus_list_view_zoom_to_level(NautilusFilesView * view,gint zoom_level)3239 nautilus_list_view_zoom_to_level (NautilusFilesView *view,
3240 gint zoom_level)
3241 {
3242 NautilusListView *list_view;
3243
3244 g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
3245
3246 list_view = NAUTILUS_LIST_VIEW (view);
3247
3248 if (list_view->details->zoom_level == zoom_level)
3249 {
3250 return;
3251 }
3252
3253 nautilus_list_view_set_zoom_level (list_view, zoom_level);
3254 g_action_group_change_action_state (nautilus_files_view_get_action_group (view),
3255 "zoom-to-level", g_variant_new_int32 (zoom_level));
3256
3257 nautilus_files_view_update_toolbar_menus (view);
3258 }
3259
3260 static void
action_zoom_to_level(GSimpleAction * action,GVariant * state,gpointer user_data)3261 action_zoom_to_level (GSimpleAction *action,
3262 GVariant *state,
3263 gpointer user_data)
3264 {
3265 NautilusFilesView *view;
3266 NautilusListZoomLevel zoom_level;
3267
3268 g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
3269
3270 view = NAUTILUS_FILES_VIEW (user_data);
3271 zoom_level = g_variant_get_int32 (state);
3272 nautilus_list_view_zoom_to_level (view, zoom_level);
3273
3274 g_simple_action_set_state (G_SIMPLE_ACTION (action), state);
3275 if (g_settings_get_enum (nautilus_list_view_preferences,
3276 NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL) != zoom_level)
3277 {
3278 g_settings_set_enum (nautilus_list_view_preferences,
3279 NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
3280 zoom_level);
3281 }
3282 }
3283
3284 static void
column_editor_response_callback(GtkWidget * dialog,int response_id,gpointer user_data)3285 column_editor_response_callback (GtkWidget *dialog,
3286 int response_id,
3287 gpointer user_data)
3288 {
3289 gtk_widget_destroy (GTK_WIDGET (dialog));
3290 }
3291
3292 static void
column_chooser_changed_callback(NautilusColumnChooser * chooser,NautilusListView * view)3293 column_chooser_changed_callback (NautilusColumnChooser *chooser,
3294 NautilusListView *view)
3295 {
3296 NautilusFile *file;
3297 char **visible_columns;
3298 char **column_order;
3299 GList *list;
3300 int i;
3301
3302 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view));
3303
3304 nautilus_column_chooser_get_settings (chooser,
3305 &visible_columns,
3306 &column_order);
3307
3308 list = NULL;
3309 for (i = 0; visible_columns[i] != NULL; ++i)
3310 {
3311 list = g_list_prepend (list, visible_columns[i]);
3312 }
3313 list = g_list_reverse (list);
3314 nautilus_file_set_metadata_list (file,
3315 NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS,
3316 list);
3317 g_list_free (list);
3318
3319 list = NULL;
3320 for (i = 0; column_order[i] != NULL; ++i)
3321 {
3322 list = g_list_prepend (list, column_order[i]);
3323 }
3324 list = g_list_reverse (list);
3325 nautilus_file_set_metadata_list (file,
3326 NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER,
3327 list);
3328 g_list_free (list);
3329
3330 apply_columns_settings (view, column_order, visible_columns);
3331
3332 g_strfreev (visible_columns);
3333 g_strfreev (column_order);
3334 }
3335
3336 static void
column_chooser_set_from_arrays(NautilusColumnChooser * chooser,NautilusListView * view,char ** visible_columns,char ** column_order)3337 column_chooser_set_from_arrays (NautilusColumnChooser *chooser,
3338 NautilusListView *view,
3339 char **visible_columns,
3340 char **column_order)
3341 {
3342 g_signal_handlers_block_by_func
3343 (chooser, G_CALLBACK (column_chooser_changed_callback), view);
3344
3345 nautilus_column_chooser_set_settings (chooser,
3346 visible_columns,
3347 column_order);
3348
3349 g_signal_handlers_unblock_by_func
3350 (chooser, G_CALLBACK (column_chooser_changed_callback), view);
3351 }
3352
3353 static void
column_chooser_set_from_settings(NautilusColumnChooser * chooser,NautilusListView * view)3354 column_chooser_set_from_settings (NautilusColumnChooser *chooser,
3355 NautilusListView *view)
3356 {
3357 char **visible_columns;
3358 char **column_order;
3359
3360 visible_columns = get_visible_columns (view);
3361 column_order = get_column_order (view);
3362
3363 column_chooser_set_from_arrays (chooser, view,
3364 visible_columns, column_order);
3365
3366 g_strfreev (visible_columns);
3367 g_strfreev (column_order);
3368 }
3369
3370 static void
column_chooser_use_default_callback(NautilusColumnChooser * chooser,NautilusListView * view)3371 column_chooser_use_default_callback (NautilusColumnChooser *chooser,
3372 NautilusListView *view)
3373 {
3374 NautilusFile *file;
3375 char **default_columns;
3376 char **default_order;
3377
3378 file = nautilus_files_view_get_directory_as_file
3379 (NAUTILUS_FILES_VIEW (view));
3380
3381 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NULL);
3382 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL);
3383
3384 /* set view values ourselves, as new metadata could not have been
3385 * updated yet.
3386 */
3387 default_columns = get_default_visible_columns (view);
3388 default_order = get_default_column_order (view);
3389
3390 apply_columns_settings (view, default_order, default_columns);
3391 column_chooser_set_from_arrays (chooser, view,
3392 default_columns, default_order);
3393
3394 g_strfreev (default_columns);
3395 g_strfreev (default_order);
3396 }
3397
3398 static GtkWidget *
create_column_editor(NautilusListView * view)3399 create_column_editor (NautilusListView *view)
3400 {
3401 GtkWidget *window;
3402 GtkWidget *label;
3403 GtkWidget *box;
3404 GtkWidget *column_chooser;
3405 NautilusFile *file;
3406 char *str;
3407 char *name;
3408 const char *label_text;
3409
3410 file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view));
3411 name = nautilus_file_get_display_name (file);
3412 str = g_strdup_printf (_("%s Visible Columns"), name);
3413 g_free (name);
3414
3415 window = gtk_dialog_new_with_buttons (str,
3416 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
3417 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
3418 NULL, NULL);
3419 g_free (str);
3420 g_signal_connect (window, "response",
3421 G_CALLBACK (column_editor_response_callback), NULL);
3422
3423 gtk_window_set_default_size (GTK_WINDOW (window), 300, 400);
3424
3425 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
3426 gtk_container_set_border_width (GTK_CONTAINER (box), 12);
3427 gtk_widget_set_hexpand (box, TRUE);
3428 gtk_widget_show (box);
3429 gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))), box,
3430 TRUE, TRUE, 0);
3431
3432 label_text = _("Choose the order of information to appear in this folder:");
3433 str = g_strconcat ("<b>", label_text, "</b>", NULL);
3434 label = gtk_label_new (NULL);
3435 gtk_label_set_markup (GTK_LABEL (label), str);
3436 gtk_label_set_line_wrap (GTK_LABEL (label), FALSE);
3437 gtk_label_set_xalign (GTK_LABEL (label), 0);
3438 gtk_label_set_yalign (GTK_LABEL (label), 0);
3439 gtk_widget_show (label);
3440 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
3441
3442 g_free (str);
3443
3444 column_chooser = nautilus_column_chooser_new (file);
3445 gtk_widget_show (column_chooser);
3446 gtk_box_pack_start (GTK_BOX (box), column_chooser, TRUE, TRUE, 0);
3447
3448 g_signal_connect (column_chooser, "changed",
3449 G_CALLBACK (column_chooser_changed_callback),
3450 view);
3451 g_signal_connect (column_chooser, "use-default",
3452 G_CALLBACK (column_chooser_use_default_callback),
3453 view);
3454
3455 column_chooser_set_from_settings
3456 (NAUTILUS_COLUMN_CHOOSER (column_chooser), view);
3457
3458 return window;
3459 }
3460
3461 static void
action_visible_columns(GSimpleAction * action,GVariant * state,gpointer user_data)3462 action_visible_columns (GSimpleAction *action,
3463 GVariant *state,
3464 gpointer user_data)
3465 {
3466 NautilusListView *list_view;
3467
3468 list_view = NAUTILUS_LIST_VIEW (user_data);
3469
3470 if (list_view->details->column_editor)
3471 {
3472 gtk_window_present (GTK_WINDOW (list_view->details->column_editor));
3473 }
3474 else
3475 {
3476 list_view->details->column_editor = create_column_editor (list_view);
3477 g_object_add_weak_pointer (G_OBJECT (list_view->details->column_editor),
3478 (gpointer *) &list_view->details->column_editor);
3479
3480 gtk_widget_show (list_view->details->column_editor);
3481 }
3482 }
3483
3484 const GActionEntry list_view_entries[] =
3485 {
3486 { "visible-columns", action_visible_columns },
3487 { "zoom-to-level", NULL, NULL, "1", action_zoom_to_level }
3488 };
3489
3490 static void
nautilus_list_view_set_zoom_level(NautilusListView * view,NautilusListZoomLevel new_level)3491 nautilus_list_view_set_zoom_level (NautilusListView *view,
3492 NautilusListZoomLevel new_level)
3493 {
3494 int column;
3495
3496 g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
3497 g_return_if_fail (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL &&
3498 new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGER);
3499
3500 if (view->details->zoom_level == new_level)
3501 {
3502 return;
3503 }
3504
3505 view->details->zoom_level = new_level;
3506
3507 /* Select correctly scaled icons. */
3508 column = nautilus_list_model_get_column_id_from_zoom_level (new_level);
3509 gtk_tree_view_column_set_attributes (view->details->file_name_column,
3510 GTK_CELL_RENDERER (view->details->pixbuf_cell),
3511 "surface", column,
3512 NULL);
3513 set_up_pixbuf_size (view);
3514 }
3515
3516 static void
nautilus_list_view_bump_zoom_level(NautilusFilesView * view,int zoom_increment)3517 nautilus_list_view_bump_zoom_level (NautilusFilesView *view,
3518 int zoom_increment)
3519 {
3520 NautilusListView *list_view;
3521 gint new_level;
3522
3523 g_return_if_fail (NAUTILUS_IS_LIST_VIEW (view));
3524
3525 list_view = NAUTILUS_LIST_VIEW (view);
3526 new_level = list_view->details->zoom_level + zoom_increment;
3527
3528 if (new_level >= NAUTILUS_LIST_ZOOM_LEVEL_SMALL &&
3529 new_level <= NAUTILUS_LIST_ZOOM_LEVEL_LARGER)
3530 {
3531 nautilus_list_view_zoom_to_level (view, new_level);
3532 }
3533 }
3534
3535 static void
nautilus_list_view_restore_standard_zoom_level(NautilusFilesView * view)3536 nautilus_list_view_restore_standard_zoom_level (NautilusFilesView *view)
3537 {
3538 nautilus_list_view_zoom_to_level (view, NAUTILUS_LIST_ZOOM_LEVEL_STANDARD);
3539 }
3540
3541 static gboolean
nautilus_list_view_can_zoom_in(NautilusFilesView * view)3542 nautilus_list_view_can_zoom_in (NautilusFilesView *view)
3543 {
3544 g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), FALSE);
3545
3546 return NAUTILUS_LIST_VIEW (view)->details->zoom_level < NAUTILUS_LIST_ZOOM_LEVEL_LARGER;
3547 }
3548
3549 static gboolean
nautilus_list_view_can_zoom_out(NautilusFilesView * view)3550 nautilus_list_view_can_zoom_out (NautilusFilesView *view)
3551 {
3552 g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), FALSE);
3553
3554 return NAUTILUS_LIST_VIEW (view)->details->zoom_level > NAUTILUS_LIST_ZOOM_LEVEL_SMALL;
3555 }
3556
3557 static gfloat
nautilus_list_view_get_zoom_level_percentage(NautilusFilesView * view)3558 nautilus_list_view_get_zoom_level_percentage (NautilusFilesView *view)
3559 {
3560 NautilusListView *list_view;
3561 guint icon_size;
3562
3563 g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), 1.0);
3564
3565 list_view = NAUTILUS_LIST_VIEW (view);
3566 icon_size = nautilus_list_model_get_icon_size_for_zoom_level (list_view->details->zoom_level);
3567
3568 return (gfloat) icon_size / NAUTILUS_LIST_ICON_SIZE_STANDARD;
3569 }
3570
3571 static gboolean
nautilus_list_view_is_zoom_level_default(NautilusFilesView * view)3572 nautilus_list_view_is_zoom_level_default (NautilusFilesView *view)
3573 {
3574 NautilusListView *list_view;
3575 guint icon_size;
3576
3577 list_view = NAUTILUS_LIST_VIEW (view);
3578 icon_size = nautilus_list_model_get_icon_size_for_zoom_level (list_view->details->zoom_level);
3579
3580 return icon_size == NAUTILUS_LIST_ICON_SIZE_STANDARD;
3581 }
3582
3583 static void
nautilus_list_view_click_policy_changed(NautilusFilesView * directory_view)3584 nautilus_list_view_click_policy_changed (NautilusFilesView *directory_view)
3585 {
3586 GdkWindow *win;
3587 GdkDisplay *display;
3588 NautilusListView *view;
3589 GtkTreeIter iter;
3590 GtkTreeView *tree;
3591
3592 view = NAUTILUS_LIST_VIEW (directory_view);
3593 display = gtk_widget_get_display (GTK_WIDGET (view));
3594
3595 /* ensure that we unset the hand cursor and refresh underlined rows */
3596 if (get_click_policy () == NAUTILUS_CLICK_POLICY_DOUBLE)
3597 {
3598 if (view->details->hover_path != NULL)
3599 {
3600 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
3601 &iter, view->details->hover_path))
3602 {
3603 gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model),
3604 view->details->hover_path, &iter);
3605 }
3606
3607 gtk_tree_path_free (view->details->hover_path);
3608 view->details->hover_path = NULL;
3609 }
3610
3611 tree = view->details->tree_view;
3612 if (gtk_widget_get_realized (GTK_WIDGET (tree)))
3613 {
3614 win = gtk_widget_get_window (GTK_WIDGET (tree));
3615 gdk_window_set_cursor (win, NULL);
3616
3617 if (display != NULL)
3618 {
3619 gdk_display_flush (display);
3620 }
3621 }
3622
3623 g_clear_object (&hand_cursor);
3624 }
3625 else if (get_click_policy () == NAUTILUS_CLICK_POLICY_SINGLE)
3626 {
3627 if (hand_cursor == NULL)
3628 {
3629 hand_cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
3630 }
3631 }
3632 }
3633
3634 static void
default_sort_order_changed_callback(gpointer callback_data)3635 default_sort_order_changed_callback (gpointer callback_data)
3636 {
3637 NautilusListView *list_view;
3638
3639 list_view = NAUTILUS_LIST_VIEW (callback_data);
3640
3641 set_sort_order_from_metadata_and_preferences (list_view);
3642 }
3643
3644 static void
default_visible_columns_changed_callback(gpointer callback_data)3645 default_visible_columns_changed_callback (gpointer callback_data)
3646 {
3647 NautilusListView *list_view;
3648
3649 list_view = NAUTILUS_LIST_VIEW (callback_data);
3650
3651 set_columns_settings_from_metadata_and_preferences (list_view);
3652 }
3653
3654 static void
default_column_order_changed_callback(gpointer callback_data)3655 default_column_order_changed_callback (gpointer callback_data)
3656 {
3657 NautilusListView *list_view;
3658
3659 list_view = NAUTILUS_LIST_VIEW (callback_data);
3660
3661 set_columns_settings_from_metadata_and_preferences (list_view);
3662 }
3663
3664 static void
nautilus_list_view_sort_directories_first_changed(NautilusFilesView * view)3665 nautilus_list_view_sort_directories_first_changed (NautilusFilesView *view)
3666 {
3667 NautilusListView *list_view;
3668
3669 list_view = NAUTILUS_LIST_VIEW (view);
3670
3671 nautilus_list_model_set_should_sort_directories_first (list_view->details->model,
3672 nautilus_files_view_should_sort_directories_first (view));
3673 }
3674
3675 static int
nautilus_list_view_compare_files(NautilusFilesView * view,NautilusFile * file1,NautilusFile * file2)3676 nautilus_list_view_compare_files (NautilusFilesView *view,
3677 NautilusFile *file1,
3678 NautilusFile *file2)
3679 {
3680 NautilusListView *list_view;
3681
3682 list_view = NAUTILUS_LIST_VIEW (view);
3683 return nautilus_list_model_compare_func (list_view->details->model, file1, file2);
3684 }
3685
3686 static void
nautilus_list_view_dispose(GObject * object)3687 nautilus_list_view_dispose (GObject *object)
3688 {
3689 NautilusListView *list_view;
3690 GtkClipboard *clipboard;
3691
3692 list_view = NAUTILUS_LIST_VIEW (object);
3693
3694 if (list_view->details->model)
3695 {
3696 g_object_unref (list_view->details->model);
3697 list_view->details->model = NULL;
3698 }
3699
3700 if (list_view->details->drag_dest)
3701 {
3702 g_object_unref (list_view->details->drag_dest);
3703 list_view->details->drag_dest = NULL;
3704 }
3705
3706 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3707 g_signal_handlers_disconnect_by_func (clipboard, on_clipboard_owner_changed, list_view);
3708 g_signal_handlers_disconnect_by_func (nautilus_preferences,
3709 default_sort_order_changed_callback,
3710 list_view);
3711 g_signal_handlers_disconnect_by_func (nautilus_list_view_preferences,
3712 default_visible_columns_changed_callback,
3713 list_view);
3714 g_signal_handlers_disconnect_by_func (nautilus_list_view_preferences,
3715 default_column_order_changed_callback,
3716 list_view);
3717
3718 g_clear_object (&list_view->details->tree_view_drag_gesture);
3719 g_clear_object (&list_view->details->tree_view_multi_press_gesture);
3720
3721 G_OBJECT_CLASS (nautilus_list_view_parent_class)->dispose (object);
3722 }
3723
3724 static void
nautilus_list_view_finalize(GObject * object)3725 nautilus_list_view_finalize (GObject *object)
3726 {
3727 NautilusListView *list_view;
3728
3729 list_view = NAUTILUS_LIST_VIEW (object);
3730
3731 g_free (list_view->details->original_name);
3732 list_view->details->original_name = NULL;
3733
3734 if (list_view->details->first_click_path)
3735 {
3736 gtk_tree_path_free (list_view->details->first_click_path);
3737 }
3738 if (list_view->details->new_selection_path)
3739 {
3740 gtk_tree_path_free (list_view->details->new_selection_path);
3741 }
3742
3743 g_list_free (list_view->details->cells);
3744 g_hash_table_destroy (list_view->details->columns);
3745
3746 if (list_view->details->hover_path != NULL)
3747 {
3748 gtk_tree_path_free (list_view->details->hover_path);
3749 }
3750
3751 if (list_view->details->column_editor != NULL)
3752 {
3753 gtk_widget_destroy (list_view->details->column_editor);
3754 }
3755
3756 g_regex_unref (list_view->details->regex);
3757
3758 g_cancellable_cancel (list_view->details->starred_cancellable);
3759 g_clear_object (&list_view->details->starred_cancellable);
3760
3761 g_signal_handlers_disconnect_by_func (list_view->details->tag_manager,
3762 on_starred_files_changed,
3763 list_view);
3764 g_clear_object (&list_view->details->tag_manager);
3765
3766 g_free (list_view->details);
3767
3768 G_OBJECT_CLASS (nautilus_list_view_parent_class)->finalize (object);
3769 }
3770
3771 static char *
nautilus_list_view_get_first_visible_file(NautilusFilesView * view)3772 nautilus_list_view_get_first_visible_file (NautilusFilesView *view)
3773 {
3774 NautilusFile *file;
3775 GtkTreePath *path;
3776 GtkTreeIter iter;
3777 NautilusListView *list_view;
3778
3779 list_view = NAUTILUS_LIST_VIEW (view);
3780
3781 if (gtk_tree_view_get_path_at_pos (list_view->details->tree_view,
3782 0, 0,
3783 &path, NULL, NULL, NULL))
3784 {
3785 gtk_tree_model_get_iter (GTK_TREE_MODEL (list_view->details->model),
3786 &iter, path);
3787
3788 gtk_tree_path_free (path);
3789
3790 gtk_tree_model_get (GTK_TREE_MODEL (list_view->details->model),
3791 &iter,
3792 NAUTILUS_LIST_MODEL_FILE_COLUMN, &file,
3793 -1);
3794 if (file)
3795 {
3796 char *uri;
3797
3798 uri = nautilus_file_get_uri (file);
3799
3800 nautilus_file_unref (file);
3801
3802 return uri;
3803 }
3804 }
3805
3806 return NULL;
3807 }
3808
3809 static void
nautilus_list_view_scroll_to_file(NautilusListView * view,NautilusFile * file)3810 nautilus_list_view_scroll_to_file (NautilusListView *view,
3811 NautilusFile *file)
3812 {
3813 GtkTreePath *path;
3814 GtkTreeIter iter;
3815
3816 if (!nautilus_list_model_get_first_iter_for_file (view->details->model, file, &iter))
3817 {
3818 return;
3819 }
3820
3821 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->model), &iter);
3822
3823 gtk_tree_view_scroll_to_cell (view->details->tree_view,
3824 path, NULL,
3825 TRUE, 0.0, 0.0);
3826
3827 gtk_tree_path_free (path);
3828 }
3829
3830 static void
list_view_scroll_to_file(NautilusFilesView * view,const char * uri)3831 list_view_scroll_to_file (NautilusFilesView *view,
3832 const char *uri)
3833 {
3834 NautilusFile *file;
3835
3836 if (uri != NULL)
3837 {
3838 /* Only if existing, since we don't want to add the file to
3839 * the directory if it has been removed since then */
3840 file = nautilus_file_get_existing_by_uri (uri);
3841 if (file != NULL)
3842 {
3843 nautilus_list_view_scroll_to_file (NAUTILUS_LIST_VIEW (view), file);
3844 nautilus_file_unref (file);
3845 }
3846 }
3847 }
3848
3849 static void
on_clipboard_contents_received(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer user_data)3850 on_clipboard_contents_received (GtkClipboard *clipboard,
3851 GtkSelectionData *selection_data,
3852 gpointer user_data)
3853 {
3854 NautilusListView *view = NAUTILUS_LIST_VIEW (user_data);
3855
3856 if (!view->details->model)
3857 {
3858 /* We've been destroyed since call */
3859 g_object_unref (view);
3860 return;
3861 }
3862
3863 if (nautilus_clipboard_is_cut_from_selection_data (selection_data))
3864 {
3865 GList *uris;
3866 GList *files;
3867
3868 uris = nautilus_clipboard_get_uri_list_from_selection_data (selection_data);
3869 files = nautilus_file_list_from_uri_list (uris);
3870 nautilus_list_model_set_highlight_for_files (view->details->model, files);
3871
3872 nautilus_file_list_free (files);
3873 g_list_free_full (uris, g_free);
3874 }
3875 else
3876 {
3877 nautilus_list_model_set_highlight_for_files (view->details->model, NULL);
3878 }
3879
3880 g_object_unref (view);
3881 }
3882
3883 static void
update_clipboard_status(NautilusListView * view)3884 update_clipboard_status (NautilusListView *view)
3885 {
3886 g_object_ref (view); /* Need to keep the object alive until we get the reply */
3887 gtk_clipboard_request_contents (nautilus_clipboard_get (GTK_WIDGET (view)),
3888 nautilus_clipboard_get_atom (),
3889 on_clipboard_contents_received,
3890 view);
3891 }
3892
3893 static void
on_clipboard_owner_changed(GtkClipboard * clipboard,GdkEvent * event,gpointer user_data)3894 on_clipboard_owner_changed (GtkClipboard *clipboard,
3895 GdkEvent *event,
3896 gpointer user_data)
3897 {
3898 update_clipboard_status (NAUTILUS_LIST_VIEW (user_data));
3899 }
3900
3901 static void
nautilus_list_view_end_loading(NautilusFilesView * view,gboolean all_files_seen)3902 nautilus_list_view_end_loading (NautilusFilesView *view,
3903 gboolean all_files_seen)
3904 {
3905 update_clipboard_status (NAUTILUS_LIST_VIEW (view));
3906 }
3907
3908 static guint
nautilus_list_view_get_id(NautilusFilesView * view)3909 nautilus_list_view_get_id (NautilusFilesView *view)
3910 {
3911 return NAUTILUS_VIEW_LIST_ID;
3912 }
3913
3914 static GdkRectangle *
get_rectangle_for_path(NautilusListView * list_view,GtkTreePath * path)3915 get_rectangle_for_path (NautilusListView *list_view,
3916 GtkTreePath *path)
3917 {
3918 GtkTreeView *tree_view = list_view->details->tree_view;
3919 GdkRectangle *rect = g_malloc0 (sizeof (GdkRectangle));
3920 int header_height;
3921
3922 gtk_tree_view_get_cell_area (tree_view,
3923 path,
3924 list_view->details->file_name_column,
3925 rect);
3926 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
3927 rect->x, rect->y,
3928 &rect->x, &rect->y);
3929
3930 /* FIXME Due to smooth scrolling, we may get the cell area while the view is
3931 * still scrolling (and still outside the view), not at the final position
3932 * of the cell after scrolling.
3933 * https://bugzilla.gnome.org/show_bug.cgi?id=746773
3934 * The following workaround guesses the final "y" coordinate by clamping it
3935 * to the widget edge. Note that the top edge has got columns header, which
3936 * is private, so first guess the header height from the difference between
3937 * widget coordinates and bin cooridinates.
3938 */
3939 gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
3940 0, 0,
3941 NULL, &header_height);
3942
3943 rect->y = CLAMP (rect->y,
3944 header_height,
3945 gtk_widget_get_allocated_height (GTK_WIDGET (list_view)) - rect->height);
3946 /* End of workaround */
3947
3948 return rect;
3949 }
3950
3951 static GdkRectangle *
nautilus_list_view_compute_rename_popover_pointing_to(NautilusFilesView * view)3952 nautilus_list_view_compute_rename_popover_pointing_to (NautilusFilesView *view)
3953 {
3954 NautilusListView *list_view;
3955 GtkTreeView *tree_view;
3956 GtkTreeSelection *selection;
3957 GList *list;
3958 GtkTreePath *path;
3959 GdkRectangle *rect;
3960
3961 list_view = NAUTILUS_LIST_VIEW (view);
3962 tree_view = list_view->details->tree_view;
3963 selection = gtk_tree_view_get_selection (tree_view);
3964 list = gtk_tree_selection_get_selected_rows (selection, NULL);
3965 path = list->data;
3966 rect = get_rectangle_for_path (list_view, path);
3967
3968 if (list_view->details->last_event_button_x > 0)
3969 {
3970 /* Point to the position in the row where it was clicked. */
3971 rect->x = list_view->details->last_event_button_x;
3972 /* Make it zero width to point exactly at rect->x.*/
3973 rect->width = 0;
3974 }
3975
3976 g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
3977
3978 return rect;
3979 }
3980
3981 static GdkRectangle *
nautilus_list_view_reveal_for_selection_context_menu(NautilusFilesView * view)3982 nautilus_list_view_reveal_for_selection_context_menu (NautilusFilesView *view)
3983 {
3984 NautilusListView *list_view;
3985 GtkTreeView *tree_view;
3986 GtkTreeSelection *tree_selection;
3987 GtkTreePath *path;
3988 GdkRectangle *rect;
3989
3990 g_return_val_if_fail (NAUTILUS_IS_LIST_VIEW (view), NULL);
3991
3992 list_view = NAUTILUS_LIST_VIEW (view);
3993 tree_view = list_view->details->tree_view;
3994 tree_selection = gtk_tree_view_get_selection (tree_view);
3995 g_return_val_if_fail (tree_selection != NULL, NULL);
3996
3997 /* Get the path to the last focused item, if selected. Otherwise, get
3998 * the path to the selected item which is sorted the lowest.
3999 */
4000 gtk_tree_view_get_cursor (tree_view, &path, NULL);
4001 if (path == NULL || !gtk_tree_selection_path_is_selected (tree_selection, path))
4002 {
4003 GList *list;
4004
4005 list = gtk_tree_selection_get_selected_rows (tree_selection, NULL);
4006 list = g_list_last (list);
4007 path = g_steal_pointer (&list->data);
4008
4009 g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
4010 }
4011
4012 gtk_tree_view_scroll_to_cell (tree_view, path, NULL, FALSE, 0.0, 0.0);
4013
4014 rect = get_rectangle_for_path (list_view, path);
4015
4016 gtk_tree_path_free (path);
4017
4018 return rect;
4019 }
4020
4021 static void
nautilus_list_view_preview_selection_event(NautilusFilesView * view,GtkDirectionType direction)4022 nautilus_list_view_preview_selection_event (NautilusFilesView *view,
4023 GtkDirectionType direction)
4024 {
4025 NautilusListView *list_view;
4026 GtkTreeView *tree_view;
4027 GtkTreeSelection *selection;
4028 GList *list;
4029 GtkTreeIter iter;
4030 GtkTreePath *path;
4031 GtkTreeModel *tree_model;
4032 gboolean moved;
4033
4034 /* We only support up and down movements for the list view */
4035 if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
4036 {
4037 return;
4038 }
4039
4040 list_view = NAUTILUS_LIST_VIEW (view);
4041 tree_view = list_view->details->tree_view;
4042 selection = gtk_tree_view_get_selection (tree_view);
4043 list = gtk_tree_selection_get_selected_rows (selection, &tree_model);
4044
4045 if (list == NULL)
4046 {
4047 return;
4048 }
4049
4050 /* Advance the first selected item, since that's what we use for
4051 * the previewer */
4052 path = list->data;
4053 moved = FALSE;
4054 if (gtk_tree_model_get_iter (tree_model, &iter, path))
4055 {
4056 if (direction == GTK_DIR_UP)
4057 {
4058 moved = gtk_tree_model_iter_previous (tree_model, &iter);
4059 }
4060 else
4061 {
4062 moved = gtk_tree_model_iter_next (tree_model, &iter);
4063 }
4064 }
4065
4066 if (moved)
4067 {
4068 g_signal_handlers_block_by_func (selection, list_selection_changed_callback, view);
4069
4070 gtk_tree_selection_unselect_all (selection);
4071 gtk_tree_selection_select_iter (selection, &iter);
4072
4073 g_signal_handlers_unblock_by_func (selection, list_selection_changed_callback, view);
4074 nautilus_files_view_notify_selection_changed (view);
4075 }
4076
4077 g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
4078 }
4079
4080 static void
nautilus_list_view_class_init(NautilusListViewClass * class)4081 nautilus_list_view_class_init (NautilusListViewClass *class)
4082 {
4083 NautilusFilesViewClass *nautilus_files_view_class;
4084
4085 nautilus_files_view_class = NAUTILUS_FILES_VIEW_CLASS (class);
4086
4087 G_OBJECT_CLASS (class)->dispose = nautilus_list_view_dispose;
4088 G_OBJECT_CLASS (class)->finalize = nautilus_list_view_finalize;
4089
4090 nautilus_files_view_class->add_files = nautilus_list_view_add_files;
4091 nautilus_files_view_class->begin_loading = nautilus_list_view_begin_loading;
4092 nautilus_files_view_class->end_loading = nautilus_list_view_end_loading;
4093 nautilus_files_view_class->bump_zoom_level = nautilus_list_view_bump_zoom_level;
4094 nautilus_files_view_class->can_zoom_in = nautilus_list_view_can_zoom_in;
4095 nautilus_files_view_class->can_zoom_out = nautilus_list_view_can_zoom_out;
4096 nautilus_files_view_class->get_zoom_level_percentage = nautilus_list_view_get_zoom_level_percentage;
4097 nautilus_files_view_class->is_zoom_level_default = nautilus_list_view_is_zoom_level_default;
4098 nautilus_files_view_class->click_policy_changed = nautilus_list_view_click_policy_changed;
4099 nautilus_files_view_class->clear = nautilus_list_view_clear;
4100 nautilus_files_view_class->file_changed = nautilus_list_view_file_changed;
4101 nautilus_files_view_class->get_backing_uri = nautilus_list_view_get_backing_uri;
4102 nautilus_files_view_class->get_selection = nautilus_list_view_get_selection;
4103 nautilus_files_view_class->get_selection_for_file_transfer = nautilus_list_view_get_selection_for_file_transfer;
4104 nautilus_files_view_class->is_empty = nautilus_list_view_is_empty;
4105 nautilus_files_view_class->remove_file = nautilus_list_view_remove_file;
4106 nautilus_files_view_class->restore_standard_zoom_level = nautilus_list_view_restore_standard_zoom_level;
4107 nautilus_files_view_class->reveal_selection = nautilus_list_view_reveal_selection;
4108 nautilus_files_view_class->select_all = nautilus_list_view_select_all;
4109 nautilus_files_view_class->select_first = nautilus_list_view_select_first;
4110 nautilus_files_view_class->set_selection = nautilus_list_view_set_selection;
4111 nautilus_files_view_class->invert_selection = nautilus_list_view_invert_selection;
4112 nautilus_files_view_class->compare_files = nautilus_list_view_compare_files;
4113 nautilus_files_view_class->sort_directories_first_changed = nautilus_list_view_sort_directories_first_changed;
4114 nautilus_files_view_class->end_file_changes = nautilus_list_view_end_file_changes;
4115 nautilus_files_view_class->get_view_id = nautilus_list_view_get_id;
4116 nautilus_files_view_class->get_first_visible_file = nautilus_list_view_get_first_visible_file;
4117 nautilus_files_view_class->scroll_to_file = list_view_scroll_to_file;
4118 nautilus_files_view_class->compute_rename_popover_pointing_to = nautilus_list_view_compute_rename_popover_pointing_to;
4119 nautilus_files_view_class->reveal_for_selection_context_menu = nautilus_list_view_reveal_for_selection_context_menu;
4120 nautilus_files_view_class->preview_selection_event = nautilus_list_view_preview_selection_event;
4121 }
4122
4123 static void
nautilus_list_view_init(NautilusListView * list_view)4124 nautilus_list_view_init (NautilusListView *list_view)
4125 {
4126 GActionGroup *view_action_group;
4127 GtkClipboard *clipboard;
4128
4129 list_view->details = g_new0 (NautilusListViewDetails, 1);
4130
4131 /* ensure that the zoom level is always set before settings up the tree view columns */
4132 list_view->details->zoom_level = get_default_zoom_level ();
4133
4134 create_and_set_up_tree_view (list_view);
4135
4136 gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (list_view)),
4137 "nautilus-list-view");
4138
4139 g_signal_connect_swapped (nautilus_preferences,
4140 "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_ORDER,
4141 G_CALLBACK (default_sort_order_changed_callback),
4142 list_view);
4143 g_signal_connect_swapped (nautilus_preferences,
4144 "changed::" NAUTILUS_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
4145 G_CALLBACK (default_sort_order_changed_callback),
4146 list_view);
4147 g_signal_connect_swapped (nautilus_list_view_preferences,
4148 "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS,
4149 G_CALLBACK (default_visible_columns_changed_callback),
4150 list_view);
4151 g_signal_connect_swapped (nautilus_list_view_preferences,
4152 "changed::" NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER,
4153 G_CALLBACK (default_column_order_changed_callback),
4154 list_view);
4155
4156 /* React to clipboard changes */
4157 clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
4158 g_signal_connect (clipboard, "owner-change",
4159 G_CALLBACK (on_clipboard_owner_changed), list_view);
4160
4161 nautilus_list_view_click_policy_changed (NAUTILUS_FILES_VIEW (list_view));
4162
4163 nautilus_list_view_set_zoom_level (list_view, get_default_zoom_level ());
4164
4165 list_view->details->hover_path = NULL;
4166
4167 view_action_group = nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (list_view));
4168 g_action_map_add_action_entries (G_ACTION_MAP (view_action_group),
4169 list_view_entries,
4170 G_N_ELEMENTS (list_view_entries),
4171 list_view);
4172 /* Keep the action synced with the actual value, so the toolbar can poll it */
4173 g_action_group_change_action_state (nautilus_files_view_get_action_group (NAUTILUS_FILES_VIEW (list_view)),
4174 "zoom-to-level", g_variant_new_int32 (get_default_zoom_level ()));
4175
4176 list_view->details->regex = g_regex_new ("\\R+", 0, G_REGEX_MATCH_NEWLINE_ANY, NULL);
4177
4178 list_view->details->tag_manager = nautilus_tag_manager_get ();
4179 list_view->details->starred_cancellable = g_cancellable_new ();
4180
4181 g_signal_connect (list_view->details->tag_manager,
4182 "starred-changed",
4183 (GCallback) on_starred_files_changed,
4184 list_view);
4185 }
4186
4187 NautilusFilesView *
nautilus_list_view_new(NautilusWindowSlot * slot)4188 nautilus_list_view_new (NautilusWindowSlot *slot)
4189 {
4190 return g_object_new (NAUTILUS_TYPE_LIST_VIEW,
4191 "window-slot", slot,
4192 NULL);
4193 }
4194