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 ¬_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