1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /* fm-directory-view.c
4  *
5  * Copyright (C) 1999, 2000  Free Software Foundation
6  * Copyright (C) 2000, 2001  Eazel, Inc.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public
19  * License along with this program; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  * Authors: Ettore Perazzoli,
24  *          John Sullivan <sullivan@eazel.com>,
25  *          Darin Adler <darin@bentspoon.com>,
26  *          Pavel Cisler <pavel@eazel.com>,
27  *          David Emory Watson <dwatson@cs.ucr.edu>
28  */
29 
30 #include <config.h>
31 #include <math.h>
32 
33 #include <gdk/gdkkeysyms.h>
34 #include <gdk/gdkx.h>
35 #include <gtk/gtk.h>
36 #include <glib/gi18n.h>
37 #include <glib/gstdio.h>
38 #include <gio/gio.h>
39 
40 #define MATE_DESKTOP_USE_UNSTABLE_API
41 #include <libmate-desktop/mate-desktop-utils.h>
42 
43 #include <eel/eel-background.h>
44 #include <eel/eel-glib-extensions.h>
45 #include <eel/eel-mate-extensions.h>
46 #include <eel/eel-gtk-extensions.h>
47 #include <eel/eel-gtk-macros.h>
48 #include <eel/eel-stock-dialogs.h>
49 #include <eel/eel-string.h>
50 #include <eel/eel-vfs-extensions.h>
51 
52 #include <libcaja-private/caja-recent.h>
53 #include <libcaja-extension/caja-menu-provider.h>
54 #include <libcaja-private/caja-clipboard.h>
55 #include <libcaja-private/caja-clipboard-monitor.h>
56 #include <libcaja-private/caja-debug-log.h>
57 #include <libcaja-private/caja-desktop-icon-file.h>
58 #include <libcaja-private/caja-desktop-directory.h>
59 #include <libcaja-private/caja-extensions.h>
60 #include <libcaja-private/caja-search-directory.h>
61 #include <libcaja-private/caja-directory-background.h>
62 #include <libcaja-private/caja-directory.h>
63 #include <libcaja-private/caja-dnd.h>
64 #include <libcaja-private/caja-file-attributes.h>
65 #include <libcaja-private/caja-file-changes-queue.h>
66 #include <libcaja-private/caja-file-dnd.h>
67 #include <libcaja-private/caja-file-operations.h>
68 #include <libcaja-private/caja-file-utilities.h>
69 #include <libcaja-private/caja-file-private.h> /* for caja_file_get_existing_by_uri */
70 #include <libcaja-private/caja-global-preferences.h>
71 #include <libcaja-private/caja-link.h>
72 #include <libcaja-private/caja-metadata.h>
73 #include <libcaja-private/caja-mime-actions.h>
74 #include <libcaja-private/caja-module.h>
75 #include <libcaja-private/caja-program-choosing.h>
76 #include <libcaja-private/caja-trash-monitor.h>
77 #include <libcaja-private/caja-ui-utilities.h>
78 #include <libcaja-private/caja-signaller.h>
79 #include <libcaja-private/caja-autorun.h>
80 #include <libcaja-private/caja-icon-names.h>
81 #include <libcaja-private/caja-undostack-manager.h>
82 
83 #include "fm-directory-view.h"
84 #include "fm-list-view.h"
85 #include "fm-desktop-icon-view.h"
86 #include "fm-actions.h"
87 #include "fm-error-reporting.h"
88 #include "fm-marshal.h"
89 #include "fm-properties-window.h"
90 #include "libcaja-private/caja-open-with-dialog.h"
91 
92 /* Minimum starting update inverval */
93 #define UPDATE_INTERVAL_MIN 100
94 /* Maximum update interval */
95 #define UPDATE_INTERVAL_MAX 2000
96 /* Amount of miliseconds the update interval is increased */
97 #define UPDATE_INTERVAL_INC 250
98 /* Interval at which the update interval is increased */
99 #define UPDATE_INTERVAL_TIMEOUT_INTERVAL 250
100 /* Milliseconds that have to pass without a change to reset the update interval */
101 #define UPDATE_INTERVAL_RESET 1000
102 
103 #define SILENT_WINDOW_OPEN_LIMIT 5
104 
105 #define DUPLICATE_HORIZONTAL_ICON_OFFSET 70
106 #define DUPLICATE_VERTICAL_ICON_OFFSET   30
107 
108 #define MAX_QUEUED_UPDATES 500
109 
110 #define FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER   	"/MenuBar/File/Open Placeholder/Open With/Applications Placeholder"
111 #define FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER    	"/MenuBar/File/Open Placeholder/Applications Placeholder"
112 #define FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER    		"/MenuBar/File/Open Placeholder/Scripts/Scripts Placeholder"
113 #define FM_DIRECTORY_VIEW_MENU_PATH_EXTENSION_ACTIONS_PLACEHOLDER       "/MenuBar/Edit/Extension Actions"
114 #define FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER  		"/MenuBar/File/New Items Placeholder/New Documents/New Documents Placeholder"
115 #define FM_DIRECTORY_VIEW_MENU_PATH_OPEN				"/MenuBar/File/Open Placeholder/Open"
116 
117 #define FM_DIRECTORY_VIEW_POPUP_PATH_SELECTION				"/selection"
118 #define FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER  	"/selection/Open Placeholder/Open With/Applications Placeholder"
119 #define FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER    	"/selection/Open Placeholder/Applications Placeholder"
120 #define FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER    		"/selection/Open Placeholder/Scripts/Scripts Placeholder"
121 #define FM_DIRECTORY_VIEW_POPUP_PATH_EXTENSION_ACTIONS			"/selection/Extension Actions"
122 #define FM_DIRECTORY_VIEW_POPUP_PATH_OPEN				"/selection/Open Placeholder/Open"
123 
124 #define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND				"/background"
125 #define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER	"/background/Before Zoom Items/New Object Items/Scripts/Scripts Placeholder"
126 #define FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER "/background/Before Zoom Items/New Object Items/New Documents/New Documents Placeholder"
127 
128 #define FM_DIRECTORY_VIEW_POPUP_PATH_LOCATION				"/location"
129 
130 #define MAX_MENU_LEVELS 5
131 #define TEMPLATE_LIMIT 30
132 
133 enum {
134 	ADD_FILE,
135 	BEGIN_FILE_CHANGES,
136 	BEGIN_LOADING,
137 	CLEAR,
138 	END_FILE_CHANGES,
139 	FLUSH_ADDED_FILES,
140 	END_LOADING,
141 	FILE_CHANGED,
142 	LOAD_ERROR,
143 	MOVE_COPY_ITEMS,
144 	REMOVE_FILE,
145 	TRASH,
146 	DELETE,
147 	LAST_SIGNAL
148 };
149 
150 enum
151 {
152   PROP_0,
153   PROP_WINDOW_SLOT
154 };
155 
156 
157 static guint signals[LAST_SIGNAL] = { 0 };
158 
159 static GdkAtom copied_files_atom;
160 
161 static char *scripts_directory_uri;
162 static int scripts_directory_uri_length;
163 
164 struct FMDirectoryViewDetails
165 {
166 	CajaWindowInfo *window;
167 	CajaWindowSlotInfo *slot;
168 	CajaDirectory *model;
169 	CajaFile *directory_as_file;
170 	CajaFile *location_popup_directory_as_file;
171 	GdkEventButton *location_popup_event;
172 	GtkActionGroup *dir_action_group;
173 	guint dir_merge_id;
174 
175 	GList *scripts_directory_list;
176 	GtkActionGroup *scripts_action_group;
177 	guint scripts_merge_id;
178 
179 	GList *templates_directory_list;
180 	GtkActionGroup *templates_action_group;
181 	guint templates_merge_id;
182 
183 	GtkActionGroup *extensions_menu_action_group;
184 	guint extensions_menu_merge_id;
185 
186 	guint display_selection_idle_id;
187 	guint update_menus_timeout_id;
188 	guint update_status_idle_id;
189 	guint reveal_selection_idle_id;
190 
191 	guint display_pending_source_id;
192 	guint changes_timeout_id;
193 
194 	guint update_interval;
195  	guint64 last_queued;
196 
197 	guint files_added_handler_id;
198 	guint files_changed_handler_id;
199 	guint load_error_handler_id;
200 	guint done_loading_handler_id;
201 	guint file_changed_handler_id;
202 
203 	guint delayed_rename_file_id;
204 
205 	GList *new_added_files;
206 	GList *new_changed_files;
207 
208 	GHashTable *non_ready_files;
209 
210 	GList *old_added_files;
211 	GList *old_changed_files;
212 
213 	GList *pending_locations_selected;
214 
215 	/* whether we are in the active slot */
216 	gboolean active;
217 
218 	/* loading indicates whether this view has begun loading a directory.
219 	 * This flag should need not be set inside subclasses. FMDirectoryView automatically
220 	 * sets 'loading' to TRUE before it begins loading a directory's contents and to FALSE
221 	 * after it finishes loading the directory and its view.
222 	 */
223 	gboolean loading;
224 	gboolean menu_states_untrustworthy;
225 	gboolean scripts_invalid;
226 	gboolean templates_invalid;
227 	gboolean reported_load_error;
228 
229 	/* flag to indicate that no file updates should be dispatched to subclasses.
230 	 * This is a workaround for bug #87701 that prevents the list view from
231 	 * losing focus when the underlying GtkTreeView is updated.
232 	 */
233 	gboolean updates_frozen;
234 	guint	 updates_queued;
235 	gboolean needs_reload;
236 
237 	gboolean sort_directories_first;
238 
239 	gboolean show_foreign_files;
240 	gboolean show_hidden_files;
241 	gboolean ignore_hidden_file_preferences;
242 
243 	gboolean show_backup_files;
244 
245 	gboolean batching_selection_level;
246 	gboolean selection_changed_while_batched;
247 
248 	gboolean selection_was_removed;
249 
250 	gboolean metadata_for_directory_as_file_pending;
251 	gboolean metadata_for_files_in_directory_pending;
252 
253 	gboolean selection_change_is_due_to_shell;
254 	gboolean send_selection_change_to_shell;
255 
256 	GtkActionGroup *open_with_action_group;
257 	guint open_with_merge_id;
258 
259 	GList *subdirectory_list;
260 
261 	gboolean allow_moves;
262 
263 	GdkPoint context_menu_position;
264 
265 	gboolean undo_active;
266 	gboolean redo_active;
267 	gchar* undo_action_description;
268 	gchar* undo_action_label;
269 	gchar* redo_action_description;
270 	gchar* redo_action_label;
271 };
272 
273 typedef struct {
274 	CajaFile *file;
275 	CajaDirectory *directory;
276 } FileAndDirectory;
277 
278 /* forward declarations */
279 
280 static gboolean display_selection_info_idle_callback           (gpointer              data);
281 static void     fm_directory_view_class_init                   (FMDirectoryViewClass *klass);
282 static void     fm_directory_view_init                         (FMDirectoryView      *view);
283 static void     fm_directory_view_duplicate_selection          (FMDirectoryView      *view,
284 								GList                *files,
285 								GArray               *item_locations);
286 static void     fm_directory_view_create_links_for_files       (FMDirectoryView      *view,
287 								GList                *files,
288 								GArray               *item_locations);
289 static void     trash_or_delete_files                          (GtkWindow            *parent_window,
290 								const GList          *files,
291 								gboolean              delete_if_all_already_in_trash,
292 								FMDirectoryView      *view);
293 static void     load_directory                                 (FMDirectoryView      *view,
294 								CajaDirectory    *directory);
295 static void     fm_directory_view_merge_menus                  (FMDirectoryView      *view);
296 static void     fm_directory_view_unmerge_menus                (FMDirectoryView      *view);
297 static void     fm_directory_view_init_show_hidden_files       (FMDirectoryView      *view);
298 static void     fm_directory_view_init_show_backup_files       (FMDirectoryView      *view);
299 static void     fm_directory_view_load_location                (CajaView         *caja_view,
300 								const char           *location);
301 static void     fm_directory_view_stop_loading                 (CajaView         *caja_view);
302 static void     fm_directory_view_drop_proxy_received_uris     (FMDirectoryView *view,
303 								const GList *source_uri_list,
304 								const char *target_uri,
305 								GdkDragAction action);
306 static void     fm_directory_view_drop_proxy_received_netscape_url (FMDirectoryView *view,
307 								    const char *netscape_url,
308 								    const char *target_uri,
309 								    GdkDragAction action);
310 static void     clipboard_changed_callback                     (CajaClipboardMonitor *monitor,
311 								FMDirectoryView      *view);
312 static void     open_one_in_new_window                         (gpointer              data,
313 								gpointer              callback_data);
314 static void     open_one_in_folder_window                      (gpointer              data,
315 								gpointer              callback_data);
316 static void     schedule_update_menus                          (FMDirectoryView      *view);
317 static void     schedule_update_menus_callback                 (gpointer              callback_data);
318 static void     remove_update_menus_timeout_callback           (FMDirectoryView      *view);
319 static void     schedule_update_status                          (FMDirectoryView      *view);
320 static void     remove_update_status_idle_callback             (FMDirectoryView *view);
321 static void     reset_update_interval                          (FMDirectoryView      *view);
322 static void     schedule_idle_display_of_pending_files         (FMDirectoryView      *view);
323 static void     unschedule_display_of_pending_files            (FMDirectoryView      *view);
324 static void     disconnect_model_handlers                      (FMDirectoryView      *view);
325 static void     metadata_for_directory_as_file_ready_callback  (CajaFile         *file,
326 								gpointer              callback_data);
327 static void     metadata_for_files_in_directory_ready_callback (CajaDirectory    *directory,
328 								GList                *files,
329 								gpointer              callback_data);
330 static void     fm_directory_view_trash_state_changed_callback (CajaTrashMonitor *trash,
331 								gboolean              state,
332 								gpointer              callback_data);
333 static void     fm_directory_view_select_file                  (FMDirectoryView      *view,
334 								CajaFile         *file);
335 
336 static GdkDragAction ask_link_action                           (FMDirectoryView      *view);
337 static void     update_templates_directory                     (FMDirectoryView *view);
338 static void     user_dirs_changed                              (FMDirectoryView *view);
339 static void     fm_directory_view_set_is_active                (FMDirectoryView *view,
340 								gboolean         is_active);
341 
342 static gboolean file_list_all_are_folders                      (GList *file_list);
343 
344 static void action_open_scripts_folder_callback    (GtkAction *action,
345 						    gpointer   callback_data);
346 static void action_cut_files_callback              (GtkAction *action,
347 						    gpointer   callback_data);
348 static void action_copy_files_callback             (GtkAction *action,
349 						    gpointer   callback_data);
350 static void action_paste_files_callback            (GtkAction *action,
351 						    gpointer   callback_data);
352 static void action_copy_to_next_pane_callback      (GtkAction *action,
353 						    gpointer   callback_data);
354 static void action_move_to_next_pane_callback      (GtkAction *action,
355 						    gpointer   callback_data);
356 static void action_rename_callback                 (GtkAction *action,
357 						    gpointer   callback_data);
358 static void action_rename_select_all_callback      (GtkAction *action,
359 						    gpointer   callback_data);
360 static void action_paste_files_into_callback       (GtkAction *action,
361 						    gpointer   callback_data);
362 static void action_connect_to_server_link_callback (GtkAction *action,
363 						    gpointer   data);
364 static void action_mount_volume_callback           (GtkAction *action,
365 						    gpointer   data);
366 static void action_unmount_volume_callback         (GtkAction *action,
367 						    gpointer   data);
368 static void action_format_volume_callback          (GtkAction *action,
369 						    gpointer   data);
370 static void action_start_volume_callback           (GtkAction *action,
371 						    gpointer   data);
372 static void action_stop_volume_callback            (GtkAction *action,
373 						    gpointer   data);
374 static void action_detect_media_callback           (GtkAction *action,
375 						    gpointer   data);
376 
377 /* location popup-related actions */
378 
379 static void action_location_open_alternate_callback (GtkAction *action,
380 						     gpointer   callback_data);
381 static void action_location_open_folder_window_callback (GtkAction *action,
382 							 gpointer   callback_data);
383 
384 static void action_location_cut_callback            (GtkAction *action,
385 						     gpointer   callback_data);
386 static void action_location_copy_callback           (GtkAction *action,
387 						     gpointer   callback_data);
388 static void action_location_trash_callback          (GtkAction *action,
389 						     gpointer   callback_data);
390 static void action_location_delete_callback         (GtkAction *action,
391 						     gpointer   callback_data);
392 static void action_location_properties_callback     (GtkAction *action,
393 						     gpointer   callback_data);
394 
395 static void unschedule_pop_up_location_context_menu (FMDirectoryView *view);
396 
397 static inline void fm_directory_view_widget_to_file_operation_position (FMDirectoryView *view,
398 									GdkPoint *position);
399 static void        fm_directory_view_widget_to_file_operation_position_xy (FMDirectoryView *view,
400 									   int *x, int *y);
401 
402 /* undo-related actions */
403 
404 static void undo_redo_menu_update_callback (CajaUndoStackManager* manager, gpointer arg1, gpointer data);
405 
406 static void undo_update_menu (FMDirectoryView *view);
407 
408 static void finish_undoredo_callback (gpointer data);
409 
410 static void real_action_undo (FMDirectoryView *view);
411 
412 static void real_action_redo (FMDirectoryView *view);
413 
414 static void action_undo_callback (GtkAction *action, gpointer callback_data);
415 
416 static void action_redo_callback (GtkAction *action, gpointer callback_data);
417 
418 EEL_CLASS_BOILERPLATE (FMDirectoryView, fm_directory_view, GTK_TYPE_SCROLLED_WINDOW)
419 
420 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, add_file)
421 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, bump_zoom_level)
422 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, can_zoom_in)
423 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, can_zoom_out)
424 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, clear)
425 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, file_changed)
426 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_background_widget)
427 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection)
428 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_selection_for_file_transfer)
429 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_item_count)
430 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, is_empty)
431 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, reset_to_defaults)
432 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, restore_default_zoom_level)
433 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, select_all)
434 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, set_selection)
435 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, zoom_to_level)
436 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, get_zoom_level)
437 EEL_IMPLEMENT_MUST_OVERRIDE_SIGNAL (fm_directory_view, invert_selection)
438 
439 typedef struct {
440 	GAppInfo *application;
441 	GList *files;
442 	FMDirectoryView *directory_view;
443 } ApplicationLaunchParameters;
444 
445 typedef struct {
446 	CajaFile *file;
447 	FMDirectoryView *directory_view;
448 } ScriptLaunchParameters;
449 
450 typedef struct {
451 	CajaFile *file;
452 	FMDirectoryView *directory_view;
453 } CreateTemplateParameters;
454 
455 static ApplicationLaunchParameters *
application_launch_parameters_new(GAppInfo * application,GList * files,FMDirectoryView * directory_view)456 application_launch_parameters_new (GAppInfo *application,
457 			      	   GList *files,
458 			           FMDirectoryView *directory_view)
459 {
460 	ApplicationLaunchParameters *result;
461 
462 	result = g_new0 (ApplicationLaunchParameters, 1);
463 	result->application = g_object_ref (application);
464 	result->files = caja_file_list_copy (files);
465 
466 	if (directory_view != NULL) {
467 		g_object_ref (directory_view);
468 		result->directory_view = directory_view;
469 	}
470 
471 	return result;
472 }
473 
474 static void
application_launch_parameters_free(ApplicationLaunchParameters * parameters)475 application_launch_parameters_free (ApplicationLaunchParameters *parameters)
476 {
477 	g_object_unref (parameters->application);
478 	caja_file_list_free (parameters->files);
479 
480 	if (parameters->directory_view != NULL) {
481 		g_object_unref (parameters->directory_view);
482 	}
483 
484 	g_free (parameters);
485 }
486 
487 static GList *
file_and_directory_list_to_files(GList * fad_list)488 file_and_directory_list_to_files (GList *fad_list)
489 {
490 	GList *res, *l;
491 	FileAndDirectory *fad = NULL;
492 
493 	res = NULL;
494 	for (l = fad_list; l != NULL; l = l->next) {
495 		fad = l->data;
496 		res = g_list_prepend (res, caja_file_ref (fad->file));
497 	}
498 	return g_list_reverse (res);
499 }
500 
501 
502 static GList *
file_and_directory_list_from_files(CajaDirectory * directory,GList * files)503 file_and_directory_list_from_files (CajaDirectory *directory, GList *files)
504 {
505 	GList *res, *l;
506 	FileAndDirectory *fad = NULL;
507 
508 	res = NULL;
509 	for (l = files; l != NULL; l = l->next) {
510 		fad = g_new0 (FileAndDirectory, 1);
511 		fad->directory = caja_directory_ref (directory);
512 		fad->file = caja_file_ref (l->data);
513 		res = g_list_prepend (res, fad);
514 	}
515 	return g_list_reverse (res);
516 }
517 
518 static void
file_and_directory_free(FileAndDirectory * fad)519 file_and_directory_free (FileAndDirectory *fad)
520 {
521 	caja_directory_unref (fad->directory);
522 	caja_file_unref (fad->file);
523 	g_free (fad);
524 }
525 
526 
527 static void
file_and_directory_list_free(GList * list)528 file_and_directory_list_free (GList *list)
529 {
530 	GList *l;
531 
532 	for (l = list; l != NULL; l = l->next) {
533 		file_and_directory_free (l->data);
534 	}
535 
536 	g_list_free (list);
537 }
538 
539 static gboolean
file_and_directory_equal(gconstpointer v1,gconstpointer v2)540 file_and_directory_equal (gconstpointer  v1,
541 			  gconstpointer  v2)
542 {
543 	const FileAndDirectory *fad1, *fad2;
544 	fad1 = v1;
545 	fad2 = v2;
546 
547 	return (fad1->file == fad2->file &&
548 		fad1->directory == fad2->directory);
549 }
550 
551 static guint
file_and_directory_hash(gconstpointer v)552 file_and_directory_hash  (gconstpointer  v)
553 {
554 	const FileAndDirectory *fad;
555 
556 	fad = v;
557 	return GPOINTER_TO_UINT (fad->file) ^ GPOINTER_TO_UINT (fad->directory);
558 }
559 
560 
561 
562 
563 static ScriptLaunchParameters *
script_launch_parameters_new(CajaFile * file,FMDirectoryView * directory_view)564 script_launch_parameters_new (CajaFile *file,
565 			      FMDirectoryView *directory_view)
566 {
567 	ScriptLaunchParameters *result;
568 
569 	result = g_new0 (ScriptLaunchParameters, 1);
570 	g_object_ref (directory_view);
571 	result->directory_view = directory_view;
572 	caja_file_ref (file);
573 	result->file = file;
574 
575 	return result;
576 }
577 
578 static void
script_launch_parameters_free(ScriptLaunchParameters * parameters)579 script_launch_parameters_free (ScriptLaunchParameters *parameters)
580 {
581 	g_object_unref (parameters->directory_view);
582 	caja_file_unref (parameters->file);
583 	g_free (parameters);
584 }
585 
586 static CreateTemplateParameters *
create_template_parameters_new(CajaFile * file,FMDirectoryView * directory_view)587 create_template_parameters_new (CajaFile *file,
588 				FMDirectoryView *directory_view)
589 {
590 	CreateTemplateParameters *result;
591 
592 	result = g_new0 (CreateTemplateParameters, 1);
593 	g_object_ref (directory_view);
594 	result->directory_view = directory_view;
595 	caja_file_ref (file);
596 	result->file = file;
597 
598 	return result;
599 }
600 
601 static void
create_templates_parameters_free(CreateTemplateParameters * parameters)602 create_templates_parameters_free (CreateTemplateParameters *parameters)
603 {
604 	g_object_unref (parameters->directory_view);
605 	caja_file_unref (parameters->file);
606 	g_free (parameters);
607 }
608 
609 CajaWindowInfo *
fm_directory_view_get_caja_window(FMDirectoryView * view)610 fm_directory_view_get_caja_window (FMDirectoryView  *view)
611 {
612 	g_assert (view->details->window != NULL);
613 
614 	return view->details->window;
615 }
616 
617 CajaWindowSlotInfo *
fm_directory_view_get_caja_window_slot(FMDirectoryView * view)618 fm_directory_view_get_caja_window_slot (FMDirectoryView  *view)
619 {
620 	g_assert (view->details->slot != NULL);
621 
622 	return view->details->slot;
623 }
624 
625 /* Returns the GtkWindow that this directory view occupies, or NULL
626  * if at the moment this directory view is not in a GtkWindow or the
627  * GtkWindow cannot be determined. Primarily used for parenting dialogs.
628  */
629 GtkWindow *
fm_directory_view_get_containing_window(FMDirectoryView * view)630 fm_directory_view_get_containing_window (FMDirectoryView *view)
631 {
632 	GtkWidget *window;
633 
634 	g_assert (FM_IS_DIRECTORY_VIEW (view));
635 
636 	window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW);
637 	if (window == NULL) {
638 		return NULL;
639 	}
640 
641 	return GTK_WINDOW (window);
642 }
643 
644 static gboolean
fm_directory_view_confirm_multiple(GtkWindow * parent_window,int count,gboolean tabs)645 fm_directory_view_confirm_multiple (GtkWindow *parent_window,
646 				    int count,
647 				    gboolean tabs)
648 {
649 	GtkDialog *dialog;
650 	char *prompt;
651 	char *detail;
652 	int response;
653 
654 	if (count <= SILENT_WINDOW_OPEN_LIMIT) {
655 		return TRUE;
656 	}
657 
658 	prompt = _("Are you sure you want to open all files?");
659 	if (tabs) {
660 		detail = g_strdup_printf (ngettext("This will open %'d separate tab.",
661 						   "This will open %'d separate tabs.", count), count);
662 	} else {
663 		detail = g_strdup_printf (ngettext("This will open %'d separate window.",
664 						   "This will open %'d separate windows.", count), count);
665 	}
666 	dialog = eel_show_yes_no_dialog (prompt, detail,
667 					 "gtk-ok", "process-stop",
668 					 parent_window);
669 	g_free (detail);
670 
671 	response = gtk_dialog_run (dialog);
672 	gtk_widget_destroy (GTK_WIDGET (dialog));
673 
674 	return response == GTK_RESPONSE_YES;
675 }
676 
677 static gboolean
selection_contains_one_item_in_menu_callback(FMDirectoryView * view,GList * selection)678 selection_contains_one_item_in_menu_callback (FMDirectoryView *view, GList *selection)
679 {
680 	if (eel_g_list_exactly_one_item (selection)) {
681 		return TRUE;
682 	}
683 
684 	/* If we've requested a menu update that hasn't yet occurred, then
685 	 * the mismatch here doesn't surprise us, and we won't complain.
686 	 * Otherwise, we will complain.
687 	 */
688 	if (!view->details->menu_states_untrustworthy) {
689 		g_warning ("Expected one selected item, found %'d. No action will be performed.",
690 			   g_list_length (selection));
691 	}
692 
693 	return FALSE;
694 }
695 
696 static gboolean
selection_not_empty_in_menu_callback(FMDirectoryView * view,GList * selection)697 selection_not_empty_in_menu_callback (FMDirectoryView *view, GList *selection)
698 {
699 	if (selection != NULL) {
700 		return TRUE;
701 	}
702 
703 	/* If we've requested a menu update that hasn't yet occurred, then
704 	 * the mismatch here doesn't surprise us, and we won't complain.
705 	 * Otherwise, we will complain.
706 	 */
707 	if (!view->details->menu_states_untrustworthy) {
708 		g_warning ("Empty selection found when selection was expected. No action will be performed.");
709 	}
710 
711 	return FALSE;
712 }
713 
714 static char *
get_view_directory(FMDirectoryView * view)715 get_view_directory (FMDirectoryView *view)
716 {
717 	char *uri, *path;
718 	GFile *f;
719 
720 	uri = caja_directory_get_uri (view->details->model);
721 	if (eel_uri_is_desktop (uri)) {
722 		g_free (uri);
723 		uri = caja_get_desktop_directory_uri ();
724 
725 	}
726 	f = g_file_new_for_uri (uri);
727 	path = g_file_get_path (f);
728 	g_object_unref (f);
729 	g_free (uri);
730 
731 	return path;
732 }
733 
734 void
fm_directory_view_activate_files(FMDirectoryView * view,GList * files,CajaWindowOpenMode mode,CajaWindowOpenFlags flags,gboolean confirm_multiple)735 fm_directory_view_activate_files (FMDirectoryView *view,
736 				  GList *files,
737 				  CajaWindowOpenMode mode,
738 				  CajaWindowOpenFlags flags,
739 				  gboolean confirm_multiple)
740 {
741 	char *path;
742 
743 	path = get_view_directory (view);
744 	caja_mime_activate_files (fm_directory_view_get_containing_window (view),
745 				      view->details->slot,
746 				      files,
747 				      path,
748 				      mode,
749 				      flags,
750 				      confirm_multiple);
751 
752 	g_free (path);
753 }
754 
755 void
fm_directory_view_activate_file(FMDirectoryView * view,CajaFile * file,CajaWindowOpenMode mode,CajaWindowOpenFlags flags)756 fm_directory_view_activate_file (FMDirectoryView *view,
757 				 CajaFile *file,
758 				 CajaWindowOpenMode mode,
759 				 CajaWindowOpenFlags flags)
760 {
761 	char *path;
762 
763 	path = get_view_directory (view);
764 	caja_mime_activate_file (fm_directory_view_get_containing_window (view),
765 				     view->details->slot,
766 				     file,
767 				     path,
768 				     mode,
769 				     flags);
770 
771 	g_free (path);
772 }
773 
774 static void
action_open_callback(GtkAction * action,gpointer callback_data)775 action_open_callback (GtkAction *action,
776 		      gpointer callback_data)
777 {
778 	GList *selection;
779 	FMDirectoryView *view;
780 
781 	view = FM_DIRECTORY_VIEW (callback_data);
782 
783 	selection = fm_directory_view_get_selection (view);
784 	fm_directory_view_activate_files (view,
785 					  selection,
786 					  CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
787 					  0,
788 					  TRUE);
789 	caja_file_list_free (selection);
790 }
791 
792 static void
action_open_close_parent_callback(GtkAction * action,gpointer callback_data)793 action_open_close_parent_callback (GtkAction *action,
794 				   gpointer callback_data)
795 {
796 	GList *selection;
797 	FMDirectoryView *view;
798 
799 	view = FM_DIRECTORY_VIEW (callback_data);
800 
801 	selection = fm_directory_view_get_selection (view);
802 	fm_directory_view_activate_files (view,
803 					  selection,
804 					  CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
805 					  CAJA_WINDOW_OPEN_FLAG_CLOSE_BEHIND,
806 					  TRUE);
807 	caja_file_list_free (selection);
808 }
809 
810 
811 static void
action_open_alternate_callback(GtkAction * action,gpointer callback_data)812 action_open_alternate_callback (GtkAction *action,
813 				gpointer callback_data)
814 {
815 	FMDirectoryView *view;
816 	GList *selection;
817 	GtkWindow *window;
818 
819 	view = FM_DIRECTORY_VIEW (callback_data);
820 	selection = fm_directory_view_get_selection (view);
821 
822 	window = fm_directory_view_get_containing_window (view);
823 
824 	if (fm_directory_view_confirm_multiple (window, g_list_length (selection), FALSE)) {
825 		g_list_foreach (selection, open_one_in_new_window, view);
826 	}
827 
828 	caja_file_list_free (selection);
829 }
830 
831 static void
action_open_new_tab_callback(GtkAction * action,gpointer callback_data)832 action_open_new_tab_callback (GtkAction *action,
833 			      gpointer callback_data)
834 {
835 	FMDirectoryView *view;
836 	GList *selection;
837 	GtkWindow *window;
838 
839 	view = FM_DIRECTORY_VIEW (callback_data);
840 	selection = fm_directory_view_get_selection (view);
841 
842 	window = fm_directory_view_get_containing_window (view);
843 
844 	if (fm_directory_view_confirm_multiple (window, g_list_length (selection), TRUE)) {
845 		fm_directory_view_activate_files (view,
846 						  selection,
847 						  CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
848 						  CAJA_WINDOW_OPEN_FLAG_NEW_TAB,
849 						  FALSE);
850 	}
851 
852 	caja_file_list_free (selection);
853 }
854 
855 static void
action_open_folder_window_callback(GtkAction * action,gpointer callback_data)856 action_open_folder_window_callback (GtkAction *action,
857 				gpointer callback_data)
858 {
859 	FMDirectoryView *view;
860 	GList *selection;
861 	GtkWindow *window;
862 
863 	view = FM_DIRECTORY_VIEW (callback_data);
864 	selection = fm_directory_view_get_selection (view);
865 
866 	window = fm_directory_view_get_containing_window (view);
867 
868 	if (fm_directory_view_confirm_multiple (window, g_list_length (selection), FALSE)) {
869 		g_list_foreach (selection, open_one_in_folder_window, view);
870 	}
871 
872 	caja_file_list_free (selection);
873 }
874 
875 static void
open_location(FMDirectoryView * directory_view,const char * new_uri,CajaWindowOpenMode mode,CajaWindowOpenFlags flags)876 open_location (FMDirectoryView *directory_view,
877 	       const char *new_uri,
878 	       CajaWindowOpenMode mode,
879 	       CajaWindowOpenFlags flags)
880 {
881 	GtkWindow *window;
882 	GFile *location;
883 
884 	g_assert (FM_IS_DIRECTORY_VIEW (directory_view));
885 	g_assert (new_uri != NULL);
886 
887 	window = fm_directory_view_get_containing_window (directory_view);
888 	caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
889 			    "directory view open_location window=%p: %s", window, new_uri);
890 	location = g_file_new_for_uri (new_uri);
891 	caja_window_slot_info_open_location (directory_view->details->slot,
892 						 location, mode, flags, NULL);
893 	g_object_unref (location);
894 }
895 
896 static void
application_selected_cb(CajaOpenWithDialog * dialog,GAppInfo * app,gpointer user_data)897 application_selected_cb (CajaOpenWithDialog *dialog,
898 			 GAppInfo *app,
899 			 gpointer user_data)
900 {
901 	GtkWindow *parent_window;
902 	CajaFile *file;
903 	GList files;
904 
905 	parent_window = GTK_WINDOW (user_data);
906 
907 	file = g_object_get_data (G_OBJECT (dialog), "directory-view:file");
908 
909 	files.next = NULL;
910 	files.prev = NULL;
911 	files.data = file;
912 	caja_launch_application (app, &files, parent_window);
913 }
914 
915 static void
choose_program(FMDirectoryView * view,CajaFile * file)916 choose_program (FMDirectoryView *view,
917 		CajaFile *file)
918 {
919 	GtkWidget *dialog;
920 	char *uri;
921 	char *mime_type;
922 
923 	g_assert (FM_IS_DIRECTORY_VIEW (view));
924 	g_assert (CAJA_IS_FILE (file));
925 
926 	caja_file_ref (file);
927 	uri = caja_file_get_uri (file);
928 	mime_type = caja_file_get_mime_type (file);
929 
930 	dialog = caja_open_with_dialog_new (uri, mime_type, NULL);
931 	g_object_set_data_full (G_OBJECT (dialog),
932 				"directory-view:file",
933 				g_object_ref (file),
934 				(GDestroyNotify)g_object_unref);
935 
936 	gtk_window_set_screen (GTK_WINDOW (dialog),
937 			       gtk_widget_get_screen (GTK_WIDGET (view)));
938 	gtk_widget_show (dialog);
939 
940 	g_signal_connect_object (dialog,
941 				 "application_selected",
942 				 G_CALLBACK (application_selected_cb),
943 				 fm_directory_view_get_containing_window (view),
944 				 0);
945 
946  	g_free (uri);
947 	g_free (mime_type);
948 	caja_file_unref (file);
949 }
950 
951 static void
open_with_other_program(FMDirectoryView * view)952 open_with_other_program (FMDirectoryView *view)
953 {
954         GList *selection;
955 
956 	g_assert (FM_IS_DIRECTORY_VIEW (view));
957 
958        	selection = fm_directory_view_get_selection (view);
959 
960 	if (selection_contains_one_item_in_menu_callback (view, selection)) {
961 		choose_program (view, CAJA_FILE (selection->data));
962 	}
963 
964 	caja_file_list_free (selection);
965 }
966 
967 static void
action_other_application_callback(GtkAction * action,gpointer callback_data)968 action_other_application_callback (GtkAction *action,
969 				   gpointer callback_data)
970 {
971 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
972 
973 	open_with_other_program (FM_DIRECTORY_VIEW (callback_data));
974 }
975 
976 static void
trash_or_delete_selected_files(FMDirectoryView * view)977 trash_or_delete_selected_files (FMDirectoryView *view)
978 {
979 	/* This might be rapidly called multiple times for the same selection
980 	 * when using keybindings. So we remember if the current selection
981 	 * was already removed (but the view doesn't know about it yet).
982 	 */
983 	if (!view->details->selection_was_removed) {
984 	        GList *selection;
985 
986 		selection = fm_directory_view_get_selection_for_file_transfer (view);
987 		trash_or_delete_files (fm_directory_view_get_containing_window (view),
988 				       selection, TRUE,
989 				       view);
990 		caja_file_list_free (selection);
991 		view->details->selection_was_removed = TRUE;
992 	}
993 }
994 
995 static gboolean
real_trash(FMDirectoryView * view)996 real_trash (FMDirectoryView *view)
997 {
998 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
999 	GtkAction *action;
1000 
1001 	action = gtk_action_group_get_action (view->details->dir_action_group,
1002 					      FM_ACTION_TRASH);
1003 	if (gtk_action_get_sensitive (action) &&
1004 	    gtk_action_get_visible (action)) {
1005 		trash_or_delete_selected_files (view);
1006 		return TRUE;
1007 	}
1008 	G_GNUC_END_IGNORE_DEPRECATIONS;
1009 	return FALSE;
1010 }
1011 
1012 static void
action_trash_callback(GtkAction * action,gpointer callback_data)1013 action_trash_callback (GtkAction *action,
1014 		       gpointer callback_data)
1015 {
1016         trash_or_delete_selected_files (FM_DIRECTORY_VIEW (callback_data));
1017 }
1018 
1019 static void
delete_selected_files(FMDirectoryView * view)1020 delete_selected_files (FMDirectoryView *view)
1021 {
1022         GList *selection;
1023 	GList *node;
1024 	GList *locations;
1025 
1026 	selection = fm_directory_view_get_selection_for_file_transfer (view);
1027 	if (selection == NULL) {
1028 		return;
1029 	}
1030 
1031 	locations = NULL;
1032 	for (node = selection; node != NULL; node = node->next) {
1033 		locations = g_list_prepend (locations,
1034 					    caja_file_get_location ((CajaFile *) node->data));
1035 	}
1036 	locations = g_list_reverse (locations);
1037 
1038 	caja_file_operations_delete (locations, fm_directory_view_get_containing_window (view), NULL, NULL);
1039 
1040 	g_list_free_full (locations, g_object_unref);
1041         caja_file_list_free (selection);
1042 }
1043 
1044 static void
action_delete_callback(GtkAction * action,gpointer callback_data)1045 action_delete_callback (GtkAction *action,
1046 			gpointer callback_data)
1047 {
1048         delete_selected_files (FM_DIRECTORY_VIEW (callback_data));
1049 }
1050 
1051 static void
action_restore_from_trash_callback(GtkAction * action,gpointer callback_data)1052 action_restore_from_trash_callback (GtkAction *action,
1053 				    gpointer callback_data)
1054 {
1055 	FMDirectoryView *view;
1056 	GList *selection;
1057 
1058 	view = FM_DIRECTORY_VIEW (callback_data);
1059 
1060 	selection = fm_directory_view_get_selection_for_file_transfer (view);
1061 	caja_restore_files_from_trash (selection,
1062 					   fm_directory_view_get_containing_window (view));
1063 
1064 	caja_file_list_free (selection);
1065 
1066 }
1067 
1068 static gboolean
real_delete(FMDirectoryView * view)1069 real_delete (FMDirectoryView *view)
1070 {
1071 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1072 	GtkAction *action;
1073 
1074 	action = gtk_action_group_get_action (view->details->dir_action_group,
1075 					      FM_ACTION_TRASH);
1076 	if (gtk_action_get_sensitive (action) &&
1077 	    gtk_action_get_visible (action)) {
1078 		delete_selected_files (view);
1079 		return TRUE;
1080 	}
1081 	G_GNUC_END_IGNORE_DEPRECATIONS;
1082 	return FALSE;
1083 }
1084 
1085 static void
action_duplicate_callback(GtkAction * action,gpointer callback_data)1086 action_duplicate_callback (GtkAction *action,
1087 			   gpointer callback_data)
1088 {
1089         FMDirectoryView *view;
1090         GList *selection;
1091 
1092         view = FM_DIRECTORY_VIEW (callback_data);
1093 	selection = fm_directory_view_get_selection_for_file_transfer (view);
1094 	if (selection_not_empty_in_menu_callback (view, selection)) {
1095 	        GArray *selected_item_locations;
1096 
1097 		/* FIXME bugzilla.gnome.org 45061:
1098 		 * should change things here so that we use a get_icon_locations (view, selection).
1099 		 * Not a problem in this case but in other places the selection may change by
1100 		 * the time we go and retrieve the icon positions, relying on the selection
1101 		 * staying intact to ensure the right sequence and count of positions is fragile.
1102 		 */
1103 		selected_item_locations = fm_directory_view_get_selected_icon_locations (view);
1104 	        fm_directory_view_duplicate_selection (view, selection, selected_item_locations);
1105 	        g_array_free (selected_item_locations, TRUE);
1106 	}
1107 
1108         caja_file_list_free (selection);
1109 }
1110 
1111 static void
action_create_link_callback(GtkAction * action,gpointer callback_data)1112 action_create_link_callback (GtkAction *action,
1113 			     gpointer callback_data)
1114 {
1115         FMDirectoryView *view;
1116         GList *selection;
1117 
1118         g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1119 
1120         view = FM_DIRECTORY_VIEW (callback_data);
1121 	selection = fm_directory_view_get_selection (view);
1122 	if (selection_not_empty_in_menu_callback (view, selection)) {
1123 	        GArray *selected_item_locations;
1124 
1125 		selected_item_locations = fm_directory_view_get_selected_icon_locations (view);
1126 	        fm_directory_view_create_links_for_files (view, selection, selected_item_locations);
1127 	        g_array_free (selected_item_locations, TRUE);
1128 	}
1129 
1130         caja_file_list_free (selection);
1131 }
1132 
1133 static void
action_select_all_callback(GtkAction * action,gpointer callback_data)1134 action_select_all_callback (GtkAction *action,
1135 			    gpointer callback_data)
1136 {
1137 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1138 
1139 	fm_directory_view_select_all (callback_data);
1140 }
1141 
1142 static void
action_invert_selection_callback(GtkAction * action,gpointer callback_data)1143 action_invert_selection_callback (GtkAction *action,
1144 				  gpointer callback_data)
1145 {
1146 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1147 
1148 	fm_directory_view_invert_selection (callback_data);
1149 }
1150 
1151 
1152 static void
pattern_select_response_cb(GtkWidget * dialog,int response,gpointer user_data)1153 pattern_select_response_cb (GtkWidget *dialog, int response, gpointer user_data)
1154 {
1155 	FMDirectoryView *view;
1156 	CajaDirectory *directory;
1157 	GtkWidget *entry;
1158 	GList *selection;
1159 	GError *error;
1160 
1161 	view = FM_DIRECTORY_VIEW (user_data);
1162 
1163 	switch (response) {
1164 	case GTK_RESPONSE_OK :
1165 		entry = g_object_get_data (G_OBJECT (dialog), "entry");
1166 		directory = fm_directory_view_get_model (view);
1167 		selection = caja_directory_match_pattern (directory,
1168 					gtk_entry_get_text (GTK_ENTRY (entry)));
1169 
1170 		if (selection) {
1171 			fm_directory_view_set_selection (view, selection);
1172 			caja_file_list_free (selection);
1173 
1174 			fm_directory_view_reveal_selection(view);
1175 		}
1176 		/* fall through */
1177 	case GTK_RESPONSE_NONE :
1178 	case GTK_RESPONSE_DELETE_EVENT :
1179 	case GTK_RESPONSE_CANCEL :
1180 		gtk_widget_destroy (GTK_WIDGET (dialog));
1181 		break;
1182 	case GTK_RESPONSE_HELP :
1183 		error = NULL;
1184 		gtk_show_uri_on_window (GTK_WINDOW (dialog),
1185 			                "help:mate-user-guide/caja-select-pattern",
1186 			                gtk_get_current_event_time (), &error);
1187 		if (error) {
1188 			eel_show_error_dialog (_("There was an error displaying help."), error->message,
1189 					       GTK_WINDOW (dialog));
1190 			g_error_free (error);
1191 		}
1192 		break;
1193 	default :
1194 		g_assert_not_reached ();
1195 	}
1196 }
1197 
1198 static void
select_pattern(FMDirectoryView * view)1199 select_pattern (FMDirectoryView *view)
1200 {
1201 	GtkWidget *dialog;
1202 	GtkWidget *label;
1203 	GtkWidget *example;
1204 	GtkWidget *grid;
1205 	GtkWidget *entry;
1206 	char *example_pattern;
1207 
1208 	dialog = gtk_dialog_new ();
1209 	gtk_window_set_title (GTK_WINDOW (dialog), _("Select Items Matching"));
1210 	gtk_window_set_transient_for (GTK_WINDOW (dialog), fm_directory_view_get_containing_window (view));
1211 	gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
1212 
1213 	eel_dialog_add_button (GTK_DIALOG (dialog),
1214 			       _("_Help"),
1215 			       "help-browser",
1216 			       GTK_RESPONSE_HELP);
1217 
1218 	eel_dialog_add_button (GTK_DIALOG (dialog),
1219 			       _("_Cancel"),
1220 			       "process-stop",
1221 			       GTK_RESPONSE_CANCEL);
1222 
1223 	eel_dialog_add_button (GTK_DIALOG (dialog),
1224 			       _("_OK"),
1225 			       "gtk-ok",
1226 			       GTK_RESPONSE_OK);
1227 
1228 	gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1229 					 GTK_RESPONSE_OK);
1230 	gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
1231 	gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
1232 
1233 	label = gtk_label_new_with_mnemonic (_("_Pattern:"));
1234 
1235 	gtk_widget_set_halign (label, GTK_ALIGN_START);
1236 
1237 	example = gtk_label_new (NULL);
1238 	gtk_widget_set_halign (example, GTK_ALIGN_START);
1239 	example_pattern = g_strdup_printf ("<b>%s</b><i>%s</i>",
1240 					   _("Examples: "),
1241 					   "*.png, file\?\?.txt, pict*.\?\?\?");
1242 	gtk_label_set_markup (GTK_LABEL (example), example_pattern);
1243 	g_free (example_pattern);
1244 	gtk_widget_set_halign (example, GTK_ALIGN_START);
1245 
1246 	entry = gtk_entry_new ();
1247 	gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
1248 	gtk_widget_set_hexpand (entry, TRUE);
1249 
1250 	grid = gtk_grid_new ();
1251 	g_object_set (grid,
1252 		      "orientation", GTK_ORIENTATION_VERTICAL,
1253 		      "border-width", 6,
1254 		      "row-spacing", 6,
1255 		      "column-spacing", 12,
1256 		      NULL);
1257 
1258 	gtk_container_add (GTK_CONTAINER (grid), label);
1259 	gtk_grid_attach_next_to (GTK_GRID (grid), entry, label,
1260 				 GTK_POS_RIGHT, 1, 1);
1261 	gtk_grid_attach_next_to (GTK_GRID (grid), example, entry,
1262 				 GTK_POS_BOTTOM, 1, 1);
1263 
1264 	gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
1265 	gtk_widget_show_all (grid);
1266 	gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), grid);
1267 
1268 	g_object_set_data (G_OBJECT (dialog), "entry", entry);
1269 	g_signal_connect (dialog, "response",
1270 			  G_CALLBACK (pattern_select_response_cb),
1271 			  view);
1272 	gtk_widget_show_all (dialog);
1273 }
1274 
1275 static void
action_select_pattern_callback(GtkAction * action,gpointer callback_data)1276 action_select_pattern_callback (GtkAction *action,
1277 				gpointer callback_data)
1278 {
1279 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1280 
1281 	select_pattern(callback_data);
1282 }
1283 
1284 static void
action_reset_to_defaults_callback(GtkAction * action,gpointer callback_data)1285 action_reset_to_defaults_callback (GtkAction *action,
1286 				   gpointer callback_data)
1287 {
1288 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1289 
1290 	fm_directory_view_reset_to_defaults (callback_data);
1291 }
1292 
1293 
1294 static void
hidden_files_mode_changed(CajaWindow * window,gpointer callback_data)1295 hidden_files_mode_changed (CajaWindow *window,
1296 			   gpointer callback_data)
1297 {
1298 	FMDirectoryView *directory_view;
1299 
1300 	directory_view = FM_DIRECTORY_VIEW (callback_data);
1301 
1302 	fm_directory_view_init_show_hidden_files (directory_view);
1303 }
1304 
1305 static void
backup_files_mode_changed(CajaWindow * window,gpointer callback_data)1306 backup_files_mode_changed (CajaWindow *window,
1307 			   gpointer callback_data)
1308 {
1309 	FMDirectoryView *directory_view;
1310 
1311 	directory_view = FM_DIRECTORY_VIEW (callback_data);
1312 
1313 	fm_directory_view_init_show_backup_files (directory_view);
1314 }
1315 
1316 static void
action_save_search_callback(GtkAction * action,gpointer callback_data)1317 action_save_search_callback (GtkAction *action,
1318 			     gpointer callback_data)
1319 {
1320 	FMDirectoryView	*directory_view;
1321 
1322         directory_view = FM_DIRECTORY_VIEW (callback_data);
1323 
1324 	if (directory_view->details->model &&
1325 	    CAJA_IS_SEARCH_DIRECTORY (directory_view->details->model)) {
1326 		CajaSearchDirectory *search;
1327 
1328 		search = CAJA_SEARCH_DIRECTORY (directory_view->details->model);
1329 		caja_search_directory_save_search (search);
1330 
1331 		/* Save search is disabled */
1332 		schedule_update_menus (directory_view);
1333 	}
1334 }
1335 
1336 static void
query_name_entry_changed_cb(GtkWidget * entry,GtkWidget * button)1337 query_name_entry_changed_cb  (GtkWidget *entry, GtkWidget *button)
1338 {
1339 	const char *text;
1340 	gboolean sensitive;
1341 
1342 	text = gtk_entry_get_text (GTK_ENTRY (entry));
1343 
1344 	sensitive = (text != NULL) && (*text != 0);
1345 
1346 	gtk_widget_set_sensitive (button, sensitive);
1347 }
1348 
1349 
1350 static void
action_save_search_as_callback(GtkAction * action,gpointer callback_data)1351 action_save_search_as_callback (GtkAction *action,
1352 				gpointer callback_data)
1353 {
1354 	FMDirectoryView	*directory_view;
1355 
1356         directory_view = FM_DIRECTORY_VIEW (callback_data);
1357 
1358 	if (directory_view->details->model &&
1359 	    CAJA_IS_SEARCH_DIRECTORY (directory_view->details->model)) {
1360 		CajaSearchDirectory *search;
1361 		GtkWidget *dialog, *grid, *label, *entry, *chooser, *save_button;
1362 
1363 		search = CAJA_SEARCH_DIRECTORY (directory_view->details->model);
1364 
1365 		dialog = gtk_dialog_new ();
1366 		gtk_window_set_title (GTK_WINDOW (dialog), _("Save Search as"));
1367 		gtk_window_set_transient_for (GTK_WINDOW (dialog), fm_directory_view_get_containing_window (directory_view));
1368 
1369 		eel_dialog_add_button (GTK_DIALOG (dialog),
1370 				       _("_Cancel"),
1371 				       "process-stop",
1372 				       GTK_RESPONSE_CANCEL);
1373 
1374 		save_button = eel_dialog_add_button (GTK_DIALOG (dialog), _("_Save"),
1375 						     "document-save", GTK_RESPONSE_OK);
1376 
1377 		gtk_dialog_set_default_response (GTK_DIALOG (dialog),
1378 						 GTK_RESPONSE_OK);
1379 		gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
1380 		gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
1381 		gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1382 
1383 		grid = gtk_grid_new ();
1384 		g_object_set (grid,
1385 			      "orientation", GTK_ORIENTATION_VERTICAL,
1386 			      "border-width", 5,
1387 			      "row-spacing", 6,
1388 			      "column-spacing", 12,
1389 			      NULL);
1390 		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), grid, TRUE, TRUE, 0);
1391 		gtk_widget_show (grid);
1392 
1393 		label = gtk_label_new_with_mnemonic (_("Search _name:"));
1394 		gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1395 		gtk_container_add (GTK_CONTAINER (grid), label);
1396 		gtk_widget_show (label);
1397 		entry = gtk_entry_new ();
1398 		gtk_widget_set_hexpand (entry, TRUE);
1399 		gtk_grid_attach_next_to (GTK_GRID (grid), entry, label,
1400 					 GTK_POS_RIGHT, 1, 1);
1401 		gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
1402 		gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
1403 
1404 		gtk_widget_set_sensitive (save_button, FALSE);
1405 		g_signal_connect (entry, "changed",
1406 				  G_CALLBACK (query_name_entry_changed_cb), save_button);
1407 
1408 		gtk_widget_show (entry);
1409 		label = gtk_label_new_with_mnemonic (_("_Folder:"));
1410 		gtk_label_set_xalign (GTK_LABEL (label), 0.0);
1411 		gtk_container_add (GTK_CONTAINER (grid), label);
1412 		gtk_widget_show (label);
1413 
1414 		chooser = gtk_file_chooser_button_new (_("Select Folder to Save Search In"),
1415 						      GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
1416 		gtk_widget_set_hexpand (chooser, TRUE);
1417 		gtk_grid_attach_next_to (GTK_GRID (grid), chooser, label,
1418 					 GTK_POS_RIGHT, 1, 1);
1419 		gtk_label_set_mnemonic_widget (GTK_LABEL (label), chooser);
1420 		gtk_widget_show (chooser);
1421 
1422 		gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (chooser), TRUE);
1423 
1424 		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser),
1425 						     g_get_home_dir ());
1426 
1427 		if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
1428 			const char *entry_text;
1429 			char *filename, *filename_utf8, *dirname, *path, *uri;
1430 			GFile *location;
1431 
1432 			entry_text = gtk_entry_get_text (GTK_ENTRY (entry));
1433 			if (g_str_has_suffix (entry_text, CAJA_SAVED_SEARCH_EXTENSION)) {
1434 				filename_utf8 = g_strdup (entry_text);
1435 			} else {
1436 				filename_utf8 = g_strconcat (entry_text, CAJA_SAVED_SEARCH_EXTENSION, NULL);
1437 			}
1438 
1439 			filename = g_filename_from_utf8 (filename_utf8, -1, NULL, NULL, NULL);
1440 			g_free (filename_utf8);
1441 
1442 			dirname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
1443 
1444 			path = g_build_filename (dirname, filename, NULL);
1445 			g_free (filename);
1446 			g_free (dirname);
1447 
1448 			uri = g_filename_to_uri (path, NULL, NULL);
1449 			g_free (path);
1450 
1451 			caja_search_directory_save_to_file (search, uri);
1452 			location = g_file_new_for_uri (uri);
1453 			caja_file_changes_queue_file_added (location);
1454 			g_object_unref (location);
1455 			caja_file_changes_consume_changes (TRUE);
1456 			g_free (uri);
1457 		}
1458 
1459 		gtk_widget_destroy (dialog);
1460 	}
1461 }
1462 
1463 
1464 static void
action_empty_trash_callback(GtkAction * action,gpointer callback_data)1465 action_empty_trash_callback (GtkAction *action,
1466 			     gpointer callback_data)
1467 {
1468         g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1469 
1470 	caja_file_operations_empty_trash (GTK_WIDGET (callback_data));
1471 }
1472 
1473 static void
action_new_folder_callback(GtkAction * action,gpointer callback_data)1474 action_new_folder_callback (GtkAction *action,
1475 			    gpointer callback_data)
1476 {
1477         g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1478 
1479 	fm_directory_view_new_folder (FM_DIRECTORY_VIEW (callback_data));
1480 }
1481 
1482 static void
action_new_empty_file_callback(GtkAction * action,gpointer callback_data)1483 action_new_empty_file_callback (GtkAction *action,
1484 				gpointer callback_data)
1485 {
1486         g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1487 
1488 	fm_directory_view_new_file (FM_DIRECTORY_VIEW (callback_data), NULL, NULL);
1489 }
1490 
1491 static void
action_new_launcher_callback(GtkAction * action,gpointer callback_data)1492 action_new_launcher_callback (GtkAction *action,
1493 			      gpointer callback_data)
1494 {
1495 	char *parent_uri;
1496 	FMDirectoryView *view;
1497 	GtkWindow *window;
1498 
1499 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1500 
1501 	view = FM_DIRECTORY_VIEW (callback_data);
1502 
1503 	parent_uri = fm_directory_view_get_backing_uri (view);
1504 
1505 	window = fm_directory_view_get_containing_window (view);
1506 	caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
1507 			    "directory view create new launcher in window=%p: %s", window, parent_uri);
1508 	caja_launch_application_from_command (gtk_widget_get_screen (GTK_WIDGET (view)),
1509 						  "mate-desktop-item-edit",
1510 						  "mate-desktop-item-edit",
1511 						  FALSE,
1512 						  "--create-new", parent_uri, NULL);
1513 
1514 	g_free (parent_uri);
1515 }
1516 
1517 static void
action_properties_callback(GtkAction * action,gpointer callback_data)1518 action_properties_callback (GtkAction *action,
1519 			    gpointer callback_data)
1520 {
1521         FMDirectoryView *view;
1522         GList *selection;
1523 
1524         g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1525 
1526         view = FM_DIRECTORY_VIEW (callback_data);
1527 	selection = fm_directory_view_get_selection (view);
1528 	if (g_list_length (selection) == 0) {
1529 		if (view->details->directory_as_file != NULL) {
1530 			GList *files;
1531 
1532 			files = g_list_append (NULL, caja_file_ref (view->details->directory_as_file));
1533 
1534 			fm_properties_window_present (files, GTK_WIDGET (view));
1535 
1536 			caja_file_list_free (files);
1537 		}
1538 	} else {
1539 		fm_properties_window_present (selection, GTK_WIDGET (view));
1540 	}
1541         caja_file_list_free (selection);
1542 }
1543 
1544 static void
action_location_properties_callback(GtkAction * action,gpointer callback_data)1545 action_location_properties_callback (GtkAction *action,
1546 				     gpointer   callback_data)
1547 {
1548 	FMDirectoryView *view;
1549 	GList           *files;
1550 
1551 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
1552 
1553 	view = FM_DIRECTORY_VIEW (callback_data);
1554 	g_assert (CAJA_IS_FILE (view->details->location_popup_directory_as_file));
1555 
1556 	files = g_list_append (NULL, caja_file_ref (view->details->location_popup_directory_as_file));
1557 
1558 	fm_properties_window_present (files, GTK_WIDGET (view));
1559 
1560 	caja_file_list_free (files);
1561 }
1562 
1563 static gboolean
all_files_in_trash(GList * files)1564 all_files_in_trash (GList *files)
1565 {
1566 	GList *node;
1567 
1568 	/* Result is ambiguous if called on NULL, so disallow. */
1569 	g_return_val_if_fail (files != NULL, FALSE);
1570 
1571 	for (node = files; node != NULL; node = node->next) {
1572 		if (!caja_file_is_in_trash (CAJA_FILE (node->data))) {
1573 			return FALSE;
1574 		}
1575 	}
1576 
1577 	return TRUE;
1578 }
1579 
1580 static gboolean
all_selected_items_in_trash(FMDirectoryView * view)1581 all_selected_items_in_trash (FMDirectoryView *view)
1582 {
1583 	GList *selection;
1584 	gboolean result;
1585 
1586 	/* If the contents share a parent directory, we need only
1587 	 * check that parent directory. Otherwise we have to inspect
1588 	 * each selected item.
1589 	 */
1590 	selection = fm_directory_view_get_selection (view);
1591 	result = (selection == NULL) ? FALSE : all_files_in_trash (selection);
1592 	caja_file_list_free (selection);
1593 
1594 	return result;
1595 }
1596 
1597 static gboolean
we_are_in_vfolder_desktop_dir(FMDirectoryView * view)1598 we_are_in_vfolder_desktop_dir (FMDirectoryView *view)
1599 {
1600 	CajaFile *file;
1601 	char *mime_type;
1602 
1603 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
1604 
1605 	if (view->details->model == NULL) {
1606 		return FALSE;
1607 	}
1608 
1609 	file = caja_directory_get_corresponding_file (view->details->model);
1610 	mime_type = caja_file_get_mime_type (file);
1611 	caja_file_unref (file);
1612 
1613 	if (mime_type != NULL
1614 	    && strcmp (mime_type, "x-directory/vfolder-desktop") == 0) {
1615 		g_free (mime_type);
1616 		return TRUE;
1617 	} else {
1618 		g_free (mime_type);
1619 		return FALSE;
1620 	}
1621 }
1622 
1623 /* Preferences changed callbacks */
1624 static void
text_attribute_names_changed_callback(gpointer callback_data)1625 text_attribute_names_changed_callback (gpointer callback_data)
1626 {
1627 	FMDirectoryView *view;
1628 
1629 	view = FM_DIRECTORY_VIEW (callback_data);
1630 
1631 	EEL_CALL_METHOD
1632 		(FM_DIRECTORY_VIEW_CLASS, view,
1633 		 text_attribute_names_changed, (view));
1634 }
1635 
1636 static void
image_display_policy_changed_callback(gpointer callback_data)1637 image_display_policy_changed_callback (gpointer callback_data)
1638 {
1639 	FMDirectoryView *view;
1640 
1641 	view = FM_DIRECTORY_VIEW (callback_data);
1642 
1643 	EEL_CALL_METHOD
1644 		(FM_DIRECTORY_VIEW_CLASS, view,
1645 		 image_display_policy_changed, (view));
1646 }
1647 
1648 static void
click_policy_changed_callback(gpointer callback_data)1649 click_policy_changed_callback (gpointer callback_data)
1650 {
1651 	FMDirectoryView *view;
1652 
1653 	view = FM_DIRECTORY_VIEW (callback_data);
1654 
1655 	EEL_CALL_METHOD
1656 		(FM_DIRECTORY_VIEW_CLASS, view,
1657 		 click_policy_changed, (view));
1658 }
1659 
1660 gboolean
fm_directory_view_should_sort_directories_first(FMDirectoryView * view)1661 fm_directory_view_should_sort_directories_first (FMDirectoryView *view)
1662 {
1663 	return view->details->sort_directories_first;
1664 }
1665 
1666 static void
sort_directories_first_changed_callback(gpointer callback_data)1667 sort_directories_first_changed_callback (gpointer callback_data)
1668 {
1669 	FMDirectoryView *view;
1670 	gboolean preference_value;
1671 
1672 	view = FM_DIRECTORY_VIEW (callback_data);
1673 
1674 	preference_value =
1675 		g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_SORT_DIRECTORIES_FIRST);
1676 
1677 	if (preference_value != view->details->sort_directories_first) {
1678 		view->details->sort_directories_first = preference_value;
1679 		EEL_CALL_METHOD
1680 			(FM_DIRECTORY_VIEW_CLASS, view,
1681 			 sort_directories_first_changed, (view));
1682 	}
1683 }
1684 
set_up_scripts_directory_global(void)1685 static void set_up_scripts_directory_global(void)
1686 {
1687 	if (scripts_directory_uri != NULL)
1688 	{
1689 		return;
1690 	}
1691 
1692 	char* scripts_directory_path;
1693 	scripts_directory_path = g_build_filename (g_get_user_config_dir (), "caja", "scripts", NULL);
1694 
1695 	if (g_mkdir_with_parents(scripts_directory_path, 0755) == 0)
1696 	{
1697 		scripts_directory_uri = g_filename_to_uri(scripts_directory_path, NULL, NULL);
1698 		scripts_directory_uri_length = strlen(scripts_directory_uri);
1699 
1700 		/* Support for GNOME Nautilus scripts
1701 		 */
1702 		char* nautilus_scripts_path = g_build_filename(g_get_home_dir(), ".gnome2", "nautilus-scripts", NULL);
1703 
1704 		if (g_file_test(nautilus_scripts_path, G_FILE_TEST_IS_DIR) == TRUE)
1705 		{
1706 			char* nautilus_syslink = g_build_filename(g_get_user_config_dir(), "caja", "scripts", "nautilus", NULL);
1707 			/* If link already exists, or also any other kind of file/dir with same name, ignore it */
1708 			if (g_file_test(nautilus_syslink, G_FILE_TEST_IS_SYMLINK) == FALSE &&
1709 				g_file_test(nautilus_syslink, G_FILE_TEST_EXISTS) == FALSE &&
1710 				g_file_test(nautilus_syslink, G_FILE_TEST_IS_DIR) == FALSE)
1711 			{
1712 				/* Check if we need to create a link */
1713 				GDir* dir = g_dir_open(nautilus_scripts_path, 0, NULL);
1714 
1715 				if (dir)
1716 				{
1717 					/* If directory contains files, we can create the link */
1718 					int count = 0;
1719 
1720 					while (g_dir_read_name(dir) != NULL)
1721 					{
1722 						count++;
1723 					}
1724 
1725 					if (count > 0)
1726 					{
1727 						/* Create link to nautilus folder */
1728 						int res = symlink (nautilus_scripts_path, nautilus_syslink);
1729 						if (res != 0)
1730 							g_warning ("Can't create symlink to nautilus scripts folder");
1731 					}
1732 
1733 					g_dir_close(dir);
1734 				}
1735 			}
1736 
1737 			g_free(nautilus_syslink);
1738 		}
1739 
1740 		g_free(nautilus_scripts_path);
1741 	}
1742 
1743 	g_free(scripts_directory_path);
1744 }
1745 
1746 static void
scripts_added_or_changed_callback(CajaDirectory * directory,GList * files,gpointer callback_data)1747 scripts_added_or_changed_callback (CajaDirectory *directory,
1748 				   GList *files,
1749 				   gpointer callback_data)
1750 {
1751 	FMDirectoryView *view;
1752 
1753 	view = FM_DIRECTORY_VIEW (callback_data);
1754 
1755 	view->details->scripts_invalid = TRUE;
1756 	if (view->details->active) {
1757 		schedule_update_menus (view);
1758 	}
1759 }
1760 
1761 static void
templates_added_or_changed_callback(CajaDirectory * directory,GList * files,gpointer callback_data)1762 templates_added_or_changed_callback (CajaDirectory *directory,
1763 				     GList *files,
1764 				     gpointer callback_data)
1765 {
1766 	FMDirectoryView *view;
1767 
1768 	view = FM_DIRECTORY_VIEW (callback_data);
1769 
1770 	view->details->templates_invalid = TRUE;
1771 	if (view->details->active) {
1772 		schedule_update_menus (view);
1773 	}
1774 }
1775 
1776 static void
add_directory_to_directory_list(FMDirectoryView * view,CajaDirectory * directory,GList ** directory_list,GCallback changed_callback)1777 add_directory_to_directory_list (FMDirectoryView *view,
1778 				 CajaDirectory *directory,
1779 				 GList **directory_list,
1780 				 GCallback changed_callback)
1781 {
1782 	CajaFileAttributes attributes;
1783 
1784 	if (g_list_find (*directory_list, directory) == NULL) {
1785 		caja_directory_ref (directory);
1786 
1787 		attributes =
1788 			CAJA_FILE_ATTRIBUTES_FOR_ICON |
1789 			CAJA_FILE_ATTRIBUTE_INFO |
1790 			CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT;
1791 
1792 		caja_directory_file_monitor_add (directory, directory_list,
1793 						     FALSE, attributes,
1794 						     (CajaDirectoryCallback)changed_callback, view);
1795 
1796 		g_signal_connect_object (directory, "files_added",
1797 					 G_CALLBACK (changed_callback), view, 0);
1798 		g_signal_connect_object (directory, "files_changed",
1799 					 G_CALLBACK (changed_callback), view, 0);
1800 
1801 		*directory_list = g_list_append	(*directory_list, directory);
1802 	}
1803 }
1804 
1805 static void
remove_directory_from_directory_list(FMDirectoryView * view,CajaDirectory * directory,GList ** directory_list,GCallback changed_callback)1806 remove_directory_from_directory_list (FMDirectoryView *view,
1807 				      CajaDirectory *directory,
1808 				      GList **directory_list,
1809 				      GCallback changed_callback)
1810 {
1811 	*directory_list = g_list_remove	(*directory_list, directory);
1812 
1813 	g_signal_handlers_disconnect_by_func (directory,
1814 					      G_CALLBACK (changed_callback),
1815 					      view);
1816 
1817 	caja_directory_file_monitor_remove (directory, directory_list);
1818 
1819 	caja_directory_unref (directory);
1820 }
1821 
1822 
1823 static void
add_directory_to_scripts_directory_list(FMDirectoryView * view,CajaDirectory * directory)1824 add_directory_to_scripts_directory_list (FMDirectoryView *view,
1825 					 CajaDirectory *directory)
1826 {
1827 	add_directory_to_directory_list (view, directory,
1828 					 &view->details->scripts_directory_list,
1829 					 G_CALLBACK (scripts_added_or_changed_callback));
1830 }
1831 
1832 static void
remove_directory_from_scripts_directory_list(FMDirectoryView * view,CajaDirectory * directory)1833 remove_directory_from_scripts_directory_list (FMDirectoryView *view,
1834 					      CajaDirectory *directory)
1835 {
1836 	remove_directory_from_directory_list (view, directory,
1837 					      &view->details->scripts_directory_list,
1838 					      G_CALLBACK (scripts_added_or_changed_callback));
1839 }
1840 
1841 static void
add_directory_to_templates_directory_list(FMDirectoryView * view,CajaDirectory * directory)1842 add_directory_to_templates_directory_list (FMDirectoryView *view,
1843 					   CajaDirectory *directory)
1844 {
1845 	add_directory_to_directory_list (view, directory,
1846 					 &view->details->templates_directory_list,
1847 					 G_CALLBACK (templates_added_or_changed_callback));
1848 }
1849 
1850 static void
remove_directory_from_templates_directory_list(FMDirectoryView * view,CajaDirectory * directory)1851 remove_directory_from_templates_directory_list (FMDirectoryView *view,
1852 						CajaDirectory *directory)
1853 {
1854 	remove_directory_from_directory_list (view, directory,
1855 					      &view->details->templates_directory_list,
1856 					      G_CALLBACK (templates_added_or_changed_callback));
1857 }
1858 
1859 static void
slot_active(CajaWindowSlot * slot,FMDirectoryView * view)1860 slot_active (CajaWindowSlot *slot,
1861 	     FMDirectoryView *view)
1862 {
1863 	g_assert (!view->details->active);
1864 	view->details->active = TRUE;
1865 
1866 	fm_directory_view_merge_menus (view);
1867 	schedule_update_menus (view);
1868 }
1869 
1870 static void
slot_inactive(CajaWindowSlot * slot,FMDirectoryView * view)1871 slot_inactive (CajaWindowSlot *slot,
1872 	       FMDirectoryView *view)
1873 {
1874 	g_assert (view->details->active ||
1875 		  gtk_widget_get_parent (GTK_WIDGET (view)) == NULL);
1876 	view->details->active = FALSE;
1877 
1878 	fm_directory_view_unmerge_menus (view);
1879 	remove_update_menus_timeout_callback (view);
1880 }
1881 
1882 static void
fm_directory_view_grab_focus(CajaView * view)1883 fm_directory_view_grab_focus (CajaView *view)
1884 {
1885 	/* focus the child of the scrolled window if it exists */
1886 	GtkWidget *child;
1887 	child = gtk_bin_get_child (GTK_BIN (view));
1888 	if (child) {
1889 		gtk_widget_grab_focus (GTK_WIDGET (child));
1890 	}
1891 }
1892 
1893 static void
view_iface_update_menus(CajaView * view)1894 view_iface_update_menus (CajaView *view)
1895 {
1896 	fm_directory_view_update_menus (FM_DIRECTORY_VIEW (view));
1897 }
1898 
1899 static GtkWidget *
fm_directory_view_get_widget(CajaView * view)1900 fm_directory_view_get_widget (CajaView *view)
1901 {
1902 	return GTK_WIDGET (view);
1903 }
1904 
1905 static int
fm_directory_view_get_selection_count(CajaView * view)1906 fm_directory_view_get_selection_count (CajaView *view)
1907 {
1908 	/* FIXME: This could be faster if we special cased it in subclasses */
1909 	GList *files;
1910 	int len;
1911 
1912 	files = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
1913 	len = g_list_length (files);
1914 	caja_file_list_free (files);
1915 
1916 	return len;
1917 }
1918 
1919 static GList *
fm_directory_view_get_selection_locations(CajaView * view)1920 fm_directory_view_get_selection_locations (CajaView *view)
1921 {
1922 	GList *files;
1923 	GList *locations;
1924 	GList *l;
1925 	GFile *location = NULL;
1926 
1927 	files = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
1928 	locations = NULL;
1929 	for (l = files; l != NULL; l = l->next) {
1930 		location = caja_file_get_location (CAJA_FILE (l->data));
1931 		locations = g_list_prepend (locations, location);
1932 	}
1933 	caja_file_list_free (files);
1934 
1935 	return g_list_reverse (locations);
1936 }
1937 
1938 static GList *
file_list_from_location_list(const GList * uri_list)1939 file_list_from_location_list (const GList *uri_list)
1940 {
1941 	GList *file_list;
1942 	const GList *node;
1943 
1944 	file_list = NULL;
1945 	for (node = uri_list; node != NULL; node = node->next) {
1946 		file_list = g_list_prepend
1947 			(file_list,
1948 			 caja_file_get (node->data));
1949 	}
1950 	return g_list_reverse (file_list);
1951 }
1952 
1953 static void
fm_directory_view_set_selection_locations(CajaView * caja_view,GList * selection_locations)1954 fm_directory_view_set_selection_locations (CajaView *caja_view,
1955 					   GList *selection_locations)
1956 {
1957 	FMDirectoryView *view;
1958 
1959 	view = FM_DIRECTORY_VIEW (caja_view);
1960 
1961 	if (!view->details->loading) {
1962 		GList *selection;
1963 
1964 		/* If we aren't still loading, set the selection right now,
1965 		 * and reveal the new selection.
1966 		 */
1967 		selection = file_list_from_location_list (selection_locations);
1968 		view->details->selection_change_is_due_to_shell = TRUE;
1969 		fm_directory_view_set_selection (view, selection);
1970 		view->details->selection_change_is_due_to_shell = FALSE;
1971 		fm_directory_view_reveal_selection (view);
1972 		caja_file_list_free (selection);
1973 	} else {
1974 		/* If we are still loading, set the list of pending URIs instead.
1975 		 * done_loading() will eventually select the pending URIs and reveal them.
1976 		 */
1977 		g_list_free_full (view->details->pending_locations_selected, g_object_unref);
1978 		view->details->pending_locations_selected =
1979 			g_list_copy_deep (selection_locations, (GCopyFunc) g_object_ref, NULL);
1980 	}
1981 }
1982 
1983 
1984 void
fm_directory_view_init_view_iface(CajaViewIface * iface)1985 fm_directory_view_init_view_iface (CajaViewIface *iface)
1986 {
1987 	iface->grab_focus = fm_directory_view_grab_focus;
1988 	iface->update_menus = view_iface_update_menus;
1989 
1990 	iface->get_widget = fm_directory_view_get_widget;
1991   	iface->load_location = fm_directory_view_load_location;
1992 	iface->stop_loading = fm_directory_view_stop_loading;
1993 
1994 	iface->get_selection_count = fm_directory_view_get_selection_count;
1995 	iface->get_selection = fm_directory_view_get_selection_locations;
1996 	iface->set_selection = fm_directory_view_set_selection_locations;
1997 	iface->set_is_active = (gpointer)fm_directory_view_set_is_active;
1998 
1999 	iface->supports_zooming = (gpointer)fm_directory_view_supports_zooming;
2000 	iface->bump_zoom_level = (gpointer)fm_directory_view_bump_zoom_level;
2001         iface->zoom_to_level = (gpointer)fm_directory_view_zoom_to_level;
2002         iface->restore_default_zoom_level = (gpointer)fm_directory_view_restore_default_zoom_level;
2003         iface->can_zoom_in = (gpointer)fm_directory_view_can_zoom_in;
2004         iface->can_zoom_out = (gpointer)fm_directory_view_can_zoom_out;
2005 	iface->get_zoom_level = (gpointer)fm_directory_view_get_zoom_level;
2006 
2007 	iface->pop_up_location_context_menu = (gpointer)fm_directory_view_pop_up_location_context_menu;
2008 	iface->drop_proxy_received_uris = (gpointer)fm_directory_view_drop_proxy_received_uris;
2009 	iface->drop_proxy_received_netscape_url = (gpointer)fm_directory_view_drop_proxy_received_netscape_url;
2010 }
2011 
2012 static void
fm_directory_view_init(FMDirectoryView * view)2013 fm_directory_view_init (FMDirectoryView *view)
2014 {
2015 	CajaDirectory *scripts_directory;
2016 
2017 	view->details = g_new0 (FMDirectoryViewDetails, 1);
2018 
2019 	/* Default to true; desktop-icon-view sets to false */
2020 	view->details->show_foreign_files = TRUE;
2021 
2022 	view->details->non_ready_files =
2023 		g_hash_table_new_full (file_and_directory_hash,
2024 				       file_and_directory_equal,
2025 				       (GDestroyNotify)file_and_directory_free,
2026 				       NULL);
2027 
2028 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view),
2029 					GTK_POLICY_AUTOMATIC,
2030 					GTK_POLICY_AUTOMATIC);
2031 	gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (view), NULL);
2032 	gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (view), NULL);
2033 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view), GTK_SHADOW_ETCHED_IN);
2034 	gtk_scrolled_window_set_overlay_scrolling (GTK_SCROLLED_WINDOW (view), FALSE);
2035 
2036 	set_up_scripts_directory_global ();
2037 	scripts_directory = caja_directory_get_by_uri (scripts_directory_uri);
2038 	add_directory_to_scripts_directory_list (view, scripts_directory);
2039 	caja_directory_unref (scripts_directory);
2040 
2041 	if (caja_should_use_templates_directory ()) {
2042 		CajaDirectory *templates_directory;
2043 		char *templates_uri;
2044 
2045 		templates_uri = caja_get_templates_directory_uri ();
2046 		templates_directory = caja_directory_get_by_uri (templates_uri);
2047 		g_free (templates_uri);
2048 		add_directory_to_templates_directory_list (view, templates_directory);
2049 		caja_directory_unref (templates_directory);
2050 	}
2051 	update_templates_directory (view);
2052 	g_signal_connect_object (caja_signaller_get_current (),
2053 				 "user_dirs_changed",
2054 				 G_CALLBACK (user_dirs_changed),
2055 				 view, G_CONNECT_SWAPPED);
2056 
2057 	view->details->sort_directories_first =
2058 		g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_SORT_DIRECTORIES_FIRST);
2059 
2060 	g_signal_connect_object (caja_trash_monitor_get (), "trash_state_changed",
2061 				 G_CALLBACK (fm_directory_view_trash_state_changed_callback), view, 0);
2062 
2063 	/* React to clipboard changes */
2064 	g_signal_connect_object (caja_clipboard_monitor_get (), "clipboard_changed",
2065 				 G_CALLBACK (clipboard_changed_callback), view, 0);
2066 
2067         /* Register to menu provider extension signal managing menu updates */
2068         g_signal_connect_object (caja_signaller_get_current (), "popup_menu_changed",
2069                          G_CALLBACK (fm_directory_view_update_menus), view, G_CONNECT_SWAPPED);
2070 
2071 	gtk_widget_show (GTK_WIDGET (view));
2072 
2073 	g_signal_connect_swapped (caja_preferences,
2074 							  "changed::" CAJA_PREFERENCES_ENABLE_DELETE,
2075 							  G_CALLBACK (schedule_update_menus_callback), view);
2076 	g_signal_connect_swapped (caja_icon_view_preferences,
2077 							  "changed::" CAJA_PREFERENCES_ICON_VIEW_CAPTIONS,
2078 							  G_CALLBACK(text_attribute_names_changed_callback),
2079 							  view);
2080 	g_signal_connect_swapped (caja_preferences,
2081 							  "changed::" CAJA_PREFERENCES_SHOW_IMAGE_FILE_THUMBNAILS,
2082 							  G_CALLBACK (image_display_policy_changed_callback),
2083 							  view);
2084 	g_signal_connect_swapped (caja_preferences,
2085 							  "changed::" CAJA_PREFERENCES_CLICK_POLICY,
2086 							  G_CALLBACK(click_policy_changed_callback),
2087 							  view);
2088 	g_signal_connect_swapped (caja_preferences,
2089 							  "changed::" CAJA_PREFERENCES_SORT_DIRECTORIES_FIRST,
2090 							  G_CALLBACK(sort_directories_first_changed_callback),
2091 							  view);
2092 	g_signal_connect_swapped (mate_lockdown_preferences,
2093 							  "changed::" CAJA_PREFERENCES_LOCKDOWN_COMMAND_LINE,
2094 							  G_CALLBACK (schedule_update_menus), view);
2095 
2096 	/* Update undo actions stuff and connect signals from the undostack manager */
2097 	view->details->undo_active = FALSE;
2098 	view->details->redo_active = FALSE;
2099 	view->details->undo_action_description = NULL;
2100 	view->details->undo_action_label = NULL;
2101 	view->details->redo_action_description = NULL;
2102 	view->details->redo_action_label = NULL;
2103 
2104 	CajaUndoStackManager* manager = caja_undostack_manager_instance ();
2105 
2106 	g_signal_connect_object (G_OBJECT(manager), "request-menu-update",
2107 		   G_CALLBACK(undo_redo_menu_update_callback), view, 0);
2108 
2109 	caja_undostack_manager_request_menu_update (caja_undostack_manager_instance());
2110 }
2111 
2112 static void
real_unmerge_menus(FMDirectoryView * view)2113 real_unmerge_menus (FMDirectoryView *view)
2114 {
2115 	GtkUIManager *ui_manager;
2116 
2117 	if (view->details->window == NULL) {
2118 		return;
2119 	}
2120 
2121 	ui_manager = caja_window_info_get_ui_manager (view->details->window);
2122 
2123 	caja_ui_unmerge_ui (ui_manager,
2124 				&view->details->dir_merge_id,
2125 				&view->details->dir_action_group);
2126 	caja_ui_unmerge_ui (ui_manager,
2127 				&view->details->extensions_menu_merge_id,
2128 				&view->details->extensions_menu_action_group);
2129 	caja_ui_unmerge_ui (ui_manager,
2130 				&view->details->open_with_merge_id,
2131 				&view->details->open_with_action_group);
2132 	caja_ui_unmerge_ui (ui_manager,
2133 				&view->details->scripts_merge_id,
2134 				&view->details->scripts_action_group);
2135 	caja_ui_unmerge_ui (ui_manager,
2136 				&view->details->templates_merge_id,
2137 				&view->details->templates_action_group);
2138 }
2139 
2140 static void
fm_directory_view_destroy(GtkWidget * object)2141 fm_directory_view_destroy (GtkWidget *object)
2142 {
2143 	FMDirectoryView *view;
2144 	GList *node, *next;
2145 
2146 	view = FM_DIRECTORY_VIEW (object);
2147 
2148 	disconnect_model_handlers (view);
2149 
2150 	fm_directory_view_unmerge_menus (view);
2151 
2152 	/* We don't own the window, so no unref */
2153 	view->details->slot = NULL;
2154 	view->details->window = NULL;
2155 
2156 	fm_directory_view_stop (view);
2157 	fm_directory_view_clear (view);
2158 
2159 	for (node = view->details->scripts_directory_list; node != NULL; node = next) {
2160 		next = node->next;
2161 		remove_directory_from_scripts_directory_list (view, node->data);
2162 	}
2163 
2164 	for (node = view->details->templates_directory_list; node != NULL; node = next) {
2165 		next = node->next;
2166 		remove_directory_from_templates_directory_list (view, node->data);
2167 	}
2168 
2169 	while (view->details->subdirectory_list != NULL) {
2170 		fm_directory_view_remove_subdirectory (view,
2171 				view->details->subdirectory_list->data);
2172 	}
2173 
2174 	remove_update_menus_timeout_callback (view);
2175 	remove_update_status_idle_callback (view);
2176 
2177 	if (view->details->display_selection_idle_id != 0) {
2178 		g_source_remove (view->details->display_selection_idle_id);
2179 		view->details->display_selection_idle_id = 0;
2180 	}
2181 
2182 	if (view->details->reveal_selection_idle_id != 0) {
2183 		g_source_remove (view->details->reveal_selection_idle_id);
2184 		view->details->reveal_selection_idle_id = 0;
2185 	}
2186 
2187 	if (view->details->delayed_rename_file_id != 0) {
2188 		g_source_remove (view->details->delayed_rename_file_id);
2189 		view->details->delayed_rename_file_id = 0;
2190 	}
2191 
2192 	if (view->details->model) {
2193 		caja_directory_unref (view->details->model);
2194 		view->details->model = NULL;
2195 	}
2196 
2197 	if (view->details->directory_as_file) {
2198 		caja_file_unref (view->details->directory_as_file);
2199 		view->details->directory_as_file = NULL;
2200 	}
2201 
2202 	EEL_CALL_PARENT (GTK_WIDGET_CLASS, destroy, (object));
2203 }
2204 
2205 static void
fm_directory_view_finalize(GObject * object)2206 fm_directory_view_finalize (GObject *object)
2207 {
2208 	FMDirectoryView *view;
2209 
2210 	view = FM_DIRECTORY_VIEW (object);
2211 
2212 	g_signal_handlers_disconnect_by_func (caja_preferences,
2213         				      schedule_update_menus_callback, view);
2214 	g_signal_handlers_disconnect_by_func (caja_icon_view_preferences,
2215         				      text_attribute_names_changed_callback, view);
2216 	g_signal_handlers_disconnect_by_func (caja_preferences,
2217         				      image_display_policy_changed_callback, view);
2218 	g_signal_handlers_disconnect_by_func (caja_preferences,
2219         				      click_policy_changed_callback, view);
2220 	g_signal_handlers_disconnect_by_func (caja_preferences,
2221         				      sort_directories_first_changed_callback, view);
2222 	g_signal_handlers_disconnect_by_func (mate_lockdown_preferences,
2223         				      schedule_update_menus, view);
2224 
2225 	unschedule_pop_up_location_context_menu (view);
2226 	if (view->details->location_popup_event != NULL) {
2227 		gdk_event_free ((GdkEvent *) view->details->location_popup_event);
2228 	}
2229 
2230 	g_hash_table_destroy (view->details->non_ready_files);
2231 
2232 	g_free (view->details);
2233 
2234 	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
2235 }
2236 
2237 /**
2238  * fm_directory_view_display_selection_info:
2239  *
2240  * Display information about the current selection, and notify the view frame of the changed selection.
2241  * @view: FMDirectoryView for which to display selection info.
2242  *
2243  **/
2244 void
fm_directory_view_display_selection_info(FMDirectoryView * view)2245 fm_directory_view_display_selection_info (FMDirectoryView *view)
2246 {
2247 	GList *selection;
2248 	goffset non_folder_size;
2249 	gboolean non_folder_size_known;
2250 	guint non_folder_count, folder_count, folder_item_count;
2251 	gboolean folder_item_count_known;
2252 	guint file_item_count;
2253 	GList *p;
2254 	char *first_item_name;
2255 	char *non_folder_str;
2256 	char *folder_count_str;
2257 	char *folder_item_count_str;
2258 	char *status_string;
2259 	char *free_space_str;
2260 	char *obj_selected_free_space_str;
2261 	CajaFile *file = NULL;
2262 
2263 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
2264 
2265 	selection = fm_directory_view_get_selection (view);
2266 
2267 	folder_item_count_known = TRUE;
2268 	folder_count = 0;
2269 	folder_item_count = 0;
2270 	non_folder_count = 0;
2271 	non_folder_size_known = FALSE;
2272 	non_folder_size = 0;
2273 	first_item_name = NULL;
2274 	folder_count_str = NULL;
2275 	non_folder_str = NULL;
2276 	folder_item_count_str = NULL;
2277 	free_space_str = NULL;
2278 	obj_selected_free_space_str = NULL;
2279 
2280 	for (p = selection; p != NULL; p = p->next) {
2281 		file = p->data;
2282 		if (caja_file_is_directory (file)) {
2283 			folder_count++;
2284 			if (caja_file_get_directory_item_count (file, &file_item_count, NULL)) {
2285 				folder_item_count += file_item_count;
2286 			} else {
2287 				folder_item_count_known = FALSE;
2288 			}
2289 		} else {
2290 			non_folder_count++;
2291 			if (!caja_file_can_get_size (file)) {
2292 				non_folder_size_known = TRUE;
2293 				non_folder_size += caja_file_get_size (file);
2294 			}
2295 		}
2296 
2297 		if (first_item_name == NULL) {
2298 			first_item_name = caja_file_get_display_name (file);
2299 		}
2300 	}
2301 
2302 	caja_file_list_free (selection);
2303 
2304 	/* Break out cases for localization's sake. But note that there are still pieces
2305 	 * being assembled in a particular order, which may be a problem for some localizers.
2306 	 */
2307 
2308 	if (folder_count != 0) {
2309 		if (folder_count == 1 && non_folder_count == 0) {
2310 			folder_count_str = g_strdup_printf (_("\"%s\" selected"), first_item_name);
2311 		} else {
2312 			folder_count_str = g_strdup_printf (ngettext("%'d folder selected",
2313 								     "%'d folders selected",
2314 								     folder_count),
2315 							    folder_count);
2316 		}
2317 
2318 		if (folder_count == 1) {
2319 			if (!folder_item_count_known) {
2320 				folder_item_count_str = g_strdup ("");
2321 			} else {
2322 				folder_item_count_str = g_strdup_printf (ngettext(" (containing %'d item)",
2323 										  " (containing %'d items)",
2324 										  folder_item_count),
2325 									 folder_item_count);
2326 			}
2327 		}
2328 		else {
2329 			if (!folder_item_count_known) {
2330 				folder_item_count_str = g_strdup ("");
2331 			} else {
2332 				/* Translators: this is preceded with a string of form 'N folders' (N more than 1) */
2333 				folder_item_count_str = g_strdup_printf (ngettext(" (containing a total of %'d item)",
2334 										  " (containing a total of %'d items)",
2335 										  folder_item_count),
2336 									 folder_item_count);
2337 			}
2338 
2339 		}
2340 	}
2341 
2342 	if (non_folder_count != 0) {
2343 		char *items_string;
2344 
2345 		if (folder_count == 0) {
2346 			if (non_folder_count == 1) {
2347 				items_string = g_strdup_printf (_("\"%s\" selected"),
2348 								  first_item_name);
2349 			} else {
2350 				items_string = g_strdup_printf (ngettext("%'d item selected",
2351 									   "%'d items selected",
2352 									   non_folder_count),
2353 								  non_folder_count);
2354 			}
2355 		} else {
2356 			/* Folders selected also, use "other" terminology */
2357 			items_string = g_strdup_printf (ngettext("%'d other item selected",
2358 								   "%'d other items selected",
2359 								   non_folder_count),
2360 							  non_folder_count);
2361 		}
2362 
2363 		if (non_folder_size_known) {
2364 			char *size_string;
2365 
2366 			if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_USE_IEC_UNITS))
2367 				size_string = g_format_size_full (non_folder_size, G_FORMAT_SIZE_IEC_UNITS);
2368 			else
2369 				size_string = g_format_size(non_folder_size);
2370 
2371 			/* Translators: This is marked for translation in case a localiser
2372 			 * needs to use something other than parentheses. The
2373 			 * first message gives the number of items selected;
2374 			 * the message in parentheses the size of those items.
2375 			 */
2376 			non_folder_str = g_strdup_printf (_("%s (%s)"),
2377 							  items_string,
2378 							  size_string);
2379 
2380 			g_free (size_string);
2381 			g_free (items_string);
2382 		} else {
2383 			non_folder_str = items_string;
2384 		}
2385 	}
2386 
2387 	free_space_str = caja_file_get_volume_free_space (view->details->directory_as_file);
2388 	if (free_space_str != NULL) {
2389 		obj_selected_free_space_str = g_strdup_printf (_("Free space: %s"), free_space_str);
2390 	}
2391 	if (folder_count == 0 && non_folder_count == 0)	{
2392 		char *item_count_str;
2393 		guint item_count;
2394 
2395 		item_count = fm_directory_view_get_item_count (view);
2396 
2397 		item_count_str = g_strdup_printf (ngettext ("%'u item", "%'u items", item_count), item_count);
2398 
2399 		if (free_space_str != NULL) {
2400 			status_string = g_strdup_printf (_("%s, Free space: %s"), item_count_str, free_space_str);
2401 			g_free (item_count_str);
2402 		} else {
2403 			status_string = item_count_str;
2404 		}
2405 
2406 	} else if (folder_count == 0) {
2407 		if (free_space_str == NULL) {
2408 			status_string = g_strdup (non_folder_str);
2409 		} else {
2410 			/* Marking this for translation, since you
2411 			 * might want to change "," to something else.
2412 			 * After the comma the amount of free space will
2413 			 * be shown.
2414 			 */
2415 			status_string = g_strdup_printf (_("%s, %s"),
2416 							 non_folder_str,
2417 							 obj_selected_free_space_str);
2418 		}
2419 	} else if (non_folder_count == 0) {
2420 		if (free_space_str == NULL) {
2421 		        /* No use marking this for translation, since you
2422 		         * can't reorder the strings, which is the main thing
2423 		         * you'd want to do.
2424 		         */
2425 			status_string = g_strdup_printf ("%s%s",
2426 							 folder_count_str,
2427 							 folder_item_count_str);
2428 		} else {
2429 			/* Marking this for translation, since you
2430 			 * might want to change "," to something else.
2431 			 * After the comma the amount of free space will
2432 			 * be shown.
2433 			 */
2434 			status_string = g_strdup_printf (_("%s%s, %s"),
2435 							 folder_count_str,
2436 							 folder_item_count_str,
2437 							 obj_selected_free_space_str);
2438 		}
2439 	} else {
2440 		if (obj_selected_free_space_str == NULL) {
2441 			/* This is marked for translation in case a localizer
2442 			 * needs to change ", " to something else. The comma
2443 			 * is between the message about the number of folders
2444 			 * and the number of items in those folders and the
2445 			 * message about the number of other items and the
2446 			 * total size of those items.
2447 			 */
2448 			status_string = g_strdup_printf (_("%s%s, %s"),
2449 							 folder_count_str,
2450 							 folder_item_count_str,
2451 							 non_folder_str);
2452 		} else {
2453 			/* This is marked for translation in case a localizer
2454 			 * needs to change ", " to something else. The first comma
2455 			 * is between the message about the number of folders
2456 			 * and the number of items in those folders and the
2457 			 * message about the number of other items and the
2458 			 * total size of those items. After the second comma
2459 			 * the free space is written.
2460 			 */
2461 			status_string = g_strdup_printf (_("%s%s, %s, %s"),
2462 							 folder_count_str,
2463 							 folder_item_count_str,
2464 							 non_folder_str,
2465 							 obj_selected_free_space_str);
2466 		}
2467 	}
2468 
2469 	g_free (free_space_str);
2470 	g_free (obj_selected_free_space_str);
2471 	g_free (first_item_name);
2472 	g_free (folder_count_str);
2473 	g_free (folder_item_count_str);
2474 	g_free (non_folder_str);
2475 
2476 	caja_window_slot_info_set_status (view->details->slot,
2477 					      status_string);
2478 	g_free (status_string);
2479 }
2480 
2481 void
fm_directory_view_send_selection_change(FMDirectoryView * view)2482 fm_directory_view_send_selection_change (FMDirectoryView *view)
2483 {
2484 	caja_window_info_report_selection_changed (view->details->window);
2485 
2486 	view->details->send_selection_change_to_shell = FALSE;
2487 }
2488 
2489 gboolean
fm_directory_view_get_allow_moves(FMDirectoryView * view)2490 fm_directory_view_get_allow_moves (FMDirectoryView *view)
2491 {
2492 	return view->details->allow_moves;
2493 }
2494 
2495 static void
fm_directory_view_load_location(CajaView * caja_view,const char * location)2496 fm_directory_view_load_location (CajaView *caja_view,
2497 				 const char *location)
2498 {
2499 	CajaDirectory *directory;
2500 	FMDirectoryView *directory_view;
2501 
2502 	directory_view = FM_DIRECTORY_VIEW (caja_view);
2503 
2504 	if (eel_uri_is_search (location)) {
2505 		directory_view->details->allow_moves = FALSE;
2506 	} else {
2507 		directory_view->details->allow_moves = TRUE;
2508 	}
2509 
2510 	directory = caja_directory_get_by_uri (location);
2511 	load_directory (directory_view, directory);
2512 	caja_directory_unref (directory);
2513 }
2514 
2515 static void
fm_directory_view_stop_loading(CajaView * caja_view)2516 fm_directory_view_stop_loading (CajaView *caja_view)
2517 {
2518 	fm_directory_view_stop (FM_DIRECTORY_VIEW (caja_view));
2519 }
2520 
2521 static gboolean
reveal_selection_idle_callback(gpointer data)2522 reveal_selection_idle_callback (gpointer data)
2523 {
2524 	FMDirectoryView *view;
2525 
2526 	view = FM_DIRECTORY_VIEW (data);
2527 
2528 	view->details->reveal_selection_idle_id = 0;
2529 	fm_directory_view_reveal_selection (view);
2530 
2531 	return FALSE;
2532 }
2533 
2534 static void
done_loading(FMDirectoryView * view,gboolean all_files_seen)2535 done_loading (FMDirectoryView *view,
2536 	      gboolean all_files_seen)
2537 {
2538 	if (!view->details->loading) {
2539 		return;
2540 	}
2541 
2542 	/* This can be called during destruction, in which case there
2543 	 * is no CajaWindowInfo any more.
2544 	 */
2545 	if (view->details->window != NULL) {
2546 		GList *locations_selected;
2547 
2548 		if (all_files_seen) {
2549 			caja_window_info_report_load_complete (view->details->window, CAJA_VIEW (view));
2550 		}
2551 
2552 		schedule_update_menus (view);
2553 		schedule_update_status (view);
2554 		reset_update_interval (view);
2555 
2556 		locations_selected = view->details->pending_locations_selected;
2557 
2558 		if (locations_selected != NULL && all_files_seen) {
2559 			GList *selection;
2560 
2561 			view->details->pending_locations_selected = NULL;
2562 
2563 			selection = file_list_from_location_list (locations_selected);
2564 
2565 			view->details->selection_change_is_due_to_shell = TRUE;
2566 			fm_directory_view_set_selection (view, selection);
2567 			view->details->selection_change_is_due_to_shell = FALSE;
2568 			caja_file_list_free (selection);
2569 
2570 			if (FM_IS_LIST_VIEW (view)) {
2571 				/* HACK: We should be able to directly call reveal_selection here,
2572 				 * but at this point the GtkTreeView hasn't allocated the new nodes
2573 				 * yet, and it has a bug in the scroll calculation dealing with this
2574 				 * special case. It would always make the selection the top row, even
2575 				 * if no scrolling would be neccessary to reveal it. So we let it
2576 				 * allocate before revealing.
2577 				 */
2578 				if (view->details->reveal_selection_idle_id != 0) {
2579 					g_source_remove (view->details->reveal_selection_idle_id);
2580 				}
2581 				view->details->reveal_selection_idle_id =
2582 					g_idle_add (reveal_selection_idle_callback, view);
2583 			} else {
2584 				fm_directory_view_reveal_selection (view);
2585 			}
2586 		}
2587 		g_list_free_full (locations_selected, g_object_unref);
2588 		fm_directory_view_display_selection_info (view);
2589 	}
2590 
2591 	fm_directory_view_end_loading (view, all_files_seen);
2592 
2593 	view->details->loading = FALSE;
2594 }
2595 
2596 
2597 typedef struct {
2598 	GHashTable *debuting_files;
2599 	GList	   *added_files;
2600 } DebutingFilesData;
2601 
2602 static void
debuting_files_data_free(DebutingFilesData * data)2603 debuting_files_data_free (DebutingFilesData *data)
2604 {
2605 	g_hash_table_unref (data->debuting_files);
2606 	caja_file_list_free (data->added_files);
2607 	g_free (data);
2608 }
2609 
2610 /* This signal handler watch for the arrival of the icons created
2611  * as the result of a file operation. Once the last one is detected
2612  * it selects and reveals them all.
2613  */
2614 static void
debuting_files_add_file_callback(FMDirectoryView * view,CajaFile * new_file,CajaDirectory * directory,DebutingFilesData * data)2615 debuting_files_add_file_callback (FMDirectoryView *view,
2616 				  CajaFile *new_file,
2617 				  CajaDirectory *directory,
2618 				  DebutingFilesData *data)
2619 {
2620 	GFile *location;
2621 
2622 	location = caja_file_get_location (new_file);
2623 
2624 	if (g_hash_table_remove (data->debuting_files, location)) {
2625 		caja_file_ref (new_file);
2626 		data->added_files = g_list_prepend (data->added_files, new_file);
2627 
2628 		if (g_hash_table_size (data->debuting_files) == 0) {
2629 			fm_directory_view_set_selection (view, data->added_files);
2630 			fm_directory_view_reveal_selection (view);
2631 			g_signal_handlers_disconnect_by_func (view,
2632 							      G_CALLBACK (debuting_files_add_file_callback),
2633 							      data);
2634 		}
2635 	}
2636 
2637 	g_object_unref (location);
2638 }
2639 
2640 typedef struct {
2641 	GList		*added_files;
2642 	FMDirectoryView *directory_view;
2643 } CopyMoveDoneData;
2644 
2645 static void
copy_move_done_data_free(CopyMoveDoneData * data)2646 copy_move_done_data_free (CopyMoveDoneData *data)
2647 {
2648 	g_assert (data != NULL);
2649 
2650 	eel_remove_weak_pointer (&data->directory_view);
2651 	caja_file_list_free (data->added_files);
2652 	g_free (data);
2653 }
2654 
2655 static void
pre_copy_move_add_file_callback(FMDirectoryView * view,CajaFile * new_file,CajaDirectory * directory,CopyMoveDoneData * data)2656 pre_copy_move_add_file_callback (FMDirectoryView *view,
2657 				 CajaFile *new_file,
2658 				 CajaDirectory *directory,
2659 				 CopyMoveDoneData *data)
2660 {
2661 	caja_file_ref (new_file);
2662 	data->added_files = g_list_prepend (data->added_files, new_file);
2663 }
2664 
2665 /* This needs to be called prior to caja_file_operations_copy_move.
2666  * It hooks up a signal handler to catch any icons that get added before
2667  * the copy_done_callback is invoked. The return value should  be passed
2668  * as the data for uri_copy_move_done_callback.
2669  */
2670 static CopyMoveDoneData *
pre_copy_move(FMDirectoryView * directory_view)2671 pre_copy_move (FMDirectoryView *directory_view)
2672 {
2673 	CopyMoveDoneData *copy_move_done_data;
2674 
2675 	copy_move_done_data = g_new0 (CopyMoveDoneData, 1);
2676 	copy_move_done_data->directory_view = directory_view;
2677 
2678 	eel_add_weak_pointer (&copy_move_done_data->directory_view);
2679 
2680 	/* We need to run after the default handler adds the folder we want to
2681 	 * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
2682 	 * must use connect_after.
2683 	 */
2684 	g_signal_connect (directory_view, "add_file",
2685 			  G_CALLBACK (pre_copy_move_add_file_callback), copy_move_done_data);
2686 
2687 	return copy_move_done_data;
2688 }
2689 
2690 /* This function is used to pull out any debuting uris that were added
2691  * and (as a side effect) remove them from the debuting uri hash table.
2692  */
2693 static gboolean
copy_move_done_partition_func(gpointer data,gpointer callback_data)2694 copy_move_done_partition_func (gpointer data, gpointer callback_data)
2695 {
2696  	GFile *location;
2697  	gboolean result;
2698 
2699 	location = caja_file_get_location (CAJA_FILE (data));
2700 	result = g_hash_table_remove ((GHashTable *) callback_data, location);
2701 	g_object_unref (location);
2702 
2703 	return result;
2704 }
2705 
2706 static gboolean
remove_not_really_moved_files(gpointer key,gpointer value,gpointer callback_data)2707 remove_not_really_moved_files (gpointer key,
2708 			       gpointer value,
2709 			       gpointer callback_data)
2710 {
2711 	GList **added_files;
2712 	GFile *loc;
2713 
2714 	loc = key;
2715 
2716 	if (GPOINTER_TO_INT (value)) {
2717 		return FALSE;
2718 	}
2719 
2720 	added_files = callback_data;
2721 	*added_files = g_list_prepend (*added_files,
2722 				       caja_file_get (loc));
2723 	return TRUE;
2724 }
2725 
2726 
2727 /* When this function is invoked, the file operation is over, but all
2728  * the icons may not have been added to the directory view yet, so
2729  * we can't select them yet.
2730  *
2731  * We're passed a hash table of the uri's to look out for, we hook
2732  * up a signal handler to await their arrival.
2733  */
2734 static void
copy_move_done_callback(GHashTable * debuting_files,gpointer data)2735 copy_move_done_callback (GHashTable *debuting_files, gpointer data)
2736 {
2737 	FMDirectoryView  *directory_view;
2738 	CopyMoveDoneData *copy_move_done_data;
2739 	DebutingFilesData  *debuting_files_data;
2740 
2741 	copy_move_done_data = (CopyMoveDoneData *) data;
2742 	directory_view = copy_move_done_data->directory_view;
2743 
2744 	if (directory_view != NULL) {
2745 		g_assert (FM_IS_DIRECTORY_VIEW (directory_view));
2746 
2747 		debuting_files_data = g_new (DebutingFilesData, 1);
2748 		debuting_files_data->debuting_files = g_hash_table_ref (debuting_files);
2749 		debuting_files_data->added_files = eel_g_list_partition
2750 			(copy_move_done_data->added_files,
2751 			 copy_move_done_partition_func,
2752 			 debuting_files,
2753 			 &copy_move_done_data->added_files);
2754 
2755 		/* We're passed the same data used by pre_copy_move_add_file_callback, so disconnecting
2756 		 * it will free data. We've already siphoned off the added_files we need, and stashed the
2757 		 * directory_view pointer.
2758 		 */
2759 		g_signal_handlers_disconnect_by_func (directory_view,
2760 						      G_CALLBACK (pre_copy_move_add_file_callback),
2761 						      data);
2762 
2763 		/* Any items in the debuting_files hash table that have
2764 		 * "FALSE" as their value aren't really being copied
2765 		 * or moved, so we can't wait for an add_file signal
2766 		 * to come in for those.
2767 		 */
2768 		g_hash_table_foreach_remove (debuting_files,
2769 					     remove_not_really_moved_files,
2770 					     &debuting_files_data->added_files);
2771 
2772 		if (g_hash_table_size (debuting_files) == 0) {
2773 			/* on the off-chance that all the icons have already been added */
2774 			if (debuting_files_data->added_files != NULL) {
2775 				fm_directory_view_set_selection (directory_view,
2776 								 debuting_files_data->added_files);
2777 				fm_directory_view_reveal_selection (directory_view);
2778 			}
2779 			debuting_files_data_free (debuting_files_data);
2780 		} else {
2781 			/* We need to run after the default handler adds the folder we want to
2782 			 * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
2783 			 * must use connect_after.
2784 			 */
2785 			g_signal_connect_data (directory_view,
2786 					       "add_file",
2787 					       G_CALLBACK (debuting_files_add_file_callback),
2788 					       debuting_files_data,
2789 					       (GClosureNotify) debuting_files_data_free,
2790 					       G_CONNECT_AFTER);
2791 		}
2792 
2793 		/* Schedule menu update for undo items */
2794 		schedule_update_menus (directory_view);
2795 
2796 	}
2797 
2798 	copy_move_done_data_free (copy_move_done_data);
2799 }
2800 
2801 static gboolean
real_file_still_belongs(FMDirectoryView * view,CajaFile * file,CajaDirectory * directory)2802 real_file_still_belongs (FMDirectoryView *view, CajaFile *file, CajaDirectory *directory)
2803 {
2804 	if (view->details->model != directory &&
2805 	    g_list_find (view->details->subdirectory_list, directory) == NULL) {
2806 		return FALSE;
2807 	}
2808 
2809 	return caja_directory_contains_file (directory, file);
2810 }
2811 
2812 static gboolean
still_should_show_file(FMDirectoryView * view,CajaFile * file,CajaDirectory * directory)2813 still_should_show_file (FMDirectoryView *view, CajaFile *file, CajaDirectory *directory)
2814 {
2815 	return fm_directory_view_should_show_file (view, file)
2816 		&& EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, file_still_belongs, (view, file, directory));
2817 }
2818 
2819 static gboolean
ready_to_load(CajaFile * file)2820 ready_to_load (CajaFile *file)
2821 {
2822 	return caja_file_check_if_ready (file,
2823 					     CAJA_FILE_ATTRIBUTES_FOR_ICON);
2824 }
2825 
2826 static int
compare_files_cover(gconstpointer a,gconstpointer b,gpointer callback_data)2827 compare_files_cover (gconstpointer a, gconstpointer b, gpointer callback_data)
2828 {
2829 	const FileAndDirectory *fad1, *fad2;
2830 	FMDirectoryView *view;
2831 
2832 	view = callback_data;
2833 	fad1 = a; fad2 = b;
2834 
2835 	if (fad1->directory < fad2->directory) {
2836 		return -1;
2837 	} else if (fad1->directory > fad2->directory) {
2838 		return 1;
2839 	} else {
2840 		return EEL_INVOKE_METHOD (FM_DIRECTORY_VIEW_CLASS, view, compare_files,
2841 					  (view, fad1->file, fad2->file));
2842 	}
2843 }
2844 static void
sort_files(FMDirectoryView * view,GList ** list)2845 sort_files (FMDirectoryView *view, GList **list)
2846 {
2847 	*list = g_list_sort_with_data (*list, compare_files_cover, view);
2848 
2849 }
2850 
2851 /* Go through all the new added and changed files.
2852  * Put any that are not ready to load in the non_ready_files hash table.
2853  * Add all the rest to the old_added_files and old_changed_files lists.
2854  * Sort the old_*_files lists if anything was added to them.
2855  */
2856 static void
process_new_files(FMDirectoryView * view)2857 process_new_files (FMDirectoryView *view)
2858 {
2859 	GList *new_added_files, *new_changed_files, *old_added_files, *old_changed_files;
2860 	GHashTable *non_ready_files;
2861 	GList *node, *next;
2862 	FileAndDirectory *pending;
2863 	gboolean in_non_ready;
2864 
2865 	new_added_files = view->details->new_added_files;
2866 	view->details->new_added_files = NULL;
2867 	new_changed_files = view->details->new_changed_files;
2868 	view->details->new_changed_files = NULL;
2869 
2870 	non_ready_files = view->details->non_ready_files;
2871 
2872 	old_added_files = view->details->old_added_files;
2873 	old_changed_files = view->details->old_changed_files;
2874 
2875 	/* Newly added files go into the old_added_files list if they're
2876 	 * ready, and into the hash table if they're not.
2877 	 */
2878 	for (node = new_added_files; node != NULL; node = next) {
2879 		next = node->next;
2880 		pending = (FileAndDirectory *)node->data;
2881 		in_non_ready = g_hash_table_lookup (non_ready_files, pending) != NULL;
2882 		if (fm_directory_view_should_show_file (view, pending->file)) {
2883 			if (ready_to_load (pending->file)) {
2884 				if (in_non_ready) {
2885 					g_hash_table_remove (non_ready_files, pending);
2886 				}
2887 				new_added_files = g_list_delete_link (new_added_files, node);
2888 				old_added_files = g_list_prepend (old_added_files, pending);
2889 			} else {
2890 				if (!in_non_ready) {
2891 					new_added_files = g_list_delete_link (new_added_files, node);
2892 					g_hash_table_insert (non_ready_files, pending, pending);
2893 				}
2894 			}
2895 		}
2896 	}
2897 	file_and_directory_list_free (new_added_files);
2898 
2899 	/* Newly changed files go into the old_added_files list if they're ready
2900 	 * and were seen non-ready in the past, into the old_changed_files list
2901 	 * if they are read and were not seen non-ready in the past, and into
2902 	 * the hash table if they're not ready.
2903 	 */
2904 	for (node = new_changed_files; node != NULL; node = next) {
2905 		next = node->next;
2906 		pending = (FileAndDirectory *)node->data;
2907 		if (!still_should_show_file (view, pending->file, pending->directory) || ready_to_load (pending->file)) {
2908 			if (g_hash_table_lookup (non_ready_files, pending) != NULL) {
2909 				g_hash_table_remove (non_ready_files, pending);
2910 				if (still_should_show_file (view, pending->file, pending->directory)) {
2911 					new_changed_files = g_list_delete_link (new_changed_files, node);
2912 					old_added_files = g_list_prepend (old_added_files, pending);
2913 				}
2914 			} else if (fm_directory_view_should_show_file (view, pending->file)) {
2915 				new_changed_files = g_list_delete_link (new_changed_files, node);
2916 				old_changed_files = g_list_prepend (old_changed_files, pending);
2917 			}
2918 		}
2919 	}
2920 	file_and_directory_list_free (new_changed_files);
2921 
2922 	/* If any files were added to old_added_files, then resort it. */
2923 	if (old_added_files != view->details->old_added_files) {
2924 		view->details->old_added_files = old_added_files;
2925 		sort_files (view, &view->details->old_added_files);
2926 	}
2927 
2928 	/* Resort old_changed_files too, since file attributes
2929 	 * relevant to sorting could have changed.
2930 	 */
2931 	if (old_changed_files != view->details->old_changed_files) {
2932 		view->details->old_changed_files = old_changed_files;
2933 		sort_files (view, &view->details->old_changed_files);
2934 	}
2935 
2936 }
2937 
2938 static void
process_old_files(FMDirectoryView * view)2939 process_old_files (FMDirectoryView *view)
2940 {
2941 	GList *files_added, *files_changed, *node;
2942 	GList *selection, *files;
2943 	gboolean send_selection_change;
2944 
2945 	files_added = view->details->old_added_files;
2946 	files_changed = view->details->old_changed_files;
2947 
2948 	send_selection_change = FALSE;
2949 
2950 	if (files_added != NULL || files_changed != NULL) {
2951 		FileAndDirectory *pending = NULL;
2952 
2953 		g_signal_emit (view, signals[BEGIN_FILE_CHANGES], 0);
2954 
2955 		for (node = files_added; node != NULL; node = node->next) {
2956 			pending = node->data;
2957 			g_signal_emit (view,
2958 				       signals[ADD_FILE], 0, pending->file, pending->directory);
2959 		}
2960 
2961 		for (node = files_changed; node != NULL; node = node->next) {
2962 			pending = node->data;
2963 			g_signal_emit (view,
2964 				       signals[still_should_show_file (view, pending->file, pending->directory)
2965 					       ? FILE_CHANGED : REMOVE_FILE], 0,
2966 				       pending->file, pending->directory);
2967 		}
2968 
2969 		g_signal_emit (view, signals[END_FILE_CHANGES], 0);
2970 
2971 		if (files_changed != NULL) {
2972 			selection = fm_directory_view_get_selection (view);
2973 			files = file_and_directory_list_to_files (files_changed);
2974 			send_selection_change = eel_g_lists_sort_and_check_for_intersection
2975 				(&files, &selection);
2976 			caja_file_list_free (files);
2977 			caja_file_list_free (selection);
2978 		}
2979 
2980 		file_and_directory_list_free (view->details->old_added_files);
2981 		view->details->old_added_files = NULL;
2982 
2983 		file_and_directory_list_free (view->details->old_changed_files);
2984 		view->details->old_changed_files = NULL;
2985 	}
2986 
2987 	if (send_selection_change) {
2988 		/* Send a selection change since some file names could
2989 		 * have changed.
2990 		 */
2991 		fm_directory_view_send_selection_change (view);
2992 	}
2993 }
2994 
2995 static void
display_pending_files(FMDirectoryView * view)2996 display_pending_files (FMDirectoryView *view)
2997 {
2998 
2999 	/* Don't dispatch any updates while the view is frozen. */
3000 	if (view->details->updates_frozen) {
3001 		return;
3002 	}
3003 
3004 	process_new_files (view);
3005 	process_old_files (view);
3006 
3007 	if (view->details->model != NULL
3008 	    && caja_directory_are_all_files_seen (view->details->model)
3009 	    && g_hash_table_size (view->details->non_ready_files) == 0) {
3010 		done_loading (view, TRUE);
3011 	}
3012 }
3013 
3014 void
fm_directory_view_freeze_updates(FMDirectoryView * view)3015 fm_directory_view_freeze_updates (FMDirectoryView *view)
3016 {
3017 	view->details->updates_frozen = TRUE;
3018 	view->details->updates_queued = 0;
3019 	view->details->needs_reload = FALSE;
3020 }
3021 
3022 void
fm_directory_view_unfreeze_updates(FMDirectoryView * view)3023 fm_directory_view_unfreeze_updates (FMDirectoryView *view)
3024 {
3025 	view->details->updates_frozen = FALSE;
3026 
3027 	if (view->details->needs_reload) {
3028 		view->details->needs_reload = FALSE;
3029 		if (view->details->model != NULL) {
3030 			load_directory (view, view->details->model);
3031 		}
3032 	} else {
3033 		schedule_idle_display_of_pending_files (view);
3034 	}
3035 }
3036 
3037 static gboolean
display_selection_info_idle_callback(gpointer data)3038 display_selection_info_idle_callback (gpointer data)
3039 {
3040 	FMDirectoryView *view;
3041 
3042 	view = FM_DIRECTORY_VIEW (data);
3043 
3044 	g_object_ref (G_OBJECT (view));
3045 
3046 	view->details->display_selection_idle_id = 0;
3047 	fm_directory_view_display_selection_info (view);
3048 	if (view->details->send_selection_change_to_shell) {
3049 		fm_directory_view_send_selection_change (view);
3050 	}
3051 
3052 	g_object_unref (G_OBJECT (view));
3053 
3054 	return FALSE;
3055 }
3056 
3057 static void
remove_update_menus_timeout_callback(FMDirectoryView * view)3058 remove_update_menus_timeout_callback (FMDirectoryView *view)
3059 {
3060 	if (view->details->update_menus_timeout_id != 0) {
3061 		g_source_remove (view->details->update_menus_timeout_id);
3062 		view->details->update_menus_timeout_id = 0;
3063 	}
3064 }
3065 
3066 static void
update_menus_if_pending(FMDirectoryView * view)3067 update_menus_if_pending (FMDirectoryView *view)
3068 {
3069 	if (!view->details->menu_states_untrustworthy) {
3070 		return;
3071 	}
3072 
3073 	remove_update_menus_timeout_callback (view);
3074 	fm_directory_view_update_menus (view);
3075 }
3076 
3077 static gboolean
update_menus_timeout_callback(gpointer data)3078 update_menus_timeout_callback (gpointer data)
3079 {
3080 	FMDirectoryView *view;
3081 
3082 	view = FM_DIRECTORY_VIEW (data);
3083 
3084 	g_object_ref (G_OBJECT (view));
3085 
3086 	view->details->update_menus_timeout_id = 0;
3087 	fm_directory_view_update_menus (view);
3088 
3089 	g_object_unref (G_OBJECT (view));
3090 
3091 	return FALSE;
3092 }
3093 
3094 static gboolean
display_pending_callback(gpointer data)3095 display_pending_callback (gpointer data)
3096 {
3097 	FMDirectoryView *view;
3098 
3099 	view = FM_DIRECTORY_VIEW (data);
3100 
3101 	g_object_ref (G_OBJECT (view));
3102 
3103 	view->details->display_pending_source_id = 0;
3104 
3105 	display_pending_files (view);
3106 
3107 	g_object_unref (G_OBJECT (view));
3108 
3109 	return FALSE;
3110 }
3111 
3112 static void
schedule_idle_display_of_pending_files(FMDirectoryView * view)3113 schedule_idle_display_of_pending_files (FMDirectoryView *view)
3114 {
3115 	/* Get rid of a pending source as it might be a timeout */
3116 	unschedule_display_of_pending_files (view);
3117 
3118 	/* We want higher priority than the idle that handles the relayout
3119 	   to avoid a resort on each add. But we still want to allow repaints
3120 	   and other hight prio events while we have pending files to show. */
3121 	view->details->display_pending_source_id =
3122 		g_idle_add_full (G_PRIORITY_DEFAULT_IDLE - 20,
3123 				 display_pending_callback, view, NULL);
3124 }
3125 
3126 static void
schedule_timeout_display_of_pending_files(FMDirectoryView * view,guint interval)3127 schedule_timeout_display_of_pending_files (FMDirectoryView *view, guint interval)
3128 {
3129  	/* No need to schedule an update if there's already one pending. */
3130 	if (view->details->display_pending_source_id != 0) {
3131  		return;
3132 	}
3133 
3134 	view->details->display_pending_source_id =
3135 		g_timeout_add (interval, display_pending_callback, view);
3136 }
3137 
3138 static void
unschedule_display_of_pending_files(FMDirectoryView * view)3139 unschedule_display_of_pending_files (FMDirectoryView *view)
3140 {
3141 	/* Get rid of source if it's active. */
3142 	if (view->details->display_pending_source_id != 0) {
3143 		g_source_remove (view->details->display_pending_source_id);
3144 		view->details->display_pending_source_id = 0;
3145 	}
3146 }
3147 
3148 static void
queue_pending_files(FMDirectoryView * view,CajaDirectory * directory,GList * files,GList ** pending_list)3149 queue_pending_files (FMDirectoryView *view,
3150 		     CajaDirectory *directory,
3151 		     GList *files,
3152 		     GList **pending_list)
3153 {
3154 	if (files == NULL) {
3155 		return;
3156 	}
3157 
3158 	/* Don't queue any more updates if we need to reload anyway */
3159 	if (view->details->needs_reload) {
3160 		return;
3161 	}
3162 
3163 	if (view->details->updates_frozen) {
3164 		view->details->updates_queued += g_list_length (files);
3165 		/* Mark the directory for reload when there are too much queued
3166 		 * changes to prevent the pending list from growing infinitely.
3167 		 */
3168 		if (view->details->updates_queued > MAX_QUEUED_UPDATES) {
3169 			view->details->needs_reload = TRUE;
3170 			return;
3171 		}
3172 	}
3173 
3174 
3175 
3176 	*pending_list = g_list_concat (file_and_directory_list_from_files (directory, files),
3177 				       *pending_list);
3178 
3179 	if (! view->details->loading || caja_directory_are_all_files_seen (directory)) {
3180 		schedule_timeout_display_of_pending_files (view, view->details->update_interval);
3181 	}
3182 }
3183 
3184 static void
remove_changes_timeout_callback(FMDirectoryView * view)3185 remove_changes_timeout_callback (FMDirectoryView *view)
3186 {
3187 	if (view->details->changes_timeout_id != 0) {
3188 		g_source_remove (view->details->changes_timeout_id);
3189 		view->details->changes_timeout_id = 0;
3190 	}
3191 }
3192 
3193 static void
reset_update_interval(FMDirectoryView * view)3194 reset_update_interval (FMDirectoryView *view)
3195 {
3196 	view->details->update_interval = UPDATE_INTERVAL_MIN;
3197 	remove_changes_timeout_callback (view);
3198 	/* Reschedule a pending timeout to idle */
3199 	if (view->details->display_pending_source_id != 0) {
3200 		schedule_idle_display_of_pending_files (view);
3201 	}
3202 }
3203 
3204 static gboolean
changes_timeout_callback(gpointer data)3205 changes_timeout_callback (gpointer data)
3206 {
3207 	gint64 now;
3208 	gint64 time_delta;
3209 	gboolean ret;
3210 	FMDirectoryView *view;
3211 
3212 	view = FM_DIRECTORY_VIEW (data);
3213 
3214 	g_object_ref (G_OBJECT (view));
3215 
3216 	now = g_get_monotonic_time();
3217 	time_delta = now - view->details->last_queued;
3218 
3219 	if (time_delta < UPDATE_INTERVAL_RESET*1000) {
3220 		if (view->details->update_interval < UPDATE_INTERVAL_MAX &&
3221 			view->details->loading) {
3222 			/* Increase */
3223 			view->details->update_interval += UPDATE_INTERVAL_INC;
3224 		}
3225 		ret = TRUE;
3226 	} else {
3227 		/* Reset */
3228 		reset_update_interval (view);
3229 		ret = FALSE;
3230 	}
3231 
3232 	g_object_unref (G_OBJECT (view));
3233 
3234 	return ret;
3235 }
3236 
3237 static void
schedule_changes(FMDirectoryView * view)3238 schedule_changes (FMDirectoryView *view)
3239 {
3240 	/* Remember when the change was queued */
3241 	view->details->last_queued = g_get_monotonic_time();
3242 
3243 	/* No need to schedule if there are already changes pending or during loading */
3244 	if (view->details->changes_timeout_id != 0 ||
3245 		view->details->loading) {
3246 		return;
3247 	}
3248 
3249 	view->details->changes_timeout_id =
3250 		g_timeout_add (UPDATE_INTERVAL_TIMEOUT_INTERVAL, changes_timeout_callback, view);
3251 }
3252 
3253 static void
action_undo_callback(GtkAction * action,gpointer callback_data)3254 action_undo_callback (GtkAction *action,
3255 			gpointer callback_data)
3256 {
3257 	real_action_undo (FM_DIRECTORY_VIEW (callback_data));
3258 }
3259 
3260 static void
action_redo_callback(GtkAction * action,gpointer callback_data)3261 action_redo_callback (GtkAction *action,
3262 			gpointer callback_data)
3263 {
3264 	real_action_redo (FM_DIRECTORY_VIEW (callback_data));
3265 }
3266 
3267 static void
files_added_callback(CajaDirectory * directory,GList * files,gpointer callback_data)3268 files_added_callback (CajaDirectory *directory,
3269 		      GList *files,
3270 		      gpointer callback_data)
3271 {
3272 	FMDirectoryView *view;
3273 	GtkWindow *window;
3274 	char *uri;
3275 
3276 	view = FM_DIRECTORY_VIEW (callback_data);
3277 
3278 	window = fm_directory_view_get_containing_window (view);
3279 	uri = fm_directory_view_get_uri (view);
3280 	caja_debug_log_with_file_list (FALSE, CAJA_DEBUG_LOG_DOMAIN_ASYNC, files,
3281 					   "files added in window %p: %s",
3282 					   window,
3283 					   uri ? uri : "(no directory)");
3284 	g_free (uri);
3285 
3286 	schedule_changes (view);
3287 
3288 	queue_pending_files (view, directory, files, &view->details->new_added_files);
3289 
3290 	/* The number of items could have changed */
3291 	schedule_update_status (view);
3292 }
3293 
3294 static void
files_changed_callback(CajaDirectory * directory,GList * files,gpointer callback_data)3295 files_changed_callback (CajaDirectory *directory,
3296 			GList *files,
3297 			gpointer callback_data)
3298 {
3299 	FMDirectoryView *view;
3300 	GtkWindow *window;
3301 	char *uri;
3302 
3303 	view = FM_DIRECTORY_VIEW (callback_data);
3304 
3305 	window = fm_directory_view_get_containing_window (view);
3306 	uri = fm_directory_view_get_uri (view);
3307 	caja_debug_log_with_file_list (FALSE, CAJA_DEBUG_LOG_DOMAIN_ASYNC, files,
3308 					   "files changed in window %p: %s",
3309 					   window,
3310 					   uri ? uri : "(no directory)");
3311 	g_free (uri);
3312 
3313 	schedule_changes (view);
3314 
3315 	queue_pending_files (view, directory, files, &view->details->new_changed_files);
3316 
3317 	/* The free space or the number of items could have changed */
3318 	schedule_update_status (view);
3319 
3320 	/* A change in MIME type could affect the Open with menu, for
3321 	 * one thing, so we need to update menus when files change.
3322 	 */
3323 	schedule_update_menus (view);
3324 }
3325 
3326 static void
done_loading_callback(CajaDirectory * directory,gpointer callback_data)3327 done_loading_callback (CajaDirectory *directory,
3328 		       gpointer callback_data)
3329 {
3330 	FMDirectoryView *view;
3331 
3332 	view = FM_DIRECTORY_VIEW (callback_data);
3333 
3334 	process_new_files (view);
3335 	if (g_hash_table_size (view->details->non_ready_files) == 0) {
3336 		/* Unschedule a pending update and schedule a new one with the minimal
3337 		 * update interval. This gives the view a short chance at gathering the
3338 		 * (cached) deep counts.
3339 		 */
3340 		unschedule_display_of_pending_files (view);
3341 		schedule_timeout_display_of_pending_files (view, UPDATE_INTERVAL_MIN);
3342 	}
3343 }
3344 
3345 static void
load_error_callback(CajaDirectory * directory,GError * error,gpointer callback_data)3346 load_error_callback (CajaDirectory *directory,
3347 		     GError *error,
3348 		     gpointer callback_data)
3349 {
3350 	FMDirectoryView *view;
3351 
3352 	view = FM_DIRECTORY_VIEW (callback_data);
3353 
3354 	/* FIXME: By doing a stop, we discard some pending files. Is
3355 	 * that OK?
3356 	 */
3357 	fm_directory_view_stop (view);
3358 
3359 	/* Emit a signal to tell subclasses that a load error has
3360 	 * occurred, so they can handle it in the UI.
3361 	 */
3362 	g_signal_emit (view,
3363 		       signals[LOAD_ERROR], 0, error);
3364 }
3365 
3366 static void
real_load_error(FMDirectoryView * view,GError * error)3367 real_load_error (FMDirectoryView *view, GError *error)
3368 {
3369 	/* Report only one error per failed directory load (from the UI
3370 	 * point of view, not from the CajaDirectory point of view).
3371 	 * Otherwise you can get multiple identical errors caused by
3372 	 * unrelated code that just happens to try to iterate this
3373 	 * directory.
3374 	 */
3375 	if (!view->details->reported_load_error) {
3376 		fm_report_error_loading_directory
3377 			(fm_directory_view_get_directory_as_file (view),
3378 			 error,
3379 			 fm_directory_view_get_containing_window (view));
3380 	}
3381 	view->details->reported_load_error = TRUE;
3382 }
3383 
3384 void
fm_directory_view_add_subdirectory(FMDirectoryView * view,CajaDirectory * directory)3385 fm_directory_view_add_subdirectory (FMDirectoryView  *view,
3386 				    CajaDirectory*directory)
3387 {
3388 	CajaFileAttributes attributes;
3389 
3390 	g_assert (!g_list_find (view->details->subdirectory_list, directory));
3391 
3392 	caja_directory_ref (directory);
3393 
3394 	attributes =
3395 		CAJA_FILE_ATTRIBUTES_FOR_ICON |
3396 		CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
3397 		CAJA_FILE_ATTRIBUTE_INFO |
3398 		CAJA_FILE_ATTRIBUTE_LINK_INFO |
3399 		CAJA_FILE_ATTRIBUTE_MOUNT |
3400 		CAJA_FILE_ATTRIBUTE_EXTENSION_INFO;
3401 
3402 	caja_directory_file_monitor_add (directory,
3403 					     &view->details->model,
3404 					     view->details->show_hidden_files,
3405 					     attributes,
3406 					     files_added_callback, view);
3407 
3408 	g_signal_connect
3409 		(directory, "files_added",
3410 		 G_CALLBACK (files_added_callback), view);
3411 	g_signal_connect
3412 		(directory, "files_changed",
3413 		 G_CALLBACK (files_changed_callback), view);
3414 
3415 	view->details->subdirectory_list = g_list_prepend (
3416 			   view->details->subdirectory_list, directory);
3417 }
3418 
3419 void
fm_directory_view_remove_subdirectory(FMDirectoryView * view,CajaDirectory * directory)3420 fm_directory_view_remove_subdirectory (FMDirectoryView  *view,
3421 				       CajaDirectory*directory)
3422 {
3423 	g_assert (g_list_find (view->details->subdirectory_list, directory));
3424 
3425 	view->details->subdirectory_list = g_list_remove (
3426 				view->details->subdirectory_list, directory);
3427 
3428 	g_signal_handlers_disconnect_by_func (directory,
3429 					      G_CALLBACK (files_added_callback),
3430 					      view);
3431 	g_signal_handlers_disconnect_by_func (directory,
3432 					      G_CALLBACK (files_changed_callback),
3433 					      view);
3434 
3435 	caja_directory_file_monitor_remove (directory, &view->details->model);
3436 
3437 	caja_directory_unref (directory);
3438 }
3439 
3440 /**
3441  * fm_directory_view_clear:
3442  *
3443  * Emit the signal to clear the contents of the view. Subclasses must
3444  * override the signal handler for this signal. This is normally called
3445  * only by FMDirectoryView.
3446  * @view: FMDirectoryView to empty.
3447  *
3448  **/
3449 void
fm_directory_view_clear(FMDirectoryView * view)3450 fm_directory_view_clear (FMDirectoryView *view)
3451 {
3452 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3453 
3454 	g_signal_emit (view, signals[CLEAR], 0);
3455 }
3456 
3457 /**
3458  * fm_directory_view_begin_loading:
3459  *
3460  * Emit the signal to prepare for loading the contents of a new location.
3461  * Subclasses might want to override the signal handler for this signal.
3462  * This is normally called only by FMDirectoryView.
3463  * @view: FMDirectoryView that is switching to view a new location.
3464  *
3465  **/
3466 void
fm_directory_view_begin_loading(FMDirectoryView * view)3467 fm_directory_view_begin_loading (FMDirectoryView *view)
3468 {
3469 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3470 
3471 	g_signal_emit (view, signals[BEGIN_LOADING], 0);
3472 }
3473 
3474 /**
3475  * fm_directory_view_end_loading:
3476  *
3477  * Emit the signal after loading the contents of a new location.
3478  * Subclasses might want to override the signal handler for this signal.
3479  * This is normally called only by FMDirectoryView.
3480  * @view: FMDirectoryView that is switching to view a new location.
3481  *
3482  **/
3483 void
fm_directory_view_end_loading(FMDirectoryView * view,gboolean all_files_seen)3484 fm_directory_view_end_loading (FMDirectoryView *view,
3485 			       gboolean all_files_seen)
3486 {
3487 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3488 
3489 	g_signal_emit (view, signals[END_LOADING], 0, all_files_seen);
3490 }
3491 
3492 /**
3493  * fm_directory_view_get_loading:
3494  * @view: an #FMDirectoryView.
3495  *
3496  * Return value: #gboolean inicating whether @view is currently loaded.
3497  *
3498  **/
3499 gboolean
fm_directory_view_get_loading(FMDirectoryView * view)3500 fm_directory_view_get_loading (FMDirectoryView *view)
3501 {
3502 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
3503 
3504 	return view->details->loading;
3505 }
3506 
3507 /**
3508  * fm_directory_view_bump_zoom_level:
3509  *
3510  * bump the current zoom level by invoking the relevant subclass through the slot
3511  *
3512  **/
3513 void
fm_directory_view_bump_zoom_level(FMDirectoryView * view,int zoom_increment)3514 fm_directory_view_bump_zoom_level (FMDirectoryView *view, int zoom_increment)
3515 {
3516 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3517 
3518 	if (!fm_directory_view_supports_zooming (view)) {
3519 		return;
3520 	}
3521 
3522 	EEL_CALL_METHOD
3523 		(FM_DIRECTORY_VIEW_CLASS, view,
3524 		 bump_zoom_level, (view, zoom_increment));
3525 }
3526 
3527 /**
3528  * fm_directory_view_zoom_to_level:
3529  *
3530  * Set the current zoom level by invoking the relevant subclass through the slot
3531  *
3532  **/
3533 void
fm_directory_view_zoom_to_level(FMDirectoryView * view,CajaZoomLevel zoom_level)3534 fm_directory_view_zoom_to_level (FMDirectoryView *view,
3535 				 CajaZoomLevel zoom_level)
3536 {
3537 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3538 
3539 	if (!fm_directory_view_supports_zooming (view)) {
3540 		return;
3541 	}
3542 
3543 	EEL_CALL_METHOD
3544 		(FM_DIRECTORY_VIEW_CLASS, view,
3545 		 zoom_to_level, (view, zoom_level));
3546 }
3547 
3548 
3549 CajaZoomLevel
fm_directory_view_get_zoom_level(FMDirectoryView * view)3550 fm_directory_view_get_zoom_level (FMDirectoryView *view)
3551 {
3552 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), CAJA_ZOOM_LEVEL_STANDARD);
3553 
3554 	if (!fm_directory_view_supports_zooming (view)) {
3555 		return CAJA_ZOOM_LEVEL_STANDARD;
3556 	}
3557 
3558 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3559 		(FM_DIRECTORY_VIEW_CLASS, view,
3560 		 get_zoom_level, (view));
3561 }
3562 
3563 /**
3564  * fm_directory_view_restore_default_zoom_level:
3565  *
3566  * restore to the default zoom level by invoking the relevant subclass through the slot
3567  *
3568  **/
3569 void
fm_directory_view_restore_default_zoom_level(FMDirectoryView * view)3570 fm_directory_view_restore_default_zoom_level (FMDirectoryView *view)
3571 {
3572 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3573 
3574 	if (!fm_directory_view_supports_zooming (view)) {
3575 		return;
3576 	}
3577 
3578 	EEL_CALL_METHOD
3579 		(FM_DIRECTORY_VIEW_CLASS, view,
3580 		 restore_default_zoom_level, (view));
3581 }
3582 
3583 /**
3584  * fm_directory_view_can_zoom_in:
3585  *
3586  * Determine whether the view can be zoomed any closer.
3587  * @view: The zoomable FMDirectoryView.
3588  *
3589  * Return value: TRUE if @view can be zoomed any closer, FALSE otherwise.
3590  *
3591  **/
3592 gboolean
fm_directory_view_can_zoom_in(FMDirectoryView * view)3593 fm_directory_view_can_zoom_in (FMDirectoryView *view)
3594 {
3595 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
3596 
3597 	if (!fm_directory_view_supports_zooming (view)) {
3598 		return FALSE;
3599 	}
3600 
3601 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3602 		(FM_DIRECTORY_VIEW_CLASS, view,
3603 		 can_zoom_in, (view));
3604 }
3605 
3606 /**
3607  * fm_directory_view_can_rename_file
3608  *
3609  * Determine whether a file can be renamed.
3610  * @file: A CajaFile
3611  *
3612  * Return value: TRUE if @file can be renamed, FALSE otherwise.
3613  *
3614  **/
3615 static gboolean
fm_directory_view_can_rename_file(FMDirectoryView * view,CajaFile * file)3616 fm_directory_view_can_rename_file (FMDirectoryView *view, CajaFile *file)
3617 {
3618 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3619 		(FM_DIRECTORY_VIEW_CLASS, view,
3620 		 can_rename_file, (view, file));
3621 }
3622 
3623 /**
3624  * fm_directory_view_can_zoom_out:
3625  *
3626  * Determine whether the view can be zoomed any further away.
3627  * @view: The zoomable FMDirectoryView.
3628  *
3629  * Return value: TRUE if @view can be zoomed any further away, FALSE otherwise.
3630  *
3631  **/
3632 gboolean
fm_directory_view_can_zoom_out(FMDirectoryView * view)3633 fm_directory_view_can_zoom_out (FMDirectoryView *view)
3634 {
3635 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
3636 
3637 	if (!fm_directory_view_supports_zooming (view)) {
3638 		return FALSE;
3639 	}
3640 
3641 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3642 		(FM_DIRECTORY_VIEW_CLASS, view,
3643 		 can_zoom_out, (view));
3644 }
3645 
3646 GtkWidget *
fm_directory_view_get_background_widget(FMDirectoryView * view)3647 fm_directory_view_get_background_widget (FMDirectoryView *view)
3648 {
3649 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
3650 
3651 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3652 		(FM_DIRECTORY_VIEW_CLASS, view,
3653 		 get_background_widget, (view));
3654 }
3655 
3656 EelBackground *
fm_directory_view_get_background(FMDirectoryView * view)3657 fm_directory_view_get_background (FMDirectoryView *view)
3658 {
3659 	return eel_get_widget_background (fm_directory_view_get_background_widget (view));
3660 }
3661 
3662 static void
real_set_is_active(FMDirectoryView * view,gboolean is_active)3663 real_set_is_active (FMDirectoryView *view,
3664 		    gboolean is_active)
3665 {
3666 	EelBackground *bg;
3667 
3668 	bg = fm_directory_view_get_background (view);
3669 	eel_background_set_active (bg, is_active);
3670 }
3671 
3672 static void
fm_directory_view_set_is_active(FMDirectoryView * view,gboolean is_active)3673 fm_directory_view_set_is_active (FMDirectoryView *view,
3674 				 gboolean is_active)
3675 {
3676 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3677 
3678 	EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view,
3679 			 set_is_active, (view, is_active));
3680 }
3681 
3682 /**
3683  * fm_directory_view_get_selection:
3684  *
3685  * Get a list of CajaFile pointers that represents the
3686  * currently-selected items in this view. Subclasses must override
3687  * the signal handler for the 'get_selection' signal. Callers are
3688  * responsible for g_free-ing the list (but not its data).
3689  * @view: FMDirectoryView whose selected items are of interest.
3690  *
3691  * Return value: GList of CajaFile pointers representing the selection.
3692  *
3693  **/
3694 GList *
fm_directory_view_get_selection(FMDirectoryView * view)3695 fm_directory_view_get_selection (FMDirectoryView *view)
3696 {
3697 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
3698 
3699 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3700 		(FM_DIRECTORY_VIEW_CLASS, view,
3701 		 get_selection, (view));
3702 }
3703 
3704 void
fm_directory_view_invert_selection(FMDirectoryView * view)3705 fm_directory_view_invert_selection (FMDirectoryView *view)
3706 {
3707 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
3708 
3709 	EEL_CALL_METHOD
3710 		(FM_DIRECTORY_VIEW_CLASS, view,
3711 		 invert_selection, (view));
3712 }
3713 
3714 GList *
fm_directory_view_get_selection_for_file_transfer(FMDirectoryView * view)3715 fm_directory_view_get_selection_for_file_transfer (FMDirectoryView *view)
3716 {
3717 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
3718 
3719 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3720 		(FM_DIRECTORY_VIEW_CLASS, view,
3721 		 get_selection_for_file_transfer, (view));
3722 }
3723 
3724 guint
fm_directory_view_get_item_count(FMDirectoryView * view)3725 fm_directory_view_get_item_count (FMDirectoryView *view)
3726 {
3727 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), 0);
3728 
3729 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
3730 		(FM_DIRECTORY_VIEW_CLASS, view,
3731 		 get_item_count, (view));
3732 }
3733 
3734 GtkUIManager *
fm_directory_view_get_ui_manager(FMDirectoryView * view)3735 fm_directory_view_get_ui_manager (FMDirectoryView  *view)
3736 {
3737 	if (view->details->window == NULL) {
3738 		return NULL;
3739 	}
3740 	return caja_window_info_get_ui_manager (view->details->window);
3741 }
3742 
3743 /**
3744  * fm_directory_view_get_model:
3745  *
3746  * Get the model for this FMDirectoryView.
3747  * @view: FMDirectoryView of interest.
3748  *
3749  * Return value: CajaDirectory for this view.
3750  *
3751  **/
3752 CajaDirectory *
fm_directory_view_get_model(FMDirectoryView * view)3753 fm_directory_view_get_model (FMDirectoryView *view)
3754 {
3755 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
3756 
3757 	return view->details->model;
3758 }
3759 
3760 GdkAtom
fm_directory_view_get_copied_files_atom(FMDirectoryView * view)3761 fm_directory_view_get_copied_files_atom (FMDirectoryView *view)
3762 {
3763 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), GDK_NONE);
3764 
3765 	return copied_files_atom;
3766 }
3767 
3768 static void
prepend_uri_one(gpointer data,gpointer callback_data)3769 prepend_uri_one (gpointer data, gpointer callback_data)
3770 {
3771 	CajaFile *file;
3772 	GList **result;
3773 
3774 	g_assert (CAJA_IS_FILE (data));
3775 	g_assert (callback_data != NULL);
3776 
3777 	result = (GList **) callback_data;
3778 	file = (CajaFile *) data;
3779 	*result = g_list_prepend (*result, caja_file_get_uri (file));
3780 }
3781 
3782 static void
offset_drop_points(GArray * relative_item_points,int x_offset,int y_offset)3783 offset_drop_points (GArray *relative_item_points,
3784 		    int x_offset, int y_offset)
3785 {
3786 	guint index;
3787 
3788 	if (relative_item_points == NULL) {
3789 		return;
3790 	}
3791 
3792 	for (index = 0; index < relative_item_points->len; index++) {
3793 		g_array_index (relative_item_points, GdkPoint, index).x += x_offset;
3794 		g_array_index (relative_item_points, GdkPoint, index).y += y_offset;
3795 	}
3796 }
3797 
3798 static void
fm_directory_view_create_links_for_files(FMDirectoryView * view,GList * files,GArray * relative_item_points)3799 fm_directory_view_create_links_for_files (FMDirectoryView *view, GList *files,
3800 					  GArray *relative_item_points)
3801 {
3802 	GList *uris;
3803 	char *dir_uri;
3804 	CopyMoveDoneData *copy_move_done_data;
3805 	g_assert (relative_item_points->len == 0
3806 		  || g_list_length (files) == relative_item_points->len);
3807 
3808         g_assert (FM_IS_DIRECTORY_VIEW (view));
3809         g_assert (files != NULL);
3810 
3811 	/* create a list of URIs */
3812 	uris = NULL;
3813 	g_list_foreach (files, prepend_uri_one, &uris);
3814 	uris = g_list_reverse (uris);
3815 
3816         g_assert (g_list_length (uris) == g_list_length (files));
3817 
3818 	/* offset the drop locations a bit so that we don't pile
3819 	 * up the icons on top of each other
3820 	 */
3821 	offset_drop_points (relative_item_points,
3822 			    DUPLICATE_HORIZONTAL_ICON_OFFSET,
3823 			    DUPLICATE_VERTICAL_ICON_OFFSET);
3824 
3825         copy_move_done_data = pre_copy_move (view);
3826 	dir_uri = fm_directory_view_get_backing_uri (view);
3827 	caja_file_operations_copy_move (uris, relative_item_points, dir_uri, GDK_ACTION_LINK,
3828 					    GTK_WIDGET (view), copy_move_done_callback, copy_move_done_data);
3829 	g_free (dir_uri);
3830 	g_list_free_full (uris, g_free);
3831 }
3832 
3833 static void
fm_directory_view_duplicate_selection(FMDirectoryView * view,GList * files,GArray * relative_item_points)3834 fm_directory_view_duplicate_selection (FMDirectoryView *view, GList *files,
3835 				       GArray *relative_item_points)
3836 {
3837 	GList *uris;
3838 	CopyMoveDoneData *copy_move_done_data;
3839 
3840         g_assert (FM_IS_DIRECTORY_VIEW (view));
3841         g_assert (files != NULL);
3842 	g_assert (g_list_length (files) == relative_item_points->len
3843 		|| relative_item_points->len == 0);
3844 
3845 	/* create a list of URIs */
3846 	uris = NULL;
3847 	g_list_foreach (files, prepend_uri_one, &uris);
3848 	uris = g_list_reverse (uris);
3849 
3850         g_assert (g_list_length (uris) == g_list_length (files));
3851 
3852 	/* offset the drop locations a bit so that we don't pile
3853 	 * up the icons on top of each other
3854 	 */
3855 	offset_drop_points (relative_item_points,
3856 			    DUPLICATE_HORIZONTAL_ICON_OFFSET,
3857 			    DUPLICATE_VERTICAL_ICON_OFFSET);
3858 
3859         copy_move_done_data = pre_copy_move (view);
3860 	caja_file_operations_copy_move (uris, relative_item_points, NULL, GDK_ACTION_COPY,
3861 		GTK_WIDGET (view), copy_move_done_callback, copy_move_done_data);
3862 	g_list_free_full (uris, g_free);
3863 }
3864 
3865 /* special_link_in_selection
3866  *
3867  * Return TRUE if one of our special links is in the selection.
3868  * Special links include the following:
3869  *	 CAJA_DESKTOP_LINK_TRASH, CAJA_DESKTOP_LINK_HOME, CAJA_DESKTOP_LINK_MOUNT
3870  */
3871 
3872 static gboolean
special_link_in_selection(FMDirectoryView * view)3873 special_link_in_selection (FMDirectoryView *view)
3874 {
3875 	gboolean saw_link;
3876 	GList *selection, *node;
3877 	CajaFile *file = NULL;
3878 
3879 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
3880 
3881 	saw_link = FALSE;
3882 
3883 	selection = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
3884 
3885 	for (node = selection; node != NULL; node = node->next) {
3886 		file = CAJA_FILE (node->data);
3887 
3888 		saw_link = CAJA_IS_DESKTOP_ICON_FILE (file);
3889 
3890 		if (saw_link) {
3891 			break;
3892 		}
3893 	}
3894 
3895 	caja_file_list_free (selection);
3896 
3897 	return saw_link;
3898 }
3899 
3900 /* desktop_or_home_dir_in_selection
3901  *
3902  * Return TRUE if either the desktop or the home directory is in the selection.
3903  */
3904 
3905 static gboolean
desktop_or_home_dir_in_selection(FMDirectoryView * view)3906 desktop_or_home_dir_in_selection (FMDirectoryView *view)
3907 {
3908 	gboolean saw_desktop_or_home_dir;
3909 	GList *selection, *node;
3910 	CajaFile *file = NULL;
3911 
3912 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
3913 
3914 	saw_desktop_or_home_dir = FALSE;
3915 
3916 	selection = fm_directory_view_get_selection (FM_DIRECTORY_VIEW (view));
3917 
3918 	for (node = selection; node != NULL; node = node->next) {
3919 		file = CAJA_FILE (node->data);
3920 
3921 		saw_desktop_or_home_dir =
3922 			caja_file_is_home (file)
3923 			|| caja_file_is_desktop_directory (file);
3924 
3925 		if (saw_desktop_or_home_dir) {
3926 			break;
3927 		}
3928 	}
3929 
3930 	caja_file_list_free (selection);
3931 
3932 	return saw_desktop_or_home_dir;
3933 }
3934 
3935 static void
trash_or_delete_done_cb(GHashTable * debuting_uris,gboolean user_cancel,FMDirectoryView * view)3936 trash_or_delete_done_cb (GHashTable *debuting_uris,
3937 			 gboolean user_cancel,
3938 			 FMDirectoryView *view)
3939 {
3940 	if (user_cancel) {
3941 		view->details->selection_was_removed = FALSE;
3942 	}
3943 }
3944 
3945 static void
trash_or_delete_files(GtkWindow * parent_window,const GList * files,gboolean delete_if_all_already_in_trash,FMDirectoryView * view)3946 trash_or_delete_files (GtkWindow *parent_window,
3947 		       const GList *files,
3948 		       gboolean delete_if_all_already_in_trash,
3949 		       FMDirectoryView *view)
3950 {
3951 	GList *locations;
3952 	const GList *node;
3953 
3954 	locations = NULL;
3955 	for (node = files; node != NULL; node = node->next) {
3956 		locations = g_list_prepend (locations,
3957 					    caja_file_get_location ((CajaFile *) node->data));
3958 	}
3959 
3960 	locations = g_list_reverse (locations);
3961 
3962 	caja_file_operations_trash_or_delete (locations,
3963 						  parent_window,
3964 						  (CajaDeleteCallback) trash_or_delete_done_cb,
3965 						  view);
3966 	g_list_free_full (locations, g_object_unref);
3967 }
3968 
3969 static gboolean
can_rename_file(FMDirectoryView * view,CajaFile * file)3970 can_rename_file (FMDirectoryView *view, CajaFile *file)
3971 {
3972 	return caja_file_can_rename (file);
3973 }
3974 
3975 static void
start_renaming_file(FMDirectoryView * view,CajaFile * file,gboolean select_all)3976 start_renaming_file (FMDirectoryView *view,
3977 		     CajaFile *file,
3978 		     gboolean select_all)
3979 {
3980 	if (file !=  NULL) {
3981 		fm_directory_view_select_file (view, file);
3982 	}
3983 }
3984 
3985 typedef struct {
3986 	FMDirectoryView *view;
3987 	CajaFile *new_file;
3988 } RenameData;
3989 
3990 static gboolean
delayed_rename_file_hack_callback(RenameData * data)3991 delayed_rename_file_hack_callback (RenameData *data)
3992 {
3993 	FMDirectoryView *view;
3994 	CajaFile *new_file;
3995 
3996 	view = data->view;
3997 	new_file = data->new_file;
3998 
3999 	if (view->details->window != NULL &&
4000 	    view->details->active) {
4001 		EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, new_file, FALSE));
4002 		fm_directory_view_reveal_selection (view);
4003 	}
4004 
4005 	return FALSE;
4006 }
4007 
4008 static void
delayed_rename_file_hack_removed(RenameData * data)4009 delayed_rename_file_hack_removed (RenameData *data)
4010 {
4011 	g_object_unref (data->view);
4012 	caja_file_unref (data->new_file);
4013 	g_free (data);
4014 }
4015 
4016 
4017 static void
rename_file(FMDirectoryView * view,CajaFile * new_file)4018 rename_file (FMDirectoryView *view, CajaFile *new_file)
4019 {
4020 	/* HACK!!!!
4021 	   This is a work around bug in listview. After the rename is
4022 	   enabled we will get file changes due to info about the new
4023 	   file being read, which will cause the model to change. When
4024 	   the model changes GtkTreeView clears the editing. This hack just
4025 	   delays editing for some time to try to avoid this problem.
4026 	   A major problem is that the selection of the row causes us
4027 	   to load the slow mimetype for the file, which leads to a
4028 	   file_changed. So, before we delay we select the row.
4029 	*/
4030 	if (FM_IS_LIST_VIEW (view)) {
4031 		RenameData *data;
4032 
4033 		fm_directory_view_select_file (view, new_file);
4034 
4035 		data = g_new (RenameData, 1);
4036 		data->view = g_object_ref (view);
4037 		data->new_file = caja_file_ref (new_file);
4038 		if (view->details->delayed_rename_file_id != 0) {
4039 			g_source_remove (view->details->delayed_rename_file_id);
4040 		}
4041 		view->details->delayed_rename_file_id =
4042 			g_timeout_add_full (G_PRIORITY_DEFAULT,
4043 					    100, (GSourceFunc)delayed_rename_file_hack_callback,
4044 					    data, (GDestroyNotify) delayed_rename_file_hack_removed);
4045 
4046 		return;
4047 	}
4048 
4049 	/* no need to select because start_renaming_file selects
4050 	 * fm_directory_view_select_file (view, new_file);
4051 	 */
4052 	EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, new_file, FALSE));
4053 	fm_directory_view_reveal_selection (view);
4054 }
4055 
4056 static void
reveal_newly_added_folder(FMDirectoryView * view,CajaFile * new_file,CajaDirectory * directory,GFile * target_location)4057 reveal_newly_added_folder (FMDirectoryView *view, CajaFile *new_file,
4058 			   CajaDirectory *directory, GFile *target_location)
4059 {
4060 	GFile *location;
4061 
4062 	location = caja_file_get_location (new_file);
4063 	if (g_file_equal (location, target_location)) {
4064 		g_signal_handlers_disconnect_by_func (view,
4065 						      G_CALLBACK (reveal_newly_added_folder),
4066 						      (void *) target_location);
4067 		rename_file (view, new_file);
4068 	}
4069 	g_object_unref (location);
4070 }
4071 
4072 typedef struct {
4073 	FMDirectoryView *directory_view;
4074 	GHashTable *added_locations;
4075 } NewFolderData;
4076 
4077 
4078 static void
track_newly_added_locations(FMDirectoryView * view,CajaFile * new_file,CajaDirectory * directory,gpointer user_data)4079 track_newly_added_locations (FMDirectoryView *view, CajaFile *new_file,
4080 			     CajaDirectory *directory, gpointer user_data)
4081 {
4082 	NewFolderData *data;
4083 
4084 	data = user_data;
4085 
4086 	g_hash_table_insert (data->added_locations, caja_file_get_location (new_file), NULL);
4087 }
4088 
4089 static void
new_folder_done(GFile * new_folder,gpointer user_data)4090 new_folder_done (GFile *new_folder, gpointer user_data)
4091 {
4092 	FMDirectoryView *directory_view;
4093 	CajaFile *file;
4094 	char screen_string[32];
4095 	GdkScreen *screen;
4096 	NewFolderData *data;
4097 
4098 	data = (NewFolderData *)user_data;
4099 
4100 	directory_view = data->directory_view;
4101 
4102 	if (directory_view == NULL) {
4103 		goto fail;
4104 	}
4105 
4106 	g_signal_handlers_disconnect_by_func (directory_view,
4107 					      G_CALLBACK (track_newly_added_locations),
4108 					      (void *) data);
4109 
4110 	if (new_folder == NULL) {
4111 		goto fail;
4112 	}
4113 
4114 	screen = gtk_widget_get_screen (GTK_WIDGET (directory_view));
4115 	g_snprintf (screen_string, sizeof (screen_string), "%d", gdk_x11_screen_get_screen_number (screen));
4116 
4117 
4118 	file = caja_file_get (new_folder);
4119 	caja_file_set_metadata
4120 		(file, CAJA_METADATA_KEY_SCREEN,
4121 		 NULL,
4122 		 screen_string);
4123 
4124 	if (g_hash_table_lookup_extended (data->added_locations, new_folder, NULL, NULL)) {
4125 		/* The file was already added */
4126 		rename_file (directory_view, file);
4127 	} else {
4128 		/* We need to run after the default handler adds the folder we want to
4129 		 * operate on. The ADD_FILE signal is registered as G_SIGNAL_RUN_LAST, so we
4130 		 * must use connect_after.
4131 		 */
4132 		g_signal_connect_data (directory_view,
4133 				       "add_file",
4134 				       G_CALLBACK (reveal_newly_added_folder),
4135 				       g_object_ref (new_folder),
4136 				       (GClosureNotify)g_object_unref,
4137 				       G_CONNECT_AFTER);
4138 	}
4139 	caja_file_unref (file);
4140 
4141  fail:
4142 	g_hash_table_destroy (data->added_locations);
4143 	eel_remove_weak_pointer (&data->directory_view);
4144 	g_free (data);
4145 }
4146 
4147 
4148 static NewFolderData *
new_folder_data_new(FMDirectoryView * directory_view)4149 new_folder_data_new (FMDirectoryView *directory_view)
4150 {
4151 	NewFolderData *data;
4152 
4153 	data = g_new (NewFolderData, 1);
4154 	data->directory_view = directory_view;
4155 	data->added_locations = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
4156 						       g_object_unref, NULL);
4157 	eel_add_weak_pointer (&data->directory_view);
4158 
4159 	return data;
4160 }
4161 
4162 static GdkPoint *
context_menu_to_file_operation_position(FMDirectoryView * directory_view)4163 context_menu_to_file_operation_position (FMDirectoryView *directory_view)
4164 {
4165 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (directory_view), NULL);
4166 
4167 	if (fm_directory_view_using_manual_layout (directory_view)
4168 	    && directory_view->details->context_menu_position.x >= 0
4169 	    && directory_view->details->context_menu_position.y >= 0) {
4170 		EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, directory_view,
4171 				 widget_to_file_operation_position,
4172 				 (directory_view, &directory_view->details->context_menu_position));
4173 		return &directory_view->details->context_menu_position;
4174 	} else {
4175 		return NULL;
4176 	}
4177 }
4178 
4179 static void
update_context_menu_position_from_event(FMDirectoryView * view,GdkEventButton * event)4180 update_context_menu_position_from_event (FMDirectoryView *view,
4181 					 GdkEventButton  *event)
4182 {
4183 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
4184 
4185 	if (event != NULL) {
4186 		view->details->context_menu_position.x = event->x;
4187 		view->details->context_menu_position.y = event->y;
4188 	} else {
4189 		view->details->context_menu_position.x = -1;
4190 		view->details->context_menu_position.y = -1;
4191 	}
4192 }
4193 
4194 void
fm_directory_view_new_folder(FMDirectoryView * directory_view)4195 fm_directory_view_new_folder (FMDirectoryView *directory_view)
4196 {
4197 	char *parent_uri;
4198 	NewFolderData *data;
4199 	GdkPoint *pos;
4200 
4201 	data = new_folder_data_new (directory_view);
4202 
4203 	g_signal_connect_data (directory_view,
4204 			       "add_file",
4205 			       G_CALLBACK (track_newly_added_locations),
4206 			       data,
4207 			       (GClosureNotify)NULL,
4208 			       G_CONNECT_AFTER);
4209 
4210 	pos = context_menu_to_file_operation_position (directory_view);
4211 
4212 	parent_uri = fm_directory_view_get_backing_uri (directory_view);
4213 	caja_file_operations_new_folder (GTK_WIDGET (directory_view),
4214 					     pos, parent_uri,
4215 					     new_folder_done, data);
4216 
4217 	g_free (parent_uri);
4218 }
4219 
4220 static NewFolderData *
setup_new_folder_data(FMDirectoryView * directory_view)4221 setup_new_folder_data (FMDirectoryView *directory_view)
4222 {
4223 	NewFolderData *data;
4224 
4225 	data = new_folder_data_new (directory_view);
4226 
4227 	g_signal_connect_data (directory_view,
4228 			       "add_file",
4229 			       G_CALLBACK (track_newly_added_locations),
4230 			       data,
4231 			       (GClosureNotify)NULL,
4232 			       G_CONNECT_AFTER);
4233 
4234 	return data;
4235 }
4236 
4237 static void
fm_directory_view_new_file_with_initial_contents(FMDirectoryView * directory_view,const char * parent_uri,const char * filename,const char * initial_contents,int length,GdkPoint * pos)4238 fm_directory_view_new_file_with_initial_contents (FMDirectoryView *directory_view,
4239 						  const char *parent_uri,
4240 						  const char *filename,
4241 						  const char *initial_contents,
4242 						  int length,
4243 						  GdkPoint *pos)
4244 {
4245 	NewFolderData *data;
4246 
4247 	g_assert (parent_uri != NULL);
4248 
4249 	data = setup_new_folder_data (directory_view);
4250 
4251 	if (pos == NULL) {
4252 		pos = context_menu_to_file_operation_position (directory_view);
4253 	}
4254 
4255 	caja_file_operations_new_file (GTK_WIDGET (directory_view),
4256 					   pos, parent_uri, filename,
4257 					   initial_contents, length,
4258 					   new_folder_done, data);
4259 }
4260 
4261 void
fm_directory_view_new_file(FMDirectoryView * directory_view,const char * parent_uri,CajaFile * source)4262 fm_directory_view_new_file (FMDirectoryView *directory_view,
4263 			    const char *parent_uri,
4264 			    CajaFile *source)
4265 {
4266 	GdkPoint *pos;
4267 	NewFolderData *data;
4268 	char *source_uri;
4269 	char *container_uri;
4270 
4271 	container_uri = NULL;
4272 	if (parent_uri == NULL) {
4273 		container_uri = fm_directory_view_get_backing_uri (directory_view);
4274 		g_assert (container_uri != NULL);
4275 	}
4276 
4277 	if (source == NULL) {
4278 		fm_directory_view_new_file_with_initial_contents (directory_view,
4279 								  parent_uri != NULL ? parent_uri : container_uri,
4280 								  NULL,
4281 								  NULL,
4282 								  0,
4283 								  NULL);
4284 		g_free (container_uri);
4285 		return;
4286 	}
4287 
4288 	g_return_if_fail (caja_file_is_local (source));
4289 
4290 	pos = context_menu_to_file_operation_position (directory_view);
4291 
4292 	data = setup_new_folder_data (directory_view);
4293 
4294 	source_uri = caja_file_get_uri (source);
4295 
4296 	caja_file_operations_new_file_from_template (GTK_WIDGET (directory_view),
4297 							 pos,
4298 							 parent_uri != NULL ? parent_uri : container_uri,
4299 							 NULL,
4300 							 source_uri,
4301 							 new_folder_done, data);
4302 
4303 	g_free (source_uri);
4304 	g_free (container_uri);
4305 }
4306 
4307 /* handle the open command */
4308 
4309 static void
open_one_in_new_window(gpointer data,gpointer callback_data)4310 open_one_in_new_window (gpointer data, gpointer callback_data)
4311 {
4312 	g_assert (CAJA_IS_FILE (data));
4313 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
4314 
4315 	fm_directory_view_activate_file (FM_DIRECTORY_VIEW (callback_data),
4316 					 CAJA_FILE (data),
4317 					 CAJA_WINDOW_OPEN_IN_NAVIGATION,
4318 					 0);
4319 }
4320 
4321 static void
open_one_in_folder_window(gpointer data,gpointer callback_data)4322 open_one_in_folder_window (gpointer data, gpointer callback_data)
4323 {
4324 	g_assert (CAJA_IS_FILE (data));
4325 	g_assert (FM_IS_DIRECTORY_VIEW (callback_data));
4326 
4327 	fm_directory_view_activate_file (FM_DIRECTORY_VIEW (callback_data),
4328 					 CAJA_FILE (data),
4329 					 CAJA_WINDOW_OPEN_IN_SPATIAL,
4330 					 0);
4331 }
4332 
4333 CajaFile *
fm_directory_view_get_directory_as_file(FMDirectoryView * view)4334 fm_directory_view_get_directory_as_file (FMDirectoryView *view)
4335 {
4336 	g_assert (FM_IS_DIRECTORY_VIEW (view));
4337 
4338 	return view->details->directory_as_file;
4339 }
4340 
4341 static void
open_with_launch_application_callback(GtkAction * action,gpointer callback_data)4342 open_with_launch_application_callback (GtkAction *action,
4343 				       gpointer callback_data)
4344 {
4345 	ApplicationLaunchParameters *launch_parameters;
4346 
4347 	launch_parameters = (ApplicationLaunchParameters *) callback_data;
4348 	caja_launch_application
4349 		(launch_parameters->application,
4350 		 launch_parameters->files,
4351 		 fm_directory_view_get_containing_window (launch_parameters->directory_view));
4352 }
4353 
4354 static void
open_parent_folder_callback(GtkAction * action,gpointer callback_data)4355 open_parent_folder_callback (GtkAction *action,
4356 			     gpointer callback_data)
4357 {
4358 	gchar *uri;
4359 
4360 	uri = (gchar *) callback_data;
4361 	g_app_info_launch_default_for_uri (uri, NULL, NULL);
4362 }
4363 
4364 static char *
escape_action_name(const char * action_name,const char * prefix)4365 escape_action_name (const char *action_name,
4366 		    const char *prefix)
4367 {
4368 	GString *s;
4369 
4370 	if (action_name == NULL) {
4371 		return NULL;
4372 	}
4373 
4374 	s = g_string_new (prefix);
4375 
4376 	while (*action_name != 0) {
4377 		switch (*action_name) {
4378 		case '\\':
4379 			g_string_append (s, "\\\\");
4380 			break;
4381 		case '/':
4382 			g_string_append (s, "\\s");
4383 			break;
4384 		case '&':
4385 			g_string_append (s, "\\a");
4386 			break;
4387 		case '"':
4388 			g_string_append (s, "\\q");
4389 			break;
4390 		default:
4391 			g_string_append_c (s, *action_name);
4392 		}
4393 
4394 		action_name ++;
4395 	}
4396 	return g_string_free (s, FALSE);
4397 }
4398 
4399 static char *
escape_action_path(const char * action_path)4400 escape_action_path (const char *action_path)
4401 {
4402 	GString *s;
4403 
4404 	if (action_path == NULL) {
4405 		return NULL;
4406 	}
4407 
4408 	s = g_string_sized_new (strlen (action_path) + 2);
4409 
4410 	while (*action_path != 0) {
4411 		switch (*action_path) {
4412 		case '\\':
4413 			g_string_append (s, "\\\\");
4414 			break;
4415 		case '&':
4416 			g_string_append (s, "\\a");
4417 			break;
4418 		case '"':
4419 			g_string_append (s, "\\q");
4420 			break;
4421 		default:
4422 			g_string_append_c (s, *action_path);
4423 		}
4424 
4425 		action_path ++;
4426 	}
4427 	return g_string_free (s, FALSE);
4428 }
4429 
4430 
4431 static void
add_submenu(GtkUIManager * ui_manager,GtkActionGroup * action_group,guint merge_id,const char * parent_path,const char * uri,const char * label,cairo_surface_t * surface,gboolean add_action)4432 add_submenu (GtkUIManager *ui_manager,
4433 	     GtkActionGroup *action_group,
4434 	     guint merge_id,
4435 	     const char *parent_path,
4436 	     const char *uri,
4437 	     const char *label,
4438 	     cairo_surface_t *surface,
4439 	     gboolean add_action)
4440 {
4441 	if (parent_path != NULL) {
4442 		char *escaped_label;
4443 		char *action_name;
4444 		char *submenu_name;
4445 		char *escaped_submenu_name;
4446 
4447 		action_name = escape_action_name (uri, "submenu_");
4448 		submenu_name = g_path_get_basename (uri);
4449 		escaped_submenu_name = escape_action_path (submenu_name);
4450 		escaped_label = eel_str_double_underscores (label);
4451 
4452 		if (add_action) {
4453 			GtkAction *action;
4454 
4455 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4456 			action = gtk_action_new (action_name,
4457 						 escaped_label,
4458 						 NULL,
4459 						 NULL);
4460 			G_GNUC_END_IGNORE_DEPRECATIONS;
4461 			if (surface != NULL) {
4462 				g_object_set_data_full (G_OBJECT (action), "menu-icon",
4463 							cairo_surface_reference (surface),
4464 							(GDestroyNotify)cairo_surface_destroy);
4465 			}
4466 
4467 			g_object_set (action, "hide-if-empty", FALSE, NULL);
4468 
4469 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4470 			gtk_action_group_add_action (action_group,
4471 						     action);
4472 			G_GNUC_END_IGNORE_DEPRECATIONS;
4473 			g_object_unref (action);
4474 		}
4475 
4476 		gtk_ui_manager_add_ui (ui_manager,
4477 				       merge_id,
4478 				       parent_path,
4479 				       escaped_submenu_name,
4480 				       action_name,
4481 				       GTK_UI_MANAGER_MENU,
4482 				       FALSE);
4483 		g_free (action_name);
4484 		g_free (escaped_label);
4485 		g_free (submenu_name);
4486 		g_free (escaped_submenu_name);
4487 	}
4488 }
4489 
4490 static void
add_application_to_open_with_menu(FMDirectoryView * view,GAppInfo * application,GList * files,int index,const char * menu_placeholder,const char * popup_placeholder,const gboolean submenu)4491 add_application_to_open_with_menu (FMDirectoryView *view,
4492 				   GAppInfo *application,
4493 				   GList *files,
4494 				   int index,
4495 				   const char *menu_placeholder,
4496 				   const char *popup_placeholder,
4497 				   const gboolean submenu)
4498 {
4499 	ApplicationLaunchParameters *launch_parameters;
4500 	char *tip;
4501 	char *label;
4502 	char *action_name;
4503 	char *escaped_app;
4504 	char *path;
4505 	GtkAction *action;
4506 	GIcon *app_icon;
4507 	GtkWidget *menuitem;
4508 
4509 	launch_parameters = application_launch_parameters_new
4510 		(application, files, view);
4511 	escaped_app = eel_str_double_underscores (g_app_info_get_display_name (application));
4512 	if (submenu)
4513 		label = g_strdup_printf ("%s", escaped_app);
4514 	else
4515 		label = g_strdup_printf (_("Open With %s"), escaped_app);
4516 
4517 	tip = g_strdup_printf (ngettext ("Use \"%s\" to open the selected item",
4518 					 "Use \"%s\" to open the selected items",
4519 					 g_list_length (files)),
4520 			       escaped_app);
4521 	g_free (escaped_app);
4522 
4523 	action_name = g_strdup_printf ("open_with_%d", index);
4524 
4525 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4526 	action = gtk_action_new (action_name,
4527 				 label,
4528 				 tip,
4529 				 NULL);
4530 	G_GNUC_END_IGNORE_DEPRECATIONS;
4531 
4532 	app_icon = g_app_info_get_icon (application);
4533 	if (app_icon != NULL) {
4534 		g_object_ref (app_icon);
4535 	} else {
4536 		app_icon = g_themed_icon_new ("application-x-executable");
4537 	}
4538 
4539 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4540 	gtk_action_set_gicon (action, app_icon);
4541 	G_GNUC_END_IGNORE_DEPRECATIONS;
4542 	g_object_unref (app_icon);
4543 
4544 	g_signal_connect_data (action, "activate",
4545 			       G_CALLBACK (open_with_launch_application_callback),
4546 			       launch_parameters,
4547 			       (GClosureNotify)application_launch_parameters_free, 0);
4548 
4549 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4550 	gtk_action_group_add_action (view->details->open_with_action_group,
4551 				     action);
4552 	G_GNUC_END_IGNORE_DEPRECATIONS;
4553 	g_object_unref (action);
4554 
4555 	gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
4556 			       view->details->open_with_merge_id,
4557 			       menu_placeholder,
4558 			       action_name,
4559 			       action_name,
4560 			       GTK_UI_MANAGER_MENUITEM,
4561 			       FALSE);
4562 
4563 	path = g_strdup_printf ("%s/%s", menu_placeholder, action_name);
4564 	menuitem = gtk_ui_manager_get_widget (
4565 			caja_window_info_get_ui_manager (view->details->window),
4566 			path);
4567 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
4568 	g_free (path);
4569 
4570 	gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
4571 			       view->details->open_with_merge_id,
4572 			       popup_placeholder,
4573 			       action_name,
4574 			       action_name,
4575 			       GTK_UI_MANAGER_MENUITEM,
4576 			       FALSE);
4577 
4578 	path = g_strdup_printf ("%s/%s", popup_placeholder, action_name);
4579 	menuitem = gtk_ui_manager_get_widget (
4580 			caja_window_info_get_ui_manager (view->details->window),
4581 			path);
4582 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
4583 
4584 	g_free (path);
4585 	g_free (action_name);
4586 	g_free (label);
4587 	g_free (tip);
4588 }
4589 
4590 static void
add_parent_folder_to_open_menu(FMDirectoryView * view,GList * files,const char * menu_placeholder,const char * popup_placeholder)4591 add_parent_folder_to_open_menu (FMDirectoryView *view,
4592 				GList *files,
4593 				const char *menu_placeholder,
4594 				const char *popup_placeholder)
4595 {
4596 	CajaFile *file;
4597 	gchar *uri;
4598 	char *tip;
4599 	char *label;
4600 	char *action_name;
4601 	char *path;
4602 	GtkAction *action;
4603 	GtkWidget *menuitem;
4604 
4605 	file = g_list_first(files)->data;
4606 
4607 	if (caja_file_is_directory (file))
4608 		return;
4609 
4610 	uri = caja_file_get_parent_uri (file);
4611 
4612 	label = g_strdup (_("Open parent location"));
4613 	tip = g_strdup (_("Open parent location for the selected item"));
4614 	action_name = g_strdup ("open_location");
4615 
4616 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4617 	action = gtk_action_new (action_name,
4618 				 label,
4619 				 tip,
4620 				 NULL);
4621 
4622 	gtk_action_set_icon_name (action, "folder");
4623 	G_GNUC_END_IGNORE_DEPRECATIONS;
4624 
4625 	g_signal_connect_data (action, "activate",
4626 			       G_CALLBACK (open_parent_folder_callback),
4627 			       uri, (GClosureNotify)g_free, 0);
4628 
4629 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4630 	gtk_action_group_add_action (view->details->open_with_action_group,
4631 				     action);
4632 	G_GNUC_END_IGNORE_DEPRECATIONS;
4633 	g_object_unref (action);
4634 
4635 	gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
4636 			       view->details->open_with_merge_id,
4637 			       menu_placeholder,
4638 			       action_name,
4639 			       action_name,
4640 			       GTK_UI_MANAGER_MENUITEM,
4641 			       FALSE);
4642 
4643 	path = g_strdup_printf ("%s/%s", menu_placeholder, action_name);
4644 	menuitem = gtk_ui_manager_get_widget (
4645 			caja_window_info_get_ui_manager (view->details->window),
4646 			path);
4647 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
4648 	g_free (path);
4649 
4650 	gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
4651 			       view->details->open_with_merge_id,
4652 			       popup_placeholder,
4653 			       action_name,
4654 			       action_name,
4655 			       GTK_UI_MANAGER_MENUITEM,
4656 			       FALSE);
4657 
4658 	path = g_strdup_printf ("%s/%s", popup_placeholder, action_name);
4659 	menuitem = gtk_ui_manager_get_widget (
4660 			caja_window_info_get_ui_manager (view->details->window),
4661 			path);
4662 	gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (menuitem), TRUE);
4663 
4664 	g_free (path);
4665 	g_free (action_name);
4666 	g_free (label);
4667 	g_free (tip);
4668 }
4669 
4670 static void
get_x_content_async_callback(char ** content,gpointer user_data)4671 get_x_content_async_callback (char **content,
4672 			      gpointer user_data)
4673 {
4674 	FMDirectoryView *view;
4675 
4676 	view = FM_DIRECTORY_VIEW (user_data);
4677 
4678 	if (view->details->window != NULL) {
4679 		schedule_update_menus (view);
4680 	}
4681 	g_object_unref (view);
4682 }
4683 
4684 static void
add_x_content_apps(FMDirectoryView * view,CajaFile * file,GList ** applications)4685 add_x_content_apps (FMDirectoryView *view, CajaFile *file, GList **applications)
4686 {
4687 	GMount *mount;
4688 	char **x_content_types;
4689 
4690 	g_return_if_fail (applications != NULL);
4691 
4692 	mount = caja_file_get_mount (file);
4693 
4694 	if (mount == NULL) {
4695 		return;
4696 	}
4697 
4698 	x_content_types = caja_autorun_get_cached_x_content_types_for_mount (mount);
4699 	if (x_content_types != NULL) {
4700 		unsigned int n;
4701 
4702 		for (n = 0; x_content_types[n] != NULL; n++) {
4703 			char *x_content_type = x_content_types[n];
4704 			GList *app_info_for_x_content_type;
4705 
4706 			app_info_for_x_content_type = g_app_info_get_all_for_type (x_content_type);
4707 			*applications = g_list_concat (*applications, app_info_for_x_content_type);
4708 		}
4709 		g_strfreev (x_content_types);
4710 	} else {
4711 		caja_autorun_get_x_content_types_for_mount_async (mount,
4712 								      get_x_content_async_callback,
4713 								      NULL,
4714 								      g_object_ref (view));
4715 
4716 	}
4717 
4718 	g_object_unref (mount);
4719 }
4720 
4721 static void
reset_open_with_menu(FMDirectoryView * view,GList * selection)4722 reset_open_with_menu (FMDirectoryView *view, GList *selection)
4723 {
4724 	GList *applications, *node;
4725 	gboolean submenu_visible, filter_default;
4726 	int num_applications;
4727 	int index;
4728 	gboolean other_applications_visible;
4729 	gboolean open_with_chooser_visible;
4730 	GtkUIManager *ui_manager;
4731 	GtkAction *action;
4732 	GAppInfo *default_app;
4733 
4734 	/* Clear any previous inserted items in the applications and viewers placeholders */
4735 
4736 	ui_manager = caja_window_info_get_ui_manager (view->details->window);
4737 	caja_ui_unmerge_ui (ui_manager,
4738 				&view->details->open_with_merge_id,
4739 				&view->details->open_with_action_group);
4740 
4741 	caja_ui_prepare_merge_ui (ui_manager,
4742 				      "OpenWithGroup",
4743 				      &view->details->open_with_merge_id,
4744 				      &view->details->open_with_action_group);
4745 
4746 	num_applications = 0;
4747 
4748 	other_applications_visible = (selection != NULL);
4749 	filter_default = (selection != NULL);
4750 
4751 	default_app = NULL;
4752 	if (filter_default) {
4753 		default_app = caja_mime_get_default_application_for_files (selection);
4754 	}
4755 
4756 	applications = NULL;
4757 	if (other_applications_visible) {
4758 		applications = caja_mime_get_applications_for_files (selection);
4759 	}
4760 
4761 	if (g_list_length (selection) == 1) {
4762 		add_x_content_apps (view, CAJA_FILE (selection->data), &applications);
4763 	}
4764 
4765 
4766 	num_applications = g_list_length (applications);
4767 
4768 	if (file_list_all_are_folders (selection)) {
4769 		submenu_visible = (num_applications > 2);
4770 	} else {
4771 		submenu_visible = (num_applications > 3);
4772 	}
4773 
4774 	for (node = applications, index = 0; node != NULL; node = node->next, index++) {
4775 		GAppInfo *application;
4776 		char *menu_path;
4777 		char *popup_path;
4778 
4779 		application = node->data;
4780 
4781 		if (default_app != NULL && g_app_info_equal (default_app, application)) {
4782 			continue;
4783 		}
4784 
4785 		if (submenu_visible) {
4786 			menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
4787 			popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_SUBMENU_PLACEHOLDER;
4788 		} else {
4789 			menu_path = FM_DIRECTORY_VIEW_MENU_PATH_APPLICATIONS_PLACEHOLDER;
4790 			popup_path = FM_DIRECTORY_VIEW_POPUP_PATH_APPLICATIONS_PLACEHOLDER;
4791 		}
4792 
4793 		gtk_ui_manager_add_ui (caja_window_info_get_ui_manager (view->details->window),
4794 				       view->details->open_with_merge_id,
4795 				       menu_path,
4796 				       "separator",
4797 				       NULL,
4798 				       GTK_UI_MANAGER_SEPARATOR,
4799 				       FALSE);
4800 
4801 		add_application_to_open_with_menu (view,
4802 						   node->data,
4803 						   selection,
4804 						   index,
4805 						   menu_path, popup_path, submenu_visible);
4806 
4807 	}
4808 	g_list_free_full (applications, g_object_unref);
4809 	if (default_app != NULL) {
4810 		g_object_unref (default_app);
4811 	}
4812 
4813 	/* Show open parent folder action if we are in search mode */
4814 	if (eel_uri_is_search (fm_directory_view_get_uri (view)) && g_list_length (selection) == 1)
4815 		add_parent_folder_to_open_menu (view,
4816 					       selection,
4817 					       FM_DIRECTORY_VIEW_MENU_PATH_OPEN,
4818 					       FM_DIRECTORY_VIEW_POPUP_PATH_OPEN);
4819 
4820 	open_with_chooser_visible = other_applications_visible &&
4821 				    g_list_length (selection) == 1;
4822 
4823 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4824 	if (submenu_visible) {
4825 		action = gtk_action_group_get_action (view->details->dir_action_group,
4826 						      FM_ACTION_OTHER_APPLICATION1);
4827 		gtk_action_set_visible (action, open_with_chooser_visible);
4828 		action = gtk_action_group_get_action (view->details->dir_action_group,
4829 						      FM_ACTION_OTHER_APPLICATION2);
4830 		gtk_action_set_visible (action, FALSE);
4831 	} else {
4832 		action = gtk_action_group_get_action (view->details->dir_action_group,
4833 						      FM_ACTION_OTHER_APPLICATION1);
4834 		gtk_action_set_visible (action, FALSE);
4835 		action = gtk_action_group_get_action (view->details->dir_action_group,
4836 						      FM_ACTION_OTHER_APPLICATION2);
4837 		gtk_action_set_visible (action, open_with_chooser_visible);
4838 	}
4839 	G_GNUC_END_IGNORE_DEPRECATIONS;
4840 }
4841 
4842 static GList *
get_all_extension_menu_items(GtkWidget * window,GList * selection)4843 get_all_extension_menu_items (GtkWidget *window,
4844 			      GList *selection)
4845 {
4846 	GList *items;
4847 	GList *providers;
4848 	GList *l;
4849 
4850 	providers = caja_extensions_get_for_type (CAJA_TYPE_MENU_PROVIDER);
4851 	items = NULL;
4852 
4853 	for (l = providers; l != NULL; l = l->next) {
4854 		CajaMenuProvider *provider;
4855 		GList *file_items;
4856 
4857 		provider = CAJA_MENU_PROVIDER (l->data);
4858 		file_items = caja_menu_provider_get_file_items (provider,
4859 								    window,
4860 								    selection);
4861 		items = g_list_concat (items, file_items);
4862 	}
4863 
4864 	caja_module_extension_list_free (providers);
4865 
4866 	return items;
4867 }
4868 
4869 typedef struct
4870 {
4871 	CajaMenuItem *item;
4872 	FMDirectoryView *view;
4873 	GList *selection;
4874 	GtkAction *action;
4875 } ExtensionActionCallbackData;
4876 
4877 
4878 static void
extension_action_callback_data_free(ExtensionActionCallbackData * data)4879 extension_action_callback_data_free (ExtensionActionCallbackData *data)
4880 {
4881 	g_object_unref (data->item);
4882 	caja_file_list_free (data->selection);
4883 
4884 	g_free (data);
4885 }
4886 
4887 static gboolean
search_in_menu_items(GList * items,const char * item_name)4888 search_in_menu_items (GList* items, const char *item_name)
4889 {
4890 	GList* list;
4891 
4892 	for (list = items; list != NULL; list = list->next) {
4893 		CajaMenu* menu;
4894 		char *name;
4895 
4896 		g_object_get (list->data, "name", &name, NULL);
4897 		if (strcmp (name, item_name) == 0) {
4898 			g_free (name);
4899 			return TRUE;
4900 		}
4901 		g_free (name);
4902 
4903 		menu = NULL;
4904 		g_object_get (list->data, "menu", &menu, NULL);
4905 		if (menu != NULL) {
4906 			gboolean ret;
4907 			GList* submenus;
4908 
4909 			submenus = caja_menu_get_items (menu);
4910 			ret = search_in_menu_items (submenus, item_name);
4911 			caja_menu_item_list_free (submenus);
4912 			g_object_unref (menu);
4913 			if (ret) {
4914 			    return TRUE;
4915 			}
4916 		}
4917 	}
4918 	return FALSE;
4919 }
4920 
4921 static void
extension_action_callback(GtkAction * action,gpointer callback_data)4922 extension_action_callback (GtkAction *action,
4923 			   gpointer callback_data)
4924 {
4925 	ExtensionActionCallbackData *data;
4926 	char *item_name;
4927 	gboolean is_valid;
4928 	GList *l;
4929 	GList *items;
4930 
4931 	data = callback_data;
4932 
4933 	/* Make sure the selected menu item is valid for the final sniffed
4934 	 * mime type */
4935 	g_object_get (data->item, "name", &item_name, NULL);
4936 	items = get_all_extension_menu_items (gtk_widget_get_toplevel (GTK_WIDGET (data->view)),
4937 					      data->selection);
4938 
4939 	is_valid = search_in_menu_items (items, item_name);
4940 
4941 	for (l = items; l != NULL; l = l->next) {
4942 		g_object_unref (l->data);
4943 	}
4944 	g_list_free (items);
4945 
4946 	g_free (item_name);
4947 
4948 	if (is_valid) {
4949 		caja_menu_item_activate (data->item);
4950 	}
4951 }
4952 
4953 static cairo_surface_t *
get_menu_icon(const char * icon_name,GtkWidget * widget)4954 get_menu_icon (const char *icon_name,
4955 	       GtkWidget  *widget)
4956 {
4957 	CajaIconInfo *info;
4958 	cairo_surface_t *surface;
4959 	int size, scale;
4960 
4961 	size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
4962 	scale = gtk_widget_get_scale_factor (widget);
4963 
4964 	if (g_path_is_absolute (icon_name)) {
4965 		info = caja_icon_info_lookup_from_path (icon_name, size, scale);
4966 	} else {
4967 		info = caja_icon_info_lookup_from_name (icon_name, size, scale);
4968 	}
4969 	surface = caja_icon_info_get_surface_nodefault_at_size (info, size);
4970 	g_object_unref (info);
4971 
4972 	return surface;
4973 }
4974 
4975 static cairo_surface_t *
get_menu_icon_for_file(CajaFile * file,GtkWidget * widget)4976 get_menu_icon_for_file (CajaFile  *file,
4977 			GtkWidget *widget)
4978 {
4979 	CajaIconInfo *info;
4980 	cairo_surface_t *surface;
4981 	int size, scale;
4982 
4983 	size = caja_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU);
4984 	scale = gtk_widget_get_scale_factor (widget);
4985 
4986 	info = caja_file_get_icon (file, size, scale, 0);
4987 	surface = caja_icon_info_get_surface_nodefault_at_size (info, size);
4988 	g_object_unref (info);
4989 
4990 	return surface;
4991 }
4992 
4993 static GtkAction *
add_extension_action_for_files(FMDirectoryView * view,CajaMenuItem * item,GList * files)4994 add_extension_action_for_files (FMDirectoryView *view,
4995 				CajaMenuItem *item,
4996 				GList *files)
4997 {
4998 	char *name, *label, *tip, *icon;
4999 	gboolean sensitive, priority;
5000 	GtkAction *action;
5001 	ExtensionActionCallbackData *data;
5002 
5003 	g_object_get (G_OBJECT (item),
5004 		      "name", &name, "label", &label,
5005 		      "tip", &tip, "icon", &icon,
5006 		      "sensitive", &sensitive,
5007 		      "priority", &priority,
5008 		      NULL);
5009 
5010 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5011 	action = gtk_action_new (name,
5012 				 label,
5013 				 tip,
5014 				 icon);
5015 	G_GNUC_END_IGNORE_DEPRECATIONS;
5016 
5017 	if (icon != NULL) {
5018 		cairo_surface_t *surface;
5019 
5020 		surface = get_menu_icon (icon, GTK_WIDGET (view));
5021 
5022 		if (surface != NULL) {
5023 			g_object_set_data_full (G_OBJECT (action), "menu-icon",
5024 						surface,
5025 						(GDestroyNotify)cairo_surface_destroy);
5026 		}
5027 	}
5028 
5029 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5030 	gtk_action_set_sensitive (action, sensitive);
5031 	G_GNUC_END_IGNORE_DEPRECATIONS;
5032 	g_object_set (action, "is-important", priority, NULL);
5033 
5034 	data = g_new0 (ExtensionActionCallbackData, 1);
5035 	data->item = g_object_ref (item);
5036 	data->view = view;
5037 	data->selection = caja_file_list_copy (files);
5038 	data->action = action;
5039 
5040 	g_signal_connect_data (action, "activate",
5041 			       G_CALLBACK (extension_action_callback),
5042 			       data,
5043 			       (GClosureNotify)extension_action_callback_data_free, 0);
5044 
5045 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5046 	gtk_action_group_add_action (view->details->extensions_menu_action_group,
5047 				     GTK_ACTION (action));
5048 	G_GNUC_END_IGNORE_DEPRECATIONS;
5049 	g_object_unref (action);
5050 
5051 	g_free (name);
5052 	g_free (label);
5053 	g_free (tip);
5054 	g_free (icon);
5055 
5056 	return action;
5057 }
5058 
5059 static void
add_extension_menu_items(FMDirectoryView * view,GList * files,GList * menu_items,const char * subdirectory)5060 add_extension_menu_items (FMDirectoryView *view,
5061 			  GList *files,
5062 			  GList *menu_items,
5063 			  const char *subdirectory)
5064 {
5065 	GtkUIManager *ui_manager;
5066 	GList *l;
5067 
5068 	ui_manager = caja_window_info_get_ui_manager (view->details->window);
5069 
5070 	for (l = menu_items; l; l = l->next) {
5071 		CajaMenuItem *item;
5072 		CajaMenu *menu;
5073 		GtkAction *action;
5074 		const gchar *action_name;
5075 		char *path;
5076 
5077 		item = CAJA_MENU_ITEM (l->data);
5078 
5079 		g_object_get (item, "menu", &menu, NULL);
5080 
5081 		action = add_extension_action_for_files (view, item, files);
5082 
5083 		path = g_build_path ("/", FM_DIRECTORY_VIEW_POPUP_PATH_EXTENSION_ACTIONS, subdirectory, NULL);
5084 		G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5085 		action_name = gtk_action_get_name (action);
5086 		G_GNUC_END_IGNORE_DEPRECATIONS;
5087 
5088 		gtk_ui_manager_add_ui (ui_manager,
5089 				       view->details->extensions_menu_merge_id,
5090 				       path,
5091 				       action_name,
5092 				       action_name,
5093 				       (menu != NULL) ? GTK_UI_MANAGER_MENU : GTK_UI_MANAGER_MENUITEM,
5094 				       FALSE);
5095 		g_free (path);
5096 
5097 		path = g_build_path ("/", FM_DIRECTORY_VIEW_MENU_PATH_EXTENSION_ACTIONS_PLACEHOLDER, subdirectory, NULL);
5098 		gtk_ui_manager_add_ui (ui_manager,
5099 				       view->details->extensions_menu_merge_id,
5100 				       path,
5101 				       action_name,
5102 				       action_name,
5103 				       (menu != NULL) ? GTK_UI_MANAGER_MENU : GTK_UI_MANAGER_MENUITEM,
5104 				       FALSE);
5105 		g_free (path);
5106 
5107 		/* recursively fill the menu */
5108 		if (menu != NULL) {
5109 			char *subdir;
5110 			GList *children;
5111 
5112 			children = caja_menu_get_items (menu);
5113 
5114 			G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5115 			subdir = g_build_path ("/", subdirectory, gtk_action_get_name (action), NULL);
5116 			G_GNUC_END_IGNORE_DEPRECATIONS;
5117 			add_extension_menu_items (view,
5118 						  files,
5119 						  children,
5120 						  subdir);
5121 
5122 			caja_menu_item_list_free (children);
5123 			g_free (subdir);
5124 		}
5125 	}
5126 }
5127 
5128 static void
reset_extension_actions_menu(FMDirectoryView * view,GList * selection)5129 reset_extension_actions_menu (FMDirectoryView *view, GList *selection)
5130 {
5131 	GList *items;
5132 	GtkUIManager *ui_manager;
5133 
5134 	/* Clear any previous inserted items in the extension actions placeholder */
5135 	ui_manager = caja_window_info_get_ui_manager (view->details->window);
5136 
5137 	caja_ui_unmerge_ui (ui_manager,
5138 				&view->details->extensions_menu_merge_id,
5139 				&view->details->extensions_menu_action_group);
5140 
5141 	caja_ui_prepare_merge_ui (ui_manager,
5142 				      "DirExtensionsMenuGroup",
5143 				      &view->details->extensions_menu_merge_id,
5144 				      &view->details->extensions_menu_action_group);
5145 
5146 	items = get_all_extension_menu_items (gtk_widget_get_toplevel (GTK_WIDGET (view)),
5147 					      selection);
5148 	if (items != NULL) {
5149 		add_extension_menu_items (view, selection, items, "");
5150 
5151 		g_list_free_full (items, g_object_unref);
5152 	}
5153 }
5154 
5155 static char *
change_to_view_directory(FMDirectoryView * view)5156 change_to_view_directory (FMDirectoryView *view)
5157 {
5158 	char *path;
5159 	char *old_path;
5160 
5161 	old_path = g_get_current_dir ();
5162 
5163 	path = get_view_directory (view);
5164 
5165 	/* FIXME: What to do about non-local directories? */
5166 	if (path != NULL) {
5167 		g_chdir (path);
5168 	}
5169 
5170 	g_free (path);
5171 
5172 	return old_path;
5173 }
5174 
5175 static char **
get_file_names_as_parameter_array(GList * selection,CajaDirectory * model)5176 get_file_names_as_parameter_array (GList *selection,
5177 				   CajaDirectory *model)
5178 {
5179 	char **parameters;
5180 	GList *node;
5181 	GFile *model_location;
5182 	int i;
5183 	CajaFile *file = NULL;
5184 	GFile *file_location = NULL;
5185 
5186 	if (model == NULL) {
5187 		return NULL;
5188 	}
5189 
5190 	parameters = g_new (char *, g_list_length (selection) + 1);
5191 
5192 	model_location = caja_directory_get_location (model);
5193 
5194 	for (node = selection, i = 0; node != NULL; node = node->next, i++) {
5195 		file = CAJA_FILE (node->data);
5196 
5197 		if (!caja_file_is_local (file)) {
5198 			parameters[i] = NULL;
5199 			g_strfreev (parameters);
5200 			return NULL;
5201 		}
5202 
5203 		file_location = caja_file_get_location (CAJA_FILE (node->data));
5204 		parameters[i] = g_file_get_relative_path (model_location, file_location);
5205 		if (parameters[i] == NULL) {
5206 			parameters[i] = g_file_get_path (file_location);
5207 		}
5208 		g_object_unref (file_location);
5209 	}
5210 
5211 	g_object_unref (model_location);
5212 
5213 	parameters[i] = NULL;
5214 	return parameters;
5215 }
5216 
5217 static char *
get_file_paths_or_uris_as_newline_delimited_string(GList * selection,gboolean get_paths)5218 get_file_paths_or_uris_as_newline_delimited_string (GList *selection, gboolean get_paths)
5219 {
5220 	char *path;
5221 	char *result;
5222 	CajaDesktopLink *link;
5223 	GString *expanding_string;
5224 	GList *node;
5225 	GFile *location;
5226 
5227 	expanding_string = g_string_new ("");
5228 	for (node = selection; node != NULL; node = node->next) {
5229 		char *uri;
5230 
5231 		uri = NULL;
5232 
5233 		if (CAJA_IS_DESKTOP_ICON_FILE (node->data)) {
5234 			link = caja_desktop_icon_file_get_link (CAJA_DESKTOP_ICON_FILE (node->data));
5235 			if (link != NULL) {
5236 				location = caja_desktop_link_get_activation_location (link);
5237 				uri = g_file_get_uri (location);
5238 				g_object_unref (location);
5239 				g_object_unref (G_OBJECT (link));
5240 			}
5241 		} else {
5242 			uri = caja_file_get_uri (CAJA_FILE (node->data));
5243 		}
5244 		if (uri == NULL) {
5245 			continue;
5246 		}
5247 
5248 		if (get_paths) {
5249 			path = g_filename_from_uri (uri, NULL, NULL);
5250 			if (path != NULL) {
5251 				g_string_append (expanding_string, path);
5252 				g_free (path);
5253 				g_string_append (expanding_string, "\n");
5254 			}
5255 		} else {
5256 			g_string_append (expanding_string, uri);
5257 			g_string_append (expanding_string, "\n");
5258 		}
5259 		g_free (uri);
5260 	}
5261 
5262 	result = expanding_string->str;
5263 	g_string_free (expanding_string, FALSE);
5264 
5265 	return result;
5266 }
5267 
5268 static char *
get_file_paths_as_newline_delimited_string(GList * selection)5269 get_file_paths_as_newline_delimited_string (GList *selection)
5270 {
5271 	return get_file_paths_or_uris_as_newline_delimited_string (selection, TRUE);
5272 }
5273 
5274 static char *
get_file_uris_as_newline_delimited_string(GList * selection)5275 get_file_uris_as_newline_delimited_string (GList *selection)
5276 {
5277 	return get_file_paths_or_uris_as_newline_delimited_string (selection, FALSE);
5278 }
5279 
5280 /* returns newly allocated strings for setting the environment variables */
5281 static void
get_strings_for_environment_variables(FMDirectoryView * view,GList * selected_files,char ** file_paths,char ** uris,char ** uri)5282 get_strings_for_environment_variables (FMDirectoryView *view, GList *selected_files,
5283 				       char **file_paths, char **uris, char **uri)
5284 {
5285 	char *directory_uri;
5286 
5287 	/* We need to check that the directory uri starts with "file:" since
5288 	 * caja_directory_is_local returns FALSE for nfs.
5289 	 */
5290 	directory_uri = caja_directory_get_uri (view->details->model);
5291 	if (eel_str_has_prefix (directory_uri, "file:") ||
5292 	    eel_uri_is_desktop (directory_uri) ||
5293 	    eel_uri_is_trash (directory_uri)) {
5294 		*file_paths = get_file_paths_as_newline_delimited_string (selected_files);
5295 	} else {
5296 		*file_paths = g_strdup ("");
5297 	}
5298 	g_free (directory_uri);
5299 
5300 	*uris = get_file_uris_as_newline_delimited_string (selected_files);
5301 
5302 	*uri = caja_directory_get_uri (view->details->model);
5303 	if (eel_uri_is_desktop (*uri)) {
5304 		g_free (*uri);
5305 		*uri = caja_get_desktop_directory_uri ();
5306 	}
5307 }
5308 
5309 static FMDirectoryView *
get_directory_view_of_extra_pane(FMDirectoryView * view)5310 get_directory_view_of_extra_pane (FMDirectoryView *view)
5311 {
5312 	CajaWindowSlotInfo *slot;
5313 
5314 	slot = caja_window_info_get_extra_slot (fm_directory_view_get_caja_window (view));
5315 	if (slot != NULL) {
5316 		CajaView *next_view;
5317 
5318 		next_view = caja_window_slot_info_get_current_view (slot);
5319 
5320 		if (FM_IS_DIRECTORY_VIEW (next_view)) {
5321 			return FM_DIRECTORY_VIEW (next_view);
5322 		}
5323 	}
5324 	return NULL;
5325 }
5326 
5327 /*
5328  * Set up some environment variables that scripts can use
5329  * to take advantage of the current Caja state.
5330  */
set_script_environment_variables(FMDirectoryView * view,GList * selected_files)5331 static void set_script_environment_variables(FMDirectoryView* view, GList* selected_files)
5332 {
5333 	char* file_paths;
5334 	char* uris;
5335 	char* uri;
5336 	char* geometry_string;
5337 	FMDirectoryView* next_view;
5338 
5339 	get_strings_for_environment_variables(view, selected_files, &file_paths, &uris, &uri);
5340 
5341 	g_setenv("CAJA_SCRIPT_SELECTED_FILE_PATHS", file_paths, TRUE);
5342 	g_setenv("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS", file_paths, TRUE); // compatibilidad GNOME
5343 
5344 	g_free(file_paths);
5345 
5346 	g_setenv("CAJA_SCRIPT_SELECTED_URIS", uris, TRUE);
5347 	g_setenv("NAUTILUS_SCRIPT_SELECTED_URIS", uris, TRUE); // compatibilidad GNOME
5348 
5349 	g_free(uris);
5350 
5351 	g_setenv("CAJA_SCRIPT_CURRENT_URI", uri, TRUE);
5352 	g_setenv("NAUTILUS_SCRIPT_CURRENT_URI", uri, TRUE); // compatibilidad GNOME
5353 
5354 
5355 	g_free(uri);
5356 
5357 	geometry_string = eel_gtk_window_get_geometry_string(GTK_WINDOW (fm_directory_view_get_containing_window (view)));
5358 
5359 	g_setenv("CAJA_SCRIPT_WINDOW_GEOMETRY", geometry_string, TRUE);
5360 	g_setenv("NAUTILUS_SCRIPT_WINDOW_GEOMETRY", geometry_string, TRUE); // compatibilidad GNOME
5361 
5362 	g_free(geometry_string);
5363 
5364 	/* next pane */
5365 	next_view = get_directory_view_of_extra_pane(view);
5366 
5367 	if (next_view)
5368 	{
5369 		GList* next_pane_selected_files = fm_directory_view_get_selection (next_view);
5370 
5371 		get_strings_for_environment_variables(next_view, next_pane_selected_files, &file_paths, &uris, &uri);
5372 
5373 		caja_file_list_free(next_pane_selected_files);
5374 	}
5375 	else
5376 	{
5377 		file_paths = g_strdup("");
5378 		uris = g_strdup("");
5379 		uri = g_strdup("");
5380 	}
5381 
5382 	g_setenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS", file_paths, TRUE);
5383 	g_setenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS", file_paths, TRUE); // compatibilidad GNOME
5384 	g_free(file_paths);
5385 
5386 	g_setenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_URIS", uris, TRUE);
5387 	g_setenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_URIS", uris, TRUE); // compatibilidad GNOME
5388 	g_free(uris);
5389 
5390 	g_setenv("CAJA_SCRIPT_NEXT_PANE_CURRENT_URI", uri, TRUE);
5391 	g_setenv("NAUTILUS_SCRIPT_NEXT_PANE_CURRENT_URI", uri, TRUE); // compatibilidad GNOME
5392 	g_free(uri);
5393 }
5394 
5395 /* Unset all the special script environment variables. */
unset_script_environment_variables(void)5396 static void unset_script_environment_variables(void)
5397 {
5398 	g_unsetenv("CAJA_SCRIPT_SELECTED_FILE_PATHS");
5399 	g_unsetenv("NAUTILUS_SCRIPT_SELECTED_FILE_PATHS");
5400 
5401 	g_unsetenv("CAJA_SCRIPT_SELECTED_URIS");
5402 	g_unsetenv("NAUTILUS_SCRIPT_SELECTED_URIS");
5403 
5404 	g_unsetenv("CAJA_SCRIPT_CURRENT_URI");
5405 	g_unsetenv("NAUTILUS_SCRIPT_CURRENT_URI");
5406 
5407 	g_unsetenv("CAJA_SCRIPT_WINDOW_GEOMETRY");
5408 	g_unsetenv("NAUTILUS_SCRIPT_WINDOW_GEOMETRY");
5409 
5410 	g_unsetenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS");
5411 	g_unsetenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS");
5412 
5413 	g_unsetenv("CAJA_SCRIPT_NEXT_PANE_SELECTED_URIS");
5414 	g_unsetenv("NAUTILUS_SCRIPT_NEXT_PANE_SELECTED_URIS");
5415 
5416 	g_unsetenv("CAJA_SCRIPT_NEXT_PANE_CURRENT_URI");
5417 	g_unsetenv("NAUTILUS_SCRIPT_NEXT_PANE_CURRENT_URI");
5418 }
5419 
5420 static void
run_script_callback(GtkAction * action,gpointer callback_data)5421 run_script_callback (GtkAction *action, gpointer callback_data)
5422 {
5423 	ScriptLaunchParameters *launch_parameters;
5424 	GdkScreen *screen;
5425 	GList *selected_files;
5426 	char *file_uri;
5427 	char *local_file_path;
5428 	char *quoted_path;
5429 	char *old_working_dir;
5430 	char **parameters, *name;
5431 	GtkWindow *window;
5432 
5433 	launch_parameters = (ScriptLaunchParameters *) callback_data;
5434 
5435 	file_uri = caja_file_get_uri (launch_parameters->file);
5436 	local_file_path = g_filename_from_uri (file_uri, NULL, NULL);
5437 	g_assert (local_file_path != NULL);
5438 	g_free (file_uri);
5439 
5440 	quoted_path = g_shell_quote (local_file_path);
5441 
5442 	old_working_dir = change_to_view_directory (launch_parameters->directory_view);
5443 
5444 	selected_files = fm_directory_view_get_selection (launch_parameters->directory_view);
5445 	set_script_environment_variables (launch_parameters->directory_view, selected_files);
5446 
5447 	parameters = get_file_names_as_parameter_array (selected_files,
5448 						        launch_parameters->directory_view->details->model);
5449 
5450 	screen = gtk_widget_get_screen (GTK_WIDGET (launch_parameters->directory_view));
5451 
5452 	name = caja_file_get_name (launch_parameters->file);
5453 	/* FIXME: handle errors with dialog? Or leave up to each script? */
5454 	window = fm_directory_view_get_containing_window (launch_parameters->directory_view);
5455 	caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
5456 			    "directory view run_script_callback, window=%p, name=\"%s\", script_path=\"%s\" (omitting script parameters)",
5457 			    window, name, local_file_path);
5458 	caja_launch_application_from_command_array (screen, name, quoted_path, FALSE,
5459 							(const char * const *) parameters);
5460 	g_free (local_file_path);
5461 	g_free (name);
5462 	g_strfreev (parameters);
5463 
5464 	caja_file_list_free (selected_files);
5465 	unset_script_environment_variables ();
5466 	g_chdir (old_working_dir);
5467 	g_free (old_working_dir);
5468 	g_free (quoted_path);
5469 }
5470 
5471 static void
add_script_to_scripts_menus(FMDirectoryView * directory_view,CajaFile * file,const char * menu_path,const char * popup_path,const char * popup_bg_path)5472 add_script_to_scripts_menus (FMDirectoryView *directory_view,
5473 			     CajaFile *file,
5474 			     const char *menu_path,
5475 			     const char *popup_path,
5476 			     const char *popup_bg_path)
5477 {
5478 	ScriptLaunchParameters *launch_parameters;
5479 	char *tip;
5480 	char *name;
5481 	char *uri;
5482 	char *action_name;
5483 	char *escaped_label;
5484 	cairo_surface_t *surface;
5485 	GtkUIManager *ui_manager;
5486 	GtkAction *action;
5487 
5488 	name = caja_file_get_display_name (file);
5489 	uri = caja_file_get_uri (file);
5490 	tip = g_strdup_printf (_("Run \"%s\" on any selected items"), name);
5491 
5492 	launch_parameters = script_launch_parameters_new (file, directory_view);
5493 
5494 	action_name = escape_action_name (uri, "script_");
5495 	escaped_label = eel_str_double_underscores (name);
5496 
5497 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5498 	action = gtk_action_new (action_name,
5499 				 escaped_label,
5500 				 tip,
5501 				 NULL);
5502 	G_GNUC_END_IGNORE_DEPRECATIONS;
5503 
5504 	surface = get_menu_icon_for_file (file, GTK_WIDGET (directory_view));
5505 	if (surface != NULL) {
5506 		g_object_set_data_full (G_OBJECT (action), "menu-icon",
5507 					surface,
5508 					(GDestroyNotify)cairo_surface_destroy);
5509 	}
5510 
5511 	g_signal_connect_data (action, "activate",
5512 			       G_CALLBACK (run_script_callback),
5513 			       launch_parameters,
5514 			       (GClosureNotify)script_launch_parameters_free, 0);
5515 
5516 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5517 	gtk_action_group_add_action_with_accel (directory_view->details->scripts_action_group,
5518 						action, NULL);
5519 	G_GNUC_END_IGNORE_DEPRECATIONS;
5520 	g_object_unref (action);
5521 
5522 	ui_manager = caja_window_info_get_ui_manager (directory_view->details->window);
5523 
5524 	gtk_ui_manager_add_ui (ui_manager,
5525 			       directory_view->details->scripts_merge_id,
5526 			       menu_path,
5527 			       action_name,
5528 			       action_name,
5529 			       GTK_UI_MANAGER_MENUITEM,
5530 			       FALSE);
5531 	gtk_ui_manager_add_ui (ui_manager,
5532 			       directory_view->details->scripts_merge_id,
5533 			       popup_path,
5534 			       action_name,
5535 			       action_name,
5536 			       GTK_UI_MANAGER_MENUITEM,
5537 			       FALSE);
5538 	gtk_ui_manager_add_ui (ui_manager,
5539 			       directory_view->details->scripts_merge_id,
5540 			       popup_bg_path,
5541 			       action_name,
5542 			       action_name,
5543 			       GTK_UI_MANAGER_MENUITEM,
5544 			       FALSE);
5545 
5546 	g_free (name);
5547 	g_free (uri);
5548 	g_free (tip);
5549 	g_free (action_name);
5550 	g_free (escaped_label);
5551 }
5552 
5553 static void
add_submenu_to_directory_menus(FMDirectoryView * directory_view,GtkActionGroup * action_group,guint merge_id,CajaFile * file,const char * menu_path,const char * popup_path,const char * popup_bg_path)5554 add_submenu_to_directory_menus (FMDirectoryView *directory_view,
5555 				GtkActionGroup *action_group,
5556 				guint merge_id,
5557 				CajaFile *file,
5558 				const char *menu_path,
5559 				const char *popup_path,
5560 				const char *popup_bg_path)
5561 {
5562 	char *name;
5563 	cairo_surface_t *surface;
5564 	char *uri;
5565 	GtkUIManager *ui_manager;
5566 
5567 	ui_manager = caja_window_info_get_ui_manager (directory_view->details->window);
5568 	uri = caja_file_get_uri (file);
5569 	name = caja_file_get_display_name (file);
5570 	surface = get_menu_icon_for_file (file, GTK_WIDGET (directory_view));
5571 	add_submenu (ui_manager, action_group, merge_id, menu_path, uri, name, surface, TRUE);
5572 	add_submenu (ui_manager, action_group, merge_id, popup_path, uri, name, surface, FALSE);
5573 	add_submenu (ui_manager, action_group, merge_id, popup_bg_path, uri, name, surface, FALSE);
5574 	if (surface) {
5575 		cairo_surface_destroy (surface);
5576 	}
5577 	g_free (name);
5578 	g_free (uri);
5579 }
5580 
5581 static gboolean
directory_belongs_in_scripts_menu(const char * uri)5582 directory_belongs_in_scripts_menu (const char *uri)
5583 {
5584 	int num_levels;
5585 	int i;
5586 
5587 	if (!eel_str_has_prefix (uri, scripts_directory_uri)) {
5588 		return FALSE;
5589 	}
5590 
5591 	num_levels = 0;
5592 	for (i = scripts_directory_uri_length; uri[i] != '\0'; i++) {
5593 		if (uri[i] == '/') {
5594 			num_levels++;
5595 		}
5596 	}
5597 
5598 	if (num_levels > MAX_MENU_LEVELS) {
5599 		return FALSE;
5600 	}
5601 
5602 	return TRUE;
5603 }
5604 
5605 static gboolean
update_directory_in_scripts_menu(FMDirectoryView * view,CajaDirectory * directory)5606 update_directory_in_scripts_menu (FMDirectoryView *view, CajaDirectory *directory)
5607 {
5608 	char *menu_path, *popup_path, *popup_bg_path;
5609 	GList *file_list, *filtered, *node;
5610 	gboolean any_scripts;
5611 	CajaDirectory *dir;
5612 	char *uri;
5613 	char *escaped_path;
5614 	CajaFile *file = NULL;
5615 
5616 	uri = caja_directory_get_uri (directory);
5617 	escaped_path = escape_action_path (uri + scripts_directory_uri_length);
5618 	g_free (uri);
5619 	menu_path = g_strconcat (FM_DIRECTORY_VIEW_MENU_PATH_SCRIPTS_PLACEHOLDER,
5620 				 escaped_path,
5621 				 NULL);
5622 	popup_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_SCRIPTS_PLACEHOLDER,
5623 				  escaped_path,
5624 				  NULL);
5625 	popup_bg_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_SCRIPTS_PLACEHOLDER,
5626 				  escaped_path,
5627 				  NULL);
5628 	g_free (escaped_path);
5629 
5630 	file_list = caja_directory_get_file_list (directory);
5631 	filtered = caja_file_list_filter_hidden (file_list, FALSE);
5632 	caja_file_list_free (file_list);
5633 
5634 	file_list = caja_file_list_sort_by_display_name (filtered);
5635 
5636 	any_scripts = FALSE;
5637 	for (node = file_list; node != NULL; node = node->next) {
5638 		file = node->data;
5639 
5640 		if (caja_file_is_launchable (file)) {
5641 			add_script_to_scripts_menus (view, file, menu_path, popup_path, popup_bg_path);
5642 			any_scripts = TRUE;
5643 		} else if (caja_file_is_directory (file)) {
5644 			uri = caja_file_get_uri (file);
5645 			if (directory_belongs_in_scripts_menu (uri)) {
5646 				dir = caja_directory_get_by_uri (uri);
5647 				add_directory_to_scripts_directory_list (view, dir);
5648 				caja_directory_unref (dir);
5649 
5650 				add_submenu_to_directory_menus (view,
5651 								view->details->scripts_action_group,
5652 								view->details->scripts_merge_id,
5653 								file, menu_path, popup_path, popup_bg_path);
5654 
5655 				any_scripts = TRUE;
5656 			}
5657 			g_free (uri);
5658 		}
5659 	}
5660 
5661 	caja_file_list_free (file_list);
5662 
5663 	g_free (popup_path);
5664 	g_free (popup_bg_path);
5665 	g_free (menu_path);
5666 
5667 	return any_scripts;
5668 }
5669 
5670 static void
update_scripts_menu(FMDirectoryView * view)5671 update_scripts_menu (FMDirectoryView *view)
5672 {
5673 	gboolean any_scripts;
5674 	GList *sorted_copy, *node;
5675 	GtkUIManager *ui_manager;
5676 	GtkAction *action;
5677 	CajaDirectory *directory = NULL;
5678 
5679 	/* There is a race condition here.  If we don't mark the scripts menu as
5680 	   valid before we begin our task then we can lose script menu updates that
5681 	   occur before we finish. */
5682 	view->details->scripts_invalid = FALSE;
5683 
5684 	ui_manager = caja_window_info_get_ui_manager (view->details->window);
5685 	caja_ui_unmerge_ui (ui_manager,
5686 				&view->details->scripts_merge_id,
5687 				&view->details->scripts_action_group);
5688 
5689 	caja_ui_prepare_merge_ui (ui_manager,
5690 				      "ScriptsGroup",
5691 				      &view->details->scripts_merge_id,
5692 				      &view->details->scripts_action_group);
5693 
5694 	/* As we walk through the directories, remove any that no longer belong. */
5695 	any_scripts = FALSE;
5696 	sorted_copy = caja_directory_list_sort_by_uri
5697 		(caja_directory_list_copy (view->details->scripts_directory_list));
5698 	for (node = sorted_copy; node != NULL; node = node->next) {
5699 		char *uri;
5700 
5701 		directory = node->data;
5702 
5703 		uri = caja_directory_get_uri (directory);
5704 		if (!directory_belongs_in_scripts_menu (uri)) {
5705 			remove_directory_from_scripts_directory_list (view, directory);
5706 		} else if (update_directory_in_scripts_menu (view, directory)) {
5707 			any_scripts = TRUE;
5708 		}
5709 		g_free (uri);
5710 	}
5711 	caja_directory_list_free (sorted_copy);
5712 
5713 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5714 	action = gtk_action_group_get_action (view->details->dir_action_group, FM_ACTION_SCRIPTS);
5715 	gtk_action_set_visible (action, any_scripts);
5716 	G_GNUC_END_IGNORE_DEPRECATIONS;
5717 }
5718 
5719 static void
create_template_callback(GtkAction * action,gpointer callback_data)5720 create_template_callback (GtkAction *action, gpointer callback_data)
5721 {
5722 	CreateTemplateParameters *parameters;
5723 
5724 	parameters = callback_data;
5725 
5726 	fm_directory_view_new_file (parameters->directory_view, NULL, parameters->file);
5727 }
5728 
5729 static void
add_template_to_templates_menus(FMDirectoryView * directory_view,CajaFile * file,const char * menu_path,const char * popup_bg_path)5730 add_template_to_templates_menus (FMDirectoryView *directory_view,
5731 				 CajaFile *file,
5732 				 const char *menu_path,
5733 				 const char *popup_bg_path)
5734 {
5735 	char *tmp, *tip, *uri, *name;
5736 	char *escaped_label;
5737 	cairo_surface_t *surface;
5738 	char *action_name;
5739 	CreateTemplateParameters *parameters;
5740 	GtkUIManager *ui_manager;
5741 	GtkAction *action;
5742 
5743 	tmp = caja_file_get_display_name (file);
5744 	name = eel_filename_strip_extension (tmp);
5745 	g_free (tmp);
5746 
5747 	uri = caja_file_get_uri (file);
5748 	tip = g_strdup_printf (_("Create Document from template \"%s\""), name);
5749 
5750 	action_name = escape_action_name (uri, "template_");
5751 	escaped_label = eel_str_double_underscores (name);
5752 
5753 	parameters = create_template_parameters_new (file, directory_view);
5754 
5755 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5756 	action = gtk_action_new (action_name,
5757 				 escaped_label,
5758 				 tip,
5759 				 NULL);
5760 	G_GNUC_END_IGNORE_DEPRECATIONS;
5761 
5762 	surface = get_menu_icon_for_file (file, GTK_WIDGET (directory_view));
5763 	if (surface != NULL) {
5764 		g_object_set_data_full (G_OBJECT (action), "menu-icon",
5765 					surface,
5766 					(GDestroyNotify)cairo_surface_destroy);
5767 	}
5768 
5769 	g_signal_connect_data (action, "activate",
5770 			       G_CALLBACK (create_template_callback),
5771 			       parameters,
5772 			       (GClosureNotify)create_templates_parameters_free, 0);
5773 
5774 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5775 	gtk_action_group_add_action (directory_view->details->templates_action_group,
5776 				     action);
5777 	G_GNUC_END_IGNORE_DEPRECATIONS;
5778 	g_object_unref (action);
5779 
5780 	ui_manager = caja_window_info_get_ui_manager (directory_view->details->window);
5781 
5782 	gtk_ui_manager_add_ui (ui_manager,
5783 			       directory_view->details->templates_merge_id,
5784 			       menu_path,
5785 			       action_name,
5786 			       action_name,
5787 			       GTK_UI_MANAGER_MENUITEM,
5788 			       FALSE);
5789 
5790 	gtk_ui_manager_add_ui (ui_manager,
5791 			       directory_view->details->templates_merge_id,
5792 			       popup_bg_path,
5793 			       action_name,
5794 			       action_name,
5795 			       GTK_UI_MANAGER_MENUITEM,
5796 			       FALSE);
5797 
5798 	g_free (escaped_label);
5799 	g_free (name);
5800 	g_free (tip);
5801 	g_free (uri);
5802 	g_free (action_name);
5803 }
5804 
5805 static void
update_templates_directory(FMDirectoryView * view)5806 update_templates_directory (FMDirectoryView *view)
5807 {
5808 	GList *node, *next;
5809 
5810 	for (node = view->details->templates_directory_list; node != NULL; node = next) {
5811 		next = node->next;
5812 		remove_directory_from_templates_directory_list (view, node->data);
5813 	}
5814 
5815 	if (caja_should_use_templates_directory ()) {
5816 		CajaDirectory *templates_directory;
5817 		char *templates_uri;
5818 
5819 		templates_uri = caja_get_templates_directory_uri ();
5820 		templates_directory = caja_directory_get_by_uri (templates_uri);
5821 		g_free (templates_uri);
5822 		add_directory_to_templates_directory_list (view, templates_directory);
5823 		caja_directory_unref (templates_directory);
5824 	}
5825 }
5826 
5827 static void
user_dirs_changed(FMDirectoryView * view)5828 user_dirs_changed (FMDirectoryView *view)
5829 {
5830 	update_templates_directory (view);
5831 	view->details->templates_invalid = TRUE;
5832 	schedule_update_menus (view);
5833 }
5834 
5835 static gboolean
directory_belongs_in_templates_menu(const char * templates_directory_uri,const char * uri)5836 directory_belongs_in_templates_menu (const char *templates_directory_uri,
5837 				     const char *uri)
5838 {
5839 	int num_levels;
5840 	int i;
5841 
5842 	if (templates_directory_uri == NULL) {
5843 		return FALSE;
5844 	}
5845 
5846 	if (!g_str_has_prefix (uri, templates_directory_uri)) {
5847 		return FALSE;
5848 	}
5849 
5850 	num_levels = 0;
5851 	for (i = strlen (templates_directory_uri); uri[i] != '\0'; i++) {
5852 		if (uri[i] == '/') {
5853 			num_levels++;
5854 		}
5855 	}
5856 
5857 	if (num_levels > MAX_MENU_LEVELS) {
5858 		return FALSE;
5859 	}
5860 
5861 	return TRUE;
5862 }
5863 
5864 static gboolean
update_directory_in_templates_menu(FMDirectoryView * view,const char * templates_directory_uri,CajaDirectory * directory)5865 update_directory_in_templates_menu (FMDirectoryView *view,
5866 				    const char *templates_directory_uri,
5867 				    CajaDirectory *directory)
5868 {
5869 	char *menu_path, *popup_bg_path;
5870 	GList *file_list, *filtered, *node;
5871 	gboolean any_templates;
5872 	CajaDirectory *dir;
5873 	char *escaped_path;
5874 	char *uri;
5875 	int num;
5876 	CajaFile *file = NULL;
5877 
5878 	/* We know this directory belongs to the template dir, so it must exist */
5879 	g_assert (templates_directory_uri);
5880 
5881 	uri = caja_directory_get_uri (directory);
5882 	escaped_path = escape_action_path (uri + strlen (templates_directory_uri));
5883 	g_free (uri);
5884 	menu_path = g_strconcat (FM_DIRECTORY_VIEW_MENU_PATH_NEW_DOCUMENTS_PLACEHOLDER,
5885 				 escaped_path,
5886 				 NULL);
5887 	popup_bg_path = g_strconcat (FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND_NEW_DOCUMENTS_PLACEHOLDER,
5888 				     escaped_path,
5889 				     NULL);
5890 	g_free (escaped_path);
5891 
5892 	file_list = caja_directory_get_file_list (directory);
5893 	filtered = caja_file_list_filter_hidden (file_list, FALSE);
5894 	caja_file_list_free (file_list);
5895 
5896 	file_list = caja_file_list_sort_by_display_name (filtered);
5897 
5898 	num = 0;
5899 	any_templates = FALSE;
5900 	for (node = file_list; num < TEMPLATE_LIMIT && node != NULL; node = node->next, num++) {
5901 		file = node->data;
5902 
5903 		if (caja_file_is_directory (file)) {
5904 			uri = caja_file_get_uri (file);
5905 			if (directory_belongs_in_templates_menu (templates_directory_uri, uri)) {
5906 				dir = caja_directory_get_by_uri (uri);
5907 				add_directory_to_templates_directory_list (view, dir);
5908 				caja_directory_unref (dir);
5909 
5910 				add_submenu_to_directory_menus (view,
5911 								view->details->templates_action_group,
5912 								view->details->templates_merge_id,
5913 								file, menu_path, NULL, popup_bg_path);
5914 
5915 				any_templates = TRUE;
5916 			}
5917 			g_free (uri);
5918 		} else if (caja_file_can_read (file)) {
5919 			add_template_to_templates_menus (view, file, menu_path, popup_bg_path);
5920 			any_templates = TRUE;
5921 		}
5922 	}
5923 
5924 	caja_file_list_free (file_list);
5925 
5926 	g_free (popup_bg_path);
5927 	g_free (menu_path);
5928 
5929 	return any_templates;
5930 }
5931 
5932 
5933 
5934 static void
update_templates_menu(FMDirectoryView * view)5935 update_templates_menu (FMDirectoryView *view)
5936 {
5937 	gboolean any_templates;
5938 	GList *sorted_copy, *node;
5939 	GtkUIManager *ui_manager;
5940 	GtkAction *action;
5941 	char *templates_directory_uri;
5942 	CajaDirectory *directory = NULL;
5943 
5944 	if (caja_should_use_templates_directory ()) {
5945 		templates_directory_uri = caja_get_templates_directory_uri ();
5946 	} else {
5947 		templates_directory_uri = NULL;
5948 	}
5949 
5950 	/* There is a race condition here.  If we don't mark the scripts menu as
5951 	   valid before we begin our task then we can lose template menu updates that
5952 	   occur before we finish. */
5953 	view->details->templates_invalid = FALSE;
5954 
5955 	ui_manager = caja_window_info_get_ui_manager (view->details->window);
5956 	caja_ui_unmerge_ui (ui_manager,
5957 				&view->details->templates_merge_id,
5958 				&view->details->templates_action_group);
5959 
5960 	caja_ui_prepare_merge_ui (ui_manager,
5961 				      "TemplatesGroup",
5962 				      &view->details->templates_merge_id,
5963 				      &view->details->templates_action_group);
5964 
5965 	/* As we walk through the directories, remove any that no longer belong. */
5966 	any_templates = FALSE;
5967 	sorted_copy = caja_directory_list_sort_by_uri
5968 		(caja_directory_list_copy (view->details->templates_directory_list));
5969 	for (node = sorted_copy; node != NULL; node = node->next) {
5970 		char *uri;
5971 
5972 		directory = node->data;
5973 
5974 		uri = caja_directory_get_uri (directory);
5975 		if (!directory_belongs_in_templates_menu (templates_directory_uri, uri)) {
5976 			remove_directory_from_templates_directory_list (view, directory);
5977 		} else if (update_directory_in_templates_menu (view,
5978 							       templates_directory_uri,
5979 							       directory)) {
5980 			any_templates = TRUE;
5981 		}
5982 		g_free (uri);
5983 	}
5984 	caja_directory_list_free (sorted_copy);
5985 
5986 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
5987 	action = gtk_action_group_get_action (view->details->dir_action_group, FM_ACTION_NO_TEMPLATES);
5988 	gtk_action_set_visible (action, !any_templates);
5989 	G_GNUC_END_IGNORE_DEPRECATIONS;
5990 
5991 	g_free (templates_directory_uri);
5992 }
5993 
5994 
5995 static void
action_open_scripts_folder_callback(GtkAction * action,gpointer callback_data)5996 action_open_scripts_folder_callback (GtkAction *action,
5997 				     gpointer callback_data)
5998 {
5999 	FMDirectoryView *view;
6000 
6001 	view = FM_DIRECTORY_VIEW (callback_data);
6002 
6003 	open_location (view, scripts_directory_uri, CAJA_WINDOW_OPEN_ACCORDING_TO_MODE, 0);
6004 
6005 	eel_show_info_dialog_with_details
6006 		(_("All executable files in this folder will appear in the "
6007 		   "Scripts menu."),
6008 		 _("Choosing a script from the menu will run "
6009 		   "that script with any selected items as input."),
6010 		 _("All executable files in this folder will appear in the "
6011 		   "Scripts menu. Choosing a script from the menu will run "
6012 		   "that script.\n\n"
6013 		   "When executed from a local folder, scripts will be passed "
6014 		   "the selected file names. When executed from a remote folder "
6015 		   "(e.g. a folder showing web or ftp content), scripts will "
6016 		   "be passed no parameters.\n\n"
6017 		   "In all cases, the following environment variables will be "
6018 		   "set by Caja, which the scripts may use:\n\n"
6019 		   "CAJA_SCRIPT_SELECTED_FILE_PATHS: newline-delimited paths for selected files (only if local)\n\n"
6020 		   "CAJA_SCRIPT_SELECTED_URIS: newline-delimited URIs for selected files\n\n"
6021 		   "CAJA_SCRIPT_CURRENT_URI: URI for current location\n\n"
6022 		   "CAJA_SCRIPT_WINDOW_GEOMETRY: position and size of current window\n\n"
6023 		   "CAJA_SCRIPT_NEXT_PANE_SELECTED_FILE_PATHS: newline-delimited paths for selected files in the inactive pane of a split-view window (only if local)\n\n"
6024 		   "CAJA_SCRIPT_NEXT_PANE_SELECTED_URIS: newline-delimited URIs for selected files in the inactive pane of a split-view window\n\n"
6025 		   "CAJA_SCRIPT_NEXT_PANE_CURRENT_URI: URI for current location in the inactive pane of a split-view window"),
6026 		 fm_directory_view_get_containing_window (view));
6027 }
6028 
6029 static GtkMenu *
create_popup_menu(FMDirectoryView * view,const char * popup_path)6030 create_popup_menu (FMDirectoryView *view, const char *popup_path)
6031 {
6032 	GtkWidget *menu;
6033 
6034 	menu = gtk_ui_manager_get_widget (caja_window_info_get_ui_manager (view->details->window),
6035 					  popup_path);
6036 	gtk_menu_set_screen (GTK_MENU (menu),
6037 			     gtk_widget_get_screen (GTK_WIDGET (view)));
6038 	gtk_widget_show (GTK_WIDGET (menu));
6039 
6040 	return GTK_MENU (menu);
6041 }
6042 
6043 static void
copy_or_cut_files(FMDirectoryView * view,GList * clipboard_contents,gboolean cut)6044 copy_or_cut_files (FMDirectoryView *view,
6045 		   GList           *clipboard_contents,
6046 		   gboolean         cut)
6047 {
6048 	int count;
6049 	char *status_string;
6050 	CajaClipboardInfo info;
6051         GtkTargetList *target_list;
6052         GtkTargetEntry *targets;
6053         int n_targets;
6054 
6055 	info.files = clipboard_contents;
6056 	info.cut = cut;
6057 
6058         target_list = gtk_target_list_new (NULL, 0);
6059         gtk_target_list_add (target_list, copied_files_atom, 0, 0);
6060         gtk_target_list_add_uri_targets (target_list, 0);
6061         gtk_target_list_add_text_targets (target_list, 0);
6062 
6063         targets = gtk_target_table_new_from_list (target_list, &n_targets);
6064         gtk_target_list_unref (target_list);
6065 
6066 	gtk_clipboard_set_with_data (caja_clipboard_get (GTK_WIDGET (view)),
6067 				     targets, n_targets,
6068 				     caja_get_clipboard_callback, caja_clear_clipboard_callback,
6069 				     NULL);
6070         gtk_target_table_free (targets, n_targets);
6071 
6072 	caja_clipboard_monitor_set_clipboard_info (caja_clipboard_monitor_get (), &info);
6073 
6074 	count = g_list_length (clipboard_contents);
6075 	if (count == 1) {
6076 		char *name;
6077 
6078 		name = caja_file_get_display_name (clipboard_contents->data);
6079 		if (cut) {
6080 			status_string = g_strdup_printf (_("\"%s\" will be moved "
6081 							   "if you select the Paste command"),
6082 							 name);
6083 		} else {
6084 			status_string = g_strdup_printf (_("\"%s\" will be copied "
6085 							   "if you select the Paste command"),
6086 							 name);
6087 		}
6088 		g_free (name);
6089 	} else {
6090 		if (cut) {
6091 			status_string = g_strdup_printf (ngettext("The %'d selected item will be moved "
6092 								  "if you select the Paste command",
6093 								  "The %'d selected items will be moved "
6094 								  "if you select the Paste command",
6095 								  count),
6096 							 count);
6097 		} else {
6098 			status_string = g_strdup_printf (ngettext("The %'d selected item will be copied "
6099 								  "if you select the Paste command",
6100 								  "The %'d selected items will be copied "
6101 								  "if you select the Paste command",
6102 								  count),
6103 							 count);
6104 		}
6105 	}
6106 
6107 	caja_window_slot_info_set_status (view->details->slot,
6108 					      status_string);
6109 	g_free (status_string);
6110 }
6111 
6112 static void
action_copy_files_callback(GtkAction * action,gpointer callback_data)6113 action_copy_files_callback (GtkAction *action,
6114 			    gpointer callback_data)
6115 {
6116 	FMDirectoryView *view;
6117 	GList *selection;
6118 
6119 	view = FM_DIRECTORY_VIEW (callback_data);
6120 
6121 	selection = fm_directory_view_get_selection_for_file_transfer (view);
6122 	copy_or_cut_files (view, selection, FALSE);
6123 	caja_file_list_free (selection);
6124 }
6125 
6126 static void
move_copy_selection_to_location(FMDirectoryView * view,int copy_action,char * target_uri)6127 move_copy_selection_to_location (FMDirectoryView *view,
6128 				 int copy_action,
6129 				 char *target_uri)
6130 {
6131 	GList *selection, *uris, *l;
6132 
6133 	selection = fm_directory_view_get_selection_for_file_transfer (view);
6134 	if (selection == NULL) {
6135 		return;
6136 	}
6137 
6138 	uris = NULL;
6139 	for (l = selection; l != NULL; l = l->next) {
6140 		uris = g_list_prepend (uris,
6141 				       caja_file_get_uri ((CajaFile *) l->data));
6142 	}
6143 	uris = g_list_reverse (uris);
6144 
6145 	fm_directory_view_move_copy_items (uris, NULL, target_uri,
6146 					   copy_action,
6147 					   0, 0,
6148 					   view);
6149 
6150 	g_list_free_full (uris, g_free);
6151 	caja_file_list_free (selection);
6152 }
6153 
6154 static void
move_copy_selection_to_next_pane(FMDirectoryView * view,int copy_action)6155 move_copy_selection_to_next_pane (FMDirectoryView *view,
6156 				  int copy_action)
6157 {
6158 	CajaWindowSlotInfo *slot;
6159 	char *dest_location;
6160 
6161 	slot = caja_window_info_get_extra_slot (fm_directory_view_get_caja_window (view));
6162 	g_return_if_fail (slot != NULL);
6163 
6164 	dest_location = caja_window_slot_info_get_current_location (slot);
6165 	g_return_if_fail (dest_location != NULL);
6166 
6167 	move_copy_selection_to_location (view, copy_action, dest_location);
6168 }
6169 
6170 static void
action_copy_to_next_pane_callback(GtkAction * action,gpointer callback_data)6171 action_copy_to_next_pane_callback (GtkAction *action, gpointer callback_data)
6172 {
6173 	FMDirectoryView *view;
6174 
6175 	view = FM_DIRECTORY_VIEW (callback_data);
6176 	move_copy_selection_to_next_pane (view,
6177 					  GDK_ACTION_COPY);
6178 }
6179 
6180 static void
action_move_to_next_pane_callback(GtkAction * action,gpointer callback_data)6181 action_move_to_next_pane_callback (GtkAction *action, gpointer callback_data)
6182 {
6183 	CajaWindowSlotInfo *slot;
6184 	char *dest_location;
6185 	FMDirectoryView *view;
6186 
6187 	view = FM_DIRECTORY_VIEW (callback_data);
6188 
6189 	slot = caja_window_info_get_extra_slot (fm_directory_view_get_caja_window (view));
6190 	g_return_if_fail (slot != NULL);
6191 
6192 	dest_location = caja_window_slot_info_get_current_location (slot);
6193 	g_return_if_fail (dest_location != NULL);
6194 
6195 	move_copy_selection_to_location (view, GDK_ACTION_MOVE, dest_location);
6196 }
6197 
6198 static void
action_copy_to_home_callback(GtkAction * action,gpointer callback_data)6199 action_copy_to_home_callback (GtkAction *action, gpointer callback_data)
6200 {
6201 	FMDirectoryView *view;
6202 	char *dest_location;
6203 
6204 	view = FM_DIRECTORY_VIEW (callback_data);
6205 
6206 	dest_location = caja_get_home_directory_uri ();
6207 	move_copy_selection_to_location (view, GDK_ACTION_COPY, dest_location);
6208 	g_free (dest_location);
6209 }
6210 
6211 static void
action_move_to_home_callback(GtkAction * action,gpointer callback_data)6212 action_move_to_home_callback (GtkAction *action, gpointer callback_data)
6213 {
6214 	FMDirectoryView *view;
6215 	char *dest_location;
6216 
6217 	view = FM_DIRECTORY_VIEW (callback_data);
6218 
6219 	dest_location = caja_get_home_directory_uri ();
6220 	move_copy_selection_to_location (view, GDK_ACTION_MOVE, dest_location);
6221 	g_free (dest_location);
6222 }
6223 
6224 static void
action_copy_to_desktop_callback(GtkAction * action,gpointer callback_data)6225 action_copy_to_desktop_callback (GtkAction *action, gpointer callback_data)
6226 {
6227 	FMDirectoryView *view;
6228 	char *dest_location;
6229 
6230 	view = FM_DIRECTORY_VIEW (callback_data);
6231 
6232 	dest_location = caja_get_desktop_directory_uri ();
6233 	move_copy_selection_to_location (view, GDK_ACTION_COPY, dest_location);
6234 	g_free (dest_location);
6235 }
6236 
6237 static void
action_move_to_desktop_callback(GtkAction * action,gpointer callback_data)6238 action_move_to_desktop_callback (GtkAction *action, gpointer callback_data)
6239 {
6240 	FMDirectoryView *view;
6241 	char *dest_location;
6242 
6243 	view = FM_DIRECTORY_VIEW (callback_data);
6244 
6245 	dest_location = caja_get_desktop_directory_uri ();
6246 	move_copy_selection_to_location (view, GDK_ACTION_MOVE, dest_location);
6247 	g_free (dest_location);
6248 }
6249 
6250 static void
action_cut_files_callback(GtkAction * action,gpointer callback_data)6251 action_cut_files_callback (GtkAction *action,
6252 			   gpointer callback_data)
6253 {
6254 	FMDirectoryView *view;
6255 	GList *selection;
6256 
6257 	view = FM_DIRECTORY_VIEW (callback_data);
6258 
6259 	selection = fm_directory_view_get_selection_for_file_transfer (view);
6260 	copy_or_cut_files (view, selection, TRUE);
6261 	caja_file_list_free (selection);
6262 }
6263 
6264 static void
paste_clipboard_data(FMDirectoryView * view,GtkSelectionData * selection_data,char * destination_uri)6265 paste_clipboard_data (FMDirectoryView *view,
6266 		      GtkSelectionData *selection_data,
6267 		      char *destination_uri)
6268 {
6269 	gboolean cut;
6270 	GList *item_uris;
6271 
6272 	cut = FALSE;
6273 	item_uris = caja_clipboard_get_uri_list_from_selection_data (selection_data, &cut,
6274 									 copied_files_atom);
6275 
6276 	if (item_uris == NULL|| destination_uri == NULL) {
6277 		caja_window_slot_info_set_status (view->details->slot,
6278 						      _("There is nothing on the clipboard to paste."));
6279 	} else {
6280 		fm_directory_view_move_copy_items (item_uris, NULL, destination_uri,
6281 						   cut ? GDK_ACTION_MOVE : GDK_ACTION_COPY,
6282 						   0, 0,
6283 						   view);
6284 
6285 		/* If items are cut then remove from clipboard */
6286 		if (cut) {
6287 			gtk_clipboard_clear (caja_clipboard_get (GTK_WIDGET (view)));
6288 		}
6289 
6290     		g_list_free_full (item_uris, g_free);
6291 	}
6292 }
6293 
6294 static void
paste_clipboard_received_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer data)6295 paste_clipboard_received_callback (GtkClipboard     *clipboard,
6296 				   GtkSelectionData *selection_data,
6297 				   gpointer          data)
6298 {
6299 	FMDirectoryView *view;
6300 	char *view_uri;
6301 
6302 	view = FM_DIRECTORY_VIEW (data);
6303 
6304 	view_uri = fm_directory_view_get_backing_uri (view);
6305 
6306 	if (view->details->window != NULL) {
6307 		paste_clipboard_data (view, selection_data, view_uri);
6308 	}
6309 
6310 	g_free (view_uri);
6311 
6312 	g_object_unref (view);
6313 }
6314 
6315 typedef struct {
6316 	FMDirectoryView *view;
6317 	CajaFile *target;
6318 } PasteIntoData;
6319 
6320 static void
paste_into_clipboard_received_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,gpointer callback_data)6321 paste_into_clipboard_received_callback (GtkClipboard     *clipboard,
6322 					GtkSelectionData *selection_data,
6323 					gpointer          callback_data)
6324 {
6325 	PasteIntoData *data;
6326 	FMDirectoryView *view;
6327 
6328 	data = (PasteIntoData *) callback_data;
6329 
6330 	view = FM_DIRECTORY_VIEW (data->view);
6331 
6332 	if (view->details->window != NULL) {
6333 		char *directory_uri;
6334 
6335 		directory_uri = caja_file_get_activation_uri (data->target);
6336 
6337 		paste_clipboard_data (view, selection_data, directory_uri);
6338 
6339 		g_free (directory_uri);
6340 	}
6341 
6342 	g_object_unref (view);
6343 	caja_file_unref (data->target);
6344 	g_free (data);
6345 }
6346 
6347 static void
action_paste_files_callback(GtkAction * action,gpointer callback_data)6348 action_paste_files_callback (GtkAction *action,
6349 			     gpointer callback_data)
6350 {
6351 	FMDirectoryView *view;
6352 
6353 	view = FM_DIRECTORY_VIEW (callback_data);
6354 
6355 	g_object_ref (view);
6356 	gtk_clipboard_request_contents (caja_clipboard_get (GTK_WIDGET (view)),
6357 					copied_files_atom,
6358 					paste_clipboard_received_callback,
6359 					view);
6360 }
6361 
6362 static void
paste_into(FMDirectoryView * view,CajaFile * target)6363 paste_into (FMDirectoryView *view,
6364 	    CajaFile *target)
6365 {
6366 	PasteIntoData *data;
6367 
6368 	g_assert (FM_IS_DIRECTORY_VIEW (view));
6369 	g_assert (CAJA_IS_FILE (target));
6370 
6371 	data = g_new (PasteIntoData, 1);
6372 
6373 	data->view = g_object_ref (view);
6374 	data->target = caja_file_ref (target);
6375 
6376 	gtk_clipboard_request_contents (caja_clipboard_get (GTK_WIDGET (view)),
6377 					copied_files_atom,
6378 					paste_into_clipboard_received_callback,
6379 					data);
6380 }
6381 
6382 static void
action_paste_files_into_callback(GtkAction * action,gpointer callback_data)6383 action_paste_files_into_callback (GtkAction *action,
6384 				  gpointer callback_data)
6385 {
6386 	FMDirectoryView *view;
6387 	GList *selection;
6388 
6389 	view = FM_DIRECTORY_VIEW (callback_data);
6390 	selection = fm_directory_view_get_selection (view);
6391 	if (selection != NULL) {
6392 		paste_into (view, CAJA_FILE (selection->data));
6393 		caja_file_list_free (selection);
6394 	}
6395 
6396 }
6397 
6398 static void
real_action_undo(FMDirectoryView * view)6399 real_action_undo (FMDirectoryView *view)
6400 {
6401 	CajaUndoStackManager *manager = caja_undostack_manager_instance ();
6402 
6403 	/* Disable menus because they are in an untrustworthy status */
6404 	view->details->undo_active = FALSE;
6405 	view->details->redo_active = FALSE;
6406 	fm_directory_view_update_menus (view);
6407 
6408 	caja_undostack_manager_undo (manager, GTK_WIDGET (view), finish_undoredo_callback);
6409 }
6410 
6411 static void
real_action_redo(FMDirectoryView * view)6412 real_action_redo (FMDirectoryView *view)
6413 {
6414 	CajaUndoStackManager *manager = caja_undostack_manager_instance ();
6415 
6416 	/* Disable menus because they are in an untrustworthy status */
6417 	view->details->undo_active = FALSE;
6418 	view->details->redo_active = FALSE;
6419 	fm_directory_view_update_menus (view);
6420 
6421 	caja_undostack_manager_redo (manager, GTK_WIDGET (view), finish_undoredo_callback);
6422 }
6423 
6424 static void
real_action_rename(FMDirectoryView * view,gboolean select_all)6425 real_action_rename (FMDirectoryView *view,
6426 		    gboolean select_all)
6427 {
6428 	GList *selection;
6429 
6430 	g_assert (FM_IS_DIRECTORY_VIEW (view));
6431 
6432 	selection = fm_directory_view_get_selection (view);
6433 
6434 	if (selection_not_empty_in_menu_callback (view, selection)) {
6435 		CajaFile *file;
6436 
6437 		file = CAJA_FILE (selection->data);
6438 
6439 		if (!select_all) {
6440 			/* directories don't have a file extension, so
6441 			 * they are always pre-selected as a whole */
6442 			select_all = caja_file_is_directory (file);
6443 		}
6444 		EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view, start_renaming_file, (view, file, select_all));
6445 	}
6446 
6447 	caja_file_list_free (selection);
6448 }
6449 
6450 static void
action_rename_callback(GtkAction * action,gpointer callback_data)6451 action_rename_callback (GtkAction *action,
6452 			gpointer callback_data)
6453 {
6454 	real_action_rename (FM_DIRECTORY_VIEW (callback_data), FALSE);
6455 }
6456 
6457 static void
action_rename_select_all_callback(GtkAction * action,gpointer callback_data)6458 action_rename_select_all_callback (GtkAction *action,
6459 				   gpointer callback_data)
6460 {
6461 	real_action_rename (FM_DIRECTORY_VIEW (callback_data), TRUE);
6462 }
6463 
6464 static void
file_mount_callback(CajaFile * file,GFile * result_location,GError * error,gpointer callback_data)6465 file_mount_callback (CajaFile  *file,
6466 		     GFile         *result_location,
6467 		     GError        *error,
6468 		     gpointer       callback_data)
6469 {
6470 	if (error != NULL &&
6471 	    (error->domain != G_IO_ERROR ||
6472 	     (error->code != G_IO_ERROR_CANCELLED &&
6473 	      error->code != G_IO_ERROR_FAILED_HANDLED &&
6474 	      error->code != G_IO_ERROR_ALREADY_MOUNTED))) {
6475 		eel_show_error_dialog (_("Unable to mount location"),
6476 				       error->message, NULL);
6477 	}
6478 }
6479 
6480 static void
file_unmount_callback(CajaFile * file,GFile * result_location,GError * error,gpointer callback_data)6481 file_unmount_callback (CajaFile  *file,
6482 		       GFile         *result_location,
6483 		       GError        *error,
6484 		       gpointer       callback_data)
6485 {
6486 	FMDirectoryView *view;
6487 
6488 	view = FM_DIRECTORY_VIEW (callback_data);
6489 	fm_directory_view_set_initiated_unmount (view, FALSE);
6490 	g_object_unref (view);
6491 
6492 	if (error != NULL &&
6493 	    (error->domain != G_IO_ERROR ||
6494 	     (error->code != G_IO_ERROR_CANCELLED &&
6495 	      error->code != G_IO_ERROR_FAILED_HANDLED))) {
6496 		eel_show_error_dialog (_("Unable to unmount location"),
6497 				       error->message, NULL);
6498 	}
6499 }
6500 
6501 static void
file_eject_callback(CajaFile * file,GFile * result_location,GError * error,gpointer callback_data)6502 file_eject_callback (CajaFile  *file,
6503 		     GFile         *result_location,
6504 		     GError        *error,
6505 		     gpointer       callback_data)
6506 {
6507 	FMDirectoryView *view;
6508 
6509 	view = FM_DIRECTORY_VIEW (callback_data);
6510 	fm_directory_view_set_initiated_unmount (view, FALSE);
6511 	g_object_unref (view);
6512 
6513 	if (error != NULL &&
6514 	    (error->domain != G_IO_ERROR ||
6515 	     (error->code != G_IO_ERROR_CANCELLED &&
6516 	      error->code != G_IO_ERROR_FAILED_HANDLED))) {
6517 		eel_show_error_dialog (_("Unable to eject location"),
6518 				       error->message, NULL);
6519 	}
6520 }
6521 
6522 static void
file_stop_callback(CajaFile * file,GFile * result_location,GError * error,gpointer callback_data)6523 file_stop_callback (CajaFile  *file,
6524 		    GFile         *result_location,
6525 		    GError        *error,
6526 		    gpointer       callback_data)
6527 {
6528 	if (error != NULL &&
6529 	    (error->domain != G_IO_ERROR ||
6530 	     (error->code != G_IO_ERROR_CANCELLED &&
6531 	      error->code != G_IO_ERROR_FAILED_HANDLED))) {
6532 		eel_show_error_dialog (_("Unable to stop drive"),
6533 				       error->message, NULL);
6534 	}
6535 }
6536 
6537 static void
action_mount_volume_callback(GtkAction * action,gpointer data)6538 action_mount_volume_callback (GtkAction *action,
6539 			      gpointer data)
6540 {
6541 	GList *selection, *l;
6542 	FMDirectoryView *view;
6543 	GMountOperation *mount_op;
6544 	CajaFile *file = NULL;
6545 
6546         view = FM_DIRECTORY_VIEW (data);
6547 
6548 	selection = fm_directory_view_get_selection (view);
6549 	for (l = selection; l != NULL; l = l->next) {
6550 		file = CAJA_FILE (l->data);
6551 
6552 		if (caja_file_can_mount (file)) {
6553 			mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6554 			g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
6555 			caja_file_mount (file, mount_op, NULL,
6556 					     file_mount_callback, NULL);
6557 			g_object_unref (mount_op);
6558 		}
6559 	}
6560 	caja_file_list_free (selection);
6561 }
6562 
6563 static void
action_unmount_volume_callback(GtkAction * action,gpointer data)6564 action_unmount_volume_callback (GtkAction *action,
6565 				gpointer data)
6566 {
6567 	GList *selection, *l;
6568 	FMDirectoryView *view;
6569 	CajaFile *file = NULL;
6570 
6571         view = FM_DIRECTORY_VIEW (data);
6572 
6573 	selection = fm_directory_view_get_selection (view);
6574 
6575 	for (l = selection; l != NULL; l = l->next) {
6576 		file = CAJA_FILE (l->data);
6577 		if (caja_file_can_unmount (file)) {
6578 			GMountOperation *mount_op;
6579 			mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6580 			fm_directory_view_set_initiated_unmount (view, TRUE);
6581 			caja_file_unmount (file, mount_op, NULL,
6582 					       file_unmount_callback, g_object_ref (view));
6583 			g_object_unref (mount_op);
6584 		}
6585 	}
6586 	caja_file_list_free (selection);
6587 }
6588 
6589 static void
action_format_volume_callback(GtkAction * action,gpointer data)6590 action_format_volume_callback (GtkAction *action,
6591 			       gpointer   data)
6592 {
6593 #ifdef TODO_GIO
6594 	GList *selection, *l;
6595 	FMDirectoryView *view;
6596 	CajaFile *file = NULL;
6597 
6598         view = FM_DIRECTORY_VIEW (data);
6599 
6600 	selection = fm_directory_view_get_selection (view);
6601 	for (l = selection; l != NULL; l = l->next) {
6602 		file = CAJA_FILE (l->data);
6603 
6604 		if (something) {
6605 			g_spawn_command_line_async ("gfloppy", NULL);
6606 		}
6607 	}
6608 	caja_file_list_free (selection);
6609 #endif
6610 }
6611 
6612 static void
action_eject_volume_callback(GtkAction * action,gpointer data)6613 action_eject_volume_callback (GtkAction *action,
6614 			      gpointer data)
6615 {
6616 	GList *selection, *l;
6617 	FMDirectoryView *view;
6618 	CajaFile *file = NULL;
6619 
6620         view = FM_DIRECTORY_VIEW (data);
6621 
6622 	selection = fm_directory_view_get_selection (view);
6623 	for (l = selection; l != NULL; l = l->next) {
6624 		file = CAJA_FILE (l->data);
6625 
6626 		if (caja_file_can_eject (file)) {
6627 			GMountOperation *mount_op;
6628 			mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6629 			fm_directory_view_set_initiated_unmount (view, TRUE);
6630 			caja_file_eject (file, mount_op, NULL,
6631 					     file_eject_callback, g_object_ref (view));
6632 			g_object_unref (mount_op);
6633 		}
6634 	}
6635 	caja_file_list_free (selection);
6636 }
6637 
6638 static void
file_start_callback(CajaFile * file,GFile * result_location,GError * error,gpointer callback_data)6639 file_start_callback (CajaFile  *file,
6640 		     GFile         *result_location,
6641 		     GError        *error,
6642 		     gpointer       callback_data)
6643 {
6644 	if (error != NULL &&
6645 	    (error->domain != G_IO_ERROR ||
6646 	     (error->code != G_IO_ERROR_CANCELLED &&
6647 	      error->code != G_IO_ERROR_FAILED_HANDLED &&
6648 	      error->code != G_IO_ERROR_ALREADY_MOUNTED))) {
6649 		eel_show_error_dialog (_("Unable to start location"),
6650 				       error->message, NULL);
6651 	}
6652 }
6653 
6654 static void
action_start_volume_callback(GtkAction * action,gpointer data)6655 action_start_volume_callback (GtkAction *action,
6656 			      gpointer   data)
6657 {
6658 	GList *selection, *l;
6659 	FMDirectoryView *view;
6660 	GMountOperation *mount_op;
6661 	CajaFile *file = NULL;
6662 
6663         view = FM_DIRECTORY_VIEW (data);
6664 
6665 	selection = fm_directory_view_get_selection (view);
6666 	for (l = selection; l != NULL; l = l->next) {
6667 		file = CAJA_FILE (l->data);
6668 
6669 		if (caja_file_can_start (file) || caja_file_can_start_degraded (file)) {
6670 			mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6671 			caja_file_start (file, mount_op, NULL,
6672 					     file_start_callback, NULL);
6673 			g_object_unref (mount_op);
6674 		}
6675 	}
6676 	caja_file_list_free (selection);
6677 }
6678 
6679 static void
action_stop_volume_callback(GtkAction * action,gpointer data)6680 action_stop_volume_callback (GtkAction *action,
6681 			     gpointer   data)
6682 {
6683 	GList *selection, *l;
6684 	FMDirectoryView *view;
6685 	CajaFile *file = NULL;
6686 
6687         view = FM_DIRECTORY_VIEW (data);
6688 
6689 	selection = fm_directory_view_get_selection (view);
6690 	for (l = selection; l != NULL; l = l->next) {
6691 		file = CAJA_FILE (l->data);
6692 
6693 		if (caja_file_can_stop (file)) {
6694 			GMountOperation *mount_op;
6695 			mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6696 			caja_file_stop (file, mount_op, NULL,
6697 					    file_stop_callback, NULL);
6698 			g_object_unref (mount_op);
6699 		}
6700 	}
6701 	caja_file_list_free (selection);
6702 }
6703 
6704 static void
action_detect_media_callback(GtkAction * action,gpointer data)6705 action_detect_media_callback (GtkAction *action,
6706 			      gpointer   data)
6707 {
6708 	GList *selection, *l;
6709 	FMDirectoryView *view;
6710 	CajaFile *file = NULL;
6711 
6712         view = FM_DIRECTORY_VIEW (data);
6713 
6714 	selection = fm_directory_view_get_selection (view);
6715 	for (l = selection; l != NULL; l = l->next) {
6716 		file = CAJA_FILE (l->data);
6717 
6718 		if (caja_file_can_poll_for_media (file) && !caja_file_is_media_check_automatic (file)) {
6719 			caja_file_poll_for_media (file);
6720 		}
6721 	}
6722 	caja_file_list_free (selection);
6723 }
6724 
6725 static void
action_self_mount_volume_callback(GtkAction * action,gpointer data)6726 action_self_mount_volume_callback (GtkAction *action,
6727 				   gpointer data)
6728 {
6729 	CajaFile *file;
6730 	FMDirectoryView *view;
6731 	GMountOperation *mount_op;
6732 
6733 	view = FM_DIRECTORY_VIEW (data);
6734 
6735 	file = fm_directory_view_get_directory_as_file (view);
6736 	if (file == NULL) {
6737 		return;
6738 	}
6739 
6740 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6741 	g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
6742 	caja_file_mount (file, mount_op, NULL, file_mount_callback, NULL);
6743 	g_object_unref (mount_op);
6744 }
6745 
6746 static void
action_self_unmount_volume_callback(GtkAction * action,gpointer data)6747 action_self_unmount_volume_callback (GtkAction *action,
6748 				     gpointer data)
6749 {
6750 	CajaFile *file;
6751 	FMDirectoryView *view;
6752 	GMountOperation *mount_op;
6753 
6754 	view = FM_DIRECTORY_VIEW (data);
6755 
6756 	file = fm_directory_view_get_directory_as_file (view);
6757 	if (file == NULL) {
6758 		return;
6759 	}
6760 
6761 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6762 	fm_directory_view_set_initiated_unmount (view, TRUE);
6763 	caja_file_unmount (file, mount_op, NULL, file_unmount_callback, g_object_ref (view));
6764 	g_object_unref (mount_op);
6765 }
6766 
6767 static void
action_self_eject_volume_callback(GtkAction * action,gpointer data)6768 action_self_eject_volume_callback (GtkAction *action,
6769 				   gpointer data)
6770 {
6771 	CajaFile *file;
6772 	FMDirectoryView *view;
6773 	GMountOperation *mount_op;
6774 
6775 	view = FM_DIRECTORY_VIEW (data);
6776 
6777 	file = fm_directory_view_get_directory_as_file (view);
6778 	if (file == NULL) {
6779 		return;
6780 	}
6781 
6782 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6783 	fm_directory_view_set_initiated_unmount (view, TRUE);
6784 	caja_file_eject (file, mount_op, NULL, file_eject_callback, g_object_ref (view));
6785 	g_object_unref (mount_op);
6786 }
6787 
6788 static void
action_self_format_volume_callback(GtkAction * action,gpointer data)6789 action_self_format_volume_callback (GtkAction *action,
6790 				    gpointer   data)
6791 {
6792 	CajaFile *file;
6793 	FMDirectoryView *view;
6794 
6795 	view = FM_DIRECTORY_VIEW (data);
6796 
6797 	file = fm_directory_view_get_directory_as_file (view);
6798 	if (file == NULL) {
6799 		return;
6800 	}
6801 
6802 #ifdef TODO_GIO
6803 	if (something) {
6804 		g_spawn_command_line_async ("gfloppy", NULL);
6805 	}
6806 #endif
6807 }
6808 
6809 static void
action_self_start_volume_callback(GtkAction * action,gpointer data)6810 action_self_start_volume_callback (GtkAction *action,
6811 				   gpointer   data)
6812 {
6813 	CajaFile *file;
6814 	FMDirectoryView *view;
6815 	GMountOperation *mount_op;
6816 
6817 	view = FM_DIRECTORY_VIEW (data);
6818 
6819 	file = fm_directory_view_get_directory_as_file (view);
6820 	if (file == NULL) {
6821 		return;
6822 	}
6823 
6824 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6825 	caja_file_start (file, mount_op, NULL, file_start_callback, NULL);
6826 	g_object_unref (mount_op);
6827 }
6828 
6829 static void
action_self_stop_volume_callback(GtkAction * action,gpointer data)6830 action_self_stop_volume_callback (GtkAction *action,
6831 				  gpointer   data)
6832 {
6833 	CajaFile *file;
6834 	FMDirectoryView *view;
6835 	GMountOperation *mount_op;
6836 
6837 	view = FM_DIRECTORY_VIEW (data);
6838 
6839 	file = fm_directory_view_get_directory_as_file (view);
6840 	if (file == NULL) {
6841 		return;
6842 	}
6843 
6844 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6845 	caja_file_stop (file, mount_op, NULL,
6846 			    file_stop_callback, NULL);
6847 	g_object_unref (mount_op);
6848 }
6849 
6850 static void
action_self_detect_media_callback(GtkAction * action,gpointer data)6851 action_self_detect_media_callback (GtkAction *action,
6852 				   gpointer   data)
6853 {
6854 	CajaFile *file;
6855 	FMDirectoryView *view;
6856 
6857 	view = FM_DIRECTORY_VIEW (data);
6858 
6859 	file = fm_directory_view_get_directory_as_file (view);
6860 	if (file == NULL) {
6861 		return;
6862 	}
6863 
6864 	caja_file_poll_for_media (file);
6865 }
6866 
6867 static void
action_location_mount_volume_callback(GtkAction * action,gpointer data)6868 action_location_mount_volume_callback (GtkAction *action,
6869 				       gpointer data)
6870 {
6871 	CajaFile *file;
6872 	FMDirectoryView *view;
6873 	GMountOperation *mount_op;
6874 
6875 	view = FM_DIRECTORY_VIEW (data);
6876 
6877 	file = view->details->location_popup_directory_as_file;
6878 	if (file == NULL) {
6879 		return;
6880 	}
6881 
6882 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6883 	g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION);
6884 	caja_file_mount (file, mount_op, NULL, file_mount_callback, NULL);
6885 	g_object_unref (mount_op);
6886 }
6887 
6888 static void
action_location_unmount_volume_callback(GtkAction * action,gpointer data)6889 action_location_unmount_volume_callback (GtkAction *action,
6890 					 gpointer data)
6891 {
6892 	CajaFile *file;
6893 	FMDirectoryView *view;
6894 	GMountOperation *mount_op;
6895 
6896 	view = FM_DIRECTORY_VIEW (data);
6897 
6898 	file = view->details->location_popup_directory_as_file;
6899 	if (file == NULL) {
6900 		return;
6901 	}
6902 
6903 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6904 	fm_directory_view_set_initiated_unmount (view, TRUE);
6905 	caja_file_unmount (file, mount_op, NULL,
6906 			       file_unmount_callback, g_object_ref (view));
6907 	g_object_unref (mount_op);
6908 }
6909 
6910 static void
action_location_eject_volume_callback(GtkAction * action,gpointer data)6911 action_location_eject_volume_callback (GtkAction *action,
6912 				       gpointer data)
6913 {
6914 	CajaFile *file;
6915 	FMDirectoryView *view;
6916 	GMountOperation *mount_op;
6917 
6918 	view = FM_DIRECTORY_VIEW (data);
6919 
6920 	file = view->details->location_popup_directory_as_file;
6921 	if (file == NULL) {
6922 		return;
6923 	}
6924 
6925 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6926 	fm_directory_view_set_initiated_unmount (view, TRUE);
6927 	caja_file_eject (file, mount_op, NULL,
6928 			     file_eject_callback, g_object_ref (view));
6929 	g_object_unref (mount_op);
6930 }
6931 
6932 static void
action_location_format_volume_callback(GtkAction * action,gpointer data)6933 action_location_format_volume_callback (GtkAction *action,
6934 					gpointer   data)
6935 {
6936 	CajaFile *file;
6937 	FMDirectoryView *view;
6938 
6939 	view = FM_DIRECTORY_VIEW (data);
6940 
6941 	file = view->details->location_popup_directory_as_file;
6942 	if (file == NULL) {
6943 		return;
6944 	}
6945 
6946 #ifdef TODO_GIO
6947 	if (something) {
6948 		g_spawn_command_line_async ("gfloppy", NULL);
6949 	}
6950 #endif
6951 }
6952 
6953 static void
action_location_start_volume_callback(GtkAction * action,gpointer data)6954 action_location_start_volume_callback (GtkAction *action,
6955 				       gpointer   data)
6956 {
6957 	CajaFile *file;
6958 	FMDirectoryView *view;
6959 	GMountOperation *mount_op;
6960 
6961 	view = FM_DIRECTORY_VIEW (data);
6962 
6963 	file = view->details->location_popup_directory_as_file;
6964 	if (file == NULL) {
6965 		return;
6966 	}
6967 
6968 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6969 	caja_file_start (file, mount_op, NULL, file_start_callback, NULL);
6970 	g_object_unref (mount_op);
6971 }
6972 
6973 static void
action_location_stop_volume_callback(GtkAction * action,gpointer data)6974 action_location_stop_volume_callback (GtkAction *action,
6975 				      gpointer   data)
6976 {
6977 	CajaFile *file;
6978 	FMDirectoryView *view;
6979 	GMountOperation *mount_op;
6980 
6981 	view = FM_DIRECTORY_VIEW (data);
6982 
6983 	file = view->details->location_popup_directory_as_file;
6984 	if (file == NULL) {
6985 		return;
6986 	}
6987 
6988 	mount_op = gtk_mount_operation_new (fm_directory_view_get_containing_window (view));
6989 	caja_file_stop (file, mount_op, NULL,
6990 			    file_stop_callback, NULL);
6991 	g_object_unref (mount_op);
6992 }
6993 
6994 static void
action_location_detect_media_callback(GtkAction * action,gpointer data)6995 action_location_detect_media_callback (GtkAction *action,
6996 				       gpointer   data)
6997 {
6998 	CajaFile *file;
6999 	FMDirectoryView *view;
7000 
7001 	view = FM_DIRECTORY_VIEW (data);
7002 
7003 	file = view->details->location_popup_directory_as_file;
7004 	if (file == NULL) {
7005 		return;
7006 	}
7007 
7008 	caja_file_poll_for_media (file);
7009 }
7010 
7011 static void
connect_to_server_response_callback(GtkDialog * dialog,int response_id,gpointer data)7012 connect_to_server_response_callback (GtkDialog *dialog,
7013 				     int response_id,
7014 				     gpointer data)
7015 {
7016 #ifdef GIO_CONVERSION_DONE
7017 	GtkEntry *entry;
7018 	char *uri;
7019 	const char *name;
7020 	char *icon;
7021 
7022 	entry = GTK_ENTRY (data);
7023 
7024 	switch (response_id) {
7025 	case GTK_RESPONSE_OK:
7026 		uri = g_object_get_data (G_OBJECT (dialog), "link-uri");
7027 		icon = g_object_get_data (G_OBJECT (dialog), "link-icon");
7028 		name = gtk_entry_get_text (entry);
7029 		mate_vfs_connect_to_server (uri, (char *)name, icon);
7030 		gtk_widget_destroy (GTK_WIDGET (dialog));
7031 		break;
7032 	case GTK_RESPONSE_NONE:
7033 	case GTK_RESPONSE_DELETE_EVENT:
7034 	case GTK_RESPONSE_CANCEL:
7035 		gtk_widget_destroy (GTK_WIDGET (dialog));
7036 		break;
7037 	default :
7038 		g_assert_not_reached ();
7039 	}
7040 #endif
7041 	/* FIXME: the above code should make a server connection permanent */
7042 	gtk_widget_destroy (GTK_WIDGET (dialog));
7043 }
7044 
7045 static void
entry_activate_callback(GtkEntry * entry,gpointer user_data)7046 entry_activate_callback (GtkEntry *entry,
7047 			 gpointer user_data)
7048 {
7049 	GtkDialog *dialog;
7050 
7051 	dialog = GTK_DIALOG (user_data);
7052 	gtk_dialog_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
7053 }
7054 
7055 static void
action_connect_to_server_link_callback(GtkAction * action,gpointer data)7056 action_connect_to_server_link_callback (GtkAction *action,
7057 					gpointer data)
7058 {
7059 	CajaFile *file;
7060 	GList *selection;
7061 	FMDirectoryView *view;
7062 	char *uri;
7063 	CajaIconInfo *icon;
7064 	const char *icon_name;
7065 	char *name;
7066 	gint scale;
7067 
7068         view = FM_DIRECTORY_VIEW (data);
7069 
7070 	selection = fm_directory_view_get_selection (view);
7071 
7072 	if (!eel_g_list_exactly_one_item (selection)) {
7073 		caja_file_list_free (selection);
7074 		return;
7075 	}
7076 
7077 	file = CAJA_FILE (selection->data);
7078 	scale = gtk_widget_get_scale_factor (GTK_WIDGET (view));
7079 
7080 	uri = caja_file_get_activation_uri (file);
7081 	icon = caja_file_get_icon (file, CAJA_ICON_SIZE_STANDARD, scale, 0);
7082 	icon_name = caja_icon_info_get_used_name (icon);
7083 	name = caja_file_get_display_name (file);
7084 
7085 	if (uri != NULL) {
7086 		GtkWidget *dialog;
7087 		GtkWidget *label;
7088 		GtkWidget *entry;
7089 		GtkWidget *box;
7090 		char *title;
7091 
7092 		title = g_strdup_printf (_("Connect to Server %s"), name);
7093 
7094 		dialog = gtk_dialog_new ();
7095 		gtk_window_set_title (GTK_WINDOW (dialog), title);
7096 		gtk_window_set_transient_for (GTK_WINDOW (dialog), fm_directory_view_get_containing_window (view));
7097 
7098 		eel_dialog_add_button (GTK_DIALOG (dialog),
7099 				       _("_Cancel"),
7100 				       "process-stop",
7101 				       GTK_RESPONSE_CANCEL);
7102 
7103 		gtk_dialog_add_button (GTK_DIALOG (dialog),
7104 				       _("_Connect"),
7105 				       GTK_RESPONSE_OK);
7106 
7107 		g_object_set_data_full (G_OBJECT (dialog), "link-uri", g_strdup (uri), g_free);
7108 		g_object_set_data_full (G_OBJECT (dialog), "link-icon", g_strdup (icon_name), g_free);
7109 
7110 		gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
7111 		gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
7112 
7113 		box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
7114 		gtk_widget_show (box);
7115 		gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
7116 				    box, TRUE, TRUE, 0);
7117 
7118 		label = gtk_label_new_with_mnemonic (_("Link _name:"));
7119 		gtk_widget_show (label);
7120 
7121 		gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 12);
7122 
7123 		entry = gtk_entry_new ();
7124 		if (name) {
7125 			gtk_entry_set_text (GTK_ENTRY (entry), name);
7126 		}
7127 		g_signal_connect (entry,
7128 				  "activate",
7129 				  G_CALLBACK (entry_activate_callback),
7130 				  dialog);
7131 
7132 		gtk_widget_show (entry);
7133 		gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
7134 
7135 		gtk_box_pack_start (GTK_BOX (box), entry, TRUE, TRUE, 12);
7136 
7137 		gtk_dialog_set_default_response (GTK_DIALOG (dialog),
7138 						 GTK_RESPONSE_OK);
7139 		g_signal_connect (dialog, "response",
7140 				  G_CALLBACK (connect_to_server_response_callback),
7141 				  entry);
7142 		gtk_widget_show (dialog);
7143 	}
7144 
7145 	g_free (uri);
7146 	g_object_unref (icon);
7147 	g_free (name);
7148 }
7149 
7150 static void
action_location_open_alternate_callback(GtkAction * action,gpointer callback_data)7151 action_location_open_alternate_callback (GtkAction *action,
7152 					 gpointer   callback_data)
7153 {
7154 	FMDirectoryView *view;
7155 	CajaFile *file;
7156 
7157 	view = FM_DIRECTORY_VIEW (callback_data);
7158 
7159 	file = view->details->location_popup_directory_as_file;
7160 	if (file == NULL) {
7161 		return;
7162 	}
7163 
7164 	fm_directory_view_activate_file (view,
7165 					 file,
7166 					 CAJA_WINDOW_OPEN_IN_NAVIGATION,
7167 					 0);
7168 }
7169 
7170 static void
action_location_open_in_new_tab_callback(GtkAction * action,gpointer callback_data)7171 action_location_open_in_new_tab_callback (GtkAction *action,
7172 					  gpointer   callback_data)
7173 {
7174 	FMDirectoryView *view;
7175 	CajaFile *file;
7176 
7177 	view = FM_DIRECTORY_VIEW (callback_data);
7178 
7179 	file = view->details->location_popup_directory_as_file;
7180 	if (file == NULL) {
7181 		return;
7182 	}
7183 
7184 	fm_directory_view_activate_file (view,
7185 					 file,
7186 					 CAJA_WINDOW_OPEN_ACCORDING_TO_MODE,
7187 					 CAJA_WINDOW_OPEN_FLAG_NEW_TAB);
7188 }
7189 
7190 static void
action_location_open_folder_window_callback(GtkAction * action,gpointer callback_data)7191 action_location_open_folder_window_callback (GtkAction *action,
7192 					     gpointer   callback_data)
7193 {
7194 	FMDirectoryView *view;
7195 	CajaFile *file;
7196 
7197 	view = FM_DIRECTORY_VIEW (callback_data);
7198 
7199 	file = view->details->location_popup_directory_as_file;
7200 	g_return_if_fail (file != NULL);
7201 
7202 	fm_directory_view_activate_file (view,
7203 					 file,
7204 					 CAJA_WINDOW_OPEN_IN_SPATIAL,
7205 					 0);
7206 }
7207 
7208 static void
action_location_cut_callback(GtkAction * action,gpointer callback_data)7209 action_location_cut_callback (GtkAction *action,
7210 			      gpointer   callback_data)
7211 {
7212 	FMDirectoryView *view;
7213 	CajaFile *file;
7214 	GList *files;
7215 
7216 	view = FM_DIRECTORY_VIEW (callback_data);
7217 
7218 	file = view->details->location_popup_directory_as_file;
7219 	g_return_if_fail (file != NULL);
7220 
7221 	files = g_list_append (NULL, file);
7222 	copy_or_cut_files (view, files, TRUE);
7223 	g_list_free (files);
7224 }
7225 
7226 static void
action_location_copy_callback(GtkAction * action,gpointer callback_data)7227 action_location_copy_callback (GtkAction *action,
7228 			       gpointer   callback_data)
7229 {
7230 	FMDirectoryView *view;
7231 	CajaFile *file;
7232 	GList *files;
7233 
7234 	view = FM_DIRECTORY_VIEW (callback_data);
7235 
7236 	file = view->details->location_popup_directory_as_file;
7237 	g_return_if_fail (file != NULL);
7238 
7239 	files = g_list_append (NULL, file);
7240 	copy_or_cut_files (view, files, FALSE);
7241 	g_list_free (files);
7242 }
7243 
7244 static void
action_location_paste_files_into_callback(GtkAction * action,gpointer callback_data)7245 action_location_paste_files_into_callback (GtkAction *action,
7246 					   gpointer callback_data)
7247 {
7248 	FMDirectoryView *view;
7249 	CajaFile *file;
7250 
7251 	view = FM_DIRECTORY_VIEW (callback_data);
7252 
7253 	file = view->details->location_popup_directory_as_file;
7254 	g_return_if_fail (file != NULL);
7255 
7256 	paste_into (view, file);
7257 }
7258 
7259 static void
action_location_trash_callback(GtkAction * action,gpointer callback_data)7260 action_location_trash_callback (GtkAction *action,
7261 				gpointer   callback_data)
7262 {
7263 	FMDirectoryView *view;
7264 	CajaFile *file;
7265 	GList *files;
7266 
7267 	view = FM_DIRECTORY_VIEW (callback_data);
7268 
7269 	file = view->details->location_popup_directory_as_file;
7270 	g_return_if_fail (file != NULL);
7271 
7272 	files = g_list_append (NULL, file);
7273 	trash_or_delete_files (fm_directory_view_get_containing_window (view),
7274 			       files, TRUE,
7275 			       view);
7276 	g_list_free (files);
7277 }
7278 
7279 static void
action_location_delete_callback(GtkAction * action,gpointer callback_data)7280 action_location_delete_callback (GtkAction *action,
7281 				 gpointer   callback_data)
7282 {
7283 	FMDirectoryView *view;
7284 	CajaFile *file;
7285 	GFile *location;
7286 	GList *files;
7287 
7288 	view = FM_DIRECTORY_VIEW (callback_data);
7289 
7290 	file = view->details->location_popup_directory_as_file;
7291 	g_return_if_fail (file != NULL);
7292 
7293 	location = caja_file_get_location (file);
7294 
7295 	files = g_list_append (NULL, location);
7296 	caja_file_operations_delete (files, fm_directory_view_get_containing_window (view),
7297 					 NULL, NULL);
7298 
7299 	g_list_free_full (files, g_object_unref);
7300 }
7301 
7302 static void
action_location_restore_from_trash_callback(GtkAction * action,gpointer callback_data)7303 action_location_restore_from_trash_callback (GtkAction *action,
7304 					     gpointer callback_data)
7305 {
7306 	FMDirectoryView *view;
7307 	CajaFile *file;
7308 	GList l;
7309 
7310 	view = FM_DIRECTORY_VIEW (callback_data);
7311 	file = view->details->location_popup_directory_as_file;
7312 
7313 	l.prev = NULL;
7314 	l.next = NULL;
7315 	l.data = file;
7316 	caja_restore_files_from_trash (&l,
7317 					   fm_directory_view_get_containing_window (view));
7318 }
7319 
7320 static void
fm_directory_view_init_show_hidden_files(FMDirectoryView * view)7321 fm_directory_view_init_show_hidden_files (FMDirectoryView *view)
7322 {
7323 	CajaWindowShowHiddenFilesMode mode;
7324 	gboolean show_hidden_changed;
7325 	gboolean show_hidden_default_setting;
7326 
7327 	if (view->details->ignore_hidden_file_preferences) {
7328 		return;
7329 	}
7330 
7331 	show_hidden_changed = FALSE;
7332 	mode = caja_window_info_get_hidden_files_mode (view->details->window);
7333 
7334 	if (mode == CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT) {
7335 		show_hidden_default_setting = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_SHOW_HIDDEN_FILES);
7336 		if (show_hidden_default_setting != view->details->show_hidden_files) {
7337 			view->details->show_hidden_files = show_hidden_default_setting;
7338 			show_hidden_changed = TRUE;
7339 		}
7340 	} else {
7341 		if (mode == CAJA_WINDOW_SHOW_HIDDEN_FILES_ENABLE) {
7342 			show_hidden_changed = !view->details->show_hidden_files;
7343 			view->details->show_hidden_files = TRUE;
7344 		} else {
7345 			show_hidden_changed = view->details->show_hidden_files;
7346 			view->details->show_hidden_files = FALSE;
7347 		}
7348 	}
7349 
7350 	if (show_hidden_changed && (view->details->model != NULL)) {
7351 		load_directory (view, view->details->model);
7352 	}
7353 
7354 }
7355 
7356 static void
fm_directory_view_init_show_backup_files(FMDirectoryView * view)7357 fm_directory_view_init_show_backup_files (FMDirectoryView *view)
7358 {
7359 	CajaWindowShowBackupFilesMode mode;
7360 	gboolean show_backup_changed;
7361 	gboolean show_backup_default_setting;
7362 
7363 	show_backup_changed = FALSE;
7364 	mode = caja_window_info_get_backup_files_mode (view->details->window);
7365 
7366 	if (mode == CAJA_WINDOW_SHOW_BACKUP_FILES_DEFAULT) {
7367 		show_backup_default_setting = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_SHOW_BACKUP_FILES);
7368 		if (show_backup_default_setting != view->details->show_backup_files) {
7369 			view->details->show_backup_files = show_backup_default_setting;
7370 			show_backup_changed = TRUE;
7371 		}
7372 	} else {
7373 		if (mode == CAJA_WINDOW_SHOW_BACKUP_FILES_ENABLE) {
7374 			show_backup_changed = !view->details->show_backup_files;
7375 			view->details->show_backup_files = TRUE;
7376 		} else {
7377 			show_backup_changed = view->details->show_backup_files;
7378 			view->details->show_backup_files = FALSE;
7379 		}
7380 	}
7381 
7382 	if (show_backup_changed && (view->details->model != NULL)) {
7383 		load_directory (view, view->details->model);
7384 	}
7385 
7386 }
7387 
7388 static const GtkActionEntry directory_view_entries[] = {
7389   /* name, icon name, label */ { "New Documents", "document-new", N_("Create _Document"),
7390                                  NULL, NULL, NULL },
7391   /* name, icon name, label */ { "Open With", NULL, N_("Open Wit_h"),
7392                                  NULL, N_("Choose a program with which to open the selected item"), NULL },
7393   /* name, icon name */        { "Properties", "document-properties",
7394   /* label, accelerator */       N_("_Properties"), "<alt>Return",
7395   /* tooltip */                  N_("View or modify the properties of each selected item"),
7396                                  G_CALLBACK (action_properties_callback) },
7397   /* name, icon name */        { "PropertiesAccel", NULL,
7398   /* label, accelerator */       "PropertiesAccel", "<control>I",
7399   /* tooltip */                  NULL,
7400                                  G_CALLBACK (action_properties_callback) },
7401   /* name, icon name */        { "New Folder", "folder-new",
7402   /* label, accelerator */       N_("Create _Folder"), "<control><shift>N",
7403   /* tooltip */                  N_("Create a new empty folder inside this folder"),
7404                                  G_CALLBACK (action_new_folder_callback) },
7405   /* name, icon name, label */ { "No Templates", NULL, N_("No templates installed"), NULL, NULL, NULL },
7406   /* name, icon name */        { "New Empty File", NULL,
7407   /* Translators: this is used to indicate that a file doesn't contain anything */
7408   /* label, accelerator */       N_("_Empty File"), NULL,
7409   /* tooltip */                  N_("Create a new empty file inside this folder"),
7410                                  G_CALLBACK (action_new_empty_file_callback) },
7411   /* name, icon name */        { "New Launcher", NULL,
7412   /* label, accelerator */       N_("Create L_auncher..."), NULL,
7413   /* tooltip */                  N_("Create a new launcher"),
7414                                  G_CALLBACK (action_new_launcher_callback) },
7415   /* name, icon name */        { "Open", NULL,
7416   /* label, accelerator */       N_("_Open"), "<control>o",
7417   /* tooltip */                  N_("Open the selected item in this window"),
7418                                  G_CALLBACK (action_open_callback) },
7419   /* name, icon name */        { "OpenAccel", NULL,
7420   /* label, accelerator */       "OpenAccel", "<alt>Down",
7421   /* tooltip */                  NULL,
7422                                  G_CALLBACK (action_open_callback) },
7423   /* name, icon name */        { "OpenAlternate", NULL,
7424   /* label, accelerator */       N_("Open in Navigation Window"), "<control><shift>w",
7425   /* tooltip */                  N_("Open each selected item in a navigation window"),
7426                                  G_CALLBACK (action_open_alternate_callback) },
7427   /* name, icon name */        { "OpenInNewTab", NULL,
7428   /* label, accelerator */       N_("Open in New _Tab"), "<control><shift>o",
7429   /* tooltip */                  N_("Open each selected item in a new tab"),
7430                                  G_CALLBACK (action_open_new_tab_callback) },
7431   /* name, icon name */        { "OpenFolderWindow", NULL,
7432   /* label, accelerator */       N_("Open in _Folder Window"), NULL,
7433   /* tooltip */                  N_("Open each selected item in a folder window"),
7434                                  G_CALLBACK (action_open_folder_window_callback) },
7435   /* name, icon name */        { "OtherApplication1", NULL,
7436   /* label, accelerator */       N_("Other _Application..."), NULL,
7437   /* tooltip */                  N_("Choose another application with which to open the selected item"),
7438                                  G_CALLBACK (action_other_application_callback) },
7439   /* name, icon name */        { "OtherApplication2", NULL,
7440   /* label, accelerator */       N_("Open With Other _Application..."), NULL,
7441   /* tooltip */                  N_("Choose another application with which to open the selected item"),
7442                                  G_CALLBACK (action_other_application_callback) },
7443   /* name, icon name */        { "Open Scripts Folder", NULL,
7444   /* label, accelerator */       N_("_Open Scripts Folder"), NULL,
7445   /* tooltip */                  N_("Show the folder containing the scripts that appear in this menu"),
7446                                  G_CALLBACK (action_open_scripts_folder_callback) },
7447   /* name, icon name */        { "Empty Trash", CAJA_ICON_TRASH,
7448   /* label, accelerator */       N_("E_mpty Trash"), NULL,
7449   /* tooltip */                  N_("Delete all items in the Trash"),
7450                                  G_CALLBACK (action_empty_trash_callback) },
7451   /* name, icon name */        { "Cut", "edit-cut",
7452   /* label, accelerator */       N_("Cu_t"), "<control>X",
7453   /* tooltip */                  N_("Prepare the selected files to be moved with a Paste command"),
7454                                  G_CALLBACK (action_cut_files_callback) },
7455   /* name, icon name */        { "Copy", "edit-copy",
7456   /* label, accelerator */       N_("_Copy"), "<control>C",
7457   /* tooltip */                  N_("Prepare the selected files to be copied with a Paste command"),
7458                                  G_CALLBACK (action_copy_files_callback) },
7459   /* name, icon name */        { "Paste", "edit-paste",
7460   /* label, accelerator */       N_("_Paste"), "<control>V",
7461   /* tooltip */                  N_("Move or copy files previously selected by a Cut or Copy command"),
7462                                  G_CALLBACK (action_paste_files_callback) },
7463   /* We make accelerator "" instead of null here to not inherit the stock
7464      accelerator for paste */
7465   /* name, icon name */        { "Paste Files Into", "edit-paste",
7466   /* label, accelerator */       N_("_Paste Into Folder"), "",
7467   /* tooltip */                  N_("Move or copy files previously selected by a Cut or Copy command into the selected folder"),
7468                                  G_CALLBACK (action_paste_files_into_callback) },
7469   /* name, icon name, label */ { "CopyToMenu", NULL, N_("Cop_y to"),
7470                                  NULL, NULL, NULL },
7471   /* name, icon name, label */ { "MoveToMenu", NULL, N_("M_ove to"),
7472                                  NULL, NULL, NULL },
7473   /* name, icon name */        { "Select All", NULL,
7474   /* label, accelerator */       N_("Select _All"), "<control>A",
7475   /* tooltip */                  N_("Select all items in this window"),
7476                                  G_CALLBACK (action_select_all_callback) },
7477   /* name, icon name */        { "Select Pattern", NULL,
7478   /* label, accelerator */       N_("Select I_tems Matching..."), "<control>S",
7479   /* tooltip */                  N_("Select items in this window matching a given pattern"),
7480                                  G_CALLBACK (action_select_pattern_callback) },
7481   /* name, icon name */        { "Invert Selection", NULL,
7482   /* label, accelerator */       N_("_Invert Selection"), "<control><shift>I",
7483   /* tooltip */                  N_("Select all and only the items that are not currently selected"),
7484                                  G_CALLBACK (action_invert_selection_callback) },
7485   /* name, icon name */        { "Duplicate", NULL,
7486   /* label, accelerator */       N_("D_uplicate"), NULL,
7487   /* tooltip */                  N_("Duplicate each selected item"),
7488                                  G_CALLBACK (action_duplicate_callback) },
7489   /* name, icon name */        { "Create Link", NULL,
7490   /* label, accelerator */       N_("Ma_ke Link"), "<control>M",
7491   /* tooltip */                  N_("Create a symbolic link for each selected item"),
7492                                  G_CALLBACK (action_create_link_callback) },
7493   /* name, icon name */        { "Rename", NULL,
7494   /* label, accelerator */       N_("_Rename..."), "F2",
7495   /* tooltip */                  N_("Rename selected item"),
7496                                  G_CALLBACK (action_rename_callback) },
7497   /* name, icon name */        { "RenameSelectAll", NULL,
7498   /* label, accelerator */       "RenameSelectAll", "<shift>F2",
7499   /* tooltip */                  NULL,
7500                                  G_CALLBACK (action_rename_select_all_callback) },
7501   /* name, icon name */        { "Trash", NULL,
7502   /* label, accelerator */       N_("Mo_ve to Trash"), NULL,
7503   /* tooltip */                  N_("Move each selected item to the Trash"),
7504                                  G_CALLBACK (action_trash_callback) },
7505   /* name, icon name */        { "Delete", NULL,
7506   /* label, accelerator */       N_("_Delete"), NULL,
7507   /* tooltip */                  N_("Delete each selected item, without moving to the Trash"),
7508                                  G_CALLBACK (action_delete_callback) },
7509   /* name, icon name */        { "Restore From Trash", NULL,
7510   /* label, accelerator */       N_("_Restore"), NULL,
7511 				 NULL,
7512                                  G_CALLBACK (action_restore_from_trash_callback) },
7513   /* name, icon name */        { FM_ACTION_UNDO, "edit-undo",
7514   /* label, accelerator */       N_("_Undo"), "<control>Z",
7515   /* tooltip */                  N_("Undo the last action"),
7516                                  G_CALLBACK (action_undo_callback) },
7517   /* name, icon name */        { FM_ACTION_REDO, "edit-redo",
7518   /* label, accelerator */       N_("_Redo"), "<control>Y",
7519   /* tooltip */                  N_("Redo the last undone action"),
7520                                  G_CALLBACK (action_redo_callback) },
7521 
7522   /*
7523    * multiview-TODO: decide whether "Reset to Defaults" should
7524    * be window-wide, and not just view-wide.
7525    * Since this also resets the "Show hidden files" mode,
7526    * it is a mixture of both ATM.
7527    */
7528   /* name, icon name */        { "Reset to Defaults", NULL,
7529   /* label, accelerator */       N_("Reset View to _Defaults"), NULL,
7530   /* tooltip */                  N_("Reset sorting order and zoom level to match preferences for this view"),
7531                                  G_CALLBACK (action_reset_to_defaults_callback) },
7532   /* name, icon name */        { "Connect To Server Link", NULL,
7533   /* label, accelerator */       N_("Connect To This Server"), NULL,
7534   /* tooltip */                  N_("Make a permanent connection to this server"),
7535                                  G_CALLBACK (action_connect_to_server_link_callback) },
7536   /* name, icon name */        { "Mount Volume", NULL,
7537   /* label, accelerator */       N_("_Mount"), NULL,
7538   /* tooltip */                  N_("Mount the selected volume"),
7539                                  G_CALLBACK (action_mount_volume_callback) },
7540   /* name, icon name */        { "Unmount Volume", NULL,
7541   /* label, accelerator */       N_("_Unmount"), NULL,
7542   /* tooltip */                  N_("Unmount the selected volume"),
7543                                  G_CALLBACK (action_unmount_volume_callback) },
7544   /* name, icon name */        { "Eject Volume", NULL,
7545   /* label, accelerator */       N_("_Eject"), NULL,
7546   /* tooltip */                  N_("Eject the selected volume"),
7547                                  G_CALLBACK (action_eject_volume_callback) },
7548   /* name, icon name */        { "Format Volume", NULL,
7549   /* label, accelerator */       N_("_Format"), NULL,
7550   /* tooltip */                  N_("Format the selected volume"),
7551                                  G_CALLBACK (action_format_volume_callback) },
7552   /* name, icon name */        { "Start Volume", NULL,
7553   /* label, accelerator */       N_("_Start"), NULL,
7554   /* tooltip */                  N_("Start the selected volume"),
7555                                  G_CALLBACK (action_start_volume_callback) },
7556   /* name, icon name */        { "Stop Volume", NULL,
7557   /* label, accelerator */       N_("_Stop"), NULL,
7558   /* tooltip */                  N_("Stop the selected volume"),
7559                                  G_CALLBACK (action_stop_volume_callback) },
7560   /* name, icon name */        { "Poll", NULL,
7561   /* label, accelerator */       N_("_Detect Media"), NULL,
7562   /* tooltip */                  N_("Detect media in the selected drive"),
7563                                  G_CALLBACK (action_detect_media_callback) },
7564   /* name, icon name */        { "Self Mount Volume", NULL,
7565   /* label, accelerator */       N_("_Mount"), NULL,
7566   /* tooltip */                  N_("Mount the volume associated with the open folder"),
7567                                  G_CALLBACK (action_self_mount_volume_callback) },
7568   /* name, icon name */        { "Self Unmount Volume", NULL,
7569   /* label, accelerator */       N_("_Unmount"), NULL,
7570   /* tooltip */                  N_("Unmount the volume associated with the open folder"),
7571                                  G_CALLBACK (action_self_unmount_volume_callback) },
7572   /* name, icon name */        { "Self Eject Volume", NULL,
7573   /* label, accelerator */       N_("_Eject"), NULL,
7574   /* tooltip */                  N_("Eject the volume associated with the open folder"),
7575                                  G_CALLBACK (action_self_eject_volume_callback) },
7576   /* name, icon name */        { "Self Format Volume", NULL,
7577   /* label, accelerator */       N_("_Format"), NULL,
7578   /* tooltip */                  N_("Format the volume associated with the open folder"),
7579                                  G_CALLBACK (action_self_format_volume_callback) },
7580   /* name, icon name */        { "Self Start Volume", NULL,
7581   /* label, accelerator */       N_("_Start"), NULL,
7582   /* tooltip */                  N_("Start the volume associated with the open folder"),
7583                                  G_CALLBACK (action_self_start_volume_callback) },
7584   /* name, icon name */        { "Self Stop Volume", NULL,
7585   /* label, accelerator */       N_("_Stop"), NULL,
7586   /* tooltip */                  N_("Stop the volume associated with the open folder"),
7587                                  G_CALLBACK (action_self_stop_volume_callback) },
7588   /* name, icon name */        { "Self Poll", NULL,
7589   /* label, accelerator */       N_("_Detect Media"), NULL,
7590   /* tooltip */                  N_("Detect media in the selected drive"),
7591                                  G_CALLBACK (action_self_detect_media_callback) },
7592   /* name, icon name */        { "OpenCloseParent", NULL,
7593   /* label, accelerator */       N_("Open File and Close window"), "<alt><shift>Down",
7594   /* tooltip */                  NULL,
7595                                  G_CALLBACK (action_open_close_parent_callback) },
7596   /* name, icon name */        { "Save Search", NULL,
7597   /* label, accelerator */       N_("Sa_ve Search"), NULL,
7598   /* tooltip */                  N_("Save the edited search"),
7599                                  G_CALLBACK (action_save_search_callback) },
7600   /* name, icon name */        { "Save Search As", NULL,
7601   /* label, accelerator */       N_("Sa_ve Search As..."), NULL,
7602   /* tooltip */                  N_("Save the current search as a file"),
7603                                  G_CALLBACK (action_save_search_as_callback) },
7604 
7605   /* Location-specific actions */
7606   /* name, icon name */        { FM_ACTION_LOCATION_OPEN_ALTERNATE, NULL,
7607   /* label, accelerator */       N_("Open in Navigation Window"), "",
7608   /* tooltip */                  N_("Open this folder in a navigation window"),
7609                                  G_CALLBACK (action_location_open_alternate_callback) },
7610   /* name, icon name */        { FM_ACTION_LOCATION_OPEN_IN_NEW_TAB, NULL,
7611   /* label, accelerator */       N_("Open in New _Tab"), "",
7612   /* tooltip */                  N_("Open this folder in a new tab"),
7613                                  G_CALLBACK (action_location_open_in_new_tab_callback) },
7614 
7615   /* name, icon name */        { FM_ACTION_LOCATION_OPEN_FOLDER_WINDOW, NULL,
7616   /* label, accelerator */       N_("Open in _Folder Window"), "",
7617   /* tooltip */                  N_("Open this folder in a folder window"),
7618                                  G_CALLBACK (action_location_open_folder_window_callback) },
7619 
7620   /* name, icon name */        { FM_ACTION_LOCATION_CUT, "edit-cut",
7621   /* label, accelerator */       N_("Cu_t"), "",
7622   /* tooltip */                  N_("Prepare this folder to be moved with a Paste command"),
7623                                  G_CALLBACK (action_location_cut_callback) },
7624   /* name, icon name */        { FM_ACTION_LOCATION_COPY, "edit-copy",
7625   /* label, accelerator */       N_("_Copy"), "",
7626   /* tooltip */                  N_("Prepare this folder to be copied with a Paste command"),
7627                                  G_CALLBACK (action_location_copy_callback) },
7628   /* name, icon name */        { FM_ACTION_LOCATION_PASTE_FILES_INTO, "edit-paste",
7629   /* label, accelerator */       N_("_Paste Into Folder"), "",
7630   /* tooltip */                  N_("Move or copy files previously selected by a Cut or Copy command into this folder"),
7631                                  G_CALLBACK (action_location_paste_files_into_callback) },
7632 
7633   /* name, icon name */        { FM_ACTION_LOCATION_TRASH, NULL,
7634   /* label, accelerator */       N_("Mo_ve to Trash"), "",
7635   /* tooltip */                  N_("Move this folder to the Trash"),
7636                                  G_CALLBACK (action_location_trash_callback) },
7637   /* name, icon name */        { FM_ACTION_LOCATION_DELETE, CAJA_ICON_DELETE,
7638   /* label, accelerator */       N_("_Delete"), "",
7639   /* tooltip */                  N_("Delete this folder, without moving to the Trash"),
7640                                  G_CALLBACK (action_location_delete_callback) },
7641   /* name, icon name */        { FM_ACTION_LOCATION_RESTORE_FROM_TRASH, NULL,
7642   /* label, accelerator */       N_("_Restore"), NULL, NULL,
7643                                  G_CALLBACK (action_location_restore_from_trash_callback) },
7644 
7645   /* name, icon name */        { "Location Mount Volume", NULL,
7646   /* label, accelerator */       N_("_Mount"), NULL,
7647   /* tooltip */                  N_("Mount the volume associated with this folder"),
7648                                  G_CALLBACK (action_location_mount_volume_callback) },
7649   /* name, icon name */        { "Location Unmount Volume", NULL,
7650   /* label, accelerator */       N_("_Unmount"), NULL,
7651   /* tooltip */                  N_("Unmount the volume associated with this folder"),
7652                                  G_CALLBACK (action_location_unmount_volume_callback) },
7653   /* name, icon name */        { "Location Eject Volume", NULL,
7654   /* label, accelerator */       N_("_Eject"), NULL,
7655   /* tooltip */                  N_("Eject the volume associated with this folder"),
7656                                  G_CALLBACK (action_location_eject_volume_callback) },
7657   /* name, icon name */        { "Location Format Volume", NULL,
7658   /* label, accelerator */       N_("_Format"), NULL,
7659   /* tooltip */                  N_("Format the volume associated with this folder"),
7660                                  G_CALLBACK (action_location_format_volume_callback) },
7661   /* name, icon name */        { "Location Start Volume", NULL,
7662   /* label, accelerator */       N_("_Start"), NULL,
7663   /* tooltip */                  N_("Start the volume associated with this folder"),
7664                                  G_CALLBACK (action_location_start_volume_callback) },
7665   /* name, icon name */        { "Location Stop Volume", NULL,
7666   /* label, accelerator */       N_("_Stop"), NULL,
7667   /* tooltip */                  N_("Stop the volume associated with this folder"),
7668                                  G_CALLBACK (action_location_stop_volume_callback) },
7669   /* name, icon name */        { "Location Poll", NULL,
7670   /* label, accelerator */       N_("_Detect Media"), NULL,
7671   /* tooltip */                  N_("Detect media in the selected drive"),
7672                                  G_CALLBACK (action_location_detect_media_callback) },
7673 
7674   /* name, icon name */        { "LocationProperties", "document-properties",
7675   /* label, accelerator */       N_("_Properties"), NULL,
7676   /* tooltip */                  N_("View or modify the properties of this folder"),
7677                                  G_CALLBACK (action_location_properties_callback) },
7678 
7679   /* name, icon name, label */ {FM_ACTION_COPY_TO_NEXT_PANE, NULL, N_("_Other pane"),
7680 				NULL, N_("Copy the current selection to the other pane in the window"),
7681 				G_CALLBACK (action_copy_to_next_pane_callback) },
7682   /* name, icon name, label */ {FM_ACTION_MOVE_TO_NEXT_PANE, NULL, N_("_Other pane"),
7683 				NULL, N_("Move the current selection to the other pane in the window"),
7684 				G_CALLBACK (action_move_to_next_pane_callback) },
7685   /* name, icon name, label */ {FM_ACTION_COPY_TO_HOME, CAJA_ICON_HOME,
7686 				N_("_Home Folder"), NULL,
7687 				N_("Copy the current selection to the home folder"),
7688 				G_CALLBACK (action_copy_to_home_callback) },
7689   /* name, icon name, label */ {FM_ACTION_MOVE_TO_HOME, CAJA_ICON_HOME,
7690 				N_("_Home Folder"), NULL,
7691 				N_("Move the current selection to the home folder"),
7692 				G_CALLBACK (action_move_to_home_callback) },
7693   /* name, icon name, label */ {FM_ACTION_COPY_TO_DESKTOP, CAJA_ICON_DESKTOP,
7694 				N_("_Desktop"), NULL,
7695 				N_("Copy the current selection to the desktop"),
7696 				G_CALLBACK (action_copy_to_desktop_callback) },
7697   /* name, icon name, label */ {FM_ACTION_MOVE_TO_DESKTOP, CAJA_ICON_DESKTOP,
7698 				N_("_Desktop"), NULL,
7699 				N_("Move the current selection to the desktop"),
7700 				G_CALLBACK (action_move_to_desktop_callback) },
7701 };
7702 
7703 static void
connect_proxy(FMDirectoryView * view,GtkAction * action,GtkWidget * proxy,GtkActionGroup * action_group)7704 connect_proxy (FMDirectoryView *view,
7705 	       GtkAction *action,
7706 	       GtkWidget *proxy,
7707 	       GtkActionGroup *action_group)
7708 {
7709 	const gchar *action_name;
7710 
7711 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
7712 	action_name = gtk_action_get_name (action);
7713 	G_GNUC_END_IGNORE_DEPRECATIONS;
7714 
7715 	if (strcmp (action_name, FM_ACTION_NEW_EMPTY_FILE) == 0 &&
7716 	    GTK_IS_IMAGE_MENU_ITEM (proxy)) {
7717 		cairo_surface_t *surface;
7718 
7719 		surface = get_menu_icon ("text-x-generic", GTK_WIDGET (view));
7720 
7721 		if (surface != NULL) {
7722 			GtkWidget *image;
7723 
7724 			image = gtk_image_new_from_surface (surface);
7725 			gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (proxy), image);
7726 
7727 			cairo_surface_destroy (surface);
7728 		}
7729 	}
7730 }
7731 
7732 static void
pre_activate(FMDirectoryView * view,GtkAction * action,GtkActionGroup * action_group)7733 pre_activate (FMDirectoryView *view,
7734 	      GtkAction *action,
7735 	      GtkActionGroup *action_group)
7736 {
7737 	GdkEvent *event;
7738 	GtkWidget *proxy;
7739 	gboolean activated_from_popup;
7740 
7741 	/* check whether action was activated through a popup menu.
7742 	 * If not, unset the last stored context menu popup position */
7743 	activated_from_popup = FALSE;
7744 
7745 	event = gtk_get_current_event ();
7746 	proxy = gtk_get_event_widget (event);
7747 
7748 	if (proxy != NULL) {
7749 		GtkWidget *toplevel;
7750 		GdkWindowTypeHint hint;
7751 
7752 		toplevel = gtk_widget_get_toplevel (proxy);
7753 
7754 		if (GTK_IS_WINDOW (toplevel)) {
7755 			hint = gtk_window_get_type_hint (GTK_WINDOW (toplevel));
7756 
7757 			if (hint == GDK_WINDOW_TYPE_HINT_POPUP_MENU) {
7758 				activated_from_popup = TRUE;
7759 			}
7760 		}
7761 	}
7762 
7763 	if (!activated_from_popup) {
7764 		update_context_menu_position_from_event (view, NULL);
7765 	}
7766 }
7767 
7768 static void
real_merge_menus(FMDirectoryView * view)7769 real_merge_menus (FMDirectoryView *view)
7770 {
7771 	GtkActionGroup *action_group;
7772 	GtkUIManager *ui_manager;
7773 	GtkAction *action;
7774 	const char *ui;
7775 	char *tooltip;
7776 
7777 	ui_manager = caja_window_info_get_ui_manager (view->details->window);
7778 
7779 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
7780 	action_group = gtk_action_group_new ("DirViewActions");
7781 #ifdef ENABLE_NLS
7782 	gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
7783 #endif /* ENABLE_NLS */
7784 	view->details->dir_action_group = action_group;
7785 	gtk_action_group_add_actions (action_group,
7786 				      directory_view_entries, G_N_ELEMENTS (directory_view_entries),
7787 				      view);
7788 	G_GNUC_END_IGNORE_DEPRECATIONS;
7789 
7790 	/* Translators: %s is a directory */
7791 	tooltip = g_strdup_printf(_("Run or manage scripts from %s"), "~/.config/caja/scripts");
7792 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
7793 	/* Create a script action here specially because its tooltip is dynamic */
7794 	action = gtk_action_new ("Scripts", _("_Scripts"), tooltip, NULL);
7795 	gtk_action_group_add_action (action_group, action);
7796 	g_object_unref (action);
7797 	g_free (tooltip);
7798 
7799 	action = gtk_action_group_get_action (action_group, FM_ACTION_NO_TEMPLATES);
7800 	gtk_action_set_sensitive (action, FALSE);
7801 	G_GNUC_END_IGNORE_DEPRECATIONS;
7802 
7803 	g_signal_connect_object (action_group, "connect-proxy",
7804 				 G_CALLBACK (connect_proxy), G_OBJECT (view),
7805 				 G_CONNECT_SWAPPED);
7806 	g_signal_connect_object (action_group, "pre-activate",
7807 				 G_CALLBACK (pre_activate), G_OBJECT (view),
7808 				 G_CONNECT_SWAPPED);
7809 
7810 	/* Insert action group at end so clipboard action group ends up before it */
7811 	gtk_ui_manager_insert_action_group (ui_manager, action_group, -1);
7812 	g_object_unref (action_group); /* owned by ui manager */
7813 
7814 	ui = caja_ui_string_get ("caja-directory-view-ui.xml");
7815 	view->details->dir_merge_id = gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL);
7816 	g_signal_connect_object (fm_directory_view_get_background (view), "settings_changed",
7817 				 G_CALLBACK (schedule_update_menus), G_OBJECT (view),
7818 				 G_CONNECT_SWAPPED);
7819 	view->details->scripts_invalid = TRUE;
7820 	view->details->templates_invalid = TRUE;
7821 }
7822 
7823 
7824 static gboolean
can_paste_into_file(CajaFile * file)7825 can_paste_into_file (CajaFile *file)
7826 {
7827 	if (caja_file_is_directory (file) &&
7828 	    caja_file_can_write (file)) {
7829 		return TRUE;
7830 	}
7831 	if (caja_file_has_activation_uri (file)) {
7832 		GFile *location;
7833 		CajaFile *activation_file;
7834 		gboolean res;
7835 
7836 		location = caja_file_get_activation_location (file);
7837 		activation_file = caja_file_get (location);
7838 		g_object_unref (location);
7839 
7840 		/* The target location might not have data for it read yet,
7841 		   and we can't want to do sync I/O, so treat the unknown
7842 		   case as can-write */
7843 		res = (caja_file_get_file_type (activation_file) == G_FILE_TYPE_UNKNOWN) ||
7844 			(caja_file_get_file_type (activation_file) == G_FILE_TYPE_DIRECTORY &&
7845 			 caja_file_can_write (activation_file));
7846 
7847 		caja_file_unref (activation_file);
7848 
7849 		return res;
7850 	}
7851 
7852 	return FALSE;
7853 }
7854 
7855 static void
clipboard_targets_received(GtkClipboard * clipboard,GdkAtom * targets,int n_targets,gpointer user_data)7856 clipboard_targets_received (GtkClipboard     *clipboard,
7857                             GdkAtom          *targets,
7858                             int               n_targets,
7859 			    gpointer          user_data)
7860 {
7861 	FMDirectoryView *view;
7862 	gboolean can_paste;
7863 	GList *selection;
7864 	int count;
7865 	GtkAction *action;
7866 
7867 	view = FM_DIRECTORY_VIEW (user_data);
7868 	can_paste = FALSE;
7869 
7870 	if (view->details->window == NULL ||
7871 	    !view->details->active) {
7872 		/* We've been destroyed or became inactive since call */
7873 		g_object_unref (view);
7874 		return;
7875 	}
7876 
7877 	if (targets) {
7878 		int i;
7879 
7880 		for (i=0; i < n_targets; i++) {
7881 			if (targets[i] == copied_files_atom) {
7882 				can_paste = TRUE;
7883 			}
7884 		}
7885 	}
7886 
7887 
7888 	selection = fm_directory_view_get_selection (view);
7889 	count = g_list_length (selection);
7890 
7891 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
7892 	action = gtk_action_group_get_action (view->details->dir_action_group,
7893 					      FM_ACTION_PASTE);
7894 	gtk_action_set_sensitive (action,
7895 				  can_paste && !fm_directory_view_is_read_only (view));
7896 
7897 	action = gtk_action_group_get_action (view->details->dir_action_group,
7898 					      FM_ACTION_PASTE_FILES_INTO);
7899 	gtk_action_set_sensitive (action,
7900 	                          can_paste && count == 1 &&
7901 	                          can_paste_into_file (CAJA_FILE (selection->data)));
7902 
7903 	action = gtk_action_group_get_action (view->details->dir_action_group,
7904 					      FM_ACTION_LOCATION_PASTE_FILES_INTO);
7905 	G_GNUC_END_IGNORE_DEPRECATIONS;
7906 	g_object_set_data (G_OBJECT (action),
7907 			   "can-paste-according-to-clipboard",
7908 			   GINT_TO_POINTER (can_paste));
7909 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
7910 	gtk_action_set_sensitive (action,
7911 				  GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
7912 						   "can-paste-according-to-clipboard")) &&
7913 				  GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
7914 						   "can-paste-according-to-destination")));
7915 	G_GNUC_END_IGNORE_DEPRECATIONS;
7916 
7917 	caja_file_list_free (selection);
7918 
7919 	g_object_unref (view);
7920 }
7921 
7922 static gboolean
showing_trash_directory(FMDirectoryView * view)7923 showing_trash_directory (FMDirectoryView *view)
7924 {
7925 	CajaFile *file;
7926 
7927 	file = fm_directory_view_get_directory_as_file (view);
7928 	if (file != NULL) {
7929 		return caja_file_is_in_trash (file);
7930 	}
7931 	return FALSE;
7932 }
7933 
7934 static gboolean
should_show_empty_trash(FMDirectoryView * view)7935 should_show_empty_trash (FMDirectoryView *view)
7936 {
7937 	return (showing_trash_directory (view) || caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION);
7938 }
7939 
7940 static gboolean
file_list_all_are_folders(GList * file_list)7941 file_list_all_are_folders (GList *file_list)
7942 {
7943 	GList *l;
7944 	char *activation_uri;
7945 	gboolean is_dir;
7946 	CajaFile *linked_file;
7947 	CajaFile *file = NULL;
7948 
7949 	for (l = file_list; l != NULL; l = l->next) {
7950 		file = CAJA_FILE (l->data);
7951 		if (caja_file_is_caja_link (file) &&
7952 		    !CAJA_IS_DESKTOP_ICON_FILE (file)) {
7953 			if (caja_file_is_launcher (file)) {
7954 				return FALSE;
7955 			}
7956 
7957 			activation_uri = caja_file_get_activation_uri (file);
7958 
7959 			if (activation_uri == NULL) {
7960 				g_free (activation_uri);
7961 				return FALSE;
7962 			}
7963 
7964 			linked_file = caja_file_get_existing_by_uri (activation_uri);
7965 
7966 			/* We might not actually know the type of the linked file yet,
7967 			 * however we don't want to schedule a read, since that might do things
7968 			 * like ask for password etc. This is a bit unfortunate, but I don't
7969 			 * know any way around it, so we do various heuristics here
7970 			 * to get things mostly right
7971 			 */
7972 			is_dir =
7973 				(linked_file != NULL &&
7974 				 caja_file_is_directory (linked_file)) ||
7975 				(activation_uri != NULL &&
7976 				 activation_uri[strlen (activation_uri) - 1] == '/');
7977 
7978 			caja_file_unref (linked_file);
7979 			g_free (activation_uri);
7980 
7981 			if (!is_dir) {
7982 				return FALSE;
7983 			}
7984 		} else if (!(caja_file_is_directory (file) ||
7985 			     CAJA_IS_DESKTOP_ICON_FILE (file))) {
7986 			return FALSE;
7987 		}
7988 	}
7989 	return TRUE;
7990 }
7991 
7992 static void
file_should_show_foreach(CajaFile * file,gboolean * show_mount,gboolean * show_unmount,gboolean * show_eject,gboolean * show_connect,gboolean * show_format,gboolean * show_start,gboolean * show_stop,gboolean * show_poll,GDriveStartStopType * start_stop_type)7993 file_should_show_foreach (CajaFile        *file,
7994 			  gboolean            *show_mount,
7995 			  gboolean            *show_unmount,
7996 			  gboolean            *show_eject,
7997 			  gboolean            *show_connect,
7998 			  gboolean            *show_format,
7999 			  gboolean            *show_start,
8000 			  gboolean            *show_stop,
8001 			  gboolean            *show_poll,
8002 			  GDriveStartStopType *start_stop_type)
8003 {
8004 	*show_mount = FALSE;
8005 	*show_unmount = FALSE;
8006 	*show_eject = FALSE;
8007 	*show_connect = FALSE;
8008 	*show_format = FALSE;
8009 	*show_start = FALSE;
8010 	*show_stop = FALSE;
8011 	*show_poll = FALSE;
8012 
8013 	if (caja_file_can_eject (file)) {
8014 		*show_eject = TRUE;
8015 	}
8016 
8017 	if (caja_file_can_mount (file)) {
8018 		*show_mount = TRUE;
8019 
8020 #ifdef TODO_GIO
8021 		if (something &&
8022 		    g_find_program_in_path ("gfloppy")) {
8023 			*show_format = TRUE;
8024 		}
8025 #endif
8026 	}
8027 
8028 	if (caja_file_can_start (file) || caja_file_can_start_degraded (file)) {
8029 		*show_start = TRUE;
8030 	}
8031 
8032 	if (caja_file_can_stop (file)) {
8033 		*show_stop = TRUE;
8034 	}
8035 
8036 	/* Dot not show both Unmount and Eject/Safe Removal; too confusing to
8037 	 * have too many menu entries */
8038 	if (caja_file_can_unmount (file) && !*show_eject && !*show_stop) {
8039 		*show_unmount = TRUE;
8040 	}
8041 
8042 	if (caja_file_can_poll_for_media (file) && !caja_file_is_media_check_automatic (file)) {
8043 		*show_poll = TRUE;
8044 	}
8045 
8046 	*start_stop_type = caja_file_get_start_stop_type (file);
8047 
8048 	if (caja_file_is_caja_link (file)) {
8049 		char *uri;
8050 
8051 		uri = caja_file_get_activation_uri (file);
8052 		if (uri != NULL &&
8053 		    (eel_istr_has_prefix (uri, "ftp:") ||
8054 		     eel_istr_has_prefix (uri, "ssh:") ||
8055 		     eel_istr_has_prefix (uri, "sftp:") ||
8056 		     eel_istr_has_prefix (uri, "dav:") ||
8057 		     eel_istr_has_prefix (uri, "davs:"))) {
8058 			*show_connect = TRUE;
8059 		}
8060 		g_free (uri);
8061 	}
8062 }
8063 
8064 static void
file_should_show_self(CajaFile * file,gboolean * show_mount,gboolean * show_unmount,gboolean * show_eject,gboolean * show_format,gboolean * show_start,gboolean * show_stop,gboolean * show_poll,GDriveStartStopType * start_stop_type)8065 file_should_show_self (CajaFile        *file,
8066 		       gboolean            *show_mount,
8067 		       gboolean            *show_unmount,
8068 		       gboolean            *show_eject,
8069 		       gboolean            *show_format,
8070 		       gboolean            *show_start,
8071 		       gboolean            *show_stop,
8072 		       gboolean            *show_poll,
8073 		       GDriveStartStopType *start_stop_type)
8074 {
8075 	*show_mount = FALSE;
8076 	*show_unmount = FALSE;
8077 	*show_eject = FALSE;
8078 	*show_format = FALSE;
8079 	*show_start = FALSE;
8080 	*show_stop = FALSE;
8081 	*show_poll = FALSE;
8082 
8083 	if (file == NULL) {
8084 		return;
8085 	}
8086 
8087 	if (caja_file_can_eject (file)) {
8088 		*show_eject = TRUE;
8089 	}
8090 
8091 	if (caja_file_can_mount (file)) {
8092 		*show_mount = TRUE;
8093 	}
8094 
8095 #ifdef TODO_GIO
8096 	if (something && g_find_program_in_path ("gfloppy")) {
8097 		*show_format = TRUE;
8098 	}
8099 #endif
8100 
8101 	if (caja_file_can_start (file) || caja_file_can_start_degraded (file)) {
8102 		*show_start = TRUE;
8103 	}
8104 
8105 	if (caja_file_can_stop (file)) {
8106 		*show_stop = TRUE;
8107 	}
8108 
8109 	/* Dot not show both Unmount and Eject/Safe Removal; too confusing to
8110 	 * have too many menu entries */
8111 	if (caja_file_can_unmount (file) && !*show_eject && !*show_stop) {
8112 		*show_unmount = TRUE;
8113 	}
8114 
8115 	if (caja_file_can_poll_for_media (file) && !caja_file_is_media_check_automatic (file)) {
8116 		*show_poll = TRUE;
8117 	}
8118 
8119 	*start_stop_type = caja_file_get_start_stop_type (file);
8120 
8121 }
8122 
8123 static gboolean
files_are_all_directories(GList * files)8124 files_are_all_directories (GList *files)
8125 {
8126 	GList *l;
8127 	gboolean all_directories;
8128 	CajaFile *file = NULL;
8129 
8130 	all_directories = TRUE;
8131 
8132 	for (l = files; l != NULL; l = l->next) {
8133 		file = CAJA_FILE (l->data);
8134 		all_directories &= caja_file_is_directory (file);
8135 	}
8136 
8137 	return all_directories;
8138 }
8139 
8140 static gboolean
files_is_none_directory(GList * files)8141 files_is_none_directory (GList *files)
8142 {
8143 	GList *l;
8144 	gboolean no_directory;
8145 	CajaFile *file = NULL;
8146 
8147 	no_directory = TRUE;
8148 
8149 	for (l = files; l != NULL; l = l->next) {
8150 		file = CAJA_FILE (l->data);
8151 		no_directory &= !caja_file_is_directory (file);
8152 	}
8153 
8154 	return no_directory;
8155 }
8156 
8157 static void
update_restore_from_trash_action(GtkAction * action,GList * files,gboolean is_self)8158 update_restore_from_trash_action (GtkAction *action,
8159 				  GList *files,
8160 				  gboolean is_self)
8161 {
8162 	CajaFile *original_file;
8163 	CajaFile *original_dir;
8164 	GHashTable *original_dirs_hash;
8165 	GList *original_dirs;
8166 	GFile *original_location;
8167 	char *original_name;
8168 
8169 	original_file = NULL;
8170 	original_dir = NULL;
8171 	original_dirs = NULL;
8172 	original_dirs_hash = NULL;
8173 	original_location = NULL;
8174 	original_name = NULL;
8175 
8176 	if (files != NULL) {
8177 		if (g_list_length (files) == 1) {
8178 			original_file = caja_file_get_trash_original_file (files->data);
8179 		} else {
8180 			original_dirs_hash = caja_trashed_files_get_original_directories (files, NULL);
8181 			if (original_dirs_hash != NULL) {
8182 				original_dirs = g_hash_table_get_keys (original_dirs_hash);
8183 				if (g_list_length (original_dirs) == 1) {
8184 					original_dir = caja_file_ref (CAJA_FILE (original_dirs->data));
8185 				}
8186 			}
8187 		}
8188 	}
8189 
8190 	if (original_file != NULL || original_dirs != NULL) {
8191 		char *tooltip;
8192 
8193 		G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8194 		gtk_action_set_visible (action, TRUE);
8195 		G_GNUC_END_IGNORE_DEPRECATIONS;
8196 
8197 		if (original_file != NULL) {
8198 			original_location = caja_file_get_location (original_file);
8199 		} else if (original_dir != NULL) {
8200 			original_location = caja_file_get_location (original_dir);
8201 		}
8202 
8203 		if (original_location != NULL) {
8204 			original_name = g_file_get_parse_name (original_location);
8205 		}
8206 
8207 		if (is_self) {
8208 			g_assert (g_list_length (files) == 1);
8209 			g_assert (original_location != NULL);
8210 			tooltip = g_strdup_printf (_("Move the open folder out of the trash to \"%s\""), original_name);
8211 		} else if (files_are_all_directories (files)) {
8212 			if (original_name != NULL) {
8213 				tooltip = g_strdup_printf (ngettext ("Move the selected folder out of the trash to \"%s\"",
8214 								     "Move the selected folders out of the trash to \"%s\"",
8215 								     g_list_length (files)), original_name);
8216 			} else {
8217 				tooltip = g_strdup_printf (ngettext ("Move the selected folder out of the trash",
8218 								     "Move the selected folders out of the trash",
8219 								     g_list_length (files)));
8220 			}
8221 		} else if (files_is_none_directory (files)) {
8222 			if (original_name != NULL) {
8223 				tooltip = g_strdup_printf (ngettext ("Move the selected file out of the trash to \"%s\"",
8224 								     "Move the selected files out of the trash to \"%s\"",
8225 								     g_list_length (files)), original_name);
8226 			} else {
8227 				tooltip = g_strdup_printf (ngettext ("Move the selected file out of the trash",
8228 								     "Move the selected files out of the trash",
8229 								     g_list_length (files)));
8230 			}
8231 		} else {
8232 			if (original_name != NULL) {
8233 				tooltip = g_strdup_printf (ngettext ("Move the selected item out of the trash to \"%s\"",
8234 								     "Move the selected items out of the trash to \"%s\"",
8235 								     g_list_length (files)), original_name);
8236 			} else {
8237 				tooltip = g_strdup_printf (ngettext ("Move the selected item out of the trash",
8238 								     "Move the selected items out of the trash",
8239 								     g_list_length (files)));
8240 			}
8241 		}
8242 		g_free (original_name);
8243 
8244 		g_object_set (action, "tooltip", tooltip, NULL);
8245 
8246 		if (original_location != NULL) {
8247 			g_object_unref (original_location);
8248 		}
8249 	} else {
8250 		G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8251 		gtk_action_set_visible (action, FALSE);
8252 		G_GNUC_END_IGNORE_DEPRECATIONS;
8253 	}
8254 
8255 	caja_file_unref (original_file);
8256 	caja_file_unref (original_dir);
8257 	g_list_free (original_dirs);
8258 
8259 	if (original_dirs_hash != NULL) {
8260 		g_hash_table_destroy (original_dirs_hash);
8261 	}
8262 }
8263 
8264 static void
real_update_menus_volumes(FMDirectoryView * view,GList * selection,gint selection_count)8265 real_update_menus_volumes (FMDirectoryView *view,
8266 			   GList *selection,
8267 			   gint selection_count)
8268 {
8269 	GList *l;
8270 	CajaFile *file;
8271 	gboolean show_mount;
8272 	gboolean show_unmount;
8273 	gboolean show_eject;
8274 	gboolean show_connect;
8275 	gboolean show_format;
8276 	gboolean show_start;
8277 	gboolean show_stop;
8278 	gboolean show_poll;
8279 	GDriveStartStopType start_stop_type;
8280 	gboolean show_self_mount;
8281 	gboolean show_self_unmount;
8282 	gboolean show_self_eject;
8283 	gboolean show_self_format;
8284 	gboolean show_self_start;
8285 	gboolean show_self_stop;
8286 	gboolean show_self_poll;
8287 	GDriveStartStopType self_start_stop_type;
8288 	GtkAction *action;
8289 
8290 	show_mount = (selection != NULL);
8291 	show_unmount = (selection != NULL);
8292 	show_eject = (selection != NULL);
8293 	show_connect = (selection != NULL && selection_count == 1);
8294 	show_format = (selection != NULL && selection_count == 1);
8295 	show_start = (selection != NULL && selection_count == 1);
8296 	show_stop = (selection != NULL && selection_count == 1);
8297 	show_poll = (selection != NULL && selection_count == 1);
8298 	start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
8299 	self_start_stop_type = G_DRIVE_START_STOP_TYPE_UNKNOWN;
8300 
8301 	for (l = selection; l != NULL && (show_mount || show_unmount
8302 					  || show_eject || show_connect
8303                                           || show_format || show_start
8304 					  || show_stop || show_poll);
8305 	     l = l->next) {
8306 		gboolean show_mount_one;
8307 		gboolean show_unmount_one;
8308 		gboolean show_eject_one;
8309 		gboolean show_connect_one;
8310 		gboolean show_format_one;
8311 		gboolean show_start_one;
8312 		gboolean show_stop_one;
8313 		gboolean show_poll_one;
8314 
8315 		file = CAJA_FILE (l->data);
8316 		file_should_show_foreach (file,
8317 					  &show_mount_one,
8318 					  &show_unmount_one,
8319 					  &show_eject_one,
8320 					  &show_connect_one,
8321                                           &show_format_one,
8322                                           &show_start_one,
8323                                           &show_stop_one,
8324 					  &show_poll_one,
8325 					  &start_stop_type);
8326 
8327 		show_mount &= show_mount_one;
8328 		show_unmount &= show_unmount_one;
8329 		show_eject &= show_eject_one;
8330 		show_connect &= show_connect_one;
8331 		show_format &= show_format_one;
8332 		show_start &= show_start_one;
8333 		show_stop &= show_stop_one;
8334 		show_poll &= show_poll_one;
8335 	}
8336 
8337 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8338 	action = gtk_action_group_get_action (view->details->dir_action_group,
8339 					      FM_ACTION_CONNECT_TO_SERVER_LINK);
8340 	gtk_action_set_visible (action, show_connect);
8341 
8342 	action = gtk_action_group_get_action (view->details->dir_action_group,
8343 					      FM_ACTION_MOUNT_VOLUME);
8344 	gtk_action_set_visible (action, show_mount);
8345 
8346 	action = gtk_action_group_get_action (view->details->dir_action_group,
8347 					      FM_ACTION_UNMOUNT_VOLUME);
8348 	gtk_action_set_visible (action, show_unmount);
8349 
8350 	action = gtk_action_group_get_action (view->details->dir_action_group,
8351 					      FM_ACTION_EJECT_VOLUME);
8352 	gtk_action_set_visible (action, show_eject);
8353 
8354 	action = gtk_action_group_get_action (view->details->dir_action_group,
8355 					      FM_ACTION_FORMAT_VOLUME);
8356 	gtk_action_set_visible (action, show_format);
8357 
8358 	action = gtk_action_group_get_action (view->details->dir_action_group,
8359 					      FM_ACTION_START_VOLUME);
8360 	gtk_action_set_visible (action, show_start);
8361 	if (show_start) {
8362 		switch (start_stop_type) {
8363 		default:
8364 		case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8365 			gtk_action_set_label (action, _("_Start"));
8366 			gtk_action_set_tooltip (action, _("Start the selected drive"));
8367 			break;
8368 		case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8369 			gtk_action_set_label (action, _("_Start"));
8370 			gtk_action_set_tooltip (action, _("Start the selected drive"));
8371 			break;
8372 		case G_DRIVE_START_STOP_TYPE_NETWORK:
8373 			gtk_action_set_label (action, _("_Connect"));
8374 			gtk_action_set_tooltip (action, _("Connect to the selected drive"));
8375 			break;
8376 		case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8377 			gtk_action_set_label (action, _("_Start Multi-disk Drive"));
8378 			gtk_action_set_tooltip (action, _("Start the selected multi-disk drive"));
8379 			break;
8380 		case G_DRIVE_START_STOP_TYPE_PASSWORD:
8381 			gtk_action_set_label (action, _("U_nlock Drive"));
8382 			gtk_action_set_tooltip (action, _("Unlock the selected drive"));
8383 			break;
8384 		}
8385 	}
8386 
8387 	action = gtk_action_group_get_action (view->details->dir_action_group,
8388 					      FM_ACTION_STOP_VOLUME);
8389 	gtk_action_set_visible (action, show_stop);
8390 	if (show_stop) {
8391 		switch (start_stop_type) {
8392 		default:
8393 		case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8394 			gtk_action_set_label (action, _("_Stop"));
8395 			gtk_action_set_tooltip (action, _("Stop the selected drive"));
8396 			break;
8397 		case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8398 			gtk_action_set_label (action, _("_Safely Remove Drive"));
8399 			gtk_action_set_tooltip (action, _("Safely remove the selected drive"));
8400 			break;
8401 		case G_DRIVE_START_STOP_TYPE_NETWORK:
8402 			gtk_action_set_label (action, _("_Disconnect"));
8403 			gtk_action_set_tooltip (action, _("Disconnect the selected drive"));
8404 			break;
8405 		case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8406 			gtk_action_set_label (action, _("_Stop Multi-disk Drive"));
8407 			gtk_action_set_tooltip (action, _("Stop the selected multi-disk drive"));
8408 			break;
8409 		case G_DRIVE_START_STOP_TYPE_PASSWORD:
8410 			gtk_action_set_label (action, _("_Lock Drive"));
8411 			gtk_action_set_tooltip (action, _("Lock the selected drive"));
8412 			break;
8413 		}
8414 	}
8415 
8416 	action = gtk_action_group_get_action (view->details->dir_action_group,
8417 					      FM_ACTION_POLL);
8418 	gtk_action_set_visible (action, show_poll);
8419 	G_GNUC_END_IGNORE_DEPRECATIONS;
8420 
8421 	show_self_mount = show_self_unmount = show_self_eject =
8422 		show_self_format = show_self_start = show_self_stop = show_self_poll = FALSE;
8423 
8424 	file = fm_directory_view_get_directory_as_file (view);
8425 	file_should_show_self (file,
8426 			       &show_self_mount,
8427 			       &show_self_unmount,
8428 			       &show_self_eject,
8429 			       &show_self_format,
8430 			       &show_self_start,
8431 			       &show_self_stop,
8432 			       &show_self_poll,
8433 			       &self_start_stop_type);
8434 
8435 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8436 	action = gtk_action_group_get_action (view->details->dir_action_group,
8437 					      FM_ACTION_SELF_MOUNT_VOLUME);
8438 	gtk_action_set_visible (action, show_self_mount);
8439 
8440 	action = gtk_action_group_get_action (view->details->dir_action_group,
8441 					      FM_ACTION_SELF_UNMOUNT_VOLUME);
8442 	gtk_action_set_visible (action, show_self_unmount);
8443 
8444 	action = gtk_action_group_get_action (view->details->dir_action_group,
8445 					      FM_ACTION_SELF_EJECT_VOLUME);
8446 	gtk_action_set_visible (action, show_self_eject);
8447 
8448 	action = gtk_action_group_get_action (view->details->dir_action_group,
8449 					      FM_ACTION_SELF_FORMAT_VOLUME);
8450 	gtk_action_set_visible (action, show_self_format);
8451 
8452 	action = gtk_action_group_get_action (view->details->dir_action_group,
8453 					      FM_ACTION_SELF_START_VOLUME);
8454 	gtk_action_set_visible (action, show_self_start);
8455 	if (show_self_start) {
8456 		switch (self_start_stop_type) {
8457 		default:
8458 		case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8459 			gtk_action_set_label (action, _("_Start"));
8460 			gtk_action_set_tooltip (action, _("Start the drive associated with the open folder"));
8461 			break;
8462 		case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8463 			gtk_action_set_label (action, _("_Start"));
8464 			gtk_action_set_tooltip (action, _("Start the drive associated with the open folder"));
8465 			break;
8466 		case G_DRIVE_START_STOP_TYPE_NETWORK:
8467 			gtk_action_set_label (action, _("_Connect"));
8468 			gtk_action_set_tooltip (action, _("Connect to the drive associated with the open folder"));
8469 			break;
8470 		case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8471 			gtk_action_set_label (action, _("_Start Multi-disk Drive"));
8472 			gtk_action_set_tooltip (action, _("Start the multi-disk drive associated with the open folder"));
8473 			break;
8474 		case G_DRIVE_START_STOP_TYPE_PASSWORD:
8475 			gtk_action_set_label (action, _("_Unlock Drive"));
8476 			gtk_action_set_tooltip (action, _("Unlock the drive associated with the open folder"));
8477 			break;
8478 		}
8479 	}
8480 
8481 	action = gtk_action_group_get_action (view->details->dir_action_group,
8482 					      FM_ACTION_SELF_STOP_VOLUME);
8483 	gtk_action_set_visible (action, show_self_stop);
8484 	if (show_self_stop) {
8485 		switch (self_start_stop_type) {
8486 		default:
8487 		case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8488 			gtk_action_set_label (action, _("_Stop"));
8489 			gtk_action_set_tooltip (action, _("_Stop the drive associated with the open folder"));
8490 			break;
8491 		case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8492 			gtk_action_set_label (action, _("_Safely Remove Drive"));
8493 			gtk_action_set_tooltip (action, _("Safely remove the drive associated with the open folder"));
8494 			break;
8495 		case G_DRIVE_START_STOP_TYPE_NETWORK:
8496 			gtk_action_set_label (action, _("_Disconnect"));
8497 			gtk_action_set_tooltip (action, _("Disconnect the drive associated with the open folder"));
8498 			break;
8499 		case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8500 			gtk_action_set_label (action, _("_Stop Multi-disk Drive"));
8501 			gtk_action_set_tooltip (action, _("Stop the multi-disk drive associated with the open folder"));
8502 			break;
8503 		case G_DRIVE_START_STOP_TYPE_PASSWORD:
8504 			gtk_action_set_label (action, _("_Lock Drive"));
8505 			gtk_action_set_tooltip (action, _("Lock the drive associated with the open folder"));
8506 			break;
8507 		}
8508 	}
8509 
8510 	action = gtk_action_group_get_action (view->details->dir_action_group,
8511 					      FM_ACTION_SELF_POLL);
8512 	gtk_action_set_visible (action, show_self_poll);
8513 	G_GNUC_END_IGNORE_DEPRECATIONS;
8514 
8515 }
8516 
8517 static void
real_update_location_menu_volumes(FMDirectoryView * view)8518 real_update_location_menu_volumes (FMDirectoryView *view)
8519 {
8520 	GtkAction *action;
8521 	CajaFile *file;
8522 	gboolean show_mount;
8523 	gboolean show_unmount;
8524 	gboolean show_eject;
8525 	gboolean show_connect;
8526 	gboolean show_format;
8527 	gboolean show_start;
8528 	gboolean show_stop;
8529 	gboolean show_poll;
8530 	GDriveStartStopType start_stop_type;
8531 
8532 	g_assert (FM_IS_DIRECTORY_VIEW (view));
8533 	g_assert (CAJA_IS_FILE (view->details->location_popup_directory_as_file));
8534 
8535 	file = CAJA_FILE (view->details->location_popup_directory_as_file);
8536 	file_should_show_foreach (file,
8537 				  &show_mount,
8538 				  &show_unmount,
8539 				  &show_eject,
8540 				  &show_connect,
8541 				  &show_format,
8542 				  &show_start,
8543 				  &show_stop,
8544 				  &show_poll,
8545 				  &start_stop_type);
8546 
8547 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8548 	action = gtk_action_group_get_action (view->details->dir_action_group,
8549 					      FM_ACTION_LOCATION_MOUNT_VOLUME);
8550 	gtk_action_set_visible (action, show_mount);
8551 
8552 	action = gtk_action_group_get_action (view->details->dir_action_group,
8553 					      FM_ACTION_LOCATION_UNMOUNT_VOLUME);
8554 	gtk_action_set_visible (action, show_unmount);
8555 
8556 	action = gtk_action_group_get_action (view->details->dir_action_group,
8557 					      FM_ACTION_LOCATION_EJECT_VOLUME);
8558 	gtk_action_set_visible (action, show_eject);
8559 
8560 	action = gtk_action_group_get_action (view->details->dir_action_group,
8561 					      FM_ACTION_LOCATION_FORMAT_VOLUME);
8562 	gtk_action_set_visible (action, show_format);
8563 
8564 	action = gtk_action_group_get_action (view->details->dir_action_group,
8565 					      FM_ACTION_LOCATION_START_VOLUME);
8566 	gtk_action_set_visible (action, show_start);
8567 	if (show_start) {
8568 		switch (start_stop_type) {
8569 		default:
8570 		case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8571 			gtk_action_set_label (action, _("_Start"));
8572 			gtk_action_set_tooltip (action, _("Start the selected drive"));
8573 			break;
8574 		case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8575 			gtk_action_set_label (action, _("_Start"));
8576 			gtk_action_set_tooltip (action, _("Start the selected drive"));
8577 			break;
8578 		case G_DRIVE_START_STOP_TYPE_NETWORK:
8579 			gtk_action_set_label (action, _("_Connect"));
8580 			gtk_action_set_tooltip (action, _("Connect to the selected drive"));
8581 			break;
8582 		case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8583 			gtk_action_set_label (action, _("_Start Multi-disk Drive"));
8584 			gtk_action_set_tooltip (action, _("Start the selected multi-disk drive"));
8585 			break;
8586 		case G_DRIVE_START_STOP_TYPE_PASSWORD:
8587 			gtk_action_set_label (action, _("_Unlock Drive"));
8588 			gtk_action_set_tooltip (action, _("Unlock the selected drive"));
8589 			break;
8590 		}
8591 	}
8592 
8593 	action = gtk_action_group_get_action (view->details->dir_action_group,
8594 					      FM_ACTION_LOCATION_STOP_VOLUME);
8595 	gtk_action_set_visible (action, show_stop);
8596 	if (show_stop) {
8597 		switch (start_stop_type) {
8598 		default:
8599 		case G_DRIVE_START_STOP_TYPE_UNKNOWN:
8600 			gtk_action_set_label (action, _("_Stop"));
8601 			gtk_action_set_tooltip (action, _("Stop the selected volume"));
8602 			break;
8603 		case G_DRIVE_START_STOP_TYPE_SHUTDOWN:
8604 			gtk_action_set_label (action, _("_Safely Remove Drive"));
8605 			gtk_action_set_tooltip (action, _("Safely remove the selected drive"));
8606 			break;
8607 		case G_DRIVE_START_STOP_TYPE_NETWORK:
8608 			gtk_action_set_label (action, _("_Disconnect"));
8609 			gtk_action_set_tooltip (action, _("Disconnect the selected drive"));
8610 			break;
8611 		case G_DRIVE_START_STOP_TYPE_MULTIDISK:
8612 			gtk_action_set_label (action, _("_Stop Multi-disk Drive"));
8613 			gtk_action_set_tooltip (action, _("Stop the selected multi-disk drive"));
8614 			break;
8615 		case G_DRIVE_START_STOP_TYPE_PASSWORD:
8616 			gtk_action_set_label (action, _("_Lock Drive"));
8617 			gtk_action_set_tooltip (action, _("Lock the selected drive"));
8618 			break;
8619 		}
8620 	}
8621 
8622 	action = gtk_action_group_get_action (view->details->dir_action_group,
8623 					      FM_ACTION_LOCATION_POLL);
8624 	gtk_action_set_visible (action, show_poll);
8625 	G_GNUC_END_IGNORE_DEPRECATIONS;
8626 }
8627 
8628 /* TODO: we should split out this routine into two functions:
8629  * Update on clipboard changes
8630  * Update on selection changes
8631  */
8632 static void
real_update_paste_menu(FMDirectoryView * view,GList * selection,gint selection_count)8633 real_update_paste_menu (FMDirectoryView *view,
8634 			GList *selection,
8635 			gint selection_count)
8636 {
8637 	gboolean can_paste_files_into;
8638 	gboolean selection_is_read_only;
8639 	gboolean is_read_only;
8640 	GtkAction *action;
8641 
8642 	selection_is_read_only = selection_count == 1 &&
8643 		(!caja_file_can_write (CAJA_FILE (selection->data)) &&
8644 		 !caja_file_has_activation_uri (CAJA_FILE (selection->data)));
8645 
8646 	is_read_only = fm_directory_view_is_read_only (view);
8647 
8648 	can_paste_files_into = (selection_count == 1 &&
8649 	                        can_paste_into_file (CAJA_FILE (selection->data)));
8650 
8651 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8652 	action = gtk_action_group_get_action (view->details->dir_action_group,
8653 					      FM_ACTION_PASTE);
8654 	gtk_action_set_sensitive (action, !is_read_only);
8655 
8656 	action = gtk_action_group_get_action (view->details->dir_action_group,
8657 					      FM_ACTION_PASTE_FILES_INTO);
8658 	gtk_action_set_visible (action, can_paste_files_into);
8659 	gtk_action_set_sensitive (action, !selection_is_read_only);
8660 	G_GNUC_END_IGNORE_DEPRECATIONS;
8661 
8662 	/* Ask the clipboard */
8663 	g_object_ref (view); /* Need to keep the object alive until we get the reply */
8664 	gtk_clipboard_request_targets (caja_clipboard_get (GTK_WIDGET (view)),
8665 				       clipboard_targets_received,
8666 				       view);
8667 }
8668 
8669 static void
real_update_location_menu(FMDirectoryView * view)8670 real_update_location_menu (FMDirectoryView *view)
8671 {
8672 	GtkAction *action;
8673 	CajaFile *file;
8674 	gboolean is_special_link;
8675 	gboolean is_desktop_or_home_dir;
8676 	gboolean can_delete_file, show_delete;
8677 	gboolean show_separate_delete_command;
8678 	gboolean show_open_folder_window;
8679 	gboolean show_open_in_new_tab;
8680 	GList l;
8681 	char *label;
8682 	char *tip;
8683 
8684 	show_open_folder_window = FALSE;
8685 	show_open_in_new_tab = FALSE;
8686 
8687 	if (caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION) {
8688 		if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
8689 			label = _("Open in New _Window");
8690 		} else {
8691 			label = _("Browse in New _Window");
8692 			show_open_folder_window = TRUE;
8693 		}
8694 
8695 		show_open_in_new_tab = TRUE;
8696 	} else {
8697 		label = g_strdup (ngettext ("_Browse Folder",
8698 					    "_Browse Folders", 1));
8699 	}
8700 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8701 	action = gtk_action_group_get_action (view->details->dir_action_group,
8702 					      FM_ACTION_LOCATION_OPEN_ALTERNATE);
8703 	g_object_set (action,
8704 		      "label", label,
8705 		      NULL);
8706 
8707 	action = gtk_action_group_get_action (view->details->dir_action_group,
8708 					      FM_ACTION_LOCATION_OPEN_IN_NEW_TAB);
8709 	gtk_action_set_visible (action, show_open_in_new_tab);
8710 	G_GNUC_END_IGNORE_DEPRECATIONS;
8711 
8712 	if (show_open_in_new_tab) {
8713 		if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
8714 			label = _("Open in New _Tab");
8715 		} else {
8716 			label = _("Browse in New _Tab");
8717 		}
8718 		g_object_set (action,
8719 			      "label", label,
8720 			      NULL);
8721 	}
8722 
8723 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8724 	action = gtk_action_group_get_action (view->details->dir_action_group,
8725 					      FM_ACTION_LOCATION_OPEN_FOLDER_WINDOW);
8726 	gtk_action_set_visible (action, show_open_folder_window);
8727 	G_GNUC_END_IGNORE_DEPRECATIONS;
8728 
8729 	file = view->details->location_popup_directory_as_file;
8730 	g_assert (CAJA_IS_FILE (file));
8731 	g_assert (caja_file_check_if_ready (file, CAJA_FILE_ATTRIBUTE_INFO |
8732 						      CAJA_FILE_ATTRIBUTE_MOUNT |
8733 						      CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO));
8734 
8735 	is_special_link = CAJA_IS_DESKTOP_ICON_FILE (file);
8736 	is_desktop_or_home_dir = caja_file_is_home (file)
8737 		|| caja_file_is_desktop_directory (file);
8738 
8739 	can_delete_file =
8740 		caja_file_can_delete (file) &&
8741 		!is_special_link &&
8742 		!is_desktop_or_home_dir;
8743 
8744 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8745 	action = gtk_action_group_get_action (view->details->dir_action_group,
8746 					      FM_ACTION_LOCATION_CUT);
8747 	gtk_action_set_sensitive (action, can_delete_file);
8748 
8749 	action = gtk_action_group_get_action (view->details->dir_action_group,
8750 					      FM_ACTION_LOCATION_PASTE_FILES_INTO);
8751 	G_GNUC_END_IGNORE_DEPRECATIONS;
8752 	g_object_set_data (G_OBJECT (action),
8753 			   "can-paste-according-to-destination",
8754 			   GINT_TO_POINTER (can_paste_into_file (file)));
8755 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8756 	gtk_action_set_sensitive (action,
8757 				  GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
8758 						   "can-paste-according-to-clipboard")) &&
8759 				  GPOINTER_TO_INT (g_object_get_data (G_OBJECT (action),
8760 						   "can-paste-according-to-destination")));
8761 	G_GNUC_END_IGNORE_DEPRECATIONS;
8762 
8763 	show_delete = TRUE;
8764 
8765 	if (file != NULL &&
8766 	    caja_file_is_in_trash (file)) {
8767 		if (caja_file_is_self_owned (file)) {
8768 			show_delete = FALSE;
8769 		}
8770 
8771 		label = _("_Delete Permanently");
8772 		tip = _("Delete the open folder permanently");
8773 		show_separate_delete_command = FALSE;
8774 	} else {
8775 		label = _("Mo_ve to Trash");
8776 		tip = _("Move the open folder to the Trash");
8777 		show_separate_delete_command = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ENABLE_DELETE);
8778 	}
8779 
8780 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8781 	action = gtk_action_group_get_action (view->details->dir_action_group,
8782 					      FM_ACTION_LOCATION_TRASH);
8783 	g_object_set (action,
8784 		      "label", label,
8785 		      "tooltip", tip,
8786 		      "icon-name", (file != NULL &&
8787 				   caja_file_is_in_trash (file)) ?
8788 					CAJA_ICON_DELETE : CAJA_ICON_TRASH_FULL,
8789 		      NULL);
8790 	gtk_action_set_sensitive (action, can_delete_file);
8791 	gtk_action_set_visible (action, show_delete);
8792 
8793 	action = gtk_action_group_get_action (view->details->dir_action_group,
8794 					      FM_ACTION_LOCATION_DELETE);
8795 	gtk_action_set_visible (action, show_separate_delete_command);
8796 	if (show_separate_delete_command) {
8797 		gtk_action_set_sensitive (action, can_delete_file);
8798 		g_object_set (action,
8799 			      "icon-name", CAJA_ICON_DELETE,
8800 			      "sensitive", can_delete_file,
8801 			      NULL);
8802 	}
8803 
8804 	action = gtk_action_group_get_action (view->details->dir_action_group,
8805 					      FM_ACTION_LOCATION_RESTORE_FROM_TRASH);
8806 	G_GNUC_END_IGNORE_DEPRECATIONS;
8807 	l.prev = NULL;
8808 	l.next = NULL;
8809 	l.data = file;
8810 	update_restore_from_trash_action (action, &l, TRUE);
8811 
8812 	real_update_location_menu_volumes (view);
8813 
8814 	/* we silently assume that fm_directory_view_supports_properties always returns the same value.
8815 	 * Therefore, we don't update the sensitivity of FM_ACTION_LOCATION_PROPERTIES */
8816 }
8817 
8818 static void
clipboard_changed_callback(CajaClipboardMonitor * monitor,FMDirectoryView * view)8819 clipboard_changed_callback (CajaClipboardMonitor *monitor, FMDirectoryView *view)
8820 {
8821 	GList *selection;
8822 	gint selection_count;
8823 
8824 	if (!view->details->active) {
8825 		return;
8826 	}
8827 
8828 	selection = fm_directory_view_get_selection (view);
8829 	selection_count = g_list_length (selection);
8830 
8831 	real_update_paste_menu (view, selection, selection_count);
8832 
8833 	caja_file_list_free (selection);
8834 
8835 }
8836 
8837 static gboolean
can_delete_all(GList * files)8838 can_delete_all (GList *files)
8839 {
8840 	GList *l;
8841 	CajaFile *file = NULL;
8842 
8843 	for (l = files; l != NULL; l = l->next) {
8844 		file = l->data;
8845 		if (!caja_file_can_delete (file)) {
8846 			return FALSE;
8847 		}
8848 	}
8849 	return TRUE;
8850 }
8851 
8852 static gboolean
has_writable_extra_pane(FMDirectoryView * view)8853 has_writable_extra_pane (FMDirectoryView *view)
8854 {
8855 	FMDirectoryView *other_view;
8856 
8857 	other_view = get_directory_view_of_extra_pane (view);
8858 	if (other_view != NULL) {
8859 		return !fm_directory_view_is_read_only (other_view);
8860 	}
8861 	return FALSE;
8862 }
8863 
8864 static void
real_update_menus(FMDirectoryView * view)8865 real_update_menus (FMDirectoryView *view)
8866 {
8867 	GList *selection, *l;
8868 	gint selection_count;
8869 	const char *tip, *label;
8870 	char *label_with_underscore;
8871 	gboolean selection_contains_special_link;
8872 	gboolean selection_contains_desktop_or_home_dir;
8873 	gboolean can_create_files;
8874 	gboolean can_delete_files;
8875 	gboolean can_copy_files;
8876 	gboolean can_link_files;
8877 	gboolean can_duplicate_files;
8878 	gboolean show_separate_delete_command;
8879 	gboolean vfolder_directory;
8880 	gboolean disable_command_line;
8881 	gboolean show_open_alternate;
8882 	gboolean can_open;
8883 	gboolean show_app;
8884 	gboolean show_save_search;
8885 	gboolean save_search_sensitive;
8886 	gboolean show_save_search_as;
8887 	gboolean show_open_folder_window;
8888 	GtkAction *action;
8889 	GAppInfo *app;
8890 	GIcon *app_icon;
8891 	GtkWidget *menuitem;
8892 	gboolean next_pane_is_writable;
8893 	gboolean show_properties;
8894 
8895 	selection = fm_directory_view_get_selection (view);
8896 	selection_count = g_list_length (selection);
8897 
8898 	selection_contains_special_link = special_link_in_selection (view);
8899 	selection_contains_desktop_or_home_dir = desktop_or_home_dir_in_selection (view);
8900 
8901 	can_create_files = fm_directory_view_supports_creating_files (view);
8902 	can_delete_files =
8903 		can_delete_all (selection) &&
8904 		selection_count != 0 &&
8905 		!selection_contains_special_link &&
8906 		!selection_contains_desktop_or_home_dir;
8907 	can_copy_files = selection_count != 0
8908 		&& !selection_contains_special_link;
8909 
8910 	can_duplicate_files = can_create_files && can_copy_files;
8911 	can_link_files = can_create_files && can_copy_files;
8912 
8913 	vfolder_directory = we_are_in_vfolder_desktop_dir (view);
8914 
8915 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
8916 	action = gtk_action_group_get_action (view->details->dir_action_group,
8917 					      FM_ACTION_RENAME);
8918 	gtk_action_set_sensitive (action,
8919 				  selection_count == 1 &&
8920 				  fm_directory_view_can_rename_file (view, selection->data));
8921 
8922 	action = gtk_action_group_get_action (view->details->dir_action_group,
8923 					      FM_ACTION_NEW_FOLDER);
8924 	gtk_action_set_sensitive (action, can_create_files);
8925 
8926 	action = gtk_action_group_get_action (view->details->dir_action_group,
8927 					      FM_ACTION_OPEN);
8928 	gtk_action_set_sensitive (action, selection_count != 0);
8929 	G_GNUC_END_IGNORE_DEPRECATIONS;
8930 
8931 	can_open = show_app = selection_count != 0;
8932 
8933 	for (l = selection; l != NULL; l = l->next) {
8934 		CajaFile *file;
8935 
8936 		file = CAJA_FILE (l->data);
8937 
8938 		/* Double-check if the files' MIME types have changed before we
8939 		   commit to a choice of applications for them. This can happen
8940 		   if, for instance, a file was originally created with 0 bytes
8941 		   and then content was added to it later-- it will change from
8942 		   plaintext to something else. */
8943 		caja_file_refresh_info (file);
8944 
8945 		if (!caja_mime_file_opens_in_external_app (file)) {
8946 			show_app = FALSE;
8947 		}
8948 
8949 		if (!show_app) {
8950 			break;
8951 		}
8952 	}
8953 
8954 	label_with_underscore = NULL;
8955 
8956 	app = NULL;
8957 	app_icon = NULL;
8958 
8959 	if (can_open && show_app) {
8960 		app = caja_mime_get_default_application_for_files (selection);
8961 	}
8962 
8963 	if (app != NULL) {
8964 		char *escaped_app;
8965 
8966 		escaped_app = eel_str_double_underscores (g_app_info_get_display_name (app));
8967 		label_with_underscore = g_strdup_printf (_("_Open With %s"),
8968 							 escaped_app);
8969 
8970 		app_icon = g_app_info_get_icon (app);
8971 		if (app_icon != NULL) {
8972 			g_object_ref (app_icon);
8973 		}
8974 
8975 		g_free (escaped_app);
8976 		g_object_unref (app);
8977 	}
8978 
8979 	g_object_set (action, "label",
8980 		      label_with_underscore ? label_with_underscore : _("_Open"),
8981 		      NULL);
8982 
8983 	menuitem = gtk_ui_manager_get_widget (
8984 			caja_window_info_get_ui_manager (view->details->window),
8985 			FM_DIRECTORY_VIEW_MENU_PATH_OPEN);
8986 
8987 	/* Only force displaying the icon if it is an application icon */
8988 	gtk_image_menu_item_set_always_show_image (
8989 		GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL);
8990 
8991 	menuitem = gtk_ui_manager_get_widget (
8992 			caja_window_info_get_ui_manager (view->details->window),
8993 			FM_DIRECTORY_VIEW_POPUP_PATH_OPEN);
8994 
8995 	/* Only force displaying the icon if it is an application icon */
8996 	gtk_image_menu_item_set_always_show_image (
8997 		GTK_IMAGE_MENU_ITEM (menuitem), app_icon != NULL);
8998 
8999 	if (app_icon == NULL) {
9000 		app_icon = g_themed_icon_new ("document-open");
9001 	}
9002 
9003 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9004 	gtk_action_set_gicon (action, app_icon);
9005 	g_object_unref (app_icon);
9006 
9007 	gtk_action_set_visible (action, can_open);
9008 	G_GNUC_END_IGNORE_DEPRECATIONS;
9009 
9010 	g_free (label_with_underscore);
9011 
9012 	show_open_alternate = file_list_all_are_folders (selection) &&
9013 				selection_count > 0 &&
9014 				!(caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_DESKTOP &&
9015 					g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ALWAYS_USE_BROWSER));
9016 	show_open_folder_window = FALSE;
9017 	if (caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION) {
9018 		if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
9019 			if (selection_count == 0 || selection_count == 1) {
9020 				label_with_underscore = g_strdup (_("Open in New _Window"));
9021 			} else {
9022 				label_with_underscore = g_strdup_printf (ngettext("Open in %'d New _Window",
9023 										  "Open in %'d New _Windows",
9024 										  selection_count),
9025 									 selection_count);
9026 			}
9027 		} else {
9028 			if (selection_count == 0 || selection_count == 1) {
9029 				label_with_underscore = g_strdup (_("Browse in New _Window"));
9030 			} else {
9031 				label_with_underscore = g_strdup_printf (ngettext("Browse in %'d New _Window",
9032 										  "Browse in %'d New _Windows",
9033 										  selection_count),
9034 									 selection_count);
9035 			}
9036 			show_open_folder_window = show_open_alternate;
9037 		}
9038 	} else {
9039 		label_with_underscore = g_strdup (ngettext ("_Browse Folder",
9040 							    "_Browse Folders",
9041 							    selection_count));
9042 	}
9043 
9044 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9045 	action = gtk_action_group_get_action (view->details->dir_action_group,
9046 					      FM_ACTION_OPEN_ALTERNATE);
9047 	g_object_set (action, "label",
9048 		      label_with_underscore,
9049 		      NULL);
9050 	g_free (label_with_underscore);
9051 
9052 	gtk_action_set_sensitive (action,  selection_count != 0);
9053 	gtk_action_set_visible (action, show_open_alternate);
9054 	G_GNUC_END_IGNORE_DEPRECATIONS;
9055 
9056 	/* Open in New Tab action */
9057 	if (caja_window_info_get_window_type (view->details->window) == CAJA_WINDOW_NAVIGATION) {
9058 
9059 		if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
9060 			if (selection_count == 0 || selection_count == 1) {
9061 				label_with_underscore = g_strdup (_("Open in New _Tab"));
9062 			} else {
9063 				label_with_underscore = g_strdup_printf (ngettext("Open in %'d New _Tab",
9064 										  "Open in %'d New _Tabs",
9065 										  selection_count),
9066 									 selection_count);
9067 			}
9068 		} else {
9069 			if (selection_count == 0 || selection_count == 1) {
9070 				label_with_underscore = g_strdup (_("Browse in New _Tab"));
9071 			} else {
9072 				label_with_underscore = g_strdup_printf (ngettext("Browse in %'d New _Tab",
9073 										  "Browse in %'d New _Tabs",
9074 										  selection_count),
9075 									 selection_count);
9076 			}
9077 		}
9078 		G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9079 		action = gtk_action_group_get_action (view->details->dir_action_group,
9080 						      FM_ACTION_OPEN_IN_NEW_TAB);
9081 		gtk_action_set_sensitive (action, selection_count != 0);
9082 		gtk_action_set_visible (action, show_open_alternate);
9083 		g_object_set (action, "label",
9084 			      label_with_underscore,
9085 			      NULL);
9086 		g_free (label_with_underscore);
9087 	} else {
9088 		action = gtk_action_group_get_action (view->details->dir_action_group,
9089 						      FM_ACTION_OPEN_IN_NEW_TAB);
9090 		gtk_action_set_visible (action, FALSE);
9091 		G_GNUC_END_IGNORE_DEPRECATIONS;
9092 	}
9093 
9094 	/* next pane actions, only in navigation mode */
9095 	if (caja_window_info_get_window_type (view->details->window) != CAJA_WINDOW_NAVIGATION) {
9096 		G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9097 		action = gtk_action_group_get_action (view->details->dir_action_group,
9098 						      FM_ACTION_COPY_TO_NEXT_PANE);
9099 		gtk_action_set_visible (action, FALSE);
9100 		action = gtk_action_group_get_action (view->details->dir_action_group,
9101 						      FM_ACTION_MOVE_TO_NEXT_PANE);
9102 		gtk_action_set_visible (action, FALSE);
9103 	}
9104 
9105 	action = gtk_action_group_get_action (view->details->dir_action_group,
9106 					      FM_ACTION_OPEN_FOLDER_WINDOW);
9107 	gtk_action_set_visible (action, show_open_folder_window);
9108 	G_GNUC_END_IGNORE_DEPRECATIONS;
9109 
9110 	/* Broken into its own function just for convenience */
9111 	reset_open_with_menu (view, selection);
9112 	reset_extension_actions_menu (view, selection);
9113 
9114 	if (all_selected_items_in_trash (view)) {
9115 		label = _("_Delete Permanently");
9116 		tip = _("Delete all selected items permanently");
9117 		show_separate_delete_command = FALSE;
9118 	} else {
9119 		label = _("Mo_ve to Trash");
9120 		tip = _("Move each selected item to the Trash");
9121 		show_separate_delete_command = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ENABLE_DELETE);
9122 	}
9123 
9124 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9125 	action = gtk_action_group_get_action (view->details->dir_action_group,
9126 					      FM_ACTION_TRASH);
9127 	g_object_set (action,
9128 		      "label", label,
9129 		      "tooltip", tip,
9130 		      "icon-name", all_selected_items_in_trash (view) ?
9131 					CAJA_ICON_DELETE : CAJA_ICON_TRASH_FULL,
9132 		      NULL);
9133 	gtk_action_set_sensitive (action, can_delete_files);
9134 
9135 	action = gtk_action_group_get_action (view->details->dir_action_group,
9136 					      FM_ACTION_DELETE);
9137 	gtk_action_set_visible (action, show_separate_delete_command);
9138 
9139 	if (show_separate_delete_command) {
9140 		g_object_set (action,
9141 			      "label", _("_Delete"),
9142 			      "icon-name", CAJA_ICON_DELETE,
9143 			      NULL);
9144 	}
9145 	gtk_action_set_sensitive (action, can_delete_files);
9146 
9147 
9148 	action = gtk_action_group_get_action (view->details->dir_action_group,
9149 					      FM_ACTION_RESTORE_FROM_TRASH);
9150 	update_restore_from_trash_action (action, selection, FALSE);
9151 
9152 	action = gtk_action_group_get_action (view->details->dir_action_group,
9153 					      FM_ACTION_DUPLICATE);
9154 	gtk_action_set_sensitive (action, can_duplicate_files);
9155 
9156 	action = gtk_action_group_get_action (view->details->dir_action_group,
9157 					      FM_ACTION_CREATE_LINK);
9158 	gtk_action_set_sensitive (action, can_link_files);
9159 	G_GNUC_END_IGNORE_DEPRECATIONS;
9160 	g_object_set (action, "label",
9161 		      ngettext ("Ma_ke Link",
9162 			      	"Ma_ke Links",
9163 				selection_count),
9164 		      NULL);
9165 
9166 	show_properties = (!FM_IS_DESKTOP_ICON_VIEW (view) || selection_count > 0) &&
9167 			   fm_directory_view_supports_properties (view);
9168 
9169 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9170 	action = gtk_action_group_get_action (view->details->dir_action_group,
9171 					      FM_ACTION_PROPERTIES);
9172 
9173 	gtk_action_set_sensitive (action, show_properties);
9174 
9175 	if (selection_count == 0) {
9176 		gtk_action_set_tooltip (action, _("View or modify the properties of the open folder"));
9177 	} else {
9178 		gtk_action_set_tooltip (action, _("View or modify the properties of each selected item"));
9179 	}
9180 
9181 	gtk_action_set_visible (action, show_properties);
9182 
9183 	action = gtk_action_group_get_action (view->details->dir_action_group,
9184 					      FM_ACTION_PROPERTIES_ACCEL);
9185 
9186 	gtk_action_set_sensitive (action, show_properties);
9187 
9188 	action = gtk_action_group_get_action (view->details->dir_action_group,
9189 					      FM_ACTION_EMPTY_TRASH);
9190 	g_object_set (action,
9191 		      "label", _("E_mpty Trash"),
9192 		      NULL);
9193 	gtk_action_set_sensitive (action, !caja_trash_monitor_is_empty ());
9194 	gtk_action_set_visible (action, should_show_empty_trash (view));
9195 	G_GNUC_END_IGNORE_DEPRECATIONS;
9196 
9197 	show_save_search = FALSE;
9198 	save_search_sensitive = FALSE;
9199 	show_save_search_as = FALSE;
9200 	if (view->details->model &&
9201 	    CAJA_IS_SEARCH_DIRECTORY (view->details->model)) {
9202 		CajaSearchDirectory *search;
9203 
9204 		search = CAJA_SEARCH_DIRECTORY (view->details->model);
9205 		if (caja_search_directory_is_saved_search (search)) {
9206 			show_save_search = TRUE;
9207 			save_search_sensitive = caja_search_directory_is_modified (search);
9208 		} else {
9209 			show_save_search_as = TRUE;
9210 		}
9211 	}
9212 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9213 	action = gtk_action_group_get_action (view->details->dir_action_group,
9214 					      FM_ACTION_SAVE_SEARCH);
9215 	gtk_action_set_visible (action, show_save_search);
9216 	gtk_action_set_sensitive (action, save_search_sensitive);
9217 	action = gtk_action_group_get_action (view->details->dir_action_group,
9218 					      FM_ACTION_SAVE_SEARCH_AS);
9219 	gtk_action_set_visible (action, show_save_search_as);
9220 
9221 
9222 	action = gtk_action_group_get_action (view->details->dir_action_group,
9223 					      FM_ACTION_SELECT_ALL);
9224 	gtk_action_set_sensitive (action, !fm_directory_view_is_empty (view));
9225 
9226 	action = gtk_action_group_get_action (view->details->dir_action_group,
9227 					      FM_ACTION_SELECT_PATTERN);
9228 	gtk_action_set_sensitive (action, !fm_directory_view_is_empty (view));
9229 
9230 	action = gtk_action_group_get_action (view->details->dir_action_group,
9231 					      FM_ACTION_INVERT_SELECTION);
9232 	gtk_action_set_sensitive (action, !fm_directory_view_is_empty (view));
9233 
9234 	action = gtk_action_group_get_action (view->details->dir_action_group,
9235 					      FM_ACTION_CUT);
9236 	gtk_action_set_sensitive (action, can_delete_files);
9237 
9238 	action = gtk_action_group_get_action (view->details->dir_action_group,
9239 					      FM_ACTION_COPY);
9240 	gtk_action_set_sensitive (action, can_copy_files);
9241 	G_GNUC_END_IGNORE_DEPRECATIONS;
9242 
9243 	real_update_paste_menu (view, selection, selection_count);
9244 
9245 	disable_command_line = g_settings_get_boolean (mate_lockdown_preferences, CAJA_PREFERENCES_LOCKDOWN_COMMAND_LINE);
9246 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9247 	action = gtk_action_group_get_action (view->details->dir_action_group,
9248 					      FM_ACTION_NEW_LAUNCHER);
9249 	gtk_action_set_visible (action, vfolder_directory && !disable_command_line);
9250 	gtk_action_set_sensitive (action, can_create_files);
9251 	G_GNUC_END_IGNORE_DEPRECATIONS;
9252 
9253 	real_update_menus_volumes (view, selection, selection_count);
9254 
9255 	undo_update_menu (view);
9256 
9257 	caja_file_list_free (selection);
9258 
9259 	if (view->details->scripts_invalid) {
9260 		update_scripts_menu (view);
9261 	}
9262 
9263 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9264 	action = gtk_action_group_get_action (view->details->dir_action_group,
9265 					      FM_ACTION_NEW_DOCUMENTS);
9266 	gtk_action_set_sensitive (action, can_create_files);
9267 	G_GNUC_END_IGNORE_DEPRECATIONS;
9268 
9269 	if (can_create_files && view->details->templates_invalid) {
9270 		update_templates_menu (view);
9271 	}
9272 
9273 	next_pane_is_writable = has_writable_extra_pane (view);
9274 
9275 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
9276 	/* next pane: works if file is copyable, and next pane is writable */
9277 	action = gtk_action_group_get_action (view->details->dir_action_group,
9278 					      FM_ACTION_COPY_TO_NEXT_PANE);
9279 	gtk_action_set_sensitive (action, can_copy_files && next_pane_is_writable);
9280 
9281 	/* move to next pane: works if file is cuttable, and next pane is writable */
9282 	action = gtk_action_group_get_action (view->details->dir_action_group,
9283 					      FM_ACTION_MOVE_TO_NEXT_PANE);
9284 	gtk_action_set_sensitive (action, can_delete_files && next_pane_is_writable);
9285 
9286 
9287 	action = gtk_action_group_get_action (view->details->dir_action_group,
9288 					      FM_ACTION_COPY_TO_HOME);
9289 	gtk_action_set_sensitive (action, can_copy_files);
9290 	action = gtk_action_group_get_action (view->details->dir_action_group,
9291 					      FM_ACTION_COPY_TO_DESKTOP);
9292 	gtk_action_set_sensitive (action, can_copy_files);
9293 
9294 	action = gtk_action_group_get_action (view->details->dir_action_group,
9295 					      FM_ACTION_MOVE_TO_HOME);
9296 	gtk_action_set_sensitive (action, can_delete_files);
9297 	action = gtk_action_group_get_action (view->details->dir_action_group,
9298 					      FM_ACTION_MOVE_TO_DESKTOP);
9299 	gtk_action_set_sensitive (action, can_delete_files);
9300 
9301 	action = gtk_action_group_get_action (view->details->dir_action_group,
9302 					      "CopyToMenu");
9303 	gtk_action_set_sensitive (action, can_copy_files);
9304 	action = gtk_action_group_get_action (view->details->dir_action_group,
9305 					      "MoveToMenu");
9306 	gtk_action_set_sensitive (action, can_delete_files);
9307 	G_GNUC_END_IGNORE_DEPRECATIONS;
9308 }
9309 
9310 /**
9311  * fm_directory_view_pop_up_selection_context_menu
9312  *
9313  * Pop up a context menu appropriate to the selected items.
9314  * @view: FMDirectoryView of interest.
9315  * @event: The event that triggered this context menu.
9316  *
9317  * Return value: CajaDirectory for this view.
9318  *
9319  **/
9320 void
fm_directory_view_pop_up_selection_context_menu(FMDirectoryView * view,GdkEventButton * event)9321 fm_directory_view_pop_up_selection_context_menu  (FMDirectoryView *view,
9322 						  GdkEventButton  *event)
9323 {
9324 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9325 
9326 	/* Make the context menu items not flash as they update to proper disabled,
9327 	 * etc. states by forcing menus to update now.
9328 	 */
9329 	update_menus_if_pending (view);
9330 
9331 	update_context_menu_position_from_event (view, event);
9332 
9333 	/* FIXME: passing event from here won't work
9334 	 * for gtk_menu_popup_at_pointer (in eel_pop_up_context_menu() )
9335 	 * if the menu is being triggered from here by the menu key
9336 	 */
9337 	eel_pop_up_context_menu (create_popup_menu
9338 	                         (view, FM_DIRECTORY_VIEW_POPUP_PATH_SELECTION),
9339 	                          NULL);
9340 }
9341 
9342 /**
9343  * fm_directory_view_pop_up_background_context_menu
9344  *
9345  * Pop up a context menu appropriate to the view globally at the last right click location.
9346  * @view: FMDirectoryView of interest.
9347  *
9348  * Return value: CajaDirectory for this view.
9349  *
9350  **/
9351 void
fm_directory_view_pop_up_background_context_menu(FMDirectoryView * view,GdkEventButton * event)9352 fm_directory_view_pop_up_background_context_menu (FMDirectoryView *view,
9353 						  GdkEventButton  *event)
9354 {
9355 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9356 
9357 	/* Make the context menu items not flash as they update to proper disabled,
9358 	 * etc. states by forcing menus to update now.
9359 	 */
9360 	update_menus_if_pending (view);
9361 
9362 	update_context_menu_position_from_event (view, event);
9363 
9364 	eel_pop_up_context_menu (create_popup_menu
9365 	                         (view, FM_DIRECTORY_VIEW_POPUP_PATH_BACKGROUND),
9366 	                         event);
9367 }
9368 
9369 static void
real_pop_up_location_context_menu(FMDirectoryView * view)9370 real_pop_up_location_context_menu (FMDirectoryView *view)
9371 {
9372 	/* always update the menu before showing it. Shouldn't be too expensive. */
9373 	real_update_location_menu (view);
9374 
9375 	update_context_menu_position_from_event (view, view->details->location_popup_event);
9376 
9377 	eel_pop_up_context_menu (create_popup_menu
9378 	                         (view, FM_DIRECTORY_VIEW_POPUP_PATH_LOCATION),
9379 	                         view->details->location_popup_event);
9380 }
9381 
9382 static void
location_popup_file_attributes_ready(CajaFile * file,gpointer data)9383 location_popup_file_attributes_ready (CajaFile *file,
9384 				      gpointer      data)
9385 {
9386 	FMDirectoryView *view;
9387 
9388 	view = FM_DIRECTORY_VIEW (data);
9389 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9390 
9391 	g_assert (file == view->details->location_popup_directory_as_file);
9392 
9393 	real_pop_up_location_context_menu (view);
9394 }
9395 
9396 static void
unschedule_pop_up_location_context_menu(FMDirectoryView * view)9397 unschedule_pop_up_location_context_menu (FMDirectoryView *view)
9398 {
9399 	if (view->details->location_popup_directory_as_file != NULL) {
9400 		g_assert (CAJA_IS_FILE (view->details->location_popup_directory_as_file));
9401 		caja_file_cancel_call_when_ready (view->details->location_popup_directory_as_file,
9402 						      location_popup_file_attributes_ready,
9403 						      view);
9404 		caja_file_unref (view->details->location_popup_directory_as_file);
9405 		view->details->location_popup_directory_as_file = NULL;
9406 	}
9407 }
9408 
9409 static void
schedule_pop_up_location_context_menu(FMDirectoryView * view,GdkEventButton * event,CajaFile * file)9410 schedule_pop_up_location_context_menu (FMDirectoryView *view,
9411 				       GdkEventButton  *event,
9412 				       CajaFile    *file)
9413 {
9414 	g_assert (CAJA_IS_FILE (file));
9415 
9416 	if (view->details->location_popup_event != NULL) {
9417 		gdk_event_free ((GdkEvent *) view->details->location_popup_event);
9418 	}
9419 	view->details->location_popup_event = (GdkEventButton *) gdk_event_copy ((GdkEvent *)event);
9420 
9421 	if (file == view->details->location_popup_directory_as_file) {
9422 		if (caja_file_check_if_ready (file, CAJA_FILE_ATTRIBUTE_INFO |
9423 							CAJA_FILE_ATTRIBUTE_MOUNT |
9424 							CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO)) {
9425 			real_pop_up_location_context_menu (view);
9426 		}
9427 	} else {
9428 		unschedule_pop_up_location_context_menu (view);
9429 
9430 		view->details->location_popup_directory_as_file = caja_file_ref (file);
9431 		caja_file_call_when_ready (view->details->location_popup_directory_as_file,
9432 					       CAJA_FILE_ATTRIBUTE_INFO |
9433 					       CAJA_FILE_ATTRIBUTE_MOUNT |
9434 					       CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO,
9435 					       location_popup_file_attributes_ready,
9436 					       view);
9437 	}
9438 }
9439 
9440 /**
9441  * fm_directory_view_pop_up_location_context_menu
9442  *
9443  * Pop up a context menu appropriate to the view globally.
9444  * @view: FMDirectoryView of interest.
9445  * @event: GdkEventButton triggering the popup.
9446  * @location: The location the popup-menu should be created for,
9447  * or NULL for the currently displayed location.
9448  *
9449  **/
9450 void
fm_directory_view_pop_up_location_context_menu(FMDirectoryView * view,GdkEventButton * event,const char * location)9451 fm_directory_view_pop_up_location_context_menu (FMDirectoryView *view,
9452 						GdkEventButton  *event,
9453 						const char      *location)
9454 {
9455 	CajaFile *file;
9456 
9457 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9458 
9459 	if (location != NULL) {
9460 		file = caja_file_get_by_uri (location);
9461 	} else {
9462 		file = caja_file_ref (view->details->directory_as_file);
9463 	}
9464 
9465 	if (file != NULL) {
9466 		schedule_pop_up_location_context_menu (view, event, file);
9467 		caja_file_unref (file);
9468 	}
9469 }
9470 
9471 static void
fm_directory_view_drop_proxy_received_uris(FMDirectoryView * view,const GList * source_uri_list,const char * target_uri,GdkDragAction action)9472 fm_directory_view_drop_proxy_received_uris (FMDirectoryView *view,
9473 					    const GList *source_uri_list,
9474 					    const char *target_uri,
9475 					    GdkDragAction action)
9476 {
9477 	char *container_uri;
9478 
9479 	container_uri = NULL;
9480 	if (target_uri == NULL) {
9481 		container_uri = fm_directory_view_get_backing_uri (view);
9482 		g_assert (container_uri != NULL);
9483 	}
9484 
9485 	if (action == GDK_ACTION_ASK) {
9486 		action = caja_drag_drop_action_ask
9487 			(GTK_WIDGET (view),
9488 			 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
9489 		if (action == 0) {
9490 			return;
9491 		}
9492 	}
9493 
9494 	caja_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
9495 						    source_uri_list,
9496 						    fm_directory_view_get_copied_files_atom (view));
9497 
9498 	fm_directory_view_move_copy_items (source_uri_list, NULL,
9499 					   target_uri != NULL ? target_uri : container_uri,
9500 					   action, 0, 0, view);
9501 
9502 	g_free (container_uri);
9503 }
9504 
9505 static void
fm_directory_view_drop_proxy_received_netscape_url(FMDirectoryView * view,const char * netscape_url,const char * target_uri,GdkDragAction action)9506 fm_directory_view_drop_proxy_received_netscape_url (FMDirectoryView *view,
9507 						    const char *netscape_url,
9508 						    const char *target_uri,
9509 						    GdkDragAction action)
9510 {
9511 	fm_directory_view_handle_netscape_url_drop (view,
9512 						    netscape_url,
9513 						    target_uri,
9514 						    action, 0, 0);
9515 }
9516 
9517 static void
schedule_update_menus(FMDirectoryView * view)9518 schedule_update_menus (FMDirectoryView *view)
9519 {
9520 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9521 
9522 	/* Don't schedule updates after destroy (#349551),
9523  	 * or if we are not active.
9524  	*/
9525 	if (view->details->window == NULL ||
9526 	    !view->details->active) {
9527 		return;
9528 	}
9529 
9530 	view->details->menu_states_untrustworthy = TRUE;
9531 
9532 	/* Schedule a menu update with the current update interval */
9533 	if (view->details->update_menus_timeout_id == 0) {
9534 		view->details->update_menus_timeout_id
9535 			= g_timeout_add (view->details->update_interval, update_menus_timeout_callback, view);
9536 	}
9537 }
9538 
9539 static void
remove_update_status_idle_callback(FMDirectoryView * view)9540 remove_update_status_idle_callback (FMDirectoryView *view)
9541 {
9542 	if (view->details->update_status_idle_id != 0) {
9543 		g_source_remove (view->details->update_status_idle_id);
9544 		view->details->update_status_idle_id = 0;
9545 	}
9546 }
9547 
9548 static gboolean
update_status_idle_callback(gpointer data)9549 update_status_idle_callback (gpointer data)
9550 {
9551 	FMDirectoryView *view;
9552 
9553 	view = FM_DIRECTORY_VIEW (data);
9554 	fm_directory_view_display_selection_info (view);
9555 	view->details->update_status_idle_id = 0;
9556 	return FALSE;
9557 }
9558 
9559 static void
schedule_update_status(FMDirectoryView * view)9560 schedule_update_status (FMDirectoryView *view)
9561 {
9562 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9563 
9564 	/* Make sure we haven't already destroyed it */
9565 	if (view->details->window == NULL) {
9566 		return;
9567 	}
9568 
9569 	if (view->details->loading) {
9570 		/* Don't update status bar while loading the dir */
9571 		return;
9572 	}
9573 
9574 	if (view->details->update_status_idle_id == 0) {
9575 		view->details->update_status_idle_id =
9576 			g_idle_add_full (G_PRIORITY_DEFAULT_IDLE - 20,
9577 					 update_status_idle_callback, view, NULL);
9578 	}
9579 }
9580 
9581 /**
9582  * fm_directory_view_notify_selection_changed:
9583  *
9584  * Notify this view that the selection has changed. This is normally
9585  * called only by subclasses.
9586  * @view: FMDirectoryView whose selection has changed.
9587  *
9588  **/
9589 void
fm_directory_view_notify_selection_changed(FMDirectoryView * view)9590 fm_directory_view_notify_selection_changed (FMDirectoryView *view)
9591 {
9592 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
9593 
9594 	if (caja_debug_log_is_domain_enabled (CAJA_DEBUG_LOG_DOMAIN_USER)) {
9595 		GList *selection;
9596 		GtkWindow *window;
9597 
9598 		selection = fm_directory_view_get_selection (view);
9599 
9600 		window = fm_directory_view_get_containing_window (view);
9601 		caja_debug_log_with_file_list (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER, selection,
9602 						   "selection changed in window %p",
9603 						   window);
9604 		caja_file_list_free (selection);
9605 	}
9606 
9607 	view->details->selection_was_removed = FALSE;
9608 
9609 	if (!view->details->selection_change_is_due_to_shell) {
9610 		view->details->send_selection_change_to_shell = TRUE;
9611 	}
9612 
9613 	/* Schedule a display of the new selection. */
9614 	if (view->details->display_selection_idle_id == 0) {
9615 		view->details->display_selection_idle_id
9616 			= g_idle_add (display_selection_info_idle_callback,
9617 				      view);
9618 	}
9619 
9620 	if (view->details->batching_selection_level != 0) {
9621 		view->details->selection_changed_while_batched = TRUE;
9622 	} else {
9623 		/* Here is the work we do only when we're not
9624 		 * batching selection changes. In other words, it's the slower
9625 		 * stuff that we don't want to slow down selection techniques
9626 		 * such as rubberband-selecting in icon view.
9627 		 */
9628 
9629 		/* Schedule an update of menu item states to match selection */
9630 		schedule_update_menus (view);
9631 	}
9632 }
9633 
9634 static void
file_changed_callback(CajaFile * file,gpointer callback_data)9635 file_changed_callback (CajaFile *file, gpointer callback_data)
9636 {
9637 	FMDirectoryView *view = FM_DIRECTORY_VIEW (callback_data);
9638 
9639 	schedule_changes (view);
9640 
9641 	schedule_update_menus (view);
9642 	schedule_update_status (view);
9643 
9644 	/* We might have different capabilities, so we need to update
9645 	 * relative icon emblems . (Writeable etc).
9646 	 * Don't do this for trash, as it never changes writability
9647 	 * but does change a lot for the file count attribute.
9648 	 */
9649 	if (!caja_file_is_in_trash (file)) {
9650 		EEL_CALL_METHOD
9651 			(FM_DIRECTORY_VIEW_CLASS, view, emblems_changed, (view));
9652 	}
9653 }
9654 
9655 /**
9656  * load_directory:
9657  *
9658  * Switch the displayed location to a new uri. If the uri is not valid,
9659  * the location will not be switched; user feedback will be provided instead.
9660  * @view: FMDirectoryView whose location will be changed.
9661  * @uri: A string representing the uri to switch to.
9662  *
9663  **/
9664 static void
load_directory(FMDirectoryView * view,CajaDirectory * directory)9665 load_directory (FMDirectoryView *view,
9666 		CajaDirectory *directory)
9667 {
9668 	CajaDirectory *old_directory;
9669 	CajaFile *old_file;
9670 	CajaFileAttributes attributes;
9671 
9672 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9673 	g_assert (CAJA_IS_DIRECTORY (directory));
9674 
9675 	fm_directory_view_stop (view);
9676 	fm_directory_view_clear (view);
9677 
9678 	view->details->loading = TRUE;
9679 
9680 	/* Update menus when directory is empty, before going to new
9681 	 * location, so they won't have any false lingering knowledge
9682 	 * of old selection.
9683 	 */
9684 	schedule_update_menus (view);
9685 
9686 	while (view->details->subdirectory_list != NULL) {
9687 		fm_directory_view_remove_subdirectory (view,
9688 				view->details->subdirectory_list->data);
9689 	}
9690 
9691 	disconnect_model_handlers (view);
9692 
9693 	old_directory = view->details->model;
9694 	caja_directory_ref (directory);
9695 	view->details->model = directory;
9696 	caja_directory_unref (old_directory);
9697 
9698 	old_file = view->details->directory_as_file;
9699 	view->details->directory_as_file =
9700 		caja_directory_get_corresponding_file (directory);
9701 	caja_file_unref (old_file);
9702 
9703 	view->details->reported_load_error = FALSE;
9704 
9705 	/* FIXME bugzilla.gnome.org 45062: In theory, we also need to monitor metadata here (as
9706          * well as doing a call when ready), in case external forces
9707          * change the directory's file metadata.
9708 	 */
9709 	attributes =
9710 		CAJA_FILE_ATTRIBUTE_INFO |
9711 		CAJA_FILE_ATTRIBUTE_MOUNT |
9712 		CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO;
9713 	view->details->metadata_for_directory_as_file_pending = TRUE;
9714 	view->details->metadata_for_files_in_directory_pending = TRUE;
9715 	caja_file_call_when_ready
9716 		(view->details->directory_as_file,
9717 		 attributes,
9718 		 metadata_for_directory_as_file_ready_callback, view);
9719 	caja_directory_call_when_ready
9720 		(view->details->model,
9721 		 attributes,
9722 		 FALSE,
9723 		 metadata_for_files_in_directory_ready_callback, view);
9724 
9725 	/* If capabilities change, then we need to update the menus
9726 	 * because of New Folder, and relative emblems.
9727 	 */
9728 	attributes =
9729 		CAJA_FILE_ATTRIBUTE_INFO |
9730 		CAJA_FILE_ATTRIBUTE_FILESYSTEM_INFO;
9731 	caja_file_monitor_add (view->details->directory_as_file,
9732 				   &view->details->directory_as_file,
9733 				   attributes);
9734 
9735 	view->details->file_changed_handler_id = g_signal_connect
9736 		(view->details->directory_as_file, "changed",
9737 		 G_CALLBACK (file_changed_callback), view);
9738 }
9739 
9740 static void
finish_loading(FMDirectoryView * view)9741 finish_loading (FMDirectoryView *view)
9742 {
9743 	CajaFileAttributes attributes;
9744 
9745 	caja_window_info_report_load_underway (view->details->window,
9746 						   CAJA_VIEW (view));
9747 
9748 	/* Tell interested parties that we've begun loading this directory now.
9749 	 * Subclasses use this to know that the new metadata is now available.
9750 	 */
9751 	fm_directory_view_begin_loading (view);
9752 
9753 	/* Assume we have now all information to show window */
9754 	caja_window_info_view_visible  (view->details->window, CAJA_VIEW (view));
9755 
9756 	if (caja_directory_are_all_files_seen (view->details->model)) {
9757 		/* Unschedule a pending update and schedule a new one with the minimal
9758 		 * update interval. This gives the view a short chance at gathering the
9759 		 * (cached) deep counts.
9760 		 */
9761 		unschedule_display_of_pending_files (view);
9762 		schedule_timeout_display_of_pending_files (view, UPDATE_INTERVAL_MIN);
9763 	}
9764 
9765 	/* Start loading. */
9766 
9767 	/* Connect handlers to learn about loading progress. */
9768 	view->details->done_loading_handler_id = g_signal_connect
9769 		(view->details->model, "done_loading",
9770 		 G_CALLBACK (done_loading_callback), view);
9771 	view->details->load_error_handler_id = g_signal_connect
9772 		(view->details->model, "load_error",
9773 		 G_CALLBACK (load_error_callback), view);
9774 
9775 	/* Monitor the things needed to get the right icon. Also
9776 	 * monitor a directory's item count because the "size"
9777 	 * attribute is based on that, and the file's metadata
9778 	 * and possible custom name.
9779 	 */
9780 	attributes =
9781 		CAJA_FILE_ATTRIBUTES_FOR_ICON |
9782 		CAJA_FILE_ATTRIBUTE_DIRECTORY_ITEM_COUNT |
9783 		CAJA_FILE_ATTRIBUTE_INFO |
9784 		CAJA_FILE_ATTRIBUTE_LINK_INFO |
9785 		CAJA_FILE_ATTRIBUTE_MOUNT |
9786 		CAJA_FILE_ATTRIBUTE_EXTENSION_INFO;
9787 
9788 	caja_directory_file_monitor_add (view->details->model,
9789 					     &view->details->model,
9790 					     view->details->show_hidden_files,
9791 					     attributes,
9792 					     files_added_callback, view);
9793 
9794     	view->details->files_added_handler_id = g_signal_connect
9795 		(view->details->model, "files_added",
9796 		 G_CALLBACK (files_added_callback), view);
9797 	view->details->files_changed_handler_id = g_signal_connect
9798 		(view->details->model, "files_changed",
9799 		 G_CALLBACK (files_changed_callback), view);
9800 }
9801 
9802 static void
finish_loading_if_all_metadata_loaded(FMDirectoryView * view)9803 finish_loading_if_all_metadata_loaded (FMDirectoryView *view)
9804 {
9805 	if (!view->details->metadata_for_directory_as_file_pending &&
9806 	    !view->details->metadata_for_files_in_directory_pending) {
9807 		finish_loading (view);
9808 	}
9809 }
9810 
9811 static void
metadata_for_directory_as_file_ready_callback(CajaFile * file,gpointer callback_data)9812 metadata_for_directory_as_file_ready_callback (CajaFile *file,
9813 			      		       gpointer callback_data)
9814 {
9815 	FMDirectoryView *view;
9816 
9817 	view = callback_data;
9818 
9819 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9820 	g_assert (view->details->directory_as_file == file);
9821 	g_assert (view->details->metadata_for_directory_as_file_pending);
9822 
9823 	view->details->metadata_for_directory_as_file_pending = FALSE;
9824 
9825 	finish_loading_if_all_metadata_loaded (view);
9826 }
9827 
9828 static void
metadata_for_files_in_directory_ready_callback(CajaDirectory * directory,GList * files,gpointer callback_data)9829 metadata_for_files_in_directory_ready_callback (CajaDirectory *directory,
9830 				   		GList *files,
9831 			           		gpointer callback_data)
9832 {
9833 	FMDirectoryView *view;
9834 
9835 	view = callback_data;
9836 
9837 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9838 	g_assert (view->details->model == directory);
9839 	g_assert (view->details->metadata_for_files_in_directory_pending);
9840 
9841 	view->details->metadata_for_files_in_directory_pending = FALSE;
9842 
9843 	finish_loading_if_all_metadata_loaded (view);
9844 }
9845 
9846 static void
finish_undoredo_callback(gpointer data)9847 finish_undoredo_callback (gpointer data)
9848 {
9849 }
9850 
9851 char **
fm_directory_view_get_emblem_names_to_exclude(FMDirectoryView * view)9852 fm_directory_view_get_emblem_names_to_exclude (FMDirectoryView *view)
9853 {
9854 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
9855 
9856 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
9857 		(FM_DIRECTORY_VIEW_CLASS, view,
9858 		 get_emblem_names_to_exclude, (view));
9859 }
9860 
9861 static char **
real_get_emblem_names_to_exclude(FMDirectoryView * view)9862 real_get_emblem_names_to_exclude (FMDirectoryView *view)
9863 {
9864 	char **excludes;
9865 	int i;
9866 
9867 	g_assert (FM_IS_DIRECTORY_VIEW (view));
9868 
9869 	excludes = g_new (char *, 3);
9870 
9871 	i = 0;
9872 	excludes[i++] = g_strdup (CAJA_FILE_EMBLEM_NAME_TRASH);
9873 
9874 	if (!caja_file_can_write (view->details->directory_as_file)) {
9875 		excludes[i++] = g_strdup (CAJA_FILE_EMBLEM_NAME_CANT_WRITE);
9876 	}
9877 
9878 	excludes[i++] = NULL;
9879 
9880 	return excludes;
9881 }
9882 
9883 /**
9884  * fm_directory_view_merge_menus:
9885  *
9886  * Add this view's menus to the window's menu bar.
9887  * @view: FMDirectoryView in question.
9888  */
9889 static void
fm_directory_view_merge_menus(FMDirectoryView * view)9890 fm_directory_view_merge_menus (FMDirectoryView *view)
9891 {
9892 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
9893 
9894 	EEL_CALL_METHOD
9895 		(FM_DIRECTORY_VIEW_CLASS, view,
9896 		 merge_menus, (view));
9897 }
9898 
9899 static void
fm_directory_view_unmerge_menus(FMDirectoryView * view)9900 fm_directory_view_unmerge_menus (FMDirectoryView *view)
9901 {
9902 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
9903 
9904 	EEL_CALL_METHOD
9905 		(FM_DIRECTORY_VIEW_CLASS, view,
9906 		 unmerge_menus, (view));
9907 }
9908 
9909 static void
disconnect_handler(GObject * object,guint * id)9910 disconnect_handler (GObject *object, guint *id)
9911 {
9912 	if (*id != 0) {
9913 		g_signal_handler_disconnect (object, *id);
9914 		*id = 0;
9915 	}
9916 }
9917 
9918 static void
disconnect_directory_handler(FMDirectoryView * view,guint * id)9919 disconnect_directory_handler (FMDirectoryView *view, guint *id)
9920 {
9921 	disconnect_handler (G_OBJECT (view->details->model), id);
9922 }
9923 
9924 static void
disconnect_directory_as_file_handler(FMDirectoryView * view,guint * id)9925 disconnect_directory_as_file_handler (FMDirectoryView *view, guint *id)
9926 {
9927 	disconnect_handler (G_OBJECT (view->details->directory_as_file), id);
9928 }
9929 
9930 static void
disconnect_model_handlers(FMDirectoryView * view)9931 disconnect_model_handlers (FMDirectoryView *view)
9932 {
9933 	if (view->details->model == NULL) {
9934 		return;
9935 	}
9936 	disconnect_directory_handler (view, &view->details->files_added_handler_id);
9937 	disconnect_directory_handler (view, &view->details->files_changed_handler_id);
9938 	disconnect_directory_handler (view, &view->details->done_loading_handler_id);
9939 	disconnect_directory_handler (view, &view->details->load_error_handler_id);
9940 	disconnect_directory_as_file_handler (view, &view->details->file_changed_handler_id);
9941 	caja_file_cancel_call_when_ready (view->details->directory_as_file,
9942 					      metadata_for_directory_as_file_ready_callback,
9943 					      view);
9944 	caja_directory_cancel_callback (view->details->model,
9945 					    metadata_for_files_in_directory_ready_callback,
9946 					    view);
9947 	caja_directory_file_monitor_remove (view->details->model,
9948 						&view->details->model);
9949 	caja_file_monitor_remove (view->details->directory_as_file,
9950 				      &view->details->directory_as_file);
9951 }
9952 
9953 /**
9954  * fm_directory_view_reset_to_defaults:
9955  *
9956  * set sorting order, zoom level, etc. to match defaults
9957  *
9958  **/
9959 void
fm_directory_view_reset_to_defaults(FMDirectoryView * view)9960 fm_directory_view_reset_to_defaults (FMDirectoryView *view)
9961 {
9962 	CajaWindowShowHiddenFilesMode mode;
9963 
9964 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
9965 
9966 	EEL_CALL_METHOD
9967 		(FM_DIRECTORY_VIEW_CLASS, view,
9968 		 reset_to_defaults, (view));
9969 	mode = caja_window_info_get_hidden_files_mode (view->details->window);
9970 	if (mode != CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT) {
9971 		caja_window_info_set_hidden_files_mode (view->details->window,
9972 							    CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT);
9973 	}
9974 }
9975 
9976 /**
9977  * fm_directory_view_select_all:
9978  *
9979  * select all the items in the view
9980  *
9981  **/
9982 void
fm_directory_view_select_all(FMDirectoryView * view)9983 fm_directory_view_select_all (FMDirectoryView *view)
9984 {
9985 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
9986 
9987 	EEL_CALL_METHOD
9988 		(FM_DIRECTORY_VIEW_CLASS, view,
9989 		 select_all, (view));
9990 }
9991 
9992 /**
9993  * fm_directory_view_set_selection:
9994  *
9995  * set the selection to the items identified in @selection. @selection
9996  * should be a list of CajaFiles
9997  *
9998  **/
9999 void
fm_directory_view_set_selection(FMDirectoryView * view,GList * selection)10000 fm_directory_view_set_selection (FMDirectoryView *view, GList *selection)
10001 {
10002 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
10003 
10004 	EEL_CALL_METHOD
10005 		(FM_DIRECTORY_VIEW_CLASS, view,
10006 		 set_selection, (view, selection));
10007 }
10008 
10009 static void
fm_directory_view_select_file(FMDirectoryView * view,CajaFile * file)10010 fm_directory_view_select_file (FMDirectoryView *view, CajaFile *file)
10011 {
10012 	GList file_list;
10013 
10014 	file_list.data = file;
10015 	file_list.next = NULL;
10016 	file_list.prev = NULL;
10017 	fm_directory_view_set_selection (view, &file_list);
10018 }
10019 
10020 /**
10021  * fm_directory_view_get_selected_icon_locations:
10022  *
10023  * return an array of locations of selected icons if available
10024  * Return value: GArray of GdkPoints
10025  *
10026  **/
10027 GArray *
fm_directory_view_get_selected_icon_locations(FMDirectoryView * view)10028 fm_directory_view_get_selected_icon_locations (FMDirectoryView *view)
10029 {
10030 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
10031 
10032 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10033 		(FM_DIRECTORY_VIEW_CLASS, view,
10034 		 get_selected_icon_locations, (view));
10035 }
10036 
10037 /**
10038  * fm_directory_view_reveal_selection:
10039  *
10040  * Scroll as necessary to reveal the selected items.
10041  **/
10042 void
fm_directory_view_reveal_selection(FMDirectoryView * view)10043 fm_directory_view_reveal_selection (FMDirectoryView *view)
10044 {
10045 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
10046 
10047 	EEL_CALL_METHOD
10048 		(FM_DIRECTORY_VIEW_CLASS, view,
10049 		 reveal_selection, (view));
10050 }
10051 
10052 /**
10053  * fm_directory_view_stop:
10054  *
10055  * Stop the current ongoing process, such as switching to a new uri.
10056  * @view: FMDirectoryView in question.
10057  *
10058  **/
10059 void
fm_directory_view_stop(FMDirectoryView * view)10060 fm_directory_view_stop (FMDirectoryView *view)
10061 {
10062 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
10063 
10064 	unschedule_display_of_pending_files (view);
10065 	reset_update_interval (view);
10066 
10067 	/* Free extra undisplayed files */
10068 	file_and_directory_list_free (view->details->new_added_files);
10069 	view->details->new_added_files = NULL;
10070 	file_and_directory_list_free (view->details->new_changed_files);
10071 	view->details->new_changed_files = NULL;
10072 	g_hash_table_remove_all (view->details->non_ready_files);
10073 	file_and_directory_list_free (view->details->old_added_files);
10074 	view->details->old_added_files = NULL;
10075 	file_and_directory_list_free (view->details->old_changed_files);
10076 	view->details->old_changed_files = NULL;
10077 	g_list_free_full (view->details->pending_locations_selected, g_object_unref);
10078 	view->details->pending_locations_selected = NULL;
10079 
10080 	if (view->details->model != NULL) {
10081 		caja_directory_file_monitor_remove (view->details->model, view);
10082 	}
10083 	done_loading (view, FALSE);
10084 }
10085 
10086 gboolean
fm_directory_view_is_read_only(FMDirectoryView * view)10087 fm_directory_view_is_read_only (FMDirectoryView *view)
10088 {
10089 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10090 
10091 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10092 		(FM_DIRECTORY_VIEW_CLASS, view,
10093 		 is_read_only, (view));
10094 }
10095 
10096 gboolean
fm_directory_view_is_empty(FMDirectoryView * view)10097 fm_directory_view_is_empty (FMDirectoryView *view)
10098 {
10099 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10100 
10101 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10102 		(FM_DIRECTORY_VIEW_CLASS, view,
10103 		 is_empty, (view));
10104 }
10105 
10106 gboolean
fm_directory_view_is_editable(FMDirectoryView * view)10107 fm_directory_view_is_editable (FMDirectoryView *view)
10108 {
10109 	CajaDirectory *directory;
10110 
10111 	directory = fm_directory_view_get_model (view);
10112 
10113 	if (directory != NULL) {
10114 		return caja_directory_is_editable (directory);
10115 	}
10116 
10117 	return TRUE;
10118 }
10119 
10120 void
fm_directory_view_set_initiated_unmount(FMDirectoryView * view,gboolean initiated_unmount)10121 fm_directory_view_set_initiated_unmount (FMDirectoryView *view,
10122 					 gboolean initiated_unmount)
10123 {
10124 	if (view->details->window != NULL) {
10125 		caja_window_info_set_initiated_unmount(view->details->window,
10126 							   initiated_unmount);
10127 	}
10128 }
10129 
10130 static gboolean
real_is_read_only(FMDirectoryView * view)10131 real_is_read_only (FMDirectoryView *view)
10132 {
10133 	CajaFile *file;
10134 
10135 	if (!fm_directory_view_is_editable (view)) {
10136 		return TRUE;
10137 	}
10138 
10139 	file = fm_directory_view_get_directory_as_file (view);
10140 	if (file != NULL) {
10141 		return !caja_file_can_write (file);
10142 	}
10143 	return FALSE;
10144 }
10145 
10146 gboolean
fm_directory_view_supports_creating_files(FMDirectoryView * view)10147 fm_directory_view_supports_creating_files (FMDirectoryView *view)
10148 {
10149 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10150 
10151 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10152 		(FM_DIRECTORY_VIEW_CLASS, view,
10153 		 supports_creating_files, (view));
10154 }
10155 
10156 gboolean
fm_directory_view_accepts_dragged_files(FMDirectoryView * view)10157 fm_directory_view_accepts_dragged_files (FMDirectoryView *view)
10158 {
10159 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10160 
10161 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10162 		(FM_DIRECTORY_VIEW_CLASS, view,
10163 		 accepts_dragged_files, (view));
10164 }
10165 
10166 /**
10167  * fm_directory_view_should_show_file
10168  *
10169  * Returns whether or not this file should be displayed based on
10170  * current filtering options.
10171  */
10172 gboolean
fm_directory_view_should_show_file(FMDirectoryView * view,CajaFile * file)10173 fm_directory_view_should_show_file (FMDirectoryView *view, CajaFile *file)
10174 {
10175 	return caja_file_should_show (file,
10176 				      view->details->show_hidden_files,
10177 				      view->details->show_foreign_files,
10178 				      view->details->show_backup_files);
10179 }
10180 
10181 static gboolean
real_supports_creating_files(FMDirectoryView * view)10182 real_supports_creating_files (FMDirectoryView *view)
10183 {
10184 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10185 
10186 	return !fm_directory_view_is_read_only (view) && !showing_trash_directory (view);
10187 }
10188 
10189 static gboolean
real_accepts_dragged_files(FMDirectoryView * view)10190 real_accepts_dragged_files (FMDirectoryView *view)
10191 {
10192 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10193 
10194 	return !fm_directory_view_is_read_only (view);
10195 }
10196 
10197 gboolean
fm_directory_view_supports_properties(FMDirectoryView * view)10198 fm_directory_view_supports_properties (FMDirectoryView *view)
10199 {
10200 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10201 
10202 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10203 		(FM_DIRECTORY_VIEW_CLASS, view,
10204 		 supports_properties, (view));
10205 }
10206 
10207 static gboolean
real_supports_properties(FMDirectoryView * view)10208 real_supports_properties (FMDirectoryView *view)
10209 {
10210 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10211 
10212 	return TRUE;
10213 }
10214 
10215 gboolean
fm_directory_view_supports_zooming(FMDirectoryView * view)10216 fm_directory_view_supports_zooming (FMDirectoryView *view)
10217 {
10218 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10219 
10220 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10221 		(FM_DIRECTORY_VIEW_CLASS, view,
10222 		 supports_zooming, (view));
10223 }
10224 
10225 static gboolean
real_supports_zooming(FMDirectoryView * view)10226 real_supports_zooming (FMDirectoryView *view)
10227 {
10228 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10229 
10230 	return TRUE;
10231 }
10232 
10233 gboolean
fm_directory_view_using_manual_layout(FMDirectoryView * view)10234 fm_directory_view_using_manual_layout (FMDirectoryView  *view)
10235 {
10236 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10237 
10238 	return EEL_CALL_METHOD_WITH_RETURN_VALUE
10239 		(FM_DIRECTORY_VIEW_CLASS, view,
10240 		 using_manual_layout, (view));
10241 }
10242 
10243 static gboolean
real_using_manual_layout(FMDirectoryView * view)10244 real_using_manual_layout (FMDirectoryView *view)
10245 {
10246 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10247 
10248 	return FALSE;
10249 }
10250 
10251 /**
10252  * fm_directory_view_update_menus:
10253  *
10254  * Update the sensitivity and wording of dynamic menu items.
10255  * @view: FMDirectoryView in question.
10256  */
10257 void
fm_directory_view_update_menus(FMDirectoryView * view)10258 fm_directory_view_update_menus (FMDirectoryView *view)
10259 {
10260 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
10261 
10262 	if (!view->details->active) {
10263 		return;
10264 	}
10265 
10266 
10267 	EEL_CALL_METHOD
10268 		(FM_DIRECTORY_VIEW_CLASS, view,
10269 		 update_menus, (view));
10270 
10271 	view->details->menu_states_untrustworthy = FALSE;
10272 }
10273 
10274 static void
schedule_update_menus_callback(gpointer callback_data)10275 schedule_update_menus_callback (gpointer callback_data)
10276 {
10277 	schedule_update_menus (FM_DIRECTORY_VIEW (callback_data));
10278 }
10279 
10280 void
fm_directory_view_ignore_hidden_file_preferences(FMDirectoryView * view)10281 fm_directory_view_ignore_hidden_file_preferences (FMDirectoryView *view)
10282 {
10283 	g_return_if_fail (view->details->model == NULL);
10284 
10285 	if (view->details->ignore_hidden_file_preferences) {
10286 		return;
10287 	}
10288 
10289 	view->details->show_hidden_files = FALSE;
10290 	view->details->ignore_hidden_file_preferences = TRUE;
10291 }
10292 
10293 void
fm_directory_view_set_show_foreign(FMDirectoryView * view,gboolean show_foreign)10294 fm_directory_view_set_show_foreign (FMDirectoryView *view,
10295 		                    gboolean show_foreign)
10296 {
10297 	view->details->show_foreign_files = show_foreign;
10298 }
10299 
10300 char *
fm_directory_view_get_uri(FMDirectoryView * view)10301 fm_directory_view_get_uri (FMDirectoryView *view)
10302 {
10303 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
10304 	if (view->details->model == NULL) {
10305 		return NULL;
10306 	}
10307 	return caja_directory_get_uri (view->details->model);
10308 }
10309 
10310 /* Get the real directory where files will be stored and created */
10311 char *
fm_directory_view_get_backing_uri(FMDirectoryView * view)10312 fm_directory_view_get_backing_uri (FMDirectoryView *view)
10313 {
10314 	CajaDirectory *directory;
10315 	char *uri;
10316 
10317 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), NULL);
10318 
10319 	if (view->details->model == NULL) {
10320 		return NULL;
10321 	}
10322 
10323 	directory = view->details->model;
10324 
10325 	if (CAJA_IS_DESKTOP_DIRECTORY (directory)) {
10326 		directory = caja_desktop_directory_get_real_directory (CAJA_DESKTOP_DIRECTORY (directory));
10327 	} else {
10328 		caja_directory_ref (directory);
10329 	}
10330 
10331 	uri = caja_directory_get_uri (directory);
10332 
10333 	caja_directory_unref (directory);
10334 
10335 	return uri;
10336 }
10337 
10338 void
fm_directory_view_move_copy_items(const GList * item_uris,GArray * relative_item_points,const char * target_uri,int copy_action,int x,int y,FMDirectoryView * view)10339 fm_directory_view_move_copy_items (const GList *item_uris,
10340 				   GArray *relative_item_points,
10341 				   const char *target_uri,
10342 				   int copy_action,
10343 				   int x, int y,
10344 				   FMDirectoryView *view)
10345 {
10346 	CajaFile *target_file;
10347 
10348 	g_assert (relative_item_points == NULL
10349 		  || relative_item_points->len == 0
10350 		  || g_list_length ((GList *)item_uris) == relative_item_points->len);
10351 
10352 	/* add the drop location to the icon offsets */
10353 	offset_drop_points (relative_item_points, x, y);
10354 
10355 	target_file = caja_file_get_existing_by_uri (target_uri);
10356 	/* special-case "command:" here instead of starting a move/copy */
10357 	if (target_file != NULL && caja_file_is_launcher (target_file)) {
10358 		caja_file_unref (target_file);
10359 		caja_launch_desktop_file (
10360 				gtk_widget_get_screen (GTK_WIDGET (view)),
10361 				target_uri, item_uris,
10362 				fm_directory_view_get_containing_window (view));
10363 		return;
10364 	} else if (copy_action == GDK_ACTION_COPY &&
10365 		   caja_is_engrampa_installed () &&
10366 		   target_file != NULL &&
10367 		   caja_file_is_archive (target_file)) {
10368 		char *command, *quoted_uri, *tmp;
10369 		const GList *l;
10370 		GdkScreen  *screen;
10371 
10372 		/* Handle dropping onto a engrampa archiver file, instead of starting a move/copy */
10373 
10374 		caja_file_unref (target_file);
10375 
10376 		quoted_uri = g_shell_quote (target_uri);
10377 		command = g_strconcat ("engrampa -a ", quoted_uri, NULL);
10378 		g_free (quoted_uri);
10379 
10380 		for (l = item_uris; l != NULL; l = l->next) {
10381 			quoted_uri = g_shell_quote ((char *) l->data);
10382 
10383 			tmp = g_strconcat (command, " ", quoted_uri, NULL);
10384 			g_free (command);
10385 			command = tmp;
10386 
10387 			g_free (quoted_uri);
10388 		}
10389 
10390 		screen = gtk_widget_get_screen (GTK_WIDGET (view));
10391 		if (screen == NULL) {
10392 			screen = gdk_screen_get_default ();
10393 		}
10394 
10395 		mate_gdk_spawn_command_line_on_screen(screen, command, NULL);
10396 		g_free (command);
10397 
10398 		return;
10399 	}
10400 	caja_file_unref (target_file);
10401 
10402 	caja_file_operations_copy_move
10403 		(item_uris, relative_item_points,
10404 		 target_uri, copy_action, GTK_WIDGET (view),
10405 		 copy_move_done_callback, pre_copy_move (view));
10406 }
10407 
10408 gboolean
fm_directory_view_can_accept_item(CajaFile * target_item,const char * item_uri,FMDirectoryView * view)10409 fm_directory_view_can_accept_item (CajaFile *target_item,
10410 				   const char *item_uri,
10411 				   FMDirectoryView *view)
10412 {
10413 	g_return_val_if_fail (CAJA_IS_FILE (target_item), FALSE);
10414 	g_return_val_if_fail (item_uri != NULL, FALSE);
10415 	g_return_val_if_fail (FM_IS_DIRECTORY_VIEW (view), FALSE);
10416 
10417 	return caja_drag_can_accept_item (target_item, item_uri);
10418 }
10419 
10420 static void
fm_directory_view_trash_state_changed_callback(CajaTrashMonitor * trash_monitor,gboolean state,gpointer callback_data)10421 fm_directory_view_trash_state_changed_callback (CajaTrashMonitor *trash_monitor,
10422 						gboolean state, gpointer callback_data)
10423 {
10424 	FMDirectoryView *view;
10425 
10426 	view = (FMDirectoryView *) callback_data;
10427 	g_assert (FM_IS_DIRECTORY_VIEW (view));
10428 
10429 	schedule_update_menus (view);
10430 }
10431 
10432 void
fm_directory_view_start_batching_selection_changes(FMDirectoryView * view)10433 fm_directory_view_start_batching_selection_changes (FMDirectoryView *view)
10434 {
10435 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
10436 
10437 	++view->details->batching_selection_level;
10438 	view->details->selection_changed_while_batched = FALSE;
10439 }
10440 
10441 void
fm_directory_view_stop_batching_selection_changes(FMDirectoryView * view)10442 fm_directory_view_stop_batching_selection_changes (FMDirectoryView *view)
10443 {
10444 	g_return_if_fail (FM_IS_DIRECTORY_VIEW (view));
10445 	g_return_if_fail (view->details->batching_selection_level > 0);
10446 
10447 	if (--view->details->batching_selection_level == 0) {
10448 		if (view->details->selection_changed_while_batched) {
10449 			fm_directory_view_notify_selection_changed (view);
10450 		}
10451 	}
10452 }
10453 
10454 static void
revert_slashes(char * string)10455 revert_slashes (char *string)
10456 {
10457 	while (*string != 0) {
10458 		if (*string == '/') {
10459 			*string = '\\';
10460 		}
10461 		string++;
10462 	}
10463 }
10464 
10465 
10466 static GdkDragAction
ask_link_action(FMDirectoryView * view)10467 ask_link_action (FMDirectoryView *view)
10468 {
10469 	int button_pressed;
10470 	GdkDragAction result;
10471 	GtkWindow *parent_window;
10472 	GtkWidget *dialog;
10473 
10474 	parent_window = NULL;
10475 
10476 	/* Don't use desktop window as parent, since that means
10477 	   we show up an all desktops etc */
10478 	if (! FM_IS_DESKTOP_ICON_VIEW (view)) {
10479 		parent_window = GTK_WINDOW (fm_directory_view_get_containing_window (view));
10480 	}
10481 
10482 	dialog = gtk_message_dialog_new (parent_window,
10483 					 GTK_DIALOG_DESTROY_WITH_PARENT,
10484 					 GTK_MESSAGE_QUESTION,
10485 					 GTK_BUTTONS_NONE,
10486 					 _("Download location?"));
10487 
10488 	gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
10489 						  _("You can download it or make a link to it."));
10490 
10491 	gtk_dialog_add_button (GTK_DIALOG (dialog),
10492 			       _("Make a _Link"), 0);
10493 
10494 	eel_dialog_add_button (GTK_DIALOG (dialog),
10495 			       _("_Cancel"),
10496 			       "process-stop", 1);
10497 
10498 	gtk_dialog_add_button (GTK_DIALOG (dialog),
10499 			       _("_Download"), 2);
10500 
10501 	gtk_window_set_title (GTK_WINDOW (dialog), ""); /* as per HIG */
10502 	gtk_window_set_focus_on_map (GTK_WINDOW (dialog), TRUE);
10503 	gtk_dialog_set_default_response (GTK_DIALOG (dialog), 2);
10504 
10505 	gtk_window_present (GTK_WINDOW (dialog));
10506 
10507 	button_pressed = gtk_dialog_run (GTK_DIALOG (dialog));
10508 
10509 	gtk_widget_destroy (dialog);
10510 
10511 	switch (button_pressed) {
10512 	case 0:
10513 		result = GDK_ACTION_LINK;
10514 		break;
10515 	case 1:
10516 	case GTK_RESPONSE_DELETE_EVENT:
10517 		result = 0;
10518 		break;
10519 	case 2:
10520 		result = GDK_ACTION_COPY;
10521 		break;
10522 	default:
10523 		g_assert_not_reached ();
10524 		result = 0;
10525 	}
10526 
10527 	return result;
10528 }
10529 
10530 typedef struct {
10531 	FMDirectoryView  *view;
10532 	GCancellable *cancellable;
10533 	char *encoded_url;
10534 	char *target_uri;
10535 	int x;
10536 	int y;
10537 	guint timeout;
10538 } NetscapeUrlDropAsk;
10539 
10540 static void
handle_netscape_url_drop_ask_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)10541 handle_netscape_url_drop_ask_cb (GObject *source_object,
10542 				 GAsyncResult *res,
10543 				 gpointer user_data)
10544 {
10545 	NetscapeUrlDropAsk *data;
10546 	GdkDragAction action;
10547 	GFileInfo *info;
10548 	GFile *f;
10549 	const char *mime_type;
10550 
10551 	data = user_data;
10552 	f = G_FILE (source_object);
10553 
10554 	info = g_file_query_info_finish (f, res, NULL);
10555 	mime_type = NULL;
10556 
10557 	if (info) {
10558 		mime_type = g_file_info_get_content_type (info);
10559 	}
10560 
10561 	if (mime_type != NULL &&
10562 	    (g_content_type_equals (mime_type, "text/html") ||
10563 	     g_content_type_equals (mime_type, "text/xml")  ||
10564 	     g_content_type_equals (mime_type, "application/xhtml+xml"))) {
10565 		action = GDK_ACTION_LINK;
10566 	} else if (mime_type != NULL &&
10567 		   g_content_type_equals (mime_type, "text/plain")) {
10568 		action = ask_link_action (data->view);
10569 	} else {
10570 		action = GDK_ACTION_COPY;
10571 	}
10572 	if (info) {
10573 		g_object_unref (info);
10574 	}
10575 
10576 	if (action != 0) {
10577 		fm_directory_view_handle_netscape_url_drop (data->view,
10578 							    data->encoded_url,
10579 							    data->target_uri,
10580 							    action,
10581 							    data->x, data->y);
10582 	}
10583 
10584 	g_object_unref (data->view);
10585 	g_object_unref (data->cancellable);
10586 	if (data->timeout != 0) {
10587 		g_source_remove (data->timeout);
10588 	}
10589 	g_free (data->encoded_url);
10590 	g_free (data->target_uri);
10591 	g_free (data);
10592 }
10593 
10594 static gboolean
handle_netscape_url_drop_timeout(gpointer user_data)10595 handle_netscape_url_drop_timeout (gpointer user_data)
10596 {
10597 	NetscapeUrlDropAsk *data;
10598 
10599 	data = user_data;
10600 
10601 	g_cancellable_cancel (data->cancellable);
10602 	data->timeout = 0;
10603 
10604 	return FALSE;
10605 }
10606 
10607 static inline void
fm_directory_view_widget_to_file_operation_position(FMDirectoryView * view,GdkPoint * position)10608 fm_directory_view_widget_to_file_operation_position (FMDirectoryView *view,
10609 						     GdkPoint *position)
10610 {
10611 	EEL_CALL_METHOD (FM_DIRECTORY_VIEW_CLASS, view,
10612 			 widget_to_file_operation_position,
10613 			 (view, position));
10614 }
10615 
10616 static void
fm_directory_view_widget_to_file_operation_position_xy(FMDirectoryView * view,int * x,int * y)10617 fm_directory_view_widget_to_file_operation_position_xy (FMDirectoryView *view,
10618 							int *x, int *y)
10619 {
10620 	GdkPoint position;
10621 
10622 	position.x = *x;
10623 	position.y = *y;
10624 	fm_directory_view_widget_to_file_operation_position (view, &position);
10625 	*x = position.x;
10626 	*y = position.y;
10627 }
10628 
10629 void
fm_directory_view_handle_netscape_url_drop(FMDirectoryView * view,const char * encoded_url,const char * target_uri,GdkDragAction action,int x,int y)10630 fm_directory_view_handle_netscape_url_drop (FMDirectoryView  *view,
10631 					    const char       *encoded_url,
10632 					    const char       *target_uri,
10633 					    GdkDragAction     action,
10634 					    int               x,
10635 					    int               y)
10636 {
10637 	GdkPoint point;
10638 	char *url, *title;
10639 	char *container_uri;
10640 	char **bits;
10641 	GList *uri_list = NULL;
10642 	GFile *f;
10643 
10644 	if (encoded_url == NULL) {
10645 		return;
10646 	}
10647 
10648 	container_uri = NULL;
10649 	if (target_uri == NULL) {
10650 		container_uri = fm_directory_view_get_backing_uri (view);
10651 		g_assert (container_uri != NULL);
10652 	}
10653 
10654 	f = g_file_new_for_uri (target_uri != NULL ? target_uri : container_uri);
10655 	if (!g_file_is_native (f)) {
10656 		eel_show_warning_dialog (_("Drag and drop is not supported."),
10657 					 _("Drag and drop is only supported on local file systems."),
10658 					 fm_directory_view_get_containing_window (view));
10659 		g_object_unref (f);
10660 		g_free (container_uri);
10661 		return;
10662 	}
10663 	g_object_unref (f);
10664 
10665 	/* _NETSCAPE_URL_ works like this: $URL\n$TITLE */
10666 	bits = g_strsplit (encoded_url, "\n", 0);
10667 	switch (g_strv_length (bits)) {
10668 	case 0:
10669 		g_strfreev (bits);
10670 		g_free (container_uri);
10671 		return;
10672 	case 1:
10673 		url = bits[0];
10674 		title = NULL;
10675 		break;
10676 	default:
10677 		url = bits[0];
10678 		title = bits[1];
10679 	}
10680 
10681 	if (action == GDK_ACTION_ASK) {
10682 		NetscapeUrlDropAsk *data;
10683 
10684 		f = g_file_new_for_uri (url);
10685 		data = g_new0 (NetscapeUrlDropAsk, 1);
10686 		data->view = g_object_ref (view);
10687 		data->cancellable = g_cancellable_new ();
10688 		data->encoded_url = g_strdup (encoded_url);
10689 		data->target_uri = g_strdup (target_uri);
10690 		data->x = x;
10691 		data->y = y;
10692 		/* Ensure we wait at most 1 second for mimetype */
10693 		data->timeout = g_timeout_add (1000,
10694 					       handle_netscape_url_drop_timeout,
10695 					       data);
10696 		g_file_query_info_async (f,
10697 					 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0,
10698 					 0, data->cancellable,
10699 					 handle_netscape_url_drop_ask_cb,
10700 					 data);
10701 
10702 		g_strfreev (bits);
10703 		g_free (container_uri);
10704 		return;
10705 	}
10706 
10707 	fm_directory_view_widget_to_file_operation_position_xy (view, &x, &y);
10708 
10709 	/* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
10710 	 * and we don't support combinations either. */
10711 	if ((action != GDK_ACTION_DEFAULT) &&
10712 	    (action != GDK_ACTION_COPY) &&
10713 	    (action != GDK_ACTION_MOVE) &&
10714 	    (action != GDK_ACTION_LINK)) {
10715 		eel_show_warning_dialog (_("Drag and drop is not supported."),
10716 					 _("An invalid drag type was used."),
10717 					 fm_directory_view_get_containing_window (view));
10718 		g_strfreev (bits);
10719 		g_free (container_uri);
10720 		return;
10721 	}
10722 
10723 	if (action == GDK_ACTION_LINK) {
10724 		char *link_name;
10725 
10726 		if (eel_str_is_empty (title)) {
10727 			GFile *f;
10728 
10729 			f = g_file_new_for_uri (url);
10730 			link_name = g_file_get_basename (f);
10731 			g_object_unref (f);
10732 		} else {
10733 			link_name = g_strdup (title);
10734 		}
10735 
10736 		if (!eel_str_is_empty (link_name)) {
10737 			GdkScreen *screen;
10738 			int screen_num;
10739 			char *link_display_name;
10740 			GError *error = NULL;
10741 			gboolean success;
10742 
10743 			link_display_name = g_strdup_printf (_("Link to %s"), link_name);
10744 
10745 			/* The filename can't contain slashes, strip em.
10746 			   (the basename of http://foo/ is http://foo/) */
10747 			revert_slashes (link_name);
10748 
10749 			point.x = x;
10750 			point.y = y;
10751 
10752 			screen = gtk_widget_get_screen (GTK_WIDGET (view));
10753 			screen_num = gdk_x11_screen_get_screen_number (screen);
10754 
10755 			success = caja_link_local_create (target_uri != NULL ? target_uri : container_uri,
10756 			                                  link_name,
10757 			                                  link_display_name,
10758 			                                  "mate-fs-bookmark",
10759 			                                  url,
10760 			                                  &point,
10761 			                                  screen_num,
10762 			                                  TRUE,
10763 			                                  &error);
10764 			if (!success) {
10765 				if (error) {
10766 					eel_show_error_dialog (_("Link Creation Failed"),
10767 					                       error->message, NULL);
10768 					g_error_free (error);
10769 				} else {
10770 					gchar *error_message = g_strdup_printf (_("Cannot create the link to %s"), url);
10771 					eel_show_error_dialog (_("Link Creation Failed"),
10772 					                       error_message, NULL);
10773 					g_free (error_message);
10774 				}
10775 			}
10776 
10777 			g_free (link_display_name);
10778 		}
10779 		g_free (link_name);
10780 	} else {
10781 		GArray *points;
10782 
10783 		GdkPoint tmp_point = { 0, 0 };
10784 
10785 		/* pass in a 1-item array of icon positions, relative to x, y */
10786 		points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
10787 		g_array_append_val (points, tmp_point);
10788 
10789 		uri_list = g_list_append (uri_list, url);
10790 
10791 		fm_directory_view_move_copy_items (uri_list, points,
10792 						   target_uri != NULL ? target_uri : container_uri,
10793 						   action, x, y, view);
10794 
10795 		g_list_free (uri_list);
10796 		g_array_free (points, TRUE);
10797 	}
10798 
10799 	g_strfreev (bits);
10800 	g_free (container_uri);
10801 }
10802 
10803 void
fm_directory_view_handle_uri_list_drop(FMDirectoryView * view,const char * item_uris,const char * target_uri,GdkDragAction action,int x,int y)10804 fm_directory_view_handle_uri_list_drop (FMDirectoryView  *view,
10805 					const char       *item_uris,
10806 					const char       *target_uri,
10807 					GdkDragAction     action,
10808 					int               x,
10809 					int               y)
10810 {
10811 	gchar **uri_list;
10812 	GList *real_uri_list = NULL;
10813 	char *container_uri;
10814 	int n_uris, i;
10815 	GArray *points;
10816 
10817 	if (item_uris == NULL) {
10818 		return;
10819 	}
10820 
10821 	container_uri = NULL;
10822 	if (target_uri == NULL) {
10823 		container_uri = fm_directory_view_get_backing_uri (view);
10824 		g_assert (container_uri != NULL);
10825 	}
10826 
10827 	if (action == GDK_ACTION_ASK) {
10828 		action = caja_drag_drop_action_ask
10829 			(GTK_WIDGET (view),
10830 			 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
10831 		if (action == 0) {
10832 			g_free (container_uri);
10833 			return;
10834 		}
10835 	}
10836 
10837 	/* We don't support GDK_ACTION_ASK or GDK_ACTION_PRIVATE
10838 	 * and we don't support combinations either. */
10839 	if ((action != GDK_ACTION_DEFAULT) &&
10840 	    (action != GDK_ACTION_COPY) &&
10841 	    (action != GDK_ACTION_MOVE) &&
10842 	    (action != GDK_ACTION_LINK)) {
10843 		eel_show_warning_dialog (_("Drag and drop is not supported."),
10844 					 _("An invalid drag type was used."),
10845 					 fm_directory_view_get_containing_window (view));
10846 		g_free (container_uri);
10847 		return;
10848 	}
10849 
10850 	n_uris = 0;
10851 	uri_list = g_uri_list_extract_uris (item_uris);
10852 	for (i = 0; uri_list[i] != NULL; i++) {
10853 		real_uri_list = g_list_append (real_uri_list, uri_list[i]);
10854 		n_uris++;
10855 	}
10856 	g_free (uri_list);
10857 
10858 	/* do nothing if no real uris are left */
10859 	if (n_uris == 0) {
10860 		g_free (container_uri);
10861 		return;
10862 	}
10863 
10864 	if (n_uris == 1) {
10865 		GdkPoint tmp_point = { 0, 0 };
10866 
10867 		/* pass in a 1-item array of icon positions, relative to x, y */
10868 		points = g_array_new (FALSE, TRUE, sizeof (GdkPoint));
10869 		g_array_append_val (points, tmp_point);
10870 	} else {
10871 		points = NULL;
10872 	}
10873 
10874 	fm_directory_view_widget_to_file_operation_position_xy (view, &x, &y);
10875 
10876 	fm_directory_view_move_copy_items (real_uri_list, points,
10877 					   target_uri != NULL ? target_uri : container_uri,
10878 					   action, x, y, view);
10879 
10880 	g_list_free_full (real_uri_list, g_free);
10881 
10882 	if (points != NULL)
10883 		g_array_free (points, TRUE);
10884 
10885 	g_free (container_uri);
10886 }
10887 
10888 void
fm_directory_view_handle_text_drop(FMDirectoryView * view,const char * text,const char * target_uri,GdkDragAction action,int x,int y)10889 fm_directory_view_handle_text_drop (FMDirectoryView  *view,
10890 				    const char       *text,
10891 				    const char       *target_uri,
10892 				    GdkDragAction     action,
10893 				    int               x,
10894 				    int               y)
10895 {
10896 	int length;
10897 	char *container_uri;
10898 	GdkPoint pos;
10899 
10900 	if (text == NULL) {
10901 		return;
10902 	}
10903 
10904 	g_return_if_fail (action == GDK_ACTION_COPY);
10905 
10906 	container_uri = NULL;
10907 	if (target_uri == NULL) {
10908 		container_uri = fm_directory_view_get_backing_uri (view);
10909 		g_assert (container_uri != NULL);
10910 	}
10911 
10912 	length = strlen (text);
10913 
10914 	pos.x = x;
10915 	pos.y = y;
10916 	fm_directory_view_widget_to_file_operation_position (view, &pos);
10917 
10918 	fm_directory_view_new_file_with_initial_contents (
10919 		view, target_uri != NULL ? target_uri : container_uri,
10920 		/* Translators: This is the filename used for when you dnd text to a directory */
10921 		_("dropped text.txt"),
10922 		text, length, &pos);
10923 
10924 	g_free (container_uri);
10925 }
10926 
10927 void
fm_directory_view_handle_raw_drop(FMDirectoryView * view,const char * raw_data,int length,const char * target_uri,const char * direct_save_uri,GdkDragAction action,int x,int y)10928 fm_directory_view_handle_raw_drop (FMDirectoryView  *view,
10929 				    const char       *raw_data,
10930 				    int               length,
10931 				    const char       *target_uri,
10932 				    const char       *direct_save_uri,
10933 				    GdkDragAction     action,
10934 				    int               x,
10935 				    int               y)
10936 {
10937 	char *container_uri, *filename;
10938 	GdkPoint pos;
10939 
10940 	if (raw_data == NULL) {
10941 		return;
10942 	}
10943 
10944 	g_return_if_fail (action == GDK_ACTION_COPY);
10945 
10946 	container_uri = NULL;
10947 	if (target_uri == NULL) {
10948 		container_uri = fm_directory_view_get_backing_uri (view);
10949 		g_assert (container_uri != NULL);
10950 	}
10951 
10952 	pos.x = x;
10953 	pos.y = y;
10954 	fm_directory_view_widget_to_file_operation_position (view, &pos);
10955 
10956 	filename = NULL;
10957 	if (direct_save_uri != NULL) {
10958 		GFile *direct_save_full;
10959 
10960 		direct_save_full = g_file_new_for_uri (direct_save_uri);
10961 		filename = g_file_get_basename (direct_save_full);
10962 	}
10963 	if (filename == NULL) {
10964 		/* Translators: This is the filename used for when you dnd raw
10965 		 * data to a directory, if the source didn't supply a name.
10966 		 */
10967 		filename = _("dropped data");
10968 	}
10969 
10970 	fm_directory_view_new_file_with_initial_contents (
10971 		view, target_uri != NULL ? target_uri : container_uri,
10972 		filename, raw_data, length, &pos);
10973 
10974 	g_free (container_uri);
10975 }
10976 
10977 gboolean
fm_directory_view_get_active(FMDirectoryView * view)10978 fm_directory_view_get_active (FMDirectoryView *view)
10979 {
10980 	g_assert (FM_IS_DIRECTORY_VIEW (view));
10981 	return view->details->active;
10982 }
10983 
10984 static GArray *
real_get_selected_icon_locations(FMDirectoryView * view)10985 real_get_selected_icon_locations (FMDirectoryView *view)
10986 {
10987         /* By default, just return an empty list. */
10988         return g_array_new (FALSE, TRUE, sizeof (GdkPoint));
10989 }
10990 
10991 static void
fm_directory_view_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)10992 fm_directory_view_set_property (GObject         *object,
10993 				guint            prop_id,
10994 				const GValue    *value,
10995 				GParamSpec      *pspec)
10996 {
10997   FMDirectoryView *directory_view;
10998   CajaWindowSlotInfo *slot;
10999   CajaWindowInfo *window;
11000 
11001   directory_view = FM_DIRECTORY_VIEW (object);
11002 
11003   switch (prop_id)  {
11004   case PROP_WINDOW_SLOT:
11005 	  g_assert (directory_view->details->slot == NULL);
11006 
11007 	  slot = CAJA_WINDOW_SLOT_INFO (g_value_get_object (value));
11008           window = caja_window_slot_info_get_window (slot);
11009 
11010 	  directory_view->details->slot = slot;
11011 	  directory_view->details->window = window;
11012 
11013 	  g_signal_connect_object (directory_view->details->slot,
11014 				   "active", G_CALLBACK (slot_active),
11015 				   directory_view, 0);
11016 	  g_signal_connect_object (directory_view->details->slot,
11017 				   "inactive", G_CALLBACK (slot_inactive),
11018 				   directory_view, 0);
11019 
11020 	  g_signal_connect_object (directory_view->details->window,
11021 				   "hidden-files-mode-changed", G_CALLBACK (hidden_files_mode_changed),
11022 				   directory_view, 0);
11023 	  fm_directory_view_init_show_hidden_files (directory_view);
11024 
11025 	  g_signal_connect_object (directory_view->details->window,
11026 				   "backup-files-mode-changed", G_CALLBACK (backup_files_mode_changed),
11027 				   directory_view, 0);
11028 	  fm_directory_view_init_show_backup_files (directory_view);
11029 
11030       break;
11031     default:
11032       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
11033       break;
11034     }
11035 }
11036 
11037 
11038 gboolean
fm_directory_view_handle_scroll_event(FMDirectoryView * directory_view,GdkEventScroll * event)11039 fm_directory_view_handle_scroll_event (FMDirectoryView *directory_view,
11040 				       GdkEventScroll *event)
11041 {
11042 	static gdouble total_delta_y = 0;
11043 	gdouble delta_x, delta_y;
11044 
11045 	if (event->state & GDK_CONTROL_MASK) {
11046 		switch (event->direction) {
11047 		case GDK_SCROLL_UP:
11048 			/* Zoom In */
11049 			fm_directory_view_bump_zoom_level (directory_view, 1);
11050 			return TRUE;
11051 
11052 		case GDK_SCROLL_DOWN:
11053 			/* Zoom Out */
11054 			fm_directory_view_bump_zoom_level (directory_view, -1);
11055 			return TRUE;
11056 
11057 		case GDK_SCROLL_SMOOTH:
11058 			gdk_event_get_scroll_deltas ((const GdkEvent *) event,
11059 			                             &delta_x, &delta_y);
11060 
11061 			/* try to emulate a normal scrolling event by summing deltas */
11062 			total_delta_y += delta_y;
11063 
11064 			if (total_delta_y >= 1) {
11065 				total_delta_y = 0;
11066 				/* emulate scroll down */
11067 				fm_directory_view_bump_zoom_level (directory_view, -1);
11068 				return TRUE;
11069 			} else if (total_delta_y <= - 1) {
11070 				total_delta_y = 0;
11071 				/* emulate scroll up */
11072 				fm_directory_view_bump_zoom_level (directory_view, 1);
11073 				return TRUE;
11074 			} else {
11075 				/* eat event */
11076 				return TRUE;
11077 			}
11078 
11079 		case GDK_SCROLL_LEFT:
11080 		case GDK_SCROLL_RIGHT:
11081 			break;
11082 
11083 		default:
11084 			g_assert_not_reached ();
11085 		}
11086 	}
11087 
11088 	return FALSE;
11089 }
11090 
11091 /* handle Shift+Scroll, which will cause a zoom-in/out */
11092 static gboolean
fm_directory_view_scroll_event(GtkWidget * widget,GdkEventScroll * event)11093 fm_directory_view_scroll_event (GtkWidget *widget,
11094 				GdkEventScroll *event)
11095 {
11096 	FMDirectoryView *directory_view;
11097 
11098 	directory_view = FM_DIRECTORY_VIEW (widget);
11099 	if (fm_directory_view_handle_scroll_event (directory_view, event)) {
11100 		return TRUE;
11101 	}
11102 
11103 	return GTK_WIDGET_CLASS (parent_class)->scroll_event (widget, event);
11104 }
11105 
11106 
11107 static void
fm_directory_view_parent_set(GtkWidget * widget,GtkWidget * old_parent)11108 fm_directory_view_parent_set (GtkWidget *widget,
11109 			      GtkWidget *old_parent)
11110 {
11111 	FMDirectoryView *view;
11112 	GtkWidget *parent;
11113 
11114 	view = FM_DIRECTORY_VIEW (widget);
11115 
11116 	parent = gtk_widget_get_parent (widget);
11117 	g_assert (parent == NULL || old_parent == NULL);
11118 
11119 	if (GTK_WIDGET_CLASS (parent_class)->parent_set != NULL) {
11120 		GTK_WIDGET_CLASS (parent_class)->parent_set (widget, old_parent);
11121 	}
11122 
11123 	if (parent != NULL) {
11124 		g_assert (old_parent == NULL);
11125 
11126 		if (view->details->slot ==
11127 		    caja_window_info_get_active_slot (view->details->window)) {
11128 			view->details->active = TRUE;
11129 
11130 			fm_directory_view_merge_menus (view);
11131 			schedule_update_menus (view);
11132 		}
11133 	} else {
11134 		fm_directory_view_unmerge_menus (view);
11135 		remove_update_menus_timeout_callback (view);
11136 	}
11137 }
11138 
11139 static void
fm_directory_view_class_init(FMDirectoryViewClass * klass)11140 fm_directory_view_class_init (FMDirectoryViewClass *klass)
11141 {
11142 	GtkWidgetClass *widget_class;
11143 	GtkScrolledWindowClass *scrolled_window_class;
11144 	GtkBindingSet *binding_set;
11145 
11146 	widget_class = GTK_WIDGET_CLASS (klass);
11147 	scrolled_window_class = GTK_SCROLLED_WINDOW_CLASS (klass);
11148 
11149 	G_OBJECT_CLASS (klass)->set_property = fm_directory_view_set_property;
11150 	G_OBJECT_CLASS (klass)->finalize = fm_directory_view_finalize;
11151 
11152 	widget_class->destroy = fm_directory_view_destroy;
11153 
11154 	widget_class->scroll_event = fm_directory_view_scroll_event;
11155 	widget_class->parent_set = fm_directory_view_parent_set;
11156 
11157 	/* Get rid of the strange 3-pixel gap that GtkScrolledWindow
11158 	 * uses by default. It does us no good.
11159 	 */
11160 	scrolled_window_class->scrollbar_spacing = 0;
11161 
11162 	signals[ADD_FILE] =
11163 		g_signal_new ("add_file",
11164 		              G_TYPE_FROM_CLASS (klass),
11165 		              G_SIGNAL_RUN_LAST,
11166 		              G_STRUCT_OFFSET (FMDirectoryViewClass, add_file),
11167 		              NULL, NULL,
11168 		              fm_marshal_VOID__OBJECT_OBJECT,
11169 		              G_TYPE_NONE, 2, CAJA_TYPE_FILE, CAJA_TYPE_DIRECTORY);
11170 	signals[BEGIN_FILE_CHANGES] =
11171 		g_signal_new ("begin_file_changes",
11172 		              G_TYPE_FROM_CLASS (klass),
11173 		              G_SIGNAL_RUN_LAST,
11174 		              G_STRUCT_OFFSET (FMDirectoryViewClass, begin_file_changes),
11175 		              NULL, NULL,
11176 		              g_cclosure_marshal_VOID__VOID,
11177 		              G_TYPE_NONE, 0);
11178 	signals[BEGIN_LOADING] =
11179 		g_signal_new ("begin_loading",
11180 		              G_TYPE_FROM_CLASS (klass),
11181 		              G_SIGNAL_RUN_LAST,
11182 		              G_STRUCT_OFFSET (FMDirectoryViewClass, begin_loading),
11183 		              NULL, NULL,
11184 		              g_cclosure_marshal_VOID__VOID,
11185 		              G_TYPE_NONE, 0);
11186 	signals[CLEAR] =
11187 		g_signal_new ("clear",
11188 		              G_TYPE_FROM_CLASS (klass),
11189 		              G_SIGNAL_RUN_LAST,
11190 		              G_STRUCT_OFFSET (FMDirectoryViewClass, clear),
11191 		              NULL, NULL,
11192 		              g_cclosure_marshal_VOID__VOID,
11193 		              G_TYPE_NONE, 0);
11194 	signals[END_FILE_CHANGES] =
11195 		g_signal_new ("end_file_changes",
11196 		              G_TYPE_FROM_CLASS (klass),
11197 		              G_SIGNAL_RUN_LAST,
11198 		              G_STRUCT_OFFSET (FMDirectoryViewClass, end_file_changes),
11199 		              NULL, NULL,
11200 		              g_cclosure_marshal_VOID__VOID,
11201 		              G_TYPE_NONE, 0);
11202 	signals[FLUSH_ADDED_FILES] =
11203 		g_signal_new ("flush_added_files",
11204 		              G_TYPE_FROM_CLASS (klass),
11205 		              G_SIGNAL_RUN_LAST,
11206 		              G_STRUCT_OFFSET (FMDirectoryViewClass, flush_added_files),
11207 		              NULL, NULL,
11208 		              g_cclosure_marshal_VOID__VOID,
11209 		              G_TYPE_NONE, 0);
11210 	signals[END_LOADING] =
11211 		g_signal_new ("end_loading",
11212 		              G_TYPE_FROM_CLASS (klass),
11213 		              G_SIGNAL_RUN_LAST,
11214 		              G_STRUCT_OFFSET (FMDirectoryViewClass, end_loading),
11215 		              NULL, NULL,
11216 		              g_cclosure_marshal_VOID__BOOLEAN,
11217 		              G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
11218 	signals[FILE_CHANGED] =
11219 		g_signal_new ("file_changed",
11220 		              G_TYPE_FROM_CLASS (klass),
11221 		              G_SIGNAL_RUN_LAST,
11222 		              G_STRUCT_OFFSET (FMDirectoryViewClass, file_changed),
11223 		              NULL, NULL,
11224 		              fm_marshal_VOID__OBJECT_OBJECT,
11225 		              G_TYPE_NONE, 2, CAJA_TYPE_FILE, CAJA_TYPE_DIRECTORY);
11226 	signals[LOAD_ERROR] =
11227 		g_signal_new ("load_error",
11228 		              G_TYPE_FROM_CLASS (klass),
11229 		              G_SIGNAL_RUN_LAST,
11230 		              G_STRUCT_OFFSET (FMDirectoryViewClass, load_error),
11231 		              NULL, NULL,
11232 		              g_cclosure_marshal_VOID__POINTER,
11233 		              G_TYPE_NONE, 1, G_TYPE_POINTER);
11234 	signals[REMOVE_FILE] =
11235 		g_signal_new ("remove_file",
11236 		              G_TYPE_FROM_CLASS (klass),
11237 		              G_SIGNAL_RUN_LAST,
11238 		              G_STRUCT_OFFSET (FMDirectoryViewClass, remove_file),
11239 		              NULL, NULL,
11240 		              fm_marshal_VOID__OBJECT_OBJECT,
11241 		              G_TYPE_NONE, 2, CAJA_TYPE_FILE, CAJA_TYPE_DIRECTORY);
11242 
11243 	klass->accepts_dragged_files = real_accepts_dragged_files;
11244 	klass->file_still_belongs = real_file_still_belongs;
11245 	klass->get_emblem_names_to_exclude = real_get_emblem_names_to_exclude;
11246 	klass->get_selected_icon_locations = real_get_selected_icon_locations;
11247 	klass->is_read_only = real_is_read_only;
11248 	klass->load_error = real_load_error;
11249 	klass->can_rename_file = can_rename_file;
11250 	klass->start_renaming_file = start_renaming_file;
11251 	klass->supports_creating_files = real_supports_creating_files;
11252 	klass->supports_properties = real_supports_properties;
11253 	klass->supports_zooming = real_supports_zooming;
11254 	klass->using_manual_layout = real_using_manual_layout;
11255 	klass->merge_menus = real_merge_menus;
11256 	klass->unmerge_menus = real_unmerge_menus;
11257 	klass->update_menus = real_update_menus;
11258 	klass->set_is_active = real_set_is_active;
11259 	/* Function pointers that subclasses must override */
11260 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, add_file);
11261 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, bump_zoom_level);
11262 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, can_zoom_in);
11263 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, can_zoom_out);
11264 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, clear);
11265 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, file_changed);
11266 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_background_widget);
11267 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_selection);
11268 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_selection_for_file_transfer);
11269 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_item_count);
11270 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, is_empty);
11271 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, reset_to_defaults);
11272 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, restore_default_zoom_level);
11273 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, select_all);
11274 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, set_selection);
11275 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, invert_selection);
11276 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, zoom_to_level);
11277 	EEL_ASSIGN_MUST_OVERRIDE_SIGNAL (klass, fm_directory_view, get_zoom_level);
11278 
11279 	copied_files_atom = gdk_atom_intern ("x-special/mate-copied-files", FALSE);
11280 
11281 	g_object_class_install_property (G_OBJECT_CLASS (klass),
11282 					 PROP_WINDOW_SLOT,
11283 					 g_param_spec_object ("window-slot",
11284 							      "Window Slot",
11285 							      "The parent window slot reference",
11286 							      CAJA_TYPE_WINDOW_SLOT_INFO,
11287 							      G_PARAM_WRITABLE |
11288 							      G_PARAM_CONSTRUCT_ONLY));
11289 
11290 	signals[TRASH] =
11291 		g_signal_new ("trash",
11292 			      G_TYPE_FROM_CLASS (klass),
11293 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
11294 			      G_STRUCT_OFFSET (FMDirectoryViewClass, trash),
11295 			      g_signal_accumulator_true_handled, NULL,
11296 			      fm_marshal_BOOLEAN__VOID,
11297 			      G_TYPE_BOOLEAN, 0);
11298 	signals[DELETE] =
11299 		g_signal_new ("delete",
11300 			      G_TYPE_FROM_CLASS (klass),
11301 			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
11302 			      G_STRUCT_OFFSET (FMDirectoryViewClass, delete),
11303 			      g_signal_accumulator_true_handled, NULL,
11304 			      fm_marshal_BOOLEAN__VOID,
11305 			      G_TYPE_BOOLEAN, 0);
11306 
11307 	binding_set = gtk_binding_set_by_class (klass);
11308 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, 0,
11309 				      "trash", 0);
11310 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, 0,
11311 				      "trash", 0);
11312 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK,
11313 				      "delete", 0);
11314 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Delete, GDK_SHIFT_MASK,
11315 				      "delete", 0);
11316 
11317 	klass->trash = real_trash;
11318 	klass->delete = real_delete;
11319 }
11320 
11321 static void
undo_redo_menu_update_callback(CajaUndoStackManager * manager,gpointer arg,gpointer data)11322 undo_redo_menu_update_callback (CajaUndoStackManager* manager, gpointer arg, gpointer data)
11323 {
11324 	FMDirectoryView *view;
11325 	view = FM_DIRECTORY_VIEW (data);
11326 
11327 	CajaUndoStackMenuData* menudata = (CajaUndoStackMenuData*) arg;
11328 
11329 	g_free(view->details->undo_action_label);
11330 	g_free(view->details->undo_action_description);
11331 	g_free(view->details->redo_action_label);
11332 	g_free(view->details->redo_action_description);
11333 
11334 	view->details->undo_active = menudata->undo_label ? TRUE : FALSE;
11335 	view->details->redo_active = menudata->redo_label ? TRUE : FALSE;
11336 
11337 	view->details->undo_action_label = g_strdup (menudata->undo_label);
11338 	view->details->undo_action_description = g_strdup (menudata->undo_description);
11339 	view->details->redo_action_label = g_strdup (menudata->redo_label);
11340 	view->details->redo_action_description =  g_strdup (menudata->redo_description);
11341 
11342 	schedule_update_menus (view);
11343 }
11344 
11345 static void
undo_update_menu(FMDirectoryView * view)11346 undo_update_menu (FMDirectoryView *view)
11347 {
11348 	GtkAction *action;
11349 	gboolean available = FALSE;
11350 	gchar* label;
11351 	gchar* tooltip;
11352 
11353 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
11354 	/* Update undo entry */
11355 	action = gtk_action_group_get_action (view->details->dir_action_group,
11356 					      FM_ACTION_UNDO);
11357 	G_GNUC_END_IGNORE_DEPRECATIONS;
11358 	available = view->details->undo_active;
11359 	if (available) {
11360 		label = view->details->undo_action_label;
11361 		tooltip = view->details->undo_action_description;
11362 	} else {
11363 		/* Reset to default info */
11364 		label = _("Undo");
11365 		tooltip = _("Undo the last action");
11366 	}
11367 	g_object_set (action,
11368 		      "label", label,
11369 		      "tooltip", tooltip,
11370 		      NULL);
11371 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
11372 	gtk_action_set_sensitive (action, available);
11373 
11374 	/* Update redo entry */
11375 	action = gtk_action_group_get_action (view->details->dir_action_group,
11376                                           FM_ACTION_REDO);
11377 	G_GNUC_END_IGNORE_DEPRECATIONS;
11378 	available = view->details->redo_active;
11379 	if (available) {
11380 		label = view->details->redo_action_label;
11381 		tooltip = view->details->redo_action_description;
11382 	} else {
11383 		/* Reset to default info */
11384 		label = _("Redo");
11385 		tooltip = _("Redo the last undone action");
11386 	}
11387 	g_object_set (action,
11388 		      "label", label,
11389 		      "tooltip", tooltip,
11390 		      NULL);
11391 	G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
11392 	gtk_action_set_sensitive (action, available);
11393 	G_GNUC_END_IGNORE_DEPRECATIONS;
11394 }
11395