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