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>
29 #include "fm-list-view.h"
32 #include "fm-error-reporting.h"
33 #include "fm-list-model.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
;
75 GtkActionGroup
*list_action_group
;
78 GtkTreeViewColumn
*file_name_column
;
79 int file_name_column_num
;
81 GtkCellRendererPixbuf
*pixbuf_cell
;
82 GtkCellRendererText
*file_name_cell
;
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
;
99 gboolean drag_started
;
101 gboolean ignore_button_release
;
103 gboolean row_selected_on_button_down
;
105 gboolean menus_ready
;
108 GtkWidget
*column_editor
;
112 NautilusFile
*renaming_file
;
113 gboolean rename_done
;
114 guint renaming_file_activate_timeout
;
117 struct SelectionForeachData
{
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
,
154 static void fm_list_view_iface_init (NautilusViewIface
*iface
);
155 static void fm_list_view_rename_callback (NautilusFile
*file
,
156 GFile
*result_location
,
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
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? */
181 tree_selection_foreach_set_boolean (GtkTreeModel
*model
,
184 gpointer callback_data
)
186 * (gboolean
*) callback_data
= TRUE
;
190 tree_selection_not_empty (GtkTreeSelection
*selection
)
195 gtk_tree_selection_selected_foreach (selection
,
196 tree_selection_foreach_set_boolean
,
202 tree_view_has_selection (GtkTreeView
*view
)
204 return tree_selection_not_empty (gtk_tree_view_get_selection (view
));
208 activate_selected_items (FMListView
*view
)
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
);
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
),
232 NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE
,
234 nautilus_file_list_free (file_list
);
239 activate_selected_items_alternate (FMListView
*view
,
246 nautilus_file_ref (file
);
247 file_list
= g_list_prepend (NULL
, file
);
249 file_list
= fm_list_view_get_selection (FM_DIRECTORY_VIEW (view
));
251 fm_directory_view_activate_files (FM_DIRECTORY_VIEW (view
),
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
);
261 button_event_modifies_selection (GdkEventButton
*event
)
263 return (event
->state
& (GDK_CONTROL_MASK
| GDK_SHIFT_MASK
)) != 0;
267 fm_list_view_did_not_drag (FMListView
*view
,
268 GdkEventButton
*event
)
270 GtkTreeView
*tree_view
;
271 GtkTreeSelection
*selection
;
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
);
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
);
305 drag_data_get_callback (GtkWidget
*widget
,
306 GdkDragContext
*context
,
307 GtkSelectionData
*selection_data
,
311 GtkTreeView
*tree_view
;
315 tree_view
= GTK_TREE_VIEW (widget
);
317 model
= gtk_tree_view_get_model (tree_view
);
323 ref_list
= g_object_get_data (G_OBJECT (context
), "drag-info");
325 if (ref_list
== NULL
) {
329 if (EGG_IS_TREE_MULTI_DRAG_SOURCE (model
))
331 egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model
),
338 filtered_selection_foreach (GtkTreeModel
*model
,
343 struct SelectionForeachData
*selection_data
;
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
354 while (gtk_tree_model_iter_parent (model
, &parent
, &child
)) {
355 if (gtk_tree_selection_iter_is_selected (selection_data
->selection
,
362 selection_data
->list
= g_list_prepend (selection_data
->list
,
363 gtk_tree_row_reference_new (model
, path
));
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
,
377 return g_list_reverse (selection_data
.list
);
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
);
388 stop_drag_check (FMListView
*view
)
390 view
->details
->drag_button
= 0;
394 get_drag_pixbuf (FMListView
*view
)
400 GdkRectangle cell_area
;
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
),
415 gtk_tree_view_get_cell_area (view
->details
->tree_view
,
417 view
->details
->file_name_column
,
420 gtk_tree_path_free (path
);
427 drag_begin_callback (GtkWidget
*widget
,
428 GdkDragContext
*context
,
434 pixbuf
= get_drag_pixbuf (view
);
436 gtk_drag_set_icon_pixbuf (context
,
439 g_object_unref (pixbuf
);
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
),
451 (GDestroyNotify
)ref_list_free
);
455 motion_notify_callback (GtkWidget
*widget
,
456 GdkEventMotion
*event
,
457 gpointer callback_data
)
460 GdkDragContext
*context
;
462 view
= FM_LIST_VIEW (callback_data
);
464 if (event
->window
!= gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget
))) {
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
),
474 &view
->details
->hover_path
,
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
);
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
;
495 fm_list_model_get_drag_types (&drag_types
,
498 source_target_list
= gtk_target_list_new (drag_types
,
502 if (gtk_drag_check_threshold (widget
,
503 view
->details
->drag_x
,
504 view
->details
->drag_y
,
507 context
= gtk_drag_begin
510 GDK_ACTION_MOVE
| GDK_ACTION_COPY
| GDK_ACTION_LINK
| GDK_ACTION_ASK
,
511 view
->details
->drag_button
,
521 leave_notify_callback (GtkWidget
*widget
,
522 GdkEventCrossing
*event
,
523 gpointer callback_data
)
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
;
539 enter_notify_callback (GtkWidget
*widget
,
540 GdkEventCrossing
*event
,
541 gpointer callback_data
)
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
),
554 &view
->details
->hover_path
,
557 if (view
->details
->hover_path
!= NULL
) {
558 gdk_window_set_cursor (widget
->window
, hand_cursor
);
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
);
571 fm_directory_view_pop_up_background_context_menu (FM_DIRECTORY_VIEW (view
), event
);
576 button_press_callback (GtkWidget
*widget
, GdkEventButton
*event
, gpointer callback_data
)
579 GtkTreeView
*tree_view
;
581 gboolean call_parent
;
583 GtkTreeSelection
*selection
;
584 GtkWidgetClass
*tree_view_class
;
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
)) {
601 fm_list_model_set_drag_view
602 (FM_LIST_MODEL (gtk_tree_view_get_model (tree_view
)),
606 g_object_get (G_OBJECT (gtk_widget_get_settings (widget
)),
607 "gtk-double-click-time", &double_click_time
,
610 /* Determine click count */
611 current_time
= eel_get_system_time ();
612 if (current_time
- last_click_time
< double_click_time
* 1000) {
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) {
626 view
->details
->ignore_button_release
= 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) {
657 file
= fm_list_model_file_for_path (view
->details
->model
, path
);
659 activate_selected_items_alternate (view
, file
);
660 nautilus_file_unref (file
);
664 tree_view_class
->button_press_event (widget
, event
);
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
)) {
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
,
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) {
694 gtk_tree_selection_select_path (selection
, path
);
696 view
->details
->ignore_button_release
= on_expander
;
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
);
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 */
744 button_release_callback (GtkWidget
*widget
,
745 GdkEventButton
*event
,
746 gpointer callback_data
)
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
);
763 popup_menu_callback (GtkWidget
*widget
, gpointer callback_data
)
767 view
= FM_LIST_VIEW (callback_data
);
769 do_popup_menu (widget
, view
, NULL
);
775 subdirectory_done_loading_callback (NautilusDirectory
*directory
, FMListView
*view
)
777 fm_list_model_subdirectory_done_loading (view
->details
->model
, directory
);
781 row_expanded_callback (GtkTreeView
*treeview
, GtkTreeIter
*iter
, GtkTreePath
*path
, gpointer callback_data
)
784 NautilusDirectory
*directory
;
786 view
= FM_LIST_VIEW (callback_data
);
788 if (fm_list_model_load_subdirectory (view
->details
->model
, path
, &directory
)) {
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
)),
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
,
804 g_signal_connect_object (directory
, "done_loading",
805 G_CALLBACK (subdirectory_done_loading_callback
),
809 nautilus_directory_unref (directory
);
813 struct UnloadDelayData
{
815 NautilusDirectory
*directory
;
820 unload_file_timeout (gpointer data
)
822 struct UnloadDelayData
*unload_data
= data
;
827 if (unload_data
->view
!= NULL
) {
828 model
= unload_data
->view
->details
->model
;
829 if (fm_list_model_get_tree_iter_from_file (model
,
831 unload_data
->directory
,
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
,
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
);
853 row_collapsed_callback (GtkTreeView
*treeview
, GtkTreeIter
*iter
, GtkTreePath
*path
, gpointer callback_data
)
857 NautilusDirectory
*directory
;
859 struct UnloadDelayData
*unload_data
;
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
,
871 if (gtk_tree_model_iter_parent (model
, &parent
, iter
)) {
872 gtk_tree_model_get (model
, &parent
,
873 FM_LIST_MODEL_SUBDIRECTORY_COLUMN
, &directory
,
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
)),
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
,
898 row_activated_callback (GtkTreeView
*treeview
, GtkTreePath
*path
,
899 GtkTreeViewColumn
*column
, FMListView
*view
)
901 activate_selected_items (view
);
905 subdirectory_unloaded_callback (FMListModel
*model
,
906 NautilusDirectory
*directory
,
907 gpointer callback_data
)
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
),
919 fm_directory_view_remove_subdirectory (FM_DIRECTORY_VIEW (view
), directory
);
923 key_press_callback (GtkWidget
*widget
, GdkEventKey
*event
, gpointer callback_data
)
925 FMDirectoryView
*view
;
926 GdkEventButton button_event
= { 0 };
929 view
= FM_DIRECTORY_VIEW (callback_data
);
932 switch (event
->keyval
) {
934 if (event
->state
& GDK_CONTROL_MASK
) {
935 fm_directory_view_pop_up_background_context_menu (view
, &button_event
);
940 if (event
->state
& GDK_CONTROL_MASK
) {
944 if (!GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (FM_LIST_VIEW (view
)->details
->tree_view
))) {
948 if ((event
->state
& GDK_SHIFT_MASK
) != 0) {
949 activate_selected_items_alternate (FM_LIST_VIEW (view
), NULL
);
951 activate_selected_items (FM_LIST_VIEW (view
));
957 if ((event
->state
& GDK_SHIFT_MASK
) != 0) {
958 activate_selected_items_alternate (FM_LIST_VIEW (view
), NULL
);
960 activate_selected_items (FM_LIST_VIEW (view
));
973 fm_list_view_reveal_selection (FMDirectoryView
*view
)
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
;
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
);
1003 sort_column_changed_callback (GtkTreeSortable
*sortable
,
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
));
1033 cell_renderer_editing_canceled (GtkCellRendererText
*cell
,
1036 fm_directory_view_unfreeze_updates (FM_DIRECTORY_VIEW (view
));
1040 cell_renderer_edited (GtkCellRendererText
*cell
,
1041 const char *path_str
,
1042 const char *new_text
,
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
),
1059 path
= gtk_tree_path_new_from_string (path_str
);
1061 gtk_tree_model_get_iter (GTK_TREE_MODEL (view
->details
->model
),
1064 gtk_tree_path_free (path
);
1066 gtk_tree_model_get (GTK_TREE_MODEL (view
->details
->model
),
1068 FM_LIST_MODEL_FILE_COLUMN
, &file
,
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
),
1087 fm_directory_view_unfreeze_updates (FM_DIRECTORY_VIEW (view
));
1091 get_root_uri_callback (NautilusTreeViewDragDest
*dest
,
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
,
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 */
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
);
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
);
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
);
1141 move_copy_items_callback (NautilusTreeViewDragDest
*dest
,
1142 const GList
*item_uris
,
1143 const char *target_uri
,
1150 fm_directory_view_move_copy_items (item_uris
,
1155 FM_DIRECTORY_VIEW (user_data
));
1159 apply_columns_settings (FMListView
*list_view
,
1160 char **column_order
,
1161 char **visible_columns
)
1164 GList
*old_view_columns
, *view_columns
;
1165 GHashTable
*visible_columns_hash
;
1166 GtkTreeViewColumn
*prev_view_column
;
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
,
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
) {
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
);
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
);
1239 filename_cell_data_func (GtkTreeViewColumn
*column
,
1240 GtkCellRenderer
*renderer
,
1241 GtkTreeModel
*model
,
1247 PangoUnderline underline
;
1249 gtk_tree_model_get (model
, iter
,
1250 view
->details
->file_name_column_num
, &text
,
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
;
1260 underline
= PANGO_UNDERLINE_SINGLE
;
1263 gtk_tree_path_free (path
);
1265 underline
= PANGO_UNDERLINE_NONE
;
1268 g_object_set (G_OBJECT (renderer
),
1270 "underline", underline
,
1276 create_and_set_up_tree_view (FMListView
*view
)
1278 GtkCellRenderer
*cell
;
1279 GtkTreeViewColumn
*column
;
1280 GtkBindingSet
*binding_set
;
1282 GList
*nautilus_columns
;
1285 view
->details
->tree_view
= GTK_TREE_VIEW (gtk_tree_view_new ());
1286 view
->details
->columns
= g_hash_table_new_full (g_str_hash
,
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
,
1301 G_CALLBACK (get_root_uri_callback
),
1303 g_signal_connect_object (view
->details
->drag_dest
,
1304 "get_file_for_path",
1305 G_CALLBACK (get_file_for_path_callback
),
1307 g_signal_connect_object (view
->details
->drag_dest
,
1309 G_CALLBACK (move_copy_items_callback
),
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
),
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
;
1371 nautilus_column
= NAUTILUS_COLUMN (l
->data
);
1373 g_object_get (nautilus_column
,
1376 "xalign", &xalign
, NULL
);
1378 column_num
= fm_list_model_add_column (view
->details
->model
,
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
,
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
,
1406 "pixbuf", FM_LIST_MODEL_SMALLEST_ICON_COLUMN
,
1407 "pixbuf_emblem", FM_LIST_MODEL_SMALLEST_EMBLEM_COLUMN
,
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
,
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
,
1424 column
= gtk_tree_view_column_new_with_attributes (label
,
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
,
1435 gtk_tree_view_column_set_resizable (column
, TRUE
);
1436 gtk_tree_view_column_set_visible (column
, TRUE
);
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"));
1459 fm_list_view_add_file (FMDirectoryView
*view
, NautilusFile
*file
, NautilusDirectory
*directory
)
1463 model
= FM_LIST_VIEW (view
)->details
->model
;
1464 fm_list_model_add_file (model
, file
, directory
);
1468 get_visible_columns (FMListView
*list_view
)
1471 GList
*visible_columns
;
1476 file
= fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view
));
1478 visible_columns
= nautilus_file_get_metadata_list
1480 NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS
,
1481 NAUTILUS_METADATA_SUBKEY_COLUMNS
);
1483 if (visible_columns
) {
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
);
1500 get_column_order (FMListView
*list_view
)
1503 GList
*column_order
;
1508 file
= fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (list_view
));
1510 column_order
= nautilus_file_get_metadata_list
1512 NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER
,
1513 NAUTILUS_METADATA_SUBKEY_COLUMNS
);
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
);
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
);
1547 set_sort_order_from_metadata_and_preferences (FMListView
*list_view
)
1549 char *sort_attribute
;
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
,
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) {
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
),
1573 sort_reversed
? GTK_SORT_DESCENDING
: GTK_SORT_ASCENDING
);
1577 list_view_changed_foreach (GtkTreeModel
*model
,
1582 gtk_tree_model_row_changed (model
, path
, iter
);
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
;
1601 set_zoom_level_from_metadata_and_preferences (FMListView
*list_view
)
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
);
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
);
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
);
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
);
1662 fm_list_view_rename_callback (NautilusFile
*file
,
1663 GFile
*result_location
,
1665 gpointer callback_data
)
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
);
1689 fm_list_view_file_changed (FMDirectoryView
*view
, NautilusFile
*file
, NautilusDirectory
*directory
)
1691 FMListView
*listview
;
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
,
1711 gtk_tree_path_free (file_path
);
1714 nautilus_file_unref (listview
->details
->renaming_file
);
1715 listview
->details
->renaming_file
= NULL
;
1720 fm_list_view_get_background_widget (FMDirectoryView
*view
)
1722 return GTK_WIDGET (view
);
1726 fm_list_view_get_selection_foreach_func (GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
1733 gtk_tree_model_get (model
, iter
,
1734 FM_LIST_MODEL_FILE_COLUMN
, &file
,
1738 (* list
) = g_list_prepend ((* list
), file
);
1743 fm_list_view_get_selection (FMDirectoryView
*view
)
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
);
1756 fm_list_view_get_selection_for_file_transfer_foreach_func (GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
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
,
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
1774 while (gtk_tree_model_iter_parent (model
, &parent
, &child
)) {
1775 if (gtk_tree_selection_iter_is_selected (selection_data
->selection
,
1782 nautilus_file_ref (file
);
1783 selection_data
->list
= g_list_prepend (selection_data
->list
, file
);
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
);
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
);
1814 fm_list_view_is_empty (FMDirectoryView
*view
)
1816 return fm_list_model_is_empty (FM_LIST_VIEW (view
)->details
->model
);
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
,
1830 gtk_tree_path_free (list_view
->details
->new_selection_path
);
1831 list_view
->details
->new_selection_path
= NULL
;
1836 fm_list_view_remove_file (FMDirectoryView
*view
, NautilusFile
*file
, NautilusDirectory
*directory
)
1839 GtkTreePath
*file_path
;
1841 GtkTreeIter temp_iter
;
1842 GtkTreeRowReference
* row_reference
;
1843 FMListView
*list_view
;
1844 GtkTreeModel
* tree_model
;
1845 GtkTreeSelection
*selection
;
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.
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
);
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
);
1895 fm_list_view_set_selection (FMDirectoryView
*view
, GList
*selection
)
1897 FMListView
*list_view
;
1898 GtkTreeSelection
*tree_selection
;
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
) {
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
);
1925 fm_list_view_invert_selection (FMDirectoryView
*view
)
1927 FMListView
*list_view
;
1928 GtkTreeSelection
*tree_selection
;
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
) {
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
);
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
));
1968 column_editor_response_callback (GtkWidget
*dialog
,
1972 gtk_widget_destroy (GTK_WIDGET (dialog
));
1976 column_chooser_changed_callback (NautilusColumnChooser
*chooser
,
1980 char **visible_columns
;
1981 char **column_order
;
1985 file
= fm_directory_view_get_directory_as_file (FM_DIRECTORY_VIEW (view
));
1987 nautilus_column_chooser_get_settings (chooser
,
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
,
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
,
2013 g_strfreev (visible_columns
);
2014 g_strfreev (column_order
);
2016 set_columns_settings_from_metadata_and_preferences (view
);
2020 column_chooser_set_from_settings (NautilusColumnChooser
*chooser
,
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
,
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
);
2044 column_chooser_use_default_callback (NautilusColumnChooser
*chooser
,
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
);
2060 create_column_editor (FMListView
*view
)
2065 GtkWidget
*column_chooser
;
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
);
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
,
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
),
2103 g_signal_connect (column_chooser
, "use_default",
2104 G_CALLBACK (column_chooser_use_default_callback
),
2107 column_chooser_set_from_settings
2108 (NAUTILUS_COLUMN_CHOOSER (column_chooser
), view
);
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
));
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
) },
2139 fm_list_view_merge_menus (FMDirectoryView
*view
)
2141 FMListView
*list_view
;
2142 GtkUIManager
*ui_manager
;
2143 GtkActionGroup
*action_group
;
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
),
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
;
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
) {
2180 EEL_CALL_PARENT (FM_DIRECTORY_VIEW_CLASS
, update_menus
, (view
));
2183 /* Reset sort criteria and zoom level to match defaults */
2185 fm_list_view_reset_to_defaults (FMDirectoryView
*view
)
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
));
2208 fm_list_view_scale_font_size (FMListView
*view
,
2209 NautilusZoomLevel new_level
)
2212 static gboolean first_time
= TRUE
;
2213 static double pango_scale
[7];
2217 g_return_if_fail (new_level
>= NAUTILUS_ZOOM_LEVEL_SMALLEST
&&
2218 new_level
<= NAUTILUS_ZOOM_LEVEL_LARGEST
);
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
],
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
],
2243 fm_list_view_set_zoom_level (FMListView
*view
,
2244 NautilusZoomLevel new_level
,
2245 gboolean always_emit
)
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
) {
2256 g_signal_emit_by_name (FM_DIRECTORY_VIEW(view
), "zoom_level_changed");
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 (),
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
),
2276 "pixbuf_emblem", emblem_column
,
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
),
2287 fm_directory_view_update_menus (FM_DIRECTORY_VIEW (view
));
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
;
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
);
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
);
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
;
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
;
2361 fm_list_view_start_renaming_file (FMDirectoryView
*view
,
2363 gboolean select_all
)
2365 FMListView
*list_view
;
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
) {
2379 if (!fm_list_model_get_first_iter_for_file (list_view
->details
->model
, file
, &iter
)) {
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
),
2394 gtk_tree_view_scroll_to_cell (list_view
->details
->tree_view
,
2396 list_view
->details
->file_name_column
,
2398 gtk_tree_view_set_cursor (list_view
->details
->tree_view
,
2400 list_view
->details
->file_name_column
,
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
));
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
),
2427 fm_list_view_click_policy_changed (FMDirectoryView
*directory_view
)
2430 GdkDisplay
*display
;
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
);
2465 } else if (click_policy_auto_value
== NAUTILUS_CLICK_POLICY_SINGLE
) {
2466 if (hand_cursor
== NULL
) {
2467 hand_cursor
= gdk_cursor_new(GDK_HAND2
);
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
);
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
);
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
);
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
);
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
));
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
);
2533 fm_list_view_using_manual_layout (FMDirectoryView
*view
)
2535 g_return_val_if_fail (FM_IS_LIST_VIEW (view
), FALSE
);
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
);
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
);
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.
2621 fm_list_view_get_first_visible_file (NautilusView
*view
)
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
,
2632 &path
, NULL
, NULL
, NULL
)) {
2633 gtk_tree_model_get_iter (GTK_TREE_MODEL (list_view
->details
->model
),
2636 gtk_tree_path_free (path
);
2638 gtk_tree_model_get (GTK_TREE_MODEL (list_view
->details
->model
),
2640 FM_LIST_MODEL_FILE_COLUMN
, &file
,
2645 uri
= nautilus_file_get_uri (file
);
2647 nautilus_file_unref (file
);
2657 fm_list_view_scroll_to_file (FMListView
*view
,
2663 if (!fm_list_model_get_first_iter_for_file (view
->details
->model
, file
, &iter
)) {
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
,
2673 gtk_tree_path_free (path
);
2677 list_view_scroll_to_file (NautilusView
*view
,
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
);
2687 fm_list_view_scroll_to_file (FM_LIST_VIEW (view
), file
);
2688 nautilus_file_unref (file
);
2694 fm_list_view_grab_focus (NautilusView
*view
)
2696 gtk_widget_grab_focus (GTK_WIDGET (FM_LIST_VIEW (view
)->details
->tree_view
));
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
);
2756 fm_list_view_get_id (NautilusView
*view
)
2758 return FM_LIST_VIEW_ID
;
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
;
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
)
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
);
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
) {
2827 if (strcmp (mime_type
, NAUTILUS_SAVED_SEARCH_MIMETYPE
) == 0){
2830 if (g_str_has_prefix (uri
, "trash:")) {
2833 if (g_str_has_prefix (uri
, EEL_SEARCH_URI
)) {
2840 static NautilusViewInfo fm_list_view
= {
2842 /* translators: this is used in the view selection dropdown
2843 * of navigation windows and in the preferences dialog */
2845 /* translators: this is used in the view menu */
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
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
);