2008-04-24 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / src / file-manager / fm-list-view.c
bloba7bbaa91c5862da8a656caa71e648d2b456e9db3
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /* fm-list-view.c - implementation of list view of directory.
5 Copyright (C) 2000 Eazel, Inc.
6 Copyright (C) 2001, 2002 Anders Carlsson <andersca@gnu.org>
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.
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.
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., 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA.
23 Authors: John Sullivan <sullivan@eazel.com>
24 Anders Carlsson <andersca@gnu.org>
25 David Emory Watson <dwatson@cs.ucr.edu>
28 #include <config.h>
29 #include "fm-list-view.h"
31 #include <string.h>
32 #include "fm-error-reporting.h"
33 #include "fm-list-model.h"
34 #include <string.h>
35 #include <eel/eel-vfs-extensions.h>
36 #include <eel/eel-glib-extensions.h>
37 #include <eel/eel-gtk-macros.h>
38 #include <gdk/gdkcursor.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <gtk/gtkdialog.h>
41 #include <gtk/gtkbindings.h>
42 #include <gtk/gtkcellrendererpixbuf.h>
43 #include <gtk/gtkcellrenderertext.h>
44 #include <gtk/gtkentry.h>
45 #include <gtk/gtklabel.h>
46 #include <gtk/gtkvbox.h>
47 #include <gtk/gtkstock.h>
48 #include <gtk/gtktreeselection.h>
49 #include <gtk/gtktreeview.h>
50 #include <libegg/eggtreemultidnd.h>
51 #include <glib/gi18n.h>
52 #include <libgnome/gnome-macros.h>
53 #include <libnautilus-extension/nautilus-column-provider.h>
54 #include <libnautilus-private/nautilus-column-chooser.h>
55 #include <libnautilus-private/nautilus-column-utilities.h>
56 #include <libnautilus-private/nautilus-debug-log.h>
57 #include <libnautilus-private/nautilus-directory-background.h>
58 #include <libnautilus-private/nautilus-dnd.h>
59 #include <libnautilus-private/nautilus-file-dnd.h>
60 #include <libnautilus-private/nautilus-file-utilities.h>
61 #include <libnautilus-private/nautilus-ui-utilities.h>
62 #include <libnautilus-private/nautilus-global-preferences.h>
63 #include <libnautilus-private/nautilus-icon-dnd.h>
64 #include <libnautilus-private/nautilus-metadata.h>
65 #include <libnautilus-private/nautilus-module.h>
66 #include <libnautilus-private/nautilus-tree-view-drag-dest.h>
67 #include <libnautilus-private/nautilus-view-factory.h>
68 #include <libnautilus-private/nautilus-clipboard.h>
69 #include <libnautilus-private/nautilus-cell-renderer-pixbuf-emblem.h>
70 #include <libnautilus-private/nautilus-cell-renderer-text-ellipsized.h>
72 struct FMListViewDetails {
73 GtkTreeView *tree_view;
74 FMListModel *model;
75 GtkActionGroup *list_action_group;
76 guint list_merge_id;
78 GtkTreeViewColumn *file_name_column;
79 int file_name_column_num;
81 GtkCellRendererPixbuf *pixbuf_cell;
82 GtkCellRendererText *file_name_cell;
83 GList *cells;
85 NautilusZoomLevel zoom_level;
87 NautilusTreeViewDragDest *drag_dest;
89 GtkTreePath *double_click_path[2]; /* Both clicks in a double click need to be on the same row */
91 GtkTreePath *new_selection_path; /* Path of the new selection after removing a file */
93 GtkTreePath *hover_path;
95 guint drag_button;
96 int drag_x;
97 int drag_y;
99 gboolean drag_started;
101 gboolean ignore_button_release;
103 gboolean row_selected_on_button_down;
105 gboolean menus_ready;
107 GHashTable *columns;
108 GtkWidget *column_editor;
110 char *original_name;
112 NautilusFile *renaming_file;
113 gboolean rename_done;
114 guint renaming_file_activate_timeout;
117 struct SelectionForeachData {
118 GList *list;
119 GtkTreeSelection *selection;
123 * The row height should be large enough to not clip emblems.
124 * Computing this would be costly, so we just choose a number
125 * that works well with the set of emblems we've designed.
127 #define LIST_VIEW_MINIMUM_ROW_HEIGHT 28
129 /* We wait two seconds after row is collapsed to unload the subdirectory */
130 #define COLLAPSE_TO_UNLOAD_DELAY 2000
132 /* Wait for the rename to end when activating a file being renamed */
133 #define WAIT_FOR_RENAME_ON_ACTIVATE 200
135 static int click_policy_auto_value;
136 static char * default_sort_order_auto_value;
137 static gboolean default_sort_reversed_auto_value;
138 static NautilusZoomLevel default_zoom_level_auto_value;
139 static char ** default_visible_columns_auto_value;
140 static char ** default_column_order_auto_value;
141 static GdkCursor * hand_cursor = NULL;
143 static GtkTargetList * source_target_list = NULL;
145 static GList *fm_list_view_get_selection (FMDirectoryView *view);
146 static GList *fm_list_view_get_selection_for_file_transfer (FMDirectoryView *view);
147 static void fm_list_view_set_zoom_level (FMListView *view,
148 NautilusZoomLevel new_level,
149 gboolean always_set_level);
150 static void fm_list_view_scale_font_size (FMListView *view,
151 NautilusZoomLevel new_level);
152 static void fm_list_view_scroll_to_file (FMListView *view,
153 NautilusFile *file);
154 static void fm_list_view_iface_init (NautilusViewIface *iface);
155 static void fm_list_view_rename_callback (NautilusFile *file,
156 GFile *result_location,
157 GError *error,
158 gpointer callback_data);
161 G_DEFINE_TYPE_WITH_CODE (FMListView, fm_list_view, FM_TYPE_DIRECTORY_VIEW,
162 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_VIEW,
163 fm_list_view_iface_init));
165 /* for EEL_CALL_PARENT */
166 #define parent_class fm_list_view_parent_class
168 static void
169 list_selection_changed_callback (GtkTreeSelection *selection, gpointer user_data)
171 FMDirectoryView *view;
173 view = FM_DIRECTORY_VIEW (user_data);
175 fm_directory_view_notify_selection_changed (view);
178 /* Move these to eel? */
180 static void
181 tree_selection_foreach_set_boolean (GtkTreeModel *model,
182 GtkTreePath *path,
183 GtkTreeIter *iter,
184 gpointer callback_data)
186 * (gboolean *) callback_data = TRUE;
189 static gboolean
190 tree_selection_not_empty (GtkTreeSelection *selection)
192 gboolean not_empty;
194 not_empty = FALSE;
195 gtk_tree_selection_selected_foreach (selection,
196 tree_selection_foreach_set_boolean,
197 &not_empty);
198 return not_empty;
201 static gboolean
202 tree_view_has_selection (GtkTreeView *view)
204 return tree_selection_not_empty (gtk_tree_view_get_selection (view));
207 static void
208 activate_selected_items (FMListView *view)
210 GList *file_list;
212 file_list = fm_list_view_get_selection (FM_DIRECTORY_VIEW (view));
215 if (view->details->renaming_file) {
216 /* We're currently renaming a file, wait until the rename is
217 finished, or the activation uri will be wrong */
218 if (view->details->renaming_file_activate_timeout == 0) {
219 view->details->renaming_file_activate_timeout =
220 g_timeout_add (WAIT_FOR_RENAME_ON_ACTIVATE, (GSourceFunc) activate_selected_items, view);
222 return;
225 if (view->details->renaming_file_activate_timeout != 0) {
226 g_source_remove (view->details->renaming_file_activate_timeout);
227 view->details->renaming_file_activate_timeout = 0;
230 fm_directory_view_activate_files (FM_DIRECTORY_VIEW (view),
231 file_list,
232 NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE,
234 nautilus_file_list_free (file_list);
238 static void
239 activate_selected_items_alternate (FMListView *view,
240 NautilusFile *file)
242 GList *file_list;
245 if (file != NULL) {
246 nautilus_file_ref (file);
247 file_list = g_list_prepend (NULL, file);
248 } else {
249 file_list = fm_list_view_get_selection (FM_DIRECTORY_VIEW (view));
251 fm_directory_view_activate_files (FM_DIRECTORY_VIEW (view),
252 file_list,
253 NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE,
254 NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND |
255 NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW);
256 nautilus_file_list_free (file_list);
260 static gboolean
261 button_event_modifies_selection (GdkEventButton *event)
263 return (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) != 0;
266 static void
267 fm_list_view_did_not_drag (FMListView *view,
268 GdkEventButton *event)
270 GtkTreeView *tree_view;
271 GtkTreeSelection *selection;
272 GtkTreePath *path;
274 tree_view = view->details->tree_view;
275 selection = gtk_tree_view_get_selection (tree_view);
277 if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
278 &path, NULL, NULL, NULL)) {
279 if ((event->button == 1 || event->button == 2)
280 && ((event->state & GDK_CONTROL_MASK) != 0 ||
281 (event->state & GDK_SHIFT_MASK) == 0)
282 && view->details->row_selected_on_button_down) {
283 if (!button_event_modifies_selection (event)) {
284 gtk_tree_selection_unselect_all (selection);
285 gtk_tree_selection_select_path (selection, path);
286 } else {
287 gtk_tree_selection_unselect_path (selection, path);
291 if ((click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE)
292 && !button_event_modifies_selection(event)) {
293 if (event->button == 1) {
294 activate_selected_items (view);
295 } else if (event->button == 2) {
296 activate_selected_items_alternate (view, NULL);
299 gtk_tree_path_free (path);
304 static void
305 drag_data_get_callback (GtkWidget *widget,
306 GdkDragContext *context,
307 GtkSelectionData *selection_data,
308 guint info,
309 guint time)
311 GtkTreeView *tree_view;
312 GtkTreeModel *model;
313 GList *ref_list;
315 tree_view = GTK_TREE_VIEW (widget);
317 model = gtk_tree_view_get_model (tree_view);
319 if (model == NULL) {
320 return;
323 ref_list = g_object_get_data (G_OBJECT (context), "drag-info");
325 if (ref_list == NULL) {
326 return;
329 if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
331 egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
332 ref_list,
333 selection_data);
337 static void
338 filtered_selection_foreach (GtkTreeModel *model,
339 GtkTreePath *path,
340 GtkTreeIter *iter,
341 gpointer data)
343 struct SelectionForeachData *selection_data;
344 GtkTreeIter parent;
345 GtkTreeIter child;
347 selection_data = data;
349 /* If the parent folder is also selected, don't include this file in the
350 * file operation, since that would copy it to the toplevel target instead
351 * of keeping it as a child of the copied folder
353 child = *iter;
354 while (gtk_tree_model_iter_parent (model, &parent, &child)) {
355 if (gtk_tree_selection_iter_is_selected (selection_data->selection,
356 &parent)) {
357 return;
359 child = parent;
362 selection_data->list = g_list_prepend (selection_data->list,
363 gtk_tree_row_reference_new (model, path));
366 static GList *
367 get_filtered_selection_refs (GtkTreeView *tree_view)
369 struct SelectionForeachData selection_data;
371 selection_data.list = NULL;
372 selection_data.selection = gtk_tree_view_get_selection (tree_view);
374 gtk_tree_selection_selected_foreach (selection_data.selection,
375 filtered_selection_foreach,
376 &selection_data);
377 return g_list_reverse (selection_data.list);
380 static void
381 ref_list_free (GList *ref_list)
383 g_list_foreach (ref_list, (GFunc) gtk_tree_row_reference_free, NULL);
384 g_list_free (ref_list);
387 static void
388 stop_drag_check (FMListView *view)
390 view->details->drag_button = 0;
393 static GdkPixbuf *
394 get_drag_pixbuf (FMListView *view)
396 GtkTreeModel *model;
397 GtkTreePath *path;
398 GtkTreeIter iter;
399 GdkPixbuf *ret;
400 GdkRectangle cell_area;
402 ret = NULL;
404 if (gtk_tree_view_get_path_at_pos (view->details->tree_view,
405 view->details->drag_x,
406 view->details->drag_y,
407 &path, NULL, NULL, NULL)) {
408 model = gtk_tree_view_get_model (view->details->tree_view);
409 gtk_tree_model_get_iter (model, &iter, path);
410 gtk_tree_model_get (model, &iter,
411 fm_list_model_get_column_id_from_zoom_level (view->details->zoom_level),
412 &ret,
413 -1);
415 gtk_tree_view_get_cell_area (view->details->tree_view,
416 path,
417 view->details->file_name_column,
418 &cell_area);
420 gtk_tree_path_free (path);
423 return ret;
426 static void
427 drag_begin_callback (GtkWidget *widget,
428 GdkDragContext *context,
429 FMListView *view)
431 GList *ref_list;
432 GdkPixbuf *pixbuf;
434 pixbuf = get_drag_pixbuf (view);
435 if (pixbuf) {
436 gtk_drag_set_icon_pixbuf (context,
437 pixbuf,
438 0, 0);
439 g_object_unref (pixbuf);
440 } else {
441 gtk_drag_set_icon_default (context);
444 stop_drag_check (view);
445 view->details->drag_started = TRUE;
447 ref_list = get_filtered_selection_refs (GTK_TREE_VIEW (widget));
448 g_object_set_data_full (G_OBJECT (context),
449 "drag-info",
450 ref_list,
451 (GDestroyNotify)ref_list_free);
454 static gboolean
455 motion_notify_callback (GtkWidget *widget,
456 GdkEventMotion *event,
457 gpointer callback_data)
459 FMListView *view;
460 GdkDragContext *context;
462 view = FM_LIST_VIEW (callback_data);
464 if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget))) {
465 return FALSE;
468 if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
469 GtkTreePath *old_hover_path;
471 old_hover_path = view->details->hover_path;
472 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
473 event->x, event->y,
474 &view->details->hover_path,
475 NULL, NULL, NULL);
477 if ((old_hover_path != NULL) != (view->details->hover_path != NULL)) {
478 if (view->details->hover_path != NULL) {
479 gdk_window_set_cursor (widget->window, hand_cursor);
480 } else {
481 gdk_window_set_cursor (widget->window, NULL);
485 if (old_hover_path != NULL) {
486 gtk_tree_path_free (old_hover_path);
490 if (view->details->drag_button != 0) {
491 if (!source_target_list) {
492 const GtkTargetEntry *drag_types;
493 int n_drag_types;
495 fm_list_model_get_drag_types (&drag_types,
496 &n_drag_types);
498 source_target_list = gtk_target_list_new (drag_types,
499 n_drag_types);
502 if (gtk_drag_check_threshold (widget,
503 view->details->drag_x,
504 view->details->drag_y,
505 event->x,
506 event->y)) {
507 context = gtk_drag_begin
508 (widget,
509 source_target_list,
510 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_ASK,
511 view->details->drag_button,
512 (GdkEvent*)event);
514 return TRUE;
517 return FALSE;
520 static gboolean
521 leave_notify_callback (GtkWidget *widget,
522 GdkEventCrossing *event,
523 gpointer callback_data)
525 FMListView *view;
527 view = FM_LIST_VIEW (callback_data);
529 if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE &&
530 view->details->hover_path != NULL) {
531 gtk_tree_path_free (view->details->hover_path);
532 view->details->hover_path = NULL;
535 return FALSE;
538 static gboolean
539 enter_notify_callback (GtkWidget *widget,
540 GdkEventCrossing *event,
541 gpointer callback_data)
543 FMListView *view;
545 view = FM_LIST_VIEW (callback_data);
547 if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
548 if (view->details->hover_path != NULL) {
549 gtk_tree_path_free (view->details->hover_path);
552 gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
553 event->x, event->y,
554 &view->details->hover_path,
555 NULL, NULL, NULL);
557 if (view->details->hover_path != NULL) {
558 gdk_window_set_cursor (widget->window, hand_cursor);
562 return FALSE;
565 static void
566 do_popup_menu (GtkWidget *widget, FMListView *view, GdkEventButton *event)
568 if (tree_view_has_selection (GTK_TREE_VIEW (widget))) {
569 fm_directory_view_pop_up_selection_context_menu (FM_DIRECTORY_VIEW (view), event);
570 } else {
571 fm_directory_view_pop_up_background_context_menu (FM_DIRECTORY_VIEW (view), event);
575 static gboolean
576 button_press_callback (GtkWidget *widget, GdkEventButton *event, gpointer callback_data)
578 FMListView *view;
579 GtkTreeView *tree_view;
580 GtkTreePath *path;
581 gboolean call_parent;
582 gboolean allow_drag;
583 GtkTreeSelection *selection;
584 GtkWidgetClass *tree_view_class;
585 gint64 current_time;
586 static gint64 last_click_time = 0;
587 static int click_count = 0;
588 int double_click_time;
589 int expander_size, horizontal_separator;
590 gboolean on_expander;
592 view = FM_LIST_VIEW (callback_data);
593 tree_view = GTK_TREE_VIEW (widget);
594 tree_view_class = GTK_WIDGET_GET_CLASS (tree_view);
595 selection = gtk_tree_view_get_selection (tree_view);
597 if (event->window != gtk_tree_view_get_bin_window (tree_view)) {
598 return FALSE;
601 fm_list_model_set_drag_view
602 (FM_LIST_MODEL (gtk_tree_view_get_model (tree_view)),
603 tree_view,
604 event->x, event->y);
606 g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
607 "gtk-double-click-time", &double_click_time,
608 NULL);
610 /* Determine click count */
611 current_time = eel_get_system_time ();
612 if (current_time - last_click_time < double_click_time * 1000) {
613 click_count++;
614 } else {
615 click_count = 0;
618 /* Stash time for next compare */
619 last_click_time = current_time;
621 /* Ignore double click if we are in single click mode */
622 if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE && click_count >= 2) {
623 return TRUE;
626 view->details->ignore_button_release = FALSE;
628 call_parent = TRUE;
629 allow_drag = FALSE;
630 if (gtk_tree_view_get_path_at_pos (tree_view, event->x, event->y,
631 &path, NULL, NULL, NULL)) {
632 /* Keep track of path of last click so double clicks only happen
633 * on the same item */
634 if ((event->button == 1 || event->button == 2) &&
635 event->type == GDK_BUTTON_PRESS) {
636 if (view->details->double_click_path[1]) {
637 gtk_tree_path_free (view->details->double_click_path[1]);
639 view->details->double_click_path[1] = view->details->double_click_path[0];
640 view->details->double_click_path[0] = gtk_tree_path_copy (path);
642 if (event->type == GDK_2BUTTON_PRESS) {
643 /* Double clicking does not trigger a D&D action. */
644 view->details->drag_button = 0;
645 if (view->details->double_click_path[1] &&
646 gtk_tree_path_compare (view->details->double_click_path[0], view->details->double_click_path[1]) == 0) {
647 /* NOTE: Activation can actually destroy the view if we're switching */
648 if (!button_event_modifies_selection (event)) {
649 if ((event->button == 1 || event->button == 3)) {
650 activate_selected_items (view);
651 } else if (event->button == 2) {
652 activate_selected_items_alternate (view, NULL);
654 } else if (event->button == 1 &&
655 (event->state & GDK_SHIFT_MASK) != 0) {
656 NautilusFile *file;
657 file = fm_list_model_file_for_path (view->details->model, path);
658 if (file != NULL) {
659 activate_selected_items_alternate (view, file);
660 nautilus_file_unref (file);
664 tree_view_class->button_press_event (widget, event);
665 } else {
667 /* We're going to filter out some situations where
668 * we can't let the default code run because all
669 * but one row would be would be deselected. We don't
670 * want that; we want the right click menu or single
671 * click to apply to everything that's currently selected. */
673 if (event->button == 3 && gtk_tree_selection_path_is_selected (selection, path)) {
674 call_parent = FALSE;
677 if ((event->button == 1 || event->button == 2) &&
678 ((event->state & GDK_CONTROL_MASK) != 0 ||
679 (event->state & GDK_SHIFT_MASK) == 0)) {
680 gtk_widget_style_get (widget,
681 "expander-size", &expander_size,
682 "horizontal-separator", &horizontal_separator,
683 NULL);
684 on_expander = (event->x <= horizontal_separator / 2 +
685 gtk_tree_path_get_depth (path) * expander_size);
688 view->details->row_selected_on_button_down = gtk_tree_selection_path_is_selected (selection, path);
689 if (view->details->row_selected_on_button_down) {
690 call_parent = on_expander;
691 view->details->ignore_button_release = call_parent;
692 } else if ((event->state & GDK_CONTROL_MASK) != 0) {
693 call_parent = FALSE;
694 gtk_tree_selection_select_path (selection, path);
695 } else {
696 view->details->ignore_button_release = on_expander;
700 if (call_parent) {
701 tree_view_class->button_press_event (widget, event);
702 } else if (gtk_tree_selection_path_is_selected (selection, path)) {
703 gtk_widget_grab_focus (widget);
706 if ((event->button == 1 || event->button == 2) &&
707 event->type == GDK_BUTTON_PRESS) {
708 view->details->drag_started = FALSE;
709 view->details->drag_button = event->button;
710 view->details->drag_x = event->x;
711 view->details->drag_y = event->y;
714 if (event->button == 3) {
715 do_popup_menu (widget, view, event);
719 gtk_tree_path_free (path);
720 } else {
721 if ((event->button == 1 || event->button == 2) &&
722 event->type == GDK_BUTTON_PRESS) {
723 if (view->details->double_click_path[1]) {
724 gtk_tree_path_free (view->details->double_click_path[1]);
726 view->details->double_click_path[1] = view->details->double_click_path[0];
727 view->details->double_click_path[0] = NULL;
729 /* Deselect if people click outside any row. It's OK to
730 let default code run; it won't reselect anything. */
731 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (tree_view));
733 if (event->button == 3) {
734 do_popup_menu (widget, view, event);
738 /* We chained to the default handler in this method, so never
739 * let the default handler run */
740 return TRUE;
743 static gboolean
744 button_release_callback (GtkWidget *widget,
745 GdkEventButton *event,
746 gpointer callback_data)
748 FMListView *view;
750 view = FM_LIST_VIEW (callback_data);
752 if (event->button == view->details->drag_button) {
753 stop_drag_check (view);
754 if (!view->details->drag_started &&
755 !view->details->ignore_button_release) {
756 fm_list_view_did_not_drag (view, event);
759 return FALSE;
762 static gboolean
763 popup_menu_callback (GtkWidget *widget, gpointer callback_data)
765 FMListView *view;
767 view = FM_LIST_VIEW (callback_data);
769 do_popup_menu (widget, view, NULL);
771 return TRUE;
774 static void
775 subdirectory_done_loading_callback (NautilusDirectory *directory, FMListView *view)
777 fm_list_model_subdirectory_done_loading (view->details->model, directory);
780 static void
781 row_expanded_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer callback_data)
783 FMListView *view;
784 NautilusDirectory *directory;
786 view = FM_LIST_VIEW (callback_data);
788 if (fm_list_model_load_subdirectory (view->details->model, path, &directory)) {
789 char *uri;
791 uri = nautilus_directory_get_uri (directory);
792 nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
793 "list view row expanded window=%p: %s",
794 fm_directory_view_get_containing_window (FM_DIRECTORY_VIEW (view)),
795 uri);
796 g_free (uri);
798 fm_directory_view_add_subdirectory (FM_DIRECTORY_VIEW (view), directory);
800 if (nautilus_directory_are_all_files_seen (directory)) {
801 fm_list_model_subdirectory_done_loading (view->details->model,
802 directory);
803 } else {
804 g_signal_connect_object (directory, "done_loading",
805 G_CALLBACK (subdirectory_done_loading_callback),
806 view, 0);
809 nautilus_directory_unref (directory);
813 struct UnloadDelayData {
814 NautilusFile *file;
815 NautilusDirectory *directory;
816 FMListView *view;
819 static gboolean
820 unload_file_timeout (gpointer data)
822 struct UnloadDelayData *unload_data = data;
823 GtkTreeIter iter;
824 FMListModel *model;
825 GtkTreePath *path;
827 if (unload_data->view != NULL) {
828 model = unload_data->view->details->model;
829 if (fm_list_model_get_tree_iter_from_file (model,
830 unload_data->file,
831 unload_data->directory,
832 &iter)) {
833 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
834 if (!gtk_tree_view_row_expanded (unload_data->view->details->tree_view,
835 path)) {
836 fm_list_model_unload_subdirectory (model, &iter);
838 gtk_tree_path_free (path);
842 eel_remove_weak_pointer (&unload_data->view);
844 if (unload_data->directory) {
845 nautilus_directory_unref (unload_data->directory);
847 nautilus_file_unref (unload_data->file);
848 g_free (unload_data);
849 return FALSE;
852 static void
853 row_collapsed_callback (GtkTreeView *treeview, GtkTreeIter *iter, GtkTreePath *path, gpointer callback_data)
855 FMListView *view;
856 NautilusFile *file;
857 NautilusDirectory *directory;
858 GtkTreeIter parent;
859 struct UnloadDelayData *unload_data;
860 GtkTreeModel *model;
861 char *uri;
863 view = FM_LIST_VIEW (callback_data);
864 model = GTK_TREE_MODEL (view->details->model);
866 gtk_tree_model_get (model, iter,
867 FM_LIST_MODEL_FILE_COLUMN, &file,
868 -1);
870 directory = NULL;
871 if (gtk_tree_model_iter_parent (model, &parent, iter)) {
872 gtk_tree_model_get (model, &parent,
873 FM_LIST_MODEL_SUBDIRECTORY_COLUMN, &directory,
874 -1);
878 uri = nautilus_file_get_uri (file);
879 nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
880 "list view row collapsed window=%p: %s",
881 fm_directory_view_get_containing_window (FM_DIRECTORY_VIEW (view)),
882 uri);
883 g_free (uri);
885 unload_data = g_new (struct UnloadDelayData, 1);
886 unload_data->view = view;
887 unload_data->file = file;
888 unload_data->directory = directory;
890 eel_add_weak_pointer (&unload_data->view);
892 g_timeout_add (COLLAPSE_TO_UNLOAD_DELAY,
893 unload_file_timeout,
894 unload_data);
897 static void
898 row_activated_callback (GtkTreeView *treeview, GtkTreePath *path,
899 GtkTreeViewColumn *column, FMListView *view)
901 activate_selected_items (view);
904 static void
905 subdirectory_unloaded_callback (FMListModel *model,
906 NautilusDirectory *directory,
907 gpointer callback_data)
909 FMListView *view;
911 g_return_if_fail (FM_IS_LIST_MODEL (model));
912 g_return_if_fail (NAUTILUS_IS_DIRECTORY (directory));
914 view = FM_LIST_VIEW(callback_data);
916 g_signal_handlers_disconnect_by_func (directory,
917 G_CALLBACK (subdirectory_done_loading_callback),
918 view);
919 fm_directory_view_remove_subdirectory (FM_DIRECTORY_VIEW (view), directory);
922 static gboolean
923 key_press_callback (GtkWidget *widget, GdkEventKey *event, gpointer callback_data)
925 FMDirectoryView *view;
926 GdkEventButton button_event = { 0 };
927 gboolean handled;
929 view = FM_DIRECTORY_VIEW (callback_data);
930 handled = FALSE;
932 switch (event->keyval) {
933 case GDK_F10:
934 if (event->state & GDK_CONTROL_MASK) {
935 fm_directory_view_pop_up_background_context_menu (view, &button_event);
936 handled = TRUE;
938 break;
939 case GDK_space:
940 if (event->state & GDK_CONTROL_MASK) {
941 handled = FALSE;
942 break;
944 if (!GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (FM_LIST_VIEW (view)->details->tree_view))) {
945 handled = FALSE;
946 break;
948 if ((event->state & GDK_SHIFT_MASK) != 0) {
949 activate_selected_items_alternate (FM_LIST_VIEW (view), NULL);
950 } else {
951 activate_selected_items (FM_LIST_VIEW (view));
953 handled = TRUE;
954 break;
955 case GDK_Return:
956 case GDK_KP_Enter:
957 if ((event->state & GDK_SHIFT_MASK) != 0) {
958 activate_selected_items_alternate (FM_LIST_VIEW (view), NULL);
959 } else {
960 activate_selected_items (FM_LIST_VIEW (view));
962 handled = TRUE;
963 break;
965 default:
966 handled = FALSE;
969 return handled;
972 static void
973 fm_list_view_reveal_selection (FMDirectoryView *view)
975 GList *selection;
977 g_return_if_fail (FM_IS_LIST_VIEW (view));
979 selection = fm_directory_view_get_selection (view);
981 /* Make sure at least one of the selected items is scrolled into view */
982 if (selection != NULL) {
983 FMListView *list_view;
984 NautilusFile *file;
985 GtkTreeIter iter;
986 GtkTreePath *path;
988 list_view = FM_LIST_VIEW (view);
989 file = selection->data;
990 if (fm_list_model_get_first_iter_for_file (list_view->details->model, file, &iter)) {
991 path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter);
993 gtk_tree_view_scroll_to_cell (list_view->details->tree_view, path, NULL, FALSE, 0.0, 0.0);
995 gtk_tree_path_free (path);
999 nautilus_file_list_free (selection);
1002 static void
1003 sort_column_changed_callback (GtkTreeSortable *sortable,
1004 FMListView *view)
1006 NautilusFile *file;
1007 gint sort_column_id;
1008 GtkSortType reversed;
1009 GQuark sort_attr, default_sort_attr;
1010 char *reversed_attr, *default_reversed_attr;
1012 file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (view));
1014 gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &reversed);
1016 sort_attr = fm_list_model_get_attribute_from_sort_column_id (view->details->model, sort_column_id);
1017 sort_column_id = fm_list_model_get_sort_column_id_from_attribute (view->details->model,
1018 g_quark_from_string (default_sort_order_auto_value));
1019 default_sort_attr = fm_list_model_get_attribute_from_sort_column_id (view->details->model, sort_column_id);
1020 nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
1021 g_quark_to_string (default_sort_attr), g_quark_to_string (sort_attr));
1023 default_reversed_attr = (default_sort_reversed_auto_value ? "true" : "false");
1024 reversed_attr = (reversed ? "true" : "false");
1025 nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
1026 default_reversed_attr, reversed_attr);
1028 /* Make sure selected item(s) is visible after sort */
1029 fm_list_view_reveal_selection (FM_DIRECTORY_VIEW (view));
1032 static void
1033 cell_renderer_editing_canceled (GtkCellRendererText *cell,
1034 FMListView *view)
1036 fm_directory_view_unfreeze_updates (FM_DIRECTORY_VIEW (view));
1039 static void
1040 cell_renderer_edited (GtkCellRendererText *cell,
1041 const char *path_str,
1042 const char *new_text,
1043 FMListView *view)
1045 GtkTreePath *path;
1046 NautilusFile *file;
1047 GtkTreeIter iter;
1049 /* Don't allow a rename with an empty string. Revert to original
1050 * without notifying the user.
1052 if (new_text[0] == '\0') {
1053 g_object_set (G_OBJECT (view->details->file_name_cell),
1054 "editable", FALSE,
1055 NULL);
1056 return;
1059 path = gtk_tree_path_new_from_string (path_str);
1061 gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
1062 &iter, path);
1064 gtk_tree_path_free (path);
1066 gtk_tree_model_get (GTK_TREE_MODEL (view->details->model),
1067 &iter,
1068 FM_LIST_MODEL_FILE_COLUMN, &file,
1069 -1);
1071 /* Only rename if name actually changed */
1072 if (strcmp (new_text, view->details->original_name) != 0) {
1073 view->details->renaming_file = nautilus_file_ref (file);
1074 view->details->rename_done = FALSE;
1075 fm_rename_file (file, new_text, fm_list_view_rename_callback, g_object_ref (view));
1076 g_free (view->details->original_name);
1077 view->details->original_name = g_strdup (new_text);
1080 nautilus_file_unref (file);
1082 /*We're done editing - make the filename-cells readonly again.*/
1083 g_object_set (G_OBJECT (view->details->file_name_cell),
1084 "editable", FALSE,
1085 NULL);
1087 fm_directory_view_unfreeze_updates (FM_DIRECTORY_VIEW (view));
1090 static char *
1091 get_root_uri_callback (NautilusTreeViewDragDest *dest,
1092 gpointer user_data)
1094 FMListView *view;
1096 view = FM_LIST_VIEW (user_data);
1098 return fm_directory_view_get_uri (FM_DIRECTORY_VIEW (view));
1101 static NautilusFile *
1102 get_file_for_path_callback (NautilusTreeViewDragDest *dest,
1103 GtkTreePath *path,
1104 gpointer user_data)
1106 FMListView *view;
1108 view = FM_LIST_VIEW (user_data);
1110 return fm_list_model_file_for_path (view->details->model, path);
1113 /* Handles an URL received from Mozilla */
1114 static void
1115 list_view_handle_netscape_url (NautilusTreeViewDragDest *dest, const char *encoded_url,
1116 const char *target_uri, GdkDragAction action, int x, int y, FMListView *view)
1118 fm_directory_view_handle_netscape_url_drop (FM_DIRECTORY_VIEW (view),
1119 encoded_url, target_uri, action, x, y);
1122 static void
1123 list_view_handle_uri_list (NautilusTreeViewDragDest *dest, const char *item_uris,
1124 const char *target_uri,
1125 GdkDragAction action, int x, int y, FMListView *view)
1127 fm_directory_view_handle_uri_list_drop (FM_DIRECTORY_VIEW (view),
1128 item_uris, target_uri, action, x, y);
1131 static void
1132 list_view_handle_text (NautilusTreeViewDragDest *dest, const char *text,
1133 const char *target_uri,
1134 GdkDragAction action, int x, int y, FMListView *view)
1136 fm_directory_view_handle_text_drop (FM_DIRECTORY_VIEW (view),
1137 text, target_uri, action, x, y);
1140 static void
1141 move_copy_items_callback (NautilusTreeViewDragDest *dest,
1142 const GList *item_uris,
1143 const char *target_uri,
1144 guint action,
1145 int x,
1146 int y,
1147 gpointer user_data)
1150 fm_directory_view_move_copy_items (item_uris,
1151 NULL,
1152 target_uri,
1153 action,
1154 x, y,
1155 FM_DIRECTORY_VIEW (user_data));
1158 static void
1159 apply_columns_settings (FMListView *list_view,
1160 char **column_order,
1161 char **visible_columns)
1163 GList *all_columns;
1164 GList *old_view_columns, *view_columns;
1165 GHashTable *visible_columns_hash;
1166 GtkTreeViewColumn *prev_view_column;
1167 GList *l;
1168 int i;
1170 /* prepare ordered list of view columns using column_order and visible_columns */
1171 view_columns = NULL;
1172 all_columns = nautilus_get_all_columns ();
1173 all_columns = nautilus_sort_columns (all_columns, column_order);
1175 /* hash table to lookup if a given column should be visible */
1176 visible_columns_hash = g_hash_table_new_full (g_str_hash,
1177 g_str_equal,
1178 (GDestroyNotify) g_free,
1179 (GDestroyNotify) g_free);
1180 for (i = 0; visible_columns[i] != NULL; ++i) {
1181 g_hash_table_insert (visible_columns_hash,
1182 g_ascii_strdown (visible_columns[i], -1),
1183 g_ascii_strdown (visible_columns[i], -1));
1186 for (l = all_columns; l != NULL; l = l->next) {
1187 char *name;
1188 char *lowercase;
1190 g_object_get (G_OBJECT (l->data), "name", &name, NULL);
1191 lowercase = g_ascii_strdown (name, -1);
1193 if (g_hash_table_lookup (visible_columns_hash, lowercase) != NULL) {
1194 GtkTreeViewColumn *view_column;
1196 view_column = g_hash_table_lookup (list_view->details->columns, name);
1197 if (view_column != NULL) {
1198 view_columns = g_list_prepend (view_columns, view_column);
1202 g_free (name);
1203 g_free (lowercase);
1206 g_hash_table_destroy (visible_columns_hash);
1207 nautilus_column_list_free (all_columns);
1209 view_columns = g_list_reverse (view_columns);
1211 /* remove columns that are not present in the configuration */
1212 old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
1213 for (l = old_view_columns; l != NULL; l = l->next) {
1214 if (g_list_find (view_columns, l->data) == NULL) {
1215 gtk_tree_view_remove_column (list_view->details->tree_view, l->data);
1218 g_list_free (old_view_columns);
1220 /* append new columns from the configuration */
1221 old_view_columns = gtk_tree_view_get_columns (list_view->details->tree_view);
1222 for (l = view_columns; l != NULL; l = l->next) {
1223 if (g_list_find (old_view_columns, l->data) == NULL) {
1224 gtk_tree_view_append_column (list_view->details->tree_view, l->data);
1227 g_list_free (old_view_columns);
1229 /* place columns in the correct order */
1230 prev_view_column = NULL;
1231 for (l = view_columns; l != NULL; l = l->next) {
1232 gtk_tree_view_move_column_after (list_view->details->tree_view, l->data, prev_view_column);
1233 prev_view_column = l->data;
1235 g_list_free (view_columns);
1238 static void
1239 filename_cell_data_func (GtkTreeViewColumn *column,
1240 GtkCellRenderer *renderer,
1241 GtkTreeModel *model,
1242 GtkTreeIter *iter,
1243 FMListView *view)
1245 char *text;
1246 GtkTreePath *path;
1247 PangoUnderline underline;
1249 gtk_tree_model_get (model, iter,
1250 view->details->file_name_column_num, &text,
1251 -1);
1253 if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
1254 path = gtk_tree_model_get_path (model, iter);
1256 if (view->details->hover_path == NULL ||
1257 gtk_tree_path_compare (path, view->details->hover_path)) {
1258 underline = PANGO_UNDERLINE_NONE;
1259 } else {
1260 underline = PANGO_UNDERLINE_SINGLE;
1263 gtk_tree_path_free (path);
1264 } else {
1265 underline = PANGO_UNDERLINE_NONE;
1268 g_object_set (G_OBJECT (renderer),
1269 "text", text,
1270 "underline", underline,
1271 NULL);
1272 g_free (text);
1275 static void
1276 create_and_set_up_tree_view (FMListView *view)
1278 GtkCellRenderer *cell;
1279 GtkTreeViewColumn *column;
1280 GtkBindingSet *binding_set;
1281 AtkObject *atk_obj;
1282 GList *nautilus_columns;
1283 GList *l;
1285 view->details->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ());
1286 view->details->columns = g_hash_table_new_full (g_str_hash,
1287 g_str_equal,
1288 (GDestroyNotify)g_free,
1289 (GDestroyNotify) g_object_unref);
1290 gtk_tree_view_set_enable_search (view->details->tree_view, TRUE);
1292 /* Don't handle backspace key. It's used to open the parent folder. */
1293 binding_set = gtk_binding_set_by_class (GTK_WIDGET_GET_CLASS (view->details->tree_view));
1294 gtk_binding_entry_clear (binding_set, GDK_BackSpace, 0);
1296 view->details->drag_dest =
1297 nautilus_tree_view_drag_dest_new (view->details->tree_view);
1299 g_signal_connect_object (view->details->drag_dest,
1300 "get_root_uri",
1301 G_CALLBACK (get_root_uri_callback),
1302 view, 0);
1303 g_signal_connect_object (view->details->drag_dest,
1304 "get_file_for_path",
1305 G_CALLBACK (get_file_for_path_callback),
1306 view, 0);
1307 g_signal_connect_object (view->details->drag_dest,
1308 "move_copy_items",
1309 G_CALLBACK (move_copy_items_callback),
1310 view, 0);
1311 g_signal_connect_object (view->details->drag_dest, "handle_netscape_url",
1312 G_CALLBACK (list_view_handle_netscape_url), view, 0);
1313 g_signal_connect_object (view->details->drag_dest, "handle_uri_list",
1314 G_CALLBACK (list_view_handle_uri_list), view, 0);
1315 g_signal_connect_object (view->details->drag_dest, "handle_text",
1316 G_CALLBACK (list_view_handle_text), view, 0);
1318 g_signal_connect_object (gtk_tree_view_get_selection (view->details->tree_view),
1319 "changed",
1320 G_CALLBACK (list_selection_changed_callback), view, 0);
1322 g_signal_connect_object (view->details->tree_view, "drag_begin",
1323 G_CALLBACK (drag_begin_callback), view, 0);
1324 g_signal_connect_object (view->details->tree_view, "drag_data_get",
1325 G_CALLBACK (drag_data_get_callback), view, 0);
1326 g_signal_connect_object (view->details->tree_view, "motion_notify_event",
1327 G_CALLBACK (motion_notify_callback), view, 0);
1328 g_signal_connect_object (view->details->tree_view, "enter_notify_event",
1329 G_CALLBACK (enter_notify_callback), view, 0);
1330 g_signal_connect_object (view->details->tree_view, "leave_notify_event",
1331 G_CALLBACK (leave_notify_callback), view, 0);
1332 g_signal_connect_object (view->details->tree_view, "button_press_event",
1333 G_CALLBACK (button_press_callback), view, 0);
1334 g_signal_connect_object (view->details->tree_view, "button_release_event",
1335 G_CALLBACK (button_release_callback), view, 0);
1336 g_signal_connect_object (view->details->tree_view, "key_press_event",
1337 G_CALLBACK (key_press_callback), view, 0);
1338 g_signal_connect_object (view->details->tree_view, "popup_menu",
1339 G_CALLBACK (popup_menu_callback), view, 0);
1340 g_signal_connect_object (view->details->tree_view, "row_expanded",
1341 G_CALLBACK (row_expanded_callback), view, 0);
1342 g_signal_connect_object (view->details->tree_view, "row_collapsed",
1343 G_CALLBACK (row_collapsed_callback), view, 0);
1344 g_signal_connect_object (view->details->tree_view, "row-activated",
1345 G_CALLBACK (row_activated_callback), view, 0);
1347 view->details->model = g_object_new (FM_TYPE_LIST_MODEL, NULL);
1348 gtk_tree_view_set_model (view->details->tree_view, GTK_TREE_MODEL (view->details->model));
1349 /* Need the model for the dnd drop icon "accept" change */
1350 fm_list_model_set_drag_view (FM_LIST_MODEL (view->details->model),
1351 view->details->tree_view, 0, 0);
1353 g_signal_connect_object (view->details->model, "sort_column_changed",
1354 G_CALLBACK (sort_column_changed_callback), view, 0);
1356 g_signal_connect_object (view->details->model, "subdirectory_unloaded",
1357 G_CALLBACK (subdirectory_unloaded_callback), view, 0);
1359 gtk_tree_selection_set_mode (gtk_tree_view_get_selection (view->details->tree_view), GTK_SELECTION_MULTIPLE);
1360 gtk_tree_view_set_rules_hint (view->details->tree_view, TRUE);
1362 nautilus_columns = nautilus_get_all_columns ();
1364 for (l = nautilus_columns; l != NULL; l = l->next) {
1365 NautilusColumn *nautilus_column;
1366 int column_num;
1367 char *name;
1368 char *label;
1369 float xalign;
1371 nautilus_column = NAUTILUS_COLUMN (l->data);
1373 g_object_get (nautilus_column,
1374 "name", &name,
1375 "label", &label,
1376 "xalign", &xalign, NULL);
1378 column_num = fm_list_model_add_column (view->details->model,
1379 nautilus_column);
1381 /* Created the name column specially, because it
1382 * has the icon in it.*/
1383 if (!strcmp (name, "name")) {
1384 /* Create the file name column */
1385 cell = nautilus_cell_renderer_pixbuf_emblem_new ();
1386 view->details->pixbuf_cell = (GtkCellRendererPixbuf *)cell;
1388 view->details->file_name_column = gtk_tree_view_column_new ();
1389 g_object_ref (view->details->file_name_column);
1390 gtk_object_sink (GTK_OBJECT (view->details->file_name_column));
1391 view->details->file_name_column_num = column_num;
1393 g_hash_table_insert (view->details->columns,
1394 g_strdup ("name"),
1395 view->details->file_name_column);
1397 gtk_tree_view_set_search_column (view->details->tree_view, column_num);
1399 gtk_tree_view_column_set_sort_column_id (view->details->file_name_column, column_num);
1400 gtk_tree_view_column_set_title (view->details->file_name_column, _("Name"));
1401 gtk_tree_view_column_set_resizable (view->details->file_name_column, TRUE);
1403 gtk_tree_view_column_pack_start (view->details->file_name_column, cell, FALSE);
1404 gtk_tree_view_column_set_attributes (view->details->file_name_column,
1405 cell,
1406 "pixbuf", FM_LIST_MODEL_SMALLEST_ICON_COLUMN,
1407 "pixbuf_emblem", FM_LIST_MODEL_SMALLEST_EMBLEM_COLUMN,
1408 NULL);
1410 cell = nautilus_cell_renderer_text_ellipsized_new ();
1411 view->details->file_name_cell = (GtkCellRendererText *)cell;
1412 g_signal_connect (cell, "edited", G_CALLBACK (cell_renderer_edited), view);
1413 g_signal_connect (cell, "editing-canceled", G_CALLBACK (cell_renderer_editing_canceled), view);
1415 gtk_tree_view_column_pack_start (view->details->file_name_column, cell, TRUE);
1416 gtk_tree_view_column_set_cell_data_func (view->details->file_name_column, cell,
1417 (GtkTreeCellDataFunc) filename_cell_data_func,
1418 view, NULL);
1419 } else {
1420 cell = gtk_cell_renderer_text_new ();
1421 g_object_set (cell, "xalign", xalign, NULL);
1422 view->details->cells = g_list_append (view->details->cells,
1423 cell);
1424 column = gtk_tree_view_column_new_with_attributes (label,
1425 cell,
1426 "text", column_num,
1427 NULL);
1428 g_object_ref (column);
1429 gtk_object_sink (GTK_OBJECT (column));
1430 gtk_tree_view_column_set_sort_column_id (column, column_num);
1431 g_hash_table_insert (view->details->columns,
1432 g_strdup (name),
1433 column);
1435 gtk_tree_view_column_set_resizable (column, TRUE);
1436 gtk_tree_view_column_set_visible (column, TRUE);
1438 g_free (name);
1439 g_free (label);
1441 nautilus_column_list_free (nautilus_columns);
1443 /* Apply the default column order and visible columns, to get it
1444 * right most of the time. The metadata will be checked when a
1445 * folder is loaded */
1446 apply_columns_settings (view,
1447 default_column_order_auto_value,
1448 default_visible_columns_auto_value);
1450 gtk_widget_show (GTK_WIDGET (view->details->tree_view));
1451 gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (view->details->tree_view));
1454 atk_obj = gtk_widget_get_accessible (GTK_WIDGET (view->details->tree_view));
1455 atk_object_set_name (atk_obj, _("List View"));
1458 static void
1459 fm_list_view_add_file (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory)
1461 FMListModel *model;
1463 model = FM_LIST_VIEW (view)->details->model;
1464 fm_list_model_add_file (model, file, directory);
1467 static char **
1468 get_visible_columns (FMListView *list_view)
1470 NautilusFile *file;
1471 GList *visible_columns;
1472 char **ret;
1474 ret = NULL;
1476 file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view));
1478 visible_columns = nautilus_file_get_metadata_list
1479 (file,
1480 NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS,
1481 NAUTILUS_METADATA_SUBKEY_COLUMNS);
1483 if (visible_columns) {
1484 GPtrArray *res;
1485 GList *l;
1487 res = g_ptr_array_new ();
1488 for (l = visible_columns; l != NULL; l = l->next) {
1489 g_ptr_array_add (res, l->data);
1491 g_ptr_array_add (res, NULL);
1493 ret = (char **) g_ptr_array_free (res, FALSE);
1496 return ret ? ret : g_strdupv (default_visible_columns_auto_value);
1499 static char **
1500 get_column_order (FMListView *list_view)
1502 NautilusFile *file;
1503 GList *column_order;
1504 char **ret;
1506 ret = NULL;
1508 file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view));
1510 column_order = nautilus_file_get_metadata_list
1511 (file,
1512 NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER,
1513 NAUTILUS_METADATA_SUBKEY_COLUMNS);
1515 if (column_order) {
1516 GPtrArray *res;
1517 GList *l;
1519 res = g_ptr_array_new ();
1520 for (l = column_order; l != NULL; l = l->next) {
1521 g_ptr_array_add (res, l->data);
1523 g_ptr_array_add (res, NULL);
1525 ret = (char **) g_ptr_array_free (res, FALSE);
1528 return ret ? ret : g_strdupv (default_visible_columns_auto_value);
1531 static void
1532 set_columns_settings_from_metadata_and_preferences (FMListView *list_view)
1534 char **column_order;
1535 char **visible_columns;
1537 column_order = get_column_order (list_view);
1538 visible_columns = get_visible_columns (list_view);
1540 apply_columns_settings (list_view, column_order, visible_columns);
1542 g_strfreev (column_order);
1543 g_strfreev (visible_columns);
1546 static void
1547 set_sort_order_from_metadata_and_preferences (FMListView *list_view)
1549 char *sort_attribute;
1550 int sort_column_id;
1551 NautilusFile *file;
1552 gboolean sort_reversed;
1554 file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view));
1555 sort_attribute = nautilus_file_get_metadata (file,
1556 NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN,
1557 NULL);
1558 sort_column_id = fm_list_model_get_sort_column_id_from_attribute (list_view->details->model,
1559 g_quark_from_string (sort_attribute));
1560 g_free (sort_attribute);
1561 if (sort_column_id == -1) {
1562 sort_column_id =
1563 fm_list_model_get_sort_column_id_from_attribute (list_view->details->model,
1564 g_quark_from_string (default_sort_order_auto_value));
1567 sort_reversed = nautilus_file_get_boolean_metadata (file,
1568 NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED,
1569 default_sort_reversed_auto_value);
1571 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_view->details->model),
1572 sort_column_id,
1573 sort_reversed ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
1576 static gboolean
1577 list_view_changed_foreach (GtkTreeModel *model,
1578 GtkTreePath *path,
1579 GtkTreeIter *iter,
1580 gpointer data)
1582 gtk_tree_model_row_changed (model, path, iter);
1583 return FALSE;
1586 static NautilusZoomLevel
1587 get_default_zoom_level (void) {
1588 NautilusZoomLevel default_zoom_level;
1590 default_zoom_level = default_zoom_level_auto_value;
1592 if (default_zoom_level < NAUTILUS_ZOOM_LEVEL_SMALLEST
1593 || NAUTILUS_ZOOM_LEVEL_LARGEST < default_zoom_level) {
1594 default_zoom_level = NAUTILUS_ZOOM_LEVEL_SMALL;
1597 return default_zoom_level;
1600 static void
1601 set_zoom_level_from_metadata_and_preferences (FMListView *list_view)
1603 NautilusFile *file;
1604 int level;
1606 if (fm_directory_view_supports_zooming (FM_DIRECTORY_VIEW (list_view))) {
1607 file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view));
1608 level = nautilus_file_get_integer_metadata (file,
1609 NAUTILUS_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL,
1610 get_default_zoom_level ());
1611 fm_list_view_set_zoom_level (list_view, level, TRUE);
1613 /* updated the rows after updating the font size */
1614 gtk_tree_model_foreach (GTK_TREE_MODEL (list_view->details->model),
1615 list_view_changed_foreach, NULL);
1619 static void
1620 fm_list_view_begin_loading (FMDirectoryView *view)
1622 FMListView *list_view;
1624 list_view = FM_LIST_VIEW (view);
1626 set_sort_order_from_metadata_and_preferences (list_view);
1627 set_zoom_level_from_metadata_and_preferences (list_view);
1628 set_columns_settings_from_metadata_and_preferences (list_view);
1631 static void
1632 stop_cell_editing (FMListView *list_view)
1634 GtkTreeViewColumn *column;
1636 /* Stop an ongoing rename to commit the name changes when the user
1637 * changes directories without exiting cell edit mode. It also prevents
1638 * the edited handler from being called on the cleared list model.
1641 column = list_view->details->file_name_column;
1642 if (column != NULL && column->editable_widget != NULL &&
1643 GTK_IS_CELL_EDITABLE (column->editable_widget)) {
1644 gtk_cell_editable_editing_done (column->editable_widget);
1648 static void
1649 fm_list_view_clear (FMDirectoryView *view)
1651 FMListView *list_view;
1653 list_view = FM_LIST_VIEW (view);
1655 if (list_view->details->model != NULL) {
1656 stop_cell_editing (list_view);
1657 fm_list_model_clear (list_view->details->model);
1661 static void
1662 fm_list_view_rename_callback (NautilusFile *file,
1663 GFile *result_location,
1664 GError *error,
1665 gpointer callback_data)
1667 FMListView *view;
1669 view = FM_LIST_VIEW (callback_data);
1671 if (view->details->renaming_file) {
1672 view->details->rename_done = TRUE;
1674 if (error != NULL) {
1675 /* If the rename failed (or was cancelled), kill renaming_file.
1676 * We won't get a change event for the rename, so otherwise
1677 * it would stay around forever.
1679 nautilus_file_unref (view->details->renaming_file);
1680 view->details->renaming_file = NULL;
1684 g_object_unref (view);
1688 static void
1689 fm_list_view_file_changed (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory)
1691 FMListView *listview;
1692 GtkTreeIter iter;
1693 GtkTreePath *file_path;
1695 listview = FM_LIST_VIEW (view);
1697 fm_list_model_file_changed (listview->details->model, file, directory);
1699 if (listview->details->renaming_file != NULL &&
1700 file == listview->details->renaming_file &&
1701 listview->details->rename_done) {
1702 /* This is (probably) the result of the rename operation, and
1703 * the tree-view changes above could have resorted the list, so
1704 * scroll to the new position
1706 if (fm_list_model_get_tree_iter_from_file (listview->details->model, file, directory, &iter)) {
1707 file_path = gtk_tree_model_get_path (GTK_TREE_MODEL (listview->details->model), &iter);
1708 gtk_tree_view_scroll_to_cell (listview->details->tree_view,
1709 file_path, NULL,
1710 FALSE, 0.0, 0.0);
1711 gtk_tree_path_free (file_path);
1714 nautilus_file_unref (listview->details->renaming_file);
1715 listview->details->renaming_file = NULL;
1719 static GtkWidget *
1720 fm_list_view_get_background_widget (FMDirectoryView *view)
1722 return GTK_WIDGET (view);
1725 static void
1726 fm_list_view_get_selection_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1728 GList **list;
1729 NautilusFile *file;
1731 list = data;
1733 gtk_tree_model_get (model, iter,
1734 FM_LIST_MODEL_FILE_COLUMN, &file,
1735 -1);
1737 if (file != NULL) {
1738 (* list) = g_list_prepend ((* list), file);
1742 static GList *
1743 fm_list_view_get_selection (FMDirectoryView *view)
1745 GList *list;
1747 list = NULL;
1749 gtk_tree_selection_selected_foreach (gtk_tree_view_get_selection (FM_LIST_VIEW (view)->details->tree_view),
1750 fm_list_view_get_selection_foreach_func, &list);
1752 return g_list_reverse (list);
1755 static void
1756 fm_list_view_get_selection_for_file_transfer_foreach_func (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1758 NautilusFile *file;
1759 struct SelectionForeachData *selection_data;
1760 GtkTreeIter parent, child;
1762 selection_data = data;
1764 gtk_tree_model_get (model, iter,
1765 FM_LIST_MODEL_FILE_COLUMN, &file,
1766 -1);
1768 if (file != NULL) {
1769 /* If the parent folder is also selected, don't include this file in the
1770 * file operation, since that would copy it to the toplevel target instead
1771 * of keeping it as a child of the copied folder
1773 child = *iter;
1774 while (gtk_tree_model_iter_parent (model, &parent, &child)) {
1775 if (gtk_tree_selection_iter_is_selected (selection_data->selection,
1776 &parent)) {
1777 return;
1779 child = parent;
1782 nautilus_file_ref (file);
1783 selection_data->list = g_list_prepend (selection_data->list, file);
1788 static GList *
1789 fm_list_view_get_selection_for_file_transfer (FMDirectoryView *view)
1791 struct SelectionForeachData selection_data;
1793 selection_data.list = NULL;
1794 selection_data.selection = gtk_tree_view_get_selection (FM_LIST_VIEW (view)->details->tree_view);
1796 gtk_tree_selection_selected_foreach (selection_data.selection,
1797 fm_list_view_get_selection_for_file_transfer_foreach_func, &selection_data);
1799 return g_list_reverse (selection_data.list);
1805 static guint
1806 fm_list_view_get_item_count (FMDirectoryView *view)
1808 g_return_val_if_fail (FM_IS_LIST_VIEW (view), 0);
1810 return fm_list_model_get_length (FM_LIST_VIEW (view)->details->model);
1813 static gboolean
1814 fm_list_view_is_empty (FMDirectoryView *view)
1816 return fm_list_model_is_empty (FM_LIST_VIEW (view)->details->model);
1819 static void
1820 fm_list_view_end_file_changes (FMDirectoryView *view)
1822 FMListView *list_view;
1824 list_view = FM_LIST_VIEW (view);
1826 if (list_view->details->new_selection_path) {
1827 gtk_tree_view_set_cursor (list_view->details->tree_view,
1828 list_view->details->new_selection_path,
1829 NULL, FALSE);
1830 gtk_tree_path_free (list_view->details->new_selection_path);
1831 list_view->details->new_selection_path = NULL;
1835 static void
1836 fm_list_view_remove_file (FMDirectoryView *view, NautilusFile *file, NautilusDirectory *directory)
1838 GtkTreePath *path;
1839 GtkTreePath *file_path;
1840 GtkTreeIter iter;
1841 GtkTreeIter temp_iter;
1842 GtkTreeRowReference* row_reference;
1843 FMListView *list_view;
1844 GtkTreeModel* tree_model;
1845 GtkTreeSelection *selection;
1847 path = NULL;
1848 row_reference = NULL;
1849 list_view = FM_LIST_VIEW (view);
1850 tree_model = GTK_TREE_MODEL(list_view->details->model);
1852 if (fm_list_model_get_tree_iter_from_file (list_view->details->model, file, directory, &iter)) {
1853 selection = gtk_tree_view_get_selection (list_view->details->tree_view);
1854 file_path = gtk_tree_model_get_path (tree_model, &iter);
1856 if (gtk_tree_selection_path_is_selected (selection, file_path)) {
1857 /* get reference for next element in the list view. If the element to be deleted is the
1858 * last one, get reference to previous element. If there is only one element in view
1859 * no need to select anything.
1861 temp_iter = iter;
1863 if (gtk_tree_model_iter_next (tree_model, &iter)) {
1864 path = gtk_tree_model_get_path (tree_model, &iter);
1865 row_reference = gtk_tree_row_reference_new (tree_model, path);
1866 } else {
1867 path = gtk_tree_model_get_path (tree_model, &temp_iter);
1868 if (gtk_tree_path_prev (path)) {
1869 row_reference = gtk_tree_row_reference_new (tree_model, path);
1872 gtk_tree_path_free (path);
1875 gtk_tree_path_free (file_path);
1877 fm_list_model_remove_file (list_view->details->model, file, directory);
1879 if (gtk_tree_row_reference_valid (row_reference)) {
1880 if (list_view->details->new_selection_path) {
1881 gtk_tree_path_free (list_view->details->new_selection_path);
1883 list_view->details->new_selection_path = gtk_tree_row_reference_get_path (row_reference);
1886 if (row_reference) {
1887 gtk_tree_row_reference_free (row_reference);
1894 static void
1895 fm_list_view_set_selection (FMDirectoryView *view, GList *selection)
1897 FMListView *list_view;
1898 GtkTreeSelection *tree_selection;
1899 GList *node;
1900 GList *iters, *l;
1901 NautilusFile *file;
1903 list_view = FM_LIST_VIEW (view);
1904 tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
1906 g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
1908 gtk_tree_selection_unselect_all (tree_selection);
1909 for (node = selection; node != NULL; node = node->next) {
1910 file = node->data;
1911 iters = fm_list_model_get_all_iters_for_file (list_view->details->model, file);
1913 for (l = iters; l != NULL; l = l->next) {
1914 gtk_tree_selection_select_iter (tree_selection,
1915 (GtkTreeIter *)l->data);
1917 eel_g_list_free_deep (iters);
1920 g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
1921 fm_directory_view_notify_selection_changed (view);
1924 static void
1925 fm_list_view_invert_selection (FMDirectoryView *view)
1927 FMListView *list_view;
1928 GtkTreeSelection *tree_selection;
1929 GList *node;
1930 GList *iters, *l;
1931 NautilusFile *file;
1932 GList *selection = NULL;
1934 list_view = FM_LIST_VIEW (view);
1935 tree_selection = gtk_tree_view_get_selection (list_view->details->tree_view);
1937 g_signal_handlers_block_by_func (tree_selection, list_selection_changed_callback, view);
1939 gtk_tree_selection_selected_foreach (tree_selection,
1940 fm_list_view_get_selection_foreach_func, &selection);
1942 gtk_tree_selection_select_all (tree_selection);
1944 for (node = selection; node != NULL; node = node->next) {
1945 file = node->data;
1946 iters = fm_list_model_get_all_iters_for_file (list_view->details->model, file);
1948 for (l = iters; l != NULL; l = l->next) {
1949 gtk_tree_selection_unselect_iter (tree_selection,
1950 (GtkTreeIter *)l->data);
1952 eel_g_list_free_deep (iters);
1955 g_list_free (selection);
1957 g_signal_handlers_unblock_by_func (tree_selection, list_selection_changed_callback, view);
1958 fm_directory_view_notify_selection_changed (view);
1961 static void
1962 fm_list_view_select_all (FMDirectoryView *view)
1964 gtk_tree_selection_select_all (gtk_tree_view_get_selection (FM_LIST_VIEW (view)->details->tree_view));
1967 static void
1968 column_editor_response_callback (GtkWidget *dialog,
1969 int response_id,
1970 gpointer user_data)
1972 gtk_widget_destroy (GTK_WIDGET (dialog));
1975 static void
1976 column_chooser_changed_callback (NautilusColumnChooser *chooser,
1977 FMListView *view)
1979 NautilusFile *file;
1980 char **visible_columns;
1981 char **column_order;
1982 GList *list;
1983 int i;
1985 file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (view));
1987 nautilus_column_chooser_get_settings (chooser,
1988 &visible_columns,
1989 &column_order);
1991 list = NULL;
1992 for (i = 0; visible_columns[i] != NULL; ++i) {
1993 list = g_list_prepend (list, visible_columns[i]);
1995 list = g_list_reverse (list);
1996 nautilus_file_set_metadata_list (file,
1997 NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS,
1998 NAUTILUS_METADATA_SUBKEY_COLUMNS,
1999 list);
2000 g_list_free (list);
2002 list = NULL;
2003 for (i = 0; column_order[i] != NULL; ++i) {
2004 list = g_list_prepend (list, column_order[i]);
2006 list = g_list_reverse (list);
2007 nautilus_file_set_metadata_list (file,
2008 NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER,
2009 NAUTILUS_METADATA_SUBKEY_COLUMNS,
2010 list);
2011 g_list_free (list);
2013 g_strfreev (visible_columns);
2014 g_strfreev (column_order);
2016 set_columns_settings_from_metadata_and_preferences (view);
2019 static void
2020 column_chooser_set_from_settings (NautilusColumnChooser *chooser,
2021 FMListView *view)
2023 char **visible_columns;
2024 char **column_order;
2026 g_signal_handlers_block_by_func
2027 (chooser, G_CALLBACK (column_chooser_changed_callback), view);
2029 visible_columns = get_visible_columns (view);
2030 column_order = get_column_order (view);
2032 nautilus_column_chooser_set_settings (chooser,
2033 visible_columns,
2034 column_order);
2036 g_strfreev (visible_columns);
2037 g_strfreev (column_order);
2039 g_signal_handlers_unblock_by_func
2040 (chooser, G_CALLBACK (column_chooser_changed_callback), view);
2043 static void
2044 column_chooser_use_default_callback (NautilusColumnChooser *chooser,
2045 FMListView *view)
2047 NautilusFile *file;
2049 file = fm_directory_view_get_directory_as_file
2050 (FM_DIRECTORY_VIEW (view));
2052 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NAUTILUS_METADATA_SUBKEY_COLUMNS, NULL);
2053 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NAUTILUS_METADATA_SUBKEY_COLUMNS, NULL);
2055 set_columns_settings_from_metadata_and_preferences (FM_LIST_VIEW (view));
2056 column_chooser_set_from_settings (chooser, view);
2059 static GtkWidget *
2060 create_column_editor (FMListView *view)
2062 GtkWidget *window;
2063 GtkWidget *label;
2064 GtkWidget *box;
2065 GtkWidget *column_chooser;
2066 NautilusFile *file;
2067 char *title;
2068 char *name;
2070 file = fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (view));
2071 name = nautilus_file_get_display_name (file);
2072 title = g_strdup_printf (_("%s Visible Columns"), name);
2073 g_free (name);
2075 window = gtk_dialog_new_with_buttons (title,
2076 GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view))),
2077 GTK_DIALOG_DESTROY_WITH_PARENT,
2078 GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
2079 NULL);
2080 g_free (title);
2081 g_signal_connect (window, "response",
2082 G_CALLBACK (column_editor_response_callback), NULL);
2084 gtk_window_set_default_size (GTK_WINDOW (window), 300, 400);
2086 box = gtk_vbox_new (FALSE, 6);
2087 gtk_container_set_border_width (GTK_CONTAINER (box), 12);
2088 gtk_widget_show (box);
2089 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (window)->vbox), box);
2091 label = gtk_label_new (_("Choose the order of information to appear in this folder."));
2092 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2093 gtk_widget_show (label);
2094 gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
2096 column_chooser = nautilus_column_chooser_new ();
2097 gtk_widget_show (column_chooser);
2098 gtk_box_pack_start (GTK_BOX (box), column_chooser, TRUE, TRUE, 0);
2100 g_signal_connect (column_chooser, "changed",
2101 G_CALLBACK (column_chooser_changed_callback),
2102 view);
2103 g_signal_connect (column_chooser, "use_default",
2104 G_CALLBACK (column_chooser_use_default_callback),
2105 view);
2107 column_chooser_set_from_settings
2108 (NAUTILUS_COLUMN_CHOOSER (column_chooser), view);
2110 return window;
2113 static void
2114 action_visible_columns_callback (GtkAction *action,
2115 gpointer callback_data)
2117 FMListView *list_view;
2119 list_view = FM_LIST_VIEW (callback_data);
2121 if (list_view->details->column_editor) {
2122 gtk_window_present (GTK_WINDOW (list_view->details->column_editor));
2123 } else {
2124 list_view->details->column_editor = create_column_editor (list_view);
2125 eel_add_weak_pointer (&list_view->details->column_editor);
2127 gtk_widget_show (list_view->details->column_editor);
2131 static const GtkActionEntry list_view_entries[] = {
2132 /* name, stock id */ { "Visible Columns", NULL,
2133 /* label, accelerator */ N_("Visible _Columns..."), NULL,
2134 /* tooltip */ N_("Select the columns visible in this folder"),
2135 G_CALLBACK (action_visible_columns_callback) },
2138 static void
2139 fm_list_view_merge_menus (FMDirectoryView *view)
2141 FMListView *list_view;
2142 GtkUIManager *ui_manager;
2143 GtkActionGroup *action_group;
2144 const char *ui;
2146 EEL_CALL_PARENT (FM_DIRECTORY_VIEW_CLASS, merge_menus, (view));
2148 list_view = FM_LIST_VIEW (view);
2150 ui_manager = fm_directory_view_get_ui_manager (view);
2152 action_group = gtk_action_group_new ("ListViewActions");
2153 gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
2154 list_view->details->list_action_group = action_group;
2155 gtk_action_group_add_actions (action_group,
2156 list_view_entries, G_N_ELEMENTS (list_view_entries),
2157 list_view);
2159 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
2160 g_object_unref (action_group); /* owned by ui manager */
2162 ui = nautilus_ui_string_get ("nautilus-list-view-ui.xml");
2163 list_view->details->list_merge_id = gtk_ui_manager_add_ui_from_string (ui_manager, ui, -1, NULL);
2165 list_view->details->menus_ready = TRUE;
2168 static void
2169 fm_list_view_update_menus (FMDirectoryView *view)
2171 FMListView *list_view;
2173 list_view = FM_LIST_VIEW (view);
2175 /* don't update if the menus aren't ready */
2176 if (!list_view->details->menus_ready) {
2177 return;
2180 EEL_CALL_PARENT (FM_DIRECTORY_VIEW_CLASS, update_menus, (view));
2183 /* Reset sort criteria and zoom level to match defaults */
2184 static void
2185 fm_list_view_reset_to_defaults (FMDirectoryView *view)
2187 NautilusFile *file;
2189 file = fm_directory_view_get_directory_as_file (view);
2191 nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN, NULL, NULL);
2192 nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED, NULL, NULL);
2193 nautilus_file_set_metadata (file, NAUTILUS_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL, NULL, NULL);
2194 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER, NAUTILUS_METADATA_SUBKEY_COLUMNS, NULL);
2195 nautilus_file_set_metadata_list (file, NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS, NAUTILUS_METADATA_SUBKEY_COLUMNS, NULL);
2197 gtk_tree_sortable_set_sort_column_id
2198 (GTK_TREE_SORTABLE (FM_LIST_VIEW (view)->details->model),
2199 fm_list_model_get_sort_column_id_from_attribute (FM_LIST_VIEW (view)->details->model,
2200 g_quark_from_string (default_sort_order_auto_value)),
2201 default_sort_reversed_auto_value ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
2203 fm_list_view_set_zoom_level (FM_LIST_VIEW (view), get_default_zoom_level (), FALSE);
2204 set_columns_settings_from_metadata_and_preferences (FM_LIST_VIEW (view));
2207 static void
2208 fm_list_view_scale_font_size (FMListView *view,
2209 NautilusZoomLevel new_level)
2211 GList *l;
2212 static gboolean first_time = TRUE;
2213 static double pango_scale[7];
2214 int medium;
2215 int i;
2217 g_return_if_fail (new_level >= NAUTILUS_ZOOM_LEVEL_SMALLEST &&
2218 new_level <= NAUTILUS_ZOOM_LEVEL_LARGEST);
2220 if (first_time) {
2221 first_time = FALSE;
2222 medium = NAUTILUS_ZOOM_LEVEL_SMALLER;
2223 pango_scale[medium] = PANGO_SCALE_MEDIUM;
2224 for (i = medium; i > NAUTILUS_ZOOM_LEVEL_SMALLEST; i--) {
2225 pango_scale[i - 1] = (1 / 1.2) * pango_scale[i];
2227 for (i = medium; i < NAUTILUS_ZOOM_LEVEL_LARGEST; i++) {
2228 pango_scale[i + 1] = 1.2 * pango_scale[i];
2232 g_object_set (G_OBJECT (view->details->file_name_cell),
2233 "scale", pango_scale[new_level],
2234 NULL);
2235 for (l = view->details->cells; l != NULL; l = l->next) {
2236 g_object_set (G_OBJECT (l->data),
2237 "scale", pango_scale[new_level],
2238 NULL);
2242 static void
2243 fm_list_view_set_zoom_level (FMListView *view,
2244 NautilusZoomLevel new_level,
2245 gboolean always_emit)
2247 int icon_size;
2248 int column, emblem_column;
2250 g_return_if_fail (FM_IS_LIST_VIEW (view));
2251 g_return_if_fail (new_level >= NAUTILUS_ZOOM_LEVEL_SMALLEST &&
2252 new_level <= NAUTILUS_ZOOM_LEVEL_LARGEST);
2254 if (view->details->zoom_level == new_level) {
2255 if (always_emit) {
2256 g_signal_emit_by_name (FM_DIRECTORY_VIEW(view), "zoom_level_changed");
2258 return;
2261 view->details->zoom_level = new_level;
2262 g_signal_emit_by_name (FM_DIRECTORY_VIEW(view), "zoom_level_changed");
2264 nautilus_file_set_integer_metadata
2265 (fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (view)),
2266 NAUTILUS_METADATA_KEY_LIST_VIEW_ZOOM_LEVEL,
2267 get_default_zoom_level (),
2268 new_level);
2270 /* Select correctly scaled icons. */
2271 column = fm_list_model_get_column_id_from_zoom_level (new_level);
2272 emblem_column = fm_list_model_get_emblem_column_id_from_zoom_level (new_level);
2273 gtk_tree_view_column_set_attributes (view->details->file_name_column,
2274 GTK_CELL_RENDERER (view->details->pixbuf_cell),
2275 "pixbuf", column,
2276 "pixbuf_emblem", emblem_column,
2277 NULL);
2279 /* Scale text. */
2280 fm_list_view_scale_font_size (view, new_level);
2282 /* Make all rows the same size. */
2283 icon_size = nautilus_get_icon_size_for_zoom_level (new_level);
2284 gtk_cell_renderer_set_fixed_size (GTK_CELL_RENDERER (view->details->pixbuf_cell),
2285 -1, icon_size);
2287 fm_directory_view_update_menus (FM_DIRECTORY_VIEW (view));
2290 static void
2291 fm_list_view_bump_zoom_level (FMDirectoryView *view, int zoom_increment)
2293 FMListView *list_view;
2294 NautilusZoomLevel new_level;
2296 g_return_if_fail (FM_IS_LIST_VIEW (view));
2298 list_view = FM_LIST_VIEW (view);
2299 new_level = list_view->details->zoom_level + zoom_increment;
2301 if (new_level >= NAUTILUS_ZOOM_LEVEL_SMALLEST &&
2302 new_level <= NAUTILUS_ZOOM_LEVEL_LARGEST) {
2303 fm_list_view_set_zoom_level (list_view, new_level, FALSE);
2307 static NautilusZoomLevel
2308 fm_list_view_get_zoom_level (FMDirectoryView *view)
2310 FMListView *list_view;
2312 g_return_val_if_fail (FM_IS_LIST_VIEW (view), NAUTILUS_ZOOM_LEVEL_STANDARD);
2314 list_view = FM_LIST_VIEW (view);
2316 return list_view->details->zoom_level;
2319 static void
2320 fm_list_view_zoom_to_level (FMDirectoryView *view,
2321 NautilusZoomLevel zoom_level)
2323 FMListView *list_view;
2325 g_return_if_fail (FM_IS_LIST_VIEW (view));
2327 list_view = FM_LIST_VIEW (view);
2329 fm_list_view_set_zoom_level (list_view, zoom_level, FALSE);
2332 static void
2333 fm_list_view_restore_default_zoom_level (FMDirectoryView *view)
2335 FMListView *list_view;
2337 g_return_if_fail (FM_IS_LIST_VIEW (view));
2339 list_view = FM_LIST_VIEW (view);
2341 fm_list_view_set_zoom_level (list_view, get_default_zoom_level (), FALSE);
2344 static gboolean
2345 fm_list_view_can_zoom_in (FMDirectoryView *view)
2347 g_return_val_if_fail (FM_IS_LIST_VIEW (view), FALSE);
2349 return FM_LIST_VIEW (view)->details->zoom_level < NAUTILUS_ZOOM_LEVEL_LARGEST;
2352 static gboolean
2353 fm_list_view_can_zoom_out (FMDirectoryView *view)
2355 g_return_val_if_fail (FM_IS_LIST_VIEW (view), FALSE);
2357 return FM_LIST_VIEW (view)->details->zoom_level > NAUTILUS_ZOOM_LEVEL_SMALLEST;
2360 static void
2361 fm_list_view_start_renaming_file (FMDirectoryView *view,
2362 NautilusFile *file,
2363 gboolean select_all)
2365 FMListView *list_view;
2366 GtkTreeIter iter;
2367 GtkTreePath *path;
2368 GtkEntry *entry;
2369 int start_offset, end_offset;
2371 list_view = FM_LIST_VIEW (view);
2373 /* Don't start renaming if another rename in this listview is
2374 * already in progress. */
2375 if (list_view->details->file_name_column && list_view->details->file_name_column->editable_widget) {
2376 return;
2379 if (!fm_list_model_get_first_iter_for_file (list_view->details->model, file, &iter)) {
2380 return;
2383 /* Freeze updates to the view to prevent losing rename focus when the tree view updates */
2384 fm_directory_view_freeze_updates (FM_DIRECTORY_VIEW (view));
2386 path = gtk_tree_model_get_path (GTK_TREE_MODEL (list_view->details->model), &iter);
2388 /*Make filename-cells editable.*/
2389 g_object_set (G_OBJECT (list_view->details->file_name_cell),
2390 "editable", TRUE,
2391 NULL);
2394 gtk_tree_view_scroll_to_cell (list_view->details->tree_view,
2395 NULL,
2396 list_view->details->file_name_column,
2397 TRUE, 0.0, 0.0);
2398 gtk_tree_view_set_cursor (list_view->details->tree_view,
2399 path,
2400 list_view->details->file_name_column,
2401 TRUE);
2403 entry = GTK_ENTRY (list_view->details->file_name_column->editable_widget);
2405 /* Free a previously allocated original_name */
2406 g_free (list_view->details->original_name);
2408 list_view->details->original_name = g_strdup (gtk_entry_get_text (entry));
2409 if (select_all) {
2410 start_offset = 0;
2411 end_offset = -1;
2412 } else {
2413 eel_filename_get_rename_region (list_view->details->original_name,
2414 &start_offset, &end_offset);
2416 gtk_editable_select_region (GTK_EDITABLE (entry), start_offset, end_offset);
2418 gtk_tree_path_free (path);
2420 nautilus_clipboard_set_up_editable
2421 (GTK_EDITABLE (entry),
2422 fm_directory_view_get_ui_manager (view),
2423 FALSE);
2426 static void
2427 fm_list_view_click_policy_changed (FMDirectoryView *directory_view)
2429 GdkWindow *win;
2430 GdkDisplay *display;
2431 FMListView *view;
2432 GtkTreeIter iter;
2433 GtkTreeView *tree;
2435 view = FM_LIST_VIEW (directory_view);
2437 /* ensure that we unset the hand cursor and refresh underlined rows */
2438 if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_DOUBLE) {
2439 if (view->details->hover_path != NULL) {
2440 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->model),
2441 &iter, view->details->hover_path)) {
2442 gtk_tree_model_row_changed (GTK_TREE_MODEL (view->details->model),
2443 view->details->hover_path, &iter);
2446 gtk_tree_path_free (view->details->hover_path);
2447 view->details->hover_path = NULL;
2450 tree = view->details->tree_view;
2451 if (GTK_WIDGET_REALIZED (GTK_WIDGET (tree))) {
2452 win = GTK_WIDGET (tree)->window;
2453 gdk_window_set_cursor (win, NULL);
2455 display = gtk_widget_get_display (GTK_WIDGET (view));
2456 if (display != NULL) {
2457 gdk_display_flush (display);
2461 if (hand_cursor != NULL) {
2462 gdk_cursor_unref (hand_cursor);
2463 hand_cursor = NULL;
2465 } else if (click_policy_auto_value == NAUTILUS_CLICK_POLICY_SINGLE) {
2466 if (hand_cursor == NULL) {
2467 hand_cursor = gdk_cursor_new(GDK_HAND2);
2472 static void
2473 default_sort_order_changed_callback (gpointer callback_data)
2475 FMListView *list_view;
2477 list_view = FM_LIST_VIEW (callback_data);
2479 set_sort_order_from_metadata_and_preferences (list_view);
2482 static void
2483 default_zoom_level_changed_callback (gpointer callback_data)
2485 FMListView *list_view;
2487 list_view = FM_LIST_VIEW (callback_data);
2489 set_zoom_level_from_metadata_and_preferences (list_view);
2492 static void
2493 default_visible_columns_changed_callback (gpointer callback_data)
2495 FMListView *list_view;
2497 list_view = FM_LIST_VIEW (callback_data);
2499 set_columns_settings_from_metadata_and_preferences (list_view);
2502 static void
2503 default_column_order_changed_callback (gpointer callback_data)
2505 FMListView *list_view;
2507 list_view = FM_LIST_VIEW (callback_data);
2509 set_columns_settings_from_metadata_and_preferences (list_view);
2512 static void
2513 fm_list_view_sort_directories_first_changed (FMDirectoryView *view)
2515 FMListView *list_view;
2517 list_view = FM_LIST_VIEW (view);
2519 fm_list_model_set_should_sort_directories_first (list_view->details->model,
2520 fm_directory_view_should_sort_directories_first (view));
2523 static int
2524 fm_list_view_compare_files (FMDirectoryView *view, NautilusFile *file1, NautilusFile *file2)
2526 FMListView *list_view;
2528 list_view = FM_LIST_VIEW (view);
2529 return fm_list_model_compare_func (list_view->details->model, file1, file2);
2532 static gboolean
2533 fm_list_view_using_manual_layout (FMDirectoryView *view)
2535 g_return_val_if_fail (FM_IS_LIST_VIEW (view), FALSE);
2537 return FALSE;
2540 static void
2541 fm_list_view_dispose (GObject *object)
2543 FMListView *list_view;
2544 GtkUIManager *ui_manager;
2546 list_view = FM_LIST_VIEW (object);
2548 if (list_view->details->model) {
2549 stop_cell_editing (list_view);
2550 g_object_unref (list_view->details->model);
2551 list_view->details->model = NULL;
2554 if (list_view->details->drag_dest) {
2555 g_object_unref (list_view->details->drag_dest);
2556 list_view->details->drag_dest = NULL;
2559 if (list_view->details->renaming_file_activate_timeout != 0) {
2560 g_source_remove (list_view->details->renaming_file_activate_timeout);
2561 list_view->details->renaming_file_activate_timeout = 0;
2564 ui_manager = fm_directory_view_get_ui_manager (FM_DIRECTORY_VIEW (list_view));
2565 if (ui_manager != NULL) {
2566 nautilus_ui_unmerge_ui (ui_manager,
2567 &list_view->details->list_merge_id,
2568 &list_view->details->list_action_group);
2571 G_OBJECT_CLASS (parent_class)->dispose (object);
2574 static void
2575 fm_list_view_finalize (GObject *object)
2577 FMListView *list_view;
2579 list_view = FM_LIST_VIEW (object);
2581 g_free (list_view->details->original_name);
2582 list_view->details->original_name = NULL;
2584 if (list_view->details->double_click_path[0]) {
2585 gtk_tree_path_free (list_view->details->double_click_path[0]);
2587 if (list_view->details->double_click_path[1]) {
2588 gtk_tree_path_free (list_view->details->double_click_path[1]);
2590 if (list_view->details->new_selection_path) {
2591 gtk_tree_path_free (list_view->details->new_selection_path);
2594 g_list_free (list_view->details->cells);
2595 g_hash_table_destroy (list_view->details->columns);
2597 if (list_view->details->hover_path != NULL) {
2598 gtk_tree_path_free (list_view->details->hover_path);
2601 if (list_view->details->column_editor != NULL) {
2602 gtk_widget_destroy (list_view->details->column_editor);
2605 g_free (list_view->details);
2607 G_OBJECT_CLASS (parent_class)->finalize (object);
2610 static void
2611 fm_list_view_emblems_changed (FMDirectoryView *directory_view)
2613 g_assert (FM_IS_LIST_VIEW (directory_view));
2615 /* FIXME: This needs to update the emblems of the icons, since
2616 * relative emblems may have changed.
2620 static char *
2621 fm_list_view_get_first_visible_file (NautilusView *view)
2623 NautilusFile *file;
2624 GtkTreePath *path;
2625 GtkTreeIter iter;
2626 FMListView *list_view;
2628 list_view = FM_LIST_VIEW (view);
2630 if (gtk_tree_view_get_path_at_pos (list_view->details->tree_view,
2631 0, 0,
2632 &path, NULL, NULL, NULL)) {
2633 gtk_tree_model_get_iter (GTK_TREE_MODEL (list_view->details->model),
2634 &iter, path);
2636 gtk_tree_path_free (path);
2638 gtk_tree_model_get (GTK_TREE_MODEL (list_view->details->model),
2639 &iter,
2640 FM_LIST_MODEL_FILE_COLUMN, &file,
2641 -1);
2642 if (file) {
2643 char *uri;
2645 uri = nautilus_file_get_uri (file);
2647 nautilus_file_unref (file);
2649 return uri;
2653 return NULL;
2656 static void
2657 fm_list_view_scroll_to_file (FMListView *view,
2658 NautilusFile *file)
2660 GtkTreePath *path;
2661 GtkTreeIter iter;
2663 if (!fm_list_model_get_first_iter_for_file (view->details->model, file, &iter)) {
2664 return;
2667 path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->model), &iter);
2669 gtk_tree_view_scroll_to_cell (view->details->tree_view,
2670 path, NULL,
2671 TRUE, 0.0, 0.0);
2673 gtk_tree_path_free (path);
2676 static void
2677 list_view_scroll_to_file (NautilusView *view,
2678 const char *uri)
2680 NautilusFile *file;
2682 if (uri != NULL) {
2683 /* Only if existing, since we don't want to add the file to
2684 the directory if it has been removed since then */
2685 file = nautilus_file_get_existing_by_uri (uri);
2686 if (file != NULL) {
2687 fm_list_view_scroll_to_file (FM_LIST_VIEW (view), file);
2688 nautilus_file_unref (file);
2693 static void
2694 fm_list_view_grab_focus (NautilusView *view)
2696 gtk_widget_grab_focus (GTK_WIDGET (FM_LIST_VIEW (view)->details->tree_view));
2700 static void
2701 fm_list_view_class_init (FMListViewClass *class)
2703 FMDirectoryViewClass *fm_directory_view_class;
2705 fm_directory_view_class = FM_DIRECTORY_VIEW_CLASS (class);
2707 G_OBJECT_CLASS (class)->dispose = fm_list_view_dispose;
2708 G_OBJECT_CLASS (class)->finalize = fm_list_view_finalize;
2710 fm_directory_view_class->add_file = fm_list_view_add_file;
2711 fm_directory_view_class->begin_loading = fm_list_view_begin_loading;
2712 fm_directory_view_class->bump_zoom_level = fm_list_view_bump_zoom_level;
2713 fm_directory_view_class->can_zoom_in = fm_list_view_can_zoom_in;
2714 fm_directory_view_class->can_zoom_out = fm_list_view_can_zoom_out;
2715 fm_directory_view_class->click_policy_changed = fm_list_view_click_policy_changed;
2716 fm_directory_view_class->clear = fm_list_view_clear;
2717 fm_directory_view_class->file_changed = fm_list_view_file_changed;
2718 fm_directory_view_class->get_background_widget = fm_list_view_get_background_widget;
2719 fm_directory_view_class->get_selection = fm_list_view_get_selection;
2720 fm_directory_view_class->get_selection_for_file_transfer = fm_list_view_get_selection_for_file_transfer;
2721 fm_directory_view_class->get_item_count = fm_list_view_get_item_count;
2722 fm_directory_view_class->is_empty = fm_list_view_is_empty;
2723 fm_directory_view_class->remove_file = fm_list_view_remove_file;
2724 fm_directory_view_class->merge_menus = fm_list_view_merge_menus;
2725 fm_directory_view_class->update_menus = fm_list_view_update_menus;
2726 fm_directory_view_class->reset_to_defaults = fm_list_view_reset_to_defaults;
2727 fm_directory_view_class->restore_default_zoom_level = fm_list_view_restore_default_zoom_level;
2728 fm_directory_view_class->reveal_selection = fm_list_view_reveal_selection;
2729 fm_directory_view_class->select_all = fm_list_view_select_all;
2730 fm_directory_view_class->set_selection = fm_list_view_set_selection;
2731 fm_directory_view_class->invert_selection = fm_list_view_invert_selection;
2732 fm_directory_view_class->compare_files = fm_list_view_compare_files;
2733 fm_directory_view_class->sort_directories_first_changed = fm_list_view_sort_directories_first_changed;
2734 fm_directory_view_class->start_renaming_file = fm_list_view_start_renaming_file;
2735 fm_directory_view_class->get_zoom_level = fm_list_view_get_zoom_level;
2736 fm_directory_view_class->zoom_to_level = fm_list_view_zoom_to_level;
2737 fm_directory_view_class->emblems_changed = fm_list_view_emblems_changed;
2738 fm_directory_view_class->end_file_changes = fm_list_view_end_file_changes;
2739 fm_directory_view_class->using_manual_layout = fm_list_view_using_manual_layout;
2741 eel_preferences_add_auto_enum (NAUTILUS_PREFERENCES_CLICK_POLICY,
2742 &click_policy_auto_value);
2743 eel_preferences_add_auto_string (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_ORDER,
2744 (const char **) &default_sort_order_auto_value);
2745 eel_preferences_add_auto_boolean (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_IN_REVERSE_ORDER,
2746 &default_sort_reversed_auto_value);
2747 eel_preferences_add_auto_enum (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
2748 (int *) &default_zoom_level_auto_value);
2749 eel_preferences_add_auto_string_array (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS,
2750 &default_visible_columns_auto_value);
2751 eel_preferences_add_auto_string_array (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER,
2752 &default_column_order_auto_value);
2755 static const char *
2756 fm_list_view_get_id (NautilusView *view)
2758 return FM_LIST_VIEW_ID;
2762 static void
2763 fm_list_view_iface_init (NautilusViewIface *iface)
2765 fm_directory_view_init_view_iface (iface);
2767 iface->get_view_id = fm_list_view_get_id;
2768 iface->get_first_visible_file = fm_list_view_get_first_visible_file;
2769 iface->scroll_to_file = list_view_scroll_to_file;
2770 iface->get_title = NULL;
2771 iface->grab_focus = fm_list_view_grab_focus;
2775 static void
2776 fm_list_view_init (FMListView *list_view)
2778 list_view->details = g_new0 (FMListViewDetails, 1);
2780 create_and_set_up_tree_view (list_view);
2782 eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_ORDER,
2783 default_sort_order_changed_callback,
2784 list_view, G_OBJECT (list_view));
2785 eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_SORT_IN_REVERSE_ORDER,
2786 default_sort_order_changed_callback,
2787 list_view, G_OBJECT (list_view));
2788 eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_ZOOM_LEVEL,
2789 default_zoom_level_changed_callback,
2790 list_view, G_OBJECT (list_view));
2791 eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_VISIBLE_COLUMNS,
2792 default_visible_columns_changed_callback,
2793 list_view, G_OBJECT (list_view));
2794 eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_LIST_VIEW_DEFAULT_COLUMN_ORDER,
2795 default_column_order_changed_callback,
2796 list_view, G_OBJECT (list_view));
2798 fm_list_view_click_policy_changed (FM_DIRECTORY_VIEW (list_view));
2800 fm_list_view_sort_directories_first_changed (FM_DIRECTORY_VIEW (list_view));
2802 /* ensure that the zoom level is always set in begin_loading */
2803 list_view->details->zoom_level = NAUTILUS_ZOOM_LEVEL_SMALLEST - 1;
2805 list_view->details->hover_path = NULL;
2808 static NautilusView *
2809 fm_list_view_create (NautilusWindowInfo *window)
2811 FMListView *view;
2813 view = g_object_new (FM_TYPE_LIST_VIEW, "window", window, NULL);
2814 g_object_ref (view);
2815 gtk_object_sink (GTK_OBJECT (view));
2816 return NAUTILUS_VIEW (view);
2819 static gboolean
2820 fm_list_view_supports_uri (const char *uri,
2821 GFileType file_type,
2822 const char *mime_type)
2824 if (file_type == G_FILE_TYPE_DIRECTORY) {
2825 return TRUE;
2827 if (strcmp (mime_type, NAUTILUS_SAVED_SEARCH_MIMETYPE) == 0){
2828 return TRUE;
2830 if (g_str_has_prefix (uri, "trash:")) {
2831 return TRUE;
2833 if (g_str_has_prefix (uri, EEL_SEARCH_URI)) {
2834 return TRUE;
2837 return FALSE;
2840 static NautilusViewInfo fm_list_view = {
2841 FM_LIST_VIEW_ID,
2842 /* translators: this is used in the view selection dropdown
2843 * of navigation windows and in the preferences dialog */
2844 N_("List View"),
2845 /* translators: this is used in the view menu */
2846 N_("_List"),
2847 N_("The list view encountered an error."),
2848 N_("The list view encountered an error while starting up."),
2849 N_("Display this location with the list view."),
2850 fm_list_view_create,
2851 fm_list_view_supports_uri
2854 void
2855 fm_list_view_register (void)
2857 fm_list_view.view_combo_label = _(fm_list_view.view_combo_label);
2858 fm_list_view.view_menu_label_with_mnemonic = _(fm_list_view.view_menu_label_with_mnemonic);
2859 fm_list_view.error_label = _(fm_list_view.error_label);
2860 fm_list_view.startup_error_label = _(fm_list_view.startup_error_label);
2861 fm_list_view.display_location_label = _(fm_list_view.display_location_label);
2863 nautilus_view_factory_register (&fm_list_view);