1 /* nautilus-files-view.c
2  *
3  * Copyright (C) 1999, 2000  Free Software Foundation
4  * Copyright (C) 2000, 2001  Eazel, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU 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  * This program 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  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
18  *
19  * Authors: Ettore Perazzoli,
20  *          John Sullivan <sullivan@eazel.com>,
21  *          Darin Adler <darin@bentspoon.com>,
22  *          Pavel Cisler <pavel@eazel.com>,
23  *          David Emory Watson <dwatson@cs.ucr.edu>
24  */
25 
26 #include "nautilus-files-view.h"
27 
28 #include <eel/eel-glib-extensions.h>
29 #include <eel/eel-gtk-extensions.h>
30 #include <eel/eel-stock-dialogs.h>
31 #include <eel/eel-string.h>
32 #include <eel/eel-vfs-extensions.h>
33 #include <fcntl.h>
34 #include <gdesktop-enums.h>
35 #include <gdk/gdkkeysyms.h>
36 #include <gdk/gdk.h>
37 #include <gio/gio.h>
38 #include <glib/gi18n.h>
39 #include <glib/gstdio.h>
40 #include <gtk/gtk.h>
41 #include <gnome-autoar/gnome-autoar.h>
42 #include <math.h>
43 #include <nautilus-extension.h>
44 #include <string.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 
48 #define DEBUG_FLAG NAUTILUS_DEBUG_DIRECTORY_VIEW
49 #include "nautilus-debug.h"
50 
51 #include "nautilus-application.h"
52 #include "nautilus-batch-rename-dialog.h"
53 #include "nautilus-batch-rename-utilities.h"
54 #include "nautilus-canvas-view.h"
55 #include "nautilus-clipboard.h"
56 #include "nautilus-compress-dialog-controller.h"
57 #include "nautilus-directory.h"
58 #include "nautilus-dnd.h"
59 #include "nautilus-enums.h"
60 #include "nautilus-error-reporting.h"
61 #include "nautilus-file-changes-queue.h"
62 #include "nautilus-file-name-widget-controller.h"
63 #include "nautilus-file-operations.h"
64 #include "nautilus-file-private.h"
65 #include "nautilus-file-undo-manager.h"
66 #include "nautilus-file-utilities.h"
67 #include "nautilus-file.h"
68 #include "nautilus-floating-bar.h"
69 #include "nautilus-global-preferences.h"
70 #include "nautilus-icon-info.h"
71 #include "nautilus-icon-names.h"
72 #include "nautilus-list-view.h"
73 #include "nautilus-metadata.h"
74 #include "nautilus-mime-actions.h"
75 #include "nautilus-module.h"
76 #include "nautilus-new-folder-dialog-controller.h"
77 #include "nautilus-previewer.h"
78 #include "nautilus-profile.h"
79 #include "nautilus-program-choosing.h"
80 #include "nautilus-properties-window.h"
81 #include "nautilus-rename-file-popover-controller.h"
82 #include "nautilus-search-directory.h"
83 #include "nautilus-signaller.h"
84 #include "nautilus-tag-manager.h"
85 #include "nautilus-toolbar.h"
86 #include "nautilus-trash-monitor.h"
87 #include "nautilus-ui-utilities.h"
88 #include "nautilus-view.h"
89 #include "nautilus-view-icon-controller.h"
90 #include "nautilus-window.h"
91 #include "nautilus-tracker-utilities.h"
92 
93 #ifdef HAVE_LIBPORTAL
94 #include <libportal/portal.h>
95 #include <libportal-gtk3/portal-gtk3.h>
96 #endif
97 
98 /* Minimum starting update inverval */
99 #define UPDATE_INTERVAL_MIN 100
100 /* Maximum update interval */
101 #define UPDATE_INTERVAL_MAX 2000
102 /* Amount of miliseconds the update interval is increased */
103 #define UPDATE_INTERVAL_INC 250
104 /* Interval at which the update interval is increased */
105 #define UPDATE_INTERVAL_TIMEOUT_INTERVAL 250
106 /* Milliseconds that have to pass without a change to reset the update interval */
107 #define UPDATE_INTERVAL_RESET 1000
108 
109 #define SILENT_WINDOW_OPEN_LIMIT 5
110 
111 #define DUPLICATE_HORIZONTAL_ICON_OFFSET 70
112 #define DUPLICATE_VERTICAL_ICON_OFFSET   30
113 
114 #define MAX_QUEUED_UPDATES 500
115 
116 #define MAX_MENU_LEVELS 5
117 #define TEMPLATE_LIMIT 30
118 
119 #define SHORTCUTS_PATH "/nautilus/scripts-accels"
120 
121 /* Delay to show the Loading... floating bar */
122 #define FLOATING_BAR_LOADING_DELAY 200 /* ms */
123 
124 #define MIN_COMMON_FILENAME_PREFIX_LENGTH 4
125 
126 enum
127 {
128     ADD_FILES,
129     BEGIN_FILE_CHANGES,
130     BEGIN_LOADING,
131     CLEAR,
132     END_FILE_CHANGES,
133     END_LOADING,
134     FILE_CHANGED,
135     MOVE_COPY_ITEMS,
136     REMOVE_FILE,
137     SELECTION_CHANGED,
138     TRASH,
139     DELETE,
140     LAST_SIGNAL
141 };
142 
143 enum
144 {
145     PROP_WINDOW_SLOT = 1,
146     PROP_SUPPORTS_ZOOMING,
147     PROP_ICON,
148     PROP_SEARCHING,
149     PROP_LOADING,
150     PROP_SELECTION,
151     PROP_LOCATION,
152     PROP_SEARCH_QUERY,
153     PROP_EXTENSIONS_BACKGROUND_MENU,
154     PROP_TEMPLATES_MENU,
155     NUM_PROPERTIES
156 };
157 
158 static guint signals[LAST_SIGNAL];
159 
160 static char *scripts_directory_uri = NULL;
161 static int scripts_directory_uri_length;
162 
163 static GHashTable *script_accels = NULL;
164 
165 typedef struct
166 {
167     /* Main components */
168     GtkWidget *overlay;
169 
170     NautilusWindowSlot *slot;
171     NautilusDirectory *model;
172     NautilusFile *directory_as_file;
173     GFile *location;
174     guint dir_merge_id;
175 
176     NautilusQuery *search_query;
177 
178     NautilusRenameFilePopoverController *rename_file_controller;
179     NautilusNewFolderDialogController *new_folder_controller;
180     NautilusCompressDialogController *compress_controller;
181 
182     gboolean supports_zooming;
183 
184     GList *scripts_directory_list;
185     GList *templates_directory_list;
186     gboolean scripts_menu_updated;
187     gboolean templates_menu_updated;
188 
189     guint display_selection_idle_id;
190     guint update_context_menus_timeout_id;
191     guint update_status_idle_id;
192     guint reveal_selection_idle_id;
193 
194     guint display_pending_source_id;
195     guint changes_timeout_id;
196 
197     guint update_interval;
198     guint64 last_queued;
199 
200     gulong files_added_handler_id;
201     gulong files_changed_handler_id;
202     gulong load_error_handler_id;
203     gulong done_loading_handler_id;
204     gulong file_changed_handler_id;
205 
206     /* Containers with FileAndDirectory* elements */
207     GList *new_added_files;
208     GList *new_changed_files;
209     GHashTable *non_ready_files;
210     GList *old_added_files;
211     GList *old_changed_files;
212 
213     GList *pending_selection;
214     GHashTable *pending_reveal;
215 
216     /* whether we are in the active slot */
217     gboolean active;
218 
219     /* loading indicates whether this view has begun loading a directory.
220      * This flag should need not be set inside subclasses. NautilusFilesView automatically
221      * sets 'loading' to TRUE before it begins loading a directory's contents and to FALSE
222      * after it finishes loading the directory and its view.
223      */
224     gboolean loading;
225 
226     gboolean in_destruction;
227 
228     gboolean sort_directories_first;
229 
230     gboolean show_hidden_files;
231     gboolean ignore_hidden_file_preferences;
232 
233     gboolean batching_selection_level;
234     gboolean selection_changed_while_batched;
235 
236     gboolean selection_was_removed;
237 
238     gboolean metadata_for_directory_as_file_pending;
239     gboolean metadata_for_files_in_directory_pending;
240 
241     GList *subdirectory_list;
242 
243     GMenu *selection_menu_model;
244     GMenu *background_menu_model;
245 
246     GtkWidget *selection_menu;
247     GtkWidget *background_menu;
248 
249     GActionGroup *view_action_group;
250 
251     GtkWidget *scrolled_window;
252 
253     /* Empty states */
254     GtkWidget *folder_is_empty_widget;
255     GtkWidget *trash_is_empty_widget;
256     GtkWidget *no_search_results_widget;
257     GtkWidget *starred_is_empty_widget;
258 
259     /* Floating bar */
260     guint floating_bar_set_status_timeout_id;
261     guint floating_bar_loading_timeout_id;
262     guint floating_bar_set_passthrough_timeout_id;
263     GtkWidget *floating_bar;
264 
265     /* Toolbar menu */
266     NautilusToolbarMenuSections *toolbar_menu_sections;
267     GtkWidget *sort_menu;
268     GtkWidget *sort_trash_time;
269     GtkWidget *visible_columns;
270     GtkWidget *stop;
271     GtkWidget *reload;
272     GtkWidget *zoom_controls_box;
273     GtkWidget *zoom_level_label;
274 
275     /* Exposed menus, for the path bar etc. */
276     GMenuModel *extensions_background_menu;
277     GMenuModel *templates_menu;
278 
279     /* Non exported menu, only for caching */
280     GMenuModel *scripts_menu;
281 
282     gulong stop_signal_handler;
283     gulong reload_signal_handler;
284 
285     GCancellable *starred_cancellable;
286     NautilusTagManager *tag_manager;
287 
288     gulong name_accepted_handler_id;
289     gulong cancelled_handler_id;
290 } NautilusFilesViewPrivate;
291 
292 /**
293  * FileAndDirectory:
294  * @file: A #NautilusFile
295  * @directory: A #NautilusDirectory where @file is present.
296  *
297  * The #FileAndDirectory struct is used to relate files to the directories they
298  * are displayed in. This is necessary because the same file can appear multiple
299  * times in the same view, by expanding folders as a tree in a list of search
300  * results. (Adapted from commit 671e4bdaa4d07b039015bedfcb5d42026e5d099e)
301  */
302 typedef struct
303 {
304     NautilusFile *file;
305     NautilusDirectory *directory;
306 } FileAndDirectory;
307 
308 typedef struct
309 {
310     NautilusFilesView *view;
311     GList *selection;
312 } CompressCallbackData;
313 
314 /* forward declarations */
315 
316 static gboolean display_selection_info_idle_callback (gpointer data);
317 static void     trash_or_delete_files (GtkWindow         *parent_window,
318                                        const GList       *files,
319                                        NautilusFilesView *view);
320 static void     load_directory (NautilusFilesView *view,
321                                 NautilusDirectory *directory);
322 static void on_clipboard_owner_changed (GtkClipboard *clipboard,
323                                         GdkEvent     *event,
324                                         gpointer      user_data);
325 static void     open_one_in_new_window (gpointer data,
326                                         gpointer callback_data);
327 static void     schedule_update_context_menus (NautilusFilesView *view);
328 static void     remove_update_context_menus_timeout_callback (NautilusFilesView *view);
329 static void     schedule_update_status (NautilusFilesView *view);
330 static void     remove_update_status_idle_callback (NautilusFilesView *view);
331 static void     reset_update_interval (NautilusFilesView *view);
332 static void     schedule_idle_display_of_pending_files (NautilusFilesView *view);
333 static void     unschedule_display_of_pending_files (NautilusFilesView *view);
334 static void     disconnect_model_handlers (NautilusFilesView *view);
335 static void     metadata_for_directory_as_file_ready_callback (NautilusFile *file,
336                                                                gpointer      callback_data);
337 static void     metadata_for_files_in_directory_ready_callback (NautilusDirectory *directory,
338                                                                 GList             *files,
339                                                                 gpointer           callback_data);
340 static void     nautilus_files_view_trash_state_changed_callback (NautilusTrashMonitor *trash,
341                                                                   gboolean              state,
342                                                                   gpointer              callback_data);
343 static void     nautilus_files_view_select_file (NautilusFilesView *view,
344                                                  NautilusFile      *file);
345 
346 static void     update_templates_directory (NautilusFilesView *view);
347 
348 static void     extract_files (NautilusFilesView *view,
349                                GList             *files,
350                                GFile             *destination_directory);
351 static void     extract_files_to_chosen_location (NautilusFilesView *view,
352                                                   GList             *files);
353 
354 static void     nautilus_files_view_check_empty_states (NautilusFilesView *view);
355 
356 static gboolean nautilus_files_view_is_searching (NautilusView *view);
357 
358 static void     nautilus_files_view_iface_init (NautilusViewInterface *view);
359 
360 static void     set_search_query_internal (NautilusFilesView *files_view,
361                                            NautilusQuery     *query,
362                                            NautilusDirectory *base_model);
363 
364 static gboolean nautilus_files_view_is_read_only (NautilusFilesView *view);
365 static void     set_wallpaper_fallback (NautilusFile *file,
366                                         gpointer      user_data);
367 
368 G_DEFINE_TYPE_WITH_CODE (NautilusFilesView,
369                          nautilus_files_view,
370                          GTK_TYPE_GRID,
371                          G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_VIEW, nautilus_files_view_iface_init)
372                          G_ADD_PRIVATE (NautilusFilesView));
373 
374 static const struct
375 {
376     unsigned int keyval;
377     const char *action;
378 } extra_view_keybindings [] =
379 {
380     /* View actions */
381     { GDK_KEY_ZoomIn, "zoom-in" },
382     { GDK_KEY_ZoomOut, "zoom-out" },
383 };
384 
385 /*
386  * Floating Bar code
387  */
388 static void
remove_loading_floating_bar(NautilusFilesView * view)389 remove_loading_floating_bar (NautilusFilesView *view)
390 {
391     NautilusFilesViewPrivate *priv;
392 
393     priv = nautilus_files_view_get_instance_private (view);
394 
395     if (priv->floating_bar_loading_timeout_id != 0)
396     {
397         g_source_remove (priv->floating_bar_loading_timeout_id);
398         priv->floating_bar_loading_timeout_id = 0;
399     }
400 
401     gtk_widget_hide (priv->floating_bar);
402     nautilus_floating_bar_cleanup_actions (NAUTILUS_FLOATING_BAR (priv->floating_bar));
403 }
404 
405 static void
real_setup_loading_floating_bar(NautilusFilesView * view)406 real_setup_loading_floating_bar (NautilusFilesView *view)
407 {
408     NautilusFilesViewPrivate *priv;
409 
410     priv = nautilus_files_view_get_instance_private (view);
411 
412     nautilus_floating_bar_cleanup_actions (NAUTILUS_FLOATING_BAR (priv->floating_bar));
413     nautilus_floating_bar_set_primary_label (NAUTILUS_FLOATING_BAR (priv->floating_bar),
414                                              nautilus_view_is_searching (NAUTILUS_VIEW (view)) ? _("Searching…") : _("Loading…"));
415     nautilus_floating_bar_set_details_label (NAUTILUS_FLOATING_BAR (priv->floating_bar), NULL);
416     nautilus_floating_bar_set_show_spinner (NAUTILUS_FLOATING_BAR (priv->floating_bar), priv->loading);
417     nautilus_floating_bar_add_action (NAUTILUS_FLOATING_BAR (priv->floating_bar),
418                                       "process-stop-symbolic",
419                                       NAUTILUS_FLOATING_BAR_ACTION_ID_STOP);
420 
421     gtk_widget_set_halign (priv->floating_bar, GTK_ALIGN_END);
422     gtk_widget_show (priv->floating_bar);
423 }
424 
425 static gboolean
setup_loading_floating_bar_timeout_cb(gpointer user_data)426 setup_loading_floating_bar_timeout_cb (gpointer user_data)
427 {
428     NautilusFilesView *view = user_data;
429     NautilusFilesViewPrivate *priv;
430 
431     priv = nautilus_files_view_get_instance_private (view);
432 
433     priv->floating_bar_loading_timeout_id = 0;
434     real_setup_loading_floating_bar (view);
435 
436     return FALSE;
437 }
438 
439 static void
setup_loading_floating_bar(NautilusFilesView * view)440 setup_loading_floating_bar (NautilusFilesView *view)
441 {
442     NautilusFilesViewPrivate *priv;
443 
444     priv = nautilus_files_view_get_instance_private (view);
445 
446     /* setup loading overlay */
447     if (priv->floating_bar_set_status_timeout_id != 0)
448     {
449         g_source_remove (priv->floating_bar_set_status_timeout_id);
450         priv->floating_bar_set_status_timeout_id = 0;
451     }
452 
453     if (priv->floating_bar_loading_timeout_id != 0)
454     {
455         g_source_remove (priv->floating_bar_loading_timeout_id);
456         priv->floating_bar_loading_timeout_id = 0;
457     }
458 
459     priv->floating_bar_loading_timeout_id =
460         g_timeout_add (FLOATING_BAR_LOADING_DELAY, setup_loading_floating_bar_timeout_cb, view);
461 }
462 
463 static void
floating_bar_action_cb(NautilusFloatingBar * floating_bar,gint action,NautilusFilesView * view)464 floating_bar_action_cb (NautilusFloatingBar *floating_bar,
465                         gint                 action,
466                         NautilusFilesView   *view)
467 {
468     NautilusFilesViewPrivate *priv;
469 
470     priv = nautilus_files_view_get_instance_private (view);
471 
472     if (action == NAUTILUS_FLOATING_BAR_ACTION_ID_STOP)
473     {
474         remove_loading_floating_bar (view);
475         nautilus_window_slot_stop_loading (priv->slot);
476     }
477 }
478 
479 static void
real_floating_bar_set_short_status(NautilusFilesView * view,const gchar * primary_status,const gchar * detail_status)480 real_floating_bar_set_short_status (NautilusFilesView *view,
481                                     const gchar       *primary_status,
482                                     const gchar       *detail_status)
483 {
484     NautilusFilesViewPrivate *priv;
485 
486     priv = nautilus_files_view_get_instance_private (view);
487 
488     if (priv->loading)
489     {
490         return;
491     }
492 
493     nautilus_floating_bar_cleanup_actions (NAUTILUS_FLOATING_BAR (priv->floating_bar));
494     nautilus_floating_bar_set_show_spinner (NAUTILUS_FLOATING_BAR (priv->floating_bar),
495                                             FALSE);
496 
497     if (primary_status == NULL && detail_status == NULL)
498     {
499         gtk_widget_hide (priv->floating_bar);
500         nautilus_floating_bar_remove_hover_timeout (NAUTILUS_FLOATING_BAR (priv->floating_bar));
501         return;
502     }
503 
504     nautilus_floating_bar_set_labels (NAUTILUS_FLOATING_BAR (priv->floating_bar),
505                                       primary_status,
506                                       detail_status);
507 
508     gtk_widget_show (priv->floating_bar);
509 }
510 
511 typedef struct
512 {
513     gchar *primary_status;
514     gchar *detail_status;
515     NautilusFilesView *view;
516 } FloatingBarSetStatusData;
517 
518 static void
floating_bar_set_status_data_free(gpointer data)519 floating_bar_set_status_data_free (gpointer data)
520 {
521     FloatingBarSetStatusData *status_data = data;
522 
523     g_free (status_data->primary_status);
524     g_free (status_data->detail_status);
525 
526     g_slice_free (FloatingBarSetStatusData, data);
527 }
528 
529 static gboolean
floating_bar_set_status_timeout_cb(gpointer data)530 floating_bar_set_status_timeout_cb (gpointer data)
531 {
532     NautilusFilesViewPrivate *priv;
533 
534     FloatingBarSetStatusData *status_data = data;
535 
536     priv = nautilus_files_view_get_instance_private (status_data->view);
537 
538     priv->floating_bar_set_status_timeout_id = 0;
539     real_floating_bar_set_short_status (status_data->view,
540                                         status_data->primary_status,
541                                         status_data->detail_status);
542 
543     return FALSE;
544 }
545 
546 static gboolean
remove_floating_bar_passthrough(gpointer data)547 remove_floating_bar_passthrough (gpointer data)
548 {
549     NautilusFilesViewPrivate *priv;
550 
551     priv = nautilus_files_view_get_instance_private (NAUTILUS_FILES_VIEW (data));
552     gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (priv->overlay),
553                                           priv->floating_bar, FALSE);
554     priv->floating_bar_set_passthrough_timeout_id = 0;
555 
556     return G_SOURCE_REMOVE;
557 }
558 
559 static void
set_floating_bar_status(NautilusFilesView * view,const gchar * primary_status,const gchar * detail_status)560 set_floating_bar_status (NautilusFilesView *view,
561                          const gchar       *primary_status,
562                          const gchar       *detail_status)
563 {
564     GtkSettings *settings;
565     gint double_click_time;
566     FloatingBarSetStatusData *status_data;
567     NautilusFilesViewPrivate *priv;
568 
569     priv = nautilus_files_view_get_instance_private (view);
570 
571     if (priv->floating_bar_set_status_timeout_id != 0)
572     {
573         g_source_remove (priv->floating_bar_set_status_timeout_id);
574         priv->floating_bar_set_status_timeout_id = 0;
575     }
576 
577     settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (view)));
578     g_object_get (settings,
579                   "gtk-double-click-time", &double_click_time,
580                   NULL);
581 
582     status_data = g_slice_new0 (FloatingBarSetStatusData);
583     status_data->primary_status = g_strdup (primary_status);
584     status_data->detail_status = g_strdup (detail_status);
585     status_data->view = view;
586 
587     if (priv->floating_bar_set_passthrough_timeout_id != 0)
588     {
589         g_source_remove (priv->floating_bar_set_passthrough_timeout_id);
590         priv->floating_bar_set_passthrough_timeout_id = 0;
591     }
592     /* Activate passthrough on the floating bar just long enough for a
593      * potential double click to happen, so to not interfere with it */
594     gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (priv->overlay),
595                                           priv->floating_bar, TRUE);
596     priv->floating_bar_set_passthrough_timeout_id = g_timeout_add ((guint) double_click_time,
597                                                                    remove_floating_bar_passthrough,
598                                                                    view);
599 
600     /* waiting for half of the double-click-time before setting
601      * the status seems to be a good approximation of not setting it
602      * too often and not delaying the statusbar too much.
603      */
604     priv->floating_bar_set_status_timeout_id =
605         g_timeout_add_full (G_PRIORITY_DEFAULT,
606                             (guint) (double_click_time / 2),
607                             floating_bar_set_status_timeout_cb,
608                             status_data,
609                             floating_bar_set_status_data_free);
610 }
611 
612 static char *
real_get_backing_uri(NautilusFilesView * view)613 real_get_backing_uri (NautilusFilesView *view)
614 {
615     NautilusFilesViewPrivate *priv;
616 
617     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
618 
619     priv = nautilus_files_view_get_instance_private (view);
620 
621     if (priv->model == NULL)
622     {
623         return NULL;
624     }
625 
626     return nautilus_directory_get_uri (priv->model);
627 }
628 
629 /**
630  *
631  * nautilus_files_view_get_backing_uri:
632  *
633  * Returns the URI for the target location of new directory, new file, new
634  * link and paste operations.
635  */
636 
637 char *
nautilus_files_view_get_backing_uri(NautilusFilesView * view)638 nautilus_files_view_get_backing_uri (NautilusFilesView *view)
639 {
640     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
641 
642     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_backing_uri (view);
643 }
644 
645 /**
646  * nautilus_files_view_select_all:
647  *
648  * select all the items in the view
649  *
650  **/
651 static void
nautilus_files_view_select_all(NautilusFilesView * view)652 nautilus_files_view_select_all (NautilusFilesView *view)
653 {
654     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
655 
656     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->select_all (view);
657 }
658 
659 static void
nautilus_files_view_select_first(NautilusFilesView * view)660 nautilus_files_view_select_first (NautilusFilesView *view)
661 {
662     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
663 
664     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->select_first (view);
665 }
666 
667 static void
nautilus_files_view_call_set_selection(NautilusFilesView * view,GList * selection)668 nautilus_files_view_call_set_selection (NautilusFilesView *view,
669                                         GList             *selection)
670 {
671     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
672 
673     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->set_selection (view, selection);
674 }
675 
676 static GList *
nautilus_files_view_get_selection_for_file_transfer(NautilusFilesView * view)677 nautilus_files_view_get_selection_for_file_transfer (NautilusFilesView *view)
678 {
679     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
680 
681     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection_for_file_transfer (view);
682 }
683 
684 static void
nautilus_files_view_invert_selection(NautilusFilesView * view)685 nautilus_files_view_invert_selection (NautilusFilesView *view)
686 {
687     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
688 
689     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->invert_selection (view);
690 }
691 
692 /**
693  * nautilus_files_view_reveal_selection:
694  *
695  * Scroll as necessary to reveal the selected items.
696  **/
697 static void
nautilus_files_view_reveal_selection(NautilusFilesView * view)698 nautilus_files_view_reveal_selection (NautilusFilesView *view)
699 {
700     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
701 
702     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reveal_selection (view);
703 }
704 
705 /**
706  * nautilus_files_view_get_toolbar_menu_sections:
707  * @view: a #NautilusFilesView
708  *
709  * Retrieves the menu sections that should be added to the toolbar menu when
710  * this view is active
711  *
712  * Returns: (transfer none): a #NautilusToolbarMenuSections with the details of
713  * which menu sections should be added to the menu
714  */
715 static NautilusToolbarMenuSections *
nautilus_files_view_get_toolbar_menu_sections(NautilusView * view)716 nautilus_files_view_get_toolbar_menu_sections (NautilusView *view)
717 {
718     NautilusFilesViewPrivate *priv;
719 
720     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
721 
722     priv = nautilus_files_view_get_instance_private (NAUTILUS_FILES_VIEW (view));
723 
724     return priv->toolbar_menu_sections;
725 }
726 
727 static GMenuModel *
nautilus_files_view_get_templates_menu(NautilusView * self)728 nautilus_files_view_get_templates_menu (NautilusView *self)
729 {
730     GMenuModel *menu;
731 
732     g_object_get (self, "templates-menu", &menu, NULL);
733 
734     return menu;
735 }
736 
737 static GMenuModel *
nautilus_files_view_get_extensions_background_menu(NautilusView * self)738 nautilus_files_view_get_extensions_background_menu (NautilusView *self)
739 {
740     GMenuModel *menu;
741 
742     g_object_get (self, "extensions-background-menu", &menu, NULL);
743 
744     return menu;
745 }
746 
747 static GMenuModel *
real_get_extensions_background_menu(NautilusView * view)748 real_get_extensions_background_menu (NautilusView *view)
749 {
750     NautilusFilesViewPrivate *priv;
751 
752     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
753 
754     priv = nautilus_files_view_get_instance_private (NAUTILUS_FILES_VIEW (view));
755 
756     return priv->extensions_background_menu;
757 }
758 
759 static GMenuModel *
real_get_templates_menu(NautilusView * view)760 real_get_templates_menu (NautilusView *view)
761 {
762     NautilusFilesViewPrivate *priv;
763 
764     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
765 
766     priv = nautilus_files_view_get_instance_private (NAUTILUS_FILES_VIEW (view));
767 
768     return priv->templates_menu;
769 }
770 
771 static void
nautilus_files_view_set_templates_menu(NautilusView * self,GMenuModel * menu)772 nautilus_files_view_set_templates_menu (NautilusView *self,
773                                         GMenuModel   *menu)
774 {
775     g_object_set (self, "templates-menu", menu, NULL);
776 }
777 
778 static void
nautilus_files_view_set_extensions_background_menu(NautilusView * self,GMenuModel * menu)779 nautilus_files_view_set_extensions_background_menu (NautilusView *self,
780                                                     GMenuModel   *menu)
781 {
782     g_object_set (self, "extensions-background-menu", menu, NULL);
783 }
784 
785 static void
real_set_extensions_background_menu(NautilusView * view,GMenuModel * menu)786 real_set_extensions_background_menu (NautilusView *view,
787                                      GMenuModel   *menu)
788 {
789     NautilusFilesViewPrivate *priv;
790 
791     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
792 
793     priv = nautilus_files_view_get_instance_private (NAUTILUS_FILES_VIEW (view));
794 
795     g_set_object (&priv->extensions_background_menu, menu);
796 }
797 
798 static void
real_set_templates_menu(NautilusView * view,GMenuModel * menu)799 real_set_templates_menu (NautilusView *view,
800                          GMenuModel   *menu)
801 {
802     NautilusFilesViewPrivate *priv;
803 
804     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
805 
806     priv = nautilus_files_view_get_instance_private (NAUTILUS_FILES_VIEW (view));
807 
808     g_set_object (&priv->templates_menu, menu);
809 }
810 
811 static gboolean
showing_trash_directory(NautilusFilesView * view)812 showing_trash_directory (NautilusFilesView *view)
813 {
814     NautilusFile *file;
815 
816     file = nautilus_files_view_get_directory_as_file (view);
817     if (file != NULL)
818     {
819         return nautilus_file_is_in_trash (file);
820     }
821     return FALSE;
822 }
823 
824 static gboolean
showing_recent_directory(NautilusFilesView * view)825 showing_recent_directory (NautilusFilesView *view)
826 {
827     NautilusFile *file;
828 
829     file = nautilus_files_view_get_directory_as_file (view);
830     if (file != NULL)
831     {
832         return nautilus_file_is_in_recent (file);
833     }
834     return FALSE;
835 }
836 
837 static gboolean
showing_starred_directory(NautilusFilesView * view)838 showing_starred_directory (NautilusFilesView *view)
839 {
840     NautilusFile *file;
841 
842     file = nautilus_files_view_get_directory_as_file (view);
843     if (file != NULL)
844     {
845         return nautilus_file_is_in_starred (file);
846     }
847     return FALSE;
848 }
849 
850 static gboolean
nautilus_files_view_supports_creating_files(NautilusFilesView * view)851 nautilus_files_view_supports_creating_files (NautilusFilesView *view)
852 {
853     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
854 
855     return !nautilus_files_view_is_read_only (view)
856            && !showing_trash_directory (view)
857            && !showing_recent_directory (view)
858            && !showing_starred_directory (view);
859 }
860 
861 static gboolean
nautilus_files_view_supports_extract_here(NautilusFilesView * view)862 nautilus_files_view_supports_extract_here (NautilusFilesView *view)
863 {
864     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
865 
866     return nautilus_files_view_supports_creating_files (view)
867            && !nautilus_view_is_searching (NAUTILUS_VIEW (view));
868 }
869 
870 static gboolean
nautilus_files_view_is_empty(NautilusFilesView * view)871 nautilus_files_view_is_empty (NautilusFilesView *view)
872 {
873     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
874 
875     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->is_empty (view);
876 }
877 
878 /**
879  * nautilus_files_view_bump_zoom_level:
880  *
881  * bump the current zoom level by invoking the relevant subclass through the slot
882  *
883  **/
884 void
nautilus_files_view_bump_zoom_level(NautilusFilesView * view,int zoom_increment)885 nautilus_files_view_bump_zoom_level (NautilusFilesView *view,
886                                      int                zoom_increment)
887 {
888     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
889 
890     if (!nautilus_files_view_supports_zooming (view))
891     {
892         return;
893     }
894 
895     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->bump_zoom_level (view, zoom_increment);
896 }
897 
898 /**
899  * nautilus_files_view_can_zoom_in:
900  *
901  * Determine whether the view can be zoomed any closer.
902  * @view: The zoomable NautilusFilesView.
903  *
904  * Return value: TRUE if @view can be zoomed any closer, FALSE otherwise.
905  *
906  **/
907 gboolean
nautilus_files_view_can_zoom_in(NautilusFilesView * view)908 nautilus_files_view_can_zoom_in (NautilusFilesView *view)
909 {
910     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
911 
912     if (!nautilus_files_view_supports_zooming (view))
913     {
914         return FALSE;
915     }
916 
917     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_zoom_in (view);
918 }
919 
920 /**
921  * nautilus_files_view_can_zoom_out:
922  *
923  * Determine whether the view can be zoomed any further away.
924  * @view: The zoomable NautilusFilesView.
925  *
926  * Return value: TRUE if @view can be zoomed any further away, FALSE otherwise.
927  *
928  **/
929 gboolean
nautilus_files_view_can_zoom_out(NautilusFilesView * view)930 nautilus_files_view_can_zoom_out (NautilusFilesView *view)
931 {
932     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
933 
934     if (!nautilus_files_view_supports_zooming (view))
935     {
936         return FALSE;
937     }
938 
939     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->can_zoom_out (view);
940 }
941 
942 gboolean
nautilus_files_view_supports_zooming(NautilusFilesView * view)943 nautilus_files_view_supports_zooming (NautilusFilesView *view)
944 {
945     NautilusFilesViewPrivate *priv;
946 
947     priv = nautilus_files_view_get_instance_private (view);
948 
949     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
950 
951     return priv->supports_zooming;
952 }
953 
954 /**
955  * nautilus_files_view_restore_standard_zoom_level:
956  *
957  * Restore the zoom level to 100%
958  */
959 static void
nautilus_files_view_restore_standard_zoom_level(NautilusFilesView * view)960 nautilus_files_view_restore_standard_zoom_level (NautilusFilesView *view)
961 {
962     if (!nautilus_files_view_supports_zooming (view))
963     {
964         return;
965     }
966 
967     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->restore_standard_zoom_level (view);
968 }
969 
970 static gfloat
nautilus_files_view_get_zoom_level_percentage(NautilusFilesView * view)971 nautilus_files_view_get_zoom_level_percentage (NautilusFilesView *view)
972 {
973     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), 1);
974 
975     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_zoom_level_percentage (view);
976 }
977 
978 static gboolean
nautilus_files_view_is_zoom_level_default(NautilusFilesView * view)979 nautilus_files_view_is_zoom_level_default (NautilusFilesView *view)
980 {
981     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
982 
983     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->is_zoom_level_default (view);
984 }
985 
986 gboolean
nautilus_files_view_is_searching(NautilusView * view)987 nautilus_files_view_is_searching (NautilusView *view)
988 {
989     NautilusFilesView *files_view;
990     NautilusFilesViewPrivate *priv;
991 
992     files_view = NAUTILUS_FILES_VIEW (view);
993     priv = nautilus_files_view_get_instance_private (files_view);
994 
995     if (!priv->model)
996     {
997         return FALSE;
998     }
999 
1000     return NAUTILUS_IS_SEARCH_DIRECTORY (priv->model);
1001 }
1002 
1003 static guint
nautilus_files_view_get_view_id(NautilusView * view)1004 nautilus_files_view_get_view_id (NautilusView *view)
1005 {
1006     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_view_id (NAUTILUS_FILES_VIEW (view));
1007 }
1008 
1009 char *
nautilus_files_view_get_first_visible_file(NautilusFilesView * view)1010 nautilus_files_view_get_first_visible_file (NautilusFilesView *view)
1011 {
1012     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_first_visible_file (view);
1013 }
1014 
1015 void
nautilus_files_view_scroll_to_file(NautilusFilesView * view,const char * uri)1016 nautilus_files_view_scroll_to_file (NautilusFilesView *view,
1017                                     const char        *uri)
1018 {
1019     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->scroll_to_file (view, uri);
1020 }
1021 
1022 /**
1023  * nautilus_files_view_get_selection:
1024  *
1025  * Get a list of NautilusFile pointers that represents the
1026  * currently-selected items in this view. Subclasses must override
1027  * the signal handler for the 'get_selection' signal. Callers are
1028  * responsible for g_free-ing the list (and unrefing its data).
1029  * @view: NautilusFilesView whose selected items are of interest.
1030  *
1031  * Return value: GList of NautilusFile pointers representing the selection.
1032  *
1033  **/
1034 static GList *
nautilus_files_view_get_selection(NautilusView * view)1035 nautilus_files_view_get_selection (NautilusView *view)
1036 {
1037     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
1038 
1039     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->get_selection (NAUTILUS_FILES_VIEW (view));
1040 }
1041 
1042 typedef struct
1043 {
1044     NautilusFile *file;
1045     NautilusFilesView *directory_view;
1046 } ScriptLaunchParameters;
1047 
1048 typedef struct
1049 {
1050     NautilusFile *file;
1051     NautilusFilesView *directory_view;
1052 } CreateTemplateParameters;
1053 
1054 static FileAndDirectory *
file_and_directory_new(NautilusFile * file,NautilusDirectory * directory)1055 file_and_directory_new (NautilusFile      *file,
1056                         NautilusDirectory *directory)
1057 {
1058     FileAndDirectory *fad;
1059 
1060     fad = g_new0 (FileAndDirectory, 1);
1061     fad->directory = nautilus_directory_ref (directory);
1062     fad->file = nautilus_file_ref (file);
1063 
1064     return fad;
1065 }
1066 
1067 static NautilusFile *
file_and_directory_get_file(FileAndDirectory * fad)1068 file_and_directory_get_file (FileAndDirectory *fad)
1069 {
1070     g_return_val_if_fail (fad != NULL, NULL);
1071 
1072     return nautilus_file_ref (fad->file);
1073 }
1074 
1075 static void
file_and_directory_free(gpointer data)1076 file_and_directory_free (gpointer data)
1077 {
1078     FileAndDirectory *fad = data;
1079 
1080     nautilus_directory_unref (fad->directory);
1081     nautilus_file_unref (fad->file);
1082     g_free (fad);
1083 }
1084 
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FileAndDirectory,file_and_directory_free)1085 G_DEFINE_AUTOPTR_CLEANUP_FUNC (FileAndDirectory, file_and_directory_free)
1086 
1087 static gboolean
1088 file_and_directory_equal (gconstpointer v1,
1089                           gconstpointer v2)
1090 {
1091     const FileAndDirectory *fad1, *fad2;
1092     fad1 = v1;
1093     fad2 = v2;
1094 
1095     return (fad1->file == fad2->file &&
1096             fad1->directory == fad2->directory);
1097 }
1098 
1099 static guint
file_and_directory_hash(gconstpointer v)1100 file_and_directory_hash  (gconstpointer v)
1101 {
1102     const FileAndDirectory *fad;
1103 
1104     fad = v;
1105     return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory);
1106 }
1107 
1108 static ScriptLaunchParameters *
script_launch_parameters_new(NautilusFile * file,NautilusFilesView * directory_view)1109 script_launch_parameters_new (NautilusFile      *file,
1110                               NautilusFilesView *directory_view)
1111 {
1112     ScriptLaunchParameters *result;
1113 
1114     result = g_new0 (ScriptLaunchParameters, 1);
1115     result->directory_view = directory_view;
1116     nautilus_file_ref (file);
1117     result->file = file;
1118 
1119     return result;
1120 }
1121 
1122 static void
script_launch_parameters_free(ScriptLaunchParameters * parameters)1123 script_launch_parameters_free (ScriptLaunchParameters *parameters)
1124 {
1125     nautilus_file_unref (parameters->file);
1126     g_free (parameters);
1127 }
1128 
1129 static CreateTemplateParameters *
create_template_parameters_new(NautilusFile * file,NautilusFilesView * directory_view)1130 create_template_parameters_new (NautilusFile      *file,
1131                                 NautilusFilesView *directory_view)
1132 {
1133     CreateTemplateParameters *result;
1134 
1135     result = g_new0 (CreateTemplateParameters, 1);
1136     result->directory_view = directory_view;
1137     nautilus_file_ref (file);
1138     result->file = file;
1139 
1140     return result;
1141 }
1142 
1143 static void
create_templates_parameters_free(CreateTemplateParameters * parameters)1144 create_templates_parameters_free (CreateTemplateParameters *parameters)
1145 {
1146     nautilus_file_unref (parameters->file);
1147     g_free (parameters);
1148 }
1149 
1150 NautilusWindow *
nautilus_files_view_get_window(NautilusFilesView * view)1151 nautilus_files_view_get_window (NautilusFilesView *view)
1152 {
1153     NautilusFilesViewPrivate *priv;
1154 
1155     priv = nautilus_files_view_get_instance_private (view);
1156 
1157     return nautilus_window_slot_get_window (priv->slot);
1158 }
1159 
1160 NautilusWindowSlot *
nautilus_files_view_get_nautilus_window_slot(NautilusFilesView * view)1161 nautilus_files_view_get_nautilus_window_slot (NautilusFilesView *view)
1162 {
1163     NautilusFilesViewPrivate *priv;
1164 
1165     priv = nautilus_files_view_get_instance_private (view);
1166 
1167     g_assert (priv->slot != NULL);
1168 
1169     return priv->slot;
1170 }
1171 
1172 /* Returns the GtkWindow that this directory view occupies, or NULL
1173  * if at the moment this directory view is not in a GtkWindow or the
1174  * GtkWindow cannot be determined. Primarily used for parenting dialogs.
1175  */
1176 static GtkWindow *
nautilus_files_view_get_containing_window(NautilusFilesView * view)1177 nautilus_files_view_get_containing_window (NautilusFilesView *view)
1178 {
1179     GtkWidget *window;
1180 
1181     g_assert (NAUTILUS_IS_FILES_VIEW (view));
1182 
1183     window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW);
1184     if (window == NULL)
1185     {
1186         return NULL;
1187     }
1188 
1189     return GTK_WINDOW (window);
1190 }
1191 
1192 static gboolean
nautilus_files_view_confirm_multiple(GtkWindow * parent_window,int count,gboolean tabs)1193 nautilus_files_view_confirm_multiple (GtkWindow *parent_window,
1194                                       int        count,
1195                                       gboolean   tabs)
1196 {
1197     GtkDialog *dialog;
1198     char *prompt;
1199     char *detail;
1200     int response;
1201 
1202     if (count <= SILENT_WINDOW_OPEN_LIMIT)
1203     {
1204         return TRUE;
1205     }
1206 
1207     prompt = _("Are you sure you want to open all files?");
1208     if (tabs)
1209     {
1210         detail = g_strdup_printf (ngettext ("This will open %'d separate tab.",
1211                                             "This will open %'d separate tabs.", count), count);
1212     }
1213     else
1214     {
1215         detail = g_strdup_printf (ngettext ("This will open %'d separate window.",
1216                                             "This will open %'d separate windows.", count), count);
1217     }
1218     dialog = eel_show_yes_no_dialog (prompt, detail,
1219                                      _("_OK"), _("_Cancel"),
1220                                      parent_window);
1221     g_free (detail);
1222 
1223     response = gtk_dialog_run (dialog);
1224     gtk_widget_destroy (GTK_WIDGET (dialog));
1225 
1226     return response == GTK_RESPONSE_YES;
1227 }
1228 
1229 static char *
get_view_directory(NautilusFilesView * view)1230 get_view_directory (NautilusFilesView *view)
1231 {
1232     NautilusFilesViewPrivate *priv;
1233     char *uri, *path;
1234     GFile *f;
1235 
1236     priv = nautilus_files_view_get_instance_private (view);
1237 
1238     uri = nautilus_directory_get_uri (priv->model);
1239     f = g_file_new_for_uri (uri);
1240     path = g_file_get_path (f);
1241     g_object_unref (f);
1242     g_free (uri);
1243 
1244     return path;
1245 }
1246 
1247 typedef struct
1248 {
1249     gchar *uri;
1250     gboolean is_update;
1251 } PreviewExportData;
1252 
1253 static void
preview_export_data_free(gpointer _data)1254 preview_export_data_free (gpointer _data)
1255 {
1256     PreviewExportData *data = _data;
1257     g_free (data->uri);
1258     g_free (data);
1259 }
1260 
G_DEFINE_AUTOPTR_CLEANUP_FUNC(PreviewExportData,preview_export_data_free)1261 G_DEFINE_AUTOPTR_CLEANUP_FUNC (PreviewExportData, preview_export_data_free)
1262 
1263 static void
1264 on_window_handle_export (NautilusWindow *window,
1265                          const char     *handle,
1266                          guint           xid,
1267                          gpointer        user_data)
1268 {
1269     g_autoptr (PreviewExportData) data = user_data;
1270     nautilus_previewer_call_show_file (data->uri, handle, xid, !data->is_update);
1271 }
1272 
1273 static void
nautilus_files_view_preview(NautilusFilesView * view,PreviewExportData * data)1274 nautilus_files_view_preview (NautilusFilesView *view,
1275                              PreviewExportData *data)
1276 {
1277     if (!nautilus_window_export_handle (nautilus_files_view_get_window (view),
1278                                         on_window_handle_export,
1279                                         data))
1280     {
1281         /* Let's use a fallback, so at least a preview will be displayed */
1282         nautilus_previewer_call_show_file (data->uri, "x11:0", 0, !data->is_update);
1283     }
1284 }
1285 
1286 void
nautilus_files_view_preview_files(NautilusFilesView * view,GList * files,GArray * locations)1287 nautilus_files_view_preview_files (NautilusFilesView *view,
1288                                    GList             *files,
1289                                    GArray            *locations)
1290 {
1291     PreviewExportData *data = g_new0 (PreviewExportData, 1);
1292 
1293     data->uri = nautilus_file_get_uri (files->data);
1294     data->is_update = FALSE;
1295 
1296     nautilus_files_view_preview (view, data);
1297 }
1298 
1299 static void
nautilus_files_view_preview_update(NautilusFilesView * view)1300 nautilus_files_view_preview_update (NautilusFilesView *view)
1301 {
1302     NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view);
1303     GtkApplication *app;
1304     GtkWindow *window;
1305     g_autolist (NautilusFile) selection = NULL;
1306     PreviewExportData *data;
1307 
1308     if (!priv->active ||
1309         !nautilus_previewer_is_visible ())
1310     {
1311         return;
1312     }
1313 
1314     app = GTK_APPLICATION (g_application_get_default ());
1315     window = GTK_WINDOW (nautilus_files_view_get_window (view));
1316     if (window == NULL || window != gtk_application_get_active_window (app))
1317     {
1318         return;
1319     }
1320 
1321     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1322     if (selection == NULL)
1323     {
1324         return;
1325     }
1326 
1327     data = g_new0 (PreviewExportData, 1);
1328     data->uri = nautilus_file_get_uri (selection->data);
1329     data->is_update = TRUE;
1330 
1331     nautilus_files_view_preview (view, data);
1332 }
1333 
1334 void
nautilus_files_view_preview_selection_event(NautilusFilesView * view,GtkDirectionType direction)1335 nautilus_files_view_preview_selection_event (NautilusFilesView *view,
1336                                              GtkDirectionType   direction)
1337 {
1338     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->preview_selection_event (view, direction);
1339 }
1340 
1341 void
nautilus_files_view_activate_selection(NautilusFilesView * view)1342 nautilus_files_view_activate_selection (NautilusFilesView *view)
1343 {
1344     g_autolist (NautilusFile) selection = NULL;
1345 
1346     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1347     nautilus_files_view_activate_files (view,
1348                                         selection,
1349                                         0,
1350                                         TRUE);
1351 }
1352 
1353 void
nautilus_files_view_activate_files(NautilusFilesView * view,GList * files,NautilusWindowOpenFlags flags,gboolean confirm_multiple)1354 nautilus_files_view_activate_files (NautilusFilesView       *view,
1355                                     GList                   *files,
1356                                     NautilusWindowOpenFlags  flags,
1357                                     gboolean                 confirm_multiple)
1358 {
1359     NautilusFilesViewPrivate *priv;
1360     GList *files_to_extract;
1361     GList *files_to_activate;
1362     char *path;
1363 
1364     if (files == NULL)
1365     {
1366         return;
1367     }
1368 
1369     priv = nautilus_files_view_get_instance_private (view);
1370 
1371     files_to_extract = nautilus_file_list_filter (files,
1372                                                   &files_to_activate,
1373                                                   (NautilusFileFilterFunc) nautilus_mime_file_extracts,
1374                                                   NULL);
1375 
1376     if (nautilus_files_view_supports_extract_here (view))
1377     {
1378         g_autoptr (GFile) location = NULL;
1379         g_autoptr (GFile) parent = NULL;
1380 
1381         location = nautilus_file_get_location (NAUTILUS_FILE (g_list_first (files)->data));
1382         /* Get a parent from a random file. We assume all files has a common parent.
1383          * But don't assume the parent is the view location, since that's not the
1384          * case in list view when expand-folder setting is set
1385          */
1386         parent = g_file_get_parent (location);
1387         extract_files (view, files_to_extract, parent);
1388     }
1389     else
1390     {
1391         extract_files_to_chosen_location (view, files_to_extract);
1392     }
1393 
1394     path = get_view_directory (view);
1395     nautilus_mime_activate_files (nautilus_files_view_get_containing_window (view),
1396                                   priv->slot,
1397                                   files_to_activate,
1398                                   path,
1399                                   flags,
1400                                   confirm_multiple);
1401 
1402     g_free (path);
1403     g_list_free (files_to_extract);
1404     g_list_free (files_to_activate);
1405 }
1406 
1407 void
nautilus_files_view_activate_file(NautilusFilesView * view,NautilusFile * file,NautilusWindowOpenFlags flags)1408 nautilus_files_view_activate_file (NautilusFilesView       *view,
1409                                    NautilusFile            *file,
1410                                    NautilusWindowOpenFlags  flags)
1411 {
1412     g_autoptr (GList) files = NULL;
1413 
1414     files = g_list_append (files, file);
1415     nautilus_files_view_activate_files (view, files, flags, FALSE);
1416 }
1417 
1418 static void
action_open_with_default_application(GSimpleAction * action,GVariant * state,gpointer user_data)1419 action_open_with_default_application (GSimpleAction *action,
1420                                       GVariant      *state,
1421                                       gpointer       user_data)
1422 {
1423     NautilusFilesView *view;
1424 
1425     view = NAUTILUS_FILES_VIEW (user_data);
1426     nautilus_files_view_activate_selection (view);
1427 }
1428 
1429 static void
action_open_item_location(GSimpleAction * action,GVariant * state,gpointer user_data)1430 action_open_item_location (GSimpleAction *action,
1431                            GVariant      *state,
1432                            gpointer       user_data)
1433 {
1434     NautilusFilesView *view;
1435     g_autolist (NautilusFile) selection = NULL;
1436     NautilusFile *item;
1437     GFile *activation_location;
1438     NautilusFile *activation_file;
1439     NautilusFile *parent;
1440     g_autoptr (GFile) parent_location = NULL;
1441 
1442     view = NAUTILUS_FILES_VIEW (user_data);
1443     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1444 
1445     if (!selection)
1446     {
1447         return;
1448     }
1449 
1450     item = NAUTILUS_FILE (selection->data);
1451     activation_location = nautilus_file_get_activation_location (item);
1452     activation_file = nautilus_file_get (activation_location);
1453     parent = nautilus_file_get_parent (activation_file);
1454     parent_location = nautilus_file_get_location (parent);
1455 
1456     if (nautilus_file_is_in_recent (item))
1457     {
1458         /* Selection logic will check against a NautilusFile of the
1459          * activation uri, not the recent:// one. Fixes bug 784516 */
1460         nautilus_file_unref (item);
1461         item = nautilus_file_ref (activation_file);
1462         selection->data = item;
1463     }
1464 
1465     nautilus_application_open_location_full (NAUTILUS_APPLICATION (g_application_get_default ()),
1466                                              parent_location, 0, selection, NULL,
1467                                              nautilus_files_view_get_nautilus_window_slot (view));
1468 
1469     nautilus_file_unref (parent);
1470     nautilus_file_unref (activation_file);
1471     g_object_unref (activation_location);
1472 }
1473 
1474 static void
action_open_item_new_tab(GSimpleAction * action,GVariant * state,gpointer user_data)1475 action_open_item_new_tab (GSimpleAction *action,
1476                           GVariant      *state,
1477                           gpointer       user_data)
1478 {
1479     NautilusFilesView *view;
1480     g_autolist (NautilusFile) selection = NULL;
1481     GtkWindow *window;
1482 
1483     view = NAUTILUS_FILES_VIEW (user_data);
1484     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1485 
1486     window = nautilus_files_view_get_containing_window (view);
1487 
1488     if (nautilus_files_view_confirm_multiple (window, g_list_length (selection), TRUE))
1489     {
1490         nautilus_files_view_activate_files (view,
1491                                             selection,
1492                                             NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB |
1493                                             NAUTILUS_WINDOW_OPEN_FLAG_DONT_MAKE_ACTIVE,
1494                                             FALSE);
1495     }
1496 }
1497 
1498 static void
app_chooser_dialog_response_cb(GtkDialog * dialog,gint response_id,gpointer user_data)1499 app_chooser_dialog_response_cb (GtkDialog *dialog,
1500                                 gint       response_id,
1501                                 gpointer   user_data)
1502 {
1503     GtkWindow *parent_window;
1504     GList *files;
1505     GAppInfo *info;
1506 
1507     parent_window = user_data;
1508     files = g_object_get_data (G_OBJECT (dialog), "directory-view:files");
1509 
1510     if (response_id != GTK_RESPONSE_OK)
1511     {
1512         goto out;
1513     }
1514 
1515     info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (dialog));
1516 
1517     g_signal_emit_by_name (nautilus_signaller_get_current (), "mime-data-changed");
1518 
1519     nautilus_launch_application (info, files, parent_window);
1520 
1521     g_object_unref (info);
1522 out:
1523     gtk_widget_destroy (GTK_WIDGET (dialog));
1524 }
1525 
1526 static void
choose_program(NautilusFilesView * view,GList * files)1527 choose_program (NautilusFilesView *view,
1528                 GList             *files)
1529 {
1530     GtkWidget *dialog;
1531     g_autofree gchar *mime_type = NULL;
1532     GtkWindow *parent_window;
1533 
1534     g_assert (NAUTILUS_IS_FILES_VIEW (view));
1535 
1536     mime_type = nautilus_file_get_mime_type (files->data);
1537     parent_window = nautilus_files_view_get_containing_window (view);
1538 
1539     dialog = gtk_app_chooser_dialog_new_for_content_type (parent_window,
1540                                                           GTK_DIALOG_MODAL |
1541                                                           GTK_DIALOG_DESTROY_WITH_PARENT |
1542                                                           GTK_DIALOG_USE_HEADER_BAR,
1543                                                           mime_type);
1544     g_object_set_data_full (G_OBJECT (dialog),
1545                             "directory-view:files",
1546                             files,
1547                             (GDestroyNotify) nautilus_file_list_free);
1548     gtk_widget_show (dialog);
1549 
1550     g_signal_connect_object (dialog, "response",
1551                              G_CALLBACK (app_chooser_dialog_response_cb),
1552                              parent_window, 0);
1553 }
1554 
1555 static void
open_with_other_program(NautilusFilesView * view)1556 open_with_other_program (NautilusFilesView *view)
1557 {
1558     g_autolist (NautilusFile) selection = NULL;
1559 
1560     g_assert (NAUTILUS_IS_FILES_VIEW (view));
1561 
1562     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1563     choose_program (view, g_steal_pointer (&selection));
1564 }
1565 
1566 static void
action_open_with_other_application(GSimpleAction * action,GVariant * state,gpointer user_data)1567 action_open_with_other_application (GSimpleAction *action,
1568                                     GVariant      *state,
1569                                     gpointer       user_data)
1570 {
1571     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
1572 
1573     open_with_other_program (NAUTILUS_FILES_VIEW (user_data));
1574 }
1575 
1576 static void
trash_or_delete_selected_files(NautilusFilesView * view)1577 trash_or_delete_selected_files (NautilusFilesView *view)
1578 {
1579     NautilusFilesViewPrivate *priv;
1580     priv = nautilus_files_view_get_instance_private (view);
1581 
1582     /* This might be rapidly called multiple times for the same selection
1583      * when using keybindings. So we remember if the current selection
1584      * was already removed (but the view doesn't know about it yet).
1585      */
1586     if (!priv->selection_was_removed)
1587     {
1588         g_autolist (NautilusFile) selection = NULL;
1589         selection = nautilus_files_view_get_selection_for_file_transfer (view);
1590         trash_or_delete_files (nautilus_files_view_get_containing_window (view),
1591                                selection,
1592                                view);
1593         priv->selection_was_removed = TRUE;
1594     }
1595 }
1596 
1597 static void
action_move_to_trash(GSimpleAction * action,GVariant * state,gpointer user_data)1598 action_move_to_trash (GSimpleAction *action,
1599                       GVariant      *state,
1600                       gpointer       user_data)
1601 {
1602     trash_or_delete_selected_files (NAUTILUS_FILES_VIEW (user_data));
1603 }
1604 
1605 static void
action_remove_from_recent(GSimpleAction * action,GVariant * state,gpointer user_data)1606 action_remove_from_recent (GSimpleAction *action,
1607                            GVariant      *state,
1608                            gpointer       user_data)
1609 {
1610     /* TODO:implement a set of functions for this, is very confusing to
1611      * call trash_or_delete_file to remove from recent, even if it does like
1612      * that not deleting/moving the files to trash */
1613     trash_or_delete_selected_files (NAUTILUS_FILES_VIEW (user_data));
1614 }
1615 
1616 static void
delete_selected_files(NautilusFilesView * view)1617 delete_selected_files (NautilusFilesView *view)
1618 {
1619     GList *selection;
1620     GList *node;
1621     GList *locations;
1622 
1623     selection = nautilus_files_view_get_selection_for_file_transfer (view);
1624     if (selection == NULL)
1625     {
1626         return;
1627     }
1628 
1629     locations = NULL;
1630     for (node = selection; node != NULL; node = node->next)
1631     {
1632         locations = g_list_prepend (locations,
1633                                     nautilus_file_get_location ((NautilusFile *) node->data));
1634     }
1635     locations = g_list_reverse (locations);
1636 
1637     nautilus_file_operations_delete_async (locations, nautilus_files_view_get_containing_window (view), NULL, NULL, NULL);
1638 
1639     g_list_free_full (locations, g_object_unref);
1640     nautilus_file_list_free (selection);
1641 }
1642 
1643 static void
action_delete(GSimpleAction * action,GVariant * state,gpointer user_data)1644 action_delete (GSimpleAction *action,
1645                GVariant      *state,
1646                gpointer       user_data)
1647 {
1648     delete_selected_files (NAUTILUS_FILES_VIEW (user_data));
1649 }
1650 
1651 static void
action_star(GSimpleAction * action,GVariant * state,gpointer user_data)1652 action_star (GSimpleAction *action,
1653              GVariant      *state,
1654              gpointer       user_data)
1655 {
1656     NautilusFilesView *view;
1657     g_autolist (NautilusFile) selection = NULL;
1658     NautilusFilesViewPrivate *priv;
1659 
1660     view = NAUTILUS_FILES_VIEW (user_data);
1661     priv = nautilus_files_view_get_instance_private (view);
1662     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1663 
1664     nautilus_tag_manager_star_files (priv->tag_manager,
1665                                      G_OBJECT (view),
1666                                      selection,
1667                                      NULL,
1668                                      priv->starred_cancellable);
1669 }
1670 
1671 static void
action_unstar(GSimpleAction * action,GVariant * state,gpointer user_data)1672 action_unstar (GSimpleAction *action,
1673                GVariant      *state,
1674                gpointer       user_data)
1675 {
1676     NautilusFilesView *view;
1677     g_autolist (NautilusFile) selection = NULL;
1678     NautilusFilesViewPrivate *priv;
1679 
1680     view = NAUTILUS_FILES_VIEW (user_data);
1681     priv = nautilus_files_view_get_instance_private (view);
1682     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
1683 
1684     nautilus_tag_manager_unstar_files (priv->tag_manager,
1685                                        G_OBJECT (view),
1686                                        selection,
1687                                        NULL,
1688                                        priv->starred_cancellable);
1689 }
1690 
1691 static void
action_restore_from_trash(GSimpleAction * action,GVariant * state,gpointer user_data)1692 action_restore_from_trash (GSimpleAction *action,
1693                            GVariant      *state,
1694                            gpointer       user_data)
1695 {
1696     NautilusFilesView *view;
1697     GList *selection;
1698 
1699     view = NAUTILUS_FILES_VIEW (user_data);
1700 
1701     selection = nautilus_files_view_get_selection_for_file_transfer (view);
1702     nautilus_restore_files_from_trash (selection,
1703                                        nautilus_files_view_get_containing_window (view));
1704 
1705     nautilus_file_list_free (selection);
1706 }
1707 
1708 static void
action_select_all(GSimpleAction * action,GVariant * state,gpointer user_data)1709 action_select_all (GSimpleAction *action,
1710                    GVariant      *state,
1711                    gpointer       user_data)
1712 {
1713     NautilusFilesView *view;
1714 
1715     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
1716 
1717     view = NAUTILUS_FILES_VIEW (user_data);
1718 
1719     nautilus_files_view_select_all (view);
1720 }
1721 
1722 static void
action_invert_selection(GSimpleAction * action,GVariant * state,gpointer user_data)1723 action_invert_selection (GSimpleAction *action,
1724                          GVariant      *state,
1725                          gpointer       user_data)
1726 {
1727     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
1728 
1729     nautilus_files_view_invert_selection (user_data);
1730 }
1731 
1732 static void
pattern_select_response_cb(GtkWidget * dialog,int response,gpointer user_data)1733 pattern_select_response_cb (GtkWidget *dialog,
1734                             int        response,
1735                             gpointer   user_data)
1736 {
1737     NautilusFilesView *view;
1738     NautilusDirectory *directory;
1739     GtkWidget *entry;
1740     GList *selection;
1741 
1742     view = NAUTILUS_FILES_VIEW (user_data);
1743 
1744     switch (response)
1745     {
1746         case GTK_RESPONSE_OK:
1747         {
1748             entry = g_object_get_data (G_OBJECT (dialog), "entry");
1749             directory = nautilus_files_view_get_model (view);
1750             selection = nautilus_directory_match_pattern (directory,
1751                                                           gtk_entry_get_text (GTK_ENTRY (entry)));
1752 
1753             nautilus_files_view_call_set_selection (view, selection);
1754             nautilus_files_view_reveal_selection (view);
1755 
1756             if (selection)
1757             {
1758                 nautilus_file_list_free (selection);
1759             }
1760             /* fall through */
1761         }
1762 
1763         case GTK_RESPONSE_NONE:
1764         case GTK_RESPONSE_DELETE_EVENT:
1765         case GTK_RESPONSE_CANCEL:
1766         {
1767             gtk_widget_destroy (GTK_WIDGET (dialog));
1768         }
1769         break;
1770 
1771         default:
1772         {
1773             g_assert_not_reached ();
1774         }
1775     }
1776 }
1777 
1778 static void
select_pattern(NautilusFilesView * view)1779 select_pattern (NautilusFilesView *view)
1780 {
1781     GtkWidget *dialog;
1782     GtkWidget *label;
1783     GtkWidget *example;
1784     GtkWidget *grid;
1785     GtkWidget *entry;
1786     char *example_pattern;
1787 
1788     dialog = gtk_dialog_new_with_buttons (_("Select Items Matching"),
1789                                           nautilus_files_view_get_containing_window (view),
1790                                           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
1791                                           _("_Cancel"),
1792                                           GTK_RESPONSE_CANCEL,
1793                                           _("_Select"),
1794                                           GTK_RESPONSE_OK,
1795                                           NULL);
1796     gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1797                                      GTK_RESPONSE_OK);
1798     gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
1799     gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
1800 
1801     label = gtk_label_new_with_mnemonic (_("_Pattern:"));
1802     gtk_widget_set_halign (label, GTK_ALIGN_START);
1803 
1804     example = gtk_label_new (NULL);
1805     gtk_widget_set_halign (example, GTK_ALIGN_START);
1806     example_pattern = g_strdup_printf ("%s<i>%s</i> ",
1807                                        _("Examples: "),
1808                                        "*.png, file\?\?.txt, pict*.\?\?\?");
1809     gtk_label_set_markup (GTK_LABEL (example), example_pattern);
1810     g_free (example_pattern);
1811 
1812     entry = gtk_entry_new ();
1813     gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
1814     gtk_widget_set_hexpand (entry, TRUE);
1815 
1816     grid = gtk_grid_new ();
1817     g_object_set (grid,
1818                   "orientation", GTK_ORIENTATION_VERTICAL,
1819                   "border-width", 6,
1820                   "row-spacing", 6,
1821                   "column-spacing", 12,
1822                   NULL);
1823 
1824     gtk_container_add (GTK_CONTAINER (grid), label);
1825     gtk_grid_attach_next_to (GTK_GRID (grid), entry, label,
1826                              GTK_POS_RIGHT, 1, 1);
1827     gtk_grid_attach_next_to (GTK_GRID (grid), example, entry,
1828                              GTK_POS_BOTTOM, 1, 1);
1829 
1830     gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
1831     gtk_widget_show_all (grid);
1832     gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), grid);
1833     g_object_set_data (G_OBJECT (dialog), "entry", entry);
1834     g_signal_connect (dialog, "response",
1835                       G_CALLBACK (pattern_select_response_cb),
1836                       view);
1837     gtk_widget_show_all (dialog);
1838 }
1839 
1840 static void
action_select_pattern(GSimpleAction * action,GVariant * state,gpointer user_data)1841 action_select_pattern (GSimpleAction *action,
1842                        GVariant      *state,
1843                        gpointer       user_data)
1844 {
1845     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
1846 
1847     select_pattern (user_data);
1848 }
1849 
1850 typedef struct
1851 {
1852     NautilusFilesView *directory_view;
1853     GHashTable *added_locations;
1854     GList *selection;
1855 } NewFolderData;
1856 
1857 typedef struct
1858 {
1859     NautilusFilesView *directory_view;
1860     GHashTable *to_remove_locations;
1861     NautilusFile *new_folder;
1862 } NewFolderSelectionData;
1863 
1864 static void
track_newly_added_locations(NautilusFilesView * view,GList * new_files,gpointer user_data)1865 track_newly_added_locations (NautilusFilesView *view,
1866                              GList             *new_files,
1867                              gpointer           user_data)
1868 {
1869     GHashTable *added_locations;
1870 
1871     added_locations = user_data;
1872 
1873     while (new_files)
1874     {
1875         NautilusFile *new_file;
1876 
1877         new_file = NAUTILUS_FILE (new_files->data);
1878 
1879         g_hash_table_add (added_locations,
1880                           nautilus_file_get_location (new_file));
1881 
1882         new_files = new_files->next;
1883     }
1884 }
1885 
1886 static void
new_folder_done(GFile * new_folder,gboolean success,gpointer user_data)1887 new_folder_done (GFile    *new_folder,
1888                  gboolean  success,
1889                  gpointer  user_data)
1890 {
1891     NautilusFilesView *directory_view;
1892     NautilusFilesViewPrivate *priv;
1893     NautilusFile *file;
1894     NewFolderData *data;
1895 
1896     data = (NewFolderData *) user_data;
1897 
1898     directory_view = data->directory_view;
1899     priv = nautilus_files_view_get_instance_private (directory_view);
1900 
1901     if (directory_view == NULL)
1902     {
1903         goto fail;
1904     }
1905 
1906     g_signal_handlers_disconnect_by_func (directory_view,
1907                                           G_CALLBACK (track_newly_added_locations),
1908                                           data->added_locations);
1909 
1910     if (new_folder == NULL)
1911     {
1912         goto fail;
1913     }
1914 
1915     file = nautilus_file_get (new_folder);
1916 
1917     if (data->selection != NULL)
1918     {
1919         GList *uris, *l;
1920         char *target_uri;
1921 
1922         uris = NULL;
1923         for (l = data->selection; l != NULL; l = l->next)
1924         {
1925             uris = g_list_prepend (uris, nautilus_file_get_uri ((NautilusFile *) l->data));
1926         }
1927         uris = g_list_reverse (uris);
1928 
1929         target_uri = nautilus_file_get_uri (file);
1930 
1931         nautilus_files_view_move_copy_items (directory_view,
1932                                              uris,
1933                                              target_uri,
1934                                              GDK_ACTION_MOVE);
1935         g_list_free_full (uris, g_free);
1936         g_free (target_uri);
1937     }
1938 
1939     if (g_hash_table_contains (data->added_locations, new_folder))
1940     {
1941         /* The file was already added */
1942         nautilus_files_view_select_file (directory_view, file);
1943         nautilus_files_view_reveal_selection (directory_view);
1944     }
1945     else
1946     {
1947         g_hash_table_insert (priv->pending_reveal,
1948                              file,
1949                              GUINT_TO_POINTER (TRUE));
1950     }
1951 
1952     nautilus_file_unref (file);
1953 
1954 fail:
1955     g_hash_table_destroy (data->added_locations);
1956 
1957     if (data->directory_view != NULL)
1958     {
1959         g_object_remove_weak_pointer (G_OBJECT (data->directory_view),
1960                                       (gpointer *) &data->directory_view);
1961     }
1962 
1963     nautilus_file_list_free (data->selection);
1964     g_free (data);
1965 }
1966 
1967 
1968 static NewFolderData *
new_folder_data_new(NautilusFilesView * directory_view,gboolean with_selection)1969 new_folder_data_new (NautilusFilesView *directory_view,
1970                      gboolean           with_selection)
1971 {
1972     NewFolderData *data;
1973 
1974     data = g_new (NewFolderData, 1);
1975     data->directory_view = directory_view;
1976     data->added_locations = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
1977                                                    g_object_unref, NULL);
1978     if (with_selection)
1979     {
1980         data->selection = nautilus_files_view_get_selection_for_file_transfer (directory_view);
1981     }
1982     else
1983     {
1984         data->selection = NULL;
1985     }
1986     g_object_add_weak_pointer (G_OBJECT (data->directory_view),
1987                                (gpointer *) &data->directory_view);
1988 
1989     return data;
1990 }
1991 
1992 static GdkRectangle *
nautilus_files_view_compute_rename_popover_pointing_to(NautilusFilesView * view)1993 nautilus_files_view_compute_rename_popover_pointing_to (NautilusFilesView *view)
1994 {
1995     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->compute_rename_popover_pointing_to (view);
1996 }
1997 
1998 static void
disconnect_rename_controller_signals(NautilusFilesView * self)1999 disconnect_rename_controller_signals (NautilusFilesView *self)
2000 {
2001     NautilusFilesViewPrivate *priv;
2002 
2003     g_assert (NAUTILUS_IS_FILES_VIEW (self));
2004 
2005     priv = nautilus_files_view_get_instance_private (self);
2006 
2007     g_clear_signal_handler (&priv->name_accepted_handler_id, priv->rename_file_controller);
2008     g_clear_signal_handler (&priv->cancelled_handler_id, priv->rename_file_controller);
2009 }
2010 
2011 static void
rename_file_popover_controller_on_name_accepted(NautilusFileNameWidgetController * controller,gpointer user_data)2012 rename_file_popover_controller_on_name_accepted (NautilusFileNameWidgetController *controller,
2013                                                  gpointer                          user_data)
2014 {
2015     NautilusFilesView *view;
2016     NautilusFile *target_file;
2017     g_autofree gchar *name = NULL;
2018     NautilusFilesViewPrivate *priv;
2019 
2020     view = NAUTILUS_FILES_VIEW (user_data);
2021     priv = nautilus_files_view_get_instance_private (view);
2022 
2023     name = nautilus_file_name_widget_controller_get_new_name (controller);
2024 
2025     target_file =
2026         nautilus_rename_file_popover_controller_get_target_file (priv->rename_file_controller);
2027 
2028     /* Put it on the queue for reveal after the view acknowledges the change */
2029     g_hash_table_insert (priv->pending_reveal,
2030                          target_file,
2031                          GUINT_TO_POINTER (FALSE));
2032 
2033     nautilus_rename_file (target_file, name, NULL, NULL);
2034 
2035     disconnect_rename_controller_signals (view);
2036 }
2037 
2038 static void
rename_file_popover_controller_on_cancelled(NautilusFileNameWidgetController * controller,gpointer user_data)2039 rename_file_popover_controller_on_cancelled (NautilusFileNameWidgetController *controller,
2040                                              gpointer                          user_data)
2041 {
2042     NautilusFilesView *view;
2043 
2044     view = NAUTILUS_FILES_VIEW (user_data);
2045 
2046     disconnect_rename_controller_signals (view);
2047 }
2048 
2049 static void
nautilus_files_view_rename_file_popover_new(NautilusFilesView * view,NautilusFile * target_file)2050 nautilus_files_view_rename_file_popover_new (NautilusFilesView *view,
2051                                              NautilusFile      *target_file)
2052 {
2053     GdkRectangle *pointing_to;
2054     NautilusFilesViewPrivate *priv;
2055 
2056     priv = nautilus_files_view_get_instance_private (view);
2057 
2058     /* Make sure the whole item is visible. The selection is a single item, the
2059      * one to rename with the popover, so we can use reveal_selection() for this.
2060      */
2061     nautilus_files_view_reveal_selection (view);
2062 
2063     pointing_to = nautilus_files_view_compute_rename_popover_pointing_to (view);
2064 
2065     nautilus_rename_file_popover_controller_show_for_file (priv->rename_file_controller,
2066                                                            target_file,
2067                                                            pointing_to,
2068                                                            GTK_WIDGET (view));
2069 
2070     priv->name_accepted_handler_id = g_signal_connect (priv->rename_file_controller,
2071                                                        "name-accepted",
2072                                                        G_CALLBACK (rename_file_popover_controller_on_name_accepted),
2073                                                        view);
2074     priv->cancelled_handler_id = g_signal_connect (priv->rename_file_controller,
2075                                                    "cancelled",
2076                                                    G_CALLBACK (rename_file_popover_controller_on_cancelled),
2077                                                    view);
2078 }
2079 
2080 static void
new_folder_dialog_controller_on_name_accepted(NautilusFileNameWidgetController * controller,gpointer user_data)2081 new_folder_dialog_controller_on_name_accepted (NautilusFileNameWidgetController *controller,
2082                                                gpointer                          user_data)
2083 {
2084     NautilusFilesView *view;
2085     NautilusFilesViewPrivate *priv;
2086     NewFolderData *data;
2087     g_autofree gchar *parent_uri = NULL;
2088     g_autofree gchar *name = NULL;
2089     NautilusFile *parent;
2090     gboolean with_selection;
2091 
2092     view = NAUTILUS_FILES_VIEW (user_data);
2093     priv = nautilus_files_view_get_instance_private (view);
2094 
2095     with_selection =
2096         nautilus_new_folder_dialog_controller_get_with_selection (priv->new_folder_controller);
2097 
2098     data = new_folder_data_new (view, with_selection);
2099 
2100     name = nautilus_file_name_widget_controller_get_new_name (controller);
2101     g_signal_connect_data (view,
2102                            "add-files",
2103                            G_CALLBACK (track_newly_added_locations),
2104                            data->added_locations,
2105                            (GClosureNotify) NULL,
2106                            G_CONNECT_AFTER);
2107 
2108     parent_uri = nautilus_files_view_get_backing_uri (view);
2109     parent = nautilus_file_get_by_uri (parent_uri);
2110     nautilus_file_operations_new_folder (GTK_WIDGET (view),
2111                                          NULL,
2112                                          parent_uri, name,
2113                                          new_folder_done, data);
2114 
2115     g_clear_object (&priv->new_folder_controller);
2116 
2117     /* After the dialog is destroyed the focus, is probably in the menu item
2118      * that created the dialog, but we want the focus to be in the newly created
2119      * folder.
2120      */
2121     gtk_widget_grab_focus (GTK_WIDGET (view));
2122 
2123     g_object_unref (parent);
2124 }
2125 
2126 static void
new_folder_dialog_controller_on_cancelled(NautilusNewFolderDialogController * controller,gpointer user_data)2127 new_folder_dialog_controller_on_cancelled (NautilusNewFolderDialogController *controller,
2128                                            gpointer                           user_data)
2129 {
2130     NautilusFilesView *view;
2131     NautilusFilesViewPrivate *priv;
2132 
2133     view = NAUTILUS_FILES_VIEW (user_data);
2134     priv = nautilus_files_view_get_instance_private (view);
2135 
2136     g_clear_object (&priv->new_folder_controller);
2137 }
2138 
2139 static void
nautilus_files_view_new_folder_dialog_new(NautilusFilesView * view,gboolean with_selection)2140 nautilus_files_view_new_folder_dialog_new (NautilusFilesView *view,
2141                                            gboolean           with_selection)
2142 {
2143     g_autoptr (NautilusDirectory) containing_directory = NULL;
2144     NautilusFilesViewPrivate *priv;
2145     g_autofree char *uri = NULL;
2146     g_autofree char *common_prefix = NULL;
2147 
2148     priv = nautilus_files_view_get_instance_private (view);
2149 
2150     if (priv->new_folder_controller != NULL)
2151     {
2152         return;
2153     }
2154 
2155     uri = nautilus_files_view_get_backing_uri (view);
2156     containing_directory = nautilus_directory_get_by_uri (uri);
2157 
2158     if (with_selection)
2159     {
2160         g_autolist (NautilusFile) selection = NULL;
2161         selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
2162         common_prefix = nautilus_get_common_filename_prefix (selection, MIN_COMMON_FILENAME_PREFIX_LENGTH);
2163     }
2164 
2165     priv->new_folder_controller =
2166         nautilus_new_folder_dialog_controller_new (nautilus_files_view_get_containing_window (view),
2167                                                    containing_directory,
2168                                                    with_selection,
2169                                                    common_prefix);
2170 
2171     g_signal_connect (priv->new_folder_controller,
2172                       "name-accepted",
2173                       (GCallback) new_folder_dialog_controller_on_name_accepted,
2174                       view);
2175     g_signal_connect (priv->new_folder_controller,
2176                       "cancelled",
2177                       (GCallback) new_folder_dialog_controller_on_cancelled,
2178                       view);
2179 }
2180 
2181 typedef struct
2182 {
2183     NautilusFilesView *view;
2184     GHashTable *added_locations;
2185 } CompressData;
2186 
2187 static void
compress_done(GFile * new_file,gboolean success,gpointer user_data)2188 compress_done (GFile    *new_file,
2189                gboolean  success,
2190                gpointer  user_data)
2191 {
2192     CompressData *data;
2193     NautilusFilesView *view;
2194     NautilusFilesViewPrivate *priv;
2195     NautilusFile *file;
2196 
2197     data = user_data;
2198     view = data->view;
2199 
2200     if (view == NULL)
2201     {
2202         goto out;
2203     }
2204 
2205     priv = nautilus_files_view_get_instance_private (view);
2206 
2207     g_signal_handlers_disconnect_by_func (view,
2208                                           G_CALLBACK (track_newly_added_locations),
2209                                           data->added_locations);
2210 
2211     if (!success)
2212     {
2213         goto out;
2214     }
2215 
2216     file = nautilus_file_get (new_file);
2217 
2218     if (g_hash_table_contains (data->added_locations, new_file))
2219     {
2220         /* The file was already added */
2221         nautilus_files_view_select_file (view, file);
2222         nautilus_files_view_reveal_selection (view);
2223     }
2224     else
2225     {
2226         g_hash_table_insert (priv->pending_reveal,
2227                              file,
2228                              GUINT_TO_POINTER (TRUE));
2229     }
2230 
2231     nautilus_file_unref (file);
2232 out:
2233     g_hash_table_destroy (data->added_locations);
2234 
2235     if (data->view != NULL)
2236     {
2237         g_object_remove_weak_pointer (G_OBJECT (data->view),
2238                                       (gpointer *) &data->view);
2239     }
2240 
2241     g_free (data);
2242 }
2243 
2244 static void
compress_dialog_controller_on_name_accepted(NautilusFileNameWidgetController * controller,gpointer user_data)2245 compress_dialog_controller_on_name_accepted (NautilusFileNameWidgetController *controller,
2246                                              gpointer                          user_data)
2247 {
2248     CompressCallbackData *callback_data = user_data;
2249     NautilusFilesView *view;
2250     g_autofree gchar *name = NULL;
2251     GList *source_files = NULL;
2252     GList *l;
2253     CompressData *data;
2254     g_autoptr (GFile) output = NULL;
2255     g_autoptr (GFile) parent = NULL;
2256     NautilusCompressionFormat compression_format;
2257     NautilusFilesViewPrivate *priv;
2258     AutoarFormat format;
2259     AutoarFilter filter;
2260     const gchar *passphrase = NULL;
2261 
2262     view = NAUTILUS_FILES_VIEW (callback_data->view);
2263     priv = nautilus_files_view_get_instance_private (view);
2264 
2265     for (l = callback_data->selection; l != NULL; l = l->next)
2266     {
2267         source_files = g_list_prepend (source_files,
2268                                        nautilus_file_get_location (l->data));
2269     }
2270     source_files = g_list_reverse (source_files);
2271 
2272     name = nautilus_file_name_widget_controller_get_new_name (controller);
2273     /* Get a parent from a random file. We assume all files has a common parent.
2274      * But don't assume the parent is the view location, since that's not the
2275      * case in list view when expand-folder setting is set
2276      */
2277     parent = g_file_get_parent (G_FILE (g_list_first (source_files)->data));
2278     output = g_file_get_child (parent, name);
2279 
2280     data = g_new (CompressData, 1);
2281     data->view = view;
2282     data->added_locations = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal,
2283                                                    g_object_unref, NULL);
2284     g_object_add_weak_pointer (G_OBJECT (data->view),
2285                                (gpointer *) &data->view);
2286 
2287     g_signal_connect_data (view,
2288                            "add-files",
2289                            G_CALLBACK (track_newly_added_locations),
2290                            data->added_locations,
2291                            NULL,
2292                            G_CONNECT_AFTER);
2293 
2294     compression_format = g_settings_get_enum (nautilus_compression_preferences,
2295                                               NAUTILUS_PREFERENCES_DEFAULT_COMPRESSION_FORMAT);
2296 
2297     switch (compression_format)
2298     {
2299         case NAUTILUS_COMPRESSION_ZIP:
2300         {
2301             format = AUTOAR_FORMAT_ZIP;
2302             filter = AUTOAR_FILTER_NONE;
2303         }
2304         break;
2305 
2306         case NAUTILUS_COMPRESSION_ENCRYPTED_ZIP:
2307         {
2308             format = AUTOAR_FORMAT_ZIP;
2309             filter = AUTOAR_FILTER_NONE;
2310             passphrase = nautilus_compress_dialog_controller_get_passphrase (priv->compress_controller);
2311         }
2312         break;
2313 
2314         case NAUTILUS_COMPRESSION_TAR_XZ:
2315         {
2316             format = AUTOAR_FORMAT_TAR;
2317             filter = AUTOAR_FILTER_XZ;
2318         }
2319         break;
2320 
2321         case NAUTILUS_COMPRESSION_7ZIP:
2322         {
2323             format = AUTOAR_FORMAT_7ZIP;
2324             filter = AUTOAR_FILTER_NONE;
2325         }
2326         break;
2327 
2328         default:
2329         {
2330             g_assert_not_reached ();
2331         }
2332     }
2333 
2334     nautilus_file_operations_compress (source_files, output,
2335                                        format,
2336                                        filter,
2337                                        passphrase,
2338                                        nautilus_files_view_get_containing_window (view),
2339                                        NULL,
2340                                        compress_done,
2341                                        data);
2342 
2343     g_list_free_full (source_files, g_object_unref);
2344     g_clear_object (&priv->compress_controller);
2345 }
2346 
2347 static void
compress_dialog_controller_on_cancelled(NautilusNewFolderDialogController * controller,gpointer user_data)2348 compress_dialog_controller_on_cancelled (NautilusNewFolderDialogController *controller,
2349                                          gpointer                           user_data)
2350 {
2351     NautilusFilesView *view;
2352     NautilusFilesViewPrivate *priv;
2353 
2354     view = NAUTILUS_FILES_VIEW (user_data);
2355     priv = nautilus_files_view_get_instance_private (view);
2356 
2357     g_clear_object (&priv->compress_controller);
2358 }
2359 
2360 static void
compress_callback_data_free(CompressCallbackData * data)2361 compress_callback_data_free (CompressCallbackData *data)
2362 {
2363     nautilus_file_list_free (data->selection);
2364     g_free (data);
2365 }
2366 
2367 static void
nautilus_files_view_compress_dialog_new(NautilusFilesView * view)2368 nautilus_files_view_compress_dialog_new (NautilusFilesView *view)
2369 {
2370     NautilusDirectory *containing_directory;
2371     NautilusFilesViewPrivate *priv;
2372     g_autolist (NautilusFile) selection = NULL;
2373     g_autofree char *common_prefix = NULL;
2374     CompressCallbackData *data;
2375 
2376     priv = nautilus_files_view_get_instance_private (view);
2377 
2378     if (priv->compress_controller != NULL)
2379     {
2380         return;
2381     }
2382 
2383     containing_directory = nautilus_directory_get_by_uri (nautilus_files_view_get_backing_uri (view));
2384 
2385     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
2386 
2387     if (g_list_length (selection) == 1)
2388     {
2389         g_autofree char *display_name = NULL;
2390 
2391         display_name = nautilus_file_get_display_name (selection->data);
2392 
2393         if (nautilus_file_is_directory (selection->data))
2394         {
2395             common_prefix = g_steal_pointer (&display_name);
2396         }
2397         else
2398         {
2399             common_prefix = eel_filename_strip_extension (display_name);
2400         }
2401     }
2402     else
2403     {
2404         common_prefix = nautilus_get_common_filename_prefix (selection,
2405                                                              MIN_COMMON_FILENAME_PREFIX_LENGTH);
2406     }
2407 
2408     priv->compress_controller = nautilus_compress_dialog_controller_new (nautilus_files_view_get_containing_window (view),
2409                                                                          containing_directory,
2410                                                                          common_prefix);
2411 
2412     data = g_new0 (CompressCallbackData, 1);
2413     data->view = view;
2414     data->selection = nautilus_files_view_get_selection_for_file_transfer (view);
2415 
2416     g_signal_connect_data (priv->compress_controller,
2417                            "name-accepted",
2418                            (GCallback) compress_dialog_controller_on_name_accepted,
2419                            data,
2420                            (GClosureNotify) compress_callback_data_free,
2421                            G_CONNECT_AFTER);
2422 
2423     g_signal_connect (priv->compress_controller,
2424                       "cancelled",
2425                       (GCallback) compress_dialog_controller_on_cancelled,
2426                       view);
2427 }
2428 
2429 static void
nautilus_files_view_new_folder(NautilusFilesView * directory_view,gboolean with_selection)2430 nautilus_files_view_new_folder (NautilusFilesView *directory_view,
2431                                 gboolean           with_selection)
2432 {
2433     nautilus_files_view_new_folder_dialog_new (directory_view, with_selection);
2434 }
2435 
2436 static NewFolderData *
setup_new_folder_data(NautilusFilesView * directory_view)2437 setup_new_folder_data (NautilusFilesView *directory_view)
2438 {
2439     NewFolderData *data;
2440 
2441     data = new_folder_data_new (directory_view, FALSE);
2442 
2443     g_signal_connect_data (directory_view,
2444                            "add-files",
2445                            G_CALLBACK (track_newly_added_locations),
2446                            data->added_locations,
2447                            (GClosureNotify) NULL,
2448                            G_CONNECT_AFTER);
2449 
2450     return data;
2451 }
2452 
2453 void
nautilus_files_view_new_file_with_initial_contents(NautilusFilesView * view,const char * parent_uri,const char * filename,const char * initial_contents,int length)2454 nautilus_files_view_new_file_with_initial_contents (NautilusFilesView *view,
2455                                                     const char        *parent_uri,
2456                                                     const char        *filename,
2457                                                     const char        *initial_contents,
2458                                                     int                length)
2459 {
2460     NewFolderData *data;
2461 
2462     g_assert (parent_uri != NULL);
2463 
2464     data = setup_new_folder_data (view);
2465 
2466     nautilus_file_operations_new_file (GTK_WIDGET (view),
2467                                        parent_uri, filename,
2468                                        initial_contents, length,
2469                                        new_folder_done, data);
2470 }
2471 
2472 static void
nautilus_files_view_new_file(NautilusFilesView * directory_view,const char * parent_uri,NautilusFile * source)2473 nautilus_files_view_new_file (NautilusFilesView *directory_view,
2474                               const char        *parent_uri,
2475                               NautilusFile      *source)
2476 {
2477     NewFolderData *data;
2478     char *source_uri;
2479     char *container_uri;
2480 
2481     container_uri = NULL;
2482     if (parent_uri == NULL)
2483     {
2484         container_uri = nautilus_files_view_get_backing_uri (directory_view);
2485         g_assert (container_uri != NULL);
2486     }
2487 
2488     if (source == NULL)
2489     {
2490         nautilus_files_view_new_file_with_initial_contents (directory_view,
2491                                                             parent_uri != NULL ? parent_uri : container_uri,
2492                                                             NULL,
2493                                                             NULL,
2494                                                             0);
2495         g_free (container_uri);
2496         return;
2497     }
2498 
2499     data = setup_new_folder_data (directory_view);
2500 
2501     source_uri = nautilus_file_get_uri (source);
2502 
2503     nautilus_file_operations_new_file_from_template (GTK_WIDGET (directory_view),
2504                                                      parent_uri != NULL ? parent_uri : container_uri,
2505                                                      NULL,
2506                                                      source_uri,
2507                                                      new_folder_done, data);
2508 
2509     g_free (source_uri);
2510     g_free (container_uri);
2511 }
2512 
2513 static void
action_new_folder(GSimpleAction * action,GVariant * state,gpointer user_data)2514 action_new_folder (GSimpleAction *action,
2515                    GVariant      *state,
2516                    gpointer       user_data)
2517 {
2518     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
2519 
2520     nautilus_files_view_new_folder (NAUTILUS_FILES_VIEW (user_data), FALSE);
2521 }
2522 
2523 static void
action_new_folder_with_selection(GSimpleAction * action,GVariant * state,gpointer user_data)2524 action_new_folder_with_selection (GSimpleAction *action,
2525                                   GVariant      *state,
2526                                   gpointer       user_data)
2527 {
2528     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
2529 
2530     nautilus_files_view_new_folder (NAUTILUS_FILES_VIEW (user_data), TRUE);
2531 }
2532 
2533 static void
action_properties(GSimpleAction * action,GVariant * state,gpointer user_data)2534 action_properties (GSimpleAction *action,
2535                    GVariant      *state,
2536                    gpointer       user_data)
2537 {
2538     NautilusFilesView *view;
2539     NautilusFilesViewPrivate *priv;
2540     g_autolist (NautilusFile) selection = NULL;
2541     GList *files;
2542 
2543     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
2544 
2545     view = NAUTILUS_FILES_VIEW (user_data);
2546     priv = nautilus_files_view_get_instance_private (view);
2547     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
2548     if (g_list_length (selection) == 0)
2549     {
2550         if (priv->directory_as_file != NULL)
2551         {
2552             files = g_list_append (NULL, nautilus_file_ref (priv->directory_as_file));
2553 
2554             nautilus_properties_window_present (files, GTK_WIDGET (view), NULL,
2555                                                 NULL, NULL);
2556 
2557             nautilus_file_list_free (files);
2558         }
2559     }
2560     else
2561     {
2562         nautilus_properties_window_present (selection, GTK_WIDGET (view), NULL,
2563                                             NULL, NULL);
2564     }
2565 }
2566 
2567 static void
action_current_dir_properties(GSimpleAction * action,GVariant * state,gpointer user_data)2568 action_current_dir_properties (GSimpleAction *action,
2569                                GVariant      *state,
2570                                gpointer       user_data)
2571 {
2572     NautilusFilesView *view;
2573     NautilusFilesViewPrivate *priv;
2574     GList *files;
2575 
2576     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (user_data));
2577 
2578     view = NAUTILUS_FILES_VIEW (user_data);
2579     priv = nautilus_files_view_get_instance_private (view);
2580 
2581     if (priv->directory_as_file != NULL)
2582     {
2583         files = g_list_append (NULL, nautilus_file_ref (priv->directory_as_file));
2584 
2585         nautilus_properties_window_present (files, GTK_WIDGET (view), NULL,
2586                                             NULL, NULL);
2587 
2588         nautilus_file_list_free (files);
2589     }
2590 }
2591 
2592 static void
nautilus_files_view_set_show_hidden_files(NautilusFilesView * view,gboolean show_hidden)2593 nautilus_files_view_set_show_hidden_files (NautilusFilesView *view,
2594                                            gboolean           show_hidden)
2595 {
2596     NautilusFilesViewPrivate *priv;
2597 
2598     priv = nautilus_files_view_get_instance_private (view);
2599 
2600     if (priv->ignore_hidden_file_preferences)
2601     {
2602         return;
2603     }
2604 
2605     if (show_hidden != priv->show_hidden_files)
2606     {
2607         priv->show_hidden_files = show_hidden;
2608 
2609         g_settings_set_boolean (gtk_filechooser_preferences,
2610                                 NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES,
2611                                 show_hidden);
2612 
2613         if (priv->model != NULL)
2614         {
2615             load_directory (view, priv->model);
2616         }
2617     }
2618 }
2619 
2620 static void
action_show_hidden_files(GSimpleAction * action,GVariant * state,gpointer user_data)2621 action_show_hidden_files (GSimpleAction *action,
2622                           GVariant      *state,
2623                           gpointer       user_data)
2624 {
2625     gboolean show_hidden;
2626     NautilusFilesView *view;
2627 
2628     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
2629 
2630     view = NAUTILUS_FILES_VIEW (user_data);
2631     show_hidden = g_variant_get_boolean (state);
2632 
2633     nautilus_files_view_set_show_hidden_files (view, show_hidden);
2634 
2635     g_simple_action_set_state (action, state);
2636 }
2637 
2638 static void
action_zoom_in(GSimpleAction * action,GVariant * state,gpointer user_data)2639 action_zoom_in (GSimpleAction *action,
2640                 GVariant      *state,
2641                 gpointer       user_data)
2642 {
2643     NautilusFilesView *view;
2644 
2645     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
2646 
2647     view = NAUTILUS_FILES_VIEW (user_data);
2648 
2649     nautilus_files_view_bump_zoom_level (view, 1);
2650 }
2651 
2652 static void
action_zoom_out(GSimpleAction * action,GVariant * state,gpointer user_data)2653 action_zoom_out (GSimpleAction *action,
2654                  GVariant      *state,
2655                  gpointer       user_data)
2656 {
2657     NautilusFilesView *view;
2658 
2659     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
2660 
2661     view = NAUTILUS_FILES_VIEW (user_data);
2662 
2663     nautilus_files_view_bump_zoom_level (view, -1);
2664 }
2665 
2666 static void
action_zoom_standard(GSimpleAction * action,GVariant * state,gpointer user_data)2667 action_zoom_standard (GSimpleAction *action,
2668                       GVariant      *state,
2669                       gpointer       user_data)
2670 {
2671     nautilus_files_view_restore_standard_zoom_level (user_data);
2672 }
2673 
2674 static void
action_open_item_new_window(GSimpleAction * action,GVariant * state,gpointer user_data)2675 action_open_item_new_window (GSimpleAction *action,
2676                              GVariant      *state,
2677                              gpointer       user_data)
2678 {
2679     NautilusFilesView *view;
2680     GtkWindow *window;
2681     GList *selection;
2682 
2683     view = NAUTILUS_FILES_VIEW (user_data);
2684     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
2685     window = GTK_WINDOW (nautilus_files_view_get_containing_window (view));
2686 
2687     if (nautilus_files_view_confirm_multiple (window, g_list_length (selection), TRUE))
2688     {
2689         g_list_foreach (selection, open_one_in_new_window, view);
2690     }
2691 
2692     nautilus_file_list_free (selection);
2693 }
2694 
2695 static void
handle_clipboard_data(NautilusFilesView * view,GtkSelectionData * selection_data,char * destination_uri,GdkDragAction action)2696 handle_clipboard_data (NautilusFilesView *view,
2697                        GtkSelectionData  *selection_data,
2698                        char              *destination_uri,
2699                        GdkDragAction      action)
2700 {
2701     GList *item_uris;
2702 
2703     item_uris = nautilus_clipboard_get_uri_list_from_selection_data (selection_data);
2704 
2705     if (item_uris != NULL && destination_uri != NULL)
2706     {
2707         nautilus_files_view_move_copy_items (view, item_uris, destination_uri,
2708                                              action);
2709 
2710         /* If items are cut then remove from clipboard */
2711         if (action == GDK_ACTION_MOVE)
2712         {
2713             gtk_clipboard_clear (nautilus_clipboard_get (GTK_WIDGET (view)));
2714         }
2715 
2716         g_list_free_full (item_uris, g_free);
2717     }
2718 }
2719 
2720 static void
paste_clipboard_data(NautilusFilesView * view,GtkSelectionData * selection_data,char * destination_uri)2721 paste_clipboard_data (NautilusFilesView *view,
2722                       GtkSelectionData  *selection_data,
2723                       char              *destination_uri)
2724 {
2725     GdkDragAction action;
2726 
2727     if (nautilus_clipboard_is_cut_from_selection_data (selection_data))
2728     {
2729         action = GDK_ACTION_MOVE;
2730     }
2731     else
2732     {
2733         action = GDK_ACTION_COPY;
2734     }
2735 
2736     handle_clipboard_data (view, selection_data, destination_uri, action);
2737 }
2738 
2739 static void
paste_clipboard_received_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer data)2740 paste_clipboard_received_callback (GtkClipboard     *clipboard,
2741                                    GtkSelectionData *selection_data,
2742                                    gpointer          data)
2743 {
2744     NautilusFilesView *view;
2745     NautilusFilesViewPrivate *priv;
2746     char *view_uri;
2747 
2748     view = NAUTILUS_FILES_VIEW (data);
2749     priv = nautilus_files_view_get_instance_private (view);
2750 
2751     view_uri = nautilus_files_view_get_backing_uri (view);
2752 
2753     if (priv->slot != NULL)
2754     {
2755         paste_clipboard_data (view, selection_data, view_uri);
2756     }
2757 
2758     g_free (view_uri);
2759 
2760     g_object_unref (view);
2761 }
2762 
2763 static void
paste_files(NautilusFilesView * view)2764 paste_files (NautilusFilesView *view)
2765 {
2766     GtkClipboard *clipboard;
2767 
2768     clipboard = nautilus_clipboard_get (GTK_WIDGET (view));
2769 
2770     /* Performing an async request of clipboard contents, corresponding unref
2771      * is in the callback.
2772      */
2773     g_object_ref (view);
2774     gtk_clipboard_request_contents (clipboard,
2775                                     nautilus_clipboard_get_atom (),
2776                                     paste_clipboard_received_callback,
2777                                     view);
2778 }
2779 
2780 static void
action_paste_files(GSimpleAction * action,GVariant * state,gpointer user_data)2781 action_paste_files (GSimpleAction *action,
2782                     GVariant      *state,
2783                     gpointer       user_data)
2784 {
2785     NautilusFilesView *view;
2786 
2787     view = NAUTILUS_FILES_VIEW (user_data);
2788 
2789     paste_files (view);
2790 }
2791 
2792 static void
action_paste_files_accel(GSimpleAction * action,GVariant * state,gpointer user_data)2793 action_paste_files_accel (GSimpleAction *action,
2794                           GVariant      *state,
2795                           gpointer       user_data)
2796 {
2797     NautilusFilesView *view;
2798 
2799     view = NAUTILUS_FILES_VIEW (user_data);
2800 
2801     if (nautilus_files_view_is_read_only (view))
2802     {
2803         show_dialog (_("Could not paste files"),
2804                      _("Permissions do not allow pasting files in this directory"),
2805                      nautilus_files_view_get_containing_window (view),
2806                      GTK_MESSAGE_ERROR);
2807     }
2808     else
2809     {
2810         paste_files (view);
2811     }
2812 }
2813 
2814 static void
create_links_clipboard_received_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer data)2815 create_links_clipboard_received_callback (GtkClipboard     *clipboard,
2816                                           GtkSelectionData *selection_data,
2817                                           gpointer          data)
2818 {
2819     NautilusFilesView *view;
2820     NautilusFilesViewPrivate *priv;
2821     char *view_uri;
2822 
2823     view = NAUTILUS_FILES_VIEW (data);
2824     priv = nautilus_files_view_get_instance_private (view);
2825 
2826     view_uri = nautilus_files_view_get_backing_uri (view);
2827 
2828     if (priv->slot != NULL)
2829     {
2830         handle_clipboard_data (view, selection_data, view_uri, GDK_ACTION_LINK);
2831     }
2832 
2833     g_free (view_uri);
2834 
2835     g_object_unref (view);
2836 }
2837 
2838 static void
action_create_links(GSimpleAction * action,GVariant * state,gpointer user_data)2839 action_create_links (GSimpleAction *action,
2840                      GVariant      *state,
2841                      gpointer       user_data)
2842 {
2843     NautilusFilesView *view;
2844 
2845     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
2846 
2847     view = NAUTILUS_FILES_VIEW (user_data);
2848 
2849     g_object_ref (view);
2850     gtk_clipboard_request_contents (nautilus_clipboard_get (GTK_WIDGET (view)),
2851                                     nautilus_clipboard_get_atom (),
2852                                     create_links_clipboard_received_callback,
2853                                     view);
2854 }
2855 
2856 static void
click_policy_changed_callback(gpointer callback_data)2857 click_policy_changed_callback (gpointer callback_data)
2858 {
2859     NautilusFilesView *view;
2860 
2861     view = NAUTILUS_FILES_VIEW (callback_data);
2862 
2863     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->click_policy_changed (view);
2864 }
2865 
2866 gboolean
nautilus_files_view_should_sort_directories_first(NautilusFilesView * view)2867 nautilus_files_view_should_sort_directories_first (NautilusFilesView *view)
2868 {
2869     NautilusFilesViewPrivate *priv;
2870     gboolean is_search;
2871 
2872     priv = nautilus_files_view_get_instance_private (view);
2873     is_search = nautilus_view_is_searching (NAUTILUS_VIEW (view));
2874 
2875     return priv->sort_directories_first && !is_search;
2876 }
2877 
2878 static void
sort_directories_first_changed_callback(gpointer callback_data)2879 sort_directories_first_changed_callback (gpointer callback_data)
2880 {
2881     NautilusFilesView *view;
2882     NautilusFilesViewPrivate *priv;
2883     gboolean preference_value;
2884 
2885     view = NAUTILUS_FILES_VIEW (callback_data);
2886     priv = nautilus_files_view_get_instance_private (view);
2887 
2888     preference_value =
2889         g_settings_get_boolean (gtk_filechooser_preferences, NAUTILUS_PREFERENCES_SORT_DIRECTORIES_FIRST);
2890 
2891     if (preference_value != priv->sort_directories_first)
2892     {
2893         priv->sort_directories_first = preference_value;
2894         NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->sort_directories_first_changed (view);
2895     }
2896 }
2897 
2898 static void
show_hidden_files_changed_callback(gpointer callback_data)2899 show_hidden_files_changed_callback (gpointer callback_data)
2900 {
2901     NautilusFilesView *view;
2902     NautilusFilesViewPrivate *priv;
2903     gboolean preference_value;
2904 
2905     view = NAUTILUS_FILES_VIEW (callback_data);
2906     priv = nautilus_files_view_get_instance_private (view);
2907 
2908     preference_value =
2909         g_settings_get_boolean (gtk_filechooser_preferences, NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES);
2910 
2911     nautilus_files_view_set_show_hidden_files (view, preference_value);
2912 
2913     if (priv->active)
2914     {
2915         schedule_update_context_menus (view);
2916     }
2917 }
2918 
2919 static gboolean
set_up_scripts_directory_global(void)2920 set_up_scripts_directory_global (void)
2921 {
2922     g_autofree gchar *old_scripts_directory_path = NULL;
2923     g_autoptr (GFile) old_scripts_directory = NULL;
2924     g_autofree gchar *scripts_directory_path = NULL;
2925     g_autoptr (GFile) scripts_directory = NULL;
2926     const char *override;
2927     GFileType file_type;
2928     g_autoptr (GError) error = NULL;
2929 
2930     if (scripts_directory_uri != NULL)
2931     {
2932         return TRUE;
2933     }
2934 
2935     scripts_directory_path = nautilus_get_scripts_directory_path ();
2936 
2937     override = g_getenv ("GNOME22_USER_DIR");
2938 
2939     if (override)
2940     {
2941         old_scripts_directory_path = g_build_filename (override,
2942                                                        "nautilus-scripts",
2943                                                        NULL);
2944     }
2945     else
2946     {
2947         old_scripts_directory_path = g_build_filename (g_get_home_dir (),
2948                                                        ".gnome2",
2949                                                        "nautilus-scripts",
2950                                                        NULL);
2951     }
2952 
2953     old_scripts_directory = g_file_new_for_path (old_scripts_directory_path);
2954     scripts_directory = g_file_new_for_path (scripts_directory_path);
2955 
2956     file_type = g_file_query_file_type (old_scripts_directory,
2957                                         G_FILE_QUERY_INFO_NONE,
2958                                         NULL);
2959 
2960     if (file_type == G_FILE_TYPE_DIRECTORY &&
2961         !g_file_query_exists (scripts_directory, NULL))
2962     {
2963         g_autoptr (GFile) updated = NULL;
2964         const char *message;
2965 
2966         /* test if we already attempted to migrate first */
2967         updated = g_file_get_child (old_scripts_directory, "DEPRECATED-DIRECTORY");
2968         message = _("Nautilus 3.6 deprecated this directory and tried migrating "
2969                     "this configuration to ~/.local/share/nautilus");
2970         if (!g_file_query_exists (updated, NULL))
2971         {
2972             g_autoptr (GFile) parent = NULL;
2973 
2974             parent = g_file_get_parent (scripts_directory);
2975             g_file_make_directory_with_parents (parent, NULL, &error);
2976 
2977             if (error == NULL ||
2978                 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
2979             {
2980                 g_clear_error (&error);
2981 
2982                 g_file_set_attribute_uint32 (parent,
2983                                              G_FILE_ATTRIBUTE_UNIX_MODE,
2984                                              S_IRWXU,
2985                                              G_FILE_QUERY_INFO_NONE,
2986                                              NULL, NULL);
2987 
2988                 g_file_move (old_scripts_directory,
2989                              scripts_directory,
2990                              G_FILE_COPY_NONE,
2991                              NULL, NULL, NULL,
2992                              &error);
2993 
2994                 if (error == NULL)
2995                 {
2996                     g_file_replace_contents (updated,
2997                                              message, strlen (message),
2998                                              NULL,
2999                                              FALSE,
3000                                              G_FILE_CREATE_PRIVATE,
3001                                              NULL, NULL, NULL);
3002                 }
3003             }
3004 
3005             g_clear_error (&error);
3006         }
3007     }
3008 
3009     g_file_make_directory_with_parents (scripts_directory, NULL, &error);
3010 
3011     if (error == NULL ||
3012         g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
3013     {
3014         g_file_set_attribute_uint32 (scripts_directory,
3015                                      G_FILE_ATTRIBUTE_UNIX_MODE,
3016                                      S_IRWXU,
3017                                      G_FILE_QUERY_INFO_NONE,
3018                                      NULL, NULL);
3019 
3020         scripts_directory_uri = g_file_get_uri (scripts_directory);
3021         scripts_directory_uri_length = strlen (scripts_directory_uri);
3022     }
3023 
3024     return scripts_directory_uri != NULL;
3025 }
3026 
3027 static void
scripts_added_or_changed_callback(NautilusDirectory * directory,GList * files,gpointer callback_data)3028 scripts_added_or_changed_callback (NautilusDirectory *directory,
3029                                    GList             *files,
3030                                    gpointer           callback_data)
3031 {
3032     NautilusFilesView *view;
3033     NautilusFilesViewPrivate *priv;
3034 
3035     view = NAUTILUS_FILES_VIEW (callback_data);
3036     priv = nautilus_files_view_get_instance_private (view);
3037 
3038     priv->scripts_menu_updated = FALSE;
3039     if (priv->active)
3040     {
3041         schedule_update_context_menus (view);
3042     }
3043 }
3044 
3045 static void
templates_added_or_changed_callback(NautilusDirectory * directory,GList * files,gpointer callback_data)3046 templates_added_or_changed_callback (NautilusDirectory *directory,
3047                                      GList             *files,
3048                                      gpointer           callback_data)
3049 {
3050     NautilusFilesView *view;
3051     NautilusFilesViewPrivate *priv;
3052 
3053     view = NAUTILUS_FILES_VIEW (callback_data);
3054     priv = nautilus_files_view_get_instance_private (view);
3055 
3056     priv->templates_menu_updated = FALSE;
3057     if (priv->active)
3058     {
3059         schedule_update_context_menus (view);
3060     }
3061 }
3062 
3063 static void
add_directory_to_directory_list(NautilusFilesView * view,NautilusDirectory * directory,GList ** directory_list,GCallback changed_callback)3064 add_directory_to_directory_list (NautilusFilesView  *view,
3065                                  NautilusDirectory  *directory,
3066                                  GList             **directory_list,
3067                                  GCallback           changed_callback)
3068 {
3069     NautilusFileAttributes attributes;
3070 
3071     if (g_list_find (*directory_list, directory) == NULL)
3072     {
3073         nautilus_directory_ref (directory);
3074 
3075         attributes =
3076             NAUTILUS_FILE_ATTRIBUTES_FOR_ICON |
3077             NAUTILUS_FILE_ATTRIBUTE_INFO |
3078             NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT;
3079 
3080         nautilus_directory_file_monitor_add (directory, directory_list,
3081                                              FALSE, attributes,
3082                                              (NautilusDirectoryCallback) changed_callback, view);
3083 
3084         g_signal_connect_object (directory, "files-added",
3085                                  G_CALLBACK (changed_callback), view, 0);
3086         g_signal_connect_object (directory, "files-changed",
3087                                  G_CALLBACK (changed_callback), view, 0);
3088 
3089         *directory_list = g_list_append (*directory_list, directory);
3090     }
3091 }
3092 
3093 static void
remove_directory_from_directory_list(NautilusFilesView * view,NautilusDirectory * directory,GList ** directory_list,GCallback changed_callback)3094 remove_directory_from_directory_list (NautilusFilesView  *view,
3095                                       NautilusDirectory  *directory,
3096                                       GList             **directory_list,
3097                                       GCallback           changed_callback)
3098 {
3099     *directory_list = g_list_remove (*directory_list, directory);
3100 
3101     g_signal_handlers_disconnect_by_func (directory,
3102                                           G_CALLBACK (changed_callback),
3103                                           view);
3104 
3105     nautilus_directory_file_monitor_remove (directory, directory_list);
3106 
3107     nautilus_directory_unref (directory);
3108 }
3109 
3110 
3111 static void
add_directory_to_scripts_directory_list(NautilusFilesView * view,NautilusDirectory * directory)3112 add_directory_to_scripts_directory_list (NautilusFilesView *view,
3113                                          NautilusDirectory *directory)
3114 {
3115     NautilusFilesViewPrivate *priv;
3116 
3117     priv = nautilus_files_view_get_instance_private (view);
3118 
3119     add_directory_to_directory_list (view, directory,
3120                                      &priv->scripts_directory_list,
3121                                      G_CALLBACK (scripts_added_or_changed_callback));
3122 }
3123 
3124 static void
remove_directory_from_scripts_directory_list(NautilusFilesView * view,NautilusDirectory * directory)3125 remove_directory_from_scripts_directory_list (NautilusFilesView *view,
3126                                               NautilusDirectory *directory)
3127 {
3128     NautilusFilesViewPrivate *priv;
3129 
3130     priv = nautilus_files_view_get_instance_private (view);
3131 
3132     remove_directory_from_directory_list (view, directory,
3133                                           &priv->scripts_directory_list,
3134                                           G_CALLBACK (scripts_added_or_changed_callback));
3135 }
3136 
3137 static void
add_directory_to_templates_directory_list(NautilusFilesView * view,NautilusDirectory * directory)3138 add_directory_to_templates_directory_list (NautilusFilesView *view,
3139                                            NautilusDirectory *directory)
3140 {
3141     NautilusFilesViewPrivate *priv;
3142 
3143     priv = nautilus_files_view_get_instance_private (view);
3144 
3145     add_directory_to_directory_list (view, directory,
3146                                      &priv->templates_directory_list,
3147                                      G_CALLBACK (templates_added_or_changed_callback));
3148 }
3149 
3150 static void
remove_directory_from_templates_directory_list(NautilusFilesView * view,NautilusDirectory * directory)3151 remove_directory_from_templates_directory_list (NautilusFilesView *view,
3152                                                 NautilusDirectory *directory)
3153 {
3154     NautilusFilesViewPrivate *priv;
3155 
3156     priv = nautilus_files_view_get_instance_private (view);
3157 
3158     remove_directory_from_directory_list (view, directory,
3159                                           &priv->templates_directory_list,
3160                                           G_CALLBACK (templates_added_or_changed_callback));
3161 }
3162 
3163 static void
slot_active_changed(NautilusWindowSlot * slot,GParamSpec * pspec,NautilusFilesView * view)3164 slot_active_changed (NautilusWindowSlot *slot,
3165                      GParamSpec         *pspec,
3166                      NautilusFilesView  *view)
3167 {
3168     NautilusFilesViewPrivate *priv;
3169 
3170     priv = nautilus_files_view_get_instance_private (view);
3171 
3172     if (priv->active == nautilus_window_slot_get_active (slot))
3173     {
3174         return;
3175     }
3176 
3177     priv->active = nautilus_window_slot_get_active (slot);
3178 
3179     if (priv->active)
3180     {
3181         /* Avoid updating the toolbar withouth making sure the toolbar
3182          * zoom slider has the correct adjustment that changes when the
3183          * view mode changes
3184          */
3185         nautilus_files_view_update_context_menus (view);
3186         nautilus_files_view_update_toolbar_menus (view);
3187 
3188         schedule_update_context_menus (view);
3189 
3190         gtk_widget_insert_action_group (GTK_WIDGET (nautilus_files_view_get_window (view)),
3191                                         "view",
3192                                         G_ACTION_GROUP (priv->view_action_group));
3193     }
3194     else
3195     {
3196         remove_update_context_menus_timeout_callback (view);
3197         gtk_widget_insert_action_group (GTK_WIDGET (nautilus_files_view_get_window (view)),
3198                                         "view",
3199                                         NULL);
3200     }
3201 }
3202 
3203 static void
nautilus_files_view_grab_focus(GtkWidget * widget)3204 nautilus_files_view_grab_focus (GtkWidget *widget)
3205 {
3206     /* focus the child of the scrolled window if it exists */
3207     NautilusFilesView *view;
3208     NautilusFilesViewPrivate *priv;
3209     GtkWidget *child;
3210 
3211     view = NAUTILUS_FILES_VIEW (widget);
3212     priv = nautilus_files_view_get_instance_private (view);
3213     child = gtk_bin_get_child (GTK_BIN (priv->scrolled_window));
3214 
3215     GTK_WIDGET_CLASS (nautilus_files_view_parent_class)->grab_focus (widget);
3216 
3217     if (child)
3218     {
3219         gtk_widget_grab_focus (GTK_WIDGET (child));
3220     }
3221 }
3222 
3223 static void
nautilus_files_view_set_selection(NautilusView * nautilus_files_view,GList * selection)3224 nautilus_files_view_set_selection (NautilusView *nautilus_files_view,
3225                                    GList        *selection)
3226 {
3227     NautilusFilesView *view;
3228     NautilusFilesViewPrivate *priv;
3229     GList *pending_selection;
3230 
3231     view = NAUTILUS_FILES_VIEW (nautilus_files_view);
3232     priv = nautilus_files_view_get_instance_private (view);
3233 
3234     if (!priv->loading)
3235     {
3236         /* If we aren't still loading, set the selection right now,
3237          * and reveal the new selection.
3238          */
3239         nautilus_files_view_call_set_selection (view, selection);
3240         nautilus_files_view_reveal_selection (view);
3241     }
3242     else
3243     {
3244         /* If we are still loading, set the list of pending URIs instead.
3245          * done_loading() will eventually select the pending URIs and reveal them.
3246          */
3247         pending_selection = g_list_copy_deep (selection,
3248                                               (GCopyFunc) g_object_ref, NULL);
3249         g_list_free_full (priv->pending_selection, g_object_unref);
3250 
3251         priv->pending_selection = pending_selection;
3252     }
3253 }
3254 
3255 static void
nautilus_files_view_destroy(GtkWidget * object)3256 nautilus_files_view_destroy (GtkWidget *object)
3257 {
3258     NautilusFilesView *view;
3259     NautilusFilesViewPrivate *priv;
3260     GtkClipboard *clipboard;
3261     GList *node, *next;
3262 
3263     view = NAUTILUS_FILES_VIEW (object);
3264     priv = nautilus_files_view_get_instance_private (view);
3265 
3266     priv->in_destruction = TRUE;
3267     nautilus_files_view_stop_loading (view);
3268 
3269     if (priv->model)
3270     {
3271         nautilus_directory_unref (priv->model);
3272         priv->model = NULL;
3273     }
3274 
3275     for (node = priv->scripts_directory_list; node != NULL; node = next)
3276     {
3277         next = node->next;
3278         remove_directory_from_scripts_directory_list (view, node->data);
3279     }
3280 
3281     for (node = priv->templates_directory_list; node != NULL; node = next)
3282     {
3283         next = node->next;
3284         remove_directory_from_templates_directory_list (view, node->data);
3285     }
3286 
3287     while (priv->subdirectory_list != NULL)
3288     {
3289         nautilus_files_view_remove_subdirectory (view,
3290                                                  priv->subdirectory_list->data);
3291     }
3292 
3293     remove_update_context_menus_timeout_callback (view);
3294     remove_update_status_idle_callback (view);
3295 
3296     if (priv->display_selection_idle_id != 0)
3297     {
3298         g_source_remove (priv->display_selection_idle_id);
3299         priv->display_selection_idle_id = 0;
3300     }
3301 
3302     if (priv->reveal_selection_idle_id != 0)
3303     {
3304         g_source_remove (priv->reveal_selection_idle_id);
3305         priv->reveal_selection_idle_id = 0;
3306     }
3307 
3308     if (priv->floating_bar_set_status_timeout_id != 0)
3309     {
3310         g_source_remove (priv->floating_bar_set_status_timeout_id);
3311         priv->floating_bar_set_status_timeout_id = 0;
3312     }
3313 
3314     if (priv->floating_bar_loading_timeout_id != 0)
3315     {
3316         g_source_remove (priv->floating_bar_loading_timeout_id);
3317         priv->floating_bar_loading_timeout_id = 0;
3318     }
3319 
3320     if (priv->floating_bar_set_passthrough_timeout_id != 0)
3321     {
3322         g_source_remove (priv->floating_bar_set_passthrough_timeout_id);
3323         priv->floating_bar_set_passthrough_timeout_id = 0;
3324     }
3325 
3326     g_signal_handlers_disconnect_by_func (nautilus_preferences,
3327                                           schedule_update_context_menus, view);
3328     g_signal_handlers_disconnect_by_func (nautilus_preferences,
3329                                           click_policy_changed_callback, view);
3330     g_signal_handlers_disconnect_by_func (gtk_filechooser_preferences,
3331                                           sort_directories_first_changed_callback, view);
3332     g_signal_handlers_disconnect_by_func (gtk_filechooser_preferences,
3333                                           show_hidden_files_changed_callback, view);
3334     g_signal_handlers_disconnect_by_func (nautilus_window_state,
3335                                           nautilus_files_view_display_selection_info, view);
3336     g_signal_handlers_disconnect_by_func (gnome_lockdown_preferences,
3337                                           schedule_update_context_menus, view);
3338     g_signal_handlers_disconnect_by_func (nautilus_trash_monitor_get (),
3339                                           nautilus_files_view_trash_state_changed_callback, view);
3340 
3341     clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
3342     g_signal_handlers_disconnect_by_func (clipboard, on_clipboard_owner_changed, view);
3343 
3344     nautilus_file_unref (priv->directory_as_file);
3345     priv->directory_as_file = NULL;
3346 
3347     g_clear_object (&priv->search_query);
3348     g_clear_object (&priv->location);
3349 
3350     /* We don't own the slot, so no unref */
3351     priv->slot = NULL;
3352 
3353     GTK_WIDGET_CLASS (nautilus_files_view_parent_class)->destroy (object);
3354 }
3355 
3356 static void
nautilus_files_view_finalize(GObject * object)3357 nautilus_files_view_finalize (GObject *object)
3358 {
3359     NautilusFilesView *view;
3360     NautilusFilesViewPrivate *priv;
3361 
3362     view = NAUTILUS_FILES_VIEW (object);
3363     priv = nautilus_files_view_get_instance_private (view);
3364 
3365     g_clear_object (&priv->view_action_group);
3366     g_clear_object (&priv->background_menu_model);
3367     g_clear_object (&priv->selection_menu_model);
3368     g_clear_object (&priv->toolbar_menu_sections->zoom_section);
3369     g_clear_object (&priv->toolbar_menu_sections->extended_section);
3370     g_clear_object (&priv->extensions_background_menu);
3371     g_clear_object (&priv->templates_menu);
3372     g_clear_object (&priv->rename_file_controller);
3373     g_clear_object (&priv->new_folder_controller);
3374     g_clear_object (&priv->compress_controller);
3375     g_free (priv->toolbar_menu_sections);
3376 
3377     g_hash_table_destroy (priv->non_ready_files);
3378     g_hash_table_destroy (priv->pending_reveal);
3379 
3380     g_cancellable_cancel (priv->starred_cancellable);
3381     g_clear_object (&priv->starred_cancellable);
3382     g_clear_object (&priv->tag_manager);
3383 
3384     G_OBJECT_CLASS (nautilus_files_view_parent_class)->finalize (object);
3385 }
3386 
3387 /**
3388  * nautilus_files_view_display_selection_info:
3389  *
3390  * Display information about the current selection, and notify the view frame of the changed selection.
3391  * @view: NautilusFilesView for which to display selection info.
3392  *
3393  **/
3394 void
nautilus_files_view_display_selection_info(NautilusFilesView * view)3395 nautilus_files_view_display_selection_info (NautilusFilesView *view)
3396 {
3397     g_autolist (NautilusFile) selection = NULL;
3398     goffset non_folder_size;
3399     gboolean non_folder_size_known;
3400     guint non_folder_count, folder_count, folder_item_count;
3401     gboolean folder_item_count_known;
3402     guint file_item_count;
3403     GList *p;
3404     char *first_item_name;
3405     char *non_folder_count_str;
3406     char *non_folder_item_count_str;
3407     char *non_folder_counts_str;
3408     char *folder_count_str;
3409     char *folder_item_count_str;
3410     char *folder_counts_str;
3411     char *primary_status;
3412     char *detail_status;
3413     NautilusFile *file;
3414 
3415     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
3416 
3417     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
3418 
3419     folder_item_count_known = TRUE;
3420     folder_count = 0;
3421     folder_item_count = 0;
3422     non_folder_count = 0;
3423     non_folder_size_known = FALSE;
3424     non_folder_size = 0;
3425     first_item_name = NULL;
3426     folder_count_str = NULL;
3427     folder_item_count_str = NULL;
3428     folder_counts_str = NULL;
3429     non_folder_count_str = NULL;
3430     non_folder_item_count_str = NULL;
3431     non_folder_counts_str = NULL;
3432 
3433     for (p = selection; p != NULL; p = p->next)
3434     {
3435         file = p->data;
3436         if (nautilus_file_is_directory (file))
3437         {
3438             folder_count++;
3439             if (nautilus_file_get_directory_item_count (file, &file_item_count, NULL))
3440             {
3441                 folder_item_count += file_item_count;
3442             }
3443             else
3444             {
3445                 folder_item_count_known = FALSE;
3446             }
3447         }
3448         else
3449         {
3450             non_folder_count++;
3451             if (!nautilus_file_can_get_size (file))
3452             {
3453                 non_folder_size_known = TRUE;
3454                 non_folder_size += nautilus_file_get_size (file);
3455             }
3456         }
3457 
3458         if (first_item_name == NULL)
3459         {
3460             first_item_name = nautilus_file_get_display_name (file);
3461         }
3462     }
3463 
3464     /* Break out cases for localization's sake. But note that there are still pieces
3465      * being assembled in a particular order, which may be a problem for some localizers.
3466      */
3467 
3468     if (folder_count != 0)
3469     {
3470         if (folder_count == 1 && non_folder_count == 0)
3471         {
3472             folder_count_str = g_strdup_printf (_("“%s” selected"), first_item_name);
3473         }
3474         else
3475         {
3476             folder_count_str = g_strdup_printf (ngettext ("%'d folder selected",
3477                                                           "%'d folders selected",
3478                                                           folder_count),
3479                                                 folder_count);
3480         }
3481 
3482         if (folder_count == 1)
3483         {
3484             if (!folder_item_count_known)
3485             {
3486                 folder_item_count_str = g_strdup ("");
3487             }
3488             else
3489             {
3490                 folder_item_count_str = g_strdup_printf (ngettext ("(containing %'d item)",
3491                                                                    "(containing %'d items)",
3492                                                                    folder_item_count),
3493                                                          folder_item_count);
3494             }
3495         }
3496         else
3497         {
3498             if (!folder_item_count_known)
3499             {
3500                 folder_item_count_str = g_strdup ("");
3501             }
3502             else
3503             {
3504                 /* translators: this is preceded with a string of form 'N folders' (N more than 1) */
3505                 folder_item_count_str = g_strdup_printf (ngettext ("(containing a total of %'d item)",
3506                                                                    "(containing a total of %'d items)",
3507                                                                    folder_item_count),
3508                                                          folder_item_count);
3509             }
3510         }
3511     }
3512 
3513     if (non_folder_count != 0)
3514     {
3515         if (folder_count == 0)
3516         {
3517             if (non_folder_count == 1)
3518             {
3519                 non_folder_count_str = g_strdup_printf (_("“%s” selected"),
3520                                                         first_item_name);
3521             }
3522             else
3523             {
3524                 non_folder_count_str = g_strdup_printf (ngettext ("%'d item selected",
3525                                                                   "%'d items selected",
3526                                                                   non_folder_count),
3527                                                         non_folder_count);
3528             }
3529         }
3530         else
3531         {
3532             /* Folders selected also, use "other" terminology */
3533             non_folder_count_str = g_strdup_printf (ngettext ("%'d other item selected",
3534                                                               "%'d other items selected",
3535                                                               non_folder_count),
3536                                                     non_folder_count);
3537         }
3538 
3539         if (non_folder_size_known)
3540         {
3541             char *size_string;
3542 
3543             size_string = g_format_size (non_folder_size);
3544             /* This is marked for translation in case a localiser
3545              * needs to use something other than parentheses. The
3546              * the message in parentheses is the size of the selected items.
3547              */
3548             non_folder_item_count_str = g_strdup_printf (_("(%s)"), size_string);
3549             g_free (size_string);
3550         }
3551         else
3552         {
3553             non_folder_item_count_str = g_strdup ("");
3554         }
3555     }
3556 
3557     if (folder_count == 0 && non_folder_count == 0)
3558     {
3559         primary_status = NULL;
3560         detail_status = NULL;
3561     }
3562     else if (folder_count == 0)
3563     {
3564         primary_status = g_strdup (non_folder_count_str);
3565         detail_status = g_strdup (non_folder_item_count_str);
3566     }
3567     else if (non_folder_count == 0)
3568     {
3569         primary_status = g_strdup (folder_count_str);
3570         detail_status = g_strdup (folder_item_count_str);
3571     }
3572     else
3573     {
3574         if (folder_item_count_known)
3575         {
3576             folder_counts_str = g_strconcat (folder_count_str, " ", folder_item_count_str, NULL);
3577         }
3578         else
3579         {
3580             folder_counts_str = g_strdup (folder_count_str);
3581         }
3582 
3583         if (non_folder_size_known)
3584         {
3585             non_folder_counts_str = g_strconcat (non_folder_count_str, " ", non_folder_item_count_str, NULL);
3586         }
3587         else
3588         {
3589             non_folder_counts_str = g_strdup (non_folder_count_str);
3590         }
3591         /* This is marked for translation in case a localizer
3592          * needs to change ", " to something else. The comma
3593          * is between the message about the number of folders
3594          * and the number of items in those folders and the
3595          * message about the number of other items and the
3596          * total size of those items.
3597          */
3598         primary_status = g_strdup_printf (_("%s, %s"),
3599                                           folder_counts_str,
3600                                           non_folder_counts_str);
3601         detail_status = NULL;
3602     }
3603 
3604     g_free (first_item_name);
3605     g_free (folder_count_str);
3606     g_free (folder_item_count_str);
3607     g_free (folder_counts_str);
3608     g_free (non_folder_count_str);
3609     g_free (non_folder_item_count_str);
3610     g_free (non_folder_counts_str);
3611 
3612     set_floating_bar_status (view, primary_status, detail_status);
3613 
3614     g_free (primary_status);
3615     g_free (detail_status);
3616 }
3617 
3618 static void
nautilus_files_view_send_selection_change(NautilusFilesView * view)3619 nautilus_files_view_send_selection_change (NautilusFilesView *view)
3620 {
3621     g_signal_emit (view, signals[SELECTION_CHANGED], 0);
3622     g_object_notify (G_OBJECT (view), "selection");
3623 }
3624 
3625 static void
nautilus_files_view_set_location(NautilusView * view,GFile * location)3626 nautilus_files_view_set_location (NautilusView *view,
3627                                   GFile        *location)
3628 {
3629     NautilusDirectory *directory;
3630     NautilusFilesView *files_view;
3631 
3632     nautilus_profile_start (NULL);
3633     files_view = NAUTILUS_FILES_VIEW (view);
3634     directory = nautilus_directory_get (location);
3635 
3636     nautilus_files_view_stop_loading (files_view);
3637     /* In case we want to load a previous search we need to extract the real
3638      * location and the search location, and load the directory when everything
3639      * is ready. That's why we cannot use the nautilus_view_set_query, because
3640      * to set a query we need a previous location loaded, but to load a search
3641      * location we need to know the real location behind it. */
3642     if (NAUTILUS_IS_SEARCH_DIRECTORY (directory))
3643     {
3644         NautilusQuery *previous_query;
3645         NautilusDirectory *base_model;
3646 
3647         base_model = nautilus_search_directory_get_base_model (NAUTILUS_SEARCH_DIRECTORY (directory));
3648         previous_query = nautilus_search_directory_get_query (NAUTILUS_SEARCH_DIRECTORY (directory));
3649         set_search_query_internal (files_view, previous_query, base_model);
3650         g_object_unref (previous_query);
3651     }
3652     else
3653     {
3654         load_directory (NAUTILUS_FILES_VIEW (view), directory);
3655     }
3656     nautilus_directory_unref (directory);
3657     nautilus_profile_end (NULL);
3658 }
3659 
3660 static gboolean
reveal_selection_idle_callback(gpointer data)3661 reveal_selection_idle_callback (gpointer data)
3662 {
3663     NautilusFilesView *view;
3664     NautilusFilesViewPrivate *priv;
3665 
3666     view = NAUTILUS_FILES_VIEW (data);
3667     priv = nautilus_files_view_get_instance_private (view);
3668 
3669     priv->reveal_selection_idle_id = 0;
3670     nautilus_files_view_reveal_selection (view);
3671 
3672     return FALSE;
3673 }
3674 
3675 static void
nautilus_files_view_check_empty_states(NautilusFilesView * view)3676 nautilus_files_view_check_empty_states (NautilusFilesView *view)
3677 {
3678     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->check_empty_states (view);
3679 }
3680 
3681 static void
real_check_empty_states(NautilusFilesView * view)3682 real_check_empty_states (NautilusFilesView *view)
3683 {
3684     NautilusFilesViewPrivate *priv;
3685     g_autofree gchar *uri = NULL;
3686 
3687     priv = nautilus_files_view_get_instance_private (view);
3688 
3689     gtk_widget_hide (priv->no_search_results_widget);
3690     gtk_widget_hide (priv->folder_is_empty_widget);
3691     gtk_widget_hide (priv->trash_is_empty_widget);
3692     gtk_widget_hide (priv->starred_is_empty_widget);
3693 
3694     if (!priv->loading &&
3695         nautilus_files_view_is_empty (view))
3696     {
3697         uri = g_file_get_uri (priv->location);
3698 
3699         if (nautilus_view_is_searching (NAUTILUS_VIEW (view)))
3700         {
3701             gtk_widget_show (priv->no_search_results_widget);
3702         }
3703         else if (eel_uri_is_trash_root (uri))
3704         {
3705             gtk_widget_show (priv->trash_is_empty_widget);
3706         }
3707         else if (eel_uri_is_starred (uri))
3708         {
3709             gtk_widget_show (priv->starred_is_empty_widget);
3710         }
3711         else
3712         {
3713             gtk_widget_show (priv->folder_is_empty_widget);
3714         }
3715     }
3716 }
3717 
3718 static void
done_loading(NautilusFilesView * view,gboolean all_files_seen)3719 done_loading (NautilusFilesView *view,
3720               gboolean           all_files_seen)
3721 {
3722     NautilusFilesViewPrivate *priv;
3723     g_autolist (NautilusFile) selection = NULL;
3724     gboolean do_reveal = FALSE;
3725 
3726     priv = nautilus_files_view_get_instance_private (view);
3727 
3728     if (!priv->loading)
3729     {
3730         return;
3731     }
3732 
3733     nautilus_profile_start (NULL);
3734 
3735     if (!priv->in_destruction)
3736     {
3737         remove_loading_floating_bar (view);
3738         schedule_update_context_menus (view);
3739         schedule_update_status (view);
3740         nautilus_files_view_update_toolbar_menus (view);
3741         reset_update_interval (view);
3742 
3743         selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
3744 
3745         if (nautilus_view_is_searching (NAUTILUS_VIEW (view)) &&
3746             all_files_seen && selection == NULL && priv->pending_selection == NULL)
3747         {
3748             nautilus_files_view_select_first (view);
3749             do_reveal = TRUE;
3750         }
3751         else if (priv->pending_selection != NULL && all_files_seen)
3752         {
3753             g_autolist (NautilusFile) pending_selection = NULL;
3754             pending_selection = g_steal_pointer (&priv->pending_selection);
3755 
3756             nautilus_files_view_call_set_selection (view, pending_selection);
3757             do_reveal = TRUE;
3758         }
3759 
3760         g_clear_pointer (&priv->pending_selection, nautilus_file_list_free);
3761 
3762         if (do_reveal)
3763         {
3764             if (NAUTILUS_IS_LIST_VIEW (view) || NAUTILUS_IS_VIEW_ICON_CONTROLLER (view))
3765             {
3766                 /* HACK: We should be able to directly call reveal_selection here,
3767                  * but at this point the GtkTreeView hasn't allocated the new nodes
3768                  * yet, and it has a bug in the scroll calculation dealing with this
3769                  * special case. It would always make the selection the top row, even
3770                  * if no scrolling would be neccessary to reveal it. So we let it
3771                  * allocate before revealing.
3772                  */
3773                 if (priv->reveal_selection_idle_id != 0)
3774                 {
3775                     g_source_remove (priv->reveal_selection_idle_id);
3776                 }
3777                 priv->reveal_selection_idle_id =
3778                     g_idle_add (reveal_selection_idle_callback, view);
3779             }
3780             else
3781             {
3782                 nautilus_files_view_reveal_selection (view);
3783             }
3784         }
3785         nautilus_files_view_display_selection_info (view);
3786     }
3787 
3788     priv->loading = FALSE;
3789     g_signal_emit (view, signals[END_LOADING], 0, all_files_seen);
3790     g_object_notify (G_OBJECT (view), "loading");
3791 
3792     if (!priv->in_destruction)
3793     {
3794         nautilus_files_view_check_empty_states (view);
3795     }
3796 
3797     nautilus_profile_end (NULL);
3798 }
3799 
3800 
3801 typedef struct
3802 {
3803     GHashTable *debuting_files;
3804     GList *added_files;
3805 } DebutingFilesData;
3806 
3807 static void
debuting_files_data_free(DebutingFilesData * data)3808 debuting_files_data_free (DebutingFilesData *data)
3809 {
3810     g_hash_table_unref (data->debuting_files);
3811     nautilus_file_list_free (data->added_files);
3812     g_free (data);
3813 }
3814 
3815 /* This signal handler watch for the arrival of the icons created
3816  * as the result of a file operation. Once the last one is detected
3817  * it selects and reveals them all.
3818  */
3819 static void
debuting_files_add_files_callback(NautilusFilesView * view,GList * new_files,DebutingFilesData * data)3820 debuting_files_add_files_callback (NautilusFilesView *view,
3821                                    GList             *new_files,
3822                                    DebutingFilesData *data)
3823 {
3824     GFile *location;
3825     GList *l;
3826 
3827     nautilus_profile_start (NULL);
3828 
3829     for (l = new_files; l != NULL; l = l->next)
3830     {
3831         location = nautilus_file_get_location (NAUTILUS_FILE (l->data));
3832 
3833         if (g_hash_table_remove (data->debuting_files, location))
3834         {
3835             nautilus_file_ref (NAUTILUS_FILE (l->data));
3836             data->added_files = g_list_prepend (data->added_files, NAUTILUS_FILE (l->data));
3837         }
3838         g_object_unref (location);
3839     }
3840 
3841     if (g_hash_table_size (data->debuting_files) == 0)
3842     {
3843         nautilus_files_view_call_set_selection (view, data->added_files);
3844         nautilus_files_view_reveal_selection (view);
3845         g_signal_handlers_disconnect_by_func (view,
3846                                               G_CALLBACK (debuting_files_add_files_callback),
3847                                               data);
3848     }
3849 
3850     nautilus_profile_end (NULL);
3851 }
3852 
3853 typedef struct
3854 {
3855     GList *added_files;
3856     NautilusFilesView *directory_view;
3857 } CopyMoveDoneData;
3858 
3859 static void
copy_move_done_data_free(CopyMoveDoneData * data)3860 copy_move_done_data_free (CopyMoveDoneData *data)
3861 {
3862     g_assert (data != NULL);
3863 
3864     if (data->directory_view != NULL)
3865     {
3866         g_object_remove_weak_pointer (G_OBJECT (data->directory_view),
3867                                       (gpointer *) &data->directory_view);
3868     }
3869 
3870     nautilus_file_list_free (data->added_files);
3871     g_free (data);
3872 }
3873 
3874 static void
pre_copy_move_add_files_callback(NautilusFilesView * view,GList * new_files,CopyMoveDoneData * data)3875 pre_copy_move_add_files_callback (NautilusFilesView *view,
3876                                   GList             *new_files,
3877                                   CopyMoveDoneData  *data)
3878 {
3879     GList *l;
3880 
3881     for (l = new_files; l != NULL; l = l->next)
3882     {
3883         nautilus_file_ref (NAUTILUS_FILE (l->data));
3884         data->added_files = g_list_prepend (data->added_files, l->data);
3885     }
3886 }
3887 
3888 /* This needs to be called prior to nautilus_file_operations_copy_move.
3889  * It hooks up a signal handler to catch any icons that get added before
3890  * the copy_done_callback is invoked. The return value should  be passed
3891  * as the data for uri_copy_move_done_callback.
3892  */
3893 static CopyMoveDoneData *
pre_copy_move(NautilusFilesView * directory_view)3894 pre_copy_move (NautilusFilesView *directory_view)
3895 {
3896     CopyMoveDoneData *copy_move_done_data;
3897 
3898     copy_move_done_data = g_new0 (CopyMoveDoneData, 1);
3899     copy_move_done_data->directory_view = directory_view;
3900 
3901     g_object_add_weak_pointer (G_OBJECT (copy_move_done_data->directory_view),
3902                                (gpointer *) &copy_move_done_data->directory_view);
3903 
3904     /* We need to run after the default handler adds the folder we want to
3905      * operate on. The ADD_FILES signal is registered as G_SIGNAL_RUN_LAST, so we
3906      * must use connect_after.
3907      */
3908     g_signal_connect_after (directory_view, "add-files",
3909                             G_CALLBACK (pre_copy_move_add_files_callback), copy_move_done_data);
3910 
3911     return copy_move_done_data;
3912 }
3913 
3914 /* This function is used to pull out any debuting uris that were added
3915  * and (as a side effect) remove them from the debuting uri hash table.
3916  */
3917 static gboolean
copy_move_done_partition_func(NautilusFile * file,gpointer callback_data)3918 copy_move_done_partition_func (NautilusFile *file,
3919                                gpointer      callback_data)
3920 {
3921     GFile *location;
3922     gboolean result;
3923 
3924     location = nautilus_file_get_location (file);
3925     result = g_hash_table_remove ((GHashTable *) callback_data, location);
3926     g_object_unref (location);
3927 
3928     return result;
3929 }
3930 
3931 static gboolean
remove_not_really_moved_files(gpointer key,gpointer value,gpointer callback_data)3932 remove_not_really_moved_files (gpointer key,
3933                                gpointer value,
3934                                gpointer callback_data)
3935 {
3936     GList **added_files;
3937     GFile *loc;
3938 
3939     loc = key;
3940 
3941     if (GPOINTER_TO_INT (value))
3942     {
3943         return FALSE;
3944     }
3945 
3946     added_files = callback_data;
3947     *added_files = g_list_prepend (*added_files,
3948                                    nautilus_file_get (loc));
3949     return TRUE;
3950 }
3951 
3952 /* When this function is invoked, the file operation is over, but all
3953  * the icons may not have been added to the directory view yet, so
3954  * we can't select them yet.
3955  *
3956  * We're passed a hash table of the uri's to look out for, we hook
3957  * up a signal handler to await their arrival.
3958  */
3959 static void
copy_move_done_callback(GHashTable * debuting_files,gboolean success,gpointer data)3960 copy_move_done_callback (GHashTable *debuting_files,
3961                          gboolean    success,
3962                          gpointer    data)
3963 {
3964     NautilusFilesView *directory_view;
3965     CopyMoveDoneData *copy_move_done_data;
3966     DebutingFilesData *debuting_files_data;
3967     GList *failed_files;
3968 
3969     copy_move_done_data = (CopyMoveDoneData *) data;
3970     directory_view = copy_move_done_data->directory_view;
3971 
3972     if (directory_view != NULL)
3973     {
3974         g_assert (NAUTILUS_IS_FILES_VIEW (directory_view));
3975 
3976         debuting_files_data = g_new (DebutingFilesData, 1);
3977         debuting_files_data->debuting_files = g_hash_table_ref (debuting_files);
3978         debuting_files_data->added_files = nautilus_file_list_filter (copy_move_done_data->added_files,
3979                                                                       &failed_files,
3980                                                                       copy_move_done_partition_func,
3981                                                                       debuting_files);
3982         nautilus_file_list_free (copy_move_done_data->added_files);
3983         copy_move_done_data->added_files = failed_files;
3984 
3985         /* We're passed the same data used by pre_copy_move_add_files_callback, so disconnecting
3986          * it will free data. We've already siphoned off the added_files we need, and stashed the
3987          * directory_view pointer.
3988          */
3989         g_signal_handlers_disconnect_by_func (directory_view,
3990                                               G_CALLBACK (pre_copy_move_add_files_callback),
3991                                               data);
3992 
3993         /* Any items in the debuting_files hash table that have
3994          * "FALSE" as their value aren't really being copied
3995          * or moved, so we can't wait for an add_files signal
3996          * to come in for those.
3997          */
3998         g_hash_table_foreach_remove (debuting_files,
3999                                      remove_not_really_moved_files,
4000                                      &debuting_files_data->added_files);
4001 
4002         if (g_hash_table_size (debuting_files) == 0)
4003         {
4004             /* on the off-chance that all the icons have already been added */
4005             if (debuting_files_data->added_files != NULL)
4006             {
4007                 nautilus_files_view_call_set_selection (directory_view,
4008                                                         debuting_files_data->added_files);
4009                 nautilus_files_view_reveal_selection (directory_view);
4010             }
4011             debuting_files_data_free (debuting_files_data);
4012         }
4013         else
4014         {
4015             /* We need to run after the default handler adds the folder we want to
4016              * operate on. The ADD_FILES signal is registered as G_SIGNAL_RUN_LAST, so we
4017              * must use connect_after.
4018              */
4019             g_signal_connect_data (directory_view,
4020                                    "add-files",
4021                                    G_CALLBACK (debuting_files_add_files_callback),
4022                                    debuting_files_data,
4023                                    (GClosureNotify) debuting_files_data_free,
4024                                    G_CONNECT_AFTER);
4025         }
4026         /* Schedule menu update for undo items */
4027         schedule_update_context_menus (directory_view);
4028     }
4029 
4030     copy_move_done_data_free (copy_move_done_data);
4031 }
4032 
4033 static gboolean
view_file_still_belongs(NautilusFilesView * view,FileAndDirectory * fad)4034 view_file_still_belongs (NautilusFilesView *view,
4035                          FileAndDirectory  *fad)
4036 {
4037     NautilusFilesViewPrivate *priv;
4038 
4039     priv = nautilus_files_view_get_instance_private (view);
4040 
4041     if (priv->model != fad->directory &&
4042         g_list_find (priv->subdirectory_list, fad->directory) == NULL)
4043     {
4044         return FALSE;
4045     }
4046 
4047     return nautilus_directory_contains_file (fad->directory, fad->file);
4048 }
4049 
4050 static gboolean
still_should_show_file(NautilusFilesView * view,FileAndDirectory * fad)4051 still_should_show_file (NautilusFilesView *view,
4052                         FileAndDirectory  *fad)
4053 {
4054     return nautilus_files_view_should_show_file (view, fad->file) &&
4055            view_file_still_belongs (view, fad);
4056 }
4057 
4058 static gboolean
ready_to_load(NautilusFile * file)4059 ready_to_load (NautilusFile *file)
4060 {
4061     return nautilus_file_check_if_ready (file,
4062                                          NAUTILUS_FILE_ATTRIBUTES_FOR_ICON);
4063 }
4064 
4065 static int
compare_files_cover(gconstpointer a,gconstpointer b,gpointer callback_data)4066 compare_files_cover (gconstpointer a,
4067                      gconstpointer b,
4068                      gpointer      callback_data)
4069 {
4070     const FileAndDirectory *fad1, *fad2;
4071     NautilusFilesView *view;
4072 
4073     view = callback_data;
4074     fad1 = a;
4075     fad2 = b;
4076 
4077     if (fad1->directory < fad2->directory)
4078     {
4079         return -1;
4080     }
4081     else if (fad1->directory > fad2->directory)
4082     {
4083         return 1;
4084     }
4085     else
4086     {
4087         return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->compare_files (view, fad1->file, fad2->file);
4088     }
4089 }
4090 static void
sort_files(NautilusFilesView * view,GList ** list)4091 sort_files (NautilusFilesView  *view,
4092             GList             **list)
4093 {
4094     *list = g_list_sort_with_data (*list, compare_files_cover, view);
4095 }
4096 
4097 /* Go through all the new added and changed files.
4098  * Put any that are not ready to load in the non_ready_files hash table.
4099  * Add all the rest to the old_added_files and old_changed_files lists.
4100  * Sort the old_*_files lists if anything was added to them.
4101  */
4102 static void
process_new_files(NautilusFilesView * view)4103 process_new_files (NautilusFilesView *view)
4104 {
4105     NautilusFilesViewPrivate *priv;
4106     g_autolist (FileAndDirectory) new_added_files = NULL;
4107     g_autolist (FileAndDirectory) new_changed_files = NULL;
4108     GList *old_added_files;
4109     GList *old_changed_files;
4110     GHashTable *non_ready_files;
4111     GList *node, *next;
4112     FileAndDirectory *pending;
4113     gboolean in_non_ready;
4114 
4115     priv = nautilus_files_view_get_instance_private (view);
4116 
4117     new_added_files = g_steal_pointer (&priv->new_added_files);
4118     new_changed_files = g_steal_pointer (&priv->new_changed_files);
4119 
4120     non_ready_files = priv->non_ready_files;
4121 
4122     old_added_files = priv->old_added_files;
4123     old_changed_files = priv->old_changed_files;
4124 
4125     /* Newly added files go into the old_added_files list if they're
4126      * ready, and into the hash table if they're not.
4127      */
4128     for (node = new_added_files; node != NULL; node = next)
4129     {
4130         next = node->next;
4131         pending = (FileAndDirectory *) node->data;
4132         in_non_ready = g_hash_table_contains (non_ready_files, pending);
4133         if (nautilus_files_view_should_show_file (view, pending->file))
4134         {
4135             if (ready_to_load (pending->file))
4136             {
4137                 if (in_non_ready)
4138                 {
4139                     g_hash_table_remove (non_ready_files, pending);
4140                 }
4141                 new_added_files = g_list_delete_link (new_added_files, node);
4142                 old_added_files = g_list_prepend (old_added_files, pending);
4143             }
4144             else
4145             {
4146                 if (!in_non_ready)
4147                 {
4148                     new_added_files = g_list_delete_link (new_added_files, node);
4149                     g_hash_table_add (non_ready_files, pending);
4150                 }
4151             }
4152         }
4153     }
4154 
4155     /* Newly changed files go into the old_added_files list if they're ready
4156      * and were seen non-ready in the past, into the old_changed_files list
4157      * if they are read and were not seen non-ready in the past, and into
4158      * the hash table if they're not ready.
4159      */
4160     for (node = new_changed_files; node != NULL; node = next)
4161     {
4162         next = node->next;
4163         pending = (FileAndDirectory *) node->data;
4164         if (!still_should_show_file (view, pending) || ready_to_load (pending->file))
4165         {
4166             if (g_hash_table_contains (non_ready_files, pending))
4167             {
4168                 g_hash_table_remove (non_ready_files, pending);
4169                 if (still_should_show_file (view, pending))
4170                 {
4171                     new_changed_files = g_list_delete_link (new_changed_files, node);
4172                     old_added_files = g_list_prepend (old_added_files, pending);
4173                 }
4174             }
4175             else
4176             {
4177                 new_changed_files = g_list_delete_link (new_changed_files, node);
4178                 old_changed_files = g_list_prepend (old_changed_files, pending);
4179             }
4180         }
4181     }
4182 
4183     /* If any files were added to old_added_files, then resort it. */
4184     if (old_added_files != priv->old_added_files)
4185     {
4186         priv->old_added_files = old_added_files;
4187         sort_files (view, &priv->old_added_files);
4188     }
4189 
4190     /* Resort old_changed_files too, since file attributes
4191      * relevant to sorting could have changed.
4192      */
4193     if (old_changed_files != priv->old_changed_files)
4194     {
4195         priv->old_changed_files = old_changed_files;
4196         sort_files (view, &priv->old_changed_files);
4197     }
4198 }
4199 
4200 static void
on_end_file_changes(NautilusFilesView * view)4201 on_end_file_changes (NautilusFilesView *view)
4202 {
4203     NautilusFilesViewPrivate *priv;
4204 
4205     priv = nautilus_files_view_get_instance_private (view);
4206 
4207     /* Addition and removal of files modify the empty state */
4208     nautilus_files_view_check_empty_states (view);
4209     /* If the view is empty, zoom slider and sort menu are insensitive */
4210     nautilus_files_view_update_toolbar_menus (view);
4211 
4212     /* Reveal files that were pending to be revealed, only if all of them
4213      * were acknowledged by the view
4214      */
4215     if (g_hash_table_size (priv->pending_reveal) > 0)
4216     {
4217         GList *keys;
4218         GList *l;
4219         gboolean all_files_acknowledged = TRUE;
4220 
4221         keys = g_hash_table_get_keys (priv->pending_reveal);
4222         for (l = keys; l && all_files_acknowledged; l = l->next)
4223         {
4224             all_files_acknowledged = GPOINTER_TO_UINT (g_hash_table_lookup (priv->pending_reveal,
4225                                                                             l->data));
4226         }
4227 
4228         if (all_files_acknowledged)
4229         {
4230             nautilus_files_view_set_selection (NAUTILUS_VIEW (view), keys);
4231             nautilus_files_view_reveal_selection (view);
4232             g_hash_table_remove_all (priv->pending_reveal);
4233         }
4234 
4235         g_list_free (keys);
4236     }
4237 }
4238 
4239 static int
compare_pointers(gconstpointer pointer_1,gconstpointer pointer_2)4240 compare_pointers (gconstpointer pointer_1,
4241                   gconstpointer pointer_2)
4242 {
4243     if (pointer_1 < pointer_2)
4244     {
4245         return -1;
4246     }
4247     else if (pointer_1 > pointer_2)
4248     {
4249         return +1;
4250     }
4251 
4252     return 0;
4253 }
4254 
4255 static gboolean
_g_lists_sort_and_check_for_intersection(GList ** list_1,GList ** list_2)4256 _g_lists_sort_and_check_for_intersection (GList **list_1,
4257                                           GList **list_2)
4258 {
4259     GList *node_1;
4260     GList *node_2;
4261     int compare_result;
4262 
4263     *list_1 = g_list_sort (*list_1, compare_pointers);
4264     *list_2 = g_list_sort (*list_2, compare_pointers);
4265 
4266     node_1 = *list_1;
4267     node_2 = *list_2;
4268 
4269     while (node_1 != NULL && node_2 != NULL)
4270     {
4271         compare_result = compare_pointers (node_1->data, node_2->data);
4272         if (compare_result == 0)
4273         {
4274             return TRUE;
4275         }
4276         if (compare_result <= 0)
4277         {
4278             node_1 = node_1->next;
4279         }
4280         if (compare_result >= 0)
4281         {
4282             node_2 = node_2->next;
4283         }
4284     }
4285 
4286     return FALSE;
4287 }
4288 
4289 static void
process_old_files(NautilusFilesView * view)4290 process_old_files (NautilusFilesView *view)
4291 {
4292     NautilusFilesViewPrivate *priv;
4293     g_autolist (FileAndDirectory) files_added = NULL;
4294     g_autolist (FileAndDirectory) files_changed = NULL;
4295     FileAndDirectory *pending;
4296     GList *files;
4297     g_autoptr (GList) pending_additions = NULL;
4298 
4299     priv = nautilus_files_view_get_instance_private (view);
4300     files_added = g_steal_pointer (&priv->old_added_files);
4301     files_changed = g_steal_pointer (&priv->old_changed_files);
4302 
4303 
4304     if (files_added != NULL || files_changed != NULL)
4305     {
4306         gboolean send_selection_change = FALSE;
4307 
4308         g_signal_emit (view, signals[BEGIN_FILE_CHANGES], 0);
4309 
4310         for (GList *node = files_added; node != NULL; node = node->next)
4311         {
4312             pending = node->data;
4313             pending_additions = g_list_prepend (pending_additions, pending->file);
4314             /* Acknowledge the files that were pending to be revealed */
4315             if (g_hash_table_contains (priv->pending_reveal, pending->file))
4316             {
4317                 g_hash_table_insert (priv->pending_reveal,
4318                                      pending->file,
4319                                      GUINT_TO_POINTER (TRUE));
4320             }
4321         }
4322 
4323         if (files_added != NULL)
4324         {
4325             g_signal_emit (view,
4326                            signals[ADD_FILES], 0, pending_additions);
4327         }
4328 
4329         for (GList *node = files_changed; node != NULL; node = node->next)
4330         {
4331             gboolean should_show_file;
4332             pending = node->data;
4333             should_show_file = still_should_show_file (view, pending);
4334             g_signal_emit (view,
4335                            signals[should_show_file ? FILE_CHANGED : REMOVE_FILE], 0,
4336                            pending->file, pending->directory);
4337 
4338             /* Acknowledge the files that were pending to be revealed */
4339             if (g_hash_table_contains (priv->pending_reveal, pending->file))
4340             {
4341                 if (should_show_file)
4342                 {
4343                     g_hash_table_insert (priv->pending_reveal,
4344                                          pending->file,
4345                                          GUINT_TO_POINTER (TRUE));
4346                 }
4347                 else
4348                 {
4349                     g_hash_table_remove (priv->pending_reveal,
4350                                          pending->file);
4351                 }
4352             }
4353         }
4354 
4355         if (files_changed != NULL)
4356         {
4357             g_autolist (NautilusFile) selection = NULL;
4358             selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
4359             files = g_list_copy_deep (files_changed, (GCopyFunc) file_and_directory_get_file, NULL);
4360             send_selection_change = _g_lists_sort_and_check_for_intersection
4361                                         (&files, &selection);
4362             nautilus_file_list_free (files);
4363         }
4364 
4365         if (send_selection_change)
4366         {
4367             /* Send a selection change since some file names could
4368              * have changed.
4369              */
4370             nautilus_files_view_send_selection_change (view);
4371         }
4372 
4373         g_signal_emit (view, signals[END_FILE_CHANGES], 0);
4374     }
4375 }
4376 
4377 static void
display_pending_files(NautilusFilesView * view)4378 display_pending_files (NautilusFilesView *view)
4379 {
4380     NautilusFilesViewPrivate *priv;
4381     g_autolist (NautilusFile) selection = NULL;
4382 
4383     process_new_files (view);
4384     process_old_files (view);
4385 
4386     priv = nautilus_files_view_get_instance_private (view);
4387     selection = nautilus_files_view_get_selection (NAUTILUS_VIEW (view));
4388 
4389     if (selection == NULL &&
4390         !priv->pending_selection &&
4391         nautilus_view_is_searching (NAUTILUS_VIEW (view)))
4392     {
4393         nautilus_files_view_select_first (view);
4394     }
4395 
4396     if (priv->model != NULL
4397         && nautilus_directory_are_all_files_seen (priv->model)
4398         && g_hash_table_size (priv->non_ready_files) == 0)
4399     {
4400         done_loading (view, TRUE);
4401     }
4402 }
4403 
4404 static gboolean
display_selection_info_idle_callback(gpointer data)4405 display_selection_info_idle_callback (gpointer data)
4406 {
4407     NautilusFilesView *view;
4408     NautilusFilesViewPrivate *priv;
4409 
4410     view = NAUTILUS_FILES_VIEW (data);
4411     priv = nautilus_files_view_get_instance_private (view);
4412 
4413     g_object_ref (G_OBJECT (view));
4414 
4415     priv->display_selection_idle_id = 0;
4416     nautilus_files_view_display_selection_info (view);
4417     nautilus_files_view_send_selection_change (view);
4418 
4419     g_object_unref (G_OBJECT (view));
4420 
4421     return FALSE;
4422 }
4423 
4424 static void
remove_update_context_menus_timeout_callback(NautilusFilesView * view)4425 remove_update_context_menus_timeout_callback (NautilusFilesView *view)
4426 {
4427     NautilusFilesViewPrivate *priv;
4428 
4429     priv = nautilus_files_view_get_instance_private (view);
4430 
4431     if (priv->update_context_menus_timeout_id != 0)
4432     {
4433         g_source_remove (priv->update_context_menus_timeout_id);
4434         priv->update_context_menus_timeout_id = 0;
4435     }
4436 }
4437 
4438 static void
update_context_menus_if_pending(NautilusFilesView * view)4439 update_context_menus_if_pending (NautilusFilesView *view)
4440 {
4441     remove_update_context_menus_timeout_callback (view);
4442 
4443     nautilus_files_view_update_context_menus (view);
4444 }
4445 
4446 static gboolean
update_context_menus_timeout_callback(gpointer data)4447 update_context_menus_timeout_callback (gpointer data)
4448 {
4449     NautilusFilesView *view;
4450     NautilusFilesViewPrivate *priv;
4451 
4452     view = NAUTILUS_FILES_VIEW (data);
4453     priv = nautilus_files_view_get_instance_private (view);
4454 
4455     g_object_ref (G_OBJECT (view));
4456 
4457     priv->update_context_menus_timeout_id = 0;
4458     nautilus_files_view_update_context_menus (view);
4459 
4460     g_object_unref (G_OBJECT (view));
4461 
4462     return FALSE;
4463 }
4464 
4465 static gboolean
display_pending_callback(gpointer data)4466 display_pending_callback (gpointer data)
4467 {
4468     NautilusFilesView *view;
4469     NautilusFilesViewPrivate *priv;
4470 
4471     view = NAUTILUS_FILES_VIEW (data);
4472     priv = nautilus_files_view_get_instance_private (view);
4473 
4474     g_object_ref (G_OBJECT (view));
4475 
4476     priv->display_pending_source_id = 0;
4477 
4478     display_pending_files (view);
4479 
4480     g_object_unref (G_OBJECT (view));
4481 
4482     return FALSE;
4483 }
4484 
4485 static void
schedule_idle_display_of_pending_files(NautilusFilesView * view)4486 schedule_idle_display_of_pending_files (NautilusFilesView *view)
4487 {
4488     NautilusFilesViewPrivate *priv;
4489 
4490     priv = nautilus_files_view_get_instance_private (view);
4491 
4492     /* Get rid of a pending source as it might be a timeout */
4493     unschedule_display_of_pending_files (view);
4494 
4495     /* We want higher priority than the idle that handles the relayout
4496      *  to avoid a resort on each add. But we still want to allow repaints
4497      *  and other hight prio events while we have pending files to show. */
4498     priv->display_pending_source_id =
4499         g_idle_add_full (G_PRIORITY_DEFAULT_IDLE - 20,
4500                          display_pending_callback, view, NULL);
4501 }
4502 
4503 static void
schedule_timeout_display_of_pending_files(NautilusFilesView * view,guint interval)4504 schedule_timeout_display_of_pending_files (NautilusFilesView *view,
4505                                            guint              interval)
4506 {
4507     NautilusFilesViewPrivate *priv;
4508 
4509     priv = nautilus_files_view_get_instance_private (view);
4510 
4511     /* No need to schedule an update if there's already one pending. */
4512     if (priv->display_pending_source_id != 0)
4513     {
4514         return;
4515     }
4516 
4517     priv->display_pending_source_id =
4518         g_timeout_add (interval, display_pending_callback, view);
4519 }
4520 
4521 static void
unschedule_display_of_pending_files(NautilusFilesView * view)4522 unschedule_display_of_pending_files (NautilusFilesView *view)
4523 {
4524     NautilusFilesViewPrivate *priv;
4525 
4526     priv = nautilus_files_view_get_instance_private (view);
4527 
4528     /* Get rid of source if it's active. */
4529     if (priv->display_pending_source_id != 0)
4530     {
4531         g_source_remove (priv->display_pending_source_id);
4532         priv->display_pending_source_id = 0;
4533     }
4534 }
4535 
4536 static void
queue_pending_files(NautilusFilesView * view,NautilusDirectory * directory,GList * files,GList ** pending_list)4537 queue_pending_files (NautilusFilesView  *view,
4538                      NautilusDirectory  *directory,
4539                      GList              *files,
4540                      GList             **pending_list)
4541 {
4542     NautilusFilesViewPrivate *priv;
4543     GList *fad_list;
4544 
4545     priv = nautilus_files_view_get_instance_private (view);
4546 
4547     if (files == NULL)
4548     {
4549         return;
4550     }
4551 
4552     fad_list = g_list_copy_deep (files, (GCopyFunc) file_and_directory_new, directory);
4553     *pending_list = g_list_concat (fad_list, *pending_list);
4554     /* Generally we don't want to show the files while the directory is loading
4555      * the files themselves, so we avoid jumping and oddities. However, for
4556      * search it can be a long wait, and we actually want to show files as
4557      * they are getting found. So for search is fine if not all files are
4558      * seen */
4559     if (!priv->loading ||
4560         (nautilus_directory_are_all_files_seen (directory) ||
4561          nautilus_view_is_searching (NAUTILUS_VIEW (view))))
4562     {
4563         schedule_timeout_display_of_pending_files (view, priv->update_interval);
4564     }
4565 }
4566 
4567 static void
remove_changes_timeout_callback(NautilusFilesView * view)4568 remove_changes_timeout_callback (NautilusFilesView *view)
4569 {
4570     NautilusFilesViewPrivate *priv;
4571 
4572     priv = nautilus_files_view_get_instance_private (view);
4573 
4574     if (priv->changes_timeout_id != 0)
4575     {
4576         g_source_remove (priv->changes_timeout_id);
4577         priv->changes_timeout_id = 0;
4578     }
4579 }
4580 
4581 static void
reset_update_interval(NautilusFilesView * view)4582 reset_update_interval (NautilusFilesView *view)
4583 {
4584     NautilusFilesViewPrivate *priv;
4585 
4586     priv = nautilus_files_view_get_instance_private (view);
4587 
4588     priv->update_interval = UPDATE_INTERVAL_MIN;
4589     remove_changes_timeout_callback (view);
4590     /* Reschedule a pending timeout to idle */
4591     if (priv->display_pending_source_id != 0)
4592     {
4593         schedule_idle_display_of_pending_files (view);
4594     }
4595 }
4596 
4597 static gboolean
changes_timeout_callback(gpointer data)4598 changes_timeout_callback (gpointer data)
4599 {
4600     gint64 now;
4601     gint64 time_delta;
4602     gboolean ret;
4603     NautilusFilesView *view;
4604     NautilusFilesViewPrivate *priv;
4605 
4606     view = NAUTILUS_FILES_VIEW (data);
4607     priv = nautilus_files_view_get_instance_private (view);
4608 
4609     g_object_ref (G_OBJECT (view));
4610 
4611     now = g_get_monotonic_time ();
4612     time_delta = now - priv->last_queued;
4613 
4614     if (time_delta < UPDATE_INTERVAL_RESET * 1000)
4615     {
4616         if (priv->update_interval < UPDATE_INTERVAL_MAX &&
4617             priv->loading)
4618         {
4619             /* Increase */
4620             priv->update_interval += UPDATE_INTERVAL_INC;
4621         }
4622         ret = TRUE;
4623     }
4624     else
4625     {
4626         /* Reset */
4627         reset_update_interval (view);
4628         ret = FALSE;
4629     }
4630 
4631     g_object_unref (G_OBJECT (view));
4632 
4633     return ret;
4634 }
4635 
4636 static void
schedule_changes(NautilusFilesView * view)4637 schedule_changes (NautilusFilesView *view)
4638 {
4639     NautilusFilesViewPrivate *priv;
4640 
4641     priv = nautilus_files_view_get_instance_private (view);
4642     /* Remember when the change was queued */
4643     priv->last_queued = g_get_monotonic_time ();
4644 
4645     /* No need to schedule if there are already changes pending or during loading */
4646     if (priv->changes_timeout_id != 0 ||
4647         priv->loading)
4648     {
4649         return;
4650     }
4651 
4652     priv->changes_timeout_id =
4653         g_timeout_add (UPDATE_INTERVAL_TIMEOUT_INTERVAL, changes_timeout_callback, view);
4654 }
4655 
4656 static void
files_added_callback(NautilusDirectory * directory,GList * files,gpointer callback_data)4657 files_added_callback (NautilusDirectory *directory,
4658                       GList             *files,
4659                       gpointer           callback_data)
4660 {
4661     NautilusFilesViewPrivate *priv;
4662     NautilusFilesView *view;
4663     GtkWindow *window;
4664     char *uri;
4665 
4666     view = NAUTILUS_FILES_VIEW (callback_data);
4667     priv = nautilus_files_view_get_instance_private (view);
4668 
4669     nautilus_profile_start (NULL);
4670 
4671     window = nautilus_files_view_get_containing_window (view);
4672     uri = nautilus_files_view_get_uri (view);
4673     DEBUG_FILES (files, "Files added in window %p: %s",
4674                  window, uri ? uri : "(no directory)");
4675     g_free (uri);
4676 
4677     schedule_changes (view);
4678 
4679     queue_pending_files (view, directory, files, &priv->new_added_files);
4680 
4681     /* The number of items could have changed */
4682     schedule_update_status (view);
4683 
4684     nautilus_profile_end (NULL);
4685 }
4686 
4687 static void
files_changed_callback(NautilusDirectory * directory,GList * files,gpointer callback_data)4688 files_changed_callback (NautilusDirectory *directory,
4689                         GList             *files,
4690                         gpointer           callback_data)
4691 {
4692     NautilusFilesViewPrivate *priv;
4693     NautilusFilesView *view;
4694     GtkWindow *window;
4695     char *uri;
4696 
4697     view = NAUTILUS_FILES_VIEW (callback_data);
4698     priv = nautilus_files_view_get_instance_private (view);
4699 
4700     window = nautilus_files_view_get_containing_window (view);
4701     uri = nautilus_files_view_get_uri (view);
4702     DEBUG_FILES (files, "Files changed in window %p: %s",
4703                  window, uri ? uri : "(no directory)");
4704     g_free (uri);
4705 
4706     schedule_changes (view);
4707 
4708     queue_pending_files (view, directory, files, &priv->new_changed_files);
4709 
4710     /* The free space or the number of items could have changed */
4711     schedule_update_status (view);
4712 
4713     /* A change in MIME type could affect the Open with menu, for
4714      * one thing, so we need to update menus when files change.
4715      */
4716     schedule_update_context_menus (view);
4717 }
4718 
4719 static void
done_loading_callback(NautilusDirectory * directory,gpointer callback_data)4720 done_loading_callback (NautilusDirectory *directory,
4721                        gpointer           callback_data)
4722 {
4723     NautilusFilesView *view;
4724     NautilusFilesViewPrivate *priv;
4725 
4726     view = NAUTILUS_FILES_VIEW (callback_data);
4727     priv = nautilus_files_view_get_instance_private (view);
4728 
4729     nautilus_profile_start (NULL);
4730     process_new_files (view);
4731     if (g_hash_table_size (priv->non_ready_files) == 0)
4732     {
4733         /* Unschedule a pending update and schedule a new one with the minimal
4734          * update interval. This gives the view a short chance at gathering the
4735          * (cached) deep counts.
4736          */
4737         unschedule_display_of_pending_files (view);
4738         schedule_timeout_display_of_pending_files (view, UPDATE_INTERVAL_MIN);
4739 
4740         remove_loading_floating_bar (view);
4741     }
4742     nautilus_profile_end (NULL);
4743 }
4744 
4745 static void
load_error_callback(NautilusDirectory * directory,GError * error,gpointer callback_data)4746 load_error_callback (NautilusDirectory *directory,
4747                      GError            *error,
4748                      gpointer           callback_data)
4749 {
4750     NautilusFilesView *view;
4751 
4752     view = NAUTILUS_FILES_VIEW (callback_data);
4753 
4754     /* FIXME: By doing a stop, we discard some pending files. Is
4755      * that OK?
4756      */
4757     nautilus_files_view_stop_loading (view);
4758 
4759     nautilus_report_error_loading_directory
4760         (nautilus_files_view_get_directory_as_file (view),
4761         error,
4762         nautilus_files_view_get_containing_window (view));
4763 }
4764 
4765 void
nautilus_files_view_add_subdirectory(NautilusFilesView * view,NautilusDirectory * directory)4766 nautilus_files_view_add_subdirectory (NautilusFilesView *view,
4767                                       NautilusDirectory *directory)
4768 {
4769     NautilusFileAttributes attributes;
4770     NautilusFilesViewPrivate *priv;
4771 
4772     priv = nautilus_files_view_get_instance_private (view);
4773 
4774     g_assert (!g_list_find (priv->subdirectory_list, directory));
4775 
4776     nautilus_directory_ref (directory);
4777 
4778     attributes =
4779         NAUTILUS_FILE_ATTRIBUTES_FOR_ICON |
4780         NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
4781         NAUTILUS_FILE_ATTRIBUTE_INFO |
4782         NAUTILUS_FILE_ATTRIBUTE_MOUNT |
4783         NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO;
4784 
4785     nautilus_directory_file_monitor_add (directory,
4786                                          &priv->model,
4787                                          priv->show_hidden_files,
4788                                          attributes,
4789                                          files_added_callback, view);
4790 
4791     g_signal_connect
4792         (directory, "files-added",
4793         G_CALLBACK (files_added_callback), view);
4794     g_signal_connect
4795         (directory, "files-changed",
4796         G_CALLBACK (files_changed_callback), view);
4797 
4798     priv->subdirectory_list = g_list_prepend (
4799         priv->subdirectory_list, directory);
4800 }
4801 
4802 void
nautilus_files_view_remove_subdirectory(NautilusFilesView * view,NautilusDirectory * directory)4803 nautilus_files_view_remove_subdirectory (NautilusFilesView *view,
4804                                          NautilusDirectory *directory)
4805 {
4806     NautilusFilesViewPrivate *priv;
4807     priv = nautilus_files_view_get_instance_private (view);
4808 
4809     g_assert (g_list_find (priv->subdirectory_list, directory));
4810 
4811     priv->subdirectory_list = g_list_remove (
4812         priv->subdirectory_list, directory);
4813 
4814     g_signal_handlers_disconnect_by_func (directory,
4815                                           G_CALLBACK (files_added_callback),
4816                                           view);
4817     g_signal_handlers_disconnect_by_func (directory,
4818                                           G_CALLBACK (files_changed_callback),
4819                                           view);
4820 
4821     nautilus_directory_file_monitor_remove (directory, &priv->model);
4822 
4823     nautilus_directory_unref (directory);
4824 }
4825 
4826 /**
4827  * nautilus_files_view_get_loading:
4828  * @view: an #NautilusFilesView.
4829  *
4830  * Return value: #gboolean inicating whether @view is currently loaded.
4831  *
4832  **/
4833 gboolean
nautilus_files_view_get_loading(NautilusFilesView * view)4834 nautilus_files_view_get_loading (NautilusFilesView *view)
4835 {
4836     NautilusFilesViewPrivate *priv;
4837 
4838     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), FALSE);
4839 
4840     priv = nautilus_files_view_get_instance_private (view);
4841 
4842     return priv->loading;
4843 }
4844 
4845 /**
4846  * nautilus_files_view_get_model:
4847  *
4848  * Get the model for this NautilusFilesView.
4849  * @view: NautilusFilesView of interest.
4850  *
4851  * Return value: NautilusDirectory for this view.
4852  *
4853  **/
4854 NautilusDirectory *
nautilus_files_view_get_model(NautilusFilesView * view)4855 nautilus_files_view_get_model (NautilusFilesView *view)
4856 {
4857     NautilusFilesViewPrivate *priv;
4858 
4859     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
4860 
4861     priv = nautilus_files_view_get_instance_private (view);
4862 
4863     return priv->model;
4864 }
4865 
4866 GtkWidget *
nautilus_files_view_get_content_widget(NautilusFilesView * view)4867 nautilus_files_view_get_content_widget (NautilusFilesView *view)
4868 {
4869     NautilusFilesViewPrivate *priv;
4870 
4871     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
4872 
4873     priv = nautilus_files_view_get_instance_private (view);
4874 
4875     return priv->scrolled_window;
4876 }
4877 
4878 /* home_dir_in_selection()
4879  *
4880  * Return TRUE if the home directory is in the selection.
4881  */
4882 
4883 static gboolean
home_dir_in_selection(GList * selection)4884 home_dir_in_selection (GList *selection)
4885 {
4886     for (GList *node = selection; node != NULL; node = node->next)
4887     {
4888         if (nautilus_file_is_home (NAUTILUS_FILE (node->data)))
4889         {
4890             return TRUE;
4891         }
4892     }
4893 
4894     return FALSE;
4895 }
4896 
4897 static void
trash_or_delete_done_cb(GHashTable * debuting_uris,gboolean user_cancel,NautilusFilesView * view)4898 trash_or_delete_done_cb (GHashTable        *debuting_uris,
4899                          gboolean           user_cancel,
4900                          NautilusFilesView *view)
4901 {
4902     NautilusFilesViewPrivate *priv;
4903 
4904     priv = nautilus_files_view_get_instance_private (view);
4905     if (user_cancel)
4906     {
4907         priv->selection_was_removed = FALSE;
4908     }
4909 }
4910 
4911 static void
trash_or_delete_files(GtkWindow * parent_window,const GList * files,NautilusFilesView * view)4912 trash_or_delete_files (GtkWindow         *parent_window,
4913                        const GList       *files,
4914                        NautilusFilesView *view)
4915 {
4916     GList *locations;
4917     const GList *node;
4918 
4919     locations = NULL;
4920     for (node = files; node != NULL; node = node->next)
4921     {
4922         locations = g_list_prepend (locations,
4923                                     nautilus_file_get_location ((NautilusFile *) node->data));
4924     }
4925 
4926     locations = g_list_reverse (locations);
4927 
4928     nautilus_file_operations_trash_or_delete_async (locations,
4929                                                     parent_window,
4930                                                     NULL,
4931                                                     (NautilusDeleteCallback) trash_or_delete_done_cb,
4932                                                     view);
4933     g_list_free_full (locations, g_object_unref);
4934 }
4935 
4936 static void
open_one_in_new_window(gpointer data,gpointer callback_data)4937 open_one_in_new_window (gpointer data,
4938                         gpointer callback_data)
4939 {
4940     g_assert (NAUTILUS_IS_FILE (data));
4941     g_assert (NAUTILUS_IS_FILES_VIEW (callback_data));
4942 
4943     nautilus_files_view_activate_file (NAUTILUS_FILES_VIEW (callback_data),
4944                                        NAUTILUS_FILE (data),
4945                                        NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW);
4946 }
4947 
4948 NautilusFile *
nautilus_files_view_get_directory_as_file(NautilusFilesView * view)4949 nautilus_files_view_get_directory_as_file (NautilusFilesView *view)
4950 {
4951     NautilusFilesViewPrivate *priv;
4952 
4953     g_assert (NAUTILUS_IS_FILES_VIEW (view));
4954 
4955     priv = nautilus_files_view_get_instance_private (view);
4956 
4957     return priv->directory_as_file;
4958 }
4959 
4960 static GdkPixbuf *
get_menu_icon_for_file(NautilusFile * file,GtkWidget * widget)4961 get_menu_icon_for_file (NautilusFile *file,
4962                         GtkWidget    *widget)
4963 {
4964     NautilusIconInfo *info;
4965     GdkPixbuf *pixbuf;
4966     int size, scale;
4967 
4968     size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
4969     scale = gtk_widget_get_scale_factor (widget);
4970 
4971     info = nautilus_file_get_icon (file, size, scale, 0);
4972     pixbuf = nautilus_icon_info_get_pixbuf_nodefault_at_size (info, size);
4973     g_object_unref (info);
4974 
4975     return pixbuf;
4976 }
4977 
4978 static GList *
get_extension_selection_menu_items(NautilusFilesView * view)4979 get_extension_selection_menu_items (NautilusFilesView *view)
4980 {
4981     NautilusWindow *window;
4982     GList *items;
4983     GList *providers;
4984     GList *l;
4985     g_autolist (NautilusFile) selection = NULL;
4986 
4987     window = nautilus_files_view_get_window (view);
4988     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
4989     providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_MENU_PROVIDER);
4990     items = NULL;
4991 
4992     for (l = providers; l != NULL; l = l->next)
4993     {
4994         NautilusMenuProvider *provider;
4995         GList *file_items;
4996 
4997         provider = NAUTILUS_MENU_PROVIDER (l->data);
4998         file_items = nautilus_menu_provider_get_file_items (provider,
4999                                                             GTK_WIDGET (window),
5000                                                             selection);
5001         items = g_list_concat (items, file_items);
5002     }
5003 
5004     nautilus_module_extension_list_free (providers);
5005 
5006     return items;
5007 }
5008 
5009 static GList *
get_extension_background_menu_items(NautilusFilesView * view)5010 get_extension_background_menu_items (NautilusFilesView *view)
5011 {
5012     NautilusFilesViewPrivate *priv;
5013     NautilusWindow *window;
5014     GList *items;
5015     GList *providers;
5016     GList *l;
5017 
5018     priv = nautilus_files_view_get_instance_private (view);
5019     window = nautilus_files_view_get_window (view);
5020     providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_MENU_PROVIDER);
5021     items = NULL;
5022 
5023     for (l = providers; l != NULL; l = l->next)
5024     {
5025         NautilusMenuProvider *provider;
5026         NautilusFileInfo *file_info;
5027         GList *file_items;
5028 
5029         provider = NAUTILUS_MENU_PROVIDER (l->data);
5030         file_info = NAUTILUS_FILE_INFO (priv->directory_as_file);
5031         file_items = nautilus_menu_provider_get_background_items (provider,
5032                                                                   GTK_WIDGET (window),
5033                                                                   file_info);
5034         items = g_list_concat (items, file_items);
5035     }
5036 
5037     nautilus_module_extension_list_free (providers);
5038 
5039     return items;
5040 }
5041 
5042 static void
extension_action_callback(GSimpleAction * action,GVariant * state,gpointer user_data)5043 extension_action_callback (GSimpleAction *action,
5044                            GVariant      *state,
5045                            gpointer       user_data)
5046 {
5047     NautilusMenuItem *item = user_data;
5048     nautilus_menu_item_activate (item);
5049 }
5050 
5051 static void
add_extension_action(NautilusFilesView * view,NautilusMenuItem * item,const char * action_name)5052 add_extension_action (NautilusFilesView *view,
5053                       NautilusMenuItem  *item,
5054                       const char        *action_name)
5055 {
5056     NautilusFilesViewPrivate *priv;
5057     gboolean sensitive;
5058     GSimpleAction *action;
5059 
5060     priv = nautilus_files_view_get_instance_private (view);
5061 
5062     g_object_get (item,
5063                   "sensitive", &sensitive,
5064                   NULL);
5065 
5066     action = g_simple_action_new (action_name, NULL);
5067     g_signal_connect_data (action, "activate",
5068                            G_CALLBACK (extension_action_callback),
5069                            g_object_ref (item),
5070                            (GClosureNotify) g_object_unref, 0);
5071 
5072     g_action_map_add_action (G_ACTION_MAP (priv->view_action_group),
5073                              G_ACTION (action));
5074     g_simple_action_set_enabled (action, sensitive);
5075 
5076     g_object_unref (action);
5077 }
5078 
5079 static GMenuModel *
build_menu_for_extension_menu_items(NautilusFilesView * view,const gchar * extension_prefix,GList * menu_items)5080 build_menu_for_extension_menu_items (NautilusFilesView *view,
5081                                      const gchar       *extension_prefix,
5082                                      GList             *menu_items)
5083 {
5084     GList *l;
5085     GMenu *gmenu;
5086     gint idx = 0;
5087 
5088     gmenu = g_menu_new ();
5089 
5090     for (l = menu_items; l; l = l->next)
5091     {
5092         NautilusMenuItem *item;
5093         NautilusMenu *menu;
5094         GMenuItem *menu_item;
5095         char *name, *label;
5096         g_autofree gchar *escaped_name = NULL;
5097         char *extension_id, *detailed_action_name;
5098 
5099         item = NAUTILUS_MENU_ITEM (l->data);
5100 
5101         g_object_get (item,
5102                       "label", &label,
5103                       "menu", &menu,
5104                       "name", &name,
5105                       NULL);
5106 
5107         escaped_name = g_uri_escape_string (name, NULL, TRUE);
5108         extension_id = g_strdup_printf ("extension_%s_%d_%s",
5109                                         extension_prefix, idx, escaped_name);
5110         add_extension_action (view, item, extension_id);
5111 
5112         detailed_action_name = g_strconcat ("view.", extension_id, NULL);
5113         menu_item = g_menu_item_new (label, detailed_action_name);
5114 
5115         if (menu != NULL)
5116         {
5117             GList *children;
5118             g_autoptr (GMenuModel) children_menu = NULL;
5119 
5120             children = nautilus_menu_get_items (menu);
5121             children_menu = build_menu_for_extension_menu_items (view, extension_id, children);
5122             g_menu_item_set_submenu (menu_item, children_menu);
5123 
5124             nautilus_menu_item_list_free (children);
5125         }
5126 
5127         g_menu_append_item (gmenu, menu_item);
5128         idx++;
5129 
5130         g_free (extension_id);
5131         g_free (detailed_action_name);
5132         g_free (name);
5133         g_free (label);
5134         g_object_unref (menu_item);
5135     }
5136 
5137     return G_MENU_MODEL (gmenu);
5138 }
5139 
5140 static void
update_extensions_menus(NautilusFilesView * view,GtkBuilder * builder)5141 update_extensions_menus (NautilusFilesView *view,
5142                          GtkBuilder        *builder)
5143 {
5144     GList *selection_items, *background_items;
5145     GObject *object;
5146     g_autoptr (GMenuModel) background_menu = NULL;
5147     g_autoptr (GMenuModel) selection_menu = NULL;
5148 
5149     selection_items = get_extension_selection_menu_items (view);
5150     if (selection_items != NULL)
5151     {
5152         selection_menu = build_menu_for_extension_menu_items (view, "extensions",
5153                                                               selection_items);
5154 
5155         object = gtk_builder_get_object (builder, "selection-extensions-section");
5156         nautilus_gmenu_set_from_model (G_MENU (object), selection_menu);
5157 
5158         nautilus_menu_item_list_free (selection_items);
5159     }
5160 
5161     background_items = get_extension_background_menu_items (view);
5162     if (background_items != NULL)
5163     {
5164         background_menu = build_menu_for_extension_menu_items (view, "extensions",
5165                                                                background_items);
5166 
5167         object = gtk_builder_get_object (builder, "background-extensions-section");
5168         nautilus_gmenu_set_from_model (G_MENU (object), background_menu);
5169 
5170         nautilus_menu_item_list_free (background_items);
5171     }
5172 
5173     nautilus_view_set_extensions_background_menu (NAUTILUS_VIEW (view), background_menu);
5174 }
5175 
5176 static char *
change_to_view_directory(NautilusFilesView * view)5177 change_to_view_directory (NautilusFilesView *view)
5178 {
5179     char *path;
5180     char *old_path;
5181 
5182     old_path = g_get_current_dir ();
5183 
5184     path = get_view_directory (view);
5185 
5186     /* FIXME: What to do about non-local directories? */
5187     if (path != NULL)
5188     {
5189         g_chdir (path);
5190     }
5191 
5192     g_free (path);
5193 
5194     return old_path;
5195 }
5196 
5197 static char **
get_file_names_as_parameter_array(GList * selection,NautilusDirectory * model)5198 get_file_names_as_parameter_array (GList             *selection,
5199                                    NautilusDirectory *model)
5200 {
5201     char **parameters;
5202     g_autoptr (GFile) model_location = NULL;
5203     int i;
5204 
5205     if (model == NULL)
5206     {
5207         return NULL;
5208     }
5209 
5210     parameters = g_new (char *, g_list_length (selection) + 1);
5211 
5212     model_location = nautilus_directory_get_location (model);
5213 
5214     i = 0;
5215     for (GList *node = selection; node != NULL; node = node->next, i++)
5216     {
5217         g_autoptr (GFile) file_location = NULL;
5218         NautilusFile *file = NAUTILUS_FILE (node->data);
5219 
5220         if (!nautilus_file_has_local_path (file))
5221         {
5222             parameters[i] = NULL;
5223             g_strfreev (parameters);
5224             return NULL;
5225         }
5226 
5227         file_location = nautilus_file_get_location (file);
5228         parameters[i] = g_file_get_relative_path (model_location, file_location);
5229         if (parameters[i] == NULL)
5230         {
5231             parameters[i] = g_file_get_path (file_location);
5232         }
5233     }
5234 
5235     parameters[i] = NULL;
5236     return parameters;
5237 }
5238 
5239 static char *
get_file_paths_or_uris_as_newline_delimited_string(GList * selection,gboolean get_paths)5240 get_file_paths_or_uris_as_newline_delimited_string (GList    *selection,
5241                                                     gboolean  get_paths)
5242 {
5243     GString *expanding_string;
5244 
5245     expanding_string = g_string_new ("");
5246     for (GList *node = selection; node != NULL; node = node->next)
5247     {
5248         NautilusFile *file = NAUTILUS_FILE (node->data);
5249         g_autofree gchar *uri = NULL;
5250 
5251         uri = nautilus_file_get_uri (file);
5252         if (uri == NULL)
5253         {
5254             continue;
5255         }
5256 
5257         if (get_paths)
5258         {
5259             g_autofree gchar *path = NULL;
5260 
5261             if (!nautilus_file_has_local_path (file))
5262             {
5263                 g_string_free (expanding_string, TRUE);
5264                 return g_strdup ("");
5265             }
5266 
5267             path = g_filename_from_uri (uri, NULL, NULL);
5268             if (path != NULL)
5269             {
5270                 g_string_append (expanding_string, path);
5271                 g_string_append (expanding_string, "\n");
5272             }
5273         }
5274         else
5275         {
5276             g_string_append (expanding_string, uri);
5277             g_string_append (expanding_string, "\n");
5278         }
5279     }
5280 
5281     return g_string_free (expanding_string, FALSE);
5282 }
5283 
5284 static char *
get_file_paths_as_newline_delimited_string(GList * selection)5285 get_file_paths_as_newline_delimited_string (GList *selection)
5286 {
5287     return get_file_paths_or_uris_as_newline_delimited_string (selection, TRUE);
5288 }
5289 
5290 static char *
get_file_uris_as_newline_delimited_string(GList * selection)5291 get_file_uris_as_newline_delimited_string (GList *selection)
5292 {
5293     return get_file_paths_or_uris_as_newline_delimited_string (selection, FALSE);
5294 }
5295 
5296 /*
5297  * Set up some environment variables that scripts can use
5298  * to take advantage of the current Nautilus state.
5299  */
5300 static void
set_script_environment_variables(NautilusFilesView * view,GList * selected_files)5301 set_script_environment_variables (NautilusFilesView *view,
5302                                   GList             *selected_files)
5303 {
5304     g_autofree gchar *file_paths = NULL;
5305     g_autofree gchar *uris = NULL;
5306     g_autofree gchar *uri = NULL;
5307     g_autofree gchar *geometry_string = NULL;
5308     NautilusFilesViewPrivate *priv;
5309 
5310     priv = nautilus_files_view_get_instance_private (view);
5311 
5312     file_paths = get_file_paths_as_newline_delimited_string (selected_files);
5313     g_setenv ("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS", file_paths, TRUE);
5314 
5315     uris = get_file_uris_as_newline_delimited_string (selected_files);
5316     g_setenv ("NAUTILUS_SCRIPT_SELECTED_URIS", uris, TRUE);
5317 
5318     uri = nautilus_directory_get_uri (priv->model);
5319     g_setenv ("NAUTILUS_SCRIPT_CURRENT_URI", uri, TRUE);
5320 
5321     geometry_string = eel_gtk_window_get_geometry_string
5322                           (GTK_WINDOW (nautilus_files_view_get_containing_window (view)));
5323     g_setenv ("NAUTILUS_SCRIPT_WINDOW_GEOMETRY", geometry_string, TRUE);
5324 }
5325 
5326 /* Unset all the special script environment variables. */
5327 static void
unset_script_environment_variables(void)5328 unset_script_environment_variables (void)
5329 {
5330     g_unsetenv ("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS");
5331     g_unsetenv ("NAUTILUS_SCRIPT_SELECTED_URIS");
5332     g_unsetenv ("NAUTILUS_SCRIPT_CURRENT_URI");
5333     g_unsetenv ("NAUTILUS_SCRIPT_WINDOW_GEOMETRY");
5334 }
5335 
5336 static void
run_script(GSimpleAction * action,GVariant * state,gpointer user_data)5337 run_script (GSimpleAction *action,
5338             GVariant      *state,
5339             gpointer       user_data)
5340 {
5341     ScriptLaunchParameters *launch_parameters;
5342     NautilusFilesViewPrivate *priv;
5343     g_autofree gchar *file_uri = NULL;
5344     g_autofree gchar *local_file_path = NULL;
5345     g_autofree gchar *quoted_path = NULL;
5346     g_autofree gchar *old_working_dir = NULL;
5347     g_autolist (NautilusFile) selection = NULL;
5348     g_auto (GStrv) parameters = NULL;
5349     GdkScreen *screen;
5350 
5351     launch_parameters = (ScriptLaunchParameters *) user_data;
5352     priv = nautilus_files_view_get_instance_private (launch_parameters->directory_view);
5353 
5354     file_uri = nautilus_file_get_uri (launch_parameters->file);
5355     local_file_path = g_filename_from_uri (file_uri, NULL, NULL);
5356     g_assert (local_file_path != NULL);
5357     quoted_path = g_shell_quote (local_file_path);
5358 
5359     old_working_dir = change_to_view_directory (launch_parameters->directory_view);
5360 
5361     selection = nautilus_view_get_selection (NAUTILUS_VIEW (launch_parameters->directory_view));
5362     set_script_environment_variables (launch_parameters->directory_view, selection);
5363 
5364     parameters = get_file_names_as_parameter_array (selection, priv->model);
5365 
5366     screen = gtk_widget_get_screen (GTK_WIDGET (launch_parameters->directory_view));
5367 
5368     DEBUG ("run_script, script_path=“%s” (omitting script parameters)",
5369            local_file_path);
5370 
5371     nautilus_launch_application_from_command_array (screen, quoted_path, FALSE,
5372                                                     (const char * const *) parameters);
5373 
5374     unset_script_environment_variables ();
5375     g_chdir (old_working_dir);
5376 }
5377 
5378 static void
add_script_to_scripts_menus(NautilusFilesView * view,NautilusFile * file,GMenu * menu)5379 add_script_to_scripts_menus (NautilusFilesView *view,
5380                              NautilusFile      *file,
5381                              GMenu             *menu)
5382 {
5383     NautilusFilesViewPrivate *priv;
5384     gchar *name;
5385     g_autofree gchar *uri = NULL;
5386     g_autofree gchar *escaped_uri = NULL;
5387     GdkPixbuf *mimetype_icon;
5388     gchar *action_name, *detailed_action_name;
5389     ScriptLaunchParameters *launch_parameters;
5390     GAction *action;
5391     GMenuItem *menu_item;
5392     const gchar *shortcut;
5393 
5394     priv = nautilus_files_view_get_instance_private (view);
5395     launch_parameters = script_launch_parameters_new (file, view);
5396 
5397     name = nautilus_file_get_display_name (file);
5398 
5399     uri = nautilus_file_get_uri (file);
5400     escaped_uri = g_uri_escape_string (uri, NULL, TRUE);
5401     action_name = g_strconcat ("script_", escaped_uri, NULL);
5402 
5403     action = G_ACTION (g_simple_action_new (action_name, NULL));
5404 
5405     g_signal_connect_data (action, "activate",
5406                            G_CALLBACK (run_script),
5407                            launch_parameters,
5408                            (GClosureNotify) script_launch_parameters_free, 0);
5409 
5410     g_action_map_add_action (G_ACTION_MAP (priv->view_action_group), action);
5411 
5412     g_object_unref (action);
5413 
5414     detailed_action_name = g_strconcat ("view.", action_name, NULL);
5415     menu_item = g_menu_item_new (name, detailed_action_name);
5416 
5417     mimetype_icon = get_menu_icon_for_file (file, GTK_WIDGET (view));
5418     if (mimetype_icon != NULL)
5419     {
5420         g_menu_item_set_icon (menu_item, G_ICON (mimetype_icon));
5421         g_object_unref (mimetype_icon);
5422     }
5423 
5424     g_menu_append_item (menu, menu_item);
5425 
5426     if ((shortcut = g_hash_table_lookup (script_accels, name)))
5427     {
5428         nautilus_application_set_accelerator (g_application_get_default (),
5429                                               detailed_action_name, shortcut);
5430     }
5431 
5432     g_free (name);
5433     g_free (action_name);
5434     g_free (detailed_action_name);
5435     g_object_unref (menu_item);
5436 }
5437 
5438 static gboolean
directory_belongs_in_scripts_menu(const char * uri)5439 directory_belongs_in_scripts_menu (const char *uri)
5440 {
5441     int num_levels;
5442     int i;
5443 
5444     if (!g_str_has_prefix (uri, scripts_directory_uri))
5445     {
5446         return FALSE;
5447     }
5448 
5449     num_levels = 0;
5450     for (i = scripts_directory_uri_length; uri[i] != '\0'; i++)
5451     {
5452         if (uri[i] == '/')
5453         {
5454             num_levels++;
5455         }
5456     }
5457 
5458     if (num_levels > MAX_MENU_LEVELS)
5459     {
5460         return FALSE;
5461     }
5462 
5463     return TRUE;
5464 }
5465 
5466 /* Expected format: accel script_name */
5467 static void
nautilus_load_custom_accel_for_scripts(void)5468 nautilus_load_custom_accel_for_scripts (void)
5469 {
5470     gchar *path, *contents;
5471     gchar **lines, **result;
5472     GError *error = NULL;
5473     const int max_len = 100;
5474     int i;
5475 
5476     path = g_build_filename (g_get_user_config_dir (), SHORTCUTS_PATH, NULL);
5477 
5478     if (g_file_get_contents (path, &contents, NULL, &error))
5479     {
5480         lines = g_strsplit (contents, "\n", -1);
5481         for (i = 0; lines[i] && (strstr (lines[i], " ") > 0); i++)
5482         {
5483             result = g_strsplit (lines[i], " ", 2);
5484             g_hash_table_insert (script_accels,
5485                                  g_strndup (result[1], max_len),
5486                                  g_strndup (result[0], max_len));
5487             g_strfreev (result);
5488         }
5489 
5490         g_free (contents);
5491         g_strfreev (lines);
5492     }
5493     else
5494     {
5495         DEBUG ("Unable to open '%s', error message: %s", path, error->message);
5496         g_clear_error (&error);
5497     }
5498 
5499     g_free (path);
5500 }
5501 
5502 static GMenu *
update_directory_in_scripts_menu(NautilusFilesView * view,NautilusDirectory * directory)5503 update_directory_in_scripts_menu (NautilusFilesView *view,
5504                                   NautilusDirectory *directory)
5505 {
5506     GList *file_list, *filtered, *node;
5507     GMenu *menu, *children_menu;
5508     GMenuItem *menu_item;
5509     gboolean any_scripts;
5510     NautilusFile *file;
5511     NautilusDirectory *dir;
5512     char *uri;
5513     gchar *file_name;
5514     int num;
5515 
5516     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
5517     g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
5518 
5519     if (script_accels == NULL)
5520     {
5521         script_accels = g_hash_table_new_full (g_str_hash, g_str_equal,
5522                                                g_free, g_free);
5523         nautilus_load_custom_accel_for_scripts ();
5524     }
5525 
5526     file_list = nautilus_directory_get_file_list (directory);
5527     filtered = nautilus_file_list_filter_hidden (file_list, FALSE);
5528     nautilus_file_list_free (file_list);
5529     menu = g_menu_new ();
5530 
5531     filtered = nautilus_file_list_sort_by_display_name (filtered);
5532 
5533     num = 0;
5534     any_scripts = FALSE;
5535     for (node = filtered; num < TEMPLATE_LIMIT && node != NULL; node = node->next, num++)
5536     {
5537         file = node->data;
5538         if (nautilus_file_is_directory (file))
5539         {
5540             uri = nautilus_file_get_uri (file);
5541             if (directory_belongs_in_scripts_menu (uri))
5542             {
5543                 dir = nautilus_directory_get_by_uri (uri);
5544                 add_directory_to_scripts_directory_list (view, dir);
5545 
5546                 children_menu = update_directory_in_scripts_menu (view, dir);
5547 
5548                 if (children_menu != NULL)
5549                 {
5550                     file_name = nautilus_file_get_display_name (file);
5551                     menu_item = g_menu_item_new_submenu (file_name,
5552                                                          G_MENU_MODEL (children_menu));
5553                     g_menu_append_item (menu, menu_item);
5554                     any_scripts = TRUE;
5555                     g_object_unref (menu_item);
5556                     g_object_unref (children_menu);
5557                     g_free (file_name);
5558                 }
5559 
5560                 nautilus_directory_unref (dir);
5561             }
5562             g_free (uri);
5563         }
5564         else if (nautilus_file_is_launchable (file))
5565         {
5566             add_script_to_scripts_menus (view, file, menu);
5567             any_scripts = TRUE;
5568         }
5569     }
5570 
5571     nautilus_file_list_free (filtered);
5572 
5573     if (!any_scripts)
5574     {
5575         g_object_unref (menu);
5576         menu = NULL;
5577     }
5578 
5579     return menu;
5580 }
5581 
5582 
5583 
5584 static void
update_scripts_menu(NautilusFilesView * view,GtkBuilder * builder)5585 update_scripts_menu (NautilusFilesView *view,
5586                      GtkBuilder        *builder)
5587 {
5588     NautilusFilesViewPrivate *priv;
5589     g_autolist (NautilusDirectory) sorted_copy = NULL;
5590     g_autoptr (NautilusDirectory) directory = NULL;
5591     g_autoptr (GMenu) submenu = NULL;
5592 
5593     priv = nautilus_files_view_get_instance_private (view);
5594 
5595     sorted_copy = nautilus_directory_list_sort_by_uri
5596                       (nautilus_directory_list_copy (priv->scripts_directory_list));
5597 
5598     for (GList *dir_l = sorted_copy; dir_l != NULL; dir_l = dir_l->next)
5599     {
5600         g_autofree char *uri = nautilus_directory_get_uri (dir_l->data);
5601         if (!directory_belongs_in_scripts_menu (uri))
5602         {
5603             remove_directory_from_scripts_directory_list (view, dir_l->data);
5604         }
5605     }
5606 
5607     directory = nautilus_directory_get_by_uri (scripts_directory_uri);
5608     submenu = update_directory_in_scripts_menu (view, directory);
5609     g_set_object (&priv->scripts_menu, G_MENU_MODEL (submenu));
5610 }
5611 
5612 static void
create_template(GSimpleAction * action,GVariant * state,gpointer user_data)5613 create_template (GSimpleAction *action,
5614                  GVariant      *state,
5615                  gpointer       user_data)
5616 {
5617     CreateTemplateParameters *parameters;
5618 
5619     parameters = user_data;
5620 
5621     nautilus_files_view_new_file (parameters->directory_view, NULL, parameters->file);
5622 }
5623 
5624 static void
add_template_to_templates_menus(NautilusFilesView * view,NautilusFile * file,GMenu * menu)5625 add_template_to_templates_menus (NautilusFilesView *view,
5626                                  NautilusFile      *file,
5627                                  GMenu             *menu)
5628 {
5629     NautilusFilesViewPrivate *priv;
5630     char *tmp, *uri, *name;
5631     g_autofree gchar *escaped_uri = NULL;
5632     GdkPixbuf *mimetype_icon;
5633     char *action_name, *detailed_action_name;
5634     CreateTemplateParameters *parameters;
5635     GAction *action;
5636     g_autofree char *label = NULL;
5637     GMenuItem *menu_item;
5638 
5639     priv = nautilus_files_view_get_instance_private (view);
5640     tmp = nautilus_file_get_display_name (file);
5641     name = eel_filename_strip_extension (tmp);
5642     g_free (tmp);
5643 
5644     uri = nautilus_file_get_uri (file);
5645     escaped_uri = g_uri_escape_string (uri, NULL, TRUE);
5646     action_name = g_strconcat ("template_", escaped_uri, NULL);
5647     action = G_ACTION (g_simple_action_new (action_name, NULL));
5648     parameters = create_template_parameters_new (file, view);
5649 
5650     g_signal_connect_data (action, "activate",
5651                            G_CALLBACK (create_template),
5652                            parameters,
5653                            (GClosureNotify) create_templates_parameters_free, 0);
5654 
5655     g_action_map_add_action (G_ACTION_MAP (priv->view_action_group), action);
5656 
5657     detailed_action_name = g_strconcat ("view.", action_name, NULL);
5658     label = eel_str_double_underscores (name);
5659     menu_item = g_menu_item_new (label, detailed_action_name);
5660 
5661     mimetype_icon = get_menu_icon_for_file (file, GTK_WIDGET (view));
5662     if (mimetype_icon != NULL)
5663     {
5664         g_menu_item_set_icon (menu_item, G_ICON (mimetype_icon));
5665         g_object_unref (mimetype_icon);
5666     }
5667 
5668     g_menu_append_item (menu, menu_item);
5669 
5670     g_free (name);
5671     g_free (uri);
5672     g_free (action_name);
5673     g_free (detailed_action_name);
5674     g_object_unref (action);
5675     g_object_unref (menu_item);
5676 }
5677 
5678 static void
update_templates_directory(NautilusFilesView * view)5679 update_templates_directory (NautilusFilesView *view)
5680 {
5681     NautilusFilesViewPrivate *priv;
5682     NautilusDirectory *templates_directory;
5683     GList *node, *next;
5684     char *templates_uri;
5685 
5686     priv = nautilus_files_view_get_instance_private (view);
5687 
5688     for (node = priv->templates_directory_list; node != NULL; node = next)
5689     {
5690         next = node->next;
5691         remove_directory_from_templates_directory_list (view, node->data);
5692     }
5693 
5694     if (nautilus_should_use_templates_directory ())
5695     {
5696         templates_uri = nautilus_get_templates_directory_uri ();
5697         templates_directory = nautilus_directory_get_by_uri (templates_uri);
5698         g_free (templates_uri);
5699         add_directory_to_templates_directory_list (view, templates_directory);
5700         nautilus_directory_unref (templates_directory);
5701     }
5702 }
5703 
5704 static gboolean
directory_belongs_in_templates_menu(const char * templates_directory_uri,const char * uri)5705 directory_belongs_in_templates_menu (const char *templates_directory_uri,
5706                                      const char *uri)
5707 {
5708     int num_levels;
5709     int i;
5710 
5711     if (templates_directory_uri == NULL)
5712     {
5713         return FALSE;
5714     }
5715 
5716     if (!g_str_has_prefix (uri, templates_directory_uri))
5717     {
5718         return FALSE;
5719     }
5720 
5721     num_levels = 0;
5722     for (i = strlen (templates_directory_uri); uri[i] != '\0'; i++)
5723     {
5724         if (uri[i] == '/')
5725         {
5726             num_levels++;
5727         }
5728     }
5729 
5730     if (num_levels > MAX_MENU_LEVELS)
5731     {
5732         return FALSE;
5733     }
5734 
5735     return TRUE;
5736 }
5737 
5738 static gboolean
filter_templates_callback(NautilusFile * file,gpointer callback_data)5739 filter_templates_callback (NautilusFile *file,
5740                            gpointer      callback_data)
5741 {
5742     gboolean show_hidden = GPOINTER_TO_INT (callback_data);
5743 
5744     if (nautilus_file_is_hidden_file (file))
5745     {
5746         if (!show_hidden)
5747         {
5748             return FALSE;
5749         }
5750 
5751         if (nautilus_file_is_directory (file))
5752         {
5753             return FALSE;
5754         }
5755     }
5756 
5757     return TRUE;
5758 }
5759 
5760 static GList *
filter_templates(GList * files,gboolean show_hidden)5761 filter_templates (GList    *files,
5762                   gboolean  show_hidden)
5763 {
5764     GList *filtered_files;
5765     GList *removed_files;
5766 
5767     filtered_files = nautilus_file_list_filter (files,
5768                                                 &removed_files,
5769                                                 filter_templates_callback,
5770                                                 GINT_TO_POINTER (show_hidden));
5771     nautilus_file_list_free (removed_files);
5772 
5773     return filtered_files;
5774 }
5775 
5776 static GMenuModel *
update_directory_in_templates_menu(NautilusFilesView * view,NautilusDirectory * directory)5777 update_directory_in_templates_menu (NautilusFilesView *view,
5778                                     NautilusDirectory *directory)
5779 {
5780     NautilusFilesViewPrivate *priv;
5781     GList *file_list, *filtered, *node;
5782     GMenu *menu;
5783     GMenuItem *menu_item;
5784     gboolean any_templates;
5785     NautilusFile *file;
5786     NautilusDirectory *dir;
5787     char *uri;
5788     char *templates_directory_uri;
5789     int num;
5790 
5791     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
5792     g_return_val_if_fail (NAUTILUS_IS_DIRECTORY (directory), NULL);
5793 
5794     priv = nautilus_files_view_get_instance_private (view);
5795 
5796     file_list = nautilus_directory_get_file_list (directory);
5797 
5798     /*
5799      * The nautilus_file_list_filter_hidden() function isn't used here, because
5800      * we want to show hidden files, but not directories. This is a compromise
5801      * to allow creating hidden files but to prevent content from .git directory
5802      * for example. See https://gitlab.gnome.org/GNOME/nautilus/issues/1413.
5803      */
5804     filtered = filter_templates (file_list, priv->show_hidden_files);
5805     nautilus_file_list_free (file_list);
5806     templates_directory_uri = nautilus_get_templates_directory_uri ();
5807     menu = g_menu_new ();
5808 
5809     filtered = nautilus_file_list_sort_by_display_name (filtered);
5810 
5811     num = 0;
5812     any_templates = FALSE;
5813     for (node = filtered; num < TEMPLATE_LIMIT && node != NULL; node = node->next, num++)
5814     {
5815         file = node->data;
5816         if (nautilus_file_is_directory (file))
5817         {
5818             uri = nautilus_file_get_uri (file);
5819             if (directory_belongs_in_templates_menu (templates_directory_uri, uri))
5820             {
5821                 g_autoptr (GMenuModel) children_menu = NULL;
5822 
5823                 dir = nautilus_directory_get_by_uri (uri);
5824                 add_directory_to_templates_directory_list (view, dir);
5825 
5826                 children_menu = update_directory_in_templates_menu (view, dir);
5827 
5828                 if (children_menu != NULL)
5829                 {
5830                     g_autofree char *display_name = NULL;
5831                     g_autofree char *label = NULL;
5832 
5833                     display_name = nautilus_file_get_display_name (file);
5834                     label = eel_str_double_underscores (display_name);
5835                     menu_item = g_menu_item_new_submenu (label, children_menu);
5836                     g_menu_append_item (menu, menu_item);
5837                     any_templates = TRUE;
5838                     g_object_unref (menu_item);
5839                 }
5840 
5841                 nautilus_directory_unref (dir);
5842             }
5843             g_free (uri);
5844         }
5845         else if (nautilus_file_can_read (file))
5846         {
5847             add_template_to_templates_menus (view, file, menu);
5848             any_templates = TRUE;
5849         }
5850     }
5851 
5852     nautilus_file_list_free (filtered);
5853     g_free (templates_directory_uri);
5854 
5855     if (!any_templates)
5856     {
5857         g_object_unref (menu);
5858         menu = NULL;
5859     }
5860 
5861     return G_MENU_MODEL (menu);
5862 }
5863 
5864 
5865 
5866 static void
update_templates_menu(NautilusFilesView * view,GtkBuilder * builder)5867 update_templates_menu (NautilusFilesView *view,
5868                        GtkBuilder        *builder)
5869 {
5870     NautilusFilesViewPrivate *priv;
5871     g_autolist (NautilusDirectory) sorted_copy = NULL;
5872     g_autoptr (NautilusDirectory) directory = NULL;
5873     g_autoptr (GMenuModel) submenu = NULL;
5874     g_autofree char *templates_directory_uri = NULL;
5875 
5876     priv = nautilus_files_view_get_instance_private (view);
5877 
5878     if (!nautilus_should_use_templates_directory ())
5879     {
5880         nautilus_view_set_templates_menu (NAUTILUS_VIEW (view), NULL);
5881         return;
5882     }
5883 
5884     templates_directory_uri = nautilus_get_templates_directory_uri ();
5885     sorted_copy = nautilus_directory_list_sort_by_uri
5886                       (nautilus_directory_list_copy (priv->templates_directory_list));
5887 
5888     for (GList *dir_l = sorted_copy; dir_l != NULL; dir_l = dir_l->next)
5889     {
5890         g_autofree char *uri = nautilus_directory_get_uri (dir_l->data);
5891         if (!directory_belongs_in_templates_menu (templates_directory_uri, uri))
5892         {
5893             remove_directory_from_templates_directory_list (view, dir_l->data);
5894         }
5895     }
5896 
5897     directory = nautilus_directory_get_by_uri (templates_directory_uri);
5898     submenu = update_directory_in_templates_menu (view, directory);
5899 
5900     nautilus_view_set_templates_menu (NAUTILUS_VIEW (view), submenu);
5901 }
5902 
5903 
5904 static void
action_open_scripts_folder(GSimpleAction * action,GVariant * state,gpointer user_data)5905 action_open_scripts_folder (GSimpleAction *action,
5906                             GVariant      *state,
5907                             gpointer       user_data)
5908 {
5909     static GFile *location = NULL;
5910 
5911     if (location == NULL)
5912     {
5913         location = g_file_new_for_uri (scripts_directory_uri);
5914     }
5915 
5916     nautilus_application_open_location_full (NAUTILUS_APPLICATION (g_application_get_default ()),
5917                                              location, 0, NULL, NULL, NULL);
5918 }
5919 
5920 typedef struct _CopyCallbackData
5921 {
5922     NautilusFilesView *view;
5923     GList *selection;
5924     gboolean is_move;
5925 } CopyCallbackData;
5926 
5927 static void
copy_data_free(CopyCallbackData * data)5928 copy_data_free (CopyCallbackData *data)
5929 {
5930     nautilus_file_list_free (data->selection);
5931     g_free (data);
5932 }
5933 
5934 static gboolean
uri_is_parent_of_selection(GList * selection,const char * uri)5935 uri_is_parent_of_selection (GList      *selection,
5936                             const char *uri)
5937 {
5938     gboolean found;
5939     GList *l;
5940     GFile *file;
5941 
5942     found = FALSE;
5943 
5944     file = g_file_new_for_uri (uri);
5945     for (l = selection; !found && l != NULL; l = l->next)
5946     {
5947         GFile *parent;
5948         parent = nautilus_file_get_parent_location (l->data);
5949         found = g_file_equal (file, parent);
5950         g_object_unref (parent);
5951     }
5952     g_object_unref (file);
5953     return found;
5954 }
5955 
5956 static void
on_destination_dialog_folder_changed(GtkFileChooser * chooser,gpointer user_data)5957 on_destination_dialog_folder_changed (GtkFileChooser *chooser,
5958                                       gpointer        user_data)
5959 {
5960     CopyCallbackData *copy_data = user_data;
5961     char *uri;
5962     gboolean found;
5963 
5964     uri = gtk_file_chooser_get_current_folder_uri (chooser);
5965     found = uri_is_parent_of_selection (copy_data->selection, uri);
5966     gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser), GTK_RESPONSE_OK, !found);
5967     g_free (uri);
5968 }
5969 
5970 static void
on_destination_dialog_response(GtkDialog * dialog,gint response_id,gpointer user_data)5971 on_destination_dialog_response (GtkDialog *dialog,
5972                                 gint       response_id,
5973                                 gpointer   user_data)
5974 {
5975     CopyCallbackData *copy_data = user_data;
5976 
5977     if (response_id == GTK_RESPONSE_OK)
5978     {
5979         char *target_uri;
5980         GList *uris, *l;
5981 
5982         target_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
5983 
5984         uris = NULL;
5985         for (l = copy_data->selection; l != NULL; l = l->next)
5986         {
5987             uris = g_list_prepend (uris,
5988                                    nautilus_file_get_uri ((NautilusFile *) l->data));
5989         }
5990         uris = g_list_reverse (uris);
5991 
5992         nautilus_files_view_move_copy_items (copy_data->view, uris, target_uri,
5993                                              copy_data->is_move ? GDK_ACTION_MOVE : GDK_ACTION_COPY);
5994 
5995         g_list_free_full (uris, g_free);
5996         g_free (target_uri);
5997     }
5998 
5999     copy_data_free (copy_data);
6000     gtk_widget_destroy (GTK_WIDGET (dialog));
6001 }
6002 
6003 static gboolean
destination_dialog_filter_cb(const GtkFileFilterInfo * filter_info,gpointer user_data)6004 destination_dialog_filter_cb (const GtkFileFilterInfo *filter_info,
6005                               gpointer                 user_data)
6006 {
6007     GList *selection = user_data;
6008     GList *l;
6009 
6010     for (l = selection; l != NULL; l = l->next)
6011     {
6012         char *uri;
6013         uri = nautilus_file_get_uri (l->data);
6014         if (strcmp (uri, filter_info->uri) == 0)
6015         {
6016             g_free (uri);
6017             return FALSE;
6018         }
6019         g_free (uri);
6020     }
6021 
6022     return TRUE;
6023 }
6024 
6025 static GList *
get_selected_folders(GList * selection)6026 get_selected_folders (GList *selection)
6027 {
6028     GList *folders;
6029     GList *l;
6030 
6031     folders = NULL;
6032     for (l = selection; l != NULL; l = l->next)
6033     {
6034         if (nautilus_file_is_directory (l->data))
6035         {
6036             folders = g_list_prepend (folders, nautilus_file_ref (l->data));
6037         }
6038     }
6039     return g_list_reverse (folders);
6040 }
6041 
6042 static void
copy_or_move_selection(NautilusFilesView * view,gboolean is_move)6043 copy_or_move_selection (NautilusFilesView *view,
6044                         gboolean           is_move)
6045 {
6046     NautilusFilesViewPrivate *priv;
6047     GtkWidget *dialog;
6048     char *uri;
6049     CopyCallbackData *copy_data;
6050     GList *selection;
6051     const gchar *title;
6052     NautilusDirectory *directory;
6053 
6054     priv = nautilus_files_view_get_instance_private (view);
6055 
6056     if (is_move)
6057     {
6058         title = _("Select Move Destination");
6059     }
6060     else
6061     {
6062         title = _("Select Copy Destination");
6063     }
6064 
6065     selection = nautilus_files_view_get_selection_for_file_transfer (view);
6066 
6067     dialog = gtk_file_chooser_dialog_new (title,
6068                                           GTK_WINDOW (nautilus_files_view_get_window (view)),
6069                                           GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
6070                                           _("_Cancel"), GTK_RESPONSE_CANCEL,
6071                                           _("_Select"), GTK_RESPONSE_OK,
6072                                           NULL);
6073     gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
6074 
6075     gtk_dialog_set_default_response (GTK_DIALOG (dialog),
6076                                      GTK_RESPONSE_OK);
6077 
6078     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
6079     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
6080 
6081     copy_data = g_new0 (CopyCallbackData, 1);
6082     copy_data->view = view;
6083     copy_data->selection = selection;
6084     copy_data->is_move = is_move;
6085 
6086     if (selection != NULL)
6087     {
6088         GtkFileFilter *filter;
6089         GList *folders;
6090 
6091         folders = get_selected_folders (selection);
6092 
6093         filter = gtk_file_filter_new ();
6094         gtk_file_filter_add_custom (filter,
6095                                     GTK_FILE_FILTER_URI,
6096                                     destination_dialog_filter_cb,
6097                                     folders,
6098                                     (GDestroyNotify) nautilus_file_list_free);
6099         gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
6100     }
6101 
6102 
6103     if (nautilus_view_is_searching (NAUTILUS_VIEW (view)))
6104     {
6105         directory = nautilus_search_directory_get_base_model (NAUTILUS_SEARCH_DIRECTORY (priv->model));
6106         uri = nautilus_directory_get_uri (directory);
6107     }
6108     else
6109     {
6110         uri = nautilus_directory_get_uri (priv->model);
6111     }
6112 
6113     gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog), uri);
6114     g_free (uri);
6115     g_signal_connect (dialog, "current-folder-changed",
6116                       G_CALLBACK (on_destination_dialog_folder_changed),
6117                       copy_data);
6118     g_signal_connect (dialog, "response",
6119                       G_CALLBACK (on_destination_dialog_response),
6120                       copy_data);
6121 
6122     gtk_widget_show_all (dialog);
6123 }
6124 
6125 static void
action_copy(GSimpleAction * action,GVariant * state,gpointer user_data)6126 action_copy (GSimpleAction *action,
6127              GVariant      *state,
6128              gpointer       user_data)
6129 {
6130     NautilusFilesView *view;
6131     GtkClipboard *clipboard;
6132     GList *selection;
6133 
6134     view = NAUTILUS_FILES_VIEW (user_data);
6135 
6136     selection = nautilus_files_view_get_selection_for_file_transfer (view);
6137     clipboard = nautilus_clipboard_get (GTK_WIDGET (view));
6138     nautilus_clipboard_prepare_for_files (clipboard, selection, FALSE);
6139 
6140     nautilus_file_list_free (selection);
6141 }
6142 
6143 static void
action_cut(GSimpleAction * action,GVariant * state,gpointer user_data)6144 action_cut (GSimpleAction *action,
6145             GVariant      *state,
6146             gpointer       user_data)
6147 {
6148     NautilusFilesView *view;
6149     GList *selection;
6150     GtkClipboard *clipboard;
6151 
6152     view = NAUTILUS_FILES_VIEW (user_data);
6153 
6154     selection = nautilus_files_view_get_selection_for_file_transfer (view);
6155     clipboard = nautilus_clipboard_get (GTK_WIDGET (view));
6156     nautilus_clipboard_prepare_for_files (clipboard, selection, TRUE);
6157 
6158     nautilus_file_list_free (selection);
6159 }
6160 
6161 static void
action_create_links_in_place(GSimpleAction * action,GVariant * state,gpointer user_data)6162 action_create_links_in_place (GSimpleAction *action,
6163                               GVariant      *state,
6164                               gpointer       user_data)
6165 {
6166     NautilusFilesView *view;
6167     GList *selection;
6168     GList *item_uris;
6169     GList *l;
6170     char *destination_uri;
6171 
6172     view = NAUTILUS_FILES_VIEW (user_data);
6173 
6174     selection = nautilus_files_view_get_selection_for_file_transfer (view);
6175 
6176     item_uris = NULL;
6177     for (l = selection; l != NULL; l = l->next)
6178     {
6179         item_uris = g_list_prepend (item_uris, nautilus_file_get_uri (l->data));
6180     }
6181     item_uris = g_list_reverse (item_uris);
6182 
6183     destination_uri = nautilus_files_view_get_backing_uri (view);
6184 
6185     nautilus_files_view_move_copy_items (view, item_uris, destination_uri,
6186                                          GDK_ACTION_LINK);
6187 
6188     g_list_free_full (item_uris, g_free);
6189     nautilus_file_list_free (selection);
6190 }
6191 
6192 static void
action_copy_to(GSimpleAction * action,GVariant * state,gpointer user_data)6193 action_copy_to (GSimpleAction *action,
6194                 GVariant      *state,
6195                 gpointer       user_data)
6196 {
6197     NautilusFilesView *view;
6198 
6199     view = NAUTILUS_FILES_VIEW (user_data);
6200     copy_or_move_selection (view, FALSE);
6201 }
6202 
6203 static void
action_move_to(GSimpleAction * action,GVariant * state,gpointer user_data)6204 action_move_to (GSimpleAction *action,
6205                 GVariant      *state,
6206                 gpointer       user_data)
6207 {
6208     NautilusFilesView *view;
6209 
6210     view = NAUTILUS_FILES_VIEW (user_data);
6211     copy_or_move_selection (view, TRUE);
6212 }
6213 
6214 typedef struct
6215 {
6216     NautilusFilesView *view;
6217     NautilusFile *target;
6218 } PasteIntoData;
6219 
6220 static void
paste_into_clipboard_received_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer callback_data)6221 paste_into_clipboard_received_callback (GtkClipboard     *clipboard,
6222                                         GtkSelectionData *selection_data,
6223                                         gpointer          callback_data)
6224 {
6225     NautilusFilesViewPrivate *priv;
6226     PasteIntoData *data;
6227     NautilusFilesView *view;
6228     char *directory_uri;
6229 
6230     data = (PasteIntoData *) callback_data;
6231 
6232     view = NAUTILUS_FILES_VIEW (data->view);
6233     priv = nautilus_files_view_get_instance_private (view);
6234 
6235     if (priv->slot != NULL)
6236     {
6237         directory_uri = nautilus_file_get_activation_uri (data->target);
6238 
6239         paste_clipboard_data (view, selection_data, directory_uri);
6240 
6241         g_free (directory_uri);
6242     }
6243 
6244     g_object_unref (view);
6245     nautilus_file_unref (data->target);
6246     g_free (data);
6247 }
6248 
6249 static void
paste_into(NautilusFilesView * view,NautilusFile * target)6250 paste_into (NautilusFilesView *view,
6251             NautilusFile      *target)
6252 {
6253     PasteIntoData *data;
6254 
6255     g_assert (NAUTILUS_IS_FILES_VIEW (view));
6256     g_assert (NAUTILUS_IS_FILE (target));
6257 
6258     data = g_new (PasteIntoData, 1);
6259 
6260     data->view = g_object_ref (view);
6261     data->target = nautilus_file_ref (target);
6262 
6263     gtk_clipboard_request_contents (nautilus_clipboard_get (GTK_WIDGET (view)),
6264                                     nautilus_clipboard_get_atom (),
6265                                     paste_into_clipboard_received_callback,
6266                                     data);
6267 }
6268 
6269 static void
action_paste_files_into(GSimpleAction * action,GVariant * state,gpointer user_data)6270 action_paste_files_into (GSimpleAction *action,
6271                          GVariant      *state,
6272                          gpointer       user_data)
6273 {
6274     NautilusFilesView *view;
6275     g_autolist (NautilusFile) selection = NULL;
6276 
6277     view = NAUTILUS_FILES_VIEW (user_data);
6278     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
6279     if (selection != NULL)
6280     {
6281         paste_into (view, NAUTILUS_FILE (selection->data));
6282     }
6283 }
6284 
6285 static void
real_action_rename(NautilusFilesView * view)6286 real_action_rename (NautilusFilesView *view)
6287 {
6288     NautilusFile *file;
6289     g_autolist (NautilusFile) selection = NULL;
6290     GtkWidget *dialog;
6291 
6292     g_assert (NAUTILUS_IS_FILES_VIEW (view));
6293 
6294     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
6295 
6296     if (selection != NULL)
6297     {
6298         /* If there is more than one file selected, invoke a batch renamer */
6299         if (selection->next != NULL)
6300         {
6301             GdkCursor *cursor;
6302             GdkDisplay *display;
6303 
6304             display = gtk_widget_get_display (GTK_WIDGET (nautilus_files_view_get_window (view)));
6305             cursor = gdk_cursor_new_from_name (display, "progress");
6306             gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (nautilus_files_view_get_window (view))),
6307                                    cursor);
6308             g_object_unref (cursor);
6309 
6310             dialog = nautilus_batch_rename_dialog_new (selection,
6311                                                        nautilus_files_view_get_model (view),
6312                                                        nautilus_files_view_get_window (view));
6313 
6314             gtk_widget_show (GTK_WIDGET (dialog));
6315         }
6316         else
6317         {
6318             file = NAUTILUS_FILE (selection->data);
6319 
6320             nautilus_files_view_rename_file_popover_new (view, file);
6321         }
6322     }
6323 }
6324 
6325 static void
action_rename(GSimpleAction * action,GVariant * state,gpointer user_data)6326 action_rename (GSimpleAction *action,
6327                GVariant      *state,
6328                gpointer       user_data)
6329 {
6330     real_action_rename (NAUTILUS_FILES_VIEW (user_data));
6331 }
6332 
6333 typedef struct
6334 {
6335     NautilusFilesView *view;
6336     GHashTable *added_locations;
6337 } ExtractData;
6338 
6339 static void
extract_done(GList * outputs,gpointer user_data)6340 extract_done (GList    *outputs,
6341               gpointer  user_data)
6342 {
6343     NautilusFilesViewPrivate *priv;
6344     ExtractData *data;
6345     GList *l;
6346     gboolean all_files_acknowledged;
6347 
6348     data = user_data;
6349 
6350     if (data->view == NULL)
6351     {
6352         goto out;
6353     }
6354 
6355     priv = nautilus_files_view_get_instance_private (data->view);
6356 
6357     g_signal_handlers_disconnect_by_func (data->view,
6358                                           G_CALLBACK (track_newly_added_locations),
6359                                           data->added_locations);
6360 
6361     if (outputs == NULL)
6362     {
6363         goto out;
6364     }
6365 
6366     all_files_acknowledged = TRUE;
6367     for (l = outputs; l && all_files_acknowledged; l = l->next)
6368     {
6369         all_files_acknowledged = g_hash_table_contains (data->added_locations,
6370                                                         l->data);
6371     }
6372 
6373     if (all_files_acknowledged)
6374     {
6375         GList *selection = NULL;
6376 
6377         for (l = outputs; l != NULL; l = l->next)
6378         {
6379             selection = g_list_prepend (selection,
6380                                         nautilus_file_get (l->data));
6381         }
6382 
6383         nautilus_files_view_set_selection (NAUTILUS_VIEW (data->view),
6384                                            selection);
6385         nautilus_files_view_reveal_selection (data->view);
6386 
6387         nautilus_file_list_free (selection);
6388     }
6389     else
6390     {
6391         for (l = outputs; l != NULL; l = l->next)
6392         {
6393             gboolean acknowledged;
6394 
6395             acknowledged = g_hash_table_contains (data->added_locations,
6396                                                   l->data);
6397 
6398             g_hash_table_insert (priv->pending_reveal,
6399                                  nautilus_file_get (l->data),
6400                                  GUINT_TO_POINTER (acknowledged));
6401         }
6402     }
6403 out:
6404     g_hash_table_destroy (data->added_locations);
6405 
6406     if (data->view != NULL)
6407     {
6408         g_object_remove_weak_pointer (G_OBJECT (data->view),
6409                                       (gpointer *) &data->view);
6410     }
6411 
6412     g_free (data);
6413 }
6414 
6415 static void
extract_files(NautilusFilesView * view,GList * files,GFile * destination_directory)6416 extract_files (NautilusFilesView *view,
6417                GList             *files,
6418                GFile             *destination_directory)
6419 {
6420     GList *locations = NULL;
6421     GList *l;
6422     gboolean extracting_to_current_directory;
6423 
6424     if (files == NULL)
6425     {
6426         return;
6427     }
6428 
6429     for (l = files; l != NULL; l = l->next)
6430     {
6431         locations = g_list_prepend (locations,
6432                                     nautilus_file_get_location (l->data));
6433     }
6434 
6435     locations = g_list_reverse (locations);
6436 
6437     extracting_to_current_directory = g_file_equal (destination_directory,
6438                                                     nautilus_view_get_location (NAUTILUS_VIEW (view)));
6439 
6440     if (extracting_to_current_directory)
6441     {
6442         ExtractData *data;
6443 
6444         data = g_new (ExtractData, 1);
6445         data->view = view;
6446         data->added_locations = g_hash_table_new_full (g_file_hash,
6447                                                        (GEqualFunc) g_file_equal,
6448                                                        g_object_unref, NULL);
6449 
6450 
6451         g_object_add_weak_pointer (G_OBJECT (data->view),
6452                                    (gpointer *) &data->view);
6453 
6454         g_signal_connect_data (view,
6455                                "add-files",
6456                                G_CALLBACK (track_newly_added_locations),
6457                                data->added_locations,
6458                                NULL,
6459                                G_CONNECT_AFTER);
6460 
6461         nautilus_file_operations_extract_files (locations,
6462                                                 destination_directory,
6463                                                 nautilus_files_view_get_containing_window (view),
6464                                                 NULL,
6465                                                 extract_done,
6466                                                 data);
6467     }
6468     else
6469     {
6470         nautilus_file_operations_extract_files (locations,
6471                                                 destination_directory,
6472                                                 nautilus_files_view_get_containing_window (view),
6473                                                 NULL,
6474                                                 NULL,
6475                                                 NULL);
6476     }
6477 
6478     g_list_free_full (locations, g_object_unref);
6479 }
6480 
6481 typedef struct
6482 {
6483     NautilusFilesView *view;
6484     GList *files;
6485 } ExtractToData;
6486 
6487 static void
on_extract_destination_dialog_response(GtkDialog * dialog,gint response_id,gpointer user_data)6488 on_extract_destination_dialog_response (GtkDialog *dialog,
6489                                         gint       response_id,
6490                                         gpointer   user_data)
6491 {
6492     ExtractToData *data;
6493 
6494     data = user_data;
6495 
6496     if (response_id == GTK_RESPONSE_OK)
6497     {
6498         g_autoptr (GFile) destination_directory = NULL;
6499 
6500         destination_directory = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
6501 
6502         extract_files (data->view, data->files, destination_directory);
6503     }
6504 
6505     gtk_widget_destroy (GTK_WIDGET (dialog));
6506     nautilus_file_list_free (data->files);
6507     g_free (data);
6508 }
6509 
6510 static void
extract_files_to_chosen_location(NautilusFilesView * view,GList * files)6511 extract_files_to_chosen_location (NautilusFilesView *view,
6512                                   GList             *files)
6513 {
6514     NautilusFilesViewPrivate *priv;
6515     ExtractToData *data;
6516     GtkWidget *dialog;
6517     g_autofree char *uri = NULL;
6518 
6519     priv = nautilus_files_view_get_instance_private (view);
6520 
6521     if (files == NULL)
6522     {
6523         return;
6524     }
6525 
6526     data = g_new (ExtractToData, 1);
6527 
6528     dialog = gtk_file_chooser_dialog_new (_("Select Extract Destination"),
6529                                           GTK_WINDOW (nautilus_files_view_get_window (view)),
6530                                           GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
6531                                           _("_Cancel"), GTK_RESPONSE_CANCEL,
6532                                           _("_Select"), GTK_RESPONSE_OK,
6533                                           NULL);
6534     gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
6535 
6536     gtk_dialog_set_default_response (GTK_DIALOG (dialog),
6537                                      GTK_RESPONSE_OK);
6538 
6539     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
6540     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
6541 
6542     /* The file chooser will not be able to display the search directory,
6543      * so we need to get the base directory of the search if we are, in fact,
6544      * in search.
6545      */
6546     if (nautilus_view_is_searching (NAUTILUS_VIEW (view)))
6547     {
6548         NautilusSearchDirectory *search_directory;
6549         NautilusDirectory *directory;
6550 
6551         search_directory = NAUTILUS_SEARCH_DIRECTORY (priv->model);
6552         directory = nautilus_search_directory_get_base_model (search_directory);
6553         uri = nautilus_directory_get_uri (directory);
6554     }
6555     else
6556     {
6557         uri = nautilus_directory_get_uri (priv->model);
6558     }
6559 
6560     gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (dialog), uri);
6561 
6562     data->view = view;
6563     data->files = nautilus_file_list_copy (files);
6564 
6565     g_signal_connect (dialog, "response",
6566                       G_CALLBACK (on_extract_destination_dialog_response),
6567                       data);
6568 
6569     gtk_widget_show_all (dialog);
6570 }
6571 
6572 static void
action_extract_here(GSimpleAction * action,GVariant * state,gpointer user_data)6573 action_extract_here (GSimpleAction *action,
6574                      GVariant      *state,
6575                      gpointer       user_data)
6576 {
6577     NautilusFilesView *view;
6578     g_autolist (NautilusFile) selection = NULL;
6579     g_autoptr (GFile) location = NULL;
6580     g_autoptr (GFile) parent = NULL;
6581 
6582     view = NAUTILUS_FILES_VIEW (user_data);
6583 
6584     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
6585     location = nautilus_file_get_location (NAUTILUS_FILE (g_list_first (selection)->data));
6586     /* Get a parent from a random file. We assume all files has a common parent.
6587      * But don't assume the parent is the view location, since that's not the
6588      * case in list view when expand-folder setting is set
6589      */
6590     parent = g_file_get_parent (location);
6591 
6592     extract_files (view, selection, parent);
6593 }
6594 
6595 static void
action_extract_to(GSimpleAction * action,GVariant * state,gpointer user_data)6596 action_extract_to (GSimpleAction *action,
6597                    GVariant      *state,
6598                    gpointer       user_data)
6599 {
6600     NautilusFilesView *view;
6601     g_autolist (NautilusFile) selection = NULL;
6602 
6603     view = NAUTILUS_FILES_VIEW (user_data);
6604 
6605     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
6606 
6607     extract_files_to_chosen_location (view, selection);
6608 }
6609 
6610 static void
action_compress(GSimpleAction * action,GVariant * state,gpointer user_data)6611 action_compress (GSimpleAction *action,
6612                  GVariant      *state,
6613                  gpointer       user_data)
6614 {
6615     NautilusFilesView *view = user_data;
6616 
6617     nautilus_files_view_compress_dialog_new (view);
6618 }
6619 
6620 static gboolean
can_run_in_terminal(GList * selection)6621 can_run_in_terminal (GList *selection)
6622 {
6623     NautilusFile *file;
6624 
6625     if (g_list_length (selection) != 1)
6626     {
6627         return FALSE;
6628     }
6629 
6630     file = NAUTILUS_FILE (selection->data);
6631 
6632     if (nautilus_file_is_launchable (file) &&
6633         nautilus_file_contains_text (file))
6634     {
6635         g_autofree gchar *activation_uri = NULL;
6636         g_autofree gchar *executable_path = NULL;
6637 
6638         activation_uri = nautilus_file_get_activation_uri (file);
6639         executable_path = g_filename_from_uri (activation_uri, NULL, NULL);
6640 
6641         if (executable_path != NULL)
6642         {
6643             return TRUE;
6644         }
6645     }
6646 
6647     return FALSE;
6648 }
6649 
6650 static void
action_run_in_terminal(GSimpleAction * action,GVariant * state,gpointer user_data)6651 action_run_in_terminal (GSimpleAction *action,
6652                         GVariant      *state,
6653                         gpointer       user_data)
6654 {
6655     NautilusFilesView *view;
6656     g_autolist (NautilusFile) selection = NULL;
6657     g_autofree char *old_working_dir = NULL;
6658     g_autofree char *uri = NULL;
6659     g_autofree char *executable_path = NULL;
6660     g_autofree char *quoted_path = NULL;
6661     GtkWindow *parent_window;
6662     GdkScreen *screen;
6663 
6664     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
6665 
6666     view = NAUTILUS_FILES_VIEW (user_data);
6667 
6668     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
6669 
6670     if (!can_run_in_terminal (selection))
6671     {
6672         return;
6673     }
6674 
6675     old_working_dir = change_to_view_directory (view);
6676 
6677     uri = nautilus_file_get_activation_uri (NAUTILUS_FILE (selection->data));
6678     executable_path = g_filename_from_uri (uri, NULL, NULL);
6679     quoted_path = g_shell_quote (executable_path);
6680 
6681     parent_window = nautilus_files_view_get_containing_window (view);
6682     screen = gtk_widget_get_screen (GTK_WIDGET (parent_window));
6683 
6684     DEBUG ("Launching in terminal %s", quoted_path);
6685 
6686     nautilus_launch_application_from_command (screen, quoted_path, TRUE, NULL);
6687 
6688     g_chdir (old_working_dir);
6689 }
6690 
6691 #define BG_KEY_PRIMARY_COLOR      "primary-color"
6692 #define BG_KEY_SECONDARY_COLOR    "secondary-color"
6693 #define BG_KEY_COLOR_TYPE         "color-shading-type"
6694 #define BG_KEY_PICTURE_PLACEMENT  "picture-options"
6695 #define BG_KEY_PICTURE_URI        "picture-uri"
6696 
6697 static void
set_uri_as_wallpaper(const char * uri)6698 set_uri_as_wallpaper (const char *uri)
6699 {
6700     GSettings *settings;
6701 
6702     settings = gnome_background_preferences;
6703 
6704     g_settings_delay (settings);
6705 
6706     if (uri == NULL)
6707     {
6708         uri = "";
6709     }
6710 
6711     g_settings_set_string (settings, BG_KEY_PICTURE_URI, uri);
6712     g_settings_set_string (settings, BG_KEY_PRIMARY_COLOR, "#000000");
6713     g_settings_set_string (settings, BG_KEY_SECONDARY_COLOR, "#000000");
6714     g_settings_set_enum (settings, BG_KEY_COLOR_TYPE, G_DESKTOP_BACKGROUND_SHADING_SOLID);
6715     g_settings_set_enum (settings, BG_KEY_PICTURE_PLACEMENT, G_DESKTOP_BACKGROUND_STYLE_ZOOM);
6716 
6717     /* Apply changes atomically. */
6718     g_settings_apply (settings);
6719 }
6720 
6721 static void
wallpaper_copy_done_callback(GHashTable * debuting_files,gboolean success,gpointer data)6722 wallpaper_copy_done_callback (GHashTable *debuting_files,
6723                               gboolean    success,
6724                               gpointer    data)
6725 {
6726     GHashTableIter iter;
6727     gpointer key, value;
6728 
6729     g_hash_table_iter_init (&iter, debuting_files);
6730     while (g_hash_table_iter_next (&iter, &key, &value))
6731     {
6732         char *uri;
6733         uri = g_file_get_uri (G_FILE (key));
6734         set_uri_as_wallpaper (uri);
6735         g_free (uri);
6736         break;
6737     }
6738 }
6739 
6740 static gboolean
can_set_wallpaper(GList * selection)6741 can_set_wallpaper (GList *selection)
6742 {
6743     NautilusFile *file;
6744 
6745     if (g_list_length (selection) != 1)
6746     {
6747         return FALSE;
6748     }
6749 
6750     file = NAUTILUS_FILE (selection->data);
6751     if (!nautilus_file_is_mime_type (file, "image/*"))
6752     {
6753         return FALSE;
6754     }
6755 
6756     /* FIXME: check file size? */
6757 
6758     return TRUE;
6759 }
6760 
6761 #ifdef HAVE_LIBPORTAL
6762 typedef struct
6763 {
6764     NautilusFile *file;
6765     NautilusFilesView *view;
6766 } WallpaperData;
6767 
6768 static void
set_wallpaper_with_portal_cb(GObject * source,GAsyncResult * result,gpointer user_data)6769 set_wallpaper_with_portal_cb (GObject      *source,
6770                               GAsyncResult *result,
6771                               gpointer      user_data)
6772 {
6773     XdpPortal *portal = XDP_PORTAL (source);
6774     g_autoptr (GError) error = NULL;
6775     WallpaperData *data = user_data;
6776 
6777     if (!xdp_portal_set_wallpaper_finish (portal, result, &error)
6778         && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
6779     {
6780         g_warning ("Failed to set wallpaper via portal: %s", error->message);
6781         set_wallpaper_fallback (data->file, data->view);
6782     }
6783 
6784     nautilus_file_unref (data->file);
6785     g_object_unref (data->view);
6786     g_free (data);
6787 }
6788 
6789 static void
set_wallpaper_with_portal(NautilusFile * file,gpointer user_data)6790 set_wallpaper_with_portal (NautilusFile *file,
6791                            gpointer      user_data)
6792 {
6793     g_autoptr (XdpPortal) portal = NULL;
6794     g_autofree gchar *uri = NULL;
6795     XdpParent *parent = NULL;
6796     GtkWidget *toplevel;
6797     WallpaperData *data;
6798 
6799     data = g_new0 (WallpaperData, 1);
6800     data->file = nautilus_file_ref (file);
6801     data->view = g_object_ref (user_data);
6802 
6803     portal = xdp_portal_new ();
6804     toplevel = gtk_widget_get_ancestor (GTK_WIDGET (user_data), GTK_TYPE_WINDOW);
6805     parent = xdp_parent_new_gtk (GTK_WINDOW (toplevel));
6806     uri = nautilus_file_get_uri (file);
6807 
6808     xdp_portal_set_wallpaper (portal,
6809                               parent,
6810                               uri,
6811                               XDP_WALLPAPER_FLAG_BACKGROUND | XDP_WALLPAPER_FLAG_PREVIEW,
6812                               NULL,
6813                               set_wallpaper_with_portal_cb,
6814                               data);
6815     xdp_parent_free (parent);
6816 }
6817 #endif /* HAVE_LIBPORTAL */
6818 
6819 static void
set_wallpaper_fallback(NautilusFile * file,gpointer user_data)6820 set_wallpaper_fallback (NautilusFile *file,
6821                         gpointer      user_data)
6822 {
6823     g_autoptr (GFile) target = NULL;
6824     g_autofree char *file_uri = NULL;
6825     g_autoptr (GFile) file_parent = NULL;
6826 
6827     /* Copy the item to Pictures/Wallpaper (internationalized),
6828      * if it's not already there, since it may be remote.
6829      * Then set it as the current wallpaper. */
6830     target = g_file_new_build_filename (g_get_user_special_dir (G_USER_DIRECTORY_PICTURES),
6831                                         _("Wallpapers"),
6832                                         NULL);
6833     g_file_make_directory_with_parents (target, NULL, NULL);
6834 
6835     file_parent = nautilus_file_get_parent_location (file);
6836     file_uri = nautilus_file_get_uri (file);
6837 
6838     if (!g_file_equal (file_parent, target))
6839     {
6840         g_autofree char *target_uri = g_file_get_uri (target);
6841         g_autoptr (GList) uris = g_list_prepend (NULL, file_uri);
6842 
6843         nautilus_file_operations_copy_move (uris,
6844                                             target_uri,
6845                                             GDK_ACTION_COPY,
6846                                             GTK_WIDGET (user_data),
6847                                             NULL,
6848                                             wallpaper_copy_done_callback,
6849                                             NULL);
6850     }
6851     else
6852     {
6853         set_uri_as_wallpaper (file_uri);
6854     }
6855 }
6856 
6857 static void
action_set_as_wallpaper(GSimpleAction * action,GVariant * state,gpointer user_data)6858 action_set_as_wallpaper (GSimpleAction *action,
6859                          GVariant      *state,
6860                          gpointer       user_data)
6861 {
6862     g_autolist (NautilusFile) selection = NULL;
6863 
6864     g_assert (NAUTILUS_IS_FILES_VIEW (user_data));
6865 
6866     selection = nautilus_view_get_selection (user_data);
6867     if (can_set_wallpaper (selection))
6868     {
6869         NautilusFile *file;
6870 
6871         file = NAUTILUS_FILE (selection->data);
6872 
6873 #ifdef HAVE_LIBPORTAL
6874         set_wallpaper_with_portal (file, user_data);
6875 #else
6876         set_wallpaper_fallback (file, user_data);
6877 #endif
6878     }
6879 }
6880 
6881 static void
file_mount_callback(NautilusFile * file,GFile * result_location,GError * error,gpointer callback_data)6882 file_mount_callback (NautilusFile *file,
6883                      GFile        *result_location,
6884                      GError       *error,
6885                      gpointer      callback_data)
6886 {
6887     NautilusFilesView *view;
6888 
6889     view = NAUTILUS_FILES_VIEW (callback_data);
6890 
6891     if (error != NULL &&
6892         (error->domain != G_IO_ERROR ||
6893          (error->code != G_IO_ERROR_CANCELLED &&
6894           error->code != G_IO_ERROR_FAILED_HANDLED &&
6895           error->code != G_IO_ERROR_ALREADY_MOUNTED)))
6896     {
6897         char *text;
6898         char *name;
6899         name = nautilus_file_get_display_name (file);
6900         /* Translators: %s is a file name formatted for display */
6901         text = g_strdup_printf (_("Unable to access “%s”"), name);
6902         show_dialog (text,
6903                      error->message,
6904                      GTK_WINDOW (nautilus_files_view_get_window (view)),
6905                      GTK_MESSAGE_ERROR);
6906         g_free (text);
6907         g_free (name);
6908     }
6909 }
6910 
6911 static void
file_unmount_callback(NautilusFile * file,GFile * result_location,GError * error,gpointer callback_data)6912 file_unmount_callback (NautilusFile *file,
6913                        GFile        *result_location,
6914                        GError       *error,
6915                        gpointer      callback_data)
6916 {
6917     NautilusFilesView *view;
6918 
6919     view = NAUTILUS_FILES_VIEW (callback_data);
6920     g_object_unref (view);
6921 
6922     if (error != NULL &&
6923         (error->domain != G_IO_ERROR ||
6924          (error->code != G_IO_ERROR_CANCELLED &&
6925           error->code != G_IO_ERROR_FAILED_HANDLED)))
6926     {
6927         char *text;
6928         char *name;
6929         name = nautilus_file_get_display_name (file);
6930         /* Translators: %s is a file name formatted for display */
6931         text = g_strdup_printf (_("Unable to remove “%s”"), name);
6932         show_dialog (text,
6933                      error->message,
6934                      GTK_WINDOW (nautilus_files_view_get_window (view)),
6935                      GTK_MESSAGE_ERROR);
6936         g_free (text);
6937         g_free (name);
6938     }
6939 }
6940 
6941 static void
file_eject_callback(NautilusFile * file,GFile * result_location,GError * error,gpointer callback_data)6942 file_eject_callback (NautilusFile *file,
6943                      GFile        *result_location,
6944                      GError       *error,
6945                      gpointer      callback_data)
6946 {
6947     NautilusFilesView *view;
6948 
6949     view = NAUTILUS_FILES_VIEW (callback_data);
6950     g_object_unref (view);
6951 
6952     if (error != NULL &&
6953         (error->domain != G_IO_ERROR ||
6954          (error->code != G_IO_ERROR_CANCELLED &&
6955           error->code != G_IO_ERROR_FAILED_HANDLED)))
6956     {
6957         char *text;
6958         char *name;
6959         name = nautilus_file_get_display_name (file);
6960         /* Translators: %s is a file name formatted for display */
6961         text = g_strdup_printf (_("Unable to eject “%s”"), name);
6962         show_dialog (text,
6963                      error->message,
6964                      GTK_WINDOW (nautilus_files_view_get_window (view)),
6965                      GTK_MESSAGE_ERROR);
6966         g_free (text);
6967         g_free (name);
6968     }
6969 }
6970 
6971 static void
file_stop_callback(NautilusFile * file,GFile * result_location,GError * error,gpointer callback_data)6972 file_stop_callback (NautilusFile *file,
6973                     GFile        *result_location,
6974                     GError       *error,
6975                     gpointer      callback_data)
6976 {
6977     NautilusFilesView *view;
6978 
6979     view = NAUTILUS_FILES_VIEW (callback_data);
6980 
6981     if (error != NULL &&
6982         (error->domain != G_IO_ERROR ||
6983          (error->code != G_IO_ERROR_CANCELLED &&
6984           error->code != G_IO_ERROR_FAILED_HANDLED)))
6985     {
6986         show_dialog (_("Unable to stop drive"),
6987                      error->message,
6988                      GTK_WINDOW (nautilus_files_view_get_window (view)),
6989                      GTK_MESSAGE_ERROR);
6990     }
6991 }
6992 
6993 static void
action_mount_volume(GSimpleAction * action,GVariant * state,gpointer user_data)6994 action_mount_volume (GSimpleAction *action,
6995                      GVariant      *state,
6996                      gpointer       user_data)
6997 {
6998     NautilusFile *file;
6999     GList *selection, *l;
7000     NautilusFilesView *view;
7001     GMountOperation *mount_op;
7002 
7003     view = NAUTILUS_FILES_VIEW (user_data);
7004 
7005     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
7006     for (l = selection; l != NULL; l = l->next)
7007     {
7008         file = NAUTILUS_FILE (l->data);
7009 
7010         if (nautilus_file_can_mount (file))
7011         {
7012             mount_op = gtk_mount_operation_new (nautilus_files_view_get_containing_window (view));
7013             g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
7014             nautilus_file_mount (file, mount_op, NULL,
7015                                  file_mount_callback,
7016                                  view);
7017             g_object_unref (mount_op);
7018         }
7019     }
7020     nautilus_file_list_free (selection);
7021 }
7022 
7023 static void
action_unmount_volume(GSimpleAction * action,GVariant * state,gpointer user_data)7024 action_unmount_volume (GSimpleAction *action,
7025                        GVariant      *state,
7026                        gpointer       user_data)
7027 {
7028     NautilusFile *file;
7029     g_autolist (NautilusFile) selection = NULL;
7030     GList *l;
7031     NautilusFilesView *view;
7032 
7033     view = NAUTILUS_FILES_VIEW (user_data);
7034 
7035     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
7036 
7037     for (l = selection; l != NULL; l = l->next)
7038     {
7039         file = NAUTILUS_FILE (l->data);
7040         if (nautilus_file_can_unmount (file))
7041         {
7042             GMountOperation *mount_op;
7043             mount_op = gtk_mount_operation_new (nautilus_files_view_get_containing_window (view));
7044             nautilus_file_unmount (file, mount_op, NULL,
7045                                    file_unmount_callback, g_object_ref (view));
7046             g_object_unref (mount_op);
7047         }
7048     }
7049 }
7050 
7051 static void
action_eject_volume(GSimpleAction * action,GVariant * state,gpointer user_data)7052 action_eject_volume (GSimpleAction *action,
7053                      GVariant      *state,
7054                      gpointer       user_data)
7055 {
7056     NautilusFile *file;
7057     g_autolist (NautilusFile) selection = NULL;
7058     GList *l;
7059     NautilusFilesView *view;
7060 
7061     view = NAUTILUS_FILES_VIEW (user_data);
7062 
7063     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
7064     for (l = selection; l != NULL; l = l->next)
7065     {
7066         file = NAUTILUS_FILE (l->data);
7067 
7068         if (nautilus_file_can_eject (file))
7069         {
7070             GMountOperation *mount_op;
7071             mount_op = gtk_mount_operation_new (nautilus_files_view_get_containing_window (view));
7072             nautilus_file_eject (file, mount_op, NULL,
7073                                  file_eject_callback, g_object_ref (view));
7074             g_object_unref (mount_op);
7075         }
7076     }
7077 }
7078 
7079 static void
file_start_callback(NautilusFile * file,GFile * result_location,GError * error,gpointer callback_data)7080 file_start_callback (NautilusFile *file,
7081                      GFile        *result_location,
7082                      GError       *error,
7083                      gpointer      callback_data)
7084 {
7085     NautilusFilesView *view;
7086 
7087     view = NAUTILUS_FILES_VIEW (callback_data);
7088 
7089     if (error != NULL &&
7090         (error->domain != G_IO_ERROR ||
7091          (error->code != G_IO_ERROR_CANCELLED &&
7092           error->code != G_IO_ERROR_FAILED_HANDLED &&
7093           error->code != G_IO_ERROR_ALREADY_MOUNTED)))
7094     {
7095         char *text;
7096         char *name;
7097         name = nautilus_file_get_display_name (file);
7098         /* Translators: %s is a file name formatted for display */
7099         text = g_strdup_printf (_("Unable to start “%s”"), name);
7100         show_dialog (text,
7101                      error->message,
7102                      GTK_WINDOW (nautilus_files_view_get_window (view)),
7103                      GTK_MESSAGE_ERROR);
7104         g_free (text);
7105         g_free (name);
7106     }
7107 }
7108 
7109 static void
action_start_volume(GSimpleAction * action,GVariant * state,gpointer user_data)7110 action_start_volume (GSimpleAction *action,
7111                      GVariant      *state,
7112                      gpointer       user_data)
7113 {
7114     NautilusFile *file;
7115     g_autolist (NautilusFile) selection = NULL;
7116     GList *l;
7117     NautilusFilesView *view;
7118     GMountOperation *mount_op;
7119 
7120     view = NAUTILUS_FILES_VIEW (user_data);
7121 
7122     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
7123     for (l = selection; l != NULL; l = l->next)
7124     {
7125         file = NAUTILUS_FILE (l->data);
7126 
7127         if (nautilus_file_can_start (file) || nautilus_file_can_start_degraded (file))
7128         {
7129             mount_op = gtk_mount_operation_new (nautilus_files_view_get_containing_window (view));
7130             nautilus_file_start (file, mount_op, NULL,
7131                                  file_start_callback, view);
7132             g_object_unref (mount_op);
7133         }
7134     }
7135 }
7136 
7137 static void
action_stop_volume(GSimpleAction * action,GVariant * state,gpointer user_data)7138 action_stop_volume (GSimpleAction *action,
7139                     GVariant      *state,
7140                     gpointer       user_data)
7141 {
7142     NautilusFile *file;
7143     g_autolist (NautilusFile) selection = NULL;
7144     GList *l;
7145     NautilusFilesView *view;
7146 
7147     view = NAUTILUS_FILES_VIEW (user_data);
7148 
7149     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
7150     for (l = selection; l != NULL; l = l->next)
7151     {
7152         file = NAUTILUS_FILE (l->data);
7153 
7154         if (nautilus_file_can_stop (file))
7155         {
7156             GMountOperation *mount_op;
7157             mount_op = gtk_mount_operation_new (nautilus_files_view_get_containing_window (view));
7158             nautilus_file_stop (file, mount_op, NULL,
7159                                 file_stop_callback, view);
7160             g_object_unref (mount_op);
7161         }
7162     }
7163 }
7164 
7165 static void
action_detect_media(GSimpleAction * action,GVariant * state,gpointer user_data)7166 action_detect_media (GSimpleAction *action,
7167                      GVariant      *state,
7168                      gpointer       user_data)
7169 {
7170     NautilusFile *file;
7171     g_autolist (NautilusFile) selection = NULL;
7172     GList *l;
7173     NautilusView *view;
7174 
7175     view = NAUTILUS_VIEW (user_data);
7176 
7177     selection = nautilus_view_get_selection (view);
7178     for (l = selection; l != NULL; l = l->next)
7179     {
7180         file = NAUTILUS_FILE (l->data);
7181 
7182         if (nautilus_file_can_poll_for_media (file) && !nautilus_file_is_media_check_automatic (file))
7183         {
7184             nautilus_file_poll_for_media (file);
7185         }
7186     }
7187 }
7188 
7189 const GActionEntry view_entries[] =
7190 {
7191     /* Toolbar menu */
7192     { "zoom-in", action_zoom_in },
7193     { "zoom-out", action_zoom_out },
7194     { "zoom-standard", action_zoom_standard },
7195     { "show-hidden-files", NULL, NULL, "true", action_show_hidden_files },
7196     /* Background menu */
7197     { "new-folder", action_new_folder },
7198     { "select-all", action_select_all },
7199     { "paste", action_paste_files },
7200     { "paste_accel", action_paste_files_accel },
7201     { "create-link", action_create_links },
7202     { "new-document" },
7203     /* Selection menu */
7204     { "scripts" },
7205     { "new-folder-with-selection", action_new_folder_with_selection },
7206     { "open-scripts-folder", action_open_scripts_folder },
7207     { "open-item-location", action_open_item_location },
7208     { "open-with-default-application", action_open_with_default_application },
7209     { "open-with-other-application", action_open_with_other_application },
7210     { "open-item-new-window", action_open_item_new_window },
7211     { "open-item-new-tab", action_open_item_new_tab },
7212     { "cut", action_cut},
7213     { "copy", action_copy},
7214     { "create-link-in-place", action_create_links_in_place },
7215     { "move-to", action_move_to},
7216     { "copy-to", action_copy_to},
7217     { "move-to-trash", action_move_to_trash},
7218     { "delete-from-trash", action_delete },
7219     { "star", action_star},
7220     { "unstar", action_unstar},
7221     /* We separate the shortcut and the menu item since we want the shortcut
7222      * to always be available, but we don't want the menu item shown if not
7223      * completely necesary. Since the visibility of the menu item is based on
7224      * the action enability, we need to split the actions for the menu and the
7225      * shortcut. */
7226     { "delete-permanently-shortcut", action_delete },
7227     { "delete-permanently-menu-item", action_delete },
7228     /* This is only shown when the setting to show always delete permanently
7229      * is set and when the common use cases for delete permanently which uses
7230      * Delete as a shortcut are not needed. For instance this will be only
7231      * present when the setting is true and when it can trash files */
7232     { "permanent-delete-permanently-menu-item", action_delete },
7233     { "remove-from-recent", action_remove_from_recent },
7234     { "restore-from-trash", action_restore_from_trash},
7235     { "paste-into", action_paste_files_into },
7236     { "rename", action_rename},
7237     { "extract-here", action_extract_here },
7238     { "extract-to", action_extract_to },
7239     { "compress", action_compress },
7240     { "properties", action_properties},
7241     { "current-directory-properties", action_current_dir_properties},
7242     { "run-in-terminal", action_run_in_terminal },
7243     { "set-as-wallpaper", action_set_as_wallpaper },
7244     { "mount-volume", action_mount_volume },
7245     { "unmount-volume", action_unmount_volume },
7246     { "eject-volume", action_eject_volume },
7247     { "start-volume", action_start_volume },
7248     { "stop-volume", action_stop_volume },
7249     { "detect-media", action_detect_media },
7250     /* Only accesible by shorcuts */
7251     { "select-pattern", action_select_pattern },
7252     { "invert-selection", action_invert_selection },
7253 };
7254 
7255 static gboolean
can_paste_into_file(NautilusFile * file)7256 can_paste_into_file (NautilusFile *file)
7257 {
7258     if (nautilus_file_is_directory (file) &&
7259         nautilus_file_can_write (file))
7260     {
7261         return TRUE;
7262     }
7263     if (nautilus_file_has_activation_uri (file))
7264     {
7265         GFile *location;
7266         NautilusFile *activation_file;
7267         gboolean res;
7268 
7269         location = nautilus_file_get_activation_location (file);
7270         activation_file = nautilus_file_get (location);
7271         g_object_unref (location);
7272 
7273         /* The target location might not have data for it read yet,
7274          *  and we can't want to do sync I/O, so treat the unknown
7275          *  case as can-write */
7276         res = (nautilus_file_get_file_type (activation_file) == G_FILE_TYPE_UNKNOWN) ||
7277               (nautilus_file_get_file_type (activation_file) == G_FILE_TYPE_DIRECTORY &&
7278                nautilus_file_can_write (activation_file));
7279 
7280         nautilus_file_unref (activation_file);
7281 
7282         return res;
7283     }
7284 
7285     return FALSE;
7286 }
7287 
7288 static void
on_clipboard_contents_received(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer user_data)7289 on_clipboard_contents_received (GtkClipboard     *clipboard,
7290                                 GtkSelectionData *selection_data,
7291                                 gpointer          user_data)
7292 {
7293     NautilusFilesViewPrivate *priv;
7294     NautilusFilesView *view;
7295     gboolean can_link_from_copied_files;
7296     gboolean settings_show_create_link;
7297     gboolean is_read_only;
7298     gboolean selection_contains_recent;
7299     gboolean selection_contains_starred;
7300     GAction *action;
7301 
7302     view = NAUTILUS_FILES_VIEW (user_data);
7303     priv = nautilus_files_view_get_instance_private (view);
7304 
7305     if (priv->slot == NULL ||
7306         !priv->active)
7307     {
7308         /* We've been destroyed or became inactive since call */
7309         g_object_unref (view);
7310         return;
7311     }
7312 
7313     settings_show_create_link = g_settings_get_boolean (nautilus_preferences,
7314                                                         NAUTILUS_PREFERENCES_SHOW_CREATE_LINK);
7315     is_read_only = nautilus_files_view_is_read_only (view);
7316     selection_contains_recent = showing_recent_directory (view);
7317     selection_contains_starred = showing_starred_directory (view);
7318     can_link_from_copied_files = !nautilus_clipboard_is_cut_from_selection_data (selection_data) &&
7319                                  !selection_contains_recent && !selection_contains_starred &&
7320                                  !is_read_only && gtk_selection_data_get_length (selection_data) > 0;
7321 
7322     action = g_action_map_lookup_action (G_ACTION_MAP (priv->view_action_group),
7323                                          "create-link");
7324     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7325                                  can_link_from_copied_files &&
7326                                  settings_show_create_link);
7327 
7328     g_object_unref (view);
7329 }
7330 
7331 static void
on_clipboard_targets_received(GtkClipboard * clipboard,GdkAtom * targets,int n_targets,gpointer user_data)7332 on_clipboard_targets_received (GtkClipboard *clipboard,
7333                                GdkAtom      *targets,
7334                                int           n_targets,
7335                                gpointer      user_data)
7336 {
7337     NautilusFilesViewPrivate *priv;
7338     NautilusFilesView *view;
7339     gboolean is_data_copied;
7340     int i;
7341     GAction *action;
7342 
7343     view = NAUTILUS_FILES_VIEW (user_data);
7344     priv = nautilus_files_view_get_instance_private (view);
7345     is_data_copied = FALSE;
7346 
7347     if (priv->slot == NULL ||
7348         !priv->active)
7349     {
7350         /* We've been destroyed or became inactive since call */
7351         g_object_unref (view);
7352         return;
7353     }
7354 
7355     if (targets)
7356     {
7357         for (i = 0; i < n_targets; i++)
7358         {
7359             if (targets[i] == nautilus_clipboard_get_atom ())
7360             {
7361                 is_data_copied = TRUE;
7362             }
7363         }
7364     }
7365 
7366     action = g_action_map_lookup_action (G_ACTION_MAP (priv->view_action_group),
7367                                          "paste");
7368     /* Take into account if the action was previously disabled for other reasons,
7369      * like the directory not being writabble */
7370     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7371                                  is_data_copied && g_action_get_enabled (action));
7372 
7373     action = g_action_map_lookup_action (G_ACTION_MAP (priv->view_action_group),
7374                                          "paste-into");
7375 
7376     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7377                                  is_data_copied && g_action_get_enabled (action));
7378 
7379     action = g_action_map_lookup_action (G_ACTION_MAP (priv->view_action_group),
7380                                          "create-link");
7381 
7382     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7383                                  is_data_copied && g_action_get_enabled (action));
7384 
7385     g_object_unref (view);
7386 }
7387 
7388 static void
file_should_show_foreach(NautilusFile * file,gboolean * show_mount,gboolean * show_unmount,gboolean * show_eject,gboolean * show_start,gboolean * show_stop,gboolean * show_poll,GDriveStartStopType * start_stop_type)7389 file_should_show_foreach (NautilusFile        *file,
7390                           gboolean            *show_mount,
7391                           gboolean            *show_unmount,
7392                           gboolean            *show_eject,
7393                           gboolean            *show_start,
7394                           gboolean            *show_stop,
7395                           gboolean            *show_poll,
7396                           GDriveStartStopType *start_stop_type)
7397 {
7398     *show_mount = FALSE;
7399     *show_unmount = FALSE;
7400     *show_eject = FALSE;
7401     *show_start = FALSE;
7402     *show_stop = FALSE;
7403     *show_poll = FALSE;
7404 
7405     if (nautilus_file_can_eject (file))
7406     {
7407         *show_eject = TRUE;
7408     }
7409 
7410     if (nautilus_file_can_mount (file))
7411     {
7412         *show_mount = TRUE;
7413     }
7414 
7415     if (nautilus_file_can_start (file) || nautilus_file_can_start_degraded (file))
7416     {
7417         *show_start = TRUE;
7418     }
7419 
7420     if (nautilus_file_can_stop (file))
7421     {
7422         *show_stop = TRUE;
7423     }
7424 
7425     /* Dot not show both Unmount and Eject/Safe Removal; too confusing to
7426      * have too many menu entries */
7427     if (nautilus_file_can_unmount (file) && !*show_eject && !*show_stop)
7428     {
7429         *show_unmount = TRUE;
7430     }
7431 
7432     if (nautilus_file_can_poll_for_media (file) && !nautilus_file_is_media_check_automatic (file))
7433     {
7434         *show_poll = TRUE;
7435     }
7436 
7437     *start_stop_type = nautilus_file_get_start_stop_type (file);
7438 }
7439 
7440 static gboolean
can_restore_from_trash(GList * files)7441 can_restore_from_trash (GList *files)
7442 {
7443     NautilusFile *original_file;
7444     NautilusFile *original_dir;
7445     GHashTable *original_dirs_hash;
7446     GList *original_dirs;
7447     gboolean can_restore;
7448 
7449     original_file = NULL;
7450     original_dir = NULL;
7451     original_dirs = NULL;
7452     original_dirs_hash = NULL;
7453 
7454     if (files != NULL)
7455     {
7456         if (g_list_length (files) == 1)
7457         {
7458             original_file = nautilus_file_get_trash_original_file (files->data);
7459         }
7460         else
7461         {
7462             original_dirs_hash = nautilus_trashed_files_get_original_directories (files, NULL);
7463             if (original_dirs_hash != NULL)
7464             {
7465                 original_dirs = g_hash_table_get_keys (original_dirs_hash);
7466                 if (g_list_length (original_dirs) == 1)
7467                 {
7468                     original_dir = nautilus_file_ref (NAUTILUS_FILE (original_dirs->data));
7469                 }
7470             }
7471         }
7472     }
7473 
7474     can_restore = original_file != NULL || original_dirs != NULL;
7475 
7476     nautilus_file_unref (original_file);
7477     nautilus_file_unref (original_dir);
7478     g_list_free (original_dirs);
7479 
7480     if (original_dirs_hash != NULL)
7481     {
7482         g_hash_table_destroy (original_dirs_hash);
7483     }
7484     return can_restore;
7485 }
7486 
7487 static void
on_clipboard_owner_changed(GtkClipboard * clipboard,GdkEvent * event,gpointer user_data)7488 on_clipboard_owner_changed (GtkClipboard *clipboard,
7489                             GdkEvent     *event,
7490                             gpointer      user_data)
7491 {
7492     NautilusFilesView *self = NAUTILUS_FILES_VIEW (user_data);
7493 
7494     /* Update paste menu item */
7495     nautilus_files_view_update_context_menus (self);
7496 }
7497 
7498 static gboolean
can_delete_all(GList * files)7499 can_delete_all (GList *files)
7500 {
7501     NautilusFile *file;
7502     GList *l;
7503 
7504     for (l = files; l != NULL; l = l->next)
7505     {
7506         file = l->data;
7507         if (!nautilus_file_can_delete (file))
7508         {
7509             return FALSE;
7510         }
7511     }
7512     return TRUE;
7513 }
7514 
7515 static gboolean
can_trash_all(GList * files)7516 can_trash_all (GList *files)
7517 {
7518     NautilusFile *file;
7519     GList *l;
7520 
7521     for (l = files; l != NULL; l = l->next)
7522     {
7523         file = l->data;
7524         if (!nautilus_file_can_trash (file))
7525         {
7526             return FALSE;
7527         }
7528     }
7529     return TRUE;
7530 }
7531 
7532 static gboolean
all_in_trash(GList * files)7533 all_in_trash (GList *files)
7534 {
7535     NautilusFile *file;
7536     GList *l;
7537 
7538     for (l = files; l != NULL; l = l->next)
7539     {
7540         file = l->data;
7541         if (!nautilus_file_is_in_trash (file))
7542         {
7543             return FALSE;
7544         }
7545     }
7546     return TRUE;
7547 }
7548 
7549 static gboolean
can_extract_all(GList * files)7550 can_extract_all (GList *files)
7551 {
7552     NautilusFile *file;
7553     GList *l;
7554 
7555     for (l = files; l != NULL; l = l->next)
7556     {
7557         file = l->data;
7558         if (!nautilus_file_is_archive (file))
7559         {
7560             return FALSE;
7561         }
7562     }
7563     return TRUE;
7564 }
7565 
7566 static gboolean
nautilus_handles_all_files_to_extract(GList * files)7567 nautilus_handles_all_files_to_extract (GList *files)
7568 {
7569     NautilusFile *file;
7570     GList *l;
7571 
7572     for (l = files; l != NULL; l = l->next)
7573     {
7574         file = l->data;
7575         if (!nautilus_mime_file_extracts (file))
7576         {
7577             return FALSE;
7578         }
7579     }
7580     return TRUE;
7581 }
7582 
7583 GActionGroup *
nautilus_files_view_get_action_group(NautilusFilesView * view)7584 nautilus_files_view_get_action_group (NautilusFilesView *view)
7585 {
7586     NautilusFilesViewPrivate *priv;
7587 
7588     g_assert (NAUTILUS_IS_FILES_VIEW (view));
7589 
7590     priv = nautilus_files_view_get_instance_private (view);
7591 
7592     return priv->view_action_group;
7593 }
7594 
7595 static void
real_update_actions_state(NautilusFilesView * view)7596 real_update_actions_state (NautilusFilesView *view)
7597 {
7598     NautilusFilesViewPrivate *priv;
7599     g_autolist (NautilusFile) selection = NULL;
7600     GList *l;
7601     gint selection_count;
7602     gboolean zoom_level_is_default;
7603     gboolean selection_contains_home_dir;
7604     gboolean selection_contains_recent;
7605     gboolean selection_contains_search;
7606     gboolean selection_contains_starred;
7607     gboolean selection_all_in_trash;
7608     gboolean selection_is_read_only;
7609     gboolean can_create_files;
7610     gboolean can_delete_files;
7611     gboolean can_move_files;
7612     gboolean can_trash_files;
7613     gboolean can_copy_files;
7614     gboolean can_paste_files_into;
7615     gboolean can_extract_files;
7616     gboolean handles_all_files_to_extract;
7617     gboolean can_extract_here;
7618     gboolean item_opens_in_view;
7619     gboolean is_read_only;
7620     GAction *action;
7621     GActionGroup *view_action_group;
7622     gboolean show_mount;
7623     gboolean show_unmount;
7624     gboolean show_eject;
7625     gboolean show_start;
7626     gboolean show_stop;
7627     gboolean show_detect_media;
7628     gboolean settings_show_delete_permanently;
7629     gboolean settings_show_create_link;
7630     GDriveStartStopType start_stop_type;
7631     g_autoptr (GFile) current_location = NULL;
7632     g_autofree gchar *current_uri = NULL;
7633     gboolean can_star_current_directory;
7634     gboolean show_star;
7635     gboolean show_unstar;
7636     gchar *uri;
7637 
7638     priv = nautilus_files_view_get_instance_private (view);
7639 
7640     view_action_group = priv->view_action_group;
7641 
7642     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
7643     selection_count = g_list_length (selection);
7644     selection_contains_home_dir = home_dir_in_selection (selection);
7645     selection_contains_recent = showing_recent_directory (view);
7646     selection_contains_starred = showing_starred_directory (view);
7647     selection_contains_search = nautilus_view_is_searching (NAUTILUS_VIEW (view));
7648     selection_is_read_only = selection_count == 1 &&
7649                              (!nautilus_file_can_write (NAUTILUS_FILE (selection->data)) &&
7650                               !nautilus_file_has_activation_uri (NAUTILUS_FILE (selection->data)));
7651     selection_all_in_trash = all_in_trash (selection);
7652     zoom_level_is_default = nautilus_files_view_is_zoom_level_default (view);
7653 
7654     is_read_only = nautilus_files_view_is_read_only (view);
7655     can_create_files = nautilus_files_view_supports_creating_files (view);
7656     can_delete_files =
7657         can_delete_all (selection) &&
7658         selection_count != 0 &&
7659         !selection_contains_home_dir;
7660     can_trash_files =
7661         can_trash_all (selection) &&
7662         selection_count != 0 &&
7663         !selection_contains_home_dir;
7664     can_copy_files = selection_count != 0;
7665     can_move_files = can_delete_files && !selection_contains_recent &&
7666                      !selection_contains_starred;
7667     can_paste_files_into = (!selection_contains_recent &&
7668                             !selection_contains_starred &&
7669                             selection_count == 1 &&
7670                             can_paste_into_file (NAUTILUS_FILE (selection->data)));
7671     can_extract_files = selection_count != 0 &&
7672                         can_extract_all (selection);
7673     can_extract_here = nautilus_files_view_supports_extract_here (view);
7674     handles_all_files_to_extract = nautilus_handles_all_files_to_extract (selection);
7675     settings_show_delete_permanently = g_settings_get_boolean (nautilus_preferences,
7676                                                                NAUTILUS_PREFERENCES_SHOW_DELETE_PERMANENTLY);
7677     settings_show_create_link = g_settings_get_boolean (nautilus_preferences,
7678                                                         NAUTILUS_PREFERENCES_SHOW_CREATE_LINK);
7679     /* Right click actions
7680      * Selection menu actions
7681      */
7682     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7683                                          "new-folder-with-selection");
7684     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7685                                  can_create_files && can_delete_files && (selection_count > 1) && !selection_contains_recent
7686                                  && !selection_contains_starred);
7687 
7688     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7689                                          "rename");
7690     if (selection_count > 1)
7691     {
7692         g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7693                                      nautilus_file_can_rename_files (selection));
7694     }
7695     else
7696     {
7697         g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7698                                      selection_count == 1 &&
7699                                      nautilus_file_can_rename (selection->data));
7700     }
7701 
7702     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7703                                          "extract-here");
7704     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7705                                  can_extract_files &&
7706                                  !handles_all_files_to_extract &&
7707                                  can_extract_here);
7708 
7709     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7710                                          "extract-to");
7711     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7712                                  can_extract_files &&
7713                                  (!handles_all_files_to_extract ||
7714                                   can_extract_here));
7715 
7716     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7717                                          "compress");
7718     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7719                                  can_create_files && can_copy_files);
7720 
7721     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7722                                          "open-item-location");
7723 
7724     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7725                                  selection_count == 1 &&
7726                                  (selection_contains_recent || selection_contains_search ||
7727                                   selection_contains_starred));
7728 
7729     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7730                                          "new-folder");
7731     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_create_files);
7732 
7733     item_opens_in_view = selection_count != 0;
7734 
7735     for (l = selection; l != NULL; l = l->next)
7736     {
7737         NautilusFile *file;
7738 
7739         file = NAUTILUS_FILE (selection->data);
7740 
7741         if (!nautilus_file_opens_in_view (file))
7742         {
7743             item_opens_in_view = FALSE;
7744         }
7745 
7746         if (!item_opens_in_view)
7747         {
7748             break;
7749         }
7750     }
7751 
7752     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7753                                          "open-with-default-application");
7754     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), selection_count != 0);
7755 
7756     /* Allow to select a different application to open the item */
7757     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7758                                          "open-with-other-application");
7759     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7760                                  selection_count > 0);
7761 
7762     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7763                                          "open-item-new-tab");
7764     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), item_opens_in_view);
7765     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7766                                          "open-item-new-window");
7767     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), item_opens_in_view);
7768     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7769                                          "run-in-terminal");
7770     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_run_in_terminal (selection));
7771     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7772                                          "set-as-wallpaper");
7773     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_set_wallpaper (selection));
7774     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7775                                          "restore-from-trash");
7776     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_restore_from_trash (selection));
7777 
7778     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7779                                          "move-to-trash");
7780     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_trash_files);
7781 
7782     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7783                                          "delete-from-trash");
7784     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7785                                  can_delete_files && selection_all_in_trash);
7786 
7787     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7788                                          "delete-permanently-shortcut");
7789     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7790                                  can_delete_files);
7791 
7792     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7793                                          "delete-permanently-menu-item");
7794     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7795                                  can_delete_files && !can_trash_files &&
7796                                  !selection_all_in_trash && !selection_contains_recent &&
7797                                  !selection_contains_starred);
7798 
7799     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7800                                          "permanent-delete-permanently-menu-item");
7801     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7802                                  can_delete_files && can_trash_files &&
7803                                  settings_show_delete_permanently &&
7804                                  !selection_all_in_trash && !selection_contains_recent &&
7805                                  !selection_contains_starred);
7806 
7807     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7808                                          "remove-from-recent");
7809     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7810                                  selection_contains_recent && selection_count > 0);
7811 
7812     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7813                                          "cut");
7814     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7815                                  can_move_files && !selection_contains_recent &&
7816                                  !selection_contains_starred);
7817     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7818                                          "copy");
7819     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7820                                  can_copy_files);
7821     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7822                                          "create-link-in-place");
7823     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7824                                  can_copy_files &&
7825                                  can_create_files &&
7826                                  settings_show_create_link);
7827     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7828                                          "copy-to");
7829     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7830                                  can_copy_files);
7831     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7832                                          "move-to");
7833     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7834                                  can_move_files && !selection_contains_recent &&
7835                                  !selection_contains_starred);
7836 
7837     /* Drive menu */
7838     show_mount = (selection != NULL);
7839     show_unmount = (selection != NULL);
7840     show_eject = (selection != NULL);
7841     show_start = (selection != NULL && selection_count == 1);
7842     show_stop = (selection != NULL && selection_count == 1);
7843     show_detect_media = (selection != NULL && selection_count == 1);
7844     for (l = selection; l != NULL && (show_mount || show_unmount
7845                                       || show_eject
7846                                       || show_start || show_stop
7847                                       || show_detect_media);
7848          l = l->next)
7849     {
7850         NautilusFile *file;
7851         gboolean show_mount_one;
7852         gboolean show_unmount_one;
7853         gboolean show_eject_one;
7854         gboolean show_start_one;
7855         gboolean show_stop_one;
7856         gboolean show_detect_media_one;
7857 
7858         file = NAUTILUS_FILE (l->data);
7859         file_should_show_foreach (file,
7860                                   &show_mount_one,
7861                                   &show_unmount_one,
7862                                   &show_eject_one,
7863                                   &show_start_one,
7864                                   &show_stop_one,
7865                                   &show_detect_media_one,
7866                                   &start_stop_type);
7867 
7868         show_mount &= show_mount_one;
7869         show_unmount &= show_unmount_one;
7870         show_eject &= show_eject_one;
7871         show_start &= show_start_one;
7872         show_stop &= show_stop_one;
7873         show_detect_media &= show_detect_media_one;
7874     }
7875 
7876     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7877                                          "mount-volume");
7878     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7879                                  show_mount);
7880     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7881                                          "unmount-volume");
7882     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7883                                  show_unmount);
7884     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7885                                          "eject-volume");
7886     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7887                                  show_eject);
7888     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7889                                          "start-volume");
7890     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7891                                  show_start);
7892     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7893                                          "stop-volume");
7894     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7895                                  show_stop);
7896     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7897                                          "detect-media");
7898     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7899                                  show_detect_media);
7900 
7901     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7902                                          "scripts");
7903     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7904                                  priv->scripts_menu != NULL);
7905 
7906     /* Background menu actions */
7907     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7908                                          "new-folder");
7909     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), can_create_files);
7910     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7911                                          "paste");
7912     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7913                                  !is_read_only && !selection_contains_recent &&
7914                                  !selection_contains_starred);
7915 
7916     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7917                                          "paste-into");
7918     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7919                                  !selection_is_read_only && !selection_contains_recent &&
7920                                  can_paste_files_into && !selection_contains_starred);
7921 
7922     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7923                                          "properties");
7924     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7925                                  TRUE);
7926     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7927                                          "current-directory-properties");
7928     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7929                                  !selection_contains_search);
7930     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7931                                          "new-document");
7932     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7933                                  can_create_files &&
7934                                  !selection_contains_recent &&
7935                                  !selection_contains_starred &&
7936                                  priv->templates_menu != NULL);
7937 
7938     /* Actions that are related to the clipboard need request, request the data
7939      * and update them once we have the data */
7940     g_object_ref (view);     /* Need to keep the object alive until we get the reply */
7941     gtk_clipboard_request_targets (nautilus_clipboard_get (GTK_WIDGET (view)),
7942                                    on_clipboard_targets_received,
7943                                    view);
7944 
7945     g_object_ref (view);     /* Need to keep the object alive until we get the reply */
7946     gtk_clipboard_request_contents (nautilus_clipboard_get (GTK_WIDGET (view)),
7947                                     nautilus_clipboard_get_atom (),
7948                                     on_clipboard_contents_received,
7949                                     view);
7950 
7951     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7952                                          "select-all");
7953     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7954                                  !nautilus_files_view_is_empty (view) &&
7955                                  !priv->loading);
7956 
7957     /* Toolbar menu actions */
7958     g_action_group_change_action_state (view_action_group,
7959                                         "show-hidden-files",
7960                                         g_variant_new_boolean (priv->show_hidden_files));
7961 
7962     /* Zoom */
7963     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7964                                          "zoom-in");
7965     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7966                                  nautilus_files_view_can_zoom_in (view));
7967     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7968                                          "zoom-out");
7969     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7970                                  nautilus_files_view_can_zoom_out (view));
7971     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7972                                          "zoom-standard");
7973     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7974                                  nautilus_files_view_supports_zooming (view) && !zoom_level_is_default);
7975     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
7976                                          "zoom-to-level");
7977     g_simple_action_set_enabled (G_SIMPLE_ACTION (action),
7978                                  !nautilus_files_view_is_empty (view));
7979 
7980     current_location = nautilus_file_get_location (nautilus_files_view_get_directory_as_file (view));
7981     current_uri = g_file_get_uri (current_location);
7982     can_star_current_directory = nautilus_tag_manager_can_star_contents (priv->tag_manager, current_location);
7983 
7984     show_star = (selection != NULL) &&
7985                 (can_star_current_directory || selection_contains_starred);
7986     show_unstar = (selection != NULL) &&
7987                   (can_star_current_directory || selection_contains_starred);
7988     for (l = selection; l != NULL; l = l->next)
7989     {
7990         NautilusFile *file;
7991 
7992         file = NAUTILUS_FILE (l->data);
7993         uri = nautilus_file_get_uri (file);
7994 
7995         if (!show_star && !show_unstar)
7996         {
7997             break;
7998         }
7999 
8000         if (nautilus_tag_manager_file_is_starred (priv->tag_manager, uri))
8001         {
8002             show_star = FALSE;
8003         }
8004         else
8005         {
8006             show_unstar = FALSE;
8007         }
8008 
8009         g_free (uri);
8010     }
8011 
8012     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
8013                                          "star");
8014     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), show_star);
8015 
8016     action = g_action_map_lookup_action (G_ACTION_MAP (view_action_group),
8017                                          "unstar");
8018     g_simple_action_set_enabled (G_SIMPLE_ACTION (action), show_unstar);
8019 }
8020 
8021 /* Convenience function to be called when updating menus,
8022  * so children can subclass it and it will be called when
8023  * they chain up to the parent in update_context_menus
8024  * or update_toolbar_menus
8025  */
8026 void
nautilus_files_view_update_actions_state(NautilusFilesView * view)8027 nautilus_files_view_update_actions_state (NautilusFilesView *view)
8028 {
8029     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8030 
8031     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->update_actions_state (view);
8032 }
8033 
8034 static void
update_selection_menu(NautilusFilesView * view,GtkBuilder * builder)8035 update_selection_menu (NautilusFilesView *view,
8036                        GtkBuilder        *builder)
8037 {
8038     NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view);
8039     g_autolist (NautilusFile) selection = NULL;
8040     GList *l;
8041     gint selection_count;
8042     gboolean show_app;
8043     gboolean show_run;
8044     gboolean show_extract;
8045     gboolean item_opens_in_view;
8046     gchar *item_label;
8047     GAppInfo *app;
8048     GIcon *app_icon;
8049     GMenuItem *menu_item;
8050     GObject *object;
8051     gboolean show_mount;
8052     gboolean show_unmount;
8053     gboolean show_eject;
8054     gboolean show_start;
8055     gboolean show_stop;
8056     gboolean show_detect_media;
8057     GDriveStartStopType start_stop_type;
8058 
8059     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
8060     selection_count = g_list_length (selection);
8061 
8062     show_mount = (selection != NULL);
8063     show_unmount = (selection != NULL);
8064     show_eject = (selection != NULL);
8065     show_start = (selection != NULL && selection_count == 1);
8066     show_stop = (selection != NULL && selection_count == 1);
8067     show_detect_media = (selection != NULL && selection_count == 1);
8068     start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
8069     item_label = g_strdup_printf (ngettext ("New Folder with Selection (%'d Item)",
8070                                             "New Folder with Selection (%'d Items)",
8071                                             selection_count),
8072                                   selection_count);
8073     menu_item = g_menu_item_new (item_label, "view.new-folder-with-selection");
8074     g_menu_item_set_attribute (menu_item, "hidden-when", "s", "action-disabled");
8075     object = gtk_builder_get_object (builder, "new-folder-with-selection-section");
8076     g_menu_append_item (G_MENU (object), menu_item);
8077     g_object_unref (menu_item);
8078     g_free (item_label);
8079 
8080     /* Open With <App> menu item */
8081     show_extract = show_app = show_run = item_opens_in_view = selection_count != 0;
8082     for (l = selection; l != NULL; l = l->next)
8083     {
8084         NautilusFile *file;
8085 
8086         file = NAUTILUS_FILE (l->data);
8087 
8088         if (!nautilus_mime_file_extracts (file))
8089         {
8090             show_extract = FALSE;
8091         }
8092 
8093         if (!nautilus_mime_file_opens_in_external_app (file))
8094         {
8095             show_app = FALSE;
8096         }
8097 
8098         if (!nautilus_mime_file_launches (file))
8099         {
8100             show_run = FALSE;
8101         }
8102 
8103         if (!nautilus_file_opens_in_view (file))
8104         {
8105             item_opens_in_view = FALSE;
8106         }
8107 
8108         if (!show_extract && !show_app && !show_run && !item_opens_in_view)
8109         {
8110             break;
8111         }
8112     }
8113 
8114     item_label = NULL;
8115     app = NULL;
8116     app_icon = NULL;
8117     if (show_app)
8118     {
8119         app = nautilus_mime_get_default_application_for_files (selection);
8120     }
8121 
8122     if (app != NULL)
8123     {
8124         char *escaped_app;
8125 
8126         escaped_app = eel_str_double_underscores (g_app_info_get_name (app));
8127         item_label = g_strdup_printf (_("Open With %s"), escaped_app);
8128 
8129         app_icon = g_app_info_get_icon (app);
8130         if (app_icon != NULL)
8131         {
8132             g_object_ref (app_icon);
8133         }
8134         g_free (escaped_app);
8135         g_object_unref (app);
8136     }
8137     else if (show_run)
8138     {
8139         item_label = g_strdup (_("Run"));
8140     }
8141     else if (show_extract)
8142     {
8143         item_label = nautilus_files_view_supports_extract_here (view) ?
8144                      g_strdup (_("Extract Here")) :
8145                      g_strdup (_("Extract to…"));
8146     }
8147     else
8148     {
8149         item_label = g_strdup (_("Open"));
8150     }
8151 
8152     menu_item = g_menu_item_new (item_label, "view.open-with-default-application");
8153     if (app_icon != NULL)
8154     {
8155         g_menu_item_set_icon (menu_item, app_icon);
8156     }
8157 
8158     object = gtk_builder_get_object (builder, "open-with-application-section");
8159     g_menu_prepend_item (G_MENU (object), menu_item);
8160 
8161     g_free (item_label);
8162     g_object_unref (menu_item);
8163 
8164     /* Drives */
8165     for (l = selection; l != NULL && (show_mount || show_unmount
8166                                       || show_eject
8167                                       || show_start || show_stop
8168                                       || show_detect_media);
8169          l = l->next)
8170     {
8171         NautilusFile *file;
8172         gboolean show_mount_one;
8173         gboolean show_unmount_one;
8174         gboolean show_eject_one;
8175         gboolean show_start_one;
8176         gboolean show_stop_one;
8177         gboolean show_detect_media_one;
8178 
8179         file = NAUTILUS_FILE (l->data);
8180         file_should_show_foreach (file,
8181                                   &show_mount_one,
8182                                   &show_unmount_one,
8183                                   &show_eject_one,
8184                                   &show_start_one,
8185                                   &show_stop_one,
8186                                   &show_detect_media_one,
8187                                   &start_stop_type);
8188 
8189         show_mount &= show_mount_one;
8190         show_unmount &= show_unmount_one;
8191         show_eject &= show_eject_one;
8192         show_start &= show_start_one;
8193         show_stop &= show_stop_one;
8194         show_detect_media &= show_detect_media_one;
8195     }
8196 
8197     if (show_start)
8198     {
8199         switch (start_stop_type)
8200         {
8201             default:
8202             case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8203             case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8204             {
8205                 item_label = _("_Start");
8206             }
8207             break;
8208 
8209             case G_DRIVE_START_STOP_TYPE_NETWORK:
8210             {
8211                 item_label = _("_Connect");
8212             }
8213             break;
8214 
8215             case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8216             {
8217                 item_label = _("_Start Multi-disk Drive");
8218             }
8219             break;
8220 
8221             case G_DRIVE_START_STOP_TYPE_PASSWORD:
8222             {
8223                 item_label = _("U_nlock Drive");
8224             }
8225             break;
8226         }
8227 
8228         menu_item = g_menu_item_new (item_label, "view.start-volume");
8229         object = gtk_builder_get_object (builder, "drive-section");
8230         g_menu_append_item (G_MENU (object), menu_item);
8231         g_object_unref (menu_item);
8232     }
8233 
8234     if (show_stop)
8235     {
8236         switch (start_stop_type)
8237         {
8238             default:
8239             case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8240             {
8241                 item_label = _("Stop Drive");
8242             }
8243             break;
8244 
8245             case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8246             {
8247                 item_label = _("_Safely Remove Drive");
8248             }
8249             break;
8250 
8251             case G_DRIVE_START_STOP_TYPE_NETWORK:
8252             {
8253                 item_label = _("_Disconnect");
8254             }
8255             break;
8256 
8257             case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8258             {
8259                 item_label = _("_Stop Multi-disk Drive");
8260             }
8261             break;
8262 
8263             case G_DRIVE_START_STOP_TYPE_PASSWORD:
8264             {
8265                 item_label = _("_Lock Drive");
8266             }
8267             break;
8268         }
8269 
8270         menu_item = g_menu_item_new (item_label, "view.stop-volume");
8271         object = gtk_builder_get_object (builder, "drive-section");
8272         g_menu_append_item (G_MENU (object), menu_item);
8273         g_object_unref (menu_item);
8274     }
8275 
8276     if (!priv->scripts_menu_updated)
8277     {
8278         update_scripts_menu (view, builder);
8279         priv->scripts_menu_updated = TRUE;
8280     }
8281     object = gtk_builder_get_object (builder, "scripts-submenu-section");
8282     nautilus_gmenu_set_from_model (G_MENU (object), priv->scripts_menu);
8283 }
8284 
8285 static void
update_background_menu(NautilusFilesView * view,GtkBuilder * builder)8286 update_background_menu (NautilusFilesView *view,
8287                         GtkBuilder        *builder)
8288 {
8289     NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view);
8290     GObject *object;
8291 
8292     if (nautilus_files_view_supports_creating_files (view) &&
8293         !showing_recent_directory (view) &&
8294         !showing_starred_directory (view))
8295     {
8296         if (!priv->templates_menu_updated)
8297         {
8298             update_templates_menu (view, builder);
8299             priv->templates_menu_updated = TRUE;
8300         }
8301 
8302         object = gtk_builder_get_object (builder, "templates-submenu");
8303         nautilus_gmenu_set_from_model (G_MENU (object), priv->templates_menu);
8304     }
8305 }
8306 
8307 static void
real_update_context_menus(NautilusFilesView * view)8308 real_update_context_menus (NautilusFilesView *view)
8309 {
8310     NautilusFilesViewPrivate *priv;
8311     g_autoptr (GtkBuilder) builder = NULL;
8312     GObject *object;
8313 
8314     priv = nautilus_files_view_get_instance_private (view);
8315     builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-files-view-context-menus.ui");
8316 
8317     g_clear_object (&priv->background_menu_model);
8318     g_clear_object (&priv->selection_menu_model);
8319 
8320     object = gtk_builder_get_object (builder, "background-menu");
8321     priv->background_menu_model = g_object_ref (G_MENU (object));
8322 
8323     object = gtk_builder_get_object (builder, "selection-menu");
8324     priv->selection_menu_model = g_object_ref (G_MENU (object));
8325 
8326     update_selection_menu (view, builder);
8327     update_background_menu (view, builder);
8328     update_extensions_menus (view, builder);
8329 
8330     nautilus_files_view_update_actions_state (view);
8331 }
8332 
8333 /* Convenience function to reset the context menus owned by the view and update
8334  * them with the current state.
8335  * Children can subclass it and add items on the menu after chaining up to the
8336  * parent, so menus are already reseted.
8337  * It will also update the actions state, which will also update children
8338  * actions state if the children subclass nautilus_files_view_update_actions_state
8339  */
8340 void
nautilus_files_view_update_context_menus(NautilusFilesView * view)8341 nautilus_files_view_update_context_menus (NautilusFilesView *view)
8342 {
8343     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8344 
8345     NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->update_context_menus (view);
8346 }
8347 
8348 static void
nautilus_files_view_reset_view_menu(NautilusFilesView * view)8349 nautilus_files_view_reset_view_menu (NautilusFilesView *view)
8350 {
8351     NautilusFilesViewPrivate *priv;
8352     GActionGroup *view_action_group;
8353     gboolean sort_available;
8354     g_autofree gchar *zoom_level_percent = NULL;
8355     NautilusFile *file;
8356 
8357     view_action_group = nautilus_files_view_get_action_group (view);
8358     priv = nautilus_files_view_get_instance_private (view);
8359     file = nautilus_files_view_get_directory_as_file (NAUTILUS_FILES_VIEW (view));
8360 
8361     gtk_widget_set_visible (priv->visible_columns,
8362                             g_action_group_has_action (view_action_group, "visible-columns"));
8363 
8364     sort_available = g_action_group_get_action_enabled (view_action_group, "sort");
8365     gtk_widget_set_visible (priv->sort_menu, sort_available);
8366     gtk_widget_set_visible (priv->sort_trash_time,
8367                             nautilus_file_is_in_trash (file));
8368 
8369     /* We want to make insensitive available actions but that are not current
8370      * available due to the directory
8371      */
8372     gtk_widget_set_sensitive (priv->sort_menu,
8373                               !nautilus_files_view_is_empty (view));
8374     gtk_widget_set_sensitive (priv->zoom_controls_box,
8375                               !nautilus_files_view_is_empty (view));
8376 
8377     zoom_level_percent = g_strdup_printf ("%.0f%%", nautilus_files_view_get_zoom_level_percentage (view) * 100.0);
8378     gtk_label_set_label (GTK_LABEL (priv->zoom_level_label), zoom_level_percent);
8379 }
8380 
8381 /* Convenience function to reset the menus owned by the view but managed on
8382  * the toolbar, and update them with the current state.
8383  * It will also update the actions state, which will also update children
8384  * actions state if the children subclass nautilus_files_view_update_actions_state
8385  */
8386 void
nautilus_files_view_update_toolbar_menus(NautilusFilesView * view)8387 nautilus_files_view_update_toolbar_menus (NautilusFilesView *view)
8388 {
8389     NautilusWindow *window;
8390     NautilusFilesViewPrivate *priv;
8391 
8392     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8393 
8394     priv = nautilus_files_view_get_instance_private (view);
8395 
8396     /* Don't update after destroy (#349551),
8397      * or if we are not active.
8398      */
8399     if (priv->slot == NULL ||
8400         !priv->active)
8401     {
8402         return;
8403     }
8404     window = nautilus_files_view_get_window (view);
8405     nautilus_window_reset_menus (window);
8406 
8407     nautilus_files_view_update_actions_state (view);
8408     nautilus_files_view_reset_view_menu (view);
8409 }
8410 
8411 static GdkRectangle *
nautilus_files_view_reveal_for_selection_context_menu(NautilusFilesView * view)8412 nautilus_files_view_reveal_for_selection_context_menu (NautilusFilesView *view)
8413 {
8414     return NAUTILUS_FILES_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->reveal_for_selection_context_menu (view);
8415 }
8416 
8417 /**
8418  * nautilus_files_view_pop_up_selection_context_menu
8419  *
8420  * Pop up a context menu appropriate to the selected items.
8421  * @view: NautilusFilesView of interest.
8422  * @event: The event that triggered this context menu.
8423  *
8424  **/
8425 void
nautilus_files_view_pop_up_selection_context_menu(NautilusFilesView * view,const GdkEvent * event)8426 nautilus_files_view_pop_up_selection_context_menu  (NautilusFilesView *view,
8427                                                     const GdkEvent    *event)
8428 {
8429     NautilusFilesViewPrivate *priv;
8430 
8431     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8432 
8433     priv = nautilus_files_view_get_instance_private (view);
8434 
8435     /* Make the context menu items not flash as they update to proper disabled,
8436      * etc. states by forcing menus to update now.
8437      */
8438     update_context_menus_if_pending (view);
8439 
8440     if (NULL == priv->selection_menu)
8441     {
8442         priv->selection_menu = gtk_menu_new ();
8443 
8444         gtk_menu_attach_to_widget (GTK_MENU (priv->selection_menu),
8445                                    GTK_WIDGET (view),
8446                                    NULL);
8447     }
8448 
8449     gtk_menu_shell_bind_model (GTK_MENU_SHELL (priv->selection_menu),
8450                                G_MENU_MODEL (priv->selection_menu_model),
8451                                NULL,
8452                                TRUE);
8453 
8454     if (event != NULL)
8455     {
8456         gtk_menu_popup_at_pointer (GTK_MENU (priv->selection_menu), event);
8457     }
8458     else
8459     {
8460         /* If triggered from the keyboard, popup at selection, not pointer */
8461         g_autofree GdkRectangle *rectangle = NULL;
8462 
8463         rectangle = nautilus_files_view_reveal_for_selection_context_menu (view);
8464         g_return_if_fail (rectangle != NULL);
8465 
8466         gtk_menu_popup_at_rect (GTK_MENU (priv->selection_menu),
8467                                 gtk_widget_get_window (GTK_WIDGET (view)),
8468                                 rectangle,
8469                                 GDK_GRAVITY_SOUTH_WEST,
8470                                 GDK_GRAVITY_NORTH_WEST,
8471                                 NULL);
8472     }
8473 }
8474 
8475 /**
8476  * nautilus_files_view_pop_up_background_context_menu
8477  *
8478  * Pop up a context menu appropriate to the location in view.
8479  * @view: NautilusFilesView of interest.
8480  *
8481  **/
8482 void
nautilus_files_view_pop_up_background_context_menu(NautilusFilesView * view,const GdkEvent * event)8483 nautilus_files_view_pop_up_background_context_menu (NautilusFilesView *view,
8484                                                     const GdkEvent    *event)
8485 {
8486     NautilusFilesViewPrivate *priv;
8487 
8488     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8489 
8490     priv = nautilus_files_view_get_instance_private (view);
8491 
8492     /* Make the context menu items not flash as they update to proper disabled,
8493      * etc. states by forcing menus to update now.
8494      */
8495     update_context_menus_if_pending (view);
8496 
8497     if (NULL == priv->background_menu)
8498     {
8499         priv->background_menu = gtk_menu_new ();
8500 
8501         gtk_menu_attach_to_widget (GTK_MENU (priv->background_menu),
8502                                    GTK_WIDGET (view),
8503                                    NULL);
8504     }
8505     gtk_menu_shell_bind_model (GTK_MENU_SHELL (priv->background_menu),
8506                                G_MENU_MODEL (priv->background_menu_model),
8507                                NULL,
8508                                TRUE);
8509     if (event != NULL)
8510     {
8511         gtk_menu_popup_at_pointer (GTK_MENU (priv->background_menu), event);
8512     }
8513     else
8514     {
8515         /* It was triggered from the keyboard, so pop up from the center of view.
8516          */
8517         gtk_menu_popup_at_widget (GTK_MENU (priv->background_menu),
8518                                   GTK_WIDGET (view),
8519                                   GDK_GRAVITY_CENTER,
8520                                   GDK_GRAVITY_CENTER,
8521                                   NULL);
8522     }
8523 }
8524 
8525 static gboolean
popup_menu_callback(NautilusFilesView * view)8526 popup_menu_callback (NautilusFilesView *view)
8527 {
8528     g_autolist (NautilusFile) selection = NULL;
8529 
8530     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
8531 
8532     if (selection != NULL)
8533     {
8534         nautilus_files_view_pop_up_selection_context_menu (view, NULL);
8535     }
8536     else
8537     {
8538         nautilus_files_view_pop_up_background_context_menu (view, NULL);
8539     }
8540 
8541     return TRUE;
8542 }
8543 
8544 static void
schedule_update_context_menus(NautilusFilesView * view)8545 schedule_update_context_menus (NautilusFilesView *view)
8546 {
8547     NautilusFilesViewPrivate *priv;
8548 
8549     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8550 
8551     priv = nautilus_files_view_get_instance_private (view);
8552 
8553     /* Don't schedule updates after destroy (#349551),
8554      * or if we are not active.
8555      */
8556     if (priv->slot == NULL ||
8557         !priv->active)
8558     {
8559         return;
8560     }
8561 
8562     /* Schedule a menu update with the current update interval */
8563     if (priv->update_context_menus_timeout_id == 0)
8564     {
8565         priv->update_context_menus_timeout_id
8566             = g_timeout_add (priv->update_interval, update_context_menus_timeout_callback, view);
8567     }
8568 }
8569 
8570 static void
remove_update_status_idle_callback(NautilusFilesView * view)8571 remove_update_status_idle_callback (NautilusFilesView *view)
8572 {
8573     NautilusFilesViewPrivate *priv;
8574 
8575     priv = nautilus_files_view_get_instance_private (view);
8576 
8577     if (priv->update_status_idle_id != 0)
8578     {
8579         g_source_remove (priv->update_status_idle_id);
8580         priv->update_status_idle_id = 0;
8581     }
8582 }
8583 
8584 static gboolean
update_status_idle_callback(gpointer data)8585 update_status_idle_callback (gpointer data)
8586 {
8587     NautilusFilesViewPrivate *priv;
8588     NautilusFilesView *view;
8589 
8590     view = NAUTILUS_FILES_VIEW (data);
8591     priv = nautilus_files_view_get_instance_private (view);
8592     nautilus_files_view_display_selection_info (view);
8593     priv->update_status_idle_id = 0;
8594     return FALSE;
8595 }
8596 
8597 static void
schedule_update_status(NautilusFilesView * view)8598 schedule_update_status (NautilusFilesView *view)
8599 {
8600     NautilusFilesViewPrivate *priv;
8601 
8602     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8603 
8604     priv = nautilus_files_view_get_instance_private (view);
8605 
8606     /* Make sure we haven't already destroyed it */
8607     if (priv->slot == NULL)
8608     {
8609         return;
8610     }
8611 
8612     if (priv->loading)
8613     {
8614         /* Don't update status bar while loading the dir */
8615         return;
8616     }
8617 
8618     if (priv->update_status_idle_id == 0)
8619     {
8620         priv->update_status_idle_id =
8621             g_idle_add_full (G_PRIORITY_DEFAULT_IDLE - 20,
8622                              update_status_idle_callback, view, NULL);
8623     }
8624 }
8625 
8626 /**
8627  * nautilus_files_view_notify_selection_changed:
8628  *
8629  * Notify this view that the selection has changed. This is normally
8630  * called only by subclasses.
8631  * @view: NautilusFilesView whose selection has changed.
8632  *
8633  **/
8634 void
nautilus_files_view_notify_selection_changed(NautilusFilesView * view)8635 nautilus_files_view_notify_selection_changed (NautilusFilesView *view)
8636 {
8637     NautilusFilesViewPrivate *priv;
8638     GtkWindow *window;
8639     g_autolist (NautilusFile) selection = NULL;
8640 
8641     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
8642 
8643     priv = nautilus_files_view_get_instance_private (view);
8644 
8645     selection = nautilus_view_get_selection (NAUTILUS_VIEW (view));
8646     window = nautilus_files_view_get_containing_window (view);
8647     DEBUG_FILES (selection, "Selection changed in window %p", window);
8648 
8649     priv->selection_was_removed = FALSE;
8650 
8651     /* Schedule a display of the new selection. */
8652     if (priv->display_selection_idle_id == 0)
8653     {
8654         priv->display_selection_idle_id
8655             = g_idle_add (display_selection_info_idle_callback,
8656                           view);
8657     }
8658 
8659     if (priv->batching_selection_level != 0)
8660     {
8661         priv->selection_changed_while_batched = TRUE;
8662     }
8663     else
8664     {
8665         /* Here is the work we do only when we're not
8666          * batching selection changes. In other words, it's the slower
8667          * stuff that we don't want to slow down selection techniques
8668          * such as rubberband-selecting in icon view.
8669          */
8670 
8671         /* Schedule an update of menu item states to match selection */
8672         schedule_update_context_menus (view);
8673     }
8674 }
8675 
8676 static void
file_changed_callback(NautilusFile * file,gpointer callback_data)8677 file_changed_callback (NautilusFile *file,
8678                        gpointer      callback_data)
8679 {
8680     NautilusFilesView *view = NAUTILUS_FILES_VIEW (callback_data);
8681 
8682     schedule_changes (view);
8683 
8684     schedule_update_context_menus (view);
8685     schedule_update_status (view);
8686 }
8687 
8688 /**
8689  * load_directory:
8690  *
8691  * Switch the displayed location to a new uri. If the uri is not valid,
8692  * the location will not be switched; user feedback will be provided instead.
8693  * @view: NautilusFilesView whose location will be changed.
8694  * @uri: A string representing the uri to switch to.
8695  *
8696  **/
8697 static void
load_directory(NautilusFilesView * view,NautilusDirectory * directory)8698 load_directory (NautilusFilesView *view,
8699                 NautilusDirectory *directory)
8700 {
8701     NautilusFileAttributes attributes;
8702     NautilusFilesViewPrivate *priv;
8703 
8704     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8705     g_assert (NAUTILUS_IS_DIRECTORY (directory));
8706 
8707     priv = nautilus_files_view_get_instance_private (view);
8708 
8709     nautilus_profile_start (NULL);
8710 
8711     nautilus_files_view_stop_loading (view);
8712     g_signal_emit (view, signals[CLEAR], 0);
8713 
8714     priv->loading = TRUE;
8715 
8716     setup_loading_floating_bar (view);
8717 
8718     /* HACK: Fix for https://gitlab.gnome.org/GNOME/nautilus/-/issues/1452 */
8719     {
8720         GtkScrolledWindow *content = GTK_SCROLLED_WINDOW (priv->scrolled_window);
8721 
8722         /* If we load a new location while the view is still scrolling due to
8723          * kinetic deceleration, we get a sudden jump to the same scrolling
8724          * position as the previous location, as well as residual scrolling
8725          * movement in the new location.
8726          *
8727          * This is both undesirable and unexpected from a user POV, so we want
8728          * to abort deceleration when switching locations.
8729          *
8730          * However, gtk_scrolled_window_cancel_deceleration() is private. So,
8731          * we make use of an undocumented behavior of ::set_kinetic_scrolling(),
8732          * which calls ::cancel_deceleration() when set to FALSE.
8733          */
8734         gtk_scrolled_window_set_kinetic_scrolling (content, FALSE);
8735         gtk_scrolled_window_set_kinetic_scrolling (content, TRUE);
8736     }
8737 
8738     /* Update menus when directory is empty, before going to new
8739      * location, so they won't have any false lingering knowledge
8740      * of old selection.
8741      */
8742     schedule_update_context_menus (view);
8743 
8744     while (priv->subdirectory_list != NULL)
8745     {
8746         nautilus_files_view_remove_subdirectory (view,
8747                                                  priv->subdirectory_list->data);
8748     }
8749 
8750     /* Avoid freeing it and won't be able to ref it */
8751     if (priv->model != directory)
8752     {
8753         nautilus_directory_unref (priv->model);
8754         priv->model = nautilus_directory_ref (directory);
8755     }
8756 
8757     nautilus_file_unref (priv->directory_as_file);
8758     priv->directory_as_file = nautilus_directory_get_corresponding_file (directory);
8759 
8760     g_clear_object (&priv->location);
8761     priv->location = nautilus_directory_get_location (directory);
8762 
8763     g_object_notify (G_OBJECT (view), "location");
8764     g_object_notify (G_OBJECT (view), "loading");
8765     g_object_notify (G_OBJECT (view), "searching");
8766 
8767     /* FIXME bugzilla.gnome.org 45062: In theory, we also need to monitor metadata here (as
8768      * well as doing a call when ready), in case external forces
8769      * change the directory's file metadata.
8770      */
8771     attributes =
8772         NAUTILUS_FILE_ATTRIBUTE_INFO |
8773         NAUTILUS_FILE_ATTRIBUTE_MOUNT |
8774         NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO;
8775     priv->metadata_for_directory_as_file_pending = TRUE;
8776     priv->metadata_for_files_in_directory_pending = TRUE;
8777     nautilus_file_call_when_ready
8778         (priv->directory_as_file,
8779         attributes,
8780         metadata_for_directory_as_file_ready_callback, view);
8781     nautilus_directory_call_when_ready
8782         (priv->model,
8783         attributes,
8784         FALSE,
8785         metadata_for_files_in_directory_ready_callback, view);
8786 
8787     /* If capabilities change, then we need to update the menus
8788      * because of New Folder, and relative emblems.
8789      */
8790     attributes =
8791         NAUTILUS_FILE_ATTRIBUTE_INFO |
8792         NAUTILUS_FILE_ATTRIBUTE_FILESYSTEM_INFO;
8793     nautilus_file_monitor_add (priv->directory_as_file,
8794                                &priv->directory_as_file,
8795                                attributes);
8796 
8797     priv->file_changed_handler_id = g_signal_connect
8798                                         (priv->directory_as_file, "changed",
8799                                         G_CALLBACK (file_changed_callback), view);
8800 
8801     nautilus_profile_end (NULL);
8802 }
8803 
8804 static void
finish_loading(NautilusFilesView * view)8805 finish_loading (NautilusFilesView *view)
8806 {
8807     NautilusFileAttributes attributes;
8808     NautilusFilesViewPrivate *priv;
8809 
8810     priv = nautilus_files_view_get_instance_private (view);
8811 
8812     nautilus_profile_start (NULL);
8813 
8814     /* Tell interested parties that we've begun loading this directory now.
8815      * Subclasses use this to know that the new metadata is now available.
8816      */
8817     nautilus_profile_start ("BEGIN_LOADING");
8818     g_signal_emit (view, signals[BEGIN_LOADING], 0);
8819     nautilus_profile_end ("BEGIN_LOADING");
8820 
8821     nautilus_files_view_check_empty_states (view);
8822 
8823     if (nautilus_directory_are_all_files_seen (priv->model))
8824     {
8825         /* Unschedule a pending update and schedule a new one with the minimal
8826          * update interval. This gives the view a short chance at gathering the
8827          * (cached) deep counts.
8828          */
8829         unschedule_display_of_pending_files (view);
8830         schedule_timeout_display_of_pending_files (view, UPDATE_INTERVAL_MIN);
8831     }
8832 
8833     /* Start loading. */
8834 
8835     /* Connect handlers to learn about loading progress. */
8836     priv->done_loading_handler_id = g_signal_connect (priv->model, "done-loading",
8837                                                       G_CALLBACK (done_loading_callback), view);
8838     priv->load_error_handler_id = g_signal_connect (priv->model, "load-error",
8839                                                     G_CALLBACK (load_error_callback), view);
8840 
8841     /* Monitor the things needed to get the right icon. Also
8842      * monitor a directory's item count because the "size"
8843      * attribute is based on that, and the file's metadata
8844      * and possible custom name.
8845      */
8846     attributes =
8847         NAUTILUS_FILE_ATTRIBUTES_FOR_ICON |
8848         NAUTILUS_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
8849         NAUTILUS_FILE_ATTRIBUTE_INFO |
8850         NAUTILUS_FILE_ATTRIBUTE_MOUNT |
8851         NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO;
8852 
8853     priv->files_added_handler_id = g_signal_connect
8854                                        (priv->model, "files-added",
8855                                        G_CALLBACK (files_added_callback), view);
8856     priv->files_changed_handler_id = g_signal_connect
8857                                          (priv->model, "files-changed",
8858                                          G_CALLBACK (files_changed_callback), view);
8859 
8860     nautilus_directory_file_monitor_add (priv->model,
8861                                          &priv->model,
8862                                          priv->show_hidden_files,
8863                                          attributes,
8864                                          files_added_callback, view);
8865 
8866     nautilus_profile_end (NULL);
8867 }
8868 
8869 static void
finish_loading_if_all_metadata_loaded(NautilusFilesView * view)8870 finish_loading_if_all_metadata_loaded (NautilusFilesView *view)
8871 {
8872     NautilusFilesViewPrivate *priv;
8873 
8874     priv = nautilus_files_view_get_instance_private (view);
8875 
8876     if (!priv->metadata_for_directory_as_file_pending &&
8877         !priv->metadata_for_files_in_directory_pending)
8878     {
8879         finish_loading (view);
8880     }
8881 }
8882 
8883 static void
metadata_for_directory_as_file_ready_callback(NautilusFile * file,gpointer callback_data)8884 metadata_for_directory_as_file_ready_callback (NautilusFile *file,
8885                                                gpointer      callback_data)
8886 {
8887     NautilusFilesView *view;
8888     NautilusFilesViewPrivate *priv;
8889 
8890     view = callback_data;
8891 
8892     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8893     priv = nautilus_files_view_get_instance_private (view);
8894     g_assert (priv->directory_as_file == file);
8895     g_assert (priv->metadata_for_directory_as_file_pending);
8896 
8897     nautilus_profile_start (NULL);
8898 
8899     priv->metadata_for_directory_as_file_pending = FALSE;
8900 
8901     finish_loading_if_all_metadata_loaded (view);
8902     nautilus_profile_end (NULL);
8903 }
8904 
8905 static void
metadata_for_files_in_directory_ready_callback(NautilusDirectory * directory,GList * files,gpointer callback_data)8906 metadata_for_files_in_directory_ready_callback (NautilusDirectory *directory,
8907                                                 GList             *files,
8908                                                 gpointer           callback_data)
8909 {
8910     NautilusFilesView *view;
8911     NautilusFilesViewPrivate *priv;
8912 
8913     view = callback_data;
8914 
8915     g_assert (NAUTILUS_IS_FILES_VIEW (view));
8916     priv = nautilus_files_view_get_instance_private (view);
8917     g_assert (priv->model == directory);
8918     g_assert (priv->metadata_for_files_in_directory_pending);
8919 
8920     nautilus_profile_start (NULL);
8921 
8922     priv->metadata_for_files_in_directory_pending = FALSE;
8923 
8924     finish_loading_if_all_metadata_loaded (view);
8925     nautilus_profile_end (NULL);
8926 }
8927 
8928 static void
disconnect_model_handlers(NautilusFilesView * view)8929 disconnect_model_handlers (NautilusFilesView *view)
8930 {
8931     NautilusFilesViewPrivate *priv;
8932 
8933     priv = nautilus_files_view_get_instance_private (view);
8934 
8935     if (priv->model == NULL)
8936     {
8937         return;
8938     }
8939     g_clear_signal_handler (&priv->files_added_handler_id, priv->model);
8940     g_clear_signal_handler (&priv->files_changed_handler_id, priv->model);
8941     g_clear_signal_handler (&priv->done_loading_handler_id, priv->model);
8942     g_clear_signal_handler (&priv->load_error_handler_id, priv->model);
8943     g_clear_signal_handler (&priv->file_changed_handler_id, priv->directory_as_file);
8944     nautilus_file_cancel_call_when_ready (priv->directory_as_file,
8945                                           metadata_for_directory_as_file_ready_callback,
8946                                           view);
8947     nautilus_directory_cancel_callback (priv->model,
8948                                         metadata_for_files_in_directory_ready_callback,
8949                                         view);
8950     nautilus_directory_file_monitor_remove (priv->model,
8951                                             &priv->model);
8952     nautilus_file_monitor_remove (priv->directory_as_file,
8953                                   &priv->directory_as_file);
8954 }
8955 
8956 static void
nautilus_files_view_select_file(NautilusFilesView * view,NautilusFile * file)8957 nautilus_files_view_select_file (NautilusFilesView *view,
8958                                  NautilusFile      *file)
8959 {
8960     GList file_list;
8961 
8962     file_list.data = file;
8963     file_list.next = NULL;
8964     file_list.prev = NULL;
8965     nautilus_files_view_call_set_selection (view, &file_list);
8966 }
8967 
8968 /**
8969  * nautilus_files_view_stop_loading:
8970  *
8971  * Stop the current ongoing process, such as switching to a new uri.
8972  * @view: NautilusFilesView in question.
8973  *
8974  **/
8975 void
nautilus_files_view_stop_loading(NautilusFilesView * view)8976 nautilus_files_view_stop_loading (NautilusFilesView *view)
8977 {
8978     NautilusFilesViewPrivate *priv;
8979 
8980     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
8981 
8982     priv = nautilus_files_view_get_instance_private (view);
8983 
8984     unschedule_display_of_pending_files (view);
8985     reset_update_interval (view);
8986 
8987     /* Free extra undisplayed files */
8988     g_list_free_full (priv->new_added_files, file_and_directory_free);
8989     priv->new_added_files = NULL;
8990 
8991     g_list_free_full (priv->new_changed_files, file_and_directory_free);
8992     priv->new_changed_files = NULL;
8993 
8994     g_hash_table_remove_all (priv->non_ready_files);
8995 
8996     g_list_free_full (priv->old_added_files, file_and_directory_free);
8997     priv->old_added_files = NULL;
8998 
8999     g_list_free_full (priv->old_changed_files, file_and_directory_free);
9000     priv->old_changed_files = NULL;
9001 
9002     g_list_free_full (priv->pending_selection, g_object_unref);
9003     priv->pending_selection = NULL;
9004 
9005     done_loading (view, FALSE);
9006 
9007     disconnect_model_handlers (view);
9008 }
9009 
9010 gboolean
nautilus_files_view_is_editable(NautilusFilesView * view)9011 nautilus_files_view_is_editable (NautilusFilesView *view)
9012 {
9013     NautilusDirectory *directory;
9014 
9015     directory = nautilus_files_view_get_model (view);
9016 
9017     if (directory != NULL)
9018     {
9019         return nautilus_directory_is_editable (directory);
9020     }
9021 
9022     return TRUE;
9023 }
9024 
9025 static gboolean
nautilus_files_view_is_read_only(NautilusFilesView * view)9026 nautilus_files_view_is_read_only (NautilusFilesView *view)
9027 {
9028     NautilusFile *file;
9029 
9030     if (!nautilus_files_view_is_editable (view))
9031     {
9032         return TRUE;
9033     }
9034 
9035     file = nautilus_files_view_get_directory_as_file (view);
9036     if (file != NULL)
9037     {
9038         return !nautilus_file_can_write (file);
9039     }
9040     return FALSE;
9041 }
9042 
9043 /**
9044  * nautilus_files_view_should_show_file
9045  *
9046  * Returns whether or not this file should be displayed based on
9047  * current filtering options.
9048  */
9049 gboolean
nautilus_files_view_should_show_file(NautilusFilesView * view,NautilusFile * file)9050 nautilus_files_view_should_show_file (NautilusFilesView *view,
9051                                       NautilusFile      *file)
9052 {
9053     NautilusFilesViewPrivate *priv;
9054 
9055     priv = nautilus_files_view_get_instance_private (view);
9056 
9057     return nautilus_file_should_show (file,
9058                                       priv->show_hidden_files);
9059 }
9060 
9061 void
nautilus_files_view_ignore_hidden_file_preferences(NautilusFilesView * view)9062 nautilus_files_view_ignore_hidden_file_preferences (NautilusFilesView *view)
9063 {
9064     NautilusFilesViewPrivate *priv;
9065 
9066     priv = nautilus_files_view_get_instance_private (view);
9067 
9068     g_return_if_fail (priv->model == NULL);
9069 
9070     if (priv->ignore_hidden_file_preferences)
9071     {
9072         return;
9073     }
9074 
9075     priv->show_hidden_files = FALSE;
9076     priv->ignore_hidden_file_preferences = TRUE;
9077 }
9078 
9079 char *
nautilus_files_view_get_uri(NautilusFilesView * view)9080 nautilus_files_view_get_uri (NautilusFilesView *view)
9081 {
9082     NautilusFilesViewPrivate *priv;
9083 
9084     g_return_val_if_fail (NAUTILUS_IS_FILES_VIEW (view), NULL);
9085 
9086     priv = nautilus_files_view_get_instance_private (view);
9087 
9088     if (priv->model == NULL)
9089     {
9090         return NULL;
9091     }
9092     return nautilus_directory_get_uri (priv->model);
9093 }
9094 
9095 void
nautilus_files_view_move_copy_items(NautilusFilesView * view,const GList * item_uris,const char * target_uri,int copy_action)9096 nautilus_files_view_move_copy_items (NautilusFilesView *view,
9097                                      const GList       *item_uris,
9098                                      const char        *target_uri,
9099                                      int                copy_action)
9100 {
9101     NautilusFile *target_file;
9102 
9103     target_file = nautilus_file_get_existing_by_uri (target_uri);
9104     if (copy_action == GDK_ACTION_COPY &&
9105         nautilus_is_file_roller_installed () &&
9106         target_file != NULL &&
9107         nautilus_file_is_archive (target_file))
9108     {
9109         char *command, *quoted_uri, *tmp;
9110         const GList *l;
9111         GdkScreen *screen;
9112 
9113         /* Handle dropping onto a file-roller archiver file, instead of starting a move/copy */
9114 
9115         nautilus_file_unref (target_file);
9116 
9117         quoted_uri = g_shell_quote (target_uri);
9118         command = g_strconcat ("file-roller -a ", quoted_uri, NULL);
9119         g_free (quoted_uri);
9120 
9121         for (l = item_uris; l != NULL; l = l->next)
9122         {
9123             quoted_uri = g_shell_quote ((char *) l->data);
9124 
9125             tmp = g_strconcat (command, " ", quoted_uri, NULL);
9126             g_free (command);
9127             command = tmp;
9128 
9129             g_free (quoted_uri);
9130         }
9131 
9132         screen = gtk_widget_get_screen (GTK_WIDGET (view));
9133         if (screen == NULL)
9134         {
9135             screen = gdk_screen_get_default ();
9136         }
9137 
9138         nautilus_launch_application_from_command (screen, command, FALSE, NULL);
9139         g_free (command);
9140 
9141         return;
9142     }
9143     nautilus_file_unref (target_file);
9144 
9145     nautilus_file_operations_copy_move
9146         (item_uris,
9147         target_uri, copy_action, GTK_WIDGET (view),
9148         NULL,
9149         copy_move_done_callback, pre_copy_move (view));
9150 }
9151 
9152 static void
nautilus_files_view_trash_state_changed_callback(NautilusTrashMonitor * trash_monitor,gboolean state,gpointer callback_data)9153 nautilus_files_view_trash_state_changed_callback (NautilusTrashMonitor *trash_monitor,
9154                                                   gboolean              state,
9155                                                   gpointer              callback_data)
9156 {
9157     NautilusFilesView *view;
9158 
9159     view = (NautilusFilesView *) callback_data;
9160     g_assert (NAUTILUS_IS_FILES_VIEW (view));
9161 
9162     schedule_update_context_menus (view);
9163 }
9164 
9165 void
nautilus_files_view_start_batching_selection_changes(NautilusFilesView * view)9166 nautilus_files_view_start_batching_selection_changes (NautilusFilesView *view)
9167 {
9168     NautilusFilesViewPrivate *priv;
9169 
9170     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
9171     priv = nautilus_files_view_get_instance_private (view);
9172 
9173     ++priv->batching_selection_level;
9174     priv->selection_changed_while_batched = FALSE;
9175 }
9176 
9177 void
nautilus_files_view_stop_batching_selection_changes(NautilusFilesView * view)9178 nautilus_files_view_stop_batching_selection_changes (NautilusFilesView *view)
9179 {
9180     NautilusFilesViewPrivate *priv;
9181 
9182     g_return_if_fail (NAUTILUS_IS_FILES_VIEW (view));
9183     priv = nautilus_files_view_get_instance_private (view);
9184     g_return_if_fail (priv->batching_selection_level > 0);
9185 
9186     if (--priv->batching_selection_level == 0)
9187     {
9188         if (priv->selection_changed_while_batched)
9189         {
9190             nautilus_files_view_notify_selection_changed (view);
9191         }
9192     }
9193 }
9194 
9195 static void
nautilus_files_view_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)9196 nautilus_files_view_get_property (GObject    *object,
9197                                   guint       prop_id,
9198                                   GValue     *value,
9199                                   GParamSpec *pspec)
9200 {
9201     NautilusFilesView *view = NAUTILUS_FILES_VIEW (object);
9202     NautilusFilesViewPrivate *priv = nautilus_files_view_get_instance_private (view);
9203 
9204     switch (prop_id)
9205     {
9206         case PROP_LOADING:
9207         {
9208             g_value_set_boolean (value, nautilus_view_is_loading (NAUTILUS_VIEW (view)));
9209         }
9210         break;
9211 
9212         case PROP_SEARCHING:
9213         {
9214             g_value_set_boolean (value, nautilus_view_is_searching (NAUTILUS_VIEW (view)));
9215         }
9216         break;
9217 
9218         case PROP_LOCATION:
9219         {
9220             g_value_set_object (value, nautilus_view_get_location (NAUTILUS_VIEW (view)));
9221         }
9222         break;
9223 
9224         case PROP_SELECTION:
9225         {
9226             g_value_set_pointer (value, nautilus_view_get_selection (NAUTILUS_VIEW (view)));
9227         }
9228         break;
9229 
9230         case PROP_SEARCH_QUERY:
9231         {
9232             g_value_set_object (value, priv->search_query);
9233         }
9234         break;
9235 
9236         case PROP_EXTENSIONS_BACKGROUND_MENU:
9237         {
9238             g_value_set_object (value,
9239                                 real_get_extensions_background_menu (NAUTILUS_VIEW (view)));
9240         }
9241         break;
9242 
9243         case PROP_TEMPLATES_MENU:
9244         {
9245             g_value_set_object (value,
9246                                 real_get_templates_menu (NAUTILUS_VIEW (view)));
9247         }
9248         break;
9249 
9250         default:
9251         {
9252             g_assert_not_reached ();
9253         }
9254         break;
9255     }
9256 }
9257 
9258 static void
nautilus_files_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)9259 nautilus_files_view_set_property (GObject      *object,
9260                                   guint         prop_id,
9261                                   const GValue *value,
9262                                   GParamSpec   *pspec)
9263 {
9264     NautilusFilesView *directory_view;
9265     NautilusFilesViewPrivate *priv;
9266     NautilusWindowSlot *slot;
9267 
9268     directory_view = NAUTILUS_FILES_VIEW (object);
9269     priv = nautilus_files_view_get_instance_private (directory_view);
9270 
9271     switch (prop_id)
9272     {
9273         case PROP_WINDOW_SLOT:
9274         {
9275             g_assert (priv->slot == NULL);
9276 
9277             slot = NAUTILUS_WINDOW_SLOT (g_value_get_object (value));
9278             priv->slot = slot;
9279 
9280             g_signal_connect_object (priv->slot,
9281                                      "notify::active", G_CALLBACK (slot_active_changed),
9282                                      directory_view, 0);
9283         }
9284         break;
9285 
9286         case PROP_SUPPORTS_ZOOMING:
9287         {
9288             priv->supports_zooming = g_value_get_boolean (value);
9289         }
9290         break;
9291 
9292         case PROP_LOCATION:
9293         {
9294             nautilus_view_set_location (NAUTILUS_VIEW (directory_view), g_value_get_object (value));
9295         }
9296         break;
9297 
9298         case PROP_SEARCH_QUERY:
9299         {
9300             nautilus_view_set_search_query (NAUTILUS_VIEW (directory_view), g_value_get_object (value));
9301         }
9302         break;
9303 
9304         case PROP_SELECTION:
9305         {
9306             nautilus_view_set_selection (NAUTILUS_VIEW (directory_view), g_value_get_pointer (value));
9307         }
9308         break;
9309 
9310         case PROP_EXTENSIONS_BACKGROUND_MENU:
9311         {
9312             real_set_extensions_background_menu (NAUTILUS_VIEW (directory_view),
9313                                                  g_value_get_object (value));
9314         }
9315         break;
9316 
9317         case PROP_TEMPLATES_MENU:
9318         {
9319             real_set_templates_menu (NAUTILUS_VIEW (directory_view),
9320                                      g_value_get_object (value));
9321         }
9322         break;
9323 
9324         default:
9325         {
9326             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
9327         }
9328         break;
9329     }
9330 }
9331 
9332 /* handle Ctrl+Scroll, which will cause a zoom-in/out */
9333 static gboolean
on_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)9334 on_event (GtkWidget *widget,
9335           GdkEvent  *event,
9336           gpointer   user_data)
9337 {
9338     NautilusFilesView *directory_view;
9339     static gdouble total_delta_y = 0;
9340     GdkModifierType state;
9341     GdkScrollDirection direction;
9342     gdouble delta_x, delta_y;
9343 
9344     directory_view = NAUTILUS_FILES_VIEW (widget);
9345 
9346     if (gdk_event_get_event_type (event) != GDK_SCROLL)
9347     {
9348         return GDK_EVENT_PROPAGATE;
9349     }
9350 
9351     if (!gdk_event_get_state (event, &state))
9352     {
9353         return GDK_EVENT_PROPAGATE;
9354     }
9355 
9356     if (!(state & GDK_CONTROL_MASK))
9357     {
9358         return GDK_EVENT_PROPAGATE;
9359     }
9360 
9361     if (gdk_event_get_scroll_direction (event, &direction))
9362     {
9363         if (direction == GDK_SCROLL_UP)
9364         {
9365             /* Zoom In */
9366             nautilus_files_view_bump_zoom_level (directory_view, 1);
9367             return GDK_EVENT_STOP;
9368         }
9369         else if (direction == GDK_SCROLL_DOWN)
9370         {
9371             /* Zoom Out */
9372             nautilus_files_view_bump_zoom_level (directory_view, -1);
9373             return GDK_EVENT_STOP;
9374         }
9375     }
9376 
9377     if (gdk_event_get_scroll_deltas (event, &delta_x, &delta_y))
9378     {
9379         /* try to emulate a normal scrolling event by summing deltas */
9380         total_delta_y += delta_y;
9381 
9382         if (total_delta_y >= 1)
9383         {
9384             total_delta_y = 0;
9385             /* emulate scroll down */
9386             nautilus_files_view_bump_zoom_level (directory_view, -1);
9387             return GDK_EVENT_STOP;
9388         }
9389         else if (total_delta_y <= -1)
9390         {
9391             total_delta_y = 0;
9392             /* emulate scroll up */
9393             nautilus_files_view_bump_zoom_level (directory_view, 1);
9394             return GDK_EVENT_STOP;
9395         }
9396         else
9397         {
9398             /* eat event */
9399             return GDK_EVENT_STOP;
9400         }
9401     }
9402 
9403     return GDK_EVENT_PROPAGATE;
9404 }
9405 
9406 static void
action_reload_enabled_changed(GActionGroup * action_group,gchar * action_name,gboolean enabled,NautilusFilesView * view)9407 action_reload_enabled_changed (GActionGroup      *action_group,
9408                                gchar             *action_name,
9409                                gboolean           enabled,
9410                                NautilusFilesView *view)
9411 {
9412     NautilusFilesViewPrivate *priv;
9413 
9414     priv = nautilus_files_view_get_instance_private (view);
9415 
9416     gtk_widget_set_visible (priv->reload, enabled);
9417 }
9418 
9419 static void
action_stop_enabled_changed(GActionGroup * action_group,gchar * action_name,gboolean enabled,NautilusFilesView * view)9420 action_stop_enabled_changed (GActionGroup      *action_group,
9421                              gchar             *action_name,
9422                              gboolean           enabled,
9423                              NautilusFilesView *view)
9424 {
9425     NautilusFilesViewPrivate *priv;
9426 
9427     priv = nautilus_files_view_get_instance_private (view);
9428 
9429     gtk_widget_set_visible (priv->stop, enabled);
9430 }
9431 
9432 static void
nautilus_files_view_parent_set(GtkWidget * widget,GtkWidget * old_parent)9433 nautilus_files_view_parent_set (GtkWidget *widget,
9434                                 GtkWidget *old_parent)
9435 {
9436     NautilusWindow *window;
9437     NautilusFilesView *view;
9438     NautilusFilesViewPrivate *priv;
9439     GtkWidget *parent;
9440 
9441     view = NAUTILUS_FILES_VIEW (widget);
9442     priv = nautilus_files_view_get_instance_private (view);
9443 
9444     parent = gtk_widget_get_parent (widget);
9445     window = nautilus_files_view_get_window (view);
9446     g_assert (parent == NULL || old_parent == NULL);
9447 
9448     if (GTK_WIDGET_CLASS (nautilus_files_view_parent_class)->parent_set != NULL)
9449     {
9450         GTK_WIDGET_CLASS (nautilus_files_view_parent_class)->parent_set (widget, old_parent);
9451     }
9452 
9453     if (priv->stop_signal_handler > 0)
9454     {
9455         g_signal_handler_disconnect (window, priv->stop_signal_handler);
9456         priv->stop_signal_handler = 0;
9457     }
9458 
9459     if (priv->reload_signal_handler > 0)
9460     {
9461         g_signal_handler_disconnect (window, priv->reload_signal_handler);
9462         priv->reload_signal_handler = 0;
9463     }
9464 
9465     if (parent != NULL)
9466     {
9467         g_assert (old_parent == NULL);
9468 
9469         if (priv->slot == nautilus_window_get_active_slot (window))
9470         {
9471             priv->active = TRUE;
9472             gtk_widget_insert_action_group (GTK_WIDGET (nautilus_files_view_get_window (view)),
9473                                             "view",
9474                                             G_ACTION_GROUP (priv->view_action_group));
9475         }
9476 
9477         priv->stop_signal_handler =
9478             g_signal_connect (window,
9479                               "action-enabled-changed::stop",
9480                               G_CALLBACK (action_stop_enabled_changed),
9481                               view);
9482         priv->reload_signal_handler =
9483             g_signal_connect (window,
9484                               "action-enabled-changed::reload",
9485                               G_CALLBACK (action_reload_enabled_changed),
9486                               view);
9487     }
9488     else
9489     {
9490         remove_update_context_menus_timeout_callback (view);
9491         /* Only remove the action group if it matchs the current view
9492          * action group. If not, we can remove an action group set by
9493          * a different view i.e. if the slot_active function is called
9494          * before this one
9495          */
9496         if (gtk_widget_get_action_group (GTK_WIDGET (window), "view") ==
9497             priv->view_action_group)
9498         {
9499             gtk_widget_insert_action_group (GTK_WIDGET (nautilus_files_view_get_window (view)),
9500                                             "view",
9501                                             NULL);
9502         }
9503     }
9504 }
9505 
9506 static gboolean
nautilus_files_view_event(GtkWidget * widget,GdkEvent * event)9507 nautilus_files_view_event (GtkWidget *widget,
9508                            GdkEvent  *event)
9509 {
9510     NautilusFilesView *view;
9511     NautilusFilesViewPrivate *priv;
9512     guint keyval;
9513 
9514     if (gdk_event_get_event_type (event) != GDK_KEY_PRESS)
9515     {
9516         return GDK_EVENT_PROPAGATE;
9517     }
9518 
9519     view = NAUTILUS_FILES_VIEW (widget);
9520     priv = nautilus_files_view_get_instance_private (view);
9521 
9522     if (G_UNLIKELY (!gdk_event_get_keyval (event, &keyval)))
9523     {
9524         g_return_val_if_reached (GDK_EVENT_PROPAGATE);
9525     }
9526 
9527     for (gint i = 0; i < G_N_ELEMENTS (extra_view_keybindings); i++)
9528     {
9529         if (extra_view_keybindings[i].keyval == keyval)
9530         {
9531             GAction *action;
9532 
9533             action = g_action_map_lookup_action (G_ACTION_MAP (priv->view_action_group),
9534                                                  extra_view_keybindings[i].action);
9535 
9536             if (g_action_get_enabled (action))
9537             {
9538                 g_action_activate (action, NULL);
9539                 return GDK_EVENT_STOP;
9540             }
9541 
9542             break;
9543         }
9544     }
9545 
9546     return GDK_EVENT_PROPAGATE;
9547 }
9548 
9549 static NautilusQuery *
nautilus_files_view_get_search_query(NautilusView * view)9550 nautilus_files_view_get_search_query (NautilusView *view)
9551 {
9552     NautilusFilesViewPrivate *priv;
9553 
9554     priv = nautilus_files_view_get_instance_private (NAUTILUS_FILES_VIEW (view));
9555 
9556     return priv->search_query;
9557 }
9558 
9559 static void
set_search_query_internal(NautilusFilesView * files_view,NautilusQuery * query,NautilusDirectory * base_model)9560 set_search_query_internal (NautilusFilesView *files_view,
9561                            NautilusQuery     *query,
9562                            NautilusDirectory *base_model)
9563 {
9564     GFile *location;
9565     NautilusFilesViewPrivate *priv;
9566 
9567     location = NULL;
9568     priv = nautilus_files_view_get_instance_private (files_view);
9569 
9570     g_set_object (&priv->search_query, query);
9571     g_object_notify (G_OBJECT (files_view), "search-query");
9572 
9573     if (!nautilus_query_is_empty (query))
9574     {
9575         if (nautilus_view_is_searching (NAUTILUS_VIEW (files_view)))
9576         {
9577             /*
9578              * Reuse the search directory and reload it.
9579              */
9580             nautilus_search_directory_set_query (NAUTILUS_SEARCH_DIRECTORY (priv->model), query);
9581             /* It's important to use load_directory instead of set_location,
9582              * since the location is already correct, however we need
9583              * to reload the directory with the new query set. But
9584              * set_location has a check for wheter the location is a
9585              * search directory, so setting the location to a search
9586              * directory when is already serching will enter a loop.
9587              */
9588             load_directory (files_view, priv->model);
9589         }
9590         else
9591         {
9592             NautilusDirectory *directory;
9593             gchar *uri;
9594 
9595             uri = nautilus_search_directory_generate_new_uri ();
9596             location = g_file_new_for_uri (uri);
9597 
9598             directory = nautilus_directory_get (location);
9599             g_assert (NAUTILUS_IS_SEARCH_DIRECTORY (directory));
9600             nautilus_search_directory_set_base_model (NAUTILUS_SEARCH_DIRECTORY (directory), base_model);
9601             nautilus_search_directory_set_query (NAUTILUS_SEARCH_DIRECTORY (directory), query);
9602 
9603             load_directory (files_view, directory);
9604 
9605             g_object_notify (G_OBJECT (files_view), "searching");
9606 
9607             nautilus_directory_unref (directory);
9608             g_free (uri);
9609         }
9610     }
9611     else
9612     {
9613         if (nautilus_view_is_searching (NAUTILUS_VIEW (files_view)))
9614         {
9615             location = nautilus_directory_get_location (base_model);
9616 
9617             nautilus_view_set_location (NAUTILUS_VIEW (files_view), location);
9618         }
9619     }
9620     g_clear_object (&location);
9621 }
9622 
9623 static void
nautilus_files_view_set_search_query(NautilusView * view,NautilusQuery * query)9624 nautilus_files_view_set_search_query (NautilusView  *view,
9625                                       NautilusQuery *query)
9626 {
9627     NautilusDirectory *base_model;
9628     NautilusFilesView *files_view;
9629     NautilusFilesViewPrivate *priv;
9630 
9631     files_view = NAUTILUS_FILES_VIEW (view);
9632     priv = nautilus_files_view_get_instance_private (files_view);
9633 
9634     if (nautilus_view_is_searching (view))
9635     {
9636         base_model = nautilus_search_directory_get_base_model (NAUTILUS_SEARCH_DIRECTORY (priv->model));
9637     }
9638     else
9639     {
9640         base_model = priv->model;
9641     }
9642 
9643     set_search_query_internal (NAUTILUS_FILES_VIEW (view), query, base_model);
9644 }
9645 
9646 static GFile *
nautilus_files_view_get_location(NautilusView * view)9647 nautilus_files_view_get_location (NautilusView *view)
9648 {
9649     NautilusFilesViewPrivate *priv;
9650     NautilusFilesView *files_view;
9651 
9652     files_view = NAUTILUS_FILES_VIEW (view);
9653     priv = nautilus_files_view_get_instance_private (files_view);
9654 
9655     return priv->location;
9656 }
9657 
9658 static gboolean
nautilus_files_view_is_loading(NautilusView * view)9659 nautilus_files_view_is_loading (NautilusView *view)
9660 {
9661     NautilusFilesViewPrivate *priv;
9662     NautilusFilesView *files_view;
9663 
9664     files_view = NAUTILUS_FILES_VIEW (view);
9665     priv = nautilus_files_view_get_instance_private (files_view);
9666 
9667     return priv->loading;
9668 }
9669 
9670 static void
nautilus_files_view_iface_init(NautilusViewInterface * iface)9671 nautilus_files_view_iface_init (NautilusViewInterface *iface)
9672 {
9673     iface->get_location = nautilus_files_view_get_location;
9674     iface->set_location = nautilus_files_view_set_location;
9675     iface->get_selection = nautilus_files_view_get_selection;
9676     iface->set_selection = nautilus_files_view_set_selection;
9677     iface->get_search_query = nautilus_files_view_get_search_query;
9678     iface->set_search_query = nautilus_files_view_set_search_query;
9679     iface->get_toolbar_menu_sections = nautilus_files_view_get_toolbar_menu_sections;
9680     iface->is_searching = nautilus_files_view_is_searching;
9681     iface->is_loading = nautilus_files_view_is_loading;
9682     iface->get_view_id = nautilus_files_view_get_view_id;
9683     iface->get_templates_menu = nautilus_files_view_get_templates_menu;
9684     iface->set_templates_menu = nautilus_files_view_set_templates_menu;
9685     iface->get_extensions_background_menu = nautilus_files_view_get_extensions_background_menu;
9686     iface->set_extensions_background_menu = nautilus_files_view_set_extensions_background_menu;
9687 }
9688 
9689 static void
nautilus_files_view_class_init(NautilusFilesViewClass * klass)9690 nautilus_files_view_class_init (NautilusFilesViewClass *klass)
9691 {
9692     GObjectClass *oclass;
9693     GtkWidgetClass *widget_class;
9694 
9695     widget_class = GTK_WIDGET_CLASS (klass);
9696     oclass = G_OBJECT_CLASS (klass);
9697 
9698     oclass->finalize = nautilus_files_view_finalize;
9699     oclass->get_property = nautilus_files_view_get_property;
9700     oclass->set_property = nautilus_files_view_set_property;
9701 
9702     widget_class->destroy = nautilus_files_view_destroy;
9703     widget_class->event = nautilus_files_view_event;
9704     widget_class->parent_set = nautilus_files_view_parent_set;
9705     widget_class->grab_focus = nautilus_files_view_grab_focus;
9706 
9707 
9708     signals[ADD_FILES] =
9709         g_signal_new ("add-files",
9710                       G_TYPE_FROM_CLASS (klass),
9711                       G_SIGNAL_RUN_LAST,
9712                       G_STRUCT_OFFSET (NautilusFilesViewClass, add_files),
9713                       NULL, NULL,
9714                       g_cclosure_marshal_generic,
9715                       G_TYPE_NONE, 1, G_TYPE_POINTER);
9716     signals[BEGIN_FILE_CHANGES] =
9717         g_signal_new ("begin-file-changes",
9718                       G_TYPE_FROM_CLASS (klass),
9719                       G_SIGNAL_RUN_LAST,
9720                       G_STRUCT_OFFSET (NautilusFilesViewClass, begin_file_changes),
9721                       NULL, NULL,
9722                       g_cclosure_marshal_VOID__VOID,
9723                       G_TYPE_NONE, 0);
9724     signals[BEGIN_LOADING] =
9725         g_signal_new ("begin-loading",
9726                       G_TYPE_FROM_CLASS (klass),
9727                       G_SIGNAL_RUN_LAST,
9728                       G_STRUCT_OFFSET (NautilusFilesViewClass, begin_loading),
9729                       NULL, NULL,
9730                       g_cclosure_marshal_VOID__VOID,
9731                       G_TYPE_NONE, 0);
9732     signals[CLEAR] =
9733         g_signal_new ("clear",
9734                       G_TYPE_FROM_CLASS (klass),
9735                       G_SIGNAL_RUN_LAST,
9736                       G_STRUCT_OFFSET (NautilusFilesViewClass, clear),
9737                       NULL, NULL,
9738                       g_cclosure_marshal_VOID__VOID,
9739                       G_TYPE_NONE, 0);
9740     signals[END_FILE_CHANGES] =
9741         g_signal_new ("end-file-changes",
9742                       G_TYPE_FROM_CLASS (klass),
9743                       G_SIGNAL_RUN_LAST,
9744                       G_STRUCT_OFFSET (NautilusFilesViewClass, end_file_changes),
9745                       NULL, NULL,
9746                       g_cclosure_marshal_VOID__VOID,
9747                       G_TYPE_NONE, 0);
9748     signals[END_LOADING] =
9749         g_signal_new ("end-loading",
9750                       G_TYPE_FROM_CLASS (klass),
9751                       G_SIGNAL_RUN_LAST,
9752                       G_STRUCT_OFFSET (NautilusFilesViewClass, end_loading),
9753                       NULL, NULL,
9754                       g_cclosure_marshal_VOID__BOOLEAN,
9755                       G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
9756     signals[FILE_CHANGED] =
9757         g_signal_new ("file-changed",
9758                       G_TYPE_FROM_CLASS (klass),
9759                       G_SIGNAL_RUN_LAST,
9760                       G_STRUCT_OFFSET (NautilusFilesViewClass, file_changed),
9761                       NULL, NULL,
9762                       g_cclosure_marshal_generic,
9763                       G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY);
9764     signals[REMOVE_FILE] =
9765         g_signal_new ("remove-file",
9766                       G_TYPE_FROM_CLASS (klass),
9767                       G_SIGNAL_RUN_LAST,
9768                       G_STRUCT_OFFSET (NautilusFilesViewClass, remove_file),
9769                       NULL, NULL,
9770                       g_cclosure_marshal_generic,
9771                       G_TYPE_NONE, 2, NAUTILUS_TYPE_FILE, NAUTILUS_TYPE_DIRECTORY);
9772     signals[SELECTION_CHANGED] =
9773         g_signal_new ("selection-changed",
9774                       G_TYPE_FROM_CLASS (klass),
9775                       G_SIGNAL_RUN_LAST,
9776                       0,
9777                       NULL, NULL,
9778                       g_cclosure_marshal_VOID__VOID,
9779                       G_TYPE_NONE, 0);
9780 
9781     klass->get_backing_uri = real_get_backing_uri;
9782     klass->get_window = nautilus_files_view_get_window;
9783     klass->update_context_menus = real_update_context_menus;
9784     klass->update_actions_state = real_update_actions_state;
9785     klass->check_empty_states = real_check_empty_states;
9786 
9787     g_object_class_install_property (
9788         oclass,
9789         PROP_WINDOW_SLOT,
9790         g_param_spec_object ("window-slot",
9791                              "Window Slot",
9792                              "The parent window slot reference",
9793                              NAUTILUS_TYPE_WINDOW_SLOT,
9794                              G_PARAM_WRITABLE |
9795                              G_PARAM_CONSTRUCT_ONLY));
9796     g_object_class_install_property (
9797         oclass,
9798         PROP_SUPPORTS_ZOOMING,
9799         g_param_spec_boolean ("supports-zooming",
9800                               "Supports zooming",
9801                               "Whether the view supports zooming",
9802                               TRUE,
9803                               G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY |
9804                               G_PARAM_STATIC_STRINGS));
9805 
9806     g_object_class_override_property (oclass, PROP_LOADING, "loading");
9807     g_object_class_override_property (oclass, PROP_SEARCHING, "searching");
9808     g_object_class_override_property (oclass, PROP_LOCATION, "location");
9809     g_object_class_override_property (oclass, PROP_SELECTION, "selection");
9810     g_object_class_override_property (oclass, PROP_SEARCH_QUERY, "search-query");
9811     g_object_class_override_property (oclass, PROP_EXTENSIONS_BACKGROUND_MENU, "extensions-background-menu");
9812     g_object_class_override_property (oclass, PROP_TEMPLATES_MENU, "templates-menu");
9813 }
9814 
9815 static void
nautilus_files_view_init(NautilusFilesView * view)9816 nautilus_files_view_init (NautilusFilesView *view)
9817 {
9818     NautilusFilesViewPrivate *priv;
9819     GtkBuilder *builder;
9820     AtkObject *atk_object;
9821     NautilusDirectory *scripts_directory;
9822     NautilusDirectory *templates_directory;
9823     gchar *templates_uri;
9824     GtkClipboard *clipboard;
9825     GApplication *app;
9826     const gchar *open_accels[] =
9827     {
9828         "Return",
9829         "KP_Enter",
9830         "<control>o",
9831         "<alt>Down",
9832         NULL
9833     };
9834     const gchar *open_properties[] =
9835     {
9836         "<control>i",
9837         "<alt>Return",
9838         NULL
9839     };
9840     const gchar *zoom_in_accels[] =
9841     {
9842         "<control>equal",
9843         "<control>plus",
9844         "<control>KP_Add",
9845         "ZoomIn",
9846         NULL
9847     };
9848     const gchar *zoom_out_accels[] =
9849     {
9850         "<control>minus",
9851         "<control>KP_Subtract",
9852         "ZoomOut",
9853         NULL
9854     };
9855     const gchar *zoom_standard_accels[] =
9856     {
9857         "<control>0",
9858         "<control>KP_0",
9859         NULL
9860     };
9861     const gchar *move_to_trash_accels[] =
9862     {
9863         "Delete",
9864         "KP_Delete",
9865         NULL
9866     };
9867     const gchar *delete_permanently_accels[] =
9868     {
9869         "<shift>Delete",
9870         "<shift>KP_Delete",
9871         NULL
9872     };
9873 
9874     nautilus_profile_start (NULL);
9875 
9876     priv = nautilus_files_view_get_instance_private (view);
9877 
9878     /* Toolbar menu */
9879     builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-toolbar-view-menu.ui");
9880     priv->toolbar_menu_sections = g_new0 (NautilusToolbarMenuSections, 1);
9881     priv->toolbar_menu_sections->supports_undo_redo = TRUE;
9882     priv->toolbar_menu_sections->zoom_section = GTK_WIDGET (g_object_ref_sink (gtk_builder_get_object (builder, "zoom_section")));
9883     priv->toolbar_menu_sections->extended_section = GTK_WIDGET (g_object_ref_sink (gtk_builder_get_object (builder, "extended_section")));
9884     priv->zoom_controls_box = GTK_WIDGET (gtk_builder_get_object (builder, "zoom_controls_box"));
9885     priv->zoom_level_label = GTK_WIDGET (gtk_builder_get_object (builder, "zoom_level_label"));
9886 
9887     priv->sort_menu = GTK_WIDGET (gtk_builder_get_object (builder, "sort_menu"));
9888     priv->sort_trash_time = GTK_WIDGET (gtk_builder_get_object (builder, "sort_trash_time"));
9889     priv->visible_columns = GTK_WIDGET (gtk_builder_get_object (builder, "visible_columns"));
9890     priv->reload = GTK_WIDGET (gtk_builder_get_object (builder, "reload"));
9891     priv->stop = GTK_WIDGET (gtk_builder_get_object (builder, "stop"));
9892 
9893     g_signal_connect (view,
9894                       "end-file-changes",
9895                       G_CALLBACK (on_end_file_changes),
9896                       view);
9897     g_signal_connect (view,
9898                       "notify::selection",
9899                       G_CALLBACK (nautilus_files_view_preview_update),
9900                       view);
9901 
9902     g_object_unref (builder);
9903 
9904     /* Main widgets */
9905     gtk_orientable_set_orientation (GTK_ORIENTABLE (view), GTK_ORIENTATION_VERTICAL);
9906     priv->overlay = gtk_overlay_new ();
9907     gtk_widget_set_vexpand (priv->overlay, TRUE);
9908     gtk_widget_set_hexpand (priv->overlay, TRUE);
9909     gtk_container_add (GTK_CONTAINER (view), priv->overlay);
9910     gtk_widget_show (priv->overlay);
9911 
9912     /* NautilusFloatingBar listen to its parent's 'event' signal
9913      * and GtkOverlay doesn't have it enabled by default, so we have to add them
9914      * here.
9915      */
9916     gtk_widget_add_events (GTK_WIDGET (priv->overlay),
9917                            GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
9918 
9919     /* Scrolled Window */
9920     priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
9921     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
9922                                     GTK_POLICY_AUTOMATIC,
9923                                     GTK_POLICY_AUTOMATIC);
9924     gtk_widget_show (priv->scrolled_window);
9925 
9926     g_signal_connect_swapped (priv->scrolled_window,
9927                               "event",
9928                               G_CALLBACK (on_event),
9929                               view);
9930     g_signal_connect_swapped (priv->scrolled_window,
9931                               "popup-menu",
9932                               G_CALLBACK (popup_menu_callback),
9933                               view);
9934 
9935     gtk_container_add (GTK_CONTAINER (priv->overlay), priv->scrolled_window);
9936 
9937     /* Empty states */
9938     builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-no-search-results.ui");
9939     priv->no_search_results_widget = GTK_WIDGET (gtk_builder_get_object (builder, "no_search_results"));
9940     gtk_overlay_add_overlay (GTK_OVERLAY (priv->overlay), priv->no_search_results_widget);
9941     gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (priv->overlay),
9942                                           priv->no_search_results_widget,
9943                                           TRUE);
9944     g_object_unref (builder);
9945 
9946     builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-folder-is-empty.ui");
9947     priv->folder_is_empty_widget = GTK_WIDGET (gtk_builder_get_object (builder, "folder_is_empty"));
9948     gtk_overlay_add_overlay (GTK_OVERLAY (priv->overlay), priv->folder_is_empty_widget);
9949     gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (priv->overlay),
9950                                           priv->folder_is_empty_widget,
9951                                           TRUE);
9952     g_object_unref (builder);
9953 
9954     builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-starred-is-empty.ui");
9955     priv->starred_is_empty_widget = GTK_WIDGET (gtk_builder_get_object (builder, "starred_is_empty"));
9956     gtk_overlay_add_overlay (GTK_OVERLAY (priv->overlay), priv->starred_is_empty_widget);
9957     gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (priv->overlay),
9958                                           priv->starred_is_empty_widget,
9959                                           TRUE);
9960     g_object_unref (builder);
9961 
9962     builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/ui/nautilus-trash-is-empty.ui");
9963     priv->trash_is_empty_widget = GTK_WIDGET (gtk_builder_get_object (builder, "trash_is_empty"));
9964     gtk_overlay_add_overlay (GTK_OVERLAY (priv->overlay), priv->trash_is_empty_widget);
9965     gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (priv->overlay),
9966                                           priv->trash_is_empty_widget,
9967                                           TRUE);
9968     g_object_unref (builder);
9969 
9970     /* Floating bar */
9971     priv->floating_bar = nautilus_floating_bar_new (NULL, NULL, FALSE);
9972     gtk_widget_set_halign (priv->floating_bar, GTK_ALIGN_END);
9973     gtk_widget_set_valign (priv->floating_bar, GTK_ALIGN_END);
9974     gtk_overlay_add_overlay (GTK_OVERLAY (priv->overlay), priv->floating_bar);
9975 
9976     g_signal_connect (priv->floating_bar,
9977                       "action",
9978                       G_CALLBACK (floating_bar_action_cb),
9979                       view);
9980 
9981     priv->non_ready_files =
9982         g_hash_table_new_full (file_and_directory_hash,
9983                                file_and_directory_equal,
9984                                file_and_directory_free,
9985                                NULL);
9986 
9987     priv->pending_reveal = g_hash_table_new (NULL, NULL);
9988 
9989     gtk_style_context_set_junction_sides (gtk_widget_get_style_context (GTK_WIDGET (view)),
9990                                           GTK_JUNCTION_TOP | GTK_JUNCTION_LEFT);
9991 
9992     if (set_up_scripts_directory_global ())
9993     {
9994         scripts_directory = nautilus_directory_get_by_uri (scripts_directory_uri);
9995         add_directory_to_scripts_directory_list (view, scripts_directory);
9996         nautilus_directory_unref (scripts_directory);
9997     }
9998     else
9999     {
10000         g_warning ("Ignoring scripts directory, it may be a broken link\n");
10001     }
10002 
10003     if (nautilus_should_use_templates_directory ())
10004     {
10005         templates_uri = nautilus_get_templates_directory_uri ();
10006         templates_directory = nautilus_directory_get_by_uri (templates_uri);
10007         g_free (templates_uri);
10008         add_directory_to_templates_directory_list (view, templates_directory);
10009         nautilus_directory_unref (templates_directory);
10010     }
10011     update_templates_directory (view);
10012 
10013     priv->sort_directories_first =
10014         g_settings_get_boolean (gtk_filechooser_preferences, NAUTILUS_PREFERENCES_SORT_DIRECTORIES_FIRST);
10015     priv->show_hidden_files =
10016         g_settings_get_boolean (gtk_filechooser_preferences, NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES);
10017 
10018     g_signal_connect_object (nautilus_trash_monitor_get (), "trash-state-changed",
10019                              G_CALLBACK (nautilus_files_view_trash_state_changed_callback), view, 0);
10020 
10021     /* React to clipboard changes */
10022     clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
10023     g_signal_connect (clipboard, "owner-change",
10024                       G_CALLBACK (on_clipboard_owner_changed), view);
10025 
10026     /* Register to menu provider extension signal managing menu updates */
10027     g_signal_connect_object (nautilus_signaller_get_current (), "popup-menu-changed",
10028                              G_CALLBACK (schedule_update_context_menus), view, G_CONNECT_SWAPPED);
10029 
10030     gtk_widget_show (GTK_WIDGET (view));
10031 
10032     g_signal_connect_swapped (nautilus_preferences,
10033                               "changed::" NAUTILUS_PREFERENCES_CLICK_POLICY,
10034                               G_CALLBACK (click_policy_changed_callback),
10035                               view);
10036     g_signal_connect_swapped (gtk_filechooser_preferences,
10037                               "changed::" NAUTILUS_PREFERENCES_SORT_DIRECTORIES_FIRST,
10038                               G_CALLBACK (sort_directories_first_changed_callback), view);
10039     g_signal_connect_swapped (gtk_filechooser_preferences,
10040                               "changed::" NAUTILUS_PREFERENCES_SHOW_HIDDEN_FILES,
10041                               G_CALLBACK (show_hidden_files_changed_callback), view);
10042     g_signal_connect_swapped (gnome_lockdown_preferences,
10043                               "changed::" NAUTILUS_PREFERENCES_LOCKDOWN_COMMAND_LINE,
10044                               G_CALLBACK (schedule_update_context_menus), view);
10045 
10046     priv->in_destruction = FALSE;
10047 
10048     /* Accessibility */
10049     atk_object = gtk_widget_get_accessible (GTK_WIDGET (view));
10050     atk_object_set_name (atk_object, _("Content View"));
10051     atk_object_set_description (atk_object, _("View of the current folder"));
10052 
10053     priv->view_action_group = G_ACTION_GROUP (g_simple_action_group_new ());
10054     g_action_map_add_action_entries (G_ACTION_MAP (priv->view_action_group),
10055                                      view_entries,
10056                                      G_N_ELEMENTS (view_entries),
10057                                      view);
10058     gtk_widget_insert_action_group (GTK_WIDGET (view),
10059                                     "view",
10060                                     G_ACTION_GROUP (priv->view_action_group));
10061     app = g_application_get_default ();
10062 
10063     /* Toolbar menu */
10064     nautilus_application_set_accelerators (app, "view.zoom-in", zoom_in_accels);
10065     nautilus_application_set_accelerators (app, "view.zoom-out", zoom_out_accels);
10066     nautilus_application_set_accelerator (app, "view.show-hidden-files", "<control>h");
10067     /* Background menu */
10068     nautilus_application_set_accelerator (app, "view.select-all", "<control>a");
10069     nautilus_application_set_accelerator (app, "view.paste_accel", "<control>v");
10070     nautilus_application_set_accelerator (app, "view.create-link", "<control>m");
10071     /* Selection menu */
10072     nautilus_application_set_accelerators (app, "view.open-with-default-application", open_accels);
10073     nautilus_application_set_accelerator (app, "view.open-item-new-tab", "<control>Return");
10074     nautilus_application_set_accelerator (app, "view.open-item-new-window", "<Shift>Return");
10075     nautilus_application_set_accelerators (app, "view.move-to-trash", move_to_trash_accels);
10076     nautilus_application_set_accelerators (app, "view.delete-from-trash", move_to_trash_accels);
10077     nautilus_application_set_accelerators (app, "view.delete-permanently-shortcut", delete_permanently_accels);
10078     /* When trash is not available, allow the "Delete" keys to delete permanently, that is, when
10079      * the menu item is available, since we never make both the trash and delete-permanently-menu-item
10080      * actions active */
10081     nautilus_application_set_accelerators (app, "view.delete-permanently-menu-item", move_to_trash_accels);
10082     nautilus_application_set_accelerators (app, "view.permanent-delete-permanently-menu-item", delete_permanently_accels);
10083     nautilus_application_set_accelerators (app, "view.properties", open_properties);
10084     nautilus_application_set_accelerator (app, "view.open-item-location", "<control><alt>o");
10085     nautilus_application_set_accelerator (app, "view.rename", "F2");
10086     nautilus_application_set_accelerator (app, "view.cut", "<control>x");
10087     nautilus_application_set_accelerator (app, "view.copy", "<control>c");
10088     nautilus_application_set_accelerator (app, "view.create-link-in-place", "<control><shift>m");
10089     nautilus_application_set_accelerator (app, "view.new-folder", "<control><shift>n");
10090     /* Only accesible by shorcuts */
10091     nautilus_application_set_accelerator (app, "view.select-pattern", "<control>s");
10092     nautilus_application_set_accelerators (app, "view.zoom-standard", zoom_standard_accels);
10093     nautilus_application_set_accelerator (app, "view.invert-selection", "<shift><control>i");
10094 
10095     priv->starred_cancellable = g_cancellable_new ();
10096     priv->tag_manager = nautilus_tag_manager_get ();
10097 
10098     priv->rename_file_controller = nautilus_rename_file_popover_controller_new ();
10099 
10100     nautilus_profile_end (NULL);
10101 }
10102 
10103 NautilusFilesView *
nautilus_files_view_new(guint id,NautilusWindowSlot * slot)10104 nautilus_files_view_new (guint               id,
10105                          NautilusWindowSlot *slot)
10106 {
10107     NautilusFilesView *view = NULL;
10108     gboolean use_experimental_views;
10109 
10110     use_experimental_views = g_settings_get_boolean (nautilus_preferences,
10111                                                      NAUTILUS_PREFERENCES_USE_EXPERIMENTAL_VIEWS);
10112     switch (id)
10113     {
10114         case NAUTILUS_VIEW_GRID_ID:
10115         {
10116             if (use_experimental_views)
10117             {
10118                 view = NAUTILUS_FILES_VIEW (nautilus_view_icon_controller_new (slot));
10119             }
10120             else
10121             {
10122                 view = nautilus_canvas_view_new (slot);
10123             }
10124         }
10125         break;
10126 
10127         case NAUTILUS_VIEW_LIST_ID:
10128         {
10129             view = nautilus_list_view_new (slot);
10130         }
10131         break;
10132     }
10133 
10134     if (view == NULL)
10135     {
10136         g_critical ("Unknown view type ID: %d", id);
10137     }
10138     else if (g_object_is_floating (view))
10139     {
10140         g_object_ref_sink (view);
10141     }
10142 
10143     return view;
10144 }
10145