/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* * Copyright (C) 2000, 2001 Eazel, Inc * Copyright (C) 2002 Anders Carlsson * Copyright (C) 2002 Darin Adler * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, * Boston, MA 02110-1335, USA. * * Authors: * Maciej Stachowiak * Anders Carlsson * Darin Adler */ /* fm-tree-view.c - tree sidebar panel */ #include #include "nemo-tree-sidebar.h" #include "nemo-tree-sidebar-model.h" #include "nemo-properties-window.h" #include "nemo-window-slot.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_FLAG NEMO_DEBUG_LIST_VIEW #include typedef struct { GObject parent; } FMTreeViewProvider; typedef struct { GObjectClass parent; } FMTreeViewProviderClass; struct FMTreeViewDetails { NemoWindow *window; GtkTreeView *tree_widget; GtkTreeModelSort *sort_model; FMTreeModel *child_model; GVolumeMonitor *volume_monitor; NemoFile *activation_file; NemoWindowOpenFlags activation_flags; NemoTreeViewDragDest *drag_dest; char *selection_location; gboolean selecting; guint show_selection_idle_id; gulong clipboard_handler_id; GtkWidget *popup; GtkWidget *popup_open; GtkWidget *popup_open_in_new_window; GtkWidget *popup_open_in_new_tab; GtkWidget *popup_create_folder; GtkWidget *popup_cut; GtkWidget *popup_copy; GtkWidget *popup_paste; GtkWidget *popup_rename; GtkWidget *popup_pin; GtkWidget *popup_unpin; GtkWidget *popup_trash; GtkWidget *popup_delete; GtkWidget *popup_properties; GtkWidget *popup_unmount_separator; GtkWidget *popup_unmount; GtkWidget *popup_eject; GtkWidget *popup_action_separator; NemoFile *popup_file; guint popup_file_idle_handler; guint selection_changed_timer; NemoActionManager *action_manager; guint action_manager_changed_id; GList *action_items; guint hidden_files_changed_id; guint sort_directories_first : 1; }; typedef struct { GList *uris; FMTreeView *view; } PrependURIParameters; typedef struct { NemoAction *action; FMTreeView *view; GtkWidget *item; } ActionPayload; static GdkAtom copied_files_atom; static void fm_tree_view_activate_file (FMTreeView *view, NemoFile *file, NemoWindowOpenFlags flags); static void create_popup_menu (FMTreeView *view); static void add_action_popup_items (FMTreeView *view); G_DEFINE_TYPE (FMTreeView, fm_tree_view, GTK_TYPE_SCROLLED_WINDOW) #define parent_class fm_tree_view_parent_class static void notify_clipboard_info (NemoClipboardMonitor *monitor, NemoClipboardInfo *info, FMTreeView *view) { if (info != NULL && info->cut) { fm_tree_model_set_highlight_for_files (view->details->child_model, info->files); } else { fm_tree_model_set_highlight_for_files (view->details->child_model, NULL); } } static gboolean show_iter_for_file (FMTreeView *view, NemoFile *file, GtkTreeIter *iter) { GtkTreeModel *model; NemoFile *parent_file; GtkTreeIter parent_iter; GtkTreePath *path, *sort_path; GtkTreeIter cur_iter; if (view->details->child_model == NULL) { return FALSE; } model = GTK_TREE_MODEL (view->details->child_model); /* check if file is visible in the same root as the currently selected folder is */ gtk_tree_view_get_cursor (view->details->tree_widget, &path, NULL); if (path != NULL) { if (gtk_tree_model_get_iter (model, &cur_iter, path) && fm_tree_model_file_get_iter (view->details->child_model, iter, file, &cur_iter)) { gtk_tree_path_free (path); return TRUE; } gtk_tree_path_free (path); } /* check if file is visible at all */ if (fm_tree_model_file_get_iter (view->details->child_model, iter, file, NULL)) { return TRUE; } parent_file = nemo_file_get_parent (file); if (parent_file == NULL) { return FALSE; } if (!show_iter_for_file (view, parent_file, &parent_iter)) { nemo_file_unref (parent_file); return FALSE; } nemo_file_unref (parent_file); if (parent_iter.user_data == NULL || parent_iter.stamp == 0) { return FALSE; } path = gtk_tree_model_get_path (model, &parent_iter); sort_path = gtk_tree_model_sort_convert_child_path_to_path (view->details->sort_model, path); gtk_tree_path_free (path); gtk_tree_view_expand_row (view->details->tree_widget, sort_path, FALSE); gtk_tree_path_free (sort_path); return FALSE; } static void refresh_highlight (FMTreeView *view) { NemoClipboardMonitor *monitor; NemoClipboardInfo *info; monitor = nemo_clipboard_monitor_get (); info = nemo_clipboard_monitor_get_clipboard_info (monitor); notify_clipboard_info (monitor, info, view); } static gboolean show_selection_idle_callback (gpointer callback_data) { FMTreeView *view; NemoFile *file, *old_file; GtkTreeIter iter; GtkTreePath *path, *sort_path; view = FM_TREE_VIEW (callback_data); view->details->show_selection_idle_id = 0; file = nemo_file_get_by_uri (view->details->selection_location); if (file == NULL) { return FALSE; } if (!nemo_file_is_directory (file)) { old_file = file; file = nemo_file_get_parent (file); nemo_file_unref (old_file); if (file == NULL) { return FALSE; } } view->details->selecting = TRUE; if (!show_iter_for_file (view, file, &iter)) { nemo_file_unref (file); return FALSE; } view->details->selecting = FALSE; path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->details->child_model), &iter); sort_path = gtk_tree_model_sort_convert_child_path_to_path (view->details->sort_model, path); gtk_tree_path_free (path); gtk_tree_view_set_cursor (view->details->tree_widget, sort_path, NULL, FALSE); if (gtk_widget_get_realized (GTK_WIDGET (view->details->tree_widget))) { gtk_tree_view_scroll_to_cell (view->details->tree_widget, sort_path, NULL, FALSE, 0, 0); } gtk_tree_path_free (sort_path); nemo_file_unref (file); refresh_highlight (view); return FALSE; } static void schedule_show_selection (FMTreeView *view) { if (view->details->show_selection_idle_id == 0) { view->details->show_selection_idle_id = g_idle_add (show_selection_idle_callback, view); } } static void schedule_select_and_show_location (FMTreeView *view, char *location) { if (view->details->selection_location != NULL) { g_free (view->details->selection_location); } view->details->selection_location = g_strdup (location); schedule_show_selection (view); } static void row_loaded_callback (GtkTreeModel *tree_model, GtkTreeIter *iter, FMTreeView *view) { NemoFile *file, *tmp_file, *selection_file; if (view->details->selection_location == NULL || !view->details->selecting || iter->user_data == NULL || iter->stamp == 0) { return; } file = fm_tree_model_iter_get_file (view->details->child_model, iter); if (file == NULL) { return; } if (!nemo_file_is_directory (file)) { nemo_file_unref(file); return; } /* if iter is ancestor of wanted selection_location then update selection */ selection_file = nemo_file_get_by_uri (view->details->selection_location); while (selection_file != NULL) { if (file == selection_file) { nemo_file_unref (file); nemo_file_unref (selection_file); schedule_show_selection (view); return; } tmp_file = nemo_file_get_parent (selection_file); nemo_file_unref (selection_file); selection_file = tmp_file; } nemo_file_unref (file); } static NemoFile * sort_model_iter_to_file (FMTreeView *view, GtkTreeIter *iter) { GtkTreeIter child_iter; gtk_tree_model_sort_convert_iter_to_child_iter (view->details->sort_model, &child_iter, iter); return fm_tree_model_iter_get_file (view->details->child_model, &child_iter); } static NemoFile * sort_model_path_to_file (FMTreeView *view, GtkTreePath *path) { GtkTreeIter iter; if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (view->details->sort_model), &iter, path)) { return NULL; } return sort_model_iter_to_file (view, &iter); } static void got_activation_uri_callback (NemoFile *file, gpointer callback_data) { char *uri, *file_uri; FMTreeView *view; GdkScreen *screen; GFile *location; NemoWindowSlot *slot; gboolean open_in_same_slot; view = FM_TREE_VIEW (callback_data); screen = gtk_widget_get_screen (GTK_WIDGET (view->details->tree_widget)); g_assert (file == view->details->activation_file); open_in_same_slot = (view->details->activation_flags & (NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW | NEMO_WINDOW_OPEN_FLAG_NEW_TAB)) == 0; slot = nemo_window_get_active_slot (view->details->window); uri = nemo_file_get_activation_uri (file); if (nemo_file_is_launcher (file)) { file_uri = nemo_file_get_uri (file); DEBUG ("Tree sidebar, launching %s", file_uri); nemo_launch_desktop_file (screen, file_uri, NULL, NULL); g_free (file_uri); } else if (uri != NULL && nemo_file_is_executable (file) && nemo_file_can_execute (file) && !nemo_file_is_directory (file)) { file_uri = g_filename_from_uri (uri, NULL, NULL); /* Non-local executables don't get launched. They act like non-executables. */ if (file_uri == NULL) { DEBUG ("Tree sidebar, opening location %s", uri); location = g_file_new_for_uri (uri); nemo_window_slot_open_location (slot, location, view->details->activation_flags); g_object_unref (location); } else { DEBUG ("Tree sidebar, launching application for %s", file_uri); nemo_launch_application_from_command (screen, file_uri, FALSE, NULL); g_free (file_uri); } } else if (uri != NULL) { if (!open_in_same_slot || view->details->selection_location == NULL || strcmp (uri, view->details->selection_location) != 0) { if (open_in_same_slot) { if (view->details->selection_location != NULL) { g_free (view->details->selection_location); } view->details->selection_location = g_strdup (uri); } DEBUG ("Tree sidebar, opening location %s", uri); location = g_file_new_for_uri (uri); nemo_window_slot_open_location (slot, location, view->details->activation_flags); g_object_unref (location); } } g_free (uri); nemo_file_unref (view->details->activation_file); view->details->activation_file = NULL; } static void cancel_activation (FMTreeView *view) { if (view->details->activation_file == NULL) { return; } nemo_file_cancel_call_when_ready (view->details->activation_file, got_activation_uri_callback, view); nemo_file_unref (view->details->activation_file); view->details->activation_file = NULL; } static void row_activated_callback (GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, FMTreeView *view) { if (gtk_tree_view_row_expanded (view->details->tree_widget, path)) { gtk_tree_view_collapse_row (view->details->tree_widget, path); } else { gtk_tree_view_expand_row (view->details->tree_widget, path, FALSE); } } static gboolean selection_changed_timer_callback(FMTreeView *view) { NemoFileAttributes attributes; GtkTreeIter iter; GtkTreeSelection *selection; /* no activation if popup menu is open */ if (view->details->popup_file != NULL) { return FALSE; } selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_widget)); cancel_activation (view); if (!gtk_tree_selection_get_selected (selection, NULL, &iter)) { return FALSE; } view->details->activation_file = sort_model_iter_to_file (view, &iter); if (view->details->activation_file == NULL) { return FALSE; } view->details->activation_flags = 0; attributes = NEMO_FILE_ATTRIBUTE_INFO | NEMO_FILE_ATTRIBUTE_LINK_INFO; nemo_file_call_when_ready (view->details->activation_file, attributes, got_activation_uri_callback, view); return FALSE; /* remove timeout */ } static void selection_changed_callback (GtkTreeSelection *selection, FMTreeView *view) { GdkEvent *event; gboolean is_keyboard; if (view->details->selection_changed_timer) { g_source_remove (view->details->selection_changed_timer); view->details->selection_changed_timer = 0; } event = gtk_get_current_event (); if (event) { is_keyboard = (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE); gdk_event_free (event); if (is_keyboard) { /* on keyboard event: delay the change */ /* TODO: make dependent on keyboard repeat rate as per Markus Bertheau ? */ view->details->selection_changed_timer = g_timeout_add (300, (GSourceFunc) selection_changed_timer_callback, view); } else { /* on mouse event: show the change immediately */ selection_changed_timer_callback (view); } } } static int compare_rows (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer callback_data) { NemoFile *file_a, *file_b; int result; /* Dummy rows are always first */ if (a->user_data == NULL) { return -1; } else if (b->user_data == NULL) { return 1; } /* don't sort root nodes */ if (fm_tree_model_iter_is_root (FM_TREE_MODEL (model), a) && fm_tree_model_iter_is_root (FM_TREE_MODEL (model), b)) { return fm_tree_model_iter_compare_roots (FM_TREE_MODEL (model), a, b); } file_a = fm_tree_model_iter_get_file (FM_TREE_MODEL (model), a); file_b = fm_tree_model_iter_get_file (FM_TREE_MODEL (model), b); if (file_a == file_b) { result = 0; } else if (file_a == NULL) { result = -1; } else if (file_b == NULL) { result = +1; } else { result = nemo_file_compare_for_sort (file_a, file_b, NEMO_FILE_SORT_BY_DISPLAY_NAME, FM_TREE_VIEW (callback_data)->details->sort_directories_first, FALSE); } nemo_file_unref (file_a); nemo_file_unref (file_b); return result; } static char * get_root_uri_callback (NemoTreeViewDragDest *dest, gpointer user_data) { /* Don't allow drops on background */ return NULL; } static NemoFile * get_file_for_path_callback (NemoTreeViewDragDest *dest, GtkTreePath *path, gpointer user_data) { FMTreeView *view; view = FM_TREE_VIEW (user_data); return sort_model_path_to_file (view, path); } static void move_copy_items_callback (NemoTreeViewDragDest *dest, const GList *item_uris, const char *target_uri, GdkDragAction action, int x, int y, gpointer user_data) { FMTreeView *view; view = FM_TREE_VIEW (user_data); nemo_clipboard_clear_if_colliding_uris (GTK_WIDGET (view), item_uris, copied_files_atom); nemo_file_operations_copy_move (item_uris, NULL, target_uri, action, GTK_WIDGET (view->details->tree_widget), NULL, NULL); } static void add_root_for_mount (FMTreeView *view, GMount *mount) { char *mount_uri, *name; GFile *root; GIcon *icon; if (g_mount_is_shadowed (mount)) return; icon = g_mount_get_icon (mount); root = g_mount_get_root (mount); mount_uri = g_file_get_uri (root); g_object_unref (root); name = g_mount_get_name (mount); fm_tree_model_add_root_uri(view->details->child_model, mount_uri, name, icon, mount); g_object_unref (icon); g_free (name); g_free (mount_uri); } static void mount_added_callback (GVolumeMonitor *volume_monitor, GMount *mount, FMTreeView *view) { add_root_for_mount (view, mount); } static void mount_removed_callback (GVolumeMonitor *volume_monitor, GMount *mount, FMTreeView *view) { GFile *root; char *mount_uri; root = g_mount_get_root (mount); mount_uri = g_file_get_uri (root); g_object_unref (root); fm_tree_model_remove_root_uri (view->details->child_model, mount_uri); g_free (mount_uri); } static void clipboard_contents_received_callback (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data) { FMTreeView *view; view = FM_TREE_VIEW (data); if (gtk_selection_data_get_data_type (selection_data) == copied_files_atom && gtk_selection_data_get_length (selection_data) > 0 && view->details->popup != NULL) { gtk_widget_set_sensitive (view->details->popup_paste, TRUE); } g_object_unref (view); } static gboolean is_parent_writable (NemoFile *file) { NemoFile *parent; gboolean result; parent = nemo_file_get_parent (file); /* No parent directory, return FALSE */ if (parent == NULL) { return FALSE; } result = nemo_file_can_write (parent); nemo_file_unref (parent); return result; } static gboolean button_pressed_callback (GtkTreeView *treeview, GdkEventButton *event, FMTreeView *view) { GtkTreePath *path, *cursor_path; gboolean parent_file_is_writable; gboolean file_is_home_or_desktop; gboolean file_is_special_link; gboolean can_move_file_to_trash; gboolean can_delete_file; gboolean using_browser; using_browser = g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_ALWAYS_USE_BROWSER); if (event->button == 3) { gboolean show_unmount = FALSE; gboolean show_eject = FALSE; GMount *mount = NULL; if (view->details->popup_file != NULL) { return FALSE; /* Already up, ignore */ } if (!gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL)) { return FALSE; } view->details->popup_file = sort_model_path_to_file (view, path); if (view->details->popup_file == NULL) { gtk_tree_path_free (path); return FALSE; } gtk_tree_view_get_cursor (view->details->tree_widget, &cursor_path, NULL); gtk_tree_path_free (path); create_popup_menu (view); if (using_browser) { gtk_widget_set_sensitive (view->details->popup_open_in_new_window, nemo_file_is_directory (view->details->popup_file)); gtk_widget_set_sensitive (view->details->popup_open_in_new_tab, nemo_file_is_directory (view->details->popup_file)); } gtk_widget_set_sensitive (view->details->popup_create_folder, nemo_file_is_directory (view->details->popup_file) && nemo_file_can_write (view->details->popup_file)); gtk_widget_set_sensitive (view->details->popup_paste, FALSE); if (nemo_file_is_directory (view->details->popup_file) && nemo_file_can_write (view->details->popup_file)) { gtk_clipboard_request_contents (nemo_clipboard_get (GTK_WIDGET (view->details->tree_widget)), copied_files_atom, clipboard_contents_received_callback, g_object_ref (view)); } can_move_file_to_trash = nemo_file_can_trash (view->details->popup_file); gtk_widget_set_sensitive (view->details->popup_trash, can_move_file_to_trash); if (g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_ENABLE_DELETE)) { parent_file_is_writable = is_parent_writable (view->details->popup_file); file_is_home_or_desktop = nemo_file_is_home (view->details->popup_file) || nemo_file_is_desktop_directory (view->details->popup_file); file_is_special_link = NEMO_IS_DESKTOP_ICON_FILE (view->details->popup_file); can_delete_file = parent_file_is_writable && !file_is_home_or_desktop && !file_is_special_link; gtk_widget_show (view->details->popup_delete); gtk_widget_set_sensitive (view->details->popup_delete, can_delete_file); } else { gtk_widget_hide (view->details->popup_delete); } mount = fm_tree_model_get_mount_for_root_node_file (view->details->child_model, view->details->popup_file); if (mount) { show_unmount = g_mount_can_unmount (mount); show_eject = g_mount_can_eject (mount); } if (show_unmount) { gtk_widget_show (view->details->popup_unmount); } else { gtk_widget_hide (view->details->popup_unmount); } if (show_eject) { gtk_widget_show (view->details->popup_eject); } else { gtk_widget_hide (view->details->popup_eject); } if (show_unmount || show_eject) { gtk_widget_show (view->details->popup_unmount_separator); } else { gtk_widget_hide (view->details->popup_unmount_separator); } if (nemo_file_get_pinning (view->details->popup_file)) { gtk_widget_hide (view->details->popup_pin); gtk_widget_show (view->details->popup_unpin); } else { gtk_widget_show (view->details->popup_pin); gtk_widget_hide (view->details->popup_unpin); } gboolean actions_visible = FALSE; GList *l; NemoFile *file = view->details->popup_file; NemoFile *parent = nemo_file_get_parent (file); GList *tmp = NULL; tmp = g_list_append (tmp, file); ActionPayload *p; for (l = view->details->action_items; l != NULL; l = l->next) { p = l->data; if (nemo_action_get_visibility (p->action, tmp, parent, FALSE)) { gchar *action_label; action_label = nemo_action_get_label (p->action, tmp, parent); gtk_menu_item_set_label (GTK_MENU_ITEM (p->item), action_label); g_free (action_label); gtk_widget_set_visible (p->item, TRUE); actions_visible = TRUE; } else { gtk_widget_set_visible (p->item, FALSE); } } gtk_widget_set_visible (view->details->popup_action_separator, actions_visible); g_list_free (tmp); // Don't free the file, just the list, the file is owned by the model. gtk_menu_popup (GTK_MENU (view->details->popup), NULL, NULL, NULL, NULL, event->button, event->time); gtk_tree_view_set_cursor (view->details->tree_widget, cursor_path, NULL, FALSE); gtk_tree_path_free (cursor_path); return FALSE; } else if (event->button == 2 && event->type == GDK_BUTTON_PRESS) { NemoFile *file; NemoWindowOpenFlags flags = 0; if (!gtk_tree_view_get_path_at_pos (treeview, event->x, event->y, &path, NULL, NULL, NULL)) { return FALSE; } file = sort_model_path_to_file (view, path); if (using_browser) { flags = (event->state & GDK_CONTROL_MASK) ? NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW : NEMO_WINDOW_OPEN_FLAG_NEW_TAB; } else { flags = NEMO_WINDOW_OPEN_FLAG_CLOSE_BEHIND; } if (file) { fm_tree_view_activate_file (view, file, flags); nemo_file_unref (file); } gtk_tree_path_free (path); return TRUE; } return FALSE; } static void fm_tree_view_activate_file (FMTreeView *view, NemoFile *file, NemoWindowOpenFlags flags) { NemoFileAttributes attributes; cancel_activation (view); view->details->activation_file = nemo_file_ref (file); view->details->activation_flags = flags; attributes = NEMO_FILE_ATTRIBUTE_INFO | NEMO_FILE_ATTRIBUTE_LINK_INFO; nemo_file_call_when_ready (view->details->activation_file, attributes, got_activation_uri_callback, view); } static void fm_tree_view_open_cb (GtkWidget *menu_item, FMTreeView *view) { fm_tree_view_activate_file (view, view->details->popup_file, 0); } static void fm_tree_view_open_in_new_tab_cb (GtkWidget *menu_item, FMTreeView *view) { fm_tree_view_activate_file (view, view->details->popup_file, NEMO_WINDOW_OPEN_FLAG_NEW_TAB); } static void fm_tree_view_open_in_new_window_cb (GtkWidget *menu_item, FMTreeView *view) { fm_tree_view_activate_file (view, view->details->popup_file, NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW); } static void new_folder_done (GFile *new_folder, gboolean success, gpointer data) { GList *list; /* show the properties window for the newly created * folder so the user can change its name */ list = g_list_prepend (NULL, nemo_file_get (new_folder)); nemo_properties_window_present (list, GTK_WIDGET (data), NULL); nemo_file_list_free (list); } static void fm_tree_view_create_folder_cb (GtkWidget *menu_item, FMTreeView *view) { char *parent_uri; parent_uri = nemo_file_get_uri (view->details->popup_file); nemo_file_operations_new_folder (GTK_WIDGET (view->details->tree_widget), NULL, parent_uri, new_folder_done, view->details->tree_widget); g_free (parent_uri); } static void copy_or_cut_files (FMTreeView *view, gboolean cut) { char *status_string, *name; NemoClipboardInfo info; GtkTargetList *target_list; GtkTargetEntry *targets; int n_targets; info.cut = cut; info.files = g_list_prepend (NULL, view->details->popup_file); target_list = gtk_target_list_new (NULL, 0); gtk_target_list_add (target_list, copied_files_atom, 0, 0); gtk_target_list_add_uri_targets (target_list, 0); gtk_target_list_add_text_targets (target_list, 0); targets = gtk_target_table_new_from_list (target_list, &n_targets); gtk_target_list_unref (target_list); gtk_clipboard_set_with_data (nemo_clipboard_get (GTK_WIDGET (view->details->tree_widget)), targets, n_targets, nemo_get_clipboard_callback, nemo_clear_clipboard_callback, NULL); gtk_target_table_free (targets, n_targets); nemo_clipboard_monitor_set_clipboard_info (nemo_clipboard_monitor_get (), &info); g_list_free (info.files); name = nemo_file_get_display_name (view->details->popup_file); if (cut) { status_string = g_strdup_printf (_("\"%s\" will be moved " "if you select the Paste command"), name); } else { status_string = g_strdup_printf (_("\"%s\" will be copied " "if you select the Paste command"), name); } g_free (name); nemo_window_push_status (view->details->window, status_string); g_free (status_string); } static void fm_tree_view_cut_cb (GtkWidget *menu_item, FMTreeView *view) { copy_or_cut_files (view, TRUE); } static void fm_tree_view_copy_cb (GtkWidget *menu_item, FMTreeView *view) { copy_or_cut_files (view, FALSE); } static void paste_clipboard_data (FMTreeView *view, GtkSelectionData *selection_data, char *destination_uri) { gboolean cut; GList *item_uris; cut = FALSE; item_uris = nemo_clipboard_get_uri_list_from_selection_data (selection_data, &cut, copied_files_atom); if (item_uris == NULL|| destination_uri == NULL) { nemo_window_push_status (view->details->window, _("There is nothing on the clipboard to paste.")); } else { nemo_file_operations_copy_move (item_uris, NULL, destination_uri, cut ? GDK_ACTION_MOVE : GDK_ACTION_COPY, GTK_WIDGET (view->details->tree_widget), NULL, NULL); /* If items are cut then remove from clipboard */ if (cut) { gtk_clipboard_clear (nemo_clipboard_get (GTK_WIDGET (view))); } g_list_free_full (item_uris, g_free); } } static void paste_into_clipboard_received_callback (GtkClipboard *clipboard, GtkSelectionData *selection_data, gpointer data) { FMTreeView *view; char *directory_uri; view = FM_TREE_VIEW (data); directory_uri = nemo_file_get_uri (view->details->popup_file); paste_clipboard_data (view, selection_data, directory_uri); g_free (directory_uri); } static void fm_tree_view_paste_cb (GtkWidget *menu_item, FMTreeView *view) { gtk_clipboard_request_contents (nemo_clipboard_get (GTK_WIDGET (view->details->tree_widget)), copied_files_atom, paste_into_clipboard_received_callback, view); } static GtkWindow * fm_tree_view_get_containing_window (FMTreeView *view) { GtkWidget *window; g_assert (FM_IS_TREE_VIEW (view)); window = gtk_widget_get_ancestor (GTK_WIDGET (view), GTK_TYPE_WINDOW); if (window == NULL) { return NULL; } return GTK_WINDOW (window); } static void fm_tree_view_pin_unpin_cb (GtkWidget *menu_item, FMTreeView *view) { nemo_file_set_pinning (view->details->popup_file, !nemo_file_get_pinning (view->details->popup_file)); } static void fm_tree_view_trash_cb (GtkWidget *menu_item, FMTreeView *view) { GList *list; if (!nemo_file_can_trash (view->details->popup_file)) { return; } list = g_list_prepend (NULL, nemo_file_get_location (view->details->popup_file)); nemo_file_operations_trash_or_delete (list, fm_tree_view_get_containing_window (view), NULL, NULL); g_list_free_full (list, g_object_unref); } static void fm_tree_view_delete_cb (GtkWidget *menu_item, FMTreeView *view) { GList *location_list; if (!g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_ENABLE_DELETE)) { return; } location_list = g_list_prepend (NULL, nemo_file_get_location (view->details->popup_file)); nemo_file_operations_delete (location_list, fm_tree_view_get_containing_window (view), NULL, NULL); g_list_free_full (location_list, g_object_unref); } static void fm_tree_view_properties_cb (GtkWidget *menu_item, FMTreeView *view) { GList *list; list = g_list_prepend (NULL, nemo_file_ref (view->details->popup_file)); nemo_properties_window_present (list, GTK_WIDGET (view->details->tree_widget), NULL); nemo_file_list_free (list); } static void fm_tree_view_unmount_cb (GtkWidget *menu_item, FMTreeView *view) { NemoFile *file = view->details->popup_file; GMount *mount; if (file == NULL) { return; } mount = fm_tree_model_get_mount_for_root_node_file (view->details->child_model, file); if (mount != NULL) { nemo_file_operations_unmount_mount (fm_tree_view_get_containing_window (view), mount, FALSE, TRUE); } } static void fm_tree_view_eject_cb (GtkWidget *menu_item, FMTreeView *view) { NemoFile *file = view->details->popup_file; GMount *mount; if (file == NULL) { return; } mount = fm_tree_model_get_mount_for_root_node_file (view->details->child_model, file); if (mount != NULL) { nemo_file_operations_unmount_mount (fm_tree_view_get_containing_window (view), mount, TRUE, TRUE); } } static gboolean free_popup_file_in_idle_cb (gpointer data) { FMTreeView *view; view = FM_TREE_VIEW (data); if (view->details->popup_file != NULL) { nemo_file_unref (view->details->popup_file); view->details->popup_file = NULL; } view->details->popup_file_idle_handler = 0; return FALSE; } static void popup_menu_deactivated (GtkMenuShell *menu_shell, gpointer data) { FMTreeView *view; view = FM_TREE_VIEW (data); /* The popup menu is deactivated. (I.E. hidden) We want to free popup_file, but can't right away as it might immediately get used if we're deactivation due to activating a menu item. So, we free it in idle */ if (view->details->popup_file != NULL && view->details->popup_file_idle_handler == 0) { view->details->popup_file_idle_handler = g_idle_add (free_popup_file_in_idle_cb, view); } } static void action_activated_callback (GtkMenuItem *item, ActionPayload *payload) { gchar *uri = NULL; FMTreeView *view = payload->view; NemoFile *file = view->details->popup_file; NemoFile *parent = nemo_file_get_parent (file); GList *tmp = NULL; tmp = g_list_append (tmp, file); nemo_action_activate (NEMO_ACTION (payload->action), tmp, parent); nemo_file_list_free (tmp); g_free (uri); } static void add_action_popup_items (FMTreeView *view) { if (view->details->action_items != NULL) g_list_free_full (view->details->action_items, g_free); view->details->action_items = NULL; GList *action_list = nemo_action_manager_list_actions (view->details->action_manager); GList *l; GtkWidget *menu_item; NemoAction *action; ActionPayload *payload; gint index = 13; for (l = action_list; l != NULL; l = l->next) { action = l->data; payload = g_new0 (ActionPayload, 1); payload->action = action; payload->view = view; menu_item = gtk_menu_item_new_with_mnemonic (nemo_action_get_orig_label (action)); payload->item = menu_item; g_signal_connect (menu_item, "activate", G_CALLBACK (action_activated_callback), payload); gtk_widget_show (menu_item); gtk_menu_shell_insert (GTK_MENU_SHELL (view->details->popup), menu_item, index); view->details->action_items = g_list_append (view->details->action_items, payload); index ++; } } /* Callback used when the file list's popup menu is detached */ static void popup_menu_detach_cb (GtkWidget *attach_widget, GtkMenu *menu) { FMTreeView *view; view = FM_TREE_VIEW (attach_widget); g_assert (FM_IS_TREE_VIEW (view)); view->details->popup = NULL; view->details->popup_open = NULL; view->details->popup_open_in_new_window = NULL; view->details->popup_open_in_new_tab = NULL; view->details->popup_create_folder = NULL; view->details->popup_cut = NULL; view->details->popup_copy = NULL; view->details->popup_paste = NULL; view->details->popup_rename = NULL; view->details->popup_pin = NULL; view->details->popup_trash = NULL; view->details->popup_delete = NULL; view->details->popup_properties = NULL; view->details->popup_unmount_separator = NULL; view->details->popup_unmount = NULL; view->details->popup_eject = NULL; view->details->popup_action_separator = NULL; } static void actions_changed_callback (FMTreeView *view) { if (view->details->popup) { gtk_menu_detach (GTK_MENU (view->details->popup)); } } static void create_popup_menu (FMTreeView *view) { GtkWidget *popup, *menu_item, *menu_image; if (view->details->popup != NULL) { /* already created */ return; } popup = gtk_menu_new (); gtk_menu_attach_to_widget (GTK_MENU (popup), GTK_WIDGET (view), popup_menu_detach_cb); g_signal_connect (popup, "deactivate", G_CALLBACK (popup_menu_deactivated), view); /* add the "open" menu item */ menu_image = gtk_image_new_from_icon_name ("folder-open-symbolic", GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Open")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_open_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_open = menu_item; /* add the "open in new tab" menu item */ menu_item = gtk_menu_item_new_with_mnemonic (_("Open in New _Tab")); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_open_in_new_tab_cb), view); g_settings_bind (nemo_preferences, NEMO_PREFERENCES_ALWAYS_USE_BROWSER, menu_item, "visible", G_SETTINGS_BIND_GET); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_open_in_new_tab = menu_item; /* add the "open in new window" menu item */ menu_item = gtk_menu_item_new_with_mnemonic (_("Open in New _Window")); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_open_in_new_window_cb), view); g_settings_bind (nemo_preferences, NEMO_PREFERENCES_ALWAYS_USE_BROWSER, menu_item, "visible", G_SETTINGS_BIND_GET); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_open_in_new_window = menu_item; eel_gtk_menu_append_separator (GTK_MENU (popup)); /* add the "create new folder" menu item */ menu_item = gtk_image_menu_item_new_with_mnemonic (_("Create New _Folder")); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_create_folder_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_create_folder = menu_item; eel_gtk_menu_append_separator (GTK_MENU (popup)); /* add the "cut folder" menu item */ menu_image = gtk_image_new_from_icon_name ("edit-cut-symbolic", GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("Cu_t")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_cut_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_cut = menu_item; /* add the "copy folder" menu item */ menu_image = gtk_image_new_from_icon_name ("edit-copy-symbolic", GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Copy")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_copy_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_copy = menu_item; /* add the "paste files into folder" menu item */ menu_image = gtk_image_new_from_icon_name ("edit-paste-symbolic", GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Paste Into Folder")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_paste_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_paste = menu_item; eel_gtk_menu_append_separator (GTK_MENU (popup)); menu_image = gtk_image_new_from_icon_name ("xapp-pin-symbolic", GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("P_in")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_pin_unpin_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_pin = menu_item; menu_image = gtk_image_new_from_icon_name ("xapp-unpin-symbolic", GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("Unp_in")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_pin_unpin_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_unpin = menu_item; eel_gtk_menu_append_separator (GTK_MENU (popup)); /* add the "move to trash" menu item */ menu_image = gtk_image_new_from_icon_name (NEMO_ICON_SYMBOLIC_TRASH_FULL, GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("Mo_ve to Trash")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_trash_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_trash = menu_item; /* add the "delete" menu item */ menu_image = gtk_image_new_from_icon_name (NEMO_ICON_DELETE, GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Delete")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_delete_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_delete = menu_item; /* Nemo Actions */ view->details->popup_action_separator = GTK_WIDGET (eel_gtk_menu_append_separator (GTK_MENU (popup))); eel_gtk_menu_append_separator (GTK_MENU (popup)); /* add the "Unmount" menu item */ menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Unmount")); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_unmount_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_unmount = menu_item; /* add the "Eject" menu item */ menu_item = gtk_image_menu_item_new_with_mnemonic (_("_Eject")); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_eject_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_eject = menu_item; /* add the unmount separator menu item */ view->details->popup_unmount_separator = GTK_WIDGET (eel_gtk_menu_append_separator (GTK_MENU (popup))); /* add the "properties" menu item */ menu_image = gtk_image_new_from_icon_name ("document-properties-symbolic", GTK_ICON_SIZE_MENU); gtk_widget_show (menu_image); menu_item = gtk_image_menu_item_new_with_mnemonic (_("P_roperties")); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), menu_image); g_signal_connect (menu_item, "activate", G_CALLBACK (fm_tree_view_properties_cb), view); gtk_widget_show (menu_item); gtk_menu_shell_append (GTK_MENU_SHELL (popup), menu_item); view->details->popup_properties = menu_item; view->details->popup = popup; add_action_popup_items (view); } static gint get_icon_scale_callback (FMTreeModel *model, FMTreeView *view) { return gtk_widget_get_scale_factor (GTK_WIDGET (view->details->tree_widget)); } static void icon_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data) { gboolean expanded; GIcon *icon; g_object_get (cell, "is-expanded", &expanded, NULL); gtk_tree_model_get (tree_model, iter, expanded ? FM_TREE_MODEL_OPEN_ICON_COLUMN : FM_TREE_MODEL_CLOSED_ICON_COLUMN, &icon, -1); g_object_set (cell, "gicon", icon, NULL); } static void create_tree (FMTreeView *view) { GtkCellRenderer *cell; GtkTreeViewColumn *column; GVolumeMonitor *volume_monitor; char *home_uri; GList *mounts, *l; char *location; GIcon *icon; NemoWindowSlot *slot; view->details->child_model = fm_tree_model_new (); view->details->sort_model = GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (view->details->child_model))); view->details->tree_widget = GTK_TREE_VIEW (gtk_tree_view_new_with_model (GTK_TREE_MODEL (view->details->sort_model))); g_object_unref (view->details->sort_model); gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (view->details->tree_widget)), "NemoSidebar"); gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (view->details->sort_model), compare_rows, view, NULL); g_signal_connect_object (view->details->child_model, "row_loaded", G_CALLBACK (row_loaded_callback), view, G_CONNECT_AFTER); g_signal_connect_object (view->details->child_model, "get-icon-scale", G_CALLBACK (get_icon_scale_callback), view, 0); #ifdef NOT_YET_USABLE /* Do we really want this? */ icon = g_themed_icon_new (NEMO_ICON_COMPUTER); fm_tree_model_add_root_uri (view->details->child_model, "computer:///", _("Computer"), icon, NULL); g_object_unref (icon); #endif home_uri = nemo_get_home_directory_uri (); icon = g_themed_icon_new (NEMO_ICON_HOME); fm_tree_model_add_root_uri (view->details->child_model, home_uri, _("Home"), icon, NULL); g_object_unref (icon); g_free (home_uri); icon = g_themed_icon_new ("favorites"); fm_tree_model_add_root_uri (view->details->child_model, "favorites:///", _("Favorites"), icon, NULL); g_object_unref (icon); icon = g_themed_icon_new (NEMO_ICON_FILESYSTEM); fm_tree_model_add_root_uri (view->details->child_model, "file:///", _("File System"), icon, NULL); g_object_unref (icon); #ifdef NOT_YET_USABLE /* Do we really want this? */ icon = g_themed_icon_new (NEMO_ICON_NETWORK); fm_tree_model_add_root_uri (view->details->child_model, "network:///", _("Network"), icon, NULL); g_object_unref (icon); #endif volume_monitor = g_volume_monitor_get (); view->details->volume_monitor = volume_monitor; mounts = g_volume_monitor_get_mounts (volume_monitor); for (l = mounts; l != NULL; l = l->next) { add_root_for_mount (view, l->data); g_object_unref (l->data); } g_list_free (mounts); g_signal_connect_object (volume_monitor, "mount_added", G_CALLBACK (mount_added_callback), view, 0); g_signal_connect_object (volume_monitor, "mount_removed", G_CALLBACK (mount_removed_callback), view, 0); g_object_unref (view->details->child_model); gtk_tree_view_set_headers_visible (view->details->tree_widget, FALSE); view->details->drag_dest = nemo_tree_view_drag_dest_new (view->details->tree_widget); g_signal_connect_object (view->details->drag_dest, "get_root_uri", G_CALLBACK (get_root_uri_callback), view, 0); g_signal_connect_object (view->details->drag_dest, "get_file_for_path", G_CALLBACK (get_file_for_path_callback), view, 0); g_signal_connect_object (view->details->drag_dest, "move_copy_items", G_CALLBACK (move_copy_items_callback), view, 0); view->details->action_manager = nemo_action_manager_new (); view->details->action_manager_changed_id = g_signal_connect_swapped (view->details->action_manager, "changed", G_CALLBACK (actions_changed_callback), view); view->details->action_items = NULL; /* Create column */ column = gtk_tree_view_column_new (); cell = gtk_cell_renderer_pixbuf_new (); gtk_tree_view_column_pack_start (column, cell, FALSE); gtk_tree_view_column_set_cell_data_func (column, cell, (GtkTreeCellDataFunc) icon_data_func, NULL, NULL); g_object_set (cell, "follow-state", TRUE, NULL); cell = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, cell, TRUE); gtk_tree_view_column_set_attributes (column, cell, "text", FM_TREE_MODEL_DISPLAY_NAME_COLUMN, "style", FM_TREE_MODEL_FONT_STYLE_COLUMN, "weight", FM_TREE_MODEL_TEXT_WEIGHT_COLUMN, NULL); gtk_tree_view_append_column (view->details->tree_widget, column); gtk_widget_show (GTK_WIDGET (view->details->tree_widget)); gtk_container_add (GTK_CONTAINER (view), GTK_WIDGET (view->details->tree_widget)); g_signal_connect_object (gtk_tree_view_get_selection (GTK_TREE_VIEW (view->details->tree_widget)), "changed", G_CALLBACK (selection_changed_callback), view, 0); g_signal_connect (G_OBJECT (view->details->tree_widget), "row-activated", G_CALLBACK (row_activated_callback), view); g_signal_connect (G_OBJECT (view->details->tree_widget), "button_press_event", G_CALLBACK (button_pressed_callback), view); slot = nemo_window_get_active_slot (view->details->window); location = nemo_window_slot_get_current_uri (slot); schedule_select_and_show_location (view, location); g_free (location); } static void update_filtering_from_preferences (FMTreeView *view) { NemoWindowShowHiddenFilesMode mode; if (view->details->child_model == NULL) { return; } mode = nemo_window_get_hidden_files_mode (view->details->window); fm_tree_model_set_show_hidden_files (view->details->child_model, mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE); fm_tree_model_set_show_only_directories (view->details->child_model, g_settings_get_boolean (nemo_tree_sidebar_preferences, NEMO_PREFERENCES_TREE_SHOW_ONLY_DIRECTORIES)); } static void parent_set_callback (GtkWidget *widget, GtkWidget *previous_parent, gpointer callback_data) { FMTreeView *view; view = FM_TREE_VIEW (callback_data); if (gtk_widget_get_parent (widget) != NULL && view->details->tree_widget == NULL) { create_tree (view); update_filtering_from_preferences (view); } } static void filtering_changed_callback (gpointer callback_data) { update_filtering_from_preferences (FM_TREE_VIEW (callback_data)); } static void loading_uri_callback (NemoWindow *window, char *location, gpointer callback_data) { FMTreeView *view; view = FM_TREE_VIEW (callback_data); schedule_select_and_show_location (view, location); } static void sort_directories_first_changed_callback (gpointer callback_data) { FMTreeView *view; gboolean preference_value; view = FM_TREE_VIEW (callback_data); preference_value = g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_SORT_DIRECTORIES_FIRST); if (preference_value != view->details->sort_directories_first) { view->details->sort_directories_first = preference_value; } gtk_tree_model_sort_reset_default_sort_func (GTK_TREE_MODEL_SORT (view->details->sort_model)); gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (view->details->sort_model), compare_rows, view, NULL); } static void fm_tree_view_init (FMTreeView *view) { view->details = g_new0 (FMTreeViewDetails, 1); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (view), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (view), NULL); gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (view), NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view), GTK_SHADOW_IN); gtk_widget_show (GTK_WIDGET (view)); g_signal_connect_object (view, "parent_set", G_CALLBACK (parent_set_callback), view, 0); view->details->selection_location = NULL; view->details->selecting = FALSE; g_signal_connect_swapped (nemo_tree_sidebar_preferences, "changed::" NEMO_PREFERENCES_TREE_SHOW_ONLY_DIRECTORIES, G_CALLBACK (filtering_changed_callback), view); g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_SORT_DIRECTORIES_FIRST, G_CALLBACK (sort_directories_first_changed_callback), view); view->details->sort_directories_first = g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_SORT_DIRECTORIES_FIRST); view->details->popup_file = NULL; view->details->clipboard_handler_id = g_signal_connect (nemo_clipboard_monitor_get (), "clipboard_info", G_CALLBACK (notify_clipboard_info), view); } static void hidden_files_mode_changed_callback (NemoWindow *window, FMTreeView *view) { update_filtering_from_preferences (view); } static void fm_tree_view_dispose (GObject *object) { FMTreeView *view; view = FM_TREE_VIEW (object); if (view->details->selection_changed_timer) { g_source_remove (view->details->selection_changed_timer); view->details->selection_changed_timer = 0; } if (view->details->drag_dest) { g_object_unref (view->details->drag_dest); view->details->drag_dest = NULL; } if (view->details->show_selection_idle_id) { g_source_remove (view->details->show_selection_idle_id); view->details->show_selection_idle_id = 0; } if (view->details->clipboard_handler_id != 0) { g_signal_handler_disconnect (nemo_clipboard_monitor_get (), view->details->clipboard_handler_id); view->details->clipboard_handler_id = 0; } cancel_activation (view); if (view->details->popup != NULL) { gtk_widget_destroy (view->details->popup); view->details->popup = NULL; } if (view->details->popup_file_idle_handler != 0) { g_source_remove (view->details->popup_file_idle_handler); view->details->popup_file_idle_handler = 0; } if (view->details->popup_file != NULL) { nemo_file_unref (view->details->popup_file); view->details->popup_file = NULL; } if (view->details->selection_location != NULL) { g_free (view->details->selection_location); view->details->selection_location = NULL; } if (view->details->volume_monitor != NULL) { g_object_unref (view->details->volume_monitor); view->details->volume_monitor = NULL; } if (view->details->hidden_files_changed_id != 0) { g_signal_handler_disconnect (view->details->window, view->details->hidden_files_changed_id); view->details->hidden_files_changed_id = 0; } if (view->details->action_manager_changed_id != 0) { g_signal_handler_disconnect (view->details->action_manager, view->details->action_manager_changed_id); view->details->action_manager_changed_id = 0; } g_clear_object (&view->details->action_manager); g_signal_handlers_disconnect_by_func (nemo_tree_sidebar_preferences, G_CALLBACK(filtering_changed_callback), view); g_signal_handlers_disconnect_by_func (nemo_tree_sidebar_preferences, G_CALLBACK(sort_directories_first_changed_callback), view); view->details->window = NULL; G_OBJECT_CLASS (parent_class)->dispose (object); } static void fm_tree_view_finalize (GObject *object) { FMTreeView *view; view = FM_TREE_VIEW (object); g_free (view->details); G_OBJECT_CLASS (parent_class)->finalize (object); } static void fm_tree_view_class_init (FMTreeViewClass *class) { G_OBJECT_CLASS (class)->dispose = fm_tree_view_dispose; G_OBJECT_CLASS (class)->finalize = fm_tree_view_finalize; copied_files_atom = gdk_atom_intern ("x-special/gnome-copied-files", FALSE); } static void fm_tree_view_set_parent_window (FMTreeView *sidebar, NemoWindow *window) { char *location; NemoWindowSlot *slot; sidebar->details->window = window; slot = nemo_window_get_active_slot (window); g_signal_connect_object (window, "loading_uri", G_CALLBACK (loading_uri_callback), sidebar, 0); location = nemo_window_slot_get_current_uri (slot); loading_uri_callback (window, location, sidebar); g_free (location); sidebar->details->hidden_files_changed_id = g_signal_connect_object (window, "hidden-files-mode-changed", G_CALLBACK (hidden_files_mode_changed_callback), sidebar, 0); } GtkWidget * nemo_tree_sidebar_new (NemoWindow *window) { FMTreeView *sidebar; sidebar = g_object_new (fm_tree_view_get_type (), NULL); fm_tree_view_set_parent_window (sidebar, window); return GTK_WIDGET (sidebar); }