1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 
3 /* fm-list-view.c - implementation of list view of directory.
4 
5    Copyright (C) 2000 Eazel, Inc.
6    Copyright (C) 2001, 2002 Anders Carlsson <andersca@gnu.org>
7 
8    The Gnome Library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Library 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    The Gnome Library 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    Library General Public License for more details.
17 
18    You should have received a copy of the GNU Library General Public
19    License along with the Gnome Library; see the file COPYING.LIB.  If not,
20    write to the Free Software Foundation, Inc., 51 Franklin Street - Suite 500,
21    Boston, MA 02110-1335, USA.
22 
23    Authors: John Sullivan <sullivan@eazel.com>
24             Anders Carlsson <andersca@gnu.org>
25 	    David Emory Watson <dwatson@cs.ucr.edu>
26 */
27 
28 #include <config.h>
29 #include "nemo-list-view.h"
30 
31 #include "nemo-application.h"
32 #include "nemo-list-model.h"
33 #include "nemo-error-reporting.h"
34 #include "nemo-view-dnd.h"
35 #include "nemo-view-factory.h"
36 #include "nemo-window.h"
37 
38 #include <string.h>
39 #include <eel/eel-vfs-extensions.h>
40 #include <eel/eel-gdk-extensions.h>
41 #include <eel/eel-glib-extensions.h>
42 #include <gdk/gdk.h>
43 #include <gdk/gdkkeysyms.h>
44 #include <gtk/gtk.h>
45 #include <libegg/eggtreemultidnd.h>
46 #include <glib/gi18n.h>
47 #include <glib-object.h>
48 #include <libnemo-extension/nemo-column-provider.h>
49 #include <libnemo-private/nemo-clipboard-monitor.h>
50 #include <libnemo-private/nemo-column-chooser.h>
51 #include <libnemo-private/nemo-column-utilities.h>
52 #include <libnemo-private/nemo-dnd.h>
53 #include <libnemo-private/nemo-file-dnd.h>
54 #include <libnemo-private/nemo-file-utilities.h>
55 #include <libnemo-private/nemo-ui-utilities.h>
56 #include <libnemo-private/nemo-global-preferences.h>
57 #include <libnemo-private/nemo-icon-dnd.h>
58 #include <libnemo-private/nemo-metadata.h>
59 #include <libnemo-private/nemo-module.h>
60 #include <libnemo-private/nemo-thumbnails.h>
61 #include <libnemo-private/nemo-tree-view-drag-dest.h>
62 #include <libnemo-private/nemo-clipboard.h>
63 
64 #define DEBUG_FLAG NEMO_DEBUG_LIST_VIEW
65 #include <libnemo-private/nemo-debug.h>
66 
67 struct NemoListViewDetails {
68 	GtkTreeView *tree_view;
69 	NemoListModel *model;
70 	GtkActionGroup *list_action_group;
71 	guint list_merge_id;
72 
73 	GtkTreeViewColumn   *file_name_column;
74 	int file_name_column_num;
75 
76 	GtkCellRendererPixbuf *pixbuf_cell;
77 	GtkCellRendererText   *file_name_cell;
78 	GList *cells;
79 	GtkCellEditable *editable_widget;
80 
81 	NemoZoomLevel zoom_level;
82 
83 	NemoTreeViewDragDest *drag_dest;
84 
85 	GtkTreePath *double_click_path[2]; /* Both clicks in a double click need to be on the same row */
86 
87 	GtkTreePath *new_selection_path;   /* Path of the new selection after removing a file */
88 
89 	GtkTreePath *hover_path;
90 
91 	guint drag_button;
92 	int drag_x;
93 	int drag_y;
94 
95     gint ok_to_load_deferred_attrs;
96     guint update_visible_icons_id;
97 
98     gboolean rename_on_release;
99 	gboolean drag_started;
100 	gboolean ignore_button_release;
101 	gboolean row_selected_on_button_down;
102 	gboolean menus_ready;
103 	gboolean active;
104 
105     gboolean rubber_banding;
106 
107 	GHashTable *columns;
108 	GtkWidget *column_editor;
109 
110 	char *original_name;
111 
112 	NemoFile *renaming_file;
113 	gboolean rename_done;
114 	guint renaming_file_activate_timeout;
115 
116 	gulong clipboard_handler_id;
117 
118 	GQuark last_sort_attr;
119 
120     gboolean tooltip_flags;
121     gboolean show_tooltips;
122 
123     gboolean click_to_rename;
124 
125     GList *current_selection;
126     gint current_selection_count;
127 };
128 
129 struct SelectionForeachData {
130 	GList *list;
131 	GtkTreeSelection *selection;
132 };
133 
134 /*
135  * The row height should be large enough to not clip emblems.
136  * Computing this would be costly, so we just choose a number
137  * that works well with the set of emblems we've designed.
138  */
139 #define LIST_VIEW_MINIMUM_ROW_HEIGHT	28
140 
141 /* We wait two seconds after row is collapsed to unload the subdirectory */
142 #define COLLAPSE_TO_UNLOAD_DELAY 2
143 
144 /* Wait for the rename to end when activating a file being renamed */
145 #define WAIT_FOR_RENAME_ON_ACTIVATE 200
146 
147 #define INITIAL_UPDATE_VISIBLE_DELAY 300
148 #define NORMAL_UPDATE_VISIBLE_DELAY 50
149 
150 static GdkCursor *              hand_cursor = NULL;
151 
152 static GtkTargetList *          source_target_list = NULL;
153 
154 static GList *nemo_list_view_get_selection                   (NemoView   *view);
155 static void   nemo_list_view_update_selection                (NemoView *view);
156 static GList *nemo_list_view_get_selection_for_file_transfer (NemoView   *view);
157 static void   nemo_list_view_set_zoom_level                  (NemoListView        *view,
158 								  NemoZoomLevel  new_level,
159 								  gboolean           always_set_level);
160 static void   nemo_list_view_scale_font_size                 (NemoListView        *view,
161 								  NemoZoomLevel  new_level);
162 static void   nemo_list_view_scroll_to_file                  (NemoListView        *view,
163 								  NemoFile      *file);
164 static void   nemo_list_view_rename_callback                 (NemoFile      *file,
165 								  GFile             *result_location,
166 								  GError            *error,
167 								  gpointer           callback_data);
168 
169 static void nemo_list_view_start_renaming_file               (NemoView *view,
170                                                               NemoFile *file,
171                                                               gboolean  select_all);
172 
173 static void   apply_columns_settings                             (NemoListView *list_view,
174                                                                   char **column_order,
175                                                                   char **visible_columns);
176 static char **get_visible_columns                                (NemoListView *list_view);
177 static char **get_default_visible_columns                        (NemoListView *list_view);
178 static char **get_column_order                                   (NemoListView *list_view);
179 static char **get_default_column_order                           (NemoListView *list_view);
180 
181 static void   set_columns_settings_from_metadata_and_preferences (NemoListView *list_view);
182 static void   queue_update_visible_icons (NemoListView *view, gint delay);
183 static NemoZoomLevel nemo_list_view_get_zoom_level (NemoView *view);
184 static void   prioritize_visible_files (NemoListView *view);
185 
186 G_DEFINE_TYPE (NemoListView, nemo_list_view, NEMO_TYPE_VIEW);
187 
188 static gint click_policy = NEMO_CLICK_POLICY_SINGLE;
189 
190 static const char * default_trash_visible_columns[] = {
191 	"name", "size", "type", "trashed_on", "trash_orig_path", NULL
192 };
193 
194 static const char * default_trash_columns_order[] = {
195 	"name", "size", "type", "trashed_on", "trash_orig_path", NULL
196 };
197 
198 static const char * default_recent_visible_columns[] = {
199     "name", "size", "type", "date_accessed", NULL
200 };
201 
202 static const char * default_recent_columns_order[] = {
203     "name", "size", "type", "date_accessed", NULL
204 };
205 
206 static const char * default_favorites_visible_columns[] = {
207     "name", "size", "date_modified", NULL
208 };
209 
210 static const char * default_favorites_columns_order[] = {
211     "name", "size", "date_modified", NULL
212 };
213 
214 static gchar **
string_array_from_string_glist(GList * list)215 string_array_from_string_glist (GList *list)
216 {
217     GPtrArray *res;
218     GList *l;
219     gchar **ret;
220 
221     res = g_ptr_array_new ();
222 
223     for (l = list; l != NULL; l = l->next) {
224         g_ptr_array_add (res, g_strdup (l->data));
225     }
226 
227     g_ptr_array_add (res, NULL);
228 
229     ret = (char **) g_ptr_array_free (res, FALSE);
230 
231     return ret;
232 }
233 
234 static const gchar*
get_default_sort_order(NemoFile * file,gboolean * reversed)235 get_default_sort_order (NemoFile *file, gboolean *reversed)
236 {
237 	NemoFileSortType default_sort_order;
238 	gboolean default_sort_reversed;
239 	const gchar *retval;
240 	const char *attributes[] = {
241 		"name", /* is really "manually" which doesn't apply to lists */
242 		"name",
243 		"size",
244 		"type",
245 		"detailed_type",
246 		"date_modified",
247 		"date_accessed",
248 		"trashed_on",
249 		NULL
250 	};
251 
252 	retval = nemo_file_get_default_sort_attribute (file, reversed);
253 
254 	if (retval == NULL) {
255 		default_sort_order = g_settings_get_enum (nemo_preferences,
256 							  NEMO_PREFERENCES_DEFAULT_SORT_ORDER);
257 		default_sort_reversed = g_settings_get_boolean (nemo_preferences,
258 								NEMO_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER);
259 
260 		retval = attributes[default_sort_order];
261 		*reversed = default_sort_reversed;
262 	}
263 
264 	return retval;
265 }
266 
267 static void
tooltip_prefs_changed_callback(NemoListView * view)268 tooltip_prefs_changed_callback (NemoListView *view)
269 {
270     view->details->show_tooltips = g_settings_get_boolean (nemo_preferences,
271                                                            NEMO_PREFERENCES_TOOLTIPS_LIST_VIEW);
272 
273     view->details->tooltip_flags = nemo_global_preferences_get_tooltip_flags ();
274 }
275 
276 static void
list_selection_changed_callback(GtkTreeSelection * selection,gpointer user_data)277 list_selection_changed_callback (GtkTreeSelection *selection, gpointer user_data)
278 {
279 	NemoView *view;
280 
281 	view = NEMO_VIEW (user_data);
282 
283 	nemo_view_notify_selection_changed (view);
284 }
285 
286 /* Move these to eel? */
287 
288 static void
tree_selection_foreach_set_boolean(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer callback_data)289 tree_selection_foreach_set_boolean (GtkTreeModel *model,
290 				    GtkTreePath *path,
291 				    GtkTreeIter *iter,
292 				    gpointer callback_data)
293 {
294 	* (gboolean *) callback_data = TRUE;
295 }
296 
297 static gboolean
tree_selection_not_empty(GtkTreeSelection * selection)298 tree_selection_not_empty (GtkTreeSelection *selection)
299 {
300 	gboolean not_empty;
301 
302 	not_empty = FALSE;
303 	gtk_tree_selection_selected_foreach (selection,
304 					     tree_selection_foreach_set_boolean,
305 					     &not_empty);
306 	return not_empty;
307 }
308 
309 static gboolean
tree_view_has_selection(GtkTreeView * view)310 tree_view_has_selection (GtkTreeView *view)
311 {
312 	return tree_selection_not_empty (gtk_tree_view_get_selection (view));
313 }
314 
315 static void
preview_selected_items(NemoListView * view)316 preview_selected_items (NemoListView *view)
317 {
318 	GList *file_list;
319 
320 	file_list = nemo_list_view_get_selection (NEMO_VIEW (view));
321 
322 	if (file_list != NULL) {
323 		nemo_view_preview_files (NEMO_VIEW (view),
324 					     file_list, NULL);
325 		nemo_file_list_free (file_list);
326 	}
327 }
328 
329 static void
activate_selected_items(NemoListView * view)330 activate_selected_items (NemoListView *view)
331 {
332 	GList *file_list;
333 
334 	file_list = nemo_list_view_get_selection (NEMO_VIEW (view));
335 
336 
337 	if (view->details->renaming_file) {
338 		/* We're currently renaming a file, wait until the rename is
339 		   finished, or the activation uri will be wrong */
340 		if (view->details->renaming_file_activate_timeout == 0) {
341 			view->details->renaming_file_activate_timeout =
342 				g_timeout_add (WAIT_FOR_RENAME_ON_ACTIVATE, (GSourceFunc) activate_selected_items, view);
343 		}
344 		return;
345 	}
346 
347 	if (view->details->renaming_file_activate_timeout != 0) {
348 		g_source_remove (view->details->renaming_file_activate_timeout);
349 		view->details->renaming_file_activate_timeout = 0;
350 	}
351 
352 	nemo_view_activate_files (NEMO_VIEW (view),
353 				      file_list,
354 				      0, TRUE);
355 	nemo_file_list_free (file_list);
356 
357 }
358 
359 static void
activate_selected_items_alternate(NemoListView * view,NemoFile * file,gboolean open_in_tab)360 activate_selected_items_alternate (NemoListView *view,
361 				   NemoFile *file,
362 				   gboolean open_in_tab)
363 {
364 	GList *file_list;
365 	NemoWindowOpenFlags flags;
366 
367 	flags = 0;
368 
369 	if (g_settings_get_boolean (nemo_preferences,
370 				    NEMO_PREFERENCES_ALWAYS_USE_BROWSER)) {
371 		if (open_in_tab) {
372 			flags |= NEMO_WINDOW_OPEN_FLAG_NEW_TAB;
373 		} else {
374 			flags |= NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW;
375 		}
376 	} else {
377 		flags |= NEMO_WINDOW_OPEN_FLAG_CLOSE_BEHIND;
378 	}
379 
380 	if (file != NULL) {
381 		nemo_file_ref (file);
382 		file_list = g_list_prepend (NULL, file);
383 	} else {
384 		file_list = nemo_list_view_get_selection (NEMO_VIEW (view));
385 	}
386 	nemo_view_activate_files (NEMO_VIEW (view),
387 				      file_list,
388 				      flags,
389 				      TRUE);
390 	nemo_file_list_free (file_list);
391 
392 }
393 
394 static gboolean
button_event_modifies_selection(GdkEventButton * event)395 button_event_modifies_selection (GdkEventButton *event)
396 {
397 	return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
398 }
399 
400 static void
nemo_list_view_did_not_drag(NemoListView * view,GdkEventButton * event)401 nemo_list_view_did_not_drag (NemoListView *view,
402 				 GdkEventButton *event)
403 {
404 	GtkTreeView *tree_view;
405 	GtkTreeSelection *selection;
406 	GtkTreePath *path;
407 
408 	tree_view = view->details->tree_view;
409 	selection = gtk_tree_view_get_selection (tree_view);
410 
411 	if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
412 					   &path, NULL, NULL, NULL)) {
413 		if ((event->button == 1 || event->button == 2)
414 		    && ((event->state & GDK_CONTROL_MASK) != 0 ||
415 			(event->state & GDK_SHIFT_MASK) == 0)
416 		    && view->details->row_selected_on_button_down) {
417 			if (!button_event_modifies_selection (event)) {
418 				gtk_tree_selection_unselect_all (selection);
419 				gtk_tree_selection_select_path (selection, path);
420 			} else {
421 				gtk_tree_selection_unselect_path (selection, path);
422 			}
423 		}
424 
425 		if ((click_policy == NEMO_CLICK_POLICY_SINGLE)
426 		    && !button_event_modifies_selection(event)) {
427 			if (event->button == 1) {
428 				activate_selected_items (view);
429 			} else if (event->button == 2) {
430 				activate_selected_items_alternate (view, NULL, TRUE);
431 			}
432 		}
433 
434         if (view->details->rename_on_release) {
435             NemoFile *file = nemo_list_model_file_for_path (view->details->model, path);
436             nemo_list_view_start_renaming_file (NEMO_VIEW (view),
437                                                 file,
438                                                 nemo_file_is_directory (file));
439             nemo_file_unref (file);
440             view->details->rename_on_release = FALSE;
441         }
442 
443 		gtk_tree_path_free (path);
444 	}
445 
446 }
447 
448 static void
drag_data_get_callback(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time)449 drag_data_get_callback (GtkWidget *widget,
450 			GdkDragContext *context,
451 			GtkSelectionData *selection_data,
452 			guint info,
453 			guint time)
454 {
455 	GtkTreeView *tree_view;
456 	GtkTreeModel *model;
457 	GList *ref_list;
458 
459 	tree_view = GTK_TREE_VIEW (widget);
460 
461 	model = gtk_tree_view_get_model (tree_view);
462 
463 	if (model == NULL) {
464 		return;
465 	}
466 
467 	ref_list = g_object_get_data (G_OBJECT (context), "drag-info");
468 
469 	if (ref_list == NULL) {
470 		return;
471 	}
472 
473 	if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model)) {
474 		egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
475 							  ref_list,
476 							  selection_data);
477 	}
478 }
479 
480 static void
filtered_selection_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)481 filtered_selection_foreach (GtkTreeModel *model,
482 			    GtkTreePath *path,
483 			    GtkTreeIter *iter,
484 			    gpointer data)
485 {
486 	struct SelectionForeachData *selection_data;
487 	GtkTreeIter parent;
488 	GtkTreeIter child;
489 
490 	selection_data = data;
491 
492 	/* If the parent folder is also selected, don't include this file in the
493 	 * file operation, since that would copy it to the toplevel target instead
494 	 * of keeping it as a child of the copied folder
495 	 */
496 	child = *iter;
497 	while (gtk_tree_model_iter_parent (model, &parent, &child)) {
498 		if (gtk_tree_selection_iter_is_selected (selection_data->selection,
499 							 &parent)) {
500 			return;
501 		}
502 		child = parent;
503 	}
504 
505 	selection_data->list = g_list_prepend (selection_data->list,
506 					       gtk_tree_row_reference_new (model, path));
507 }
508 
509 static GList *
get_filtered_selection_refs(GtkTreeView * tree_view)510 get_filtered_selection_refs (GtkTreeView *tree_view)
511 {
512 	struct SelectionForeachData selection_data;
513 
514 	selection_data.list = NULL;
515 	selection_data.selection = gtk_tree_view_get_selection (tree_view);
516 
517 	gtk_tree_selection_selected_foreach (selection_data.selection,
518 					     filtered_selection_foreach,
519 					     &selection_data);
520 	return g_list_reverse (selection_data.list);
521 }
522 
523 static void
ref_list_free(GList * ref_list)524 ref_list_free (GList *ref_list)
525 {
526 	g_list_foreach (ref_list, (GFunc) gtk_tree_row_reference_free, NULL);
527 	g_list_free (ref_list);
528 }
529 
530 static void
stop_drag_check(NemoListView * view)531 stop_drag_check (NemoListView *view)
532 {
533 	view->details->drag_button = 0;
534 }
535 
536 static cairo_surface_t *
get_drag_surface(NemoListView * view)537 get_drag_surface (NemoListView *view)
538 {
539 	GtkTreeModel *model;
540 	GtkTreePath *path;
541 	GtkTreeIter iter;
542 	cairo_surface_t *ret;
543 	GdkRectangle cell_area;
544 
545 	ret = NULL;
546 
547 	if (gtk_tree_view_get_path_at_pos (view->details->tree_view,
548 					   view->details->drag_x,
549 					   view->details->drag_y,
550 					   &path, NULL, NULL, NULL)) {
551 		model = gtk_tree_view_get_model (view->details->tree_view);
552 		gtk_tree_model_get_iter (model, &iter, path);
553 		gtk_tree_model_get (model, &iter,
554 				    nemo_list_model_get_column_id_from_zoom_level (view->details->zoom_level),
555 				    &ret,
556 				    -1);
557 
558 		gtk_tree_view_get_cell_area (view->details->tree_view,
559 					     path,
560 					     view->details->file_name_column,
561 					     &cell_area);
562 
563 		gtk_tree_path_free (path);
564 	}
565 
566 	return ret;
567 }
568 
569 static void
drag_begin_callback(GtkWidget * widget,GdkDragContext * context,NemoListView * view)570 drag_begin_callback (GtkWidget *widget,
571 		     GdkDragContext *context,
572 		     NemoListView *view)
573 {
574 	GList *ref_list;
575 
576     cairo_surface_t *surface;
577 
578     surface = get_drag_surface (view);
579     if (surface) {
580         gtk_drag_set_icon_surface (context, surface);
581         cairo_surface_destroy (surface);
582 	} else {
583 		gtk_drag_set_icon_default (context);
584 	}
585 
586 	stop_drag_check (view);
587 	view->details->drag_started = TRUE;
588 
589 	ref_list = get_filtered_selection_refs (GTK_TREE_VIEW (widget));
590 	g_object_set_data_full (G_OBJECT (context),
591 				"drag-info",
592 				ref_list,
593 				(GDestroyNotify)ref_list_free);
594 }
595 
596 static gboolean
motion_notify_callback(GtkWidget * widget,GdkEventMotion * event,gpointer callback_data)597 motion_notify_callback (GtkWidget *widget,
598 			GdkEventMotion *event,
599 			gpointer callback_data)
600 {
601 	NemoListView *view;
602 
603 	view = NEMO_LIST_VIEW (callback_data);
604 
605     if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))) {
606         return GDK_EVENT_PROPAGATE;
607     }
608 
609 	if (click_policy == NEMO_CLICK_POLICY_SINGLE) {
610 		GtkTreePath *old_hover_path;
611 
612 		old_hover_path = view->details->hover_path;
613 		gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
614 					       event->x, event->y,
615 					       &view->details->hover_path,
616 					       NULL, NULL, NULL);
617 
618 		if ((old_hover_path != NULL) != (view->details->hover_path != NULL)) {
619 			if (view->details->hover_path != NULL) {
620 				gdk_window_set_cursor (gtk_widget_get_window (widget), hand_cursor);
621 			} else {
622 				gdk_window_set_cursor (gtk_widget_get_window (widget), NULL);
623 			}
624 		}
625 
626 		if (old_hover_path != NULL) {
627 			gtk_tree_path_free (old_hover_path);
628 		}
629 	}
630 
631     /* If we're already rubber-banding, we can skip all of this logic and just let the parent
632      * class continue to handle selection */
633     if (view->details->drag_button != 0 && !view->details->rubber_banding) {
634         GtkTreePath *path;
635         GtkTreeSelection *selection;
636         gboolean is_new_self_selection;
637 
638         selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
639 
640         gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
641                            event->x, event->y,
642                            &path,
643                            NULL, NULL, NULL);
644 
645         /* This looks complicated but it's just verbose:  We'll only consider allowing rubber-banding
646          * to begin if the following are TRUE: a) The current row is the only row currently selected,
647          * and  b) This is the first click that's been made on this row - meaning, the button-press-event
648          * that preceded this motion-event was the one that caused this row to be selected. */
649         is_new_self_selection = gtk_tree_selection_count_selected_rows (selection) == 1 &&
650                                 gtk_tree_selection_path_is_selected (selection, path) &&
651                                 (!view->details->double_click_path[1] ||
652                                 (view->details->double_click_path[1] &&
653                                 gtk_tree_path_compare (view->details->double_click_path[0],
654                                                        view->details->double_click_path[1]) != 0));
655 
656         gtk_tree_path_free (path);
657 
658         /* We also want to further restrict rubber-banding to be initiated only in blank areas of the row.
659          * This allows DnD to operate on a new selection like before, when the motion begins over text or
660          * icons */
661         if (is_new_self_selection && gtk_tree_view_is_blank_at_pos (GTK_TREE_VIEW (widget),
662                                                                     event->x, event->y,
663                                                                     NULL, NULL, NULL, NULL)) {
664             /* If this is a candidate for rubber-banding, track that state in the view, and allow the event
665              * to continue into Gtk (which handles rubber-band selection for us) */
666             view->details->rubber_banding = TRUE;
667 
668             return GDK_EVENT_PROPAGATE;
669         }
670 
671         /* All other cases, allow DnD to potentially begin */
672         if (!source_target_list) {
673             source_target_list = nemo_list_model_get_drag_target_list ();
674         }
675 
676         if (gtk_drag_check_threshold (widget,
677                                       view->details->drag_x,
678                                       view->details->drag_y,
679                                       event->x,
680                                       event->y)) {
681             gtk_drag_begin (widget,
682                             source_target_list,
683                             GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK,
684                             view->details->drag_button,
685                             (GdkEvent*) event);
686         }
687 
688         /* The event is handled by the DnD begin, don't propagate further */
689         return GDK_EVENT_STOP;
690     }
691 
692     return GDK_EVENT_PROPAGATE;
693 }
694 
695 static gboolean
query_tooltip_callback(GtkWidget * widget,gint x,gint y,gboolean kb_mode,GtkTooltip * tooltip,gpointer user_data)696 query_tooltip_callback (GtkWidget *widget,
697                         gint x,
698                         gint y,
699                         gboolean kb_mode,
700                         GtkTooltip *tooltip,
701                         gpointer user_data)
702 {
703     NemoListView *list_view;
704     gboolean ret;
705 
706     ret = FALSE;
707 
708     list_view = NEMO_LIST_VIEW (user_data);
709 
710     if (list_view->details->show_tooltips) {
711         GtkTreeIter iter;
712         NemoFile *file;
713         GtkTreePath *path = NULL;
714         GtkTreeModel *model = GTK_TREE_MODEL (list_view->details->model);
715 
716         if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y,
717                                                kb_mode,
718                                                &model, &path, &iter)) {
719 
720             if (!gtk_tree_view_is_blank_at_pos (GTK_TREE_VIEW (widget), x, y, NULL, NULL, NULL, NULL)) {
721                 gtk_tree_model_get (GTK_TREE_MODEL (list_view->details->model),
722                                     &iter,
723                                     NEMO_LIST_MODEL_FILE_COLUMN, &file,
724                                     -1);
725                 if (file) {
726                     gchar *tooltip_text;
727 
728                     tooltip_text = nemo_file_construct_tooltip (file, list_view->details->tooltip_flags);
729                     gtk_tooltip_set_text (tooltip, tooltip_text);
730                     gtk_tree_view_set_tooltip_cell (GTK_TREE_VIEW (widget), tooltip, path, NULL, NULL);
731                     g_free (tooltip_text);
732 
733                     ret = TRUE;
734                 }
735             }
736         }
737         gtk_tree_path_free (path);
738     }
739 
740     return ret;
741 }
742 
743 static gboolean
leave_notify_callback(GtkWidget * widget,GdkEventCrossing * event,gpointer callback_data)744 leave_notify_callback (GtkWidget *widget,
745 		       GdkEventCrossing *event,
746 		       gpointer callback_data)
747 {
748 	NemoListView *view;
749 
750 	view = NEMO_LIST_VIEW (callback_data);
751 
752 	if (click_policy == NEMO_CLICK_POLICY_SINGLE &&
753 	    view->details->hover_path != NULL) {
754 		gtk_tree_path_free (view->details->hover_path);
755 		view->details->hover_path = NULL;
756 	}
757 
758 	return FALSE;
759 }
760 
761 static gboolean
enter_notify_callback(GtkWidget * widget,GdkEventCrossing * event,gpointer callback_data)762 enter_notify_callback (GtkWidget *widget,
763 		       GdkEventCrossing *event,
764 		       gpointer callback_data)
765 {
766 	NemoListView *view;
767 
768 	view = NEMO_LIST_VIEW (callback_data);
769 
770 	if (click_policy == NEMO_CLICK_POLICY_SINGLE) {
771 		if (view->details->hover_path != NULL) {
772 			gtk_tree_path_free (view->details->hover_path);
773 		}
774 
775 		gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
776 					       event->x, event->y,
777 					       &view->details->hover_path,
778 					       NULL, NULL, NULL);
779 
780 		if (view->details->hover_path != NULL) {
781 			gdk_window_set_cursor (gtk_widget_get_window (widget), hand_cursor);
782 		}
783 	}
784 
785 	return FALSE;
786 }
787 
788 static void
do_popup_menu(GtkWidget * widget,NemoListView * view,GdkEventButton * event)789 do_popup_menu (GtkWidget *widget, NemoListView *view, GdkEventButton *event)
790 {
791  	if (tree_view_has_selection (GTK_TREE_VIEW (widget))) {
792 		nemo_view_pop_up_selection_context_menu (NEMO_VIEW (view), event);
793 	} else {
794                 nemo_view_pop_up_background_context_menu (NEMO_VIEW (view), event);
795 	}
796 }
797 
798 static void
row_activated_callback(GtkTreeView * treeview,GtkTreePath * path,GtkTreeViewColumn * column,NemoListView * view)799 row_activated_callback (GtkTreeView *treeview, GtkTreePath *path,
800 			GtkTreeViewColumn *column, NemoListView *view)
801 {
802 	activate_selected_items (view);
803 }
804 
805 static void
columns_reordered_callback(AtkObject * atk,gpointer user_data)806 columns_reordered_callback (AtkObject *atk,
807                             gpointer user_data)
808 {
809     NemoListView *view = NEMO_LIST_VIEW (user_data);
810     NemoDirectory *directory;
811 
812     gchar **columns;
813     GList *vis_columns = NULL;
814     int i;
815     NemoFile *file = nemo_view_get_directory_as_file (NEMO_VIEW (view));
816 
817     columns = get_visible_columns (view);
818 
819     for (i = 0; columns[i] != NULL; ++i) {
820         vis_columns = g_list_prepend (vis_columns, columns[i]);
821     }
822 
823     vis_columns = g_list_reverse (vis_columns);
824 
825     GList *tv_list, *iter, *l;
826     GList *list = NULL;
827 
828     tv_list = gtk_tree_view_get_columns (view->details->tree_view);
829 
830     for (iter = tv_list; iter != NULL; iter = iter->next) {
831         for (l = vis_columns; l != NULL; l = l->next) {
832             if (iter->data == g_hash_table_lookup (view->details->columns, l->data))
833                 list = g_list_prepend (list, (gchar *)l->data);
834         }
835     }
836 
837     list = g_list_reverse (list);
838 
839     directory = nemo_view_get_model (NEMO_VIEW (view));
840 
841     if (nemo_global_preferences_get_ignore_view_metadata ()) {
842         nemo_window_set_ignore_meta_column_order (nemo_view_get_nemo_window (NEMO_VIEW (view)), list);
843     } else if (NEMO_IS_SEARCH_DIRECTORY (directory)) {
844         gchar **column_array = string_array_from_string_glist (list);
845         g_settings_set_strv (nemo_list_view_preferences,
846                              NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS,
847                              (const gchar **) column_array);
848 
849         g_strfreev (column_array);
850     } else {
851         nemo_file_set_metadata_list (file,
852                                      NEMO_METADATA_KEY_LIST_VIEW_COLUMN_ORDER,
853                                      list);
854     }
855     g_list_free_full (list, g_free);
856     g_free (columns);
857     g_list_free (vis_columns);
858     g_list_free (tv_list);
859 }
860 
861 static gboolean
clicked_on_text_in_name_cell(NemoListView * view,GtkTreePath * path,GdkEventButton * event)862 clicked_on_text_in_name_cell (NemoListView *view, GtkTreePath *path, GdkEventButton *event)
863 {
864     gboolean ret = FALSE;
865 
866     NemoListViewDetails *details = view->details;
867     int x_col_offset, x_cell_offset, width, expander_size, horizontal_separator, expansion_offset;
868 
869     x_col_offset = gtk_tree_view_column_get_x_offset (details->file_name_column);
870 
871     gtk_tree_view_column_cell_get_position (details->file_name_column,
872                                             GTK_CELL_RENDERER (details->file_name_cell),
873                                             &x_cell_offset, &width);
874 
875     gtk_widget_style_get (GTK_WIDGET (details->tree_view),
876                                       "expander-size", &expander_size,
877                                       "horizontal-separator", &horizontal_separator,
878                                       NULL);
879 
880     expander_size += 4;
881     expansion_offset = ((horizontal_separator / 2) + gtk_tree_path_get_depth (path) * expander_size);
882 
883     ret = (event->x > (expansion_offset + x_col_offset + x_cell_offset) &&
884            event->x < (x_col_offset + x_cell_offset + width)) &&
885            !gtk_tree_view_is_blank_at_pos (GTK_TREE_VIEW (view->details->tree_view),
886                                                           event->x, event->y,
887                                                           NULL, NULL, NULL, NULL);
888 
889     return ret;
890 }
891 
892 static gboolean
clicked_within_double_click_interval(NemoListView * view)893 clicked_within_double_click_interval (NemoListView *view)
894 {
895     static gint64 last_click_time = 0;
896     static int click_count = 0;
897 
898     gint64 current_time;
899     gint interval;
900 
901     /* fetch system double-click time */
902     g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (view))),
903               "gtk-double-click-time", &interval,
904               NULL);
905 
906     current_time = g_get_monotonic_time ();
907     if (current_time - last_click_time < interval * 1000) {
908         click_count++;
909     } else {
910         click_count = 0;
911     }
912 
913     /* Stash time for next compare */
914     last_click_time = current_time;
915 
916     /* Only allow double click */
917     if (click_count == 1) {
918         click_count = 0;
919         last_click_time = 0;
920         return TRUE;
921     } else {
922         return FALSE;
923     }
924 }
925 
926 static gboolean
clicked_within_slow_click_interval_on_text(NemoListView * view,GtkTreePath * path,GdkEventButton * event)927 clicked_within_slow_click_interval_on_text (NemoListView *view, GtkTreePath *path, GdkEventButton *event)
928 {
929     static gint64 last_slow_click_time = 0;
930     static gint slow_click_count = 0;
931     gint64 current_time;
932     gint interval;
933     gint double_click_interval;
934 
935     /* fetch system double-click time */
936     g_object_get (G_OBJECT (gtk_widget_get_settings (GTK_WIDGET (view))),
937                   "gtk-double-click-time", &double_click_interval,
938                   NULL);
939 
940     /* slow click interval is always 2 seconds longer than the system
941      * double-click interval. */
942 
943     interval = double_click_interval + 2000;
944 
945     current_time = g_get_monotonic_time ();
946     if (current_time - last_slow_click_time < interval * 1000) {
947         slow_click_count = 1;
948     } else {
949         slow_click_count = 0;
950     }
951 
952     /* Stash time for next compare */
953     last_slow_click_time = current_time;
954 
955     GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_view));
956 
957     GList *selected = gtk_tree_selection_get_selected_rows (selection, NULL);
958     gint selected_count = g_list_length (selected);
959 
960     g_list_free_full (selected, (GDestroyNotify) gtk_tree_path_free);
961 
962     if (selected_count != 1)
963         return FALSE;
964 
965     /* Only allow second click on text to trigger this */
966     if (slow_click_count == 1 && view->details->double_click_path[1] &&
967         gtk_tree_path_compare (view->details->double_click_path[0], view->details->double_click_path[1]) == 0 &&
968         clicked_on_text_in_name_cell (view, path, event)) {
969         slow_click_count = 0;
970 
971         return TRUE;
972     } else {
973         return FALSE;
974     }
975 }
976 
977 static gboolean
handle_icon_double_click(NemoListView * view,GtkTreePath * path,GdkEventButton * event,gboolean on_expander)978 handle_icon_double_click (NemoListView *view, GtkTreePath *path, GdkEventButton *event, gboolean on_expander)
979 {
980     /* Ignore double click if we are in single click mode */
981     if (click_policy == NEMO_CLICK_POLICY_SINGLE) {
982         return FALSE;
983     }
984 
985     if (clicked_within_double_click_interval (view) &&
986         view->details->double_click_path[1] &&
987         gtk_tree_path_compare (view->details->double_click_path[0], view->details->double_click_path[1]) == 0 &&
988         !on_expander) {
989         /* NOTE: Activation can actually destroy the view if we're switching */
990         if (!button_event_modifies_selection (event)) {
991             if ((event->button == 1 || event->button == 3)) {
992                 activate_selected_items (view);
993             } else if (event->button == 2) {
994                 activate_selected_items_alternate (view, NULL, TRUE);
995             }
996 
997             return TRUE;
998         } else if (event->button == 1 &&
999                (event->state & GDK_SHIFT_MASK) != 0) {
1000             NemoFile *file;
1001             file = nemo_list_model_file_for_path (view->details->model, path);
1002             if (file != NULL) {
1003                 activate_selected_items_alternate (view, file, TRUE);
1004                 nemo_file_unref (file);
1005             }
1006 
1007             return TRUE;
1008         }
1009     }
1010 
1011     return FALSE;
1012 }
1013 
1014 static gboolean
handle_icon_slow_two_click(NemoListView * view,GtkTreePath * path,GdkEventButton * event)1015 handle_icon_slow_two_click (NemoListView *view, GtkTreePath *path, GdkEventButton *event)
1016 {
1017     NemoListViewDetails *details;
1018     NemoFile *file;
1019     gboolean can_rename;
1020 
1021     details = view->details;
1022 
1023     if (!details->click_to_rename)
1024         return FALSE;
1025 
1026     file = nemo_list_model_file_for_path (view->details->model, path);
1027     can_rename = nemo_file_can_rename (file);
1028     nemo_file_unref (file);
1029 
1030     if (!can_rename)
1031         return FALSE;
1032 
1033     if (clicked_within_slow_click_interval_on_text (view, path, event) && !button_event_modifies_selection (event)) {
1034         return TRUE;
1035     }
1036 
1037     return FALSE;
1038 }
1039 
1040 static gboolean
button_press_callback(GtkWidget * widget,GdkEventButton * event,gpointer callback_data)1041 button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callback_data)
1042 {
1043 	NemoListView *view;
1044 	GtkTreeView *tree_view;
1045 	GtkTreePath *path;
1046 	gboolean call_parent;
1047 	GtkTreeSelection *selection;
1048 	GtkWidgetClass *tree_view_class;
1049 
1050 	int expander_size, horizontal_separator;
1051 	gboolean on_expander;
1052 	gboolean blank_click;
1053 
1054 	view = NEMO_LIST_VIEW (callback_data);
1055 	tree_view = GTK_TREE_VIEW (widget);
1056 	tree_view_class = GTK_WIDGET_GET_CLASS (tree_view);
1057 	selection = gtk_tree_view_get_selection (tree_view);
1058 	blank_click = FALSE;
1059 
1060 	/* Don't handle extra mouse buttons here */
1061 	if (event->button > 5) {
1062 		return FALSE;
1063 	}
1064 
1065     if (event->type == GDK_2BUTTON_PRESS || event->type == GDK_3BUTTON_PRESS) {
1066         return TRUE;
1067     }
1068 
1069 	if (event->window != gtk_tree_view_get_bin_window (tree_view)) {
1070 		return FALSE;
1071 	}
1072 
1073     if (!nemo_view_get_active (NEMO_VIEW (view))) {
1074         NemoWindowSlot *slot = nemo_view_get_nemo_window_slot (NEMO_VIEW (view));
1075         nemo_window_slot_make_hosting_pane_active (slot);
1076         return TRUE;
1077     }
1078 
1079 	nemo_list_model_set_drag_view
1080 		(NEMO_LIST_MODEL (gtk_tree_view_get_model (tree_view)),
1081 		 tree_view,
1082 		 event->x, event->y);
1083 
1084 	view->details->ignore_button_release = FALSE;
1085 
1086 	call_parent = TRUE;
1087 	if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
1088 					   &path, NULL, NULL, NULL)) {
1089 		gtk_widget_style_get (widget,
1090 				      "expander-size", &expander_size,
1091 				      "horizontal-separator", &horizontal_separator,
1092 				      NULL);
1093 		/* TODO we should not hardcode this extra padding. It is
1094 		 * EXPANDER_EXTRA_PADDING from GtkTreeView.
1095 		 */
1096 		expander_size += 4;
1097 		on_expander = (event->x <= horizontal_separator / 2 +
1098 			       gtk_tree_path_get_depth (path) * expander_size);
1099 
1100 		/* Keep track of path of last click so double clicks only happen
1101 		 * on the same item */
1102 		if ((event->button == 1 || event->button == 2)  &&
1103 		    event->type == GDK_BUTTON_PRESS) {
1104 			if (view->details->double_click_path[1]) {
1105 				gtk_tree_path_free (view->details->double_click_path[1]);
1106 			}
1107 			view->details->double_click_path[1] = view->details->double_click_path[0];
1108 			view->details->double_click_path[0] = gtk_tree_path_copy (path);
1109 		}
1110 
1111 		if (handle_icon_double_click (view, path, event, on_expander)) {
1112 			/* Double clicking does not trigger a D&D action. */
1113 			view->details->drag_button = 0;
1114 
1115 		} else {
1116             /* queue up renaming if we've clicked within the slow-click timeframe.  Don't actually
1117                do it, however, until there's a button release (this allows dragging to occur on
1118                single items, without triggering rename) */
1119             view->details->rename_on_release = handle_icon_slow_two_click (view, path, event);
1120 
1121 			/* We're going to filter out some situations where
1122 			 * we can't let the default code run because all
1123 			 * but one row would be would be deselected. We don't
1124 			 * want that; we want the right click menu or single
1125 			 * click to apply to everything that's currently selected. */
1126 
1127 			if (event->button == 3) {
1128 				blank_click =
1129 					(!gtk_tree_selection_path_is_selected (selection, path) &&
1130 					 gtk_tree_view_is_blank_at_pos (tree_view, event->x, event->y, NULL, NULL, NULL, NULL));
1131 			}
1132 
1133 			if (event->button == 3 &&
1134 			    (blank_click || gtk_tree_selection_path_is_selected (selection, path))) {
1135 				call_parent = FALSE;
1136 			}
1137 
1138 			if ((event->button == 1 || event->button == 2) &&
1139 			    ((event->state & GDK_CONTROL_MASK) != 0 ||
1140 			     (event->state & GDK_SHIFT_MASK) == 0)) {
1141 				view->details->row_selected_on_button_down = gtk_tree_selection_path_is_selected (selection, path);
1142 				if (view->details->row_selected_on_button_down) {
1143 					call_parent = on_expander;
1144 					view->details->ignore_button_release = call_parent;
1145 				} else if ((event->state & GDK_CONTROL_MASK) != 0) {
1146 					GList *selected_rows;
1147 					GList *l;
1148 
1149 					call_parent = FALSE;
1150 					if ((event->state & GDK_SHIFT_MASK) != 0) {
1151 						GtkTreePath *cursor;
1152 						gtk_tree_view_get_cursor (tree_view, &cursor, NULL);
1153 						if (cursor != NULL) {
1154 							gtk_tree_selection_select_range (selection, cursor, path);
1155 						} else {
1156 							gtk_tree_selection_select_path (selection, path);
1157 						}
1158 					} else {
1159 						gtk_tree_selection_select_path (selection, path);
1160 					}
1161 					selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1162 
1163 					/* This unselects everything */
1164 					gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
1165 
1166 					/* So select it again */
1167 					l = selected_rows;
1168 					while (l != NULL) {
1169 						GtkTreePath *p = l->data;
1170 						l = l->next;
1171 						gtk_tree_selection_select_path (selection, p);
1172 						gtk_tree_path_free (p);
1173 					}
1174 					g_list_free (selected_rows);
1175 				} else {
1176 					view->details->ignore_button_release = on_expander;
1177 				}
1178 			}
1179 
1180 			if (call_parent) {
1181 				g_signal_handlers_block_by_func (tree_view,
1182 								 row_activated_callback,
1183 								 view);
1184 
1185 				tree_view_class->button_press_event (widget, event);
1186 
1187 				g_signal_handlers_unblock_by_func (tree_view,
1188 								   row_activated_callback,
1189 								   view);
1190 			} else if (gtk_tree_selection_path_is_selected (selection, path)) {
1191 				gtk_widget_grab_focus (widget);
1192 			}
1193 
1194 			if ((event->button == 1 || event->button == 2) &&
1195 			    event->type == GDK_BUTTON_PRESS) {
1196 				view->details->drag_started = FALSE;
1197 				view->details->drag_button = event->button;
1198 				view->details->drag_x = event->x;
1199 				view->details->drag_y = event->y;
1200 			}
1201 
1202 			if (event->button == 3) {
1203 				if (blank_click) {
1204 					gtk_tree_selection_unselect_all (selection);
1205 				}
1206 				do_popup_menu (widget, view, event);
1207 			}
1208 		}
1209 
1210 		gtk_tree_path_free (path);
1211 	} else {
1212 		if ((event->button == 1 || event->button == 2)  &&
1213 		    event->type == GDK_BUTTON_PRESS) {
1214 			if (view->details->double_click_path[1]) {
1215 				gtk_tree_path_free (view->details->double_click_path[1]);
1216 			}
1217 			view->details->double_click_path[1] = view->details->double_click_path[0];
1218 			view->details->double_click_path[0] = NULL;
1219 		}
1220 		/* Deselect if people click outside any row. It's OK to
1221 		   let default code run; it won't reselect anything. */
1222 		gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view));
1223 		tree_view_class->button_press_event (widget, event);
1224 
1225 		if (event->button == 3) {
1226 			do_popup_menu (widget, view, event);
1227 		}
1228 	}
1229 
1230 	/* We chained to the default handler in this method, so never
1231 	 * let the default handler run */
1232 	return TRUE;
1233 }
1234 
1235 static gboolean
button_release_callback(GtkWidget * widget,GdkEventButton * event,gpointer callback_data)1236 button_release_callback (GtkWidget *widget,
1237 			 GdkEventButton *event,
1238 			 gpointer callback_data)
1239 {
1240 	NemoListView *view;
1241 
1242 	view = NEMO_LIST_VIEW (callback_data);
1243 
1244     view->details->rubber_banding = FALSE;
1245 
1246 	if (event->button == view->details->drag_button) {
1247 		stop_drag_check (view);
1248 		if (!view->details->drag_started &&
1249 		    !view->details->ignore_button_release) {
1250 			nemo_list_view_did_not_drag (view, event);
1251 		}
1252 	}
1253 	return FALSE;
1254 }
1255 
1256 static gboolean
popup_menu_callback(GtkWidget * widget,gpointer callback_data)1257 popup_menu_callback (GtkWidget *widget, gpointer callback_data)
1258 {
1259  	NemoListView *view;
1260 
1261 	view = NEMO_LIST_VIEW (callback_data);
1262 
1263 	do_popup_menu (widget, view, NULL);
1264 
1265 	return TRUE;
1266 }
1267 
1268 static void
subdirectory_done_loading_callback(NemoDirectory * directory,NemoListView * view)1269 subdirectory_done_loading_callback (NemoDirectory *directory, NemoListView *view)
1270 {
1271 	nemo_list_model_subdirectory_done_loading (view->details->model, directory);
1272 
1273     queue_update_visible_icons (view, INITIAL_UPDATE_VISIBLE_DELAY);
1274 }
1275 
1276 static void
row_expanded_callback(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * path,gpointer callback_data)1277 row_expanded_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer callback_data)
1278 {
1279  	NemoListView *view;
1280  	NemoDirectory *directory;
1281 
1282 	view = NEMO_LIST_VIEW (callback_data);
1283 
1284 	if (nemo_list_model_load_subdirectory (view->details->model, path, &directory)) {
1285 		char *uri;
1286 
1287 		uri = nemo_directory_get_uri (directory);
1288 		DEBUG ("Row expaded callback for uri %s", uri);
1289 		g_free (uri);
1290 
1291 		nemo_view_add_subdirectory (NEMO_VIEW (view), directory);
1292         nemo_list_model_set_expanding (view->details->model, directory);
1293 
1294 		if (nemo_directory_are_all_files_seen (directory)) {
1295 			nemo_list_model_subdirectory_done_loading (view->details->model,
1296 								 directory);
1297 		} else {
1298 			g_signal_connect_object (directory, "done_loading",
1299 						 G_CALLBACK (subdirectory_done_loading_callback),
1300 						 view, 0);
1301 		}
1302 
1303 		nemo_directory_unref (directory);
1304 	}
1305 }
1306 
1307 struct UnloadDelayData {
1308 	NemoFile *file;
1309 	NemoDirectory *directory;
1310 	NemoListView *view;
1311 };
1312 
1313 static gboolean
unload_file_timeout(gpointer data)1314 unload_file_timeout (gpointer data)
1315 {
1316 	struct UnloadDelayData *unload_data = data;
1317 	GtkTreeIter iter;
1318 	NemoListModel *model;
1319 	GtkTreePath *path;
1320 
1321 	if (unload_data->view != NULL) {
1322 		model = unload_data->view->details->model;
1323 		if (nemo_list_model_get_tree_iter_from_file (model,
1324 							   unload_data->file,
1325 							   unload_data->directory,
1326 							   &iter)) {
1327 			path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1328 			if (!gtk_tree_view_row_expanded (unload_data->view->details->tree_view,
1329 							 path)) {
1330 				nemo_list_model_unload_subdirectory (model, &iter);
1331 			}
1332 			gtk_tree_path_free (path);
1333 		}
1334 
1335 		g_object_remove_weak_pointer (G_OBJECT (unload_data->view),
1336 					      (gpointer *) &unload_data->view);
1337 	}
1338 
1339 	if (unload_data->directory) {
1340 		nemo_directory_unref (unload_data->directory);
1341 	}
1342 	nemo_file_unref (unload_data->file);
1343 	g_free (unload_data);
1344 	return FALSE;
1345 }
1346 
1347 static void
row_collapsed_callback(GtkTreeView * treeview,GtkTreeIter * iter,GtkTreePath * path,gpointer callback_data)1348 row_collapsed_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer callback_data)
1349 {
1350  	NemoListView *view;
1351  	NemoFile *file;
1352 	NemoDirectory *directory;
1353 	GtkTreeIter parent;
1354 	struct UnloadDelayData *unload_data;
1355 	GtkTreeModel *model;
1356 	char *uri;
1357 
1358 	view = NEMO_LIST_VIEW (callback_data);
1359 	model = GTK_TREE_MODEL (view->details->model);
1360 
1361 	gtk_tree_model_get (model, iter,
1362 			    NEMO_LIST_MODEL_FILE_COLUMN, &file,
1363 			    -1);
1364 
1365 	directory = NULL;
1366 	if (gtk_tree_model_iter_parent (model, &parent, iter)) {
1367 		gtk_tree_model_get (model, &parent,
1368 				    NEMO_LIST_MODEL_SUBDIRECTORY_COLUMN, &directory,
1369 				    -1);
1370 	}
1371 
1372 
1373 	uri = nemo_file_get_uri (file);
1374 	DEBUG ("Row collapsed callback for uri %s", uri);
1375 	g_free (uri);
1376 
1377 	unload_data = g_new (struct UnloadDelayData, 1);
1378 	unload_data->view = view;
1379 	unload_data->file = file;
1380 	unload_data->directory = directory;
1381 
1382 	g_object_add_weak_pointer (G_OBJECT (unload_data->view),
1383 				   (gpointer *) &unload_data->view);
1384 
1385 	g_timeout_add_seconds (COLLAPSE_TO_UNLOAD_DELAY,
1386 			       unload_file_timeout,
1387 			       unload_data);
1388 }
1389 
1390 static void
subdirectory_unloaded_callback(NemoListModel * model,NemoDirectory * directory,gpointer callback_data)1391 subdirectory_unloaded_callback (NemoListModel *model,
1392 				NemoDirectory *directory,
1393 				gpointer callback_data)
1394 {
1395 	NemoListView *view;
1396 
1397 	g_return_if_fail (NEMO_IS_LIST_MODEL (model));
1398 	g_return_if_fail (NEMO_IS_DIRECTORY (directory));
1399 
1400 	view = NEMO_LIST_VIEW(callback_data);
1401 
1402 	g_signal_handlers_disconnect_by_func (directory,
1403 					      G_CALLBACK (subdirectory_done_loading_callback),
1404 					      view);
1405 	nemo_view_remove_subdirectory (NEMO_VIEW (view), directory);
1406 }
1407 
1408 static gboolean
key_press_callback(GtkWidget * widget,GdkEventKey * event,gpointer callback_data)1409 key_press_callback (GtkWidget *widget, GdkEventKey *event, gpointer callback_data)
1410 {
1411 	NemoView *view;
1412 	GdkEventButton button_event = { 0 };
1413 	gboolean handled;
1414 	GtkTreeView *tree_view;
1415 	GtkTreePath *path;
1416 
1417 	tree_view = GTK_TREE_VIEW (widget);
1418 
1419 	view = NEMO_VIEW (callback_data);
1420 	handled = FALSE;
1421 
1422 	switch (event->keyval) {
1423 	case GDK_KEY_F10:
1424 		if (event->state & GDK_CONTROL_MASK) {
1425 			nemo_view_pop_up_background_context_menu (view, &button_event);
1426 			handled = TRUE;
1427 		}
1428 		break;
1429 	case GDK_KEY_Right:
1430 		gtk_tree_view_get_cursor (tree_view, &path, NULL);
1431 		if (path) {
1432 			gtk_tree_view_expand_row (tree_view, path, FALSE);
1433 			gtk_tree_path_free (path);
1434 		}
1435 		handled = TRUE;
1436 		break;
1437 	case GDK_KEY_Left:
1438 		gtk_tree_view_get_cursor (tree_view, &path, NULL);
1439 		if (path) {
1440 			if (!gtk_tree_view_collapse_row (tree_view, path)) {
1441 				/* if the row is already collapsed or doesn't have any children,
1442 				 * jump to the parent row instead.
1443 				 */
1444 				if ((gtk_tree_path_get_depth (path) > 1) && gtk_tree_path_up (path)) {
1445 					gtk_tree_view_set_cursor (tree_view, path, NULL, FALSE);
1446 				}
1447 			}
1448 
1449 			gtk_tree_path_free (path);
1450 		}
1451 		handled = TRUE;
1452 		break;
1453 	case GDK_KEY_space:
1454 		if (event->state & GDK_CONTROL_MASK) {
1455 			handled = FALSE;
1456 			break;
1457 		}
1458 		if (!gtk_widget_has_focus (GTK_WIDGET (NEMO_LIST_VIEW (view)->details->tree_view))) {
1459 			handled = FALSE;
1460 			break;
1461 		}
1462 		if ((event->state & GDK_SHIFT_MASK) != 0) {
1463 			activate_selected_items_alternate (NEMO_LIST_VIEW (view), NULL, TRUE);
1464 		} else {
1465 			preview_selected_items (NEMO_LIST_VIEW (view));
1466 		}
1467 		handled = TRUE;
1468 		break;
1469 	case GDK_KEY_Return:
1470 	case GDK_KEY_KP_Enter:
1471 		if ((event->state & GDK_SHIFT_MASK) != 0) {
1472 			activate_selected_items_alternate (NEMO_LIST_VIEW (view), NULL, TRUE);
1473 		} else {
1474 			activate_selected_items (NEMO_LIST_VIEW (view));
1475 		}
1476 		handled = TRUE;
1477 		break;
1478 	case GDK_KEY_v:
1479 		/* Eat Control + v to not enable type ahead */
1480 		if ((event->state & GDK_CONTROL_MASK) != 0) {
1481 			handled = TRUE;
1482 		}
1483 		break;
1484 
1485 	default:
1486 		handled = FALSE;
1487 	}
1488 
1489 	return handled;
1490 }
1491 
1492 static void
set_ok_to_load_deferred_attrs(NemoListView * list_view,gboolean ok)1493 set_ok_to_load_deferred_attrs (NemoListView  *list_view,
1494                        gboolean       ok)
1495 {
1496     list_view->details->ok_to_load_deferred_attrs = ok;
1497 
1498     if (ok) {
1499         queue_update_visible_icons (list_view, INITIAL_UPDATE_VISIBLE_DELAY);
1500     }
1501 }
1502 
1503 static void
nemo_list_view_reveal_selection(NemoView * view)1504 nemo_list_view_reveal_selection (NemoView *view)
1505 {
1506 	GList *selection;
1507 
1508 	g_return_if_fail (NEMO_IS_LIST_VIEW (view));
1509 
1510         selection = nemo_view_get_selection (view);
1511 
1512 	/* Make sure at least one of the selected items is scrolled into view */
1513 	if (selection != NULL) {
1514 		NemoListView *list_view;
1515 		NemoFile *file;
1516 		GtkTreeIter iter;
1517 		GtkTreePath *path;
1518 
1519 		list_view = NEMO_LIST_VIEW (view);
1520 		file = selection->data;
1521 		if (nemo_list_model_get_first_iter_for_file (list_view->details->model, file, &iter)) {
1522 			path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter);
1523 
1524 			gtk_tree_view_scroll_to_cell (list_view->details->tree_view, path, NULL, FALSE, 0.0, 0.0);
1525 
1526 			gtk_tree_path_free (path);
1527 		}
1528 	}
1529 
1530         nemo_file_list_free (selection);
1531 }
1532 
1533 static gboolean
sort_criterion_changes_due_to_user(GtkTreeView * tree_view)1534 sort_criterion_changes_due_to_user (GtkTreeView *tree_view)
1535 {
1536 	GList *columns, *p;
1537 	GtkTreeViewColumn *column;
1538 	GSignalInvocationHint *ihint;
1539 	gboolean ret;
1540 
1541 	ret = FALSE;
1542 
1543 	columns = gtk_tree_view_get_columns (tree_view);
1544 	for (p = columns; p != NULL; p = p->next) {
1545 		column = p->data;
1546 		ihint = g_signal_get_invocation_hint (column);
1547 		if (ihint != NULL) {
1548 			ret = TRUE;
1549 			break;
1550 		}
1551 	}
1552 	g_list_free (columns);
1553 
1554 	return ret;
1555 }
1556 
1557 static void
sort_column_changed_callback(GtkTreeSortable * sortable,NemoListView * view)1558 sort_column_changed_callback (GtkTreeSortable *sortable,
1559 			      NemoListView *view)
1560 {
1561 	NemoFile *file;
1562 	gint sort_column_id, default_sort_column_id;
1563 	GtkSortType reversed;
1564 	GQuark sort_attr, default_sort_attr;
1565 	char *reversed_attr, *default_reversed_attr;
1566 	gboolean default_sort_reversed;
1567 
1568 	file = nemo_view_get_directory_as_file (NEMO_VIEW (view));
1569 
1570 	gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &reversed);
1571 	sort_attr = nemo_list_model_get_attribute_from_sort_column_id (view->details->model, sort_column_id);
1572 
1573 	default_sort_column_id = nemo_list_model_get_sort_column_id_from_attribute (view->details->model,
1574 										  g_quark_from_string (get_default_sort_order (file, &default_sort_reversed)));
1575 	default_sort_attr = nemo_list_model_get_attribute_from_sort_column_id (view->details->model, default_sort_column_id);
1576 
1577         if (nemo_global_preferences_get_ignore_view_metadata ())
1578                 nemo_window_set_ignore_meta_sort_column (nemo_view_get_nemo_window (NEMO_VIEW (view)),
1579                                                          g_quark_to_string (sort_attr));
1580         else
1581                 nemo_file_set_metadata (file, NEMO_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
1582                                         g_quark_to_string (default_sort_attr), g_quark_to_string (sort_attr));
1583 
1584 	default_reversed_attr = (default_sort_reversed ? (char *)"true" : (char *)"false");
1585 
1586 	if (view->details->last_sort_attr != sort_attr &&
1587 	    sort_criterion_changes_due_to_user (view->details->tree_view)) {
1588 		/* at this point, the sort order is always GTK_SORT_ASCENDING, if the sort column ID
1589 		 * switched. Invert the sort order, if it's the default criterion with a reversed preference,
1590 		 * or if it makes sense for the attribute (i.e. date). */
1591 		if (sort_attr == default_sort_attr) {
1592 			/* use value from preferences */
1593 			reversed = g_settings_get_boolean (nemo_preferences,
1594 							   NEMO_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER);
1595 		} else {
1596 			reversed = nemo_file_is_date_sort_attribute_q (sort_attr);
1597 		}
1598 
1599 		if (reversed) {
1600 			g_signal_handlers_block_by_func (sortable, sort_column_changed_callback, view);
1601 			gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->details->model),
1602 							      sort_column_id,
1603 							      GTK_SORT_DESCENDING);
1604 			g_signal_handlers_unblock_by_func (sortable, sort_column_changed_callback, view);
1605 		}
1606 	}
1607 
1608     if (nemo_global_preferences_get_ignore_view_metadata ()) {
1609         nemo_window_set_ignore_meta_sort_direction (nemo_view_get_nemo_window (NEMO_VIEW (view)),
1610                                                     reversed ? SORT_DESCENDING : SORT_ASCENDING);
1611     } else {
1612         reversed_attr = (reversed ? (char *)"true" : (char *)"false");
1613         nemo_file_set_metadata (file, NEMO_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
1614                                 default_reversed_attr, reversed_attr);
1615     }
1616 
1617 	/* Make sure selected item(s) is visible after sort */
1618 	nemo_list_view_reveal_selection (NEMO_VIEW (view));
1619 
1620 	view->details->last_sort_attr = sort_attr;
1621 }
1622 
1623 static gboolean
editable_focus_out_cb(GtkWidget * widget,GdkEvent * event,gpointer user_data)1624 editable_focus_out_cb (GtkWidget *widget,
1625 		       GdkEvent *event,
1626 		       gpointer user_data)
1627 {
1628 	NemoListView *view = user_data;
1629 
1630 	nemo_view_set_is_renaming (NEMO_VIEW (view), FALSE);
1631 	nemo_view_unfreeze_updates (NEMO_VIEW (view));
1632 
1633     return GDK_EVENT_PROPAGATE;
1634 }
1635 
1636 static void
cell_renderer_editing_started_cb(GtkCellRenderer * renderer,GtkCellEditable * editable,const gchar * path_str,NemoListView * list_view)1637 cell_renderer_editing_started_cb (GtkCellRenderer *renderer,
1638 				  GtkCellEditable *editable,
1639 				  const gchar *path_str,
1640 				  NemoListView *list_view)
1641 {
1642 	GtkEntry *entry;
1643 
1644 	entry = GTK_ENTRY (editable);
1645 	list_view->details->editable_widget = editable;
1646 
1647 	/* Free a previously allocated original_name */
1648 	g_free (list_view->details->original_name);
1649 
1650 	list_view->details->original_name = g_strdup (gtk_entry_get_text (entry));
1651 
1652 	g_signal_connect (entry, "focus-out-event",
1653 			  G_CALLBACK (editable_focus_out_cb), list_view);
1654 
1655 	nemo_clipboard_set_up_editable
1656 		(GTK_EDITABLE (entry),
1657 		 nemo_view_get_ui_manager (NEMO_VIEW (list_view)),
1658 		 FALSE);
1659 }
1660 
1661 static void
cell_renderer_editing_canceled(GtkCellRendererText * cell,NemoListView * view)1662 cell_renderer_editing_canceled (GtkCellRendererText *cell,
1663 				NemoListView    *view)
1664 {
1665     view->details->editable_widget = NULL;
1666 	nemo_view_set_is_renaming (NEMO_VIEW (view), FALSE);
1667 	nemo_view_unfreeze_updates (NEMO_VIEW (view));
1668 }
1669 
1670 static void
cell_renderer_edited(GtkCellRendererText * cell,const char * path_str,const char * new_text,NemoListView * view)1671 cell_renderer_edited (GtkCellRendererText *cell,
1672 		      const char          *path_str,
1673 		      const char          *new_text,
1674 		      NemoListView    *view)
1675 {
1676 	GtkTreePath *path;
1677 	NemoFile *file;
1678 	GtkTreeIter iter;
1679 
1680 	view->details->editable_widget = NULL;
1681 	nemo_view_set_is_renaming (NEMO_VIEW (view), FALSE);
1682 
1683 	/* Don't allow a rename with an empty string. Revert to original
1684 	 * without notifying the user.
1685 	 */
1686 	if (new_text[0] == '\0') {
1687 		g_object_set (G_OBJECT (view->details->file_name_cell),
1688 			      "editable", FALSE,
1689 			      NULL);
1690 		nemo_view_unfreeze_updates (NEMO_VIEW (view));
1691 		return;
1692 	}
1693 
1694 	path = gtk_tree_path_new_from_string (path_str);
1695 
1696 	gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
1697 				 &iter, path);
1698 
1699 	gtk_tree_path_free (path);
1700 
1701 	gtk_tree_model_get (GTK_TREE_MODEL (view->details->model),
1702 			    &iter,
1703 			    NEMO_LIST_MODEL_FILE_COLUMN, &file,
1704 			    -1);
1705 
1706 	/* Only rename if name actually changed */
1707 	if (strcmp (new_text, view->details->original_name) != 0) {
1708 		view->details->renaming_file = nemo_file_ref (file);
1709 		view->details->rename_done = FALSE;
1710 		nemo_rename_file (file, new_text, nemo_list_view_rename_callback, g_object_ref (view));
1711 		g_free (view->details->original_name);
1712 		view->details->original_name = g_strdup (new_text);
1713 	}
1714 
1715 	nemo_file_unref (file);
1716 
1717 	/*We're done editing - make the filename-cells readonly again.*/
1718 	g_object_set (G_OBJECT (view->details->file_name_cell),
1719 		      "editable", FALSE,
1720 		      NULL);
1721 
1722 	nemo_view_unfreeze_updates (NEMO_VIEW (view));
1723 }
1724 
1725 static char *
get_root_uri_callback(NemoTreeViewDragDest * dest,gpointer user_data)1726 get_root_uri_callback (NemoTreeViewDragDest *dest,
1727 		       gpointer user_data)
1728 {
1729 	NemoListView *view;
1730 
1731 	view = NEMO_LIST_VIEW (user_data);
1732 
1733 	return nemo_view_get_uri (NEMO_VIEW (view));
1734 }
1735 
1736 static NemoFile *
get_file_for_path_callback(NemoTreeViewDragDest * dest,GtkTreePath * path,gpointer user_data)1737 get_file_for_path_callback (NemoTreeViewDragDest *dest,
1738 			    GtkTreePath *path,
1739 			    gpointer user_data)
1740 {
1741 	NemoListView *view;
1742 
1743 	view = NEMO_LIST_VIEW (user_data);
1744 
1745 	return nemo_list_model_file_for_path (view->details->model, path);
1746 }
1747 
1748 /* Handles an URL received from Mozilla */
1749 static void
list_view_handle_netscape_url(NemoTreeViewDragDest * dest,const char * encoded_url,const char * target_uri,GdkDragAction action,int x,int y,NemoListView * view)1750 list_view_handle_netscape_url (NemoTreeViewDragDest *dest, const char *encoded_url,
1751 			       const char *target_uri, GdkDragAction action, int x, int y, NemoListView *view)
1752 {
1753 	nemo_view_handle_netscape_url_drop (NEMO_VIEW (view),
1754 						encoded_url, target_uri, action, x, y);
1755 }
1756 
1757 static void
list_view_handle_uri_list(NemoTreeViewDragDest * dest,const char * item_uris,const char * target_uri,GdkDragAction action,int x,int y,NemoListView * view)1758 list_view_handle_uri_list (NemoTreeViewDragDest *dest, const char *item_uris,
1759 			   const char *target_uri,
1760 			   GdkDragAction action, int x, int y, NemoListView *view)
1761 {
1762 	nemo_view_handle_uri_list_drop (NEMO_VIEW (view),
1763 					    item_uris, target_uri, action, x, y);
1764 }
1765 
1766 static void
list_view_handle_text(NemoTreeViewDragDest * dest,const char * text,const char * target_uri,GdkDragAction action,int x,int y,NemoListView * view)1767 list_view_handle_text (NemoTreeViewDragDest *dest, const char *text,
1768 		       const char *target_uri,
1769 		       GdkDragAction action, int x, int y, NemoListView *view)
1770 {
1771 	nemo_view_handle_text_drop (NEMO_VIEW (view),
1772 					text, target_uri, action, x, y);
1773 }
1774 
1775 static void
list_view_handle_raw(NemoTreeViewDragDest * dest,const char * raw_data,int length,const char * target_uri,const char * direct_save_uri,GdkDragAction action,int x,int y,NemoListView * view)1776 list_view_handle_raw (NemoTreeViewDragDest *dest, const char *raw_data,
1777 		      int length, const char *target_uri, const char *direct_save_uri,
1778 		      GdkDragAction action, int x, int y, NemoListView *view)
1779 {
1780 	nemo_view_handle_raw_drop (NEMO_VIEW (view),
1781 				       raw_data, length, target_uri, direct_save_uri,
1782 				       action, x, y);
1783 }
1784 
1785 static void
move_copy_items_callback(NemoTreeViewDragDest * dest,const GList * item_uris,const char * target_uri,guint action,int x,int y,gpointer user_data)1786 move_copy_items_callback (NemoTreeViewDragDest *dest,
1787 			  const GList *item_uris,
1788 			  const char *target_uri,
1789 			  guint action,
1790 			  int x,
1791 			  int y,
1792 			  gpointer user_data)
1793 
1794 {
1795 	NemoView *view = user_data;
1796 
1797 	nemo_clipboard_clear_if_colliding_uris (GTK_WIDGET (view),
1798 						    item_uris,
1799 						    nemo_view_get_copied_files_atom (view));
1800 	nemo_view_move_copy_items (view,
1801 				       item_uris,
1802 				       NULL,
1803 				       target_uri,
1804 				       action,
1805 				       x, y);
1806 }
1807 
1808 static void
column_header_menu_toggled(GtkCheckMenuItem * menu_item,NemoListView * list_view)1809 column_header_menu_toggled (GtkCheckMenuItem *menu_item,
1810                             NemoListView *list_view)
1811 {
1812 	NemoFile *file;
1813     NemoDirectory *directory;
1814     char **visible_columns;
1815     const char *menu_item_column_id;
1816 	GList *list = NULL;
1817     GList *l, *current_view_columns;
1818 	int i;
1819 
1820     file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
1821     menu_item_column_id = g_object_get_data (G_OBJECT (menu_item), "column-name");
1822 
1823     current_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
1824 
1825     for (l = current_view_columns; l != NULL; l = l->next) {
1826         GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (l->data);
1827 
1828         const char *current_id = g_object_get_data (G_OBJECT (c), "column-id");
1829 
1830         if (g_strcmp0 (current_id, menu_item_column_id) == 0) {
1831             if (gtk_check_menu_item_get_active (menu_item)) {
1832                 list = g_list_prepend (list, g_strdup (current_id));
1833             }
1834         } else {
1835             if (gtk_tree_view_column_get_visible (c))
1836                 list = g_list_prepend (list, g_strdup (current_id));
1837         }
1838     }
1839 
1840     directory = nemo_view_get_model (NEMO_VIEW (list_view));
1841 
1842     list = g_list_reverse (list);
1843 
1844     if (nemo_global_preferences_get_ignore_view_metadata ())
1845         nemo_window_set_ignore_meta_visible_columns (nemo_view_get_nemo_window (NEMO_VIEW (list_view)), list);
1846     else if (NEMO_IS_SEARCH_DIRECTORY (directory)) {
1847         gchar **column_array = string_array_from_string_glist (list);
1848         g_settings_set_strv (nemo_list_view_preferences,
1849                              NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS,
1850                              (const gchar **) column_array);
1851 
1852         g_strfreev (column_array);
1853     } else
1854         nemo_file_set_metadata_list (file,
1855                                      NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS,
1856                                      list);
1857 
1858     visible_columns = g_new0 (char *, g_list_length (list) + 1);
1859     for (i = 0, l = list; l != NULL; ++i, l = l->next) {
1860 		visible_columns[i] = l->data;
1861     }
1862 
1863 	/* set view values ourselves, as new metadata could not have been
1864 	 * updated yet.
1865 	 */
1866     apply_columns_settings (list_view, visible_columns, visible_columns);
1867 
1868     g_list_free (list);
1869     g_list_free (current_view_columns);
1870     g_strfreev (visible_columns);
1871 }
1872 
1873 static void
column_header_menu_use_default(GtkMenuItem * menu_item,NemoListView * list_view)1874 column_header_menu_use_default (GtkMenuItem *menu_item,
1875                                 NemoListView *list_view)
1876 {
1877 	NemoFile *file;
1878     NemoDirectory *directory;
1879 	char **default_columns;
1880 	char **default_order;
1881 
1882 	file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
1883 
1884     g_signal_handlers_block_by_func (list_view->details->tree_view,
1885                                      columns_reordered_callback,
1886                                      list_view);
1887 
1888     if (nemo_global_preferences_get_ignore_view_metadata ()) {
1889         NemoWindow *window = nemo_view_get_nemo_window (NEMO_VIEW (list_view));
1890         nemo_window_set_ignore_meta_visible_columns (window, NULL);
1891         nemo_window_set_ignore_meta_column_order (window, NULL);
1892     } else {
1893         nemo_file_set_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NULL);
1894         nemo_file_set_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL);
1895     }
1896 
1897     directory = nemo_view_get_model (NEMO_VIEW (list_view));
1898 
1899     if (NEMO_IS_SEARCH_DIRECTORY (directory))
1900         g_settings_reset (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS);
1901 
1902     default_columns = get_default_visible_columns (list_view);
1903 
1904     default_order = get_default_column_order (list_view);
1905 
1906 	/* set view values ourselves, as new metadata could not have been
1907 	 * updated yet.
1908 	 */
1909 	apply_columns_settings (list_view, default_order, default_columns);
1910 
1911     g_signal_handlers_unblock_by_func (list_view->details->tree_view,
1912                                        columns_reordered_callback,
1913                                        list_view);
1914 
1915 	g_strfreev (default_columns);
1916 	g_strfreev (default_order);
1917 }
1918 
1919 static void
column_header_menu_disable_sort(GtkMenuItem * menu_item,NemoListView * list_view)1920 column_header_menu_disable_sort (GtkMenuItem *menu_item,
1921                                  NemoListView *list_view)
1922 {
1923     gboolean active = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menu_item));
1924 
1925     nemo_list_model_set_temporarily_disable_sort (list_view->details->model, active);
1926 }
1927 
1928 static gboolean
column_header_clicked(GtkWidget * column_button,GdkEventButton * event,NemoListView * list_view)1929 column_header_clicked (GtkWidget *column_button,
1930                        GdkEventButton *event,
1931                        NemoListView *list_view)
1932 {
1933     GList *current_view_columns, *l;
1934     NemoFile *file;
1935     GtkWidget *menu;
1936     GtkWidget *menu_item;
1937 
1938 	if (event->button != GDK_BUTTON_SECONDARY) {
1939 		return FALSE;
1940 	}
1941 
1942     file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
1943 
1944     menu = gtk_menu_new ();
1945 
1946     current_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
1947 
1948     for (l = current_view_columns; l != NULL; l = l->next) {
1949         const char *name;
1950         char *label;
1951         char *lowercase;
1952         gboolean visible;
1953 
1954         GtkTreeViewColumn *c = GTK_TREE_VIEW_COLUMN (l->data);
1955 
1956         name = g_object_get_data (G_OBJECT (c), "column-id");
1957 
1958         if (!nemo_file_is_in_trash (file)) {
1959             if (g_strcmp0 (name, "trashed_on") == 0 ||
1960                 g_strcmp0 (name, "trash_orig_path") == 0)
1961                 continue;
1962         }
1963 
1964         g_object_get (G_OBJECT (c),
1965                       "title", &label,
1966                       "visible", &visible,
1967                       NULL);
1968 
1969         lowercase = g_ascii_strdown (name, -1);
1970 
1971         menu_item = gtk_check_menu_item_new_with_label (label);
1972         gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1973 
1974         g_object_set_data_full (G_OBJECT (menu_item),
1975                                 "column-name", g_strdup (name), g_free);
1976 
1977         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), visible);
1978 
1979         /* Don't allow hiding the filename */
1980         if (g_strcmp0 (lowercase, "name") == 0) {
1981             gtk_widget_set_sensitive (GTK_WIDGET (menu_item), FALSE);
1982         }
1983 
1984         g_signal_connect (menu_item,
1985                           "toggled",
1986                           G_CALLBACK (column_header_menu_toggled),
1987                           list_view);
1988 
1989         g_clear_pointer (&lowercase, g_free);
1990         g_clear_pointer (&label, g_free);
1991     }
1992 
1993 	menu_item = gtk_separator_menu_item_new ();
1994 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1995 
1996 	menu_item = gtk_menu_item_new_with_label (_("Use Default"));
1997 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
1998 
1999 	g_signal_connect (menu_item,
2000 	                  "activate",
2001 	                  G_CALLBACK (column_header_menu_use_default),
2002 	                  list_view);
2003 
2004     menu_item = gtk_separator_menu_item_new ();
2005     gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
2006 
2007     menu_item = gtk_check_menu_item_new_with_label (_("Temporarily disable auto-sort"));
2008     gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
2009 
2010     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item),
2011                                     nemo_list_model_get_temporarily_disable_sort (list_view->details->model));
2012 
2013     g_signal_connect (menu_item,
2014                       "activate",
2015                       G_CALLBACK (column_header_menu_disable_sort),
2016                       list_view);
2017 
2018 	gtk_widget_show_all (menu);
2019 	gtk_menu_popup_for_device (GTK_MENU (menu),
2020 	                           gdk_event_get_device ((GdkEvent *) event),
2021 	                           NULL, NULL, NULL, NULL, NULL,
2022 	                           event->button, event->time);
2023 
2024 	return TRUE;
2025 }
2026 
2027 static void
apply_columns_settings(NemoListView * list_view,char ** column_order,char ** visible_columns)2028 apply_columns_settings (NemoListView *list_view,
2029 			char **column_order,
2030 			char **visible_columns)
2031 {
2032 	GList *all_columns;
2033 	NemoFile *file;
2034 	GList *old_view_columns, *view_columns;
2035 	GHashTable *visible_columns_hash;
2036 	GList *l;
2037     gint i;
2038 
2039 	file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
2040 
2041 	/* prepare ordered list of view columns using column_order and visible_columns */
2042 	view_columns = NULL;
2043 
2044 	all_columns = nemo_get_columns_for_file (file);
2045 	all_columns = nemo_sort_columns (all_columns, column_order);
2046 
2047 	/* hash table to lookup if a given column should be visible */
2048 	visible_columns_hash = g_hash_table_new_full (g_str_hash,
2049 						      g_str_equal,
2050 						      (GDestroyNotify) g_free,
2051 						      (GDestroyNotify) g_free);
2052 	for (i = 0; visible_columns[i] != NULL; ++i) {
2053 		g_hash_table_insert (visible_columns_hash,
2054 				     g_ascii_strdown (visible_columns[i], -1),
2055 				     g_ascii_strdown (visible_columns[i], -1));
2056 	}
2057 
2058 	for (l = all_columns; l != NULL; l = l->next) {
2059 		char *name;
2060 		char *lowercase;
2061 
2062 		g_object_get (G_OBJECT (l->data), "name", &name, NULL);
2063 		lowercase = g_ascii_strdown (name, -1);
2064 
2065 		if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL) {
2066 			GtkTreeViewColumn *view_column;
2067 
2068 			view_column = g_hash_table_lookup (list_view->details->columns, name);
2069 			if (view_column != NULL) {
2070 				view_columns = g_list_prepend (view_columns, view_column);
2071 			}
2072 		}
2073 
2074 		g_free (name);
2075 		g_free (lowercase);
2076 	}
2077 
2078 	g_hash_table_destroy (visible_columns_hash);
2079 	nemo_column_list_free (all_columns);
2080 
2081 	view_columns = g_list_reverse (view_columns);
2082 
2083 	/* hide columns that are not present in the configuration */
2084 	old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
2085 	for (l = old_view_columns; l != NULL; l = l->next) {
2086 		if (g_list_find (view_columns, l->data) == NULL) {
2087 			gtk_tree_view_column_set_visible (l->data, FALSE);
2088 		}
2089 	}
2090 	g_list_free (old_view_columns);
2091 
2092     /* see bug: https://github.com/GNOME/gtk/commit/497e877755f1fa1
2093      * Explanation for branching - move_column_after generates useless logfile spam,
2094      * and to avoid it, simply removing and adding columns in a different order works
2095      * just as well.  The problem is, gtk versions < 3.22.25 lack the patch referenced
2096      * in the above bug report.  An additional problem is that different pre-3.22.25
2097      * versions behave differently depending on other code changes in GtkTreeViewColumn.
2098      * Mint 18 (gtk 3.18.9) using the add/remove column method would make a new button
2099      * widget upon reparenting, losing existing signal handlers.  In 3.22.11, however,
2100      * (debian stretch, LMDE3,) we get a nice segfault.
2101      *
2102      * This may seem a long way to go for a clean log, but the warnings can accumulate
2103      * quickly...
2104      */
2105 
2106     if (gtk_check_version (3, 22, 25) == NULL) {
2107         gint prev_view_column;
2108 
2109         prev_view_column = 0;
2110         for (l = view_columns; l != NULL; l = l->next) {
2111             g_signal_handlers_disconnect_by_func (gtk_tree_view_column_get_button (l->data),
2112                                                   column_header_clicked, list_view);
2113 
2114             gtk_tree_view_remove_column (list_view->details->tree_view, g_object_ref (l->data));
2115             gtk_tree_view_insert_column (list_view->details->tree_view, l->data, prev_view_column ++);
2116 
2117             g_signal_connect (gtk_tree_view_column_get_button (l->data),
2118                               "button-press-event",
2119                               G_CALLBACK (column_header_clicked),
2120                               list_view);
2121 
2122             gtk_tree_view_column_set_visible (l->data, TRUE);
2123             g_object_unref (l->data);
2124         }
2125     } else {
2126         GtkTreeViewColumn *prev_view_column;
2127 
2128         /* show new columns from the configuration */
2129         for (l = view_columns; l != NULL; l = l->next) {
2130             gtk_tree_view_column_set_visible (l->data, TRUE);
2131         }
2132 
2133         /* place columns in the correct order */
2134         prev_view_column = NULL;
2135         for (l = view_columns; l != NULL; l = l->next) {
2136             gtk_tree_view_move_column_after (list_view->details->tree_view, l->data, prev_view_column);
2137             prev_view_column = l->data;
2138         }
2139     }
2140 
2141     g_list_free (view_columns);
2142 }
2143 
2144 static void
filename_cell_data_func(GtkTreeViewColumn * column,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,NemoListView * view)2145 filename_cell_data_func (GtkTreeViewColumn *column,
2146 			 GtkCellRenderer   *renderer,
2147 			 GtkTreeModel      *model,
2148 			 GtkTreeIter       *iter,
2149 			 NemoListView        *view)
2150 {
2151 	char *text;
2152 	GtkTreePath *path;
2153 	PangoUnderline underline;
2154     gint weight;
2155 
2156 	gtk_tree_model_get (model, iter,
2157 			    view->details->file_name_column_num, &text,
2158                 NEMO_LIST_MODEL_TEXT_WEIGHT_COLUMN, &weight,
2159 			    -1);
2160 
2161 	if (click_policy == NEMO_CLICK_POLICY_SINGLE) {
2162 		path = gtk_tree_model_get_path (model, iter);
2163 
2164 		if (view->details->hover_path == NULL ||
2165 		    gtk_tree_path_compare (path, view->details->hover_path)) {
2166 			underline = PANGO_UNDERLINE_NONE;
2167 		} else {
2168 			underline = PANGO_UNDERLINE_SINGLE;
2169 		}
2170 
2171 		gtk_tree_path_free (path);
2172 	} else {
2173 		underline = PANGO_UNDERLINE_NONE;
2174 	}
2175 
2176 	g_object_set (G_OBJECT (renderer),
2177 		      "text", text,
2178 		      "underline", underline,
2179               "weight", weight,
2180 		      NULL);
2181 
2182     g_free (text);
2183 }
2184 
2185 static gboolean
focus_in_event_callback(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)2186 focus_in_event_callback (GtkWidget *widget, GdkEventFocus *event, gpointer user_data)
2187 {
2188 	NemoWindowSlot *slot;
2189 	NemoListView *list_view = NEMO_LIST_VIEW (user_data);
2190 
2191 	/* make the corresponding slot (and the pane that contains it) active */
2192 	slot = nemo_view_get_nemo_window_slot (NEMO_VIEW (list_view));
2193 	nemo_window_slot_make_hosting_pane_active (slot);
2194 
2195 	return FALSE;
2196 }
2197 
2198 static void
prioritize_visible_files(NemoListView * view)2199 prioritize_visible_files (NemoListView *view)
2200 {
2201     NemoFile *last_file;
2202     // GList *queue_list, *l;
2203     GdkRectangle vrect;
2204     GtkTreeIter iter;
2205     GtkTreePath *path;
2206     gint icon_size, cy, start_y, end_y, stepdown;
2207     gint bin_y;
2208 
2209     gtk_tree_view_get_visible_rect (view->details->tree_view,
2210                                     &vrect);
2211     icon_size = nemo_get_list_icon_size_for_zoom_level (nemo_list_view_get_zoom_level (NEMO_VIEW (view)));
2212 
2213     gtk_tree_view_convert_tree_to_bin_window_coords(view->details->tree_view,
2214                                                     1, vrect.y,
2215                                                     NULL, &bin_y);
2216 
2217     stepdown = icon_size * .75;
2218 
2219     start_y = bin_y - (vrect.height / 2);
2220     end_y = bin_y + vrect.height + (vrect.height / 2);
2221 
2222     last_file = NULL;
2223     cy = end_y;
2224 
2225     // Images that start out un-thumbnailed end up resolving in reverse
2226     // order, so work bottom-up here.
2227     while (cy > start_y) {
2228         if (gtk_tree_view_get_path_at_pos (view->details->tree_view,
2229                                            1, cy,
2230                                            &path, NULL, NULL, NULL)) {
2231             NemoFile *file;
2232             gboolean shown;
2233 
2234             gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
2235                                      &iter, path);
2236 
2237             gtk_tree_path_free (path);
2238             gtk_tree_model_get (GTK_TREE_MODEL (view->details->model),
2239                                 &iter,
2240                                 NEMO_LIST_MODEL_ICON_SHOWN, &shown,
2241                                 NEMO_LIST_MODEL_FILE_COLUMN, &file, -1);
2242 
2243             /* We'll catch some files twice, so filter them out */
2244             if (file != NULL && file != last_file) {
2245                 last_file = file;
2246 
2247                 if (nemo_file_get_load_deferred_attrs (file) == NEMO_FILE_LOAD_DEFERRED_ATTRS_NO) {
2248                     nemo_file_set_load_deferred_attrs (file, NEMO_FILE_LOAD_DEFERRED_ATTRS_YES);
2249                 }
2250 
2251                 if (nemo_file_is_thumbnailing (file)) {
2252                     gchar *uri = nemo_file_get_uri (file);
2253 
2254                     nemo_thumbnail_prioritize (uri);
2255                     g_free (uri);
2256                 } else {
2257                     nemo_file_invalidate_attributes (file, NEMO_FILE_DEFERRED_ATTRIBUTES);
2258                 }
2259             }
2260         }
2261 
2262         cy -= stepdown;
2263     }
2264 }
2265 
2266 static gboolean
update_visible_icons_cb(NemoListView * view)2267 update_visible_icons_cb (NemoListView *view)
2268 {
2269     prioritize_visible_files (view);
2270 
2271     view->details->update_visible_icons_id = 0;
2272     return G_SOURCE_REMOVE;
2273 }
2274 
2275 static void
queue_update_visible_icons(NemoListView * view,gint delay)2276 queue_update_visible_icons(NemoListView *view,
2277                            gint          delay)
2278 {
2279     if (view->details->update_visible_icons_id > 0) {
2280         g_source_remove (view->details->update_visible_icons_id);
2281     }
2282 
2283     view->details->update_visible_icons_id = g_timeout_add (delay, (GSourceFunc) update_visible_icons_cb, view);
2284 }
2285 
2286 static void
handle_vadjustment_changed(GtkAdjustment * adjustment,NemoListView * view)2287 handle_vadjustment_changed (GtkAdjustment *adjustment,
2288                             NemoListView  *view)
2289 {
2290     queue_update_visible_icons (view, NORMAL_UPDATE_VISIBLE_DELAY);
2291 }
2292 
2293 static gint
get_icon_scale_callback(NemoListModel * model,NemoListView * view)2294 get_icon_scale_callback (NemoListModel *model,
2295                          NemoListView  *view)
2296 {
2297    return gtk_widget_get_scale_factor (GTK_WIDGET (view->details->tree_view));
2298 }
2299 
2300 static void
on_treeview_realized(GtkWidget * widget,gpointer user_data)2301 on_treeview_realized (GtkWidget *widget,
2302                       gpointer   user_data)
2303 {
2304     NemoListView *view = NEMO_LIST_VIEW (user_data);
2305     GtkAdjustment *adjust;
2306 
2307     adjust = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (view->details->tree_view));
2308     g_signal_connect (adjust,
2309                       "value-changed",
2310                       G_CALLBACK (handle_vadjustment_changed),
2311                       view);
2312 }
2313 
2314 static void
create_and_set_up_tree_view(NemoListView * view)2315 create_and_set_up_tree_view (NemoListView *view)
2316 {
2317 	GtkCellRenderer *cell;
2318 	GtkTreeViewColumn *column;
2319 	GtkBindingSet *binding_set;
2320 	AtkObject *atk_obj;
2321 	GList *nemo_columns;
2322 	GList *l;
2323 	gchar **default_column_order, **default_visible_columns;
2324 
2325 	view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
2326 
2327     gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (view->details->tree_view), TRUE);
2328 
2329 	view->details->columns = g_hash_table_new_full (g_str_hash,
2330 							g_str_equal,
2331 							(GDestroyNotify) g_free,
2332 							NULL);
2333 
2334 	gtk_tree_view_set_enable_search (view->details->tree_view, TRUE);
2335 
2336 	/* Don't handle backspace key. It's used to open the parent folder. */
2337 	binding_set = gtk_binding_set_by_class (GTK_WIDGET_GET_CLASS (view->details->tree_view));
2338 	gtk_binding_entry_remove (binding_set, GDK_KEY_BackSpace, 0);
2339 
2340 	view->details->drag_dest =
2341 		nemo_tree_view_drag_dest_new (view->details->tree_view);
2342 
2343 	g_signal_connect_object (view->details->drag_dest,
2344 				 "get_root_uri",
2345 				 G_CALLBACK (get_root_uri_callback),
2346 				 view, 0);
2347 	g_signal_connect_object (view->details->drag_dest,
2348 				 "get_file_for_path",
2349 				 G_CALLBACK (get_file_for_path_callback),
2350 				 view, 0);
2351 	g_signal_connect_object (view->details->drag_dest,
2352 				 "move_copy_items",
2353 				 G_CALLBACK (move_copy_items_callback),
2354 				 view, 0);
2355 	g_signal_connect_object (view->details->drag_dest, "handle_netscape_url",
2356 				 G_CALLBACK (list_view_handle_netscape_url), view, 0);
2357 	g_signal_connect_object (view->details->drag_dest, "handle_uri_list",
2358 				 G_CALLBACK (list_view_handle_uri_list), view, 0);
2359 	g_signal_connect_object (view->details->drag_dest, "handle_text",
2360 				 G_CALLBACK (list_view_handle_text), view, 0);
2361 	g_signal_connect_object (view->details->drag_dest, "handle_raw",
2362 				 G_CALLBACK (list_view_handle_raw), view, 0);
2363 
2364 	g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view),
2365 				 "changed",
2366 				 G_CALLBACK (list_selection_changed_callback), view, 0);
2367 
2368     g_signal_connect_object (GTK_WIDGET (view->details->tree_view), "query-tooltip",
2369                              G_CALLBACK (query_tooltip_callback), view, 0);
2370 
2371 	g_signal_connect_object (view->details->tree_view, "drag_begin",
2372 				 G_CALLBACK (drag_begin_callback), view, 0);
2373 	g_signal_connect_object (view->details->tree_view, "drag_data_get",
2374 				 G_CALLBACK (drag_data_get_callback), view, 0);
2375 	g_signal_connect_object (view->details->tree_view, "motion_notify_event",
2376 				 G_CALLBACK (motion_notify_callback), view, 0);
2377 	g_signal_connect_object (view->details->tree_view, "enter_notify_event",
2378 				 G_CALLBACK (enter_notify_callback), view, 0);
2379 	g_signal_connect_object (view->details->tree_view, "leave_notify_event",
2380 				 G_CALLBACK (leave_notify_callback), view, 0);
2381 	g_signal_connect_object (view->details->tree_view, "button_press_event",
2382 				 G_CALLBACK (button_press_callback), view, 0);
2383 	g_signal_connect_object (view->details->tree_view, "button_release_event",
2384 				 G_CALLBACK (button_release_callback), view, 0);
2385 	g_signal_connect_object (view->details->tree_view, "key_press_event",
2386 				 G_CALLBACK (key_press_callback), view, 0);
2387 	g_signal_connect_object (view->details->tree_view, "popup_menu",
2388                                  G_CALLBACK (popup_menu_callback), view, 0);
2389 	g_signal_connect_object (view->details->tree_view, "row_expanded",
2390                                  G_CALLBACK (row_expanded_callback), view, 0);
2391 	g_signal_connect_object (view->details->tree_view, "row_collapsed",
2392                                  G_CALLBACK (row_collapsed_callback), view, 0);
2393 	g_signal_connect_object (view->details->tree_view, "row-activated",
2394                                  G_CALLBACK (row_activated_callback), view, 0);
2395 
2396     	g_signal_connect_object (view->details->tree_view, "focus_in_event",
2397 				 G_CALLBACK(focus_in_event_callback), view, 0);
2398 
2399     g_signal_connect (view->details->tree_view, "realize", G_CALLBACK (on_treeview_realized), view);
2400 
2401 	view->details->model = g_object_new (NEMO_TYPE_LIST_MODEL, NULL);
2402 	gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model));
2403 	/* Need the model for the dnd drop icon "accept" change */
2404 	nemo_list_model_set_drag_view (NEMO_LIST_MODEL (view->details->model),
2405 				     view->details->tree_view,  0, 0);
2406 
2407 	g_signal_connect_object (view->details->model, "sort_column_changed",
2408 				 G_CALLBACK (sort_column_changed_callback), view, 0);
2409 
2410 	g_signal_connect_object (view->details->model, "subdirectory_unloaded",
2411 				 G_CALLBACK (subdirectory_unloaded_callback), view, 0);
2412 
2413     g_signal_connect_object (view->details->model, "get-icon-scale",
2414                  G_CALLBACK (get_icon_scale_callback), view, 0);
2415 
2416 	gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view->details->tree_view), GTK_SELECTION_MULTIPLE);
2417 	gtk_tree_view_set_rules_hint (view->details->tree_view, TRUE);
2418 
2419 	nemo_columns = nemo_get_all_columns ();
2420 
2421 	for (l = nemo_columns; l != NULL; l = l->next) {
2422 		NemoColumn *nemo_column;
2423 		int column_num;
2424 		char *name;
2425 		char *label;
2426 		float xalign;
2427 
2428 		nemo_column = NEMO_COLUMN (l->data);
2429 
2430 		g_object_get (nemo_column,
2431 			      "name", &name,
2432 			      "label", &label,
2433 			      "xalign", &xalign, NULL);
2434 
2435 		column_num = nemo_list_model_add_column (view->details->model,
2436 						       nemo_column);
2437 
2438 		/* Created the name column specially, because it
2439 		 * has the icon in it.*/
2440 		if (!strcmp (name, "name")) {
2441 			/* Create the file name column */
2442 			cell = gtk_cell_renderer_pixbuf_new ();
2443 			view->details->pixbuf_cell = (GtkCellRendererPixbuf *)cell;
2444 
2445 			view->details->file_name_column = gtk_tree_view_column_new ();
2446             gtk_tree_view_append_column (view->details->tree_view,
2447                                          view->details->file_name_column);
2448 
2449             g_object_set_data_full (G_OBJECT (view->details->file_name_column),
2450                                     "column-id", g_strdup (name),
2451                                     g_free);
2452 
2453 			view->details->file_name_column_num = column_num;
2454 
2455 			g_hash_table_insert (view->details->columns,
2456 					     g_strdup ("name"),
2457 					     view->details->file_name_column);
2458 
2459             g_signal_connect (gtk_tree_view_column_get_button (view->details->file_name_column),
2460                               "button-press-event",
2461                               G_CALLBACK (column_header_clicked),
2462                               view);
2463 
2464 			gtk_tree_view_set_search_column (view->details->tree_view, column_num);
2465 
2466 			gtk_tree_view_column_set_sort_column_id (view->details->file_name_column, column_num);
2467 			gtk_tree_view_column_set_title (view->details->file_name_column, _("Name"));
2468 			gtk_tree_view_column_set_resizable (view->details->file_name_column, TRUE);
2469             gtk_tree_view_column_set_min_width (view->details->file_name_column, 100);
2470             gtk_tree_view_column_set_reorderable (view->details->file_name_column, TRUE);
2471             gtk_tree_view_column_set_expand (view->details->file_name_column, TRUE);
2472 
2473 			gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE);
2474 			gtk_tree_view_column_set_attributes (view->details->file_name_column,
2475 							     cell,
2476 							     "surface", NEMO_LIST_MODEL_SMALLEST_ICON_COLUMN,
2477 							     NULL);
2478 
2479 			cell = gtk_cell_renderer_text_new ();
2480 			view->details->file_name_cell = (GtkCellRendererText *)cell;
2481             g_object_set (cell,
2482                           "xpad", 5,
2483                           "ellipsize", PANGO_ELLIPSIZE_END,
2484                           "width-chars", 40,
2485                           NULL);
2486 
2487 			g_signal_connect (cell, "edited", G_CALLBACK (cell_renderer_edited), view);
2488 			g_signal_connect (cell, "editing-canceled", G_CALLBACK (cell_renderer_editing_canceled), view);
2489 			g_signal_connect (cell, "editing-started", G_CALLBACK (cell_renderer_editing_started_cb), view);
2490 
2491 			gtk_tree_view_column_pack_start (view->details->file_name_column, cell, TRUE);
2492 			gtk_tree_view_column_set_cell_data_func (view->details->file_name_column, cell,
2493 								 (GtkTreeCellDataFunc) filename_cell_data_func,
2494 								 view, NULL);
2495 		} else {
2496 			cell = gtk_cell_renderer_text_new ();
2497             g_object_set (cell,
2498                           "xalign", xalign,
2499                           "xpad", 5,
2500                           NULL);
2501 			view->details->cells = g_list_append (view->details->cells,
2502 							      cell);
2503             column = gtk_tree_view_column_new ();
2504 
2505             g_object_set_data_full (G_OBJECT (column),
2506                                     "column-id", g_strdup (name),
2507                                     g_free);
2508 
2509             gtk_tree_view_column_set_title (column, label);
2510             gtk_tree_view_column_pack_start (column, cell, TRUE);
2511             gtk_tree_view_column_set_attributes (column, cell,
2512                                                  "text", column_num,
2513                                                  "weight", NEMO_LIST_MODEL_TEXT_WEIGHT_COLUMN,
2514                                                  NULL);
2515 
2516             gtk_tree_view_append_column (view->details->tree_view, column);
2517             gtk_tree_view_column_set_min_width (column, 30);
2518 			gtk_tree_view_column_set_sort_column_id (column, column_num);
2519 
2520             g_hash_table_insert (view->details->columns,
2521                                  g_strdup (name),
2522                                  column);
2523 
2524             g_signal_connect (gtk_tree_view_column_get_button (column),
2525                               "button-press-event",
2526                               G_CALLBACK (column_header_clicked),
2527                               view);
2528 
2529 			gtk_tree_view_column_set_resizable (column, TRUE);
2530             gtk_tree_view_column_set_reorderable (column, TRUE);
2531 		}
2532 		g_free (name);
2533 		g_free (label);
2534 	}
2535 	nemo_column_list_free (nemo_columns);
2536 
2537 	default_visible_columns = g_settings_get_strv (nemo_list_view_preferences,
2538 						       NEMO_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS);
2539 	default_column_order = g_settings_get_strv (nemo_list_view_preferences,
2540 						    NEMO_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER);
2541 
2542 	/* Apply the default column order and visible columns, to get it
2543 	 * right most of the time. The metadata will be checked when a
2544 	 * folder is loaded */
2545 	apply_columns_settings (view,
2546 				default_column_order,
2547 				default_visible_columns);
2548 
2549 	gtk_widget_show (GTK_WIDGET (view->details->tree_view));
2550 	gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (view->details->tree_view));
2551 
2552         atk_obj = gtk_widget_get_accessible (GTK_WIDGET (view->details->tree_view));
2553         atk_object_set_name (atk_obj, _("List View"));
2554 
2555     gtk_widget_set_has_tooltip (GTK_WIDGET (view->details->tree_view), TRUE);
2556 
2557 	g_strfreev (default_visible_columns);
2558 	g_strfreev (default_column_order);
2559 }
2560 
2561 static void
nemo_list_view_add_file(NemoView * view,NemoFile * file,NemoDirectory * directory)2562 nemo_list_view_add_file (NemoView *view, NemoFile *file, NemoDirectory *directory)
2563 {
2564 	NemoListModel *model;
2565 
2566     if (nemo_file_has_thumbnail_access_problem (file)) {
2567         nemo_application_set_cache_flag (nemo_application_get_singleton ());
2568         nemo_window_slot_check_bad_cache_bar (nemo_view_get_nemo_window_slot (view));
2569     }
2570 
2571 	model = NEMO_LIST_VIEW (view)->details->model;
2572 	nemo_list_model_add_file (model, file, directory);
2573     queue_update_visible_icons (NEMO_LIST_VIEW (view), INITIAL_UPDATE_VISIBLE_DELAY);
2574 }
2575 
2576 static char **
get_default_visible_columns(NemoListView * list_view)2577 get_default_visible_columns (NemoListView *list_view)
2578 {
2579     NemoFile *file;
2580     NemoDirectory *directory;
2581 
2582     file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
2583 
2584     if (nemo_file_is_in_trash (file)) {
2585         return g_strdupv ((gchar **) default_trash_visible_columns);
2586     }
2587 
2588     if (nemo_file_is_in_recent (file)) {
2589         return g_strdupv ((gchar **) default_recent_visible_columns);
2590     }
2591 
2592     if (nemo_file_is_in_favorites (file)) {
2593         return g_strdupv ((gchar **) default_favorites_visible_columns);
2594     }
2595 
2596     directory = nemo_view_get_model (NEMO_VIEW (list_view));
2597     if (NEMO_IS_SEARCH_DIRECTORY (directory)) {
2598         return g_settings_get_strv (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS);
2599     }
2600 
2601     return g_settings_get_strv (nemo_list_view_preferences,
2602                                 NEMO_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS);
2603 }
2604 
2605 static char **
get_visible_columns(NemoListView * list_view)2606 get_visible_columns (NemoListView *list_view)
2607 {
2608 	NemoFile *file;
2609 	GList *visible_columns;
2610 	char **ret;
2611 
2612 	ret = NULL;
2613 
2614 	file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
2615 
2616     if (nemo_global_preferences_get_ignore_view_metadata ()) {
2617         visible_columns = nemo_window_get_ignore_meta_visible_columns (nemo_view_get_nemo_window (NEMO_VIEW (list_view)));
2618     } else {
2619         visible_columns = nemo_file_get_metadata_list (file,
2620                                                    NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS);
2621     }
2622 
2623 	if (visible_columns) {
2624         ret = string_array_from_string_glist (visible_columns);
2625         g_list_free_full (visible_columns, g_free);
2626 	}
2627 
2628 	if (ret != NULL) {
2629 		return ret;
2630 	}
2631 
2632 	return get_default_visible_columns (list_view);
2633 }
2634 
2635 static char **
get_default_column_order(NemoListView * list_view)2636 get_default_column_order (NemoListView *list_view)
2637 {
2638     NemoFile *file;
2639     NemoDirectory *directory;
2640 
2641     file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
2642 
2643     if (nemo_file_is_in_trash (file)) {
2644         return g_strdupv ((gchar **) default_trash_columns_order);
2645     }
2646 
2647     if (nemo_file_is_in_recent (file)) {
2648         return g_strdupv ((gchar **) default_recent_columns_order);
2649     }
2650 
2651     if (nemo_file_is_in_favorites (file)) {
2652         return g_strdupv ((gchar **) default_favorites_columns_order);
2653     }
2654 
2655     directory = nemo_view_get_model (NEMO_VIEW (list_view));
2656     if (NEMO_IS_SEARCH_DIRECTORY (directory)) {
2657         return g_settings_get_strv (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS);
2658     }
2659 
2660     return g_settings_get_strv (nemo_list_view_preferences,
2661                                 NEMO_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER);
2662 }
2663 
2664 static char **
get_column_order(NemoListView * list_view)2665 get_column_order (NemoListView *list_view)
2666 {
2667 	NemoFile *file;
2668 	GList *column_order;
2669 	char **ret;
2670 
2671 	ret = NULL;
2672 
2673 	file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
2674 
2675     if (nemo_global_preferences_get_ignore_view_metadata ()) {
2676         column_order = nemo_window_get_ignore_meta_column_order (nemo_view_get_nemo_window (NEMO_VIEW (list_view)));
2677     } else {
2678         column_order = nemo_file_get_metadata_list (file,
2679                                                     NEMO_METADATA_KEY_LIST_VIEW_COLUMN_ORDER);
2680     }
2681 
2682     if (column_order) {
2683         ret = string_array_from_string_glist (column_order);
2684         g_list_free_full (column_order, g_free);
2685     }
2686 
2687 	if (ret != NULL) {
2688 		return ret;
2689 	}
2690 
2691 	return get_default_column_order (list_view);
2692 }
2693 
2694 static void
set_columns_settings_from_metadata_and_preferences(NemoListView * list_view)2695 set_columns_settings_from_metadata_and_preferences (NemoListView *list_view)
2696 {
2697 	char **column_order;
2698 	char **visible_columns;
2699 
2700 	column_order = get_column_order (list_view);
2701 	visible_columns = get_visible_columns (list_view);
2702 
2703 	apply_columns_settings (list_view, column_order, visible_columns);
2704 
2705 	g_strfreev (column_order);
2706 	g_strfreev (visible_columns);
2707 }
2708 
2709 static void
set_sort_order_from_metadata_and_preferences(NemoListView * list_view)2710 set_sort_order_from_metadata_and_preferences (NemoListView *list_view)
2711 {
2712 	char *sort_attribute;
2713 	int sort_column_id;
2714 	NemoFile *file;
2715 	gboolean sort_reversed, default_sort_reversed;
2716 	const gchar *default_sort_order;
2717 
2718 	file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
2719 
2720         if (nemo_global_preferences_get_ignore_view_metadata ())
2721                 sort_attribute = g_strdup (nemo_window_get_ignore_meta_sort_column (nemo_view_get_nemo_window (NEMO_VIEW (list_view))));
2722         else
2723                 sort_attribute = nemo_file_get_metadata (file,
2724                                                          NEMO_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
2725                                                          NULL);
2726 	sort_column_id = nemo_list_model_get_sort_column_id_from_attribute (list_view->details->model,
2727 									  g_quark_from_string (sort_attribute));
2728 	g_free (sort_attribute);
2729 
2730 	default_sort_order = get_default_sort_order (file, &default_sort_reversed);
2731 
2732 	if (sort_column_id == -1) {
2733 		sort_column_id =
2734 			nemo_list_model_get_sort_column_id_from_attribute (list_view->details->model,
2735 									 g_quark_from_string (default_sort_order));
2736 	}
2737 
2738     if (nemo_global_preferences_get_ignore_view_metadata ()) {
2739         gint dir = nemo_window_get_ignore_meta_sort_direction (nemo_view_get_nemo_window (NEMO_VIEW (list_view)));
2740         sort_reversed = dir > SORT_NULL ? dir == SORT_DESCENDING : default_sort_reversed;
2741     } else {
2742         sort_reversed = nemo_file_get_boolean_metadata (file,
2743                                                         NEMO_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
2744                                                         default_sort_reversed);
2745     }
2746     gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_view->details->model),
2747                                                              sort_column_id,
2748                                                              sort_reversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
2749 }
2750 
2751 static gboolean
list_view_changed_foreach(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)2752 list_view_changed_foreach (GtkTreeModel *model,
2753               		   GtkTreePath  *path,
2754 			   GtkTreeIter  *iter,
2755 			   gpointer      data)
2756 {
2757 	gtk_tree_model_row_changed (model, path, iter);
2758 	return FALSE;
2759 }
2760 
2761 static NemoZoomLevel
get_default_zoom_level(void)2762 get_default_zoom_level (void) {
2763 	NemoZoomLevel default_zoom_level;
2764 
2765 	default_zoom_level = g_settings_get_enum (nemo_list_view_preferences,
2766 						  NEMO_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL);
2767 
2768 	if (default_zoom_level <  NEMO_ZOOM_LEVEL_SMALLEST
2769 	    || NEMO_ZOOM_LEVEL_LARGEST < default_zoom_level) {
2770 		default_zoom_level = NEMO_ZOOM_LEVEL_SMALL;
2771 	}
2772 
2773 	return default_zoom_level;
2774 }
2775 
2776 static void
set_zoom_level_from_metadata_and_preferences(NemoListView * list_view)2777 set_zoom_level_from_metadata_and_preferences (NemoListView *list_view)
2778 {
2779 	NemoFile *file;
2780 	int level;
2781 
2782 	if (nemo_view_supports_zooming (NEMO_VIEW (list_view))) {
2783 		file = nemo_view_get_directory_as_file (NEMO_VIEW (list_view));
2784         if (nemo_global_preferences_get_ignore_view_metadata ()) {
2785             gchar *uri;
2786 
2787             uri = nemo_file_get_uri (file);
2788 
2789             if (eel_uri_is_search (uri)) {
2790                 level = get_default_zoom_level ();
2791             } else {
2792                 gint ignore_level;
2793                 ignore_level = nemo_window_get_ignore_meta_zoom_level (nemo_view_get_nemo_window (NEMO_VIEW (list_view)));
2794 
2795                 level = ignore_level > -1 ? ignore_level : get_default_zoom_level ();
2796             }
2797 
2798             g_free (uri);
2799         } else {
2800             level = nemo_file_get_integer_metadata (file,
2801 							    NEMO_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL,
2802 							    get_default_zoom_level ());
2803         }
2804 		nemo_list_view_set_zoom_level (list_view, level, TRUE);
2805 
2806 		/* updated the rows after updating the font size */
2807 		gtk_tree_model_foreach (GTK_TREE_MODEL (list_view->details->model),
2808 					list_view_changed_foreach, NULL);
2809 	}
2810 }
2811 
2812 static void
nemo_list_view_begin_loading(NemoView * view)2813 nemo_list_view_begin_loading (NemoView *view)
2814 {
2815 	NemoListView *list_view;
2816 
2817 	list_view = NEMO_LIST_VIEW (view);
2818 
2819 	set_sort_order_from_metadata_and_preferences (list_view);
2820 	set_zoom_level_from_metadata_and_preferences (list_view);
2821 	set_columns_settings_from_metadata_and_preferences (list_view);
2822 
2823     set_ok_to_load_deferred_attrs (list_view, FALSE);
2824 
2825     AtkObject *atk = gtk_widget_get_accessible (GTK_WIDGET (NEMO_LIST_VIEW (view)->details->tree_view));
2826 
2827     g_signal_connect_object (atk, "column-reordered",
2828                              G_CALLBACK (columns_reordered_callback), view, 0);
2829 }
2830 
2831 static void
stop_cell_editing(NemoListView * list_view)2832 stop_cell_editing (NemoListView *list_view)
2833 {
2834 	GtkTreeViewColumn *column;
2835 
2836 	/* Stop an ongoing rename to commit the name changes when the user
2837 	 * changes directories without exiting cell edit mode. It also prevents
2838 	 * the edited handler from being called on the cleared list model.
2839 	 */
2840 	column = list_view->details->file_name_column;
2841 	if (column != NULL && list_view->details->editable_widget != NULL &&
2842 	    GTK_IS_CELL_EDITABLE (list_view->details->editable_widget)) {
2843 		gtk_cell_editable_editing_done (list_view->details->editable_widget);
2844 	}
2845 }
2846 
2847 static void
nemo_list_view_clear(NemoView * view)2848 nemo_list_view_clear (NemoView *view)
2849 {
2850 	NemoListView *list_view;
2851     GtkTreeSelection *tree_selection;
2852 
2853 	list_view = NEMO_LIST_VIEW (view);
2854 
2855     list_view->details->ok_to_load_deferred_attrs = FALSE;
2856 
2857     if (list_view->details->update_visible_icons_id > 0) {
2858         g_source_remove (list_view->details->update_visible_icons_id);
2859         list_view->details->update_visible_icons_id = 0;
2860     }
2861 
2862     tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
2863 
2864     g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
2865 
2866 	if (list_view->details->model != NULL) {
2867 		stop_cell_editing (list_view);
2868 		nemo_list_model_clear (list_view->details->model);
2869 	}
2870 
2871     g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
2872 }
2873 
2874 static void
nemo_list_view_rename_callback(NemoFile * file,GFile * result_location,GError * error,gpointer callback_data)2875 nemo_list_view_rename_callback (NemoFile *file,
2876 				    GFile *result_location,
2877 				    GError *error,
2878 				    gpointer callback_data)
2879 {
2880 	NemoListView *view;
2881 
2882 	view = NEMO_LIST_VIEW (callback_data);
2883 
2884 	if (view->details->renaming_file) {
2885 		view->details->rename_done = TRUE;
2886 
2887 		if (error != NULL) {
2888 			/* If the rename failed (or was cancelled), kill renaming_file.
2889 			 * We won't get a change event for the rename, so otherwise
2890 			 * it would stay around forever.
2891 			 */
2892 			nemo_file_unref (view->details->renaming_file);
2893 			view->details->renaming_file = NULL;
2894 		}
2895 	}
2896 
2897 	g_object_unref (view);
2898 }
2899 
2900 
2901 static void
nemo_list_view_file_changed(NemoView * view,NemoFile * file,NemoDirectory * directory)2902 nemo_list_view_file_changed (NemoView *view, NemoFile *file, NemoDirectory *directory)
2903 {
2904 	NemoListView *listview;
2905 	GtkTreeIter iter;
2906 	GtkTreePath *file_path;
2907 
2908 	listview = NEMO_LIST_VIEW (view);
2909 
2910 	nemo_list_model_file_changed (listview->details->model, file, directory);
2911 
2912 	if (listview->details->renaming_file != NULL &&
2913 	    file == listview->details->renaming_file &&
2914 	    listview->details->rename_done) {
2915 		/* This is (probably) the result of the rename operation, and
2916 		 * the tree-view changes above could have resorted the list, so
2917 		 * scroll to the new position
2918 		 */
2919 		if (nemo_list_model_get_tree_iter_from_file (listview->details->model, file, directory, &iter)) {
2920 			file_path = gtk_tree_model_get_path (GTK_TREE_MODEL (listview->details->model), &iter);
2921 			gtk_tree_view_scroll_to_cell (listview->details->tree_view,
2922 						      file_path, NULL,
2923 						      FALSE, 0.0, 0.0);
2924 			gtk_tree_path_free (file_path);
2925 		}
2926 
2927 		nemo_file_unref (listview->details->renaming_file);
2928 		listview->details->renaming_file = NULL;
2929 	}
2930 }
2931 
2932 typedef struct {
2933 	GtkTreePath *path;
2934 	gboolean is_common;
2935 	gboolean is_root;
2936 } HasCommonParentData;
2937 
2938 static void
tree_selection_has_common_parent_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)2939 tree_selection_has_common_parent_foreach_func (GtkTreeModel *model,
2940 						GtkTreePath *path,
2941 						GtkTreeIter *iter,
2942 						gpointer user_data)
2943 {
2944 	HasCommonParentData *data;
2945 	GtkTreePath *parent_path;
2946 	gboolean has_parent;
2947 
2948 	data = (HasCommonParentData *) user_data;
2949 
2950 	parent_path = gtk_tree_path_copy (path);
2951 	gtk_tree_path_up (parent_path);
2952 
2953 	has_parent = (gtk_tree_path_get_depth (parent_path) > 0) ? TRUE : FALSE;
2954 
2955 	if (!has_parent) {
2956 		data->is_root = TRUE;
2957 	}
2958 
2959 	if (data->is_common && !data->is_root) {
2960 		if (data->path == NULL) {
2961 			data->path = gtk_tree_path_copy (parent_path);
2962 		} else if (gtk_tree_path_compare (data->path, parent_path) != 0) {
2963 			data->is_common = FALSE;
2964 		}
2965 	}
2966 
2967 	gtk_tree_path_free (parent_path);
2968 }
2969 
2970 static void
tree_selection_has_common_parent(GtkTreeSelection * selection,gboolean * is_common,gboolean * is_root)2971 tree_selection_has_common_parent (GtkTreeSelection *selection,
2972 				  gboolean *is_common,
2973 				  gboolean *is_root)
2974 {
2975 	HasCommonParentData data;
2976 
2977 	g_assert (is_common != NULL);
2978 	g_assert (is_root != NULL);
2979 
2980 	data.path = NULL;
2981 	data.is_common = *is_common = TRUE;
2982 	data.is_root = *is_root = FALSE;
2983 
2984 	gtk_tree_selection_selected_foreach (selection,
2985 					     tree_selection_has_common_parent_foreach_func,
2986 					     &data);
2987 
2988 	*is_common = data.is_common;
2989 	*is_root = data.is_root;
2990 
2991 	if (data.path != NULL) {
2992 		gtk_tree_path_free (data.path);
2993 	}
2994 }
2995 
2996 static char *
nemo_list_view_get_backing_uri(NemoView * view)2997 nemo_list_view_get_backing_uri (NemoView *view)
2998 {
2999 	NemoListView *list_view;
3000 	NemoListModel *list_model;
3001 	NemoFile *file;
3002 	GtkTreeView *tree_view;
3003 	GtkTreeSelection *selection;
3004 	GtkTreePath *path;
3005 	GList *paths;
3006 	guint length;
3007 	char *uri;
3008 
3009 	g_return_val_if_fail (NEMO_IS_LIST_VIEW (view), NULL);
3010 
3011 	list_view = NEMO_LIST_VIEW (view);
3012 	list_model = list_view->details->model;
3013 	tree_view = list_view->details->tree_view;
3014 
3015 	g_assert (list_model);
3016 
3017 	/* We currently handle three common cases here:
3018 	 * (a) if the selection contains non-filesystem items (i.e., the
3019 	 *     "(Empty)" label), we return the uri of the parent.
3020 	 * (b) if the selection consists of exactly one _expanded_ directory, we
3021 	 *     return its URI.
3022 	 * (c) if the selection consists of either exactly one item which is not
3023 	 *     an expanded directory) or multiple items in the same directory,
3024 	 *     we return the URI of the common parent.
3025 	 */
3026 
3027 	uri = NULL;
3028 
3029 	selection = gtk_tree_view_get_selection (tree_view);
3030 	length = gtk_tree_selection_count_selected_rows (selection);
3031 
3032 	if (length == 1) {
3033 
3034 		paths = gtk_tree_selection_get_selected_rows (selection, NULL);
3035 		path = (GtkTreePath *) paths->data;
3036 
3037 		file = nemo_list_model_file_for_path (list_model, path);
3038 		if (file == NULL) {
3039 			/* The selected item is a label, not a file */
3040 			gtk_tree_path_up (path);
3041 			file = nemo_list_model_file_for_path (list_model, path);
3042 		}
3043 
3044 		if (file != NULL) {
3045 			if (nemo_file_is_directory (file) &&
3046 			    gtk_tree_view_row_expanded (tree_view, path)) {
3047 				uri = nemo_file_get_uri (file);
3048 			}
3049 			nemo_file_unref (file);
3050 		}
3051 
3052 		gtk_tree_path_free (path);
3053 		g_list_free (paths);
3054 	}
3055 
3056 	if (uri == NULL && length > 0) {
3057 
3058 		gboolean is_common, is_root;
3059 
3060 		/* Check that all the selected items belong to the same
3061 		 * directory and that directory is not the root directory (which
3062 		 * is handled by NemoView::get_backing_directory.) */
3063 
3064 		tree_selection_has_common_parent (selection, &is_common, &is_root);
3065 
3066 		if (is_common && !is_root) {
3067 
3068 			paths = gtk_tree_selection_get_selected_rows (selection, NULL);
3069 			path = (GtkTreePath *) paths->data;
3070 
3071 			file = nemo_list_model_file_for_path (list_model, path);
3072 			g_assert (file != NULL);
3073 			uri = nemo_file_get_parent_uri (file);
3074 			nemo_file_unref (file);
3075 
3076 			g_list_foreach (paths, (GFunc) gtk_tree_path_free, NULL);
3077 			g_list_free (paths);
3078 		}
3079 	}
3080 
3081 	if (uri != NULL) {
3082 		return uri;
3083 	}
3084 
3085 	return NEMO_VIEW_CLASS (nemo_list_view_parent_class)->get_backing_uri (view);
3086 }
3087 
3088 static void
nemo_list_view_get_selection_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)3089 nemo_list_view_get_selection_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
3090 {
3091 	GList **list;
3092 	NemoFile *file;
3093 
3094 	list = data;
3095 
3096 	gtk_tree_model_get (model, iter,
3097 			    NEMO_LIST_MODEL_FILE_COLUMN, &file,
3098 			    -1);
3099 
3100 	if (file != NULL) {
3101 		(* list) = g_list_prepend ((* list), file);
3102 	}
3103 }
3104 
3105 static GList *
nemo_list_view_get_selection(NemoView * view)3106 nemo_list_view_get_selection (NemoView *view)
3107 {
3108 	GList *list;
3109 
3110 	list = NULL;
3111 
3112 	gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (NEMO_LIST_VIEW (view)->details->tree_view),
3113 					     nemo_list_view_get_selection_foreach_func, &list);
3114 
3115 	return g_list_reverse (list);
3116 }
3117 
3118 static GList *
nemo_list_view_peek_selection(NemoView * view)3119 nemo_list_view_peek_selection (NemoView *view)
3120 {
3121     NemoListView *list_view = NEMO_LIST_VIEW (view);
3122 
3123     if (list_view->details->current_selection_count == -1) {
3124         nemo_list_view_update_selection (NEMO_VIEW (list_view));
3125     }
3126 
3127     return list_view->details->current_selection;
3128 }
3129 
3130 static gint
nemo_list_view_get_selection_count(NemoView * view)3131 nemo_list_view_get_selection_count (NemoView *view)
3132 {
3133     NemoListView *list_view = NEMO_LIST_VIEW (view);
3134 
3135     if (list_view->details->current_selection_count == -1) {
3136         nemo_list_view_update_selection (NEMO_VIEW (list_view));
3137     }
3138 
3139     return list_view->details->current_selection_count;
3140 }
3141 
3142 static void
nemo_list_view_update_selection(NemoView * view)3143 nemo_list_view_update_selection (NemoView *view)
3144 {
3145     NemoListView *list_view = NEMO_LIST_VIEW (view);
3146 
3147     if (list_view->details->current_selection != NULL) {
3148         g_list_free (list_view->details->current_selection);
3149 
3150         list_view->details->current_selection = NULL;
3151         list_view->details->current_selection_count = 0;
3152     }
3153 
3154     list_view->details->current_selection = nemo_list_view_get_selection (view);
3155     list_view->details->current_selection_count = g_list_length (list_view->details->current_selection);
3156 }
3157 
3158 static void
nemo_list_view_get_selection_for_file_transfer_foreach_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)3159 nemo_list_view_get_selection_for_file_transfer_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
3160 {
3161 	NemoFile *file;
3162 	struct SelectionForeachData *selection_data;
3163 	GtkTreeIter parent, child;
3164 
3165 	selection_data = data;
3166 
3167 	gtk_tree_model_get (model, iter,
3168 			    NEMO_LIST_MODEL_FILE_COLUMN, &file,
3169 			    -1);
3170 
3171 	if (file != NULL) {
3172 		/* If the parent folder is also selected, don't include this file in the
3173 		 * file operation, since that would copy it to the toplevel target instead
3174 		 * of keeping it as a child of the copied folder
3175 		 */
3176 		child = *iter;
3177 		while (gtk_tree_model_iter_parent (model, &parent, &child)) {
3178 			if (gtk_tree_selection_iter_is_selected (selection_data->selection,
3179 								 &parent)) {
3180 				return;
3181 			}
3182 			child = parent;
3183 		}
3184 
3185 		nemo_file_ref (file);
3186 		selection_data->list = g_list_prepend (selection_data->list, file);
3187 	}
3188 }
3189 
3190 
3191 static GList *
nemo_list_view_get_selection_for_file_transfer(NemoView * view)3192 nemo_list_view_get_selection_for_file_transfer (NemoView *view)
3193 {
3194 	struct SelectionForeachData selection_data;
3195 
3196 	selection_data.list = NULL;
3197 	selection_data.selection = gtk_tree_view_get_selection (NEMO_LIST_VIEW (view)->details->tree_view);
3198 
3199 	gtk_tree_selection_selected_foreach (selection_data.selection,
3200 					     nemo_list_view_get_selection_for_file_transfer_foreach_func, &selection_data);
3201 
3202 	return g_list_reverse (selection_data.list);
3203 }
3204 
3205 
3206 
3207 
3208 static guint
nemo_list_view_get_item_count(NemoView * view)3209 nemo_list_view_get_item_count (NemoView *view)
3210 {
3211 	g_return_val_if_fail (NEMO_IS_LIST_VIEW (view), 0);
3212 
3213 	return nemo_list_model_get_length (NEMO_LIST_VIEW (view)->details->model);
3214 }
3215 
3216 static gboolean
nemo_list_view_is_empty(NemoView * view)3217 nemo_list_view_is_empty (NemoView *view)
3218 {
3219 	return nemo_list_model_is_empty (NEMO_LIST_VIEW (view)->details->model);
3220 }
3221 
3222 static void
nemo_list_view_end_file_changes(NemoView * view)3223 nemo_list_view_end_file_changes (NemoView *view)
3224 {
3225 	NemoListView *list_view;
3226 
3227 	list_view = NEMO_LIST_VIEW (view);
3228 
3229 	if (list_view->details->new_selection_path) {
3230 		gtk_tree_view_set_cursor (list_view->details->tree_view,
3231 					  list_view->details->new_selection_path,
3232 					  NULL, FALSE);
3233 		gtk_tree_path_free (list_view->details->new_selection_path);
3234 		list_view->details->new_selection_path = NULL;
3235 	}
3236 }
3237 
3238 static void
nemo_list_view_remove_file(NemoView * view,NemoFile * file,NemoDirectory * directory)3239 nemo_list_view_remove_file (NemoView *view, NemoFile *file, NemoDirectory *directory)
3240 {
3241 	GtkTreePath *path;
3242 	GtkTreePath *file_path;
3243 	GtkTreeIter iter;
3244 	GtkTreeIter temp_iter;
3245 	GtkTreeRowReference* row_reference;
3246 	NemoListView *list_view;
3247 	GtkTreeModel* tree_model;
3248 	GtkTreeSelection *selection;
3249 
3250 	path = NULL;
3251 	row_reference = NULL;
3252 	list_view = NEMO_LIST_VIEW (view);
3253 	tree_model = GTK_TREE_MODEL(list_view->details->model);
3254 
3255 	if (nemo_list_model_get_tree_iter_from_file (list_view->details->model, file, directory, &iter)) {
3256 		selection = gtk_tree_view_get_selection (list_view->details->tree_view);
3257 		file_path = gtk_tree_model_get_path (tree_model, &iter);
3258 
3259 		if (gtk_tree_selection_path_is_selected (selection, file_path)) {
3260 			/* get reference for next element in the list view. If the element to be deleted is the
3261 			 * last one, get reference to previous element. If there is only one element in view
3262 			 * no need to select anything.
3263 			 */
3264 			temp_iter = iter;
3265 
3266 			if (gtk_tree_model_iter_next (tree_model, &iter)) {
3267 				path = gtk_tree_model_get_path (tree_model, &iter);
3268 				row_reference = gtk_tree_row_reference_new (tree_model, path);
3269 			} else {
3270 				path = gtk_tree_model_get_path (tree_model, &temp_iter);
3271 				if (gtk_tree_path_prev (path)) {
3272 					row_reference = gtk_tree_row_reference_new (tree_model, path);
3273 				}
3274 			}
3275 			gtk_tree_path_free (path);
3276 		}
3277 
3278 		gtk_tree_path_free (file_path);
3279 
3280 		nemo_list_model_remove_file (list_view->details->model, file, directory);
3281 
3282 		if (gtk_tree_row_reference_valid (row_reference)) {
3283 			if (list_view->details->new_selection_path) {
3284 				gtk_tree_path_free (list_view->details->new_selection_path);
3285 			}
3286 			list_view->details->new_selection_path = gtk_tree_row_reference_get_path (row_reference);
3287 		}
3288 
3289 		if (row_reference) {
3290 			gtk_tree_row_reference_free (row_reference);
3291 		}
3292 	}
3293 
3294 
3295 }
3296 
3297 static void
nemo_list_view_set_selection(NemoView * view,GList * selection)3298 nemo_list_view_set_selection (NemoView *view, GList *selection)
3299 {
3300 	NemoListView *list_view;
3301 	GtkTreeSelection *tree_selection;
3302 	GList *node;
3303 	GList *iters, *l;
3304 	NemoFile *file;
3305 
3306 	list_view = NEMO_LIST_VIEW (view);
3307 	tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
3308 
3309 	g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
3310 
3311 	gtk_tree_selection_unselect_all (tree_selection);
3312 	for (node = selection; node != NULL; node = node->next) {
3313 		file = node->data;
3314 		iters = nemo_list_model_get_all_iters_for_file (list_view->details->model, file);
3315 
3316 		for (l = iters; l != NULL; l = l->next) {
3317 			gtk_tree_selection_select_iter (tree_selection,
3318 							(GtkTreeIter *)l->data);
3319 		}
3320 		g_list_free_full (iters, g_free);
3321 	}
3322 
3323 	g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
3324 	nemo_view_notify_selection_changed (view);
3325 }
3326 
3327 static void
nemo_list_view_invert_selection(NemoView * view)3328 nemo_list_view_invert_selection (NemoView *view)
3329 {
3330 	NemoListView *list_view;
3331 	GtkTreeSelection *tree_selection;
3332 	GList *node;
3333 	GList *iters, *l;
3334 	NemoFile *file;
3335 	GList *selection = NULL;
3336 
3337 	list_view = NEMO_LIST_VIEW (view);
3338 	tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
3339 
3340 	g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
3341 
3342 	gtk_tree_selection_selected_foreach (tree_selection,
3343 					     nemo_list_view_get_selection_foreach_func, &selection);
3344 
3345 	gtk_tree_selection_select_all (tree_selection);
3346 
3347 	for (node = selection; node != NULL; node = node->next) {
3348 		file = node->data;
3349 		iters = nemo_list_model_get_all_iters_for_file (list_view->details->model, file);
3350 
3351 		for (l = iters; l != NULL; l = l->next) {
3352 			gtk_tree_selection_unselect_iter (tree_selection,
3353 							  (GtkTreeIter *)l->data);
3354 		}
3355 		g_list_free_full (iters, g_free);
3356 	}
3357 
3358 	g_list_free (selection);
3359 
3360 	g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
3361 	nemo_view_notify_selection_changed (view);
3362 }
3363 
3364 static void
nemo_list_view_select_all(NemoView * view)3365 nemo_list_view_select_all (NemoView *view)
3366 {
3367 	gtk_tree_selection_select_all (gtk_tree_view_get_selection (NEMO_LIST_VIEW (view)->details->tree_view));
3368 }
3369 
3370 static void
nemo_list_view_merge_menus(NemoView * view)3371 nemo_list_view_merge_menus (NemoView *view)
3372 {
3373   NemoListView *list_view;
3374   GtkUIManager *ui_manager;
3375   GtkActionGroup *action_group;
3376 
3377   list_view = NEMO_LIST_VIEW (view);
3378 
3379   NEMO_VIEW_CLASS (nemo_list_view_parent_class)->merge_menus (view);
3380 
3381   ui_manager = nemo_view_get_ui_manager (view);
3382 
3383   action_group = gtk_action_group_new ("ListViewActions");
3384   gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
3385   list_view->details->list_action_group = action_group;
3386 
3387   gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
3388   g_object_unref (action_group); /* owned by ui manager */
3389 
3390   list_view->details->list_merge_id =
3391     gtk_ui_manager_add_ui_from_resource (ui_manager, "/org/nemo/nemo-list-view-ui.xml", NULL);
3392 
3393   list_view->details->menus_ready = TRUE;
3394 }
3395 
3396 static void
nemo_list_view_unmerge_menus(NemoView * view)3397 nemo_list_view_unmerge_menus (NemoView *view)
3398 {
3399   NemoListView *list_view;
3400   GtkUIManager *ui_manager;
3401 
3402   list_view = NEMO_LIST_VIEW (view);
3403 
3404   NEMO_VIEW_CLASS (nemo_list_view_parent_class)->unmerge_menus (view);
3405 
3406   ui_manager = nemo_view_get_ui_manager (view);
3407   if (ui_manager != NULL) {
3408     nemo_ui_unmerge_ui (ui_manager,
3409           &list_view->details->list_merge_id,
3410           &list_view->details->list_action_group);
3411   }
3412 }
3413 
3414 static void
nemo_list_view_update_menus(NemoView * view)3415 nemo_list_view_update_menus (NemoView *view)
3416 {
3417 	NemoListView *list_view;
3418 
3419         list_view = NEMO_LIST_VIEW (view);
3420 
3421 	/* don't update if the menus aren't ready */
3422 	if (!list_view->details->menus_ready) {
3423 		return;
3424 	}
3425 
3426 	NEMO_VIEW_CLASS (nemo_list_view_parent_class)->update_menus (view);
3427 }
3428 
3429 /* Reset sort criteria and zoom level to match defaults */
3430 static void
nemo_list_view_reset_to_defaults(NemoView * view)3431 nemo_list_view_reset_to_defaults (NemoView *view)
3432 {
3433 	NemoFile *file;
3434     NemoDirectory *directory;
3435 
3436 	file = nemo_view_get_directory_as_file (view);
3437 
3438     g_signal_handlers_block_by_func (NEMO_LIST_VIEW (view)->details->tree_view,
3439                                      columns_reordered_callback,
3440                                      NEMO_LIST_VIEW (view));
3441 
3442     if (nemo_global_preferences_get_ignore_view_metadata ()) {
3443         NemoWindow *window = nemo_view_get_nemo_window (NEMO_VIEW (view));
3444         nemo_window_set_ignore_meta_sort_column (window, NULL);
3445         nemo_window_set_ignore_meta_sort_direction (window, SORT_NULL);
3446         nemo_window_set_ignore_meta_zoom_level (window, NEMO_ZOOM_LEVEL_NULL);
3447         nemo_window_set_ignore_meta_column_order (window, NULL);
3448         nemo_window_set_ignore_meta_visible_columns (window, NULL);
3449     } else {
3450         nemo_file_set_metadata (file, NEMO_METADATA_KEY_LIST_VIEW_SORT_COLUMN, NULL, NULL);
3451         nemo_file_set_metadata (file, NEMO_METADATA_KEY_LIST_VIEW_SORT_REVERSED, NULL, NULL);
3452         nemo_file_set_metadata (file, NEMO_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL, NULL, NULL);
3453         nemo_file_set_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NULL);
3454         nemo_file_set_metadata_list (file, NEMO_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NULL);
3455     }
3456 
3457     directory = nemo_view_get_model (view);
3458 
3459     if (NEMO_IS_SEARCH_DIRECTORY (directory))
3460         g_settings_reset (nemo_list_view_preferences, NEMO_PREFERENCES_LIST_VIEW_SEARCH_VISIBLE_COLUMNS);
3461 
3462     char **default_columns, **default_order;
3463 
3464     default_columns = get_default_visible_columns (NEMO_LIST_VIEW (view));
3465     default_order = get_default_column_order (NEMO_LIST_VIEW (view));
3466     apply_columns_settings (NEMO_LIST_VIEW (view), default_order, default_columns);
3467 
3468     g_signal_handlers_unblock_by_func (NEMO_LIST_VIEW (view)->details->tree_view,
3469                                        columns_reordered_callback,
3470                                        NEMO_LIST_VIEW (view));
3471 }
3472 
3473 static void
nemo_list_view_scale_font_size(NemoListView * view,NemoZoomLevel new_level)3474 nemo_list_view_scale_font_size (NemoListView *view,
3475 				    NemoZoomLevel new_level)
3476 {
3477 	GList *l;
3478 	static gboolean first_time = TRUE;
3479 	static double pango_scale[7];
3480 	int medium;
3481 	int i;
3482 
3483 	g_return_if_fail (new_level >= NEMO_ZOOM_LEVEL_SMALLEST &&
3484 			  new_level <= NEMO_ZOOM_LEVEL_LARGEST);
3485 
3486 	if (first_time) {
3487 		first_time = FALSE;
3488 		medium = NEMO_ZOOM_LEVEL_SMALLER;
3489 		pango_scale[medium] = PANGO_SCALE_MEDIUM;
3490 		for (i = medium; i > NEMO_ZOOM_LEVEL_SMALLEST; i--) {
3491 			pango_scale[i - 1] = (1 / 1.2) * pango_scale[i];
3492 		}
3493 		for (i = medium; i < NEMO_ZOOM_LEVEL_LARGEST; i++) {
3494 			pango_scale[i + 1] = 1.2 * pango_scale[i];
3495 		}
3496 	}
3497 
3498 	g_object_set (G_OBJECT (view->details->file_name_cell),
3499 		      "scale", pango_scale[new_level],
3500 		      NULL);
3501 	for (l = view->details->cells; l != NULL; l = l->next) {
3502 		g_object_set (G_OBJECT (l->data),
3503 			      "scale", pango_scale[new_level],
3504 			      NULL);
3505 	}
3506 }
3507 
3508 static void
nemo_list_view_set_zoom_level(NemoListView * view,NemoZoomLevel new_level,gboolean always_emit)3509 nemo_list_view_set_zoom_level (NemoListView *view,
3510 				   NemoZoomLevel new_level,
3511 				   gboolean always_emit)
3512 {
3513     NemoFile *file;
3514 	int icon_size;
3515 	int column;
3516 
3517 	g_return_if_fail (NEMO_IS_LIST_VIEW (view));
3518 	g_return_if_fail (new_level >= NEMO_ZOOM_LEVEL_SMALLEST &&
3519 			  new_level <= NEMO_ZOOM_LEVEL_LARGEST);
3520 
3521 	if (view->details->zoom_level == new_level) {
3522 		if (always_emit) {
3523 			g_signal_emit_by_name (NEMO_VIEW(view), "zoom_level_changed");
3524 		}
3525 		return;
3526 	}
3527 
3528 	view->details->zoom_level = new_level;
3529 	g_signal_emit_by_name (NEMO_VIEW(view), "zoom_level_changed");
3530 
3531     file = nemo_view_get_directory_as_file (NEMO_VIEW (view));
3532 
3533     if (nemo_global_preferences_get_ignore_view_metadata ()) {
3534         gchar *uri;
3535 
3536         uri = nemo_file_get_uri (file);
3537 
3538         if (!eel_uri_is_search (uri)) {
3539             nemo_window_set_ignore_meta_zoom_level (nemo_view_get_nemo_window (NEMO_VIEW (view)), new_level);
3540         }
3541 
3542         g_free (uri);
3543     } else {
3544         nemo_file_set_integer_metadata (file,
3545                                         NEMO_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL,
3546                                         get_default_zoom_level (),
3547                                         new_level);
3548     }
3549 
3550 	/* Select correctly scaled icons. */
3551 	column = nemo_list_model_get_column_id_from_zoom_level (new_level);
3552 	gtk_tree_view_column_set_attributes (view->details->file_name_column,
3553 					     GTK_CELL_RENDERER (view->details->pixbuf_cell),
3554 					     "surface", column,
3555 					     NULL);
3556 
3557 	/* Scale text. */
3558 	nemo_list_view_scale_font_size (view, new_level);
3559 
3560 	/* Make all rows the same size. */
3561 	icon_size = nemo_get_list_icon_size_for_zoom_level (new_level);
3562 	gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (view->details->pixbuf_cell),
3563 					  -1, icon_size);
3564 
3565 	nemo_view_update_menus (NEMO_VIEW (view));
3566 
3567 	/* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=641518 */
3568 	gtk_tree_view_columns_autosize (view->details->tree_view);
3569 }
3570 
3571 static void
nemo_list_view_bump_zoom_level(NemoView * view,int zoom_increment)3572 nemo_list_view_bump_zoom_level (NemoView *view, int zoom_increment)
3573 {
3574 	NemoListView *list_view;
3575 	gint new_level;
3576 
3577 	g_return_if_fail (NEMO_IS_LIST_VIEW (view));
3578 
3579 	list_view = NEMO_LIST_VIEW (view);
3580 	new_level = list_view->details->zoom_level + zoom_increment;
3581 
3582 	if (new_level >= NEMO_ZOOM_LEVEL_SMALLEST &&
3583 	    new_level <= NEMO_ZOOM_LEVEL_LARGEST) {
3584 		nemo_list_view_set_zoom_level (list_view, new_level, FALSE);
3585 	}
3586 }
3587 
3588 static NemoZoomLevel
nemo_list_view_get_zoom_level(NemoView * view)3589 nemo_list_view_get_zoom_level (NemoView *view)
3590 {
3591 	NemoListView *list_view;
3592 
3593 	g_return_val_if_fail (NEMO_IS_LIST_VIEW (view), NEMO_ZOOM_LEVEL_STANDARD);
3594 
3595 	list_view = NEMO_LIST_VIEW (view);
3596 
3597 	return list_view->details->zoom_level;
3598 }
3599 
3600 static void
nemo_list_view_zoom_to_level(NemoView * view,NemoZoomLevel zoom_level)3601 nemo_list_view_zoom_to_level (NemoView *view,
3602 				  NemoZoomLevel zoom_level)
3603 {
3604 	NemoListView *list_view;
3605 
3606 	g_return_if_fail (NEMO_IS_LIST_VIEW (view));
3607 
3608 	list_view = NEMO_LIST_VIEW (view);
3609 
3610 	nemo_list_view_set_zoom_level (list_view, zoom_level, FALSE);
3611 }
3612 
3613 static void
nemo_list_view_restore_default_zoom_level(NemoView * view)3614 nemo_list_view_restore_default_zoom_level (NemoView *view)
3615 {
3616 	NemoListView *list_view;
3617 
3618 	g_return_if_fail (NEMO_IS_LIST_VIEW (view));
3619 
3620 	list_view = NEMO_LIST_VIEW (view);
3621 
3622 	nemo_list_view_set_zoom_level (list_view, get_default_zoom_level (), FALSE);
3623 }
3624 
3625 static NemoZoomLevel
nemo_list_view_get_default_zoom_level(NemoView * view)3626 nemo_list_view_get_default_zoom_level (NemoView *view)
3627 {
3628     g_return_val_if_fail (NEMO_IS_LIST_VIEW (view), NEMO_ZOOM_LEVEL_NULL);
3629 
3630     return get_default_zoom_level();
3631 }
3632 
3633 static gboolean
nemo_list_view_can_zoom_in(NemoView * view)3634 nemo_list_view_can_zoom_in (NemoView *view)
3635 {
3636 	g_return_val_if_fail (NEMO_IS_LIST_VIEW (view), FALSE);
3637 
3638 	return NEMO_LIST_VIEW (view)->details->zoom_level	< NEMO_ZOOM_LEVEL_LARGEST;
3639 }
3640 
3641 static gboolean
nemo_list_view_can_zoom_out(NemoView * view)3642 nemo_list_view_can_zoom_out (NemoView *view)
3643 {
3644 	g_return_val_if_fail (NEMO_IS_LIST_VIEW (view), FALSE);
3645 
3646 	return NEMO_LIST_VIEW (view)->details->zoom_level > NEMO_ZOOM_LEVEL_SMALLEST;
3647 }
3648 
3649 static void
nemo_list_view_start_renaming_file(NemoView * view,NemoFile * file,gboolean select_all)3650 nemo_list_view_start_renaming_file (NemoView *view,
3651 					NemoFile *file,
3652 					gboolean select_all)
3653 {
3654 	NemoListView *list_view;
3655 	GtkTreeIter iter;
3656 	GtkTreePath *path;
3657 
3658 	list_view = NEMO_LIST_VIEW (view);
3659 
3660 	/* Select all if we are in renaming mode already */
3661 	if (list_view->details->file_name_column && list_view->details->editable_widget) {
3662 		gtk_editable_select_region (GTK_EDITABLE (list_view->details->editable_widget),
3663 					    0,
3664 					    -1);
3665 		return;
3666 	}
3667 
3668 	if (!nemo_list_model_get_first_iter_for_file (list_view->details->model, file, &iter)) {
3669 		return;
3670 	}
3671 
3672 	/* call parent class to make sure the right icon is selected */
3673 	NEMO_VIEW_CLASS (nemo_list_view_parent_class)->start_renaming_file (view, file, select_all);
3674 
3675 	/* Freeze updates to the view to prevent losing rename focus when the tree view updates */
3676 	nemo_view_freeze_updates (NEMO_VIEW (view));
3677 
3678 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter);
3679 
3680 	/* Make filename-cells editable. */
3681 	g_object_set (G_OBJECT (list_view->details->file_name_cell),
3682 		      "editable", TRUE,
3683 		      NULL);
3684 
3685 	gtk_tree_view_scroll_to_cell (list_view->details->tree_view,
3686 				      NULL,
3687 				      list_view->details->file_name_column,
3688 				      TRUE, 0.0, 0.0);
3689 	gtk_tree_view_set_cursor_on_cell (list_view->details->tree_view,
3690 					  path,
3691 					  list_view->details->file_name_column,
3692 					  GTK_CELL_RENDERER (list_view->details->file_name_cell),
3693 					  TRUE);
3694 
3695 	/* set cursor also triggers editing-started, where we save the editable widget */
3696 	if (list_view->details->editable_widget != NULL) {
3697         int start_offset = 0;
3698         int end_offset = -1;
3699 
3700         if (!select_all) {
3701             eel_filename_get_rename_region (list_view->details->original_name,
3702                            &start_offset, &end_offset);
3703         }
3704 
3705 		gtk_editable_select_region (GTK_EDITABLE (list_view->details->editable_widget),
3706 					    start_offset, end_offset);
3707 	}
3708 
3709 	gtk_tree_path_free (path);
3710 }
3711 
3712 static void
nemo_list_view_click_to_rename_mode_changed(NemoView * directory_view)3713 nemo_list_view_click_to_rename_mode_changed (NemoView *directory_view)
3714 {
3715     NemoListView *view;
3716 
3717     g_assert (NEMO_IS_LIST_VIEW (directory_view));
3718 
3719     view = NEMO_LIST_VIEW (directory_view);
3720 
3721     view->details->click_to_rename = g_settings_get_boolean (nemo_preferences,
3722                                                                   NEMO_PREFERENCES_CLICK_TO_RENAME);
3723 }
3724 
3725 static void
nemo_list_view_click_policy_changed(NemoView * directory_view)3726 nemo_list_view_click_policy_changed (NemoView *directory_view)
3727 {
3728 	GdkWindow *win;
3729 	GdkDisplay *display;
3730 	NemoListView *view;
3731 	GtkTreeIter iter;
3732 	GtkTreeView *tree;
3733 
3734 	view = NEMO_LIST_VIEW (directory_view);
3735 
3736     click_policy = g_settings_get_enum (nemo_preferences,
3737                                         NEMO_PREFERENCES_CLICK_POLICY);
3738 
3739 	/* ensure that we unset the hand cursor and refresh underlined rows */
3740 	if (click_policy == NEMO_CLICK_POLICY_DOUBLE) {
3741 		if (view->details->hover_path != NULL) {
3742 			if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
3743 						     &iter, view->details->hover_path)) {
3744 				gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model),
3745 							    view->details->hover_path, &iter);
3746 			}
3747 
3748 			gtk_tree_path_free (view->details->hover_path);
3749 			view->details->hover_path = NULL;
3750 		}
3751 
3752 		tree = view->details->tree_view;
3753 		if (gtk_widget_get_realized (GTK_WIDGET (tree))) {
3754 			win = gtk_widget_get_window (GTK_WIDGET (tree));
3755 			gdk_window_set_cursor (win, NULL);
3756 
3757 			display = gtk_widget_get_display (GTK_WIDGET (view));
3758 			if (display != NULL) {
3759 				gdk_display_flush (display);
3760 			}
3761 		}
3762 
3763 		g_clear_object (&hand_cursor);
3764 	} else if (click_policy == NEMO_CLICK_POLICY_SINGLE) {
3765 		if (hand_cursor == NULL) {
3766 			hand_cursor = gdk_cursor_new(GDK_HAND2);
3767 		}
3768 	}
3769 }
3770 
3771 static void
default_sort_order_changed_callback(gpointer callback_data)3772 default_sort_order_changed_callback (gpointer callback_data)
3773 {
3774 	NemoListView *list_view;
3775 
3776 	list_view = NEMO_LIST_VIEW (callback_data);
3777 
3778 	set_sort_order_from_metadata_and_preferences (list_view);
3779 }
3780 
3781 static void
default_zoom_level_changed_callback(gpointer callback_data)3782 default_zoom_level_changed_callback (gpointer callback_data)
3783 {
3784 	NemoListView *list_view;
3785 
3786 	list_view = NEMO_LIST_VIEW (callback_data);
3787 
3788 	set_zoom_level_from_metadata_and_preferences (list_view);
3789 }
3790 
3791 static void
default_visible_columns_changed_callback(gpointer callback_data)3792 default_visible_columns_changed_callback (gpointer callback_data)
3793 {
3794 	NemoListView *list_view;
3795 
3796 	list_view = NEMO_LIST_VIEW (callback_data);
3797 
3798 	set_columns_settings_from_metadata_and_preferences (list_view);
3799 }
3800 
3801 static void
default_column_order_changed_callback(gpointer callback_data)3802 default_column_order_changed_callback (gpointer callback_data)
3803 {
3804 	NemoListView *list_view;
3805 
3806 	list_view = NEMO_LIST_VIEW (callback_data);
3807 
3808 	set_columns_settings_from_metadata_and_preferences (list_view);
3809 }
3810 
3811 static void
nemo_list_view_sort_directories_first_changed(NemoView * view)3812 nemo_list_view_sort_directories_first_changed (NemoView *view)
3813 {
3814 	NemoListView *list_view;
3815 
3816 	list_view = NEMO_LIST_VIEW (view);
3817 
3818 	nemo_list_model_set_should_sort_directories_first (list_view->details->model,
3819 							 nemo_view_should_sort_directories_first (view));
3820 }
3821 
3822 static int
nemo_list_view_compare_files(NemoView * view,NemoFile * file1,NemoFile * file2)3823 nemo_list_view_compare_files (NemoView *view, NemoFile *file1, NemoFile *file2)
3824 {
3825 	NemoListView *list_view;
3826 
3827 	list_view = NEMO_LIST_VIEW (view);
3828 	return nemo_list_model_compare_func (list_view->details->model, file1, file2);
3829 }
3830 
3831 static gboolean
nemo_list_view_using_manual_layout(NemoView * view)3832 nemo_list_view_using_manual_layout (NemoView *view)
3833 {
3834 	g_return_val_if_fail (NEMO_IS_LIST_VIEW (view), FALSE);
3835 
3836 	return FALSE;
3837 }
3838 
3839 static void
nemo_list_view_dispose(GObject * object)3840 nemo_list_view_dispose (GObject *object)
3841 {
3842 	NemoListView *list_view;
3843 
3844 	list_view = NEMO_LIST_VIEW (object);
3845 
3846 	if (list_view->details->model) {
3847 		stop_cell_editing (list_view);
3848 		g_object_unref (list_view->details->model);
3849 		list_view->details->model = NULL;
3850 	}
3851 
3852 	if (list_view->details->drag_dest) {
3853 		g_object_unref (list_view->details->drag_dest);
3854 		list_view->details->drag_dest = NULL;
3855 	}
3856 
3857 	if (list_view->details->renaming_file_activate_timeout != 0) {
3858 		g_source_remove (list_view->details->renaming_file_activate_timeout);
3859 		list_view->details->renaming_file_activate_timeout = 0;
3860 	}
3861 
3862     if (list_view->details->update_visible_icons_id > 0) {
3863         g_source_remove (list_view->details->update_visible_icons_id);
3864         list_view->details->update_visible_icons_id = 0;
3865     }
3866 
3867 	if (list_view->details->clipboard_handler_id != 0) {
3868 		g_signal_handler_disconnect (nemo_clipboard_monitor_get (),
3869 		                             list_view->details->clipboard_handler_id);
3870 		list_view->details->clipboard_handler_id = 0;
3871 	}
3872 
3873     G_OBJECT_CLASS (nemo_list_view_parent_class)->dispose (object);
3874 }
3875 
3876 static void
nemo_list_view_finalize(GObject * object)3877 nemo_list_view_finalize (GObject *object)
3878 {
3879 	NemoListView *list_view;
3880 
3881 	list_view = NEMO_LIST_VIEW (object);
3882 
3883 	g_free (list_view->details->original_name);
3884 	list_view->details->original_name = NULL;
3885 
3886 	if (list_view->details->double_click_path[0]) {
3887 		gtk_tree_path_free (list_view->details->double_click_path[0]);
3888 	}
3889 	if (list_view->details->double_click_path[1]) {
3890 		gtk_tree_path_free (list_view->details->double_click_path[1]);
3891 	}
3892 	if (list_view->details->new_selection_path) {
3893 		gtk_tree_path_free (list_view->details->new_selection_path);
3894 	}
3895 
3896 	g_list_free (list_view->details->cells);
3897 	g_hash_table_destroy (list_view->details->columns);
3898 
3899 	if (list_view->details->hover_path != NULL) {
3900 		gtk_tree_path_free (list_view->details->hover_path);
3901 	}
3902 
3903 	if (list_view->details->column_editor != NULL) {
3904 		gtk_widget_destroy (list_view->details->column_editor);
3905 	}
3906 
3907 	g_free (list_view->details);
3908 
3909 	g_signal_handlers_disconnect_by_func (nemo_preferences,
3910 					      default_sort_order_changed_callback,
3911 					      list_view);
3912 	g_signal_handlers_disconnect_by_func (nemo_list_view_preferences,
3913 					      default_zoom_level_changed_callback,
3914 					      list_view);
3915 	g_signal_handlers_disconnect_by_func (nemo_list_view_preferences,
3916 					      default_visible_columns_changed_callback,
3917 					      list_view);
3918 	g_signal_handlers_disconnect_by_func (nemo_list_view_preferences,
3919 					      default_column_order_changed_callback,
3920 					      list_view);
3921 
3922     g_signal_handlers_disconnect_by_func (nemo_preferences,
3923                                           tooltip_prefs_changed_callback,
3924                                           list_view);
3925 
3926 	G_OBJECT_CLASS (nemo_list_view_parent_class)->finalize (object);
3927 }
3928 
3929 static char *
nemo_list_view_get_first_visible_file(NemoView * view)3930 nemo_list_view_get_first_visible_file (NemoView *view)
3931 {
3932 	NemoFile *file;
3933 	GtkTreePath *path;
3934 	GtkTreeIter iter;
3935 	NemoListView *list_view;
3936 
3937 	list_view = NEMO_LIST_VIEW (view);
3938 
3939 	if (gtk_tree_view_get_path_at_pos (list_view->details->tree_view,
3940 					   0, 0,
3941 					   &path, NULL, NULL, NULL)) {
3942 		gtk_tree_model_get_iter (GTK_TREE_MODEL (list_view->details->model),
3943 					 &iter, path);
3944 
3945 		gtk_tree_path_free (path);
3946 
3947 		gtk_tree_model_get (GTK_TREE_MODEL (list_view->details->model),
3948 				    &iter,
3949 				    NEMO_LIST_MODEL_FILE_COLUMN, &file,
3950 				    -1);
3951 		if (file) {
3952 			char *uri;
3953 
3954 			uri = nemo_file_get_uri (file);
3955 
3956 			nemo_file_unref (file);
3957 
3958 			return uri;
3959 		}
3960 	}
3961 
3962 	return NULL;
3963 }
3964 
3965 static void
nemo_list_view_scroll_to_file(NemoListView * view,NemoFile * file)3966 nemo_list_view_scroll_to_file (NemoListView *view,
3967 				   NemoFile *file)
3968 {
3969 	GtkTreePath *path;
3970 	GtkTreeIter iter;
3971 
3972 	if (!nemo_list_model_get_first_iter_for_file (view->details->model, file, &iter)) {
3973 		return;
3974 	}
3975 
3976 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->model), &iter);
3977 
3978 	gtk_tree_view_scroll_to_cell (view->details->tree_view,
3979 				      path, NULL,
3980 				      TRUE, 0.0, 0.0);
3981 
3982 	gtk_tree_path_free (path);
3983 }
3984 
3985 static void
list_view_scroll_to_file(NemoView * view,const char * uri)3986 list_view_scroll_to_file (NemoView *view,
3987 			  const char *uri)
3988 {
3989 	NemoFile *file;
3990 
3991 	if (uri != NULL) {
3992 		/* Only if existing, since we don't want to add the file to
3993 		   the directory if it has been removed since then */
3994 		file = nemo_file_get_existing_by_uri (uri);
3995 		if (file != NULL) {
3996 			nemo_list_view_scroll_to_file (NEMO_LIST_VIEW (view), file);
3997 			nemo_file_unref (file);
3998 		}
3999 	}
4000 }
4001 
4002 static void
list_view_notify_clipboard_info(NemoClipboardMonitor * monitor,NemoClipboardInfo * info,NemoListView * view)4003 list_view_notify_clipboard_info (NemoClipboardMonitor *monitor,
4004                                  NemoClipboardInfo *info,
4005                                  NemoListView *view)
4006 {
4007 	/* this could be called as a result of _end_loading() being
4008 	 * called after _dispose(), where the model is cleared.
4009 	 */
4010 	if (view->details->model == NULL) {
4011 		return;
4012 	}
4013 
4014 	if (info != NULL && info->cut) {
4015 		nemo_list_model_set_highlight_for_files (view->details->model, info->files);
4016 	} else {
4017 		nemo_list_model_set_highlight_for_files (view->details->model, NULL);
4018 	}
4019 }
4020 
4021 static void
nemo_list_view_end_loading(NemoView * view,gboolean all_files_seen)4022 nemo_list_view_end_loading (NemoView *view,
4023 				gboolean all_files_seen)
4024 {
4025 	NemoClipboardMonitor *monitor;
4026 	NemoClipboardInfo *info;
4027 
4028     set_ok_to_load_deferred_attrs (NEMO_LIST_VIEW (view), TRUE);
4029 
4030 	monitor = nemo_clipboard_monitor_get ();
4031 	info = nemo_clipboard_monitor_get_clipboard_info (monitor);
4032 
4033 	list_view_notify_clipboard_info (monitor, info, NEMO_LIST_VIEW (view));
4034 }
4035 
4036 static const char *
nemo_list_view_get_id(NemoView * view)4037 nemo_list_view_get_id (NemoView *view)
4038 {
4039 	return NEMO_LIST_VIEW_ID;
4040 }
4041 
4042 static void
nemo_list_view_class_init(NemoListViewClass * class)4043 nemo_list_view_class_init (NemoListViewClass *class)
4044 {
4045 	NemoViewClass *nemo_view_class;
4046 
4047 	nemo_view_class = NEMO_VIEW_CLASS (class);
4048 
4049 	G_OBJECT_CLASS (class)->dispose = nemo_list_view_dispose;
4050 	G_OBJECT_CLASS (class)->finalize = nemo_list_view_finalize;
4051 
4052 	nemo_view_class->add_file = nemo_list_view_add_file;
4053 	nemo_view_class->begin_loading = nemo_list_view_begin_loading;
4054 	nemo_view_class->end_loading = nemo_list_view_end_loading;
4055 	nemo_view_class->bump_zoom_level = nemo_list_view_bump_zoom_level;
4056 	nemo_view_class->can_zoom_in = nemo_list_view_can_zoom_in;
4057 	nemo_view_class->can_zoom_out = nemo_list_view_can_zoom_out;
4058         nemo_view_class->click_policy_changed = nemo_list_view_click_policy_changed;
4059 	nemo_view_class->clear = nemo_list_view_clear;
4060 	nemo_view_class->file_changed = nemo_list_view_file_changed;
4061 	nemo_view_class->get_backing_uri = nemo_list_view_get_backing_uri;
4062 	nemo_view_class->get_selection = nemo_list_view_get_selection;
4063     nemo_view_class->peek_selection = nemo_list_view_peek_selection;
4064     nemo_view_class->get_selection_count = nemo_list_view_get_selection_count;
4065 	nemo_view_class->get_selection_for_file_transfer = nemo_list_view_get_selection_for_file_transfer;
4066 	nemo_view_class->get_item_count = nemo_list_view_get_item_count;
4067 	nemo_view_class->is_empty = nemo_list_view_is_empty;
4068 	nemo_view_class->remove_file = nemo_list_view_remove_file;
4069     nemo_view_class->merge_menus = nemo_list_view_merge_menus;
4070     nemo_view_class->unmerge_menus = nemo_list_view_unmerge_menus;
4071 	nemo_view_class->update_menus = nemo_list_view_update_menus;
4072 	nemo_view_class->reset_to_defaults = nemo_list_view_reset_to_defaults;
4073 	nemo_view_class->restore_default_zoom_level = nemo_list_view_restore_default_zoom_level;
4074     nemo_view_class->get_default_zoom_level = nemo_list_view_get_default_zoom_level;
4075 	nemo_view_class->reveal_selection = nemo_list_view_reveal_selection;
4076 	nemo_view_class->select_all = nemo_list_view_select_all;
4077 	nemo_view_class->set_selection = nemo_list_view_set_selection;
4078 	nemo_view_class->invert_selection = nemo_list_view_invert_selection;
4079 	nemo_view_class->compare_files = nemo_list_view_compare_files;
4080 	nemo_view_class->sort_directories_first_changed = nemo_list_view_sort_directories_first_changed;
4081 	nemo_view_class->start_renaming_file = nemo_list_view_start_renaming_file;
4082 	nemo_view_class->get_zoom_level = nemo_list_view_get_zoom_level;
4083 	nemo_view_class->zoom_to_level = nemo_list_view_zoom_to_level;
4084 	nemo_view_class->end_file_changes = nemo_list_view_end_file_changes;
4085 	nemo_view_class->using_manual_layout = nemo_list_view_using_manual_layout;
4086 	nemo_view_class->get_view_id = nemo_list_view_get_id;
4087 	nemo_view_class->get_first_visible_file = nemo_list_view_get_first_visible_file;
4088 	nemo_view_class->scroll_to_file = list_view_scroll_to_file;
4089     nemo_view_class->click_to_rename_mode_changed = nemo_list_view_click_to_rename_mode_changed;
4090 }
4091 
4092 static void
nemo_list_view_init(NemoListView * list_view)4093 nemo_list_view_init (NemoListView *list_view)
4094 {
4095 	list_view->details = g_new0 (NemoListViewDetails, 1);
4096 
4097 	create_and_set_up_tree_view (list_view);
4098 
4099 	g_signal_connect_swapped (nemo_preferences,
4100 				  "changed::" NEMO_PREFERENCES_DEFAULT_SORT_ORDER,
4101 				  G_CALLBACK (default_sort_order_changed_callback),
4102 				  list_view);
4103 	g_signal_connect_swapped (nemo_preferences,
4104 				  "changed::" NEMO_PREFERENCES_DEFAULT_SORT_IN_REVERSE_ORDER,
4105 				  G_CALLBACK (default_sort_order_changed_callback),
4106 				  list_view);
4107 	g_signal_connect_swapped (nemo_list_view_preferences,
4108 				  "changed::" NEMO_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
4109 				  G_CALLBACK (default_zoom_level_changed_callback),
4110 				  list_view);
4111 	g_signal_connect_swapped (nemo_list_view_preferences,
4112 				  "changed::" NEMO_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS,
4113 				  G_CALLBACK (default_visible_columns_changed_callback),
4114 				  list_view);
4115 	g_signal_connect_swapped (nemo_list_view_preferences,
4116 				  "changed::" NEMO_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER,
4117 				  G_CALLBACK (default_column_order_changed_callback),
4118 				  list_view);
4119 
4120     g_signal_connect_swapped (nemo_preferences,
4121                               "changed::" NEMO_PREFERENCES_TOOLTIPS_LIST_VIEW,
4122                               G_CALLBACK (tooltip_prefs_changed_callback),
4123                               list_view);
4124 
4125     g_signal_connect_swapped (nemo_preferences,
4126                               "changed::" NEMO_PREFERENCES_TOOLTIP_FILE_TYPE,
4127                               G_CALLBACK (tooltip_prefs_changed_callback),
4128                               list_view);
4129 
4130     g_signal_connect_swapped (nemo_preferences,
4131                               "changed::" NEMO_PREFERENCES_TOOLTIP_MOD_DATE,
4132                               G_CALLBACK (tooltip_prefs_changed_callback),
4133                               list_view);
4134 
4135     g_signal_connect_swapped (nemo_preferences,
4136                               "changed::" NEMO_PREFERENCES_TOOLTIP_ACCESS_DATE,
4137                               G_CALLBACK (tooltip_prefs_changed_callback),
4138                               list_view);
4139 
4140     g_signal_connect_swapped (nemo_preferences,
4141                               "changed::" NEMO_PREFERENCES_TOOLTIP_FULL_PATH,
4142                               G_CALLBACK (tooltip_prefs_changed_callback),
4143                               list_view);
4144 
4145     tooltip_prefs_changed_callback (list_view);
4146 
4147 	nemo_list_view_click_policy_changed (NEMO_VIEW (list_view));
4148     nemo_list_view_click_to_rename_mode_changed (NEMO_VIEW (list_view));
4149 
4150 	nemo_list_view_sort_directories_first_changed (NEMO_VIEW (list_view));
4151 
4152     list_view->details->current_selection_count = -1;
4153 
4154 	/* ensure that the zoom level is always set in begin_loading */
4155 	list_view->details->zoom_level = NEMO_ZOOM_LEVEL_SMALLEST - 1;
4156 
4157 	list_view->details->hover_path = NULL;
4158 	list_view->details->clipboard_handler_id =
4159 		g_signal_connect (nemo_clipboard_monitor_get (),
4160 		                  "clipboard_info",
4161 		                  G_CALLBACK (list_view_notify_clipboard_info), list_view);
4162 }
4163 
4164 static NemoView *
nemo_list_view_create(NemoWindowSlot * slot)4165 nemo_list_view_create (NemoWindowSlot *slot)
4166 {
4167 	NemoListView *view;
4168 
4169 	view = g_object_new (NEMO_TYPE_LIST_VIEW,
4170 			     "window-slot", slot,
4171 			     NULL);
4172 	return NEMO_VIEW (view);
4173 }
4174 
4175 static gboolean
nemo_list_view_supports_uri(const char * uri,GFileType file_type,const char * mime_type)4176 nemo_list_view_supports_uri (const char *uri,
4177 				 GFileType file_type,
4178 				 const char *mime_type)
4179 {
4180 	if (file_type == G_FILE_TYPE_DIRECTORY) {
4181 		return TRUE;
4182 	}
4183 	if (strcmp (mime_type, NEMO_SAVED_SEARCH_MIMETYPE) == 0){
4184 		return TRUE;
4185 	}
4186 	if (g_str_has_prefix (uri, "trash:")) {
4187 		return TRUE;
4188 	}
4189     if (g_str_has_prefix (uri, "recent:")) {
4190         return TRUE;
4191     }
4192     if (g_str_has_prefix (uri, "favorites:")) {
4193         return TRUE;
4194     }
4195 	if (g_str_has_prefix (uri, EEL_SEARCH_URI)) {
4196 		return TRUE;
4197 	}
4198 
4199 	return FALSE;
4200 }
4201 
4202 static NemoViewInfo nemo_list_view = {
4203 	(char *)NEMO_LIST_VIEW_ID,
4204 	/* translators: this is used in the view selection dropdown
4205 	 * of navigation windows and in the preferences dialog */
4206 	(char *)N_("List View"),
4207 	/* translators: this is used in the view menu */
4208 	(char *)N_("_List"),
4209 	(char *)N_("The list view encountered an error."),
4210 	(char *)N_("The list view encountered an error while starting up."),
4211 	(char *)N_("Display this location with the list view."),
4212 	nemo_list_view_create,
4213 	nemo_list_view_supports_uri
4214 };
4215 
4216 void
nemo_list_view_register(void)4217 nemo_list_view_register (void)
4218 {
4219 	nemo_list_view.view_combo_label = _(nemo_list_view.view_combo_label);
4220 	nemo_list_view.view_menu_label_with_mnemonic = _(nemo_list_view.view_menu_label_with_mnemonic);
4221 	nemo_list_view.error_label = _(nemo_list_view.error_label);
4222 	nemo_list_view.startup_error_label = _(nemo_list_view.startup_error_label);
4223 	nemo_list_view.display_location_label = _(nemo_list_view.display_location_label);
4224 
4225 	nemo_view_factory_register (&nemo_list_view);
4226 }
4227 
4228 GtkTreeView*
nemo_list_view_get_tree_view(NemoListView * list_view)4229 nemo_list_view_get_tree_view (NemoListView *list_view)
4230 {
4231 	return list_view->details->tree_view;
4232 }
4233