diff --git a/.gitignore b/.gitignore index 2a64eee33e..6a65214ed4 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ debian/files *.debhelper obj-x86_64-linux-gnu +nemo-places-treeview/ diff --git a/libnemo-private/nemo-action.c b/libnemo-private/nemo-action.c index 671cfc2150..7113ebad1e 100644 --- a/libnemo-private/nemo-action.c +++ b/libnemo-private/nemo-action.c @@ -1431,11 +1431,7 @@ get_insertion_string (NemoAction *action, break; } - gchar *ret = str->str; - - g_string_free (str, FALSE); - - return ret; + return g_string_free (str, FALSE); } static GString * @@ -1591,9 +1587,7 @@ get_final_label (NemoAction *action, DEBUG ("Action Label: %s", str->str); - gchar *ret = str->str; - g_string_free (str, FALSE); - return ret; + return g_string_free (str, FALSE); } static gchar * @@ -1616,9 +1610,7 @@ get_final_tt (NemoAction *action, DEBUG ("Action Tooltip: %s", str->str); - gchar *ret = str->str; - g_string_free (str, FALSE); - return ret; + return g_string_free (str, FALSE); } static void diff --git a/libnemo-private/nemo-directory.c b/libnemo-private/nemo-directory.c index 0a11ae58dd..55ec9f0f44 100644 --- a/libnemo-private/nemo-directory.c +++ b/libnemo-private/nemo-directory.c @@ -214,7 +214,27 @@ filtering_changed_callback (gpointer callback_data) /* Preference about which items to show has changed, so we * can't trust any of our precomputed directory counts. */ - g_hash_table_foreach (directories, invalidate_one_count, NULL); + //g_hash_table_foreach (directories, invalidate_one_count, NULL); + // the hash table was changed during g_hash_table_foreach call so the following code checks for + // non existig keys cwhile processing the list + GList *keys = g_hash_table_get_keys(directories); + + for (GList *l = keys; l != NULL; l = l->next) { + gpointer key = l->data; + + // Prüfen ob der Key noch existiert + if (!g_hash_table_contains(directories, key)) + continue; + + gpointer value = g_hash_table_lookup(directories, key); + if (value == NULL) + continue; // Value bereits gelöscht + + invalidate_one_count(key, value, NULL); + } + + g_list_free(keys); + } void diff --git a/libnemo-private/nemo-search-engine-advanced.c b/libnemo-private/nemo-search-engine-advanced.c index 8f16c5aa10..c05d98bfb7 100644 --- a/libnemo-private/nemo-search-engine-advanced.c +++ b/libnemo-private/nemo-search-engine-advanced.c @@ -770,9 +770,9 @@ load_contents (SearchThreadData *data, break; } - if (chunk != NULL) { + //if (chunk != NULL) { g_string_append_len (str, chunk, len); - } + //} } while (!g_cancellable_is_cancelled (data->cancellable)); g_input_stream_close (stream, diff --git a/search-helpers/nemo-mso-to-txt.c b/search-helpers/nemo-mso-to-txt.c index fcbdb4a758..e1cbc4bd39 100644 --- a/search-helpers/nemo-mso-to-txt.c +++ b/search-helpers/nemo-mso-to-txt.c @@ -61,7 +61,7 @@ process_file (GString *collective, gsf_input_read (GSF_INPUT (file), size, chunk); - if (chunk != NULL) + // if (chunk != NULL) always true { remaining -= size; contents = g_string_append_len (contents, (const gchar *) chunk, size); diff --git a/src/nemo-file-management-properties.c b/src/nemo-file-management-properties.c index 35184ccf2c..6f3ec9ec9b 100644 --- a/src/nemo-file-management-properties.c +++ b/src/nemo-file-management-properties.c @@ -568,8 +568,13 @@ bind_builder_bool (GtkBuilder *builder, const char *widget_name, const char *prefs) { + GObject *object = gtk_builder_get_object (builder, widget_name); + if (!object) { + g_warning ("⚠️ bind_builder_bool: object '%s' not found in builder!", widget_name); + return; + } g_settings_bind (settings, prefs, - gtk_builder_get_object (builder, widget_name), + object, "active", G_SETTINGS_BIND_DEFAULT); } diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 5a1bb6d548..ae6d1463e1 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -22,8 +22,10 @@ * */ + #include +#include "nemo-tree-sidebar-model.h" #include #include #include @@ -32,6 +34,9 @@ #include #include +#include +#include +#include #include #include #include @@ -41,11 +46,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include #include @@ -81,14 +89,20 @@ static gint menu_icon_pixels = 16; #define g_drive_is_removable g_drive_is_media_removable #endif + typedef struct { GtkScrolledWindow parent; GtkTreeView *tree_view; GtkTreeViewColumn *eject_column; GtkCellRenderer *eject_icon_cell_renderer; GtkCellRenderer *editable_renderer; - char *uri; - GtkTreeStore *store; + GtkCellRenderer *icon_cell_renderer; + GtkCellRenderer *padding_cell_renderer; + GtkCellRenderer *heading_cell_renderer; + char *selection_location; + gboolean selecting; + void *store; + GtkTreeModel *sort_model; GtkTreeModel *store_filter; NemoWindow *window; @@ -106,6 +120,9 @@ typedef struct { GtkActionGroup *action_action_group; guint action_action_group_merge_id; + GtkActionGroup *tv_action_group; + guint tv_action_group_merge_id; + gboolean actions_need_update; gboolean devices_header_added; @@ -149,6 +166,18 @@ typedef struct { guint popup_menu_action_index; guint update_places_on_idle_id; + guint hidden_files_changed_id; // filter hidden files/directories + guint show_selection_idle_id; // + gboolean use_file_treeview; // switch file treeview functionality on/off + + NemoFile *popup_file; + guint popup_file_idle_handler; + + guint selection_changed_timer; + NemoFile *activation_file; + NemoWindowOpenFlags activation_flags; + NemoTreeViewDragDest *drag_dest; + } NemoPlacesSidebar; typedef struct { @@ -164,13 +193,16 @@ typedef struct { } NemoPlacesSidebarProviderClass; enum { + PLACES_SIDEBAR_COLUMN_NAME, // FM_TREE_MODEL_DISPLAY_NAME_COLUMN + PLACES_SIDEBAR_COLUMN_GICON, // FM_TREE_MODEL_CLOSED_ICON_COLUMN + PLACES_SIDEBAR_OPEN_ICON_COLUMN, // FM_TREE_MODEL_OPEN_ICON_COLUMN + PLACES_SIDEBAR_FONT_STYLE_COLUMN, // FM_TREE_MODEL_FONT_STYLE_COLUMN + PLACES_SIDEBAR_TEXT_WEIGHT_COLUMN, // FM_TREE_MODEL_TEXT_WEIGHT_COLUMN PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_SIDEBAR_COLUMN_URI, PLACES_SIDEBAR_COLUMN_DRIVE, PLACES_SIDEBAR_COLUMN_VOLUME, PLACES_SIDEBAR_COLUMN_MOUNT, - PLACES_SIDEBAR_COLUMN_NAME, - PLACES_SIDEBAR_COLUMN_GICON, PLACES_SIDEBAR_COLUMN_INDEX, PLACES_SIDEBAR_COLUMN_EJECT, PLACES_SIDEBAR_COLUMN_NO_EJECT, @@ -182,11 +214,12 @@ enum { PLACES_SIDEBAR_COLUMN_HEADING_TEXT, PLACES_SIDEBAR_COLUMN_DF_PERCENT, PLACES_SIDEBAR_COLUMN_SHOW_DF, - + PLACES_SIDEBAR_COLUMN_TREE_NAME, // used for search in file tree view only PLACES_SIDEBAR_COLUMN_COUNT }; typedef enum { + PLACES_TREE_FOLDER, PLACES_BUILT_IN, PLACES_XDG_DIR, PLACES_MOUNTED_VOLUME, @@ -208,6 +241,21 @@ enum { POSITION_LOWER }; +// Polling-Intervall for network filesystems (ms) +#define POLL_INTERVAL 5000 + +typedef struct { + NemoPlacesSidebar *sidebar; + GtkTreeRowReference *tree_ref; + char *uri; + NemoFile *file; // + GFileMonitor *monitor; + GIcon *icon; + GFile *dir; + guint poll_id; + GList *last_snapshot; +} TreeNodeData; + static void open_selected_bookmark (NemoPlacesSidebar *sidebar, GtkTreeModel *model, GtkTreeIter *iter, @@ -227,6 +275,12 @@ static void update_places (NemoPlacesSidebar *sideb static void update_places_on_idle (NemoPlacesSidebar *sidebar); static void rebuild_menu (NemoPlacesSidebar *sidebar); static void actions_changed (gpointer user_data); +static gchar *get_icon_name (const gchar *uri); +static void refresh_highlight (NemoPlacesSidebar *sidebar); +static gboolean get_selected_iter (NemoPlacesSidebar *sidebar, GtkTreeIter *iter); +static gboolean get_selected_iter_for_store (NemoPlacesSidebar *sidebar, GtkTreeIter *iter); +static gboolean nemo_places_sidebar_row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path, gpointer user_data); + /* Identifiers for target types */ enum { GTK_TREE_MODEL_ROW, @@ -296,6 +350,50 @@ decrement_bookmark_breakpoint (NemoPlacesSidebar *sidebar) g_signal_handlers_unblock_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); } + +static void +get_fm_tree_model_iter (GtkTreeModel *model, + GtkTreeIter *iter, + GtkTreeIter *fm_iter) +{ + GtkTreeIter tmp_iter = *iter; + GtkTreeModel *child_model = model; + + // If filter model → pass to child model + if (GTK_IS_TREE_MODEL_FILTER (model)) { + gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), + &tmp_iter, iter); + child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model)); + } + + // If sort model → go one step deeper + if (GTK_IS_TREE_MODEL_SORT (child_model)) { + gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (child_model), + fm_iter, &tmp_iter); + } else { + *fm_iter = tmp_iter; + } +} + +static char * +get_fm_tree_model_uri ( NemoPlacesSidebar *sidebar, + GtkTreeModel *model, + GtkTreeIter *iter) +{ + if(!sidebar->use_file_treeview)return NULL; + char *uri=NULL; + if(uri==0) { + GtkTreeIter fm_iter; + get_fm_tree_model_iter (model, iter, &fm_iter); + NemoFile *file = fm_tree_model_iter_get_file (FM_TREE_MODEL (sidebar->store), &fm_iter); + if(file) { + uri = nemo_file_get_uri(file); + nemo_file_unref(file); + } + } + return uri; +} + static gboolean should_show_desktop (void) { @@ -322,17 +420,31 @@ add_heading (NemoPlacesSidebar *sidebar, const gchar *title) { GtkTreeIter cat_iter; + if(sidebar->use_file_treeview) { + if (!fm_tree_model_append_head_root_node(sidebar->store, title, &cat_iter)) { + g_warning("Failed to append head root node"); + return cat_iter; // You must return an invalid iter + } + fm_tree_model_set (sidebar->store, &cat_iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_HEADING, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type, + PLACES_SIDEBAR_COLUMN_HEADING_TEXT, title, + PLACES_SIDEBAR_COLUMN_INDEX, -1, + PLACES_SIDEBAR_COLUMN_EJECT, FALSE, + PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE, + -1); - gtk_tree_store_append (sidebar->store, &cat_iter, NULL); - gtk_tree_store_set (sidebar->store, &cat_iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_HEADING, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type, - PLACES_SIDEBAR_COLUMN_HEADING_TEXT, title, - PLACES_SIDEBAR_COLUMN_INDEX, -1, - PLACES_SIDEBAR_COLUMN_EJECT, FALSE, - PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE, - -1); - + } else { + gtk_tree_store_append (sidebar->store, &cat_iter, NULL); + gtk_tree_store_set (sidebar->store, &cat_iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_HEADING, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type, + PLACES_SIDEBAR_COLUMN_HEADING_TEXT, title, + PLACES_SIDEBAR_COLUMN_INDEX, -1, + PLACES_SIDEBAR_COLUMN_EJECT, FALSE, + PLACES_SIDEBAR_COLUMN_NO_EJECT, TRUE, + -1); + } return cat_iter; } @@ -408,13 +520,13 @@ new_place_info (PlaceType place_type, static void free_place_info (PlaceInfo *info) { - g_free (info->name); - g_free (info->icon_name); - g_free (info->uri); + g_clear_pointer(&info->name, g_free); + g_clear_pointer(&info->icon_name, g_free); + g_clear_pointer(&info->uri, g_free); g_clear_object (&info->drive); g_clear_object (&info->volume); g_clear_object (&info->mount); - g_free (info->tooltip); + g_clear_pointer(&info->tooltip, g_free); g_free (info); } @@ -456,15 +568,20 @@ add_place (NemoPlacesSidebar *sidebar, } gicon = (icon_name != NULL) ? g_themed_icon_new (icon_name) : NULL; - - gtk_tree_store_append (sidebar->store, &iter, &cat_iter); - gtk_tree_store_set (sidebar->store, &iter, - PLACES_SIDEBAR_COLUMN_GICON, gicon, + if(sidebar->use_file_treeview) { + if(uri) { + fm_tree_model_add_root_uri_head (sidebar->store, uri, &cat_iter , &iter, name , + gicon ? g_object_ref(gicon) : NULL, mount ? g_object_ref(mount) : NULL); + } else { + fm_tree_model_append_child_node (sidebar->store, &cat_iter, &iter); + } + fm_tree_model_set (sidebar->store, &iter, + PLACES_SIDEBAR_COLUMN_GICON, gicon ? g_object_ref(gicon) : NULL, PLACES_SIDEBAR_COLUMN_NAME, name, PLACES_SIDEBAR_COLUMN_URI, uri, - PLACES_SIDEBAR_COLUMN_DRIVE, drive, - PLACES_SIDEBAR_COLUMN_VOLUME, volume, - PLACES_SIDEBAR_COLUMN_MOUNT, mount, + PLACES_SIDEBAR_COLUMN_DRIVE, drive ? g_object_ref(drive) : NULL, + PLACES_SIDEBAR_COLUMN_VOLUME, volume ? g_object_ref(volume) : NULL, + PLACES_SIDEBAR_COLUMN_MOUNT, mount ? g_object_ref(mount) : NULL, PLACES_SIDEBAR_COLUMN_ROW_TYPE, place_type, PLACES_SIDEBAR_COLUMN_INDEX, index, PLACES_SIDEBAR_COLUMN_EJECT, show_eject_button, @@ -477,8 +594,30 @@ add_place (NemoPlacesSidebar *sidebar, PLACES_SIDEBAR_COLUMN_DF_PERCENT, df_percent, PLACES_SIDEBAR_COLUMN_SHOW_DF, show_df_percent, -1); + } else { + gtk_tree_store_append (sidebar->store, &iter, &cat_iter); + gtk_tree_store_set (sidebar->store, &iter, + PLACES_SIDEBAR_COLUMN_GICON, gicon ? g_object_ref(gicon) : NULL, + PLACES_SIDEBAR_COLUMN_NAME, name, + PLACES_SIDEBAR_COLUMN_URI, uri, + PLACES_SIDEBAR_COLUMN_DRIVE, drive ? g_object_ref(drive) : NULL, + PLACES_SIDEBAR_COLUMN_VOLUME, volume ? g_object_ref(volume) : NULL, + PLACES_SIDEBAR_COLUMN_MOUNT, mount ? g_object_ref(mount) : NULL, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, place_type, + PLACES_SIDEBAR_COLUMN_INDEX, index, + PLACES_SIDEBAR_COLUMN_EJECT, show_eject_button, + PLACES_SIDEBAR_COLUMN_NO_EJECT, !show_eject_button, + PLACES_SIDEBAR_COLUMN_BOOKMARK, place_type != PLACES_BOOKMARK, + PLACES_SIDEBAR_COLUMN_TOOLTIP, tooltip, + PLACES_SIDEBAR_COLUMN_EJECT_ICON, show_eject_button ? "media-eject-symbolic" : NULL, + PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, EJECT_ICON_SIZE_NOT_HOVERED, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, section_type, + PLACES_SIDEBAR_COLUMN_DF_PERCENT, df_percent, + PLACES_SIDEBAR_COLUMN_SHOW_DF, show_df_percent, + -1); + } - g_clear_object (&gicon); + g_object_unref(gicon); return cat_iter; } @@ -497,8 +636,23 @@ restore_selection_foreach (GtkTreeModel *model, gpointer user_data) { RestoreLocationData *data = user_data; - gchar *uri; - + gchar *uri=NULL; + if (FM_IS_TREE_MODEL(model)) { + NemoFile *file = fm_tree_model_iter_get_file (FM_TREE_MODEL (model), iter); + if(file) { + uri = nemo_file_get_uri(file); + if(uri) { + if (g_strcmp0 (uri, data->last_uri) == 0 || + g_strcmp0 (uri, data->location) == 0) { + data->path = gtk_tree_path_copy (path); + } + g_free (uri); + nemo_file_unref (file); + return (data->path != NULL); + } + nemo_file_unref (file); + } + } gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); @@ -507,7 +661,7 @@ restore_selection_foreach (GtkTreeModel *model, data->path = gtk_tree_path_copy (path); } - g_free (uri); + if(uri)g_free (uri); return (data->path != NULL); } @@ -556,7 +710,8 @@ restore_expand_state_foreach (GtkTreeModel *model, static void restore_expand_state (NemoPlacesSidebar *sidebar) { - gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store_filter), + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_model_foreach (model, restore_expand_state_foreach, sidebar); } @@ -598,7 +753,8 @@ sidebar_update_restore_selection (NemoPlacesSidebar *sidebar, data.sidebar = sidebar; data.path = NULL; - gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store_filter), + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_model_foreach (model, restore_selection_foreach, &data); if (data.path != NULL) { @@ -712,13 +868,36 @@ get_icon_name (const gchar *uri) return icon_name; } +static void +update_filtering_from_preferences (NemoPlacesSidebar *sidebar) +{ + NemoWindowShowHiddenFilesMode mode; + + if (sidebar->store == NULL) { + return; + } + + mode = nemo_window_get_hidden_files_mode (sidebar->window); + + fm_tree_model_set_show_hidden_files (sidebar->store, + mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE); + + fm_tree_model_set_show_only_directories (sidebar->store,TRUE); +} + +static void tree_model_remove_all_nodes (NemoPlacesSidebar *sidebar) +{ + if(sidebar->use_file_treeview) { + fm_tree_model_remove_all_nodes( (FMTreeModel*) (sidebar->store) ); + } else { + gtk_tree_store_clear (GTK_TREE_STORE (sidebar->store)); + } +} static void update_places (NemoPlacesSidebar *sidebar) { NemoBookmark *bookmark; - GtkTreeSelection *selection; GtkTreeIter last_iter, cat_iter; - GtkTreeModel *model; GVolumeMonitor *volume_monitor; GList *mounts, *l, *ll; GMount *mount; @@ -741,19 +920,22 @@ update_places (NemoPlacesSidebar *sidebar) sidebar->updating_sidebar = TRUE; - model = NULL; + if(sidebar->store == NULL) return; last_uri = NULL; g_clear_pointer (&sidebar->top_bookend_uri, g_free); g_clear_pointer (&sidebar->bottom_bookend_uri, g_free); - selection = gtk_tree_view_get_selection (sidebar->tree_view); - if (gtk_tree_selection_get_selected (selection, &model, &last_iter)) { - gtk_tree_model_get (model, - &last_iter, - PLACES_SIDEBAR_COLUMN_URI, &last_uri, -1); + if (get_selected_iter (sidebar, &last_iter)) { + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_model_get (GTK_TREE_MODEL (model), + &last_iter, + PLACES_SIDEBAR_COLUMN_URI, &last_uri, -1); + if(last_uri == NULL) { + last_uri = get_fm_tree_model_uri (sidebar, model, &last_iter); + } } - gtk_tree_store_clear (sidebar->store); + tree_model_remove_all_nodes(sidebar); sidebar->devices_header_added = FALSE; sidebar->bookmarks_header_added = FALSE; @@ -764,6 +946,10 @@ update_places (NemoPlacesSidebar *sidebar) network_mounts = network_volumes = NULL; volume_monitor = sidebar->volume_monitor; + if(sidebar->use_file_treeview) { + update_filtering_from_preferences (sidebar); + } + cat_iter = add_heading (sidebar, SECTION_COMPUTER, _("My Computer")); /* add built in bookmarks */ @@ -986,7 +1172,6 @@ update_places (NemoPlacesSidebar *sidebar) continue; } } - g_object_unref (root); g_object_unref (mount); } @@ -1283,6 +1468,159 @@ update_places (NemoPlacesSidebar *sidebar) g_free (last_uri); } + + +static gboolean +show_iter_for_file (NemoPlacesSidebar *sidebar, NemoFile *file, GtkTreeIter *iter) +{ + GtkTreeModel *model; + NemoFile *parent_file; + GtkTreeIter parent_iter; + GtkTreePath *path, *sort_path; + GtkTreeIter cur_iter; + + if (sidebar->store == NULL) { + return FALSE; + } + model = GTK_TREE_MODEL (sidebar->store); + + /* check if file is visible in the same root as the currently selected folder is */ + gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); + if (path != NULL) { + if (gtk_tree_model_get_iter (model, &cur_iter, path) && + fm_tree_model_file_get_iter (sidebar->store, 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 (sidebar->store, + iter, file, NULL)) { + return TRUE; + } + + parent_file = nemo_file_get_parent (file); + + if (parent_file == NULL) { + return FALSE; + } + if (!show_iter_for_file (sidebar, 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 + ( GTK_TREE_MODEL_SORT(sidebar->sort_model), path); + gtk_tree_path_free (path); + gtk_tree_view_expand_row (sidebar->tree_view, sort_path, FALSE); + gtk_tree_path_free (sort_path); + + return FALSE; +} + +static gboolean +show_selection_idle_callback (gpointer callback_data) +{ + NemoPlacesSidebar *sidebar; + + NemoFile *file, *old_file; + GtkTreeIter iter; + GtkTreePath *path, *sort_path=NULL; + + sidebar = NEMO_PLACES_SIDEBAR (callback_data); + + sidebar->show_selection_idle_id = 0; + + file = nemo_file_get_by_uri (sidebar->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; + } + } + + sidebar->selecting = TRUE; + if (!show_iter_for_file (sidebar, file, &iter)) { + nemo_file_unref (file); + return FALSE; + } + sidebar->selecting = FALSE; + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); + if (path == NULL) { + g_warning("Cannot get child path from iter"); + return FALSE; + } + // The empty path [] refers to top_root_node or dummy — skip + if (gtk_tree_path_get_depth(path) == 0) { + g_debug("Skipping sort conversion for invisible top_root_node"); + gtk_tree_path_free(path); + return FALSE; + } + + g_assert(sidebar->sort_model != NULL); + g_assert(sidebar->store != NULL); + GtkTreeModel *real_child = gtk_tree_model_sort_get_model(GTK_TREE_MODEL_SORT (sidebar->sort_model)); + g_assert(real_child == GTK_TREE_MODEL(sidebar->store)); + + sort_path = gtk_tree_model_sort_convert_child_path_to_path + (GTK_TREE_MODEL_SORT (sidebar->sort_model), path); +if (!sort_path) { + // g_warning("sort_path == NULL for path %s", gtk_tree_path_to_string(path)); + + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + if (gtk_widget_get_realized (GTK_WIDGET (sidebar->tree_view))) { + gtk_tree_view_scroll_to_cell (sidebar->tree_view, path, NULL, FALSE, 0, 0); + } + gtk_tree_path_free(path); + return FALSE; +} + + gtk_tree_path_free (path); + gtk_tree_view_set_cursor (sidebar->tree_view, sort_path, NULL, FALSE); + if (gtk_widget_get_realized (GTK_WIDGET (sidebar->tree_view))) { + gtk_tree_view_scroll_to_cell (sidebar->tree_view, sort_path, NULL, FALSE, 0, 0); + } + gtk_tree_path_free (sort_path); + + nemo_file_unref (file); + refresh_highlight (sidebar); + + return FALSE; +} + +static void +schedule_show_selection (NemoPlacesSidebar *sidebar) +{ + if (sidebar->show_selection_idle_id == 0) { + sidebar->show_selection_idle_id = g_idle_add (show_selection_idle_callback, sidebar); + } +} + +static void +schedule_select_and_show_location (NemoPlacesSidebar *sidebar, char *location) +{ + if (sidebar->selection_location != NULL) { + g_free (sidebar->selection_location); + } + sidebar->selection_location = g_strdup (location); + schedule_show_selection (sidebar); +} + + static void mount_added_callback (GVolumeMonitor *volume_monitor, GMount *mount, @@ -1420,6 +1758,7 @@ clicked_eject_button (NemoPlacesSidebar *sidebar, } } + gdk_event_free(event); return FALSE; } @@ -1433,56 +1772,66 @@ desktop_setting_changed_callback (gpointer user_data) update_places (sidebar); } + + + static void loading_uri_callback (NemoWindow *window, - char *location, - NemoPlacesSidebar *sidebar) + char *location, + NemoPlacesSidebar *sidebar) { GtkTreeSelection *selection; GtkTreeIter iter_cat, iter_child; gboolean valid_cat, valid_child; char *uri; gboolean found = FALSE; + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); - if (strcmp (sidebar->uri, location) != 0) { - g_free (sidebar->uri); - sidebar->uri = g_strdup (location); - - /* set selection if any place matches location */ - selection = gtk_tree_view_get_selection (sidebar->tree_view); - gtk_tree_selection_unselect_all (selection); - valid_cat = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store_filter), - &iter_cat); - - while (valid_cat) { - valid_child = gtk_tree_model_iter_children (GTK_TREE_MODEL (sidebar->store_filter), - &iter_child, - &iter_cat); - while (valid_child) { - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter_child, - PLACES_SIDEBAR_COLUMN_URI, &uri, - -1); - if (uri != NULL) { - if (strcmp (uri, location) == 0) { - g_free (uri); - gtk_tree_selection_select_iter (selection, &iter_child); - found = TRUE; - break; - } - g_free (uri); - } - valid_child = gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store_filter), - &iter_child); - } - if (found) { - break; - } - valid_cat = gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store_filter), - &iter_cat); + if(sidebar->use_file_treeview) { + schedule_select_and_show_location (sidebar, location); + } else { + if (strcmp (sidebar->selection_location, location) != 0) { + g_free (sidebar->selection_location); + sidebar->selection_location = g_strdup (location); + + /* set selection if any place matches location */ + selection = gtk_tree_view_get_selection (sidebar->tree_view); + gtk_tree_selection_unselect_all (selection); + valid_cat = gtk_tree_model_get_iter_first (model, + &iter_cat); + + while (valid_cat) { + valid_child = gtk_tree_model_iter_children (model, + &iter_child, + &iter_cat); + while (valid_child) { + gtk_tree_model_get (model, &iter_child, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + if (uri != NULL) { + if (strcmp (uri, location) == 0) { + g_free (uri); + gtk_tree_selection_select_iter (selection, &iter_child); + found = TRUE; + break; + } + g_free (uri); + } + valid_child = gtk_tree_model_iter_next (model, + &iter_child); + } + if (found) { + break; + } + valid_cat = gtk_tree_model_iter_next (model, + &iter_cat); + } } - } + } } + + typedef struct { NemoPlacesSidebar *sidebar; GdkRectangle rect; @@ -1617,13 +1966,16 @@ compute_drop_position (GtkTreeView *tree_view, PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, PLACES_SIDEBAR_COLUMN_URI, &drop_target_uri, -1); + if(drop_target_uri==NULL) { + drop_target_uri = get_fm_tree_model_uri (sidebar, model, &iter); + } if (!cat_is_expanded (sidebar, section_type) && place_type == PLACES_HEADING) { if (sidebar->expand_timeout_source > 0) { goto fail; } CategoryExpandPayload *payload; GtkTreeViewColumn *col; - col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 2); + col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 0); payload = g_new0 (CategoryExpandPayload, 1); payload->sidebar = sidebar; gtk_tree_view_get_cell_area (tree_view, @@ -1664,7 +2016,7 @@ compute_drop_position (GtkTreeView *tree_view, GdkRectangle rect; GtkTreeViewColumn *col; - col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 1); + col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 0); gtk_tree_view_get_cell_area (tree_view, *path, col, @@ -1778,7 +2130,9 @@ drag_motion_callback (GtkTreeView *tree_view, if (!sidebar->in_drag) { sidebar->in_drag = TRUE; - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); + if(!sidebar->use_file_treeview) { + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); + } } path = NULL; @@ -1803,12 +2157,16 @@ drag_motion_callback (GtkTreeView *tree_view, if (sidebar->drag_list == NULL) { action = 0; } else { - gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store_filter), + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), + gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + if(uri == NULL) { + uri = get_fm_tree_model_uri (sidebar, model, &iter); + } nemo_drag_default_drop_action_for_icons (context, uri, sidebar->drag_list, @@ -1952,6 +2310,24 @@ get_selected_iter (NemoPlacesSidebar *sidebar, return gtk_tree_selection_get_selected (selection, NULL, iter); } +static gboolean +get_selected_iter_for_store (NemoPlacesSidebar *sidebar, + GtkTreeIter *fm_iter) +{ + GtkTreeSelection *selection; + GtkTreeModel *model; + GtkTreeIter view_iter; + + selection = gtk_tree_view_get_selection (sidebar->tree_view); + + if (!gtk_tree_selection_get_selected (selection, &model, &view_iter)) + return FALSE; + + get_fm_tree_model_iter (model, &view_iter, fm_iter); + + return TRUE; +} + static void update_bookmark_breakpoint (NemoPlacesSidebar *sidebar, SectionType old_type, @@ -1977,11 +2353,11 @@ reorder_bookmarks (NemoPlacesSidebar *sidebar, int old_position; /* Get the selected path */ - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &old_section_type, PLACES_SIDEBAR_COLUMN_INDEX, &old_position, @@ -2009,7 +2385,9 @@ idle_hide_bookmarks (gpointer user_data) if (sidebar->in_drag) { sidebar->in_drag = FALSE; - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); + if(!sidebar->use_file_treeview) { + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); + } } return FALSE; @@ -2121,7 +2499,9 @@ drag_data_received_callback (GtkWidget *widget, gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &drop_uri, -1); - + if(drop_uri==NULL) { + drop_uri = get_fm_tree_model_uri (sidebar, model, &iter); + } switch (info) { case TEXT_URI_LIST: selection_list = build_selection_list ((const gchar *) gtk_selection_data_get_data (selection_data)); @@ -2242,6 +2622,17 @@ check_visibility (GMount *mount, } } +static void +set_action_sensitive (GtkActionGroup *action_group, + const gchar *name, + gboolean sensitive) +{ + GtkAction *action; + + action = gtk_action_group_get_action (action_group, name); + gtk_action_set_sensitive (action, sensitive); +} + static void set_action_visible (GtkActionGroup *action_group, const gchar *name, @@ -2253,95 +2644,469 @@ set_action_visible (GtkActionGroup *action_group, gtk_action_set_visible (action, visible); } +static GdkAtom copied_files_atom; + static void -update_menu_states (NemoPlacesSidebar *sidebar) +clipboard_contents_received_callback (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) { - GtkTreeIter iter; - PlaceType type; - GDrive *drive = NULL; - GVolume *volume = NULL; - GMount *mount = NULL; - GFile *location; - NemoDirectory *directory = NULL; - gboolean show_mount; - gboolean show_unmount; - gboolean show_eject; - gboolean show_rescan; - gboolean show_start; - gboolean show_stop; - gboolean show_empty_trash; - gboolean show_properties; - char *uri = NULL; + NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (data); - type = PLACES_BUILT_IN; + if (gtk_selection_data_get_data_type (selection_data) == copied_files_atom + && gtk_selection_data_get_length (selection_data) > 0) { + set_action_sensitive (sidebar->tv_action_group, NEMO_ACTION_PASTE, TRUE); + } +} - if (sidebar->popup_menu == NULL) { - return; - } - if (get_selected_iter (sidebar, &iter)) { - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - PLACES_SIDEBAR_COLUMN_VOLUME, &volume, - PLACES_SIDEBAR_COLUMN_MOUNT, &mount, - PLACES_SIDEBAR_COLUMN_URI, &uri, - -1); - } +static gboolean +is_parent_writable (NemoFile *file) +{ + NemoFile *parent; + gboolean result; - if (uri) { - NemoFile *file = nemo_file_get_by_uri (uri); - NemoFile *parent = nemo_file_get_parent (file); + parent = nemo_file_get_parent (file); - GList *selection = g_list_prepend (NULL, file); + /* No parent directory, return FALSE */ + if (parent == NULL) { + return FALSE; + } - nemo_action_manager_update_action_states (sidebar->action_manager, - sidebar->action_action_group, - selection, - parent, - TRUE, - FALSE, - GTK_WINDOW (sidebar->window)); - nemo_file_list_free (selection); - nemo_file_unref (parent); - } + result = nemo_file_can_write (parent); + nemo_file_unref (parent); - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_ADD_BOOKMARK, (type == PLACES_MOUNTED_VOLUME)); - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_REMOVE, (type == PLACES_BOOKMARK)); - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_RENAME, (type == PLACES_BOOKMARK)); - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_EMPTY_TRASH_CONDITIONAL, !nemo_trash_monitor_is_empty ()); + return result; +} - check_visibility (mount, volume, drive, - &show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop); +static GtkWindow * +fm_tree_view_get_containing_window (NemoPlacesSidebar *sidebar) +{ + GtkWidget *window; - /* We actually want both eject and unmount since eject will unmount all volumes. - * TODO: hide unmount if the drive only has a single mountable volume - */ - show_empty_trash = (uri != NULL) && - (!strcmp (uri, "trash:///")); + g_assert (NEMO_PLACES_SIDEBAR (sidebar)); - g_free (uri); + window = gtk_widget_get_ancestor (GTK_WIDGET (sidebar), GTK_TYPE_WINDOW); + if (window == NULL) { + return NULL; + } - /* Only show properties for local mounts */ - show_properties = (mount != NULL); - if (mount != NULL) { - location = g_mount_get_default_location (mount); - directory = nemo_directory_get (location); + return GTK_WINDOW (window); +} - show_properties = nemo_directory_is_local (directory); +static void +fm_tree_view_delete_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + GList *location_list; - nemo_directory_unref (directory); - g_object_unref (location); + if (!g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_ENABLE_DELETE)) { + return; } - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_MOUNT_VOLUME, show_mount); - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_UNMOUNT_VOLUME, show_unmount); - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_EJECT_VOLUME, show_eject); - set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_DETECT_MEDIA, show_rescan); + location_list = g_list_prepend (NULL, + nemo_file_get_location (sidebar->popup_file)); + + nemo_file_operations_delete (location_list, fm_tree_view_get_containing_window (sidebar), NULL, NULL); + g_list_free_full (location_list, g_object_unref); +} + +static void +fm_tree_view_trash_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + GList *list; + + if (!nemo_file_can_trash (sidebar->popup_file)) { + return; + } + + list = g_list_prepend (NULL, + nemo_file_get_location (sidebar->popup_file)); + + nemo_file_operations_trash_or_delete (list, + fm_tree_view_get_containing_window (sidebar), + NULL, NULL); + g_list_free_full (list, g_object_unref); +} + +static void +fm_tree_view_pin_unpin_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + nemo_file_set_pinning (sidebar->popup_file, + !nemo_file_get_pinning (sidebar->popup_file)); +} + +static void +paste_clipboard_data (NemoPlacesSidebar *sidebar, + 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 (sidebar->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 (sidebar->tree_view), + NULL, NULL); + + /* If items are cut then remove from clipboard */ + if (cut) { + gtk_clipboard_clear (nemo_clipboard_get (GTK_WIDGET (sidebar))); + } + + g_list_free_full (item_uris, g_free); + } +} + +static void +paste_into_clipboard_received_callback (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) +{ + char *directory_uri; + NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (data); + + directory_uri = nemo_file_get_uri (sidebar->popup_file); + + paste_clipboard_data (sidebar, selection_data, directory_uri); + + g_free (directory_uri); +} + +static void +fm_tree_view_paste_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + gtk_clipboard_request_contents (nemo_clipboard_get (GTK_WIDGET (sidebar->tree_view)), + copied_files_atom, + paste_into_clipboard_received_callback, sidebar); +} + +static void +copy_or_cut_files (NemoPlacesSidebar *sidebar, + gboolean cut) +{ + GtkClipboard *clipboard; + char *status_string, *name; + NemoClipboardInfo info; + GtkTargetList *target_list; + GtkTargetEntry *targets; + int n_targets; + + info.cut = cut; + info.files = g_list_prepend (NULL, sidebar->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); + + clipboard = nemo_clipboard_get (GTK_WIDGET (sidebar)); + + gtk_clipboard_set_with_data (clipboard, + targets, n_targets, + nemo_get_clipboard_callback, nemo_clear_clipboard_callback, + NULL); + gtk_clipboard_set_can_store (clipboard, NULL, 0); + 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 (sidebar->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 (sidebar->window, + status_string); + g_free (status_string); +} + +static void +fm_tree_view_cut_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + copy_or_cut_files (sidebar, TRUE); +} + +static void +fm_tree_view_copy_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + copy_or_cut_files (sidebar, FALSE); +} + + +static void +update_menu_states_tree (NemoPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + 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; + gboolean is_dir; + gboolean can_write; + NemoFile *file=NULL; + + if (get_selected_iter_for_store (sidebar, &iter)) { + file = fm_tree_model_iter_get_file (FM_TREE_MODEL (sidebar->store), &iter); + } + using_browser = g_settings_get_boolean (nemo_preferences, + NEMO_PREFERENCES_ALWAYS_USE_BROWSER); + + gboolean show_unmount = FALSE; + gboolean show_eject = FALSE; + GMount *mount = NULL; + + NemoFile *parent = nemo_file_get_parent (file); + GList *selected = g_list_prepend (NULL, file); + + nemo_action_manager_update_action_states (sidebar->action_manager, + sidebar->action_action_group, + selected, + parent, + FALSE, + FALSE, + GTK_WINDOW (sidebar->window)); + nemo_file_ref (file); + nemo_file_list_free (selected); // fils is unrefed here + nemo_file_unref (parent); + + is_dir = nemo_file_is_directory (file); + can_write = nemo_file_can_write (file); + + set_action_sensitive (sidebar->tv_action_group, NEMO_ACTION_OPEN_ALTERNATE, is_dir); + set_action_sensitive (sidebar->tv_action_group, NEMO_ACTION_OPEN_IN_NEW_TAB, is_dir); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_OPEN_ALTERNATE, using_browser); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_OPEN_IN_NEW_TAB, using_browser); + set_action_sensitive (sidebar->tv_action_group, NEMO_ACTION_NEW_FOLDER, is_dir && can_write); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_NEW_FOLDER, !nemo_file_is_in_favorites (file)); + set_action_sensitive (sidebar->tv_action_group, NEMO_ACTION_PASTE, FALSE); + + if (is_dir && can_write) { + gtk_clipboard_request_contents (nemo_clipboard_get (GTK_WIDGET (sidebar->tree_view)), + copied_files_atom, + clipboard_contents_received_callback, sidebar); + } + + can_move_file_to_trash = nemo_file_can_trash (file); + set_action_sensitive (sidebar->tv_action_group, NEMO_ACTION_TRASH, can_move_file_to_trash); + + if (g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_ENABLE_DELETE)) { + parent_file_is_writable = is_parent_writable (file); + file_is_home_or_desktop = nemo_file_is_home (file) + || nemo_file_is_desktop_directory (file); + file_is_special_link = NEMO_IS_DESKTOP_ICON_FILE (file); + + can_delete_file = parent_file_is_writable + && !file_is_home_or_desktop + && !file_is_special_link; + + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_DELETE, TRUE); + set_action_sensitive (sidebar->tv_action_group, NEMO_ACTION_DELETE, can_delete_file); + } else { + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_DELETE, FALSE); + } + + mount = fm_tree_model_get_mount_for_root_node_file (sidebar->store, file); + if (mount) { + show_unmount = g_mount_can_unmount (mount); + show_eject = g_mount_can_eject (mount); + } + + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_UNMOUNT_VOLUME, show_unmount); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_EJECT_VOLUME, show_eject); + + if (!nemo_file_is_in_favorites (file)) { + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_PIN_FILE, !nemo_file_get_pinning (file)); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_UNPIN_FILE, nemo_file_get_pinning (file)); + } else { + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_PIN_FILE, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_UNPIN_FILE, FALSE); + } + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_PROPERTIES, FALSE); + nemo_file_unref (file); +} + +static void +update_menu_states (NemoPlacesSidebar *sidebar) +{ + GtkTreeIter iter; + PlaceType type; + GDrive *drive = NULL; + GVolume *volume = NULL; + GMount *mount = NULL; + GFile *location; + NemoDirectory *directory = NULL; + gboolean show_mount; + gboolean show_unmount; + gboolean show_eject; + gboolean show_rescan; + gboolean show_start; + gboolean show_stop; + gboolean show_empty_trash; + gboolean show_properties; + char *uri = NULL; + + type = PLACES_BUILT_IN; + + if (sidebar->popup_menu == NULL) { + return; + } + + if (get_selected_iter_for_store (sidebar, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + } + if(uri==NULL && sidebar->use_file_treeview) { + uri = get_fm_tree_model_uri (sidebar, sidebar->store, &iter); + } + if (uri) { + NemoFile *file = sidebar->use_file_treeview ? fm_tree_model_iter_get_file(sidebar->store, &iter) : nemo_file_get_by_uri (uri); + if(file) { + directory = nemo_directory_get_for_file(file); + show_properties = nemo_directory_is_local (directory); + } + NemoFile *parent = nemo_file_get_parent (file); + + GList *selection = g_list_prepend (NULL, file); + + nemo_action_manager_update_action_states (sidebar->action_manager, + sidebar->action_action_group, + selection, + parent, + TRUE, + FALSE, + GTK_WINDOW (sidebar->window)); + /* nemo_file_list_free (selection); internally unrefs file! */ + nemo_file_list_free (selection); + nemo_file_unref (parent); + } + + /* Only show properties for local mounts */ + //show_properties = (mount != NULL); + if (mount != NULL) { + location = g_mount_get_default_location (mount); + directory = nemo_directory_get (location); + + show_properties = nemo_directory_is_local (directory); + + nemo_directory_unref (directory); + g_object_unref (location); + } + + switch(type) { + case PLACES_TREE_FOLDER: { + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_CUT, TRUE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_COPY, TRUE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_PASTE, TRUE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_TRASH, TRUE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_DELETE, TRUE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_PROPERTIES, FALSE); + if(sidebar->use_file_treeview)update_menu_states_tree(sidebar); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_ADD_BOOKMARK, TRUE); + /* + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_ADD_BOOKMARK, TRUE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_REMOVE, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_RENAME, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_NEW_FOLDER, TRUE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_PROPERTIES, show_properties); + * */ + } + return; + case PLACES_BUILT_IN: + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_ADD_BOOKMARK, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_REMOVE, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_RENAME, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_PROPERTIES, show_properties); + break; + case PLACES_MOUNTED_VOLUME: { + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_ADD_BOOKMARK, TRUE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_REMOVE, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_RENAME, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_PROPERTIES, show_properties); + } + break; + case PLACES_BOOKMARK: + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_ADD_BOOKMARK, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_REMOVE, TRUE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_RENAME, TRUE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_PROPERTIES, show_properties); + break; + default: + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_ADD_BOOKMARK, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_REMOVE, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_RENAME, FALSE); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_PROPERTIES, FALSE); + break; + } + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_NEW_FOLDER, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_DELETE, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_UNMOUNT_VOLUME, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_EJECT_VOLUME, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_PIN_FILE, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_UNPIN_FILE, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_TRASH, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_CUT, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_COPY, FALSE); + set_action_visible (sidebar->tv_action_group, NEMO_ACTION_PASTE, FALSE); + + + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_EMPTY_TRASH_CONDITIONAL, !nemo_trash_monitor_is_empty ()); + + + /* We actually want both eject and unmount since eject will unmount all volumes. + * TODO: hide unmount if the drive only has a single mountable volume + */ + show_empty_trash = (uri != NULL) && + (!strcmp (uri, "trash:///")); + + g_free (uri); + + check_visibility (mount, volume, drive, + &show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_MOUNT_VOLUME, show_mount); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_UNMOUNT_VOLUME, show_unmount); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_EJECT_VOLUME, show_eject); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_SIDEBAR_DETECT_MEDIA, show_rescan); set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_START_VOLUME, show_start); set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_STOP_VOLUME, show_stop); + set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_EMPTY_TRASH_CONDITIONAL, show_empty_trash); + /* set_action_visible (sidebar->bookmark_action_group, NEMO_ACTION_PROPERTIES, show_properties); + */ /* Adjust start/stop items to reflect the type of the drive */ GtkAction *start_action, *stop_action; @@ -2374,7 +3139,7 @@ update_menu_states (NemoPlacesSidebar *sidebar) default: case G_DRIVE_START_STOP_TYPE_UNKNOWN: - /* uses defaults set above */ + // Uses defaults set above break; } } @@ -2385,6 +3150,7 @@ update_menu_states (NemoPlacesSidebar *sidebar) } /* Callback used when the selection in the shortcuts tree changes */ + static void bookmarks_selection_changed_cb (GtkTreeSelection *selection, NemoPlacesSidebar *sidebar) @@ -2392,6 +3158,7 @@ bookmarks_selection_changed_cb (GtkTreeSelection *selection, update_menu_states (sidebar); } + static void volume_mounted_cb (GVolume *volume, gboolean success, @@ -2471,9 +3238,12 @@ open_selected_bookmark (NemoPlacesSidebar *sidebar, if (!iter) { return; } - - gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); - + GtkTreeIter fm_iter; + get_fm_tree_model_iter (model, iter, &fm_iter); + gtk_tree_model_get (sidebar->store, &fm_iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + if (!uri) { + uri = get_fm_tree_model_uri (sidebar, sidebar->store, &fm_iter); + } if (uri != NULL) { DEBUG ("Activating bookmark %s", uri); @@ -2498,11 +3268,21 @@ open_selected_bookmark (NemoPlacesSidebar *sidebar, GVolume *volume; NemoWindowSlot *slt; - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - PLACES_SIDEBAR_COLUMN_VOLUME, &volume, - -1); - + if(sidebar->use_file_treeview) { + if( iter_is_valid ( FM_TREE_MODEL(sidebar->store), &fm_iter)) { + gtk_tree_model_get ( GTK_TREE_MODEL (sidebar->store), &fm_iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + } else { + return; + } + } else { + gtk_tree_model_get ( GTK_TREE_MODEL (sidebar->store), &fm_iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + } if (volume != NULL && !sidebar->mounting) { sidebar->mounting = TRUE; @@ -2577,17 +3357,19 @@ open_shortcut_in_new_tab_cb (GtkAction *item, static void add_bookmark (NemoPlacesSidebar *sidebar) { - GtkTreeModel *model; + //GtkTreeModel *model; GtkTreeIter iter; char *uri; GFile *location; NemoBookmark *bookmark; - model = gtk_tree_view_get_model (sidebar->tree_view); - - if (get_selected_iter (sidebar, &iter)) { - gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + //model = gtk_tree_view_get_model (sidebar->tree_view); + if (get_selected_iter_for_store (sidebar, &iter)) { + gtk_tree_model_get (sidebar->store, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + if (uri == NULL) { + uri = get_fm_tree_model_uri (sidebar, sidebar->store, &iter); + } if (uri == NULL) { return; } @@ -2620,9 +3402,10 @@ rename_selected_bookmark (NemoPlacesSidebar *sidebar) GtkTreePath *path; GtkTreeViewColumn *column; PlaceType type; + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); if (get_selected_iter (sidebar, &iter)) { - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, -1); @@ -2630,8 +3413,8 @@ rename_selected_bookmark (NemoPlacesSidebar *sidebar) return; } - path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iter); - column = gtk_tree_view_get_column (GTK_TREE_VIEW (sidebar->tree_view), 2); + path = gtk_tree_model_get_path (model, &iter); + column = gtk_tree_view_get_column (GTK_TREE_VIEW (sidebar->tree_view), 0); g_object_set (sidebar->editable_renderer, "editable", TRUE, NULL); gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (sidebar->tree_view), path, column, sidebar->editable_renderer, TRUE); @@ -2654,11 +3437,11 @@ remove_selected_bookmarks (NemoPlacesSidebar *sidebar) PlaceType type; int index; - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, -1); @@ -2666,7 +3449,7 @@ remove_selected_bookmarks (NemoPlacesSidebar *sidebar) return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_INDEX, &index, -1); @@ -2690,11 +3473,11 @@ mount_shortcut_cb (GtkAction *item, GtkTreeIter iter; GVolume *volume; - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_VOLUME, &volume, -1); @@ -2772,11 +3555,11 @@ do_unmount_selection (NemoPlacesSidebar *sidebar) GtkTreeIter iter; GMount *mount; - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_MOUNT, &mount, -1); @@ -2920,11 +3703,11 @@ eject_shortcut_cb (GtkAction *item, GVolume *volume; GDrive *drive; - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_MOUNT, &mount, PLACES_SIDEBAR_COLUMN_VOLUME, &volume, PLACES_SIDEBAR_COLUMN_DRIVE, &drive, @@ -2949,7 +3732,7 @@ eject_or_unmount_bookmark (NemoPlacesSidebar *sidebar, GDrive *drive; gboolean ret; - model = GTK_TREE_MODEL (sidebar->store_filter); + model = gtk_tree_view_get_model (sidebar->tree_view); if (!path) { return FALSE; @@ -2994,7 +3777,7 @@ eject_or_unmount_selection (NemoPlacesSidebar *sidebar) return FALSE; } - path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iter); + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store), &iter); if (path == NULL) { return FALSE; } @@ -3035,11 +3818,11 @@ rescan_shortcut_cb (GtkAction *item, GtkTreeIter iter; GDrive *drive; - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_DRIVE, &drive, -1); @@ -3079,11 +3862,11 @@ start_shortcut_cb (GtkAction *item, GtkTreeIter iter; GDrive *drive; - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_DRIVE, &drive, -1); @@ -3133,11 +3916,11 @@ stop_shortcut_cb (GtkAction *item, GtkTreeIter iter; GDrive *drive; - if (!get_selected_iter (sidebar, &iter)) { + if (!get_selected_iter_for_store (sidebar, &iter)) { return; } - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_DRIVE, &drive, -1); @@ -3157,82 +3940,330 @@ empty_trash_cb (GtkAction *item, nemo_file_operations_empty_trash (GTK_WIDGET (sidebar->window)); } +static gboolean +select_first_child_row_or_expand (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + GtkTreeIter child_iter; + gboolean res; + + GtkTreePath *path = gtk_tree_model_get_path (model, iter); + if (!path) + return FALSE; + gboolean expanded = gtk_tree_view_row_expanded(sidebar->tree_view, path); + + if(expanded) { + res = gtk_tree_model_iter_children(model, &child_iter, iter); + if(res) { + *iter = child_iter; + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + } + } else + gtk_tree_view_expand_row(sidebar->tree_view, path, FALSE); + + gtk_tree_path_free (path); + return TRUE; +} + +static gboolean +goto_parent_or_collapse (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + GtkTreeIter parent_iter; + gboolean res=TRUE; + + GtkTreePath *path = gtk_tree_model_get_path (model, iter); + if (!path) + return FALSE; + gboolean expanded = gtk_tree_view_row_expanded(sidebar->tree_view, path); + + if(expanded) + gtk_tree_view_collapse_row(sidebar->tree_view, path); + else { + res = gtk_tree_model_iter_parent(model, &parent_iter, iter); + if(res) { + *iter = parent_iter; + gtk_tree_path_free (path); + path = gtk_tree_model_get_path (model, iter); + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + } + } + + gtk_tree_path_free (path); + return res; +} + + static gboolean find_prev_or_next_row (NemoPlacesSidebar *sidebar, GtkTreeIter *iter, gboolean go_up) { - GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); gboolean res; + GtkTreeIter copy; int place_type; + copy = *iter; + if (go_up) { - res = gtk_tree_model_iter_previous (model, iter); + res = gtk_tree_model_iter_previous (model, ©); } else { - res = gtk_tree_model_iter_next (model, iter); + res = gtk_tree_model_iter_next (model, ©); } if (res) { - gtk_tree_model_get (model, iter, + gtk_tree_model_get (model, ©, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, -1); if (place_type == PLACES_HEADING) { if (go_up) { - res = gtk_tree_model_iter_previous (model, iter); + res = gtk_tree_model_iter_previous (model, ©); } else { - res = gtk_tree_model_iter_next (model, iter); + res = gtk_tree_model_iter_next (model, ©); } } } + if (res) + *iter = copy; return res; } static gboolean -find_prev_row (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +goto_first_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { - return find_prev_or_next_row (sidebar, iter, TRUE); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + GtkTreePath *path; + GtkTreeIter child_iter; + + // Get the tree path of the current node + path = gtk_tree_model_get_path(model, iter); + if (!path) + return FALSE; + + // Check if this node is currently expanded in the tree view + gboolean expanded = gtk_tree_view_row_expanded(sidebar->tree_view, path); + + if (!expanded) { + gtk_tree_path_free(path); + return FALSE; // not expanded → stop here + } + + // Try to get the first child (works only if the node is expanded) + gboolean has_child = gtk_tree_model_iter_children(model, &child_iter, iter); + + gtk_tree_path_free(path); + + if (!has_child) + return FALSE; + + // Replace the current iterator with the iterator of the first child + *iter = child_iter; + return TRUE; } static gboolean -find_next_row (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +goto_prev_row_or_last_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { - return find_prev_or_next_row (sidebar, iter, FALSE); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + GtkTreeIter prev_iter; + GtkTreeIter current_iter; + GtkTreeIter deepest_iter; + + // Try to move to the previous sibling row + if (!gtk_tree_model_iter_previous(model, iter)) { + return FALSE; // No previous sibling → nothing to do + } + prev_iter = *iter; + + // Get the GtkTreePath for the new current row (the previous one) + GtkTreePath *path = gtk_tree_model_get_path(model, iter); + if (!path) + return FALSE; + + // Check if the previous row is expanded in the tree view + gboolean expanded = gtk_tree_view_row_expanded(sidebar->tree_view, path); + gtk_tree_path_free(path); + + if (!expanded) { + // Not expanded → we stop here (iter already points to it) + return TRUE; + } + + // The previous row is expanded → go down to its last visible child (recursively) + if (!gtk_tree_model_iter_children(model, ¤t_iter, &prev_iter)) { + return TRUE; // Expanded but has no children + } + + // Start with the first child + deepest_iter = current_iter; + + // Iterate through all children to find the last one + GtkTreeIter next_iter = current_iter; + while (gtk_tree_model_iter_next(model, &next_iter)) { + deepest_iter = next_iter; + } + + // Now check if the deepest_iter has children and is expanded + GtkTreePath *deepest_path = gtk_tree_model_get_path(model, &deepest_iter); + if (deepest_path) { + gboolean deepest_expanded = gtk_tree_view_row_expanded(sidebar->tree_view, deepest_path); + gtk_tree_path_free(deepest_path); + + // If the deepest child is expanded, recursively find its last child + while (deepest_expanded) { + GtkTreeIter child_iter; + if (!gtk_tree_model_iter_children(model, &child_iter, &deepest_iter)) { + break; // No more children + } + + // Find the last child of the current deepest_iter + deepest_iter = child_iter; + GtkTreeIter temp_iter = child_iter; + while (gtk_tree_model_iter_next(model, &temp_iter)) { + deepest_iter = temp_iter; + } + + // Check if the new deepest_iter is expanded + deepest_path = gtk_tree_model_get_path(model, &deepest_iter); + if (deepest_path) { + deepest_expanded = gtk_tree_view_row_expanded(sidebar->tree_view, deepest_path); + gtk_tree_path_free(deepest_path); + } else { + deepest_expanded = FALSE; + } + } + } + + // Replace current iter with the deepest child + *iter = deepest_iter; + return TRUE; } -static void -properties_cb (GtkAction *item, - NemoPlacesSidebar *sidebar) + +static gboolean +iter_equal(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b) { - GtkTreeModel *model; - GtkTreePath *path = NULL; - GtkTreeIter iter; - GList *list; - NemoFile *file; - char *uri; + GtkTreePath *path_a = gtk_tree_model_get_path(model, a); + GtkTreePath *path_b = gtk_tree_model_get_path(model, b); - model = gtk_tree_view_get_model (sidebar->tree_view); - gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); + gboolean equal = gtk_tree_path_compare(path_a, path_b) == 0; - if (path == NULL || !gtk_tree_model_get_iter (model, &iter, path)) { - gtk_tree_path_free (path); - return; - } + gtk_tree_path_free(path_a); + gtk_tree_path_free(path_b); - gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + return equal; +} - if (uri != NULL) { +static gboolean +find_parent_or_next_sibling_row(NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + GtkTreeIter current_iter = *iter; + + while (TRUE) { + GtkTreeIter parent_iter; + if (!gtk_tree_model_iter_parent(model, &parent_iter, ¤t_iter)) { + // no parent → no higher sibling found + *iter = current_iter; + if (gtk_tree_model_iter_next(model, ¤t_iter)) { + *iter = current_iter; + return TRUE; + } + return FALSE; + } - file = nemo_file_get_by_uri (uri); - list = g_list_prepend (NULL, nemo_file_ref (file)); + // Search for the next visible sibling beside the parent + GtkTreeIter child_iter; + if (!gtk_tree_model_iter_children(model, &child_iter, &parent_iter)) { + current_iter = parent_iter; + continue; // No children → one level up + } + + do { + if (iter_equal(model, &child_iter, ¤t_iter)) { + // We are at the current iterator → check next child + if (gtk_tree_model_iter_next(model, &child_iter)) { + *iter = child_iter; + return TRUE; + } else { + break; // no mre siblings -> next higher level + } + } + } while (gtk_tree_model_iter_next(model, &child_iter)); + + // Next sibling not found on this level → one level higher + current_iter = parent_iter; + } +} - nemo_properties_window_present (list, GTK_WIDGET (sidebar), NULL); - nemo_file_list_free (list); - g_free (uri); +static gboolean +select_prev_or_next_node (NemoPlacesSidebar *sidebar, + GtkTreeIter *iter, + gboolean go_up) +{ + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + gboolean res; + GtkTreeIter iterCopy; + + iterCopy = *iter; + + if (go_up) { + res = goto_prev_row_or_last_child_if_expanded (sidebar, &iterCopy); + if(!res) { + // go to parent + iterCopy = *iter; + res = gtk_tree_model_iter_parent(model, &iterCopy, iter); + } + } else { + res = goto_first_child_if_expanded (sidebar, &iterCopy); + if(!res) { + // go to next child in same row + iterCopy = *iter; + res = gtk_tree_model_iter_next (model, &iterCopy); + if(!res) { + iterCopy = *iter; + res = find_parent_or_next_sibling_row(sidebar, &iterCopy); + if(!res) + iterCopy = *iter; + } + } + } + if (res) { + *iter = iterCopy; + GtkTreePath *path = gtk_tree_model_get_path (model, &iterCopy); + + GtkTreeSelection *sel = gtk_tree_view_get_selection(sidebar->tree_view); + gtk_tree_selection_unselect_all(sel); + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + gtk_tree_path_free (path); } + return res; - gtk_tree_path_free (path); +} + + +static gboolean +find_next_row (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + return find_prev_or_next_row (sidebar, iter, FALSE); +} + +static void +properties_cb (GtkAction *item, + NemoPlacesSidebar *sidebar) +{ + GList *list; + if(sidebar->popup_file == NULL) return; + list = g_list_prepend (NULL, nemo_file_ref (sidebar->popup_file)); + + nemo_properties_window_present (list, GTK_WIDGET (sidebar->tree_view), NULL); + + nemo_file_list_free (list); } static gboolean @@ -3243,14 +4274,14 @@ nemo_places_sidebar_focus (GtkWidget *widget, GtkTreePath *path; GtkTreeIter iter, child_iter; gboolean res; - + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); res = get_selected_iter (sidebar, &iter); if (!res) { - gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store_filter), &iter); - gtk_tree_model_iter_children (GTK_TREE_MODEL (sidebar->store_filter), &child_iter, &iter); + gtk_tree_model_get_iter_first (model, &iter); + gtk_tree_model_iter_children (model, &child_iter, &iter); res = find_next_row (sidebar, &child_iter); if (res) { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iter); + path = gtk_tree_model_get_path (model, &iter); gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); gtk_tree_path_free (path); } @@ -3267,68 +4298,76 @@ bookmarks_key_press_event_cb (GtkWidget *widget, { guint modifiers; GtkTreeIter selected_iter; - GtkTreePath *path; - if (event->keyval == GDK_KEY_slash || - event->keyval == GDK_KEY_KP_Divide || - event->keyval == GDK_KEY_asciitilde) { - if (gtk_bindings_activate_event (G_OBJECT (sidebar->window), event)) { - return GDK_EVENT_STOP; - } + switch (event->keyval) { + case GDK_KEY_slash: + case GDK_KEY_KP_Divide: + case GDK_KEY_asciitilde: + if (gtk_bindings_activate_event(G_OBJECT(sidebar->window), event)) + return GDK_EVENT_STOP; + break; + default: + break; } - if (!get_selected_iter (sidebar, &selected_iter)) { + if (!get_selected_iter (sidebar, &selected_iter)) return FALSE; - } modifiers = gtk_accelerator_get_default_mod_mask (); - if ((event->keyval == GDK_KEY_Return || - event->keyval == GDK_KEY_KP_Enter || - event->keyval == GDK_KEY_ISO_Enter || - event->keyval == GDK_KEY_space)) { - NemoWindowOpenFlags flags = 0; - - if ((event->state & modifiers) == GDK_SHIFT_MASK) { - flags = NEMO_WINDOW_OPEN_FLAG_NEW_TAB; - } else if ((event->state & modifiers) == GDK_CONTROL_MASK) { - flags = NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW; - } - - open_selected_bookmark (sidebar, GTK_TREE_MODEL (sidebar->store_filter), - &selected_iter, flags); - return TRUE; - } - - if (event->keyval == GDK_KEY_Down && - (event->state & modifiers) == GDK_MOD1_MASK) { - return eject_or_unmount_selection (sidebar); - } - - if (event->keyval == GDK_KEY_Up) { - if (find_prev_row (sidebar, &selected_iter)) { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &selected_iter); - gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); - gtk_tree_path_free (path); - } - return TRUE; - } - - if (event->keyval == GDK_KEY_Down) { - if (find_next_row (sidebar, &selected_iter)) { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &selected_iter); - gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); - gtk_tree_path_free (path); - } - return TRUE; - } - - if ((event->keyval == GDK_KEY_F2) - && (event->state & modifiers) == 0) { - rename_selected_bookmark (sidebar); - return TRUE; + switch(event->keyval) { + case GDK_KEY_Return: + case GDK_KEY_KP_Enter: + case GDK_KEY_ISO_Enter: + case GDK_KEY_space: + NemoWindowOpenFlags flags = 0; + if ((event->state & modifiers) == GDK_SHIFT_MASK) { + flags = NEMO_WINDOW_OPEN_FLAG_NEW_TAB; + } else if ((event->state & modifiers) == GDK_CONTROL_MASK) { + flags = NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW; + } + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); + open_selected_bookmark (sidebar, model, + &selected_iter, flags); + return TRUE; + break; + case GDK_KEY_Down: + if ((event->state & modifiers) == GDK_MOD1_MASK) { + return eject_or_unmount_selection (sidebar); + } else { + if ((event->state & modifiers) == 0) { + select_prev_or_next_node (sidebar, &selected_iter, FALSE); + return TRUE; + } + } + break; + case GDK_KEY_Up: + if ((event->state & modifiers) == 0) { + select_prev_or_next_node (sidebar, &selected_iter, TRUE); + return TRUE; + } + break; + case GDK_KEY_F2: + if ((event->state & modifiers) == 0) { + rename_selected_bookmark (sidebar); + return TRUE; + } + break; + case GDK_KEY_Left: + if ((event->state & modifiers) == 0) { + goto_parent_or_collapse(sidebar, &selected_iter); + return TRUE; + } + break; + case GDK_KEY_Right: + if ((event->state & modifiers) == 0) { + select_first_child_row_or_expand(sidebar, &selected_iter); + return TRUE; + } + break; + default: + return FALSE; } - return FALSE; } @@ -3339,8 +4378,8 @@ run_action_callback (GtkAction *action, gpointer user_data) gchar *uri = NULL; GtkTreeIter iter; - if (get_selected_iter (sidebar, &iter)) { - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + if (get_selected_iter_for_store (sidebar, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); } @@ -3361,6 +4400,35 @@ run_action_callback (GtkAction *action, gpointer user_data) g_free (uri); } +static gboolean +free_popup_file_in_idle_cb (gpointer user_data) +{ + NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (user_data); + + if (sidebar->popup_file != NULL) { + nemo_file_unref (sidebar->popup_file); + sidebar->popup_file = NULL; + } + sidebar->popup_file_idle_handler = 0; + return FALSE; +} + +static void +popup_menu_deactivated (GtkMenuShell *menu_shell, gpointer user_data) +{ + NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (user_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 (sidebar->popup_file != NULL && + sidebar->popup_file_idle_handler == 0) { + sidebar->popup_file_idle_handler = g_idle_add (free_popup_file_in_idle_cb, sidebar); + } +} + #if GTK_CHECK_VERSION (3, 24, 8) static void moved_to_rect_cb (GdkWindow *window, @@ -3401,7 +4469,19 @@ static void bookmarks_popup_menu (NemoPlacesSidebar *sidebar, GdkEventButton *event) { + char *uri = NULL; + GtkTreeIter iter; + update_menu_states (sidebar); + if (get_selected_iter_for_store (sidebar, &iter)) { + uri = get_fm_tree_model_uri (sidebar, sidebar->store, &iter); + if(uri!=NULL) { + NemoFile *file = nemo_file_get_by_uri (uri); + sidebar->popup_file = nemo_file_ref (file); + nemo_file_unref (file); + g_free (uri); + } + } eel_pop_up_context_menu (GTK_MENU(sidebar->popup_menu), (GdkEvent *) event, GTK_WIDGET (sidebar)); @@ -3483,6 +4563,42 @@ clear_ui (NemoPlacesSidebar *sidebar) &sidebar->action_action_group_merge_id, &sidebar->action_action_group); + nemo_ui_unmerge_ui (sidebar->ui_manager, + &sidebar->tv_action_group_merge_id, + &sidebar->tv_action_group); + +} + +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 +create_folder_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + char *parent_uri; + if(sidebar->popup_file == NULL) return; + parent_uri = nemo_file_get_uri (sidebar->popup_file); + nemo_file_operations_new_folder (GTK_WIDGET (sidebar->tree_view), + NULL, + parent_uri, + new_folder_done, sidebar->tree_view); + + g_free (parent_uri); } static const GtkActionEntry bookmark_action_entries[] = { @@ -3502,6 +4618,24 @@ static const GtkActionEntry bookmark_action_entries[] = { { NEMO_ACTION_PROPERTIES, NULL, N_("_Properties"), NULL, NULL, G_CALLBACK (properties_cb) }, }; +static const GtkActionEntry tree_sidebar_menu_entries[] = { + { NEMO_ACTION_OPEN, "folder-open-symbolic", N_("_Open"), NULL, NULL, G_CALLBACK (open_shortcut_cb) }, + { NEMO_ACTION_OPEN_IN_NEW_TAB, NULL, N_("Open in New _Tab"), NULL, NULL, G_CALLBACK (open_shortcut_in_new_tab_cb) }, + { NEMO_ACTION_OPEN_ALTERNATE, NULL, N_("Open in New _Window"), NULL, NULL, G_CALLBACK (open_shortcut_in_new_window_cb) }, + { NEMO_ACTION_NEW_FOLDER, NULL, N_("Create New _Folder"), NULL, NULL, G_CALLBACK (create_folder_cb) }, + { NEMO_ACTION_CUT, "edit-cut-symbolic", N_("Cu_t"), NULL, NULL, G_CALLBACK (fm_tree_view_cut_cb) }, + { NEMO_ACTION_COPY, "edit-copy-symbolic", N_("_Copy"), NULL, NULL, G_CALLBACK (fm_tree_view_copy_cb) }, + { NEMO_ACTION_PASTE, "edit-paste-symbolic", N_("_Paste Into Folder"), NULL, NULL, G_CALLBACK (fm_tree_view_paste_cb) }, + { NEMO_ACTION_PIN_FILE, "xapp-pin-symbolic", N_("P_in"), NULL, NULL, G_CALLBACK (fm_tree_view_pin_unpin_cb) }, + { NEMO_ACTION_UNPIN_FILE, "xapp-unpin-symbolic", N_("Unp_in"), NULL, NULL, G_CALLBACK (fm_tree_view_pin_unpin_cb) }, + { NEMO_ACTION_TRASH, "user-trash-full-symbolic", N_("Mo_ve to Trash"), NULL, NULL, G_CALLBACK (fm_tree_view_trash_cb) }, + { NEMO_ACTION_DELETE, "edit-delete-symbolic", N_("_Delete"), NULL, NULL, G_CALLBACK (fm_tree_view_delete_cb) }, + { NEMO_ACTION_UNMOUNT_VOLUME, NULL, N_("_Unmount"), NULL, NULL, G_CALLBACK (unmount_shortcut_cb) }, + { NEMO_ACTION_EJECT_VOLUME, NULL, N_("_Eject"), NULL, NULL, G_CALLBACK (eject_shortcut_cb) }, + { NEMO_ACTION_PROPERTIES, "document-properties-symbolic", N_("_Properties"), NULL, NULL, G_CALLBACK (properties_cb) }, +}; + + static void rebuild_menu (NemoPlacesSidebar *sidebar) { @@ -3525,6 +4659,11 @@ rebuild_menu (NemoPlacesSidebar *sidebar) &sidebar->action_action_group_merge_id, &sidebar->action_action_group); + nemo_ui_prepare_merge_ui (sidebar->ui_manager, + "NemoTreeviewSidebarFileActions", + &sidebar->tv_action_group_merge_id, + &sidebar->tv_action_group); + sidebar->bookmark_action_group_merge_id = gtk_ui_manager_add_ui_from_resource (sidebar->ui_manager, "/org/nemo/nemo-places-sidebar-ui.xml", NULL); @@ -3533,6 +4672,17 @@ rebuild_menu (NemoPlacesSidebar *sidebar) G_N_ELEMENTS (bookmark_action_entries), sidebar); + sidebar->tv_action_group_merge_id = + gtk_ui_manager_add_ui_from_resource (sidebar->ui_manager, "/org/nemo/nemo-tree-sidebar-ui.xml", NULL); + + + gtk_action_group_add_actions (sidebar->tv_action_group, + tree_sidebar_menu_entries, + G_N_ELEMENTS (tree_sidebar_menu_entries), + sidebar); + + + nemo_action_manager_iterate_actions (sidebar->action_manager, (NemoActionManagerIterFunc) add_action_to_ui, sidebar); @@ -3542,6 +4692,10 @@ rebuild_menu (NemoPlacesSidebar *sidebar) gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (GTK_WIDGET (sidebar->window))); sidebar->popup_menu = menu; + g_signal_connect (sidebar->popup_menu, "deactivate", + G_CALLBACK (popup_menu_deactivated), + sidebar); + #if GTK_CHECK_VERSION (3, 24, 8) g_signal_connect (sidebar->popup_menu, "realize", G_CALLBACK (popup_menu_realized), @@ -3675,8 +4829,9 @@ clear_eject_hover (GtkTreeModel *model, ClearHoverData *hdata = data; gint size; gboolean can_eject = FALSE; - - gtk_tree_model_get (model, iter, + GtkTreeIter fm_iter; + get_fm_tree_model_iter (model, iter, &fm_iter); + gtk_tree_model_get (hdata->sidebar->store, &fm_iter, PLACES_SIDEBAR_COLUMN_EJECT, &can_eject, -1); @@ -3686,10 +4841,15 @@ clear_eject_hover (GtkTreeModel *model, } else { size = EJECT_ICON_SIZE_NOT_HOVERED; } - - gtk_tree_store_set (hdata->sidebar->store, iter, - PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, size, - -1); + if(hdata->sidebar->use_file_treeview) { + fm_tree_model_set (hdata->sidebar->store, &fm_iter, + PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, size, + -1); + } else { + gtk_tree_store_set (hdata->sidebar->store, &fm_iter, + PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, size, + -1); + } return FALSE; } @@ -3716,7 +4876,12 @@ motion_notify_cb (GtkWidget *widget, model = gtk_tree_view_get_model (GTK_TREE_VIEW (sidebar->tree_view)); if (over_eject_button (sidebar, event->x, event->y, &path)) { - store_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path); + if(sidebar->use_file_treeview) { + store_path = path; + } else { + store_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (model), path); + gtk_tree_path_free (path); + } } ClearHoverData data = { sidebar, store_path }; @@ -3726,7 +4891,7 @@ motion_notify_cb (GtkWidget *widget, gtk_tree_path_free (store_path); } - gtk_tree_path_free (path); + return FALSE; } @@ -3812,6 +4977,9 @@ query_tooltip_callback (GtkWidget *widget, tooltip_markup = g_strdup (_("Stop")); } } else { + if (type == PLACES_BUILT_IN) { + return FALSE; + } gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_TOOLTIP, &tooltip_markup, @@ -3842,24 +5010,35 @@ update_expanded_state (GtkTreeView *tree_view, return; SectionType type; + PlaceType placetype; GtkTreeIter heading_iter; GtkTreeModel *model = gtk_tree_view_get_model (tree_view); gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &heading_iter, path); - gtk_tree_model_get (model, iter, + gtk_tree_model_get (model, &heading_iter, PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &type, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &placetype, -1); - if (type == SECTION_COMPUTER) { - sidebar->my_computer_expanded = expanded; - g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_MY_COMPUTER_EXPANDED, expanded); - } else if (type == SECTION_BOOKMARKS) { - sidebar->bookmarks_expanded = expanded; - g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_BOOKMARKS_EXPANDED, expanded); - } else if (type == SECTION_DEVICES) { - sidebar->devices_expanded = expanded; - g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_DEVICES_EXPANDED, expanded); - } else if (type == SECTION_NETWORK) { - sidebar->network_expanded = expanded; - g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_NETWORK_EXPANDED, expanded); + if(placetype == PLACES_HEADING) { + switch (type) { + case SECTION_COMPUTER: + sidebar->my_computer_expanded = expanded; + g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_MY_COMPUTER_EXPANDED, expanded); + break; + case SECTION_BOOKMARKS: + sidebar->bookmarks_expanded = expanded; + g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_BOOKMARKS_EXPANDED, expanded); + break; + case SECTION_DEVICES: + sidebar->devices_expanded = expanded; + g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_DEVICES_EXPANDED, expanded); + break; + case SECTION_NETWORK: + sidebar->network_expanded = expanded; + g_settings_set_boolean (nemo_window_state, NEMO_WINDOW_STATE_NETWORK_EXPANDED, expanded); + break; + default: + return; + } } } @@ -3888,6 +5067,9 @@ row_expanded_cb (GtkTreeView *tree_view, path, user_data, TRUE); + GtkTreeSelection *sel = gtk_tree_view_get_selection(tree_view); + gtk_tree_selection_unselect_all(sel); + gtk_tree_selection_select_path(sel, path); } static void @@ -3901,8 +5083,11 @@ row_activated_cb (GtkTreeView *tree_view, PlaceType place_type; NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (user_data); - GtkTreeModel *model = gtk_tree_view_get_model (tree_view); + GtkTreeModel *model = (GtkTreeModel *) (sidebar->store); gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); + if(sidebar->use_file_treeview) { + if(!iter_is_valid(FM_TREE_MODEL(model), &iter))return; + } gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, @@ -3935,8 +5120,8 @@ bookmarks_edited (GtkCellRenderer *cell, g_object_set (cell, "editable", FALSE, NULL); path = gtk_tree_path_new_from_string (path_string); - gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store_filter), &iter, path); - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store), &iter, path); + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store), &iter, PLACES_SIDEBAR_COLUMN_INDEX, &index, -1); gtk_tree_path_free (path); @@ -4006,25 +5191,68 @@ icon_cell_renderer_func (GtkTreeViewColumn *column, GtkTreeIter *iter, gpointer user_data) { - PlaceType type; + gboolean expanded; + GIcon *gicon = NULL; + g_object_get (cell, + "is-expanded", &expanded, + NULL); gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); + expanded ? PLACES_SIDEBAR_OPEN_ICON_COLUMN : PLACES_SIDEBAR_COLUMN_GICON, + &gicon, + -1); - if (type == PLACES_HEADING) { - g_object_set (cell, - "visible", FALSE, - NULL); + if (gicon) { + g_object_set(cell, "visible", TRUE, "gicon", gicon, NULL); + g_object_unref(gicon); } else { - g_object_set (cell, - "visible", TRUE, - "xpad", 3, - "ypad", 2, - NULL); + g_object_set(cell, "visible", FALSE, NULL); } } + + +static void +set_treeview_style(GtkTreeView *treeview, gboolean focused) +{ + GtkCssProvider *provider = gtk_css_provider_new(); + + if (focused) { + gtk_css_provider_load_from_data(provider, + "treeview.view:selected { " + " background-color: @theme_selected_bg_color; " + " color: @theme_selected_fg_color; " + "}", -1, NULL); + } else { + gtk_css_provider_load_from_data(provider, + "treeview.view:selected { " + " background-color: #d0d0d0; " + " color: @theme_fg_color; " + "}", -1, NULL); + } + + GtkStyleContext *context = gtk_widget_get_style_context(GTK_WIDGET(treeview)); + gtk_style_context_add_provider(context, + GTK_STYLE_PROVIDER(provider), + GTK_STYLE_PROVIDER_PRIORITY_USER); + g_object_unref(provider); +} + +static gboolean +on_tree_focus_in(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) +{ + set_treeview_style(GTK_TREE_VIEW(widget), TRUE); + return FALSE; // Keep default behavior +} + +static gboolean +on_tree_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) +{ + set_treeview_style(GTK_TREE_VIEW(widget), FALSE); + return FALSE; // Keep default behavior +} + + static void padding_cell_renderer_func (GtkTreeViewColumn *column, GtkCellRenderer *cell, @@ -4032,50 +5260,262 @@ padding_cell_renderer_func (GtkTreeViewColumn *column, GtkTreeIter *iter, gpointer user_data) { - PlaceType type; + GtkTreePath *path = gtk_tree_model_get_path(model, iter); + int depth = gtk_tree_path_get_depth(path); /* Root = 1 */ + gtk_tree_path_free(path); - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); + g_object_set(cell, + "width", 3 + (depth - 1) * 0, /* optional: leicht anpassen */ + "visible", TRUE, + NULL); +} + + +static void +text_cell_renderer_func(GtkTreeViewColumn *column, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + gchar *name_text = NULL; + gchar *heading_text = NULL; + gint row_type = 0; + + gtk_tree_model_get(model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, + PLACES_SIDEBAR_COLUMN_NAME, &name_text, + -1); + + if (row_type == PLACES_HEADING) { + /* For Headings use HEADING_TEXT (bold) */ + g_free(name_text); + + gtk_tree_model_get(model, iter, + PLACES_SIDEBAR_COLUMN_HEADING_TEXT, &heading_text, + -1); + + g_object_set(renderer, + "weight", PANGO_WEIGHT_BOLD, + "weight-set", TRUE, + NULL); + + g_object_set(renderer, "text", heading_text ? heading_text : "", NULL); + g_free(heading_text); + } else { + const gchar *display = ""; + if (name_text != NULL && *name_text != '\0') + display = name_text; + + g_object_set(renderer, + "weight-set", FALSE, + NULL); + + g_object_set(renderer, "text", display, NULL); + + g_free(name_text); + } +} + +enum +{ + PROP_DISK_FULL_PERCENTAGE = 1, + PROP_SHOW_DISK_FULL_PERCENTAGE = 2, +}; + +static void +nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView *tree_view) +{ + GtkCellRenderer *cell; + GtkTreeViewColumn *primary_column; + + /* === primary row === */ + primary_column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_expand(primary_column, TRUE); + + /* --- padding Cell renderer --- */ + cell = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(primary_column, cell, FALSE); + gtk_tree_view_column_set_cell_data_func(primary_column, cell, padding_cell_renderer_func, sidebar, NULL); + sidebar->padding_cell_renderer = cell; + + /* --- icon cell renderer --- */ + cell = gtk_cell_renderer_pixbuf_new(); + g_object_set(cell, + "xalign", 0.0, + "follow-state", TRUE, + NULL); + gtk_tree_view_column_pack_start(primary_column, cell, FALSE); + gtk_tree_view_column_set_cell_data_func(primary_column, cell, icon_cell_renderer_func, sidebar, NULL); + sidebar->icon_cell_renderer = cell; + + /* --- text renderer --- */ + cell = sidebar->editable_renderer = nemo_cell_renderer_disk_new(); + NEMO_CELL_RENDERER_DISK(cell)->direction = gtk_widget_get_direction(GTK_WIDGET(tree_view)); + gtk_tree_view_column_pack_start(primary_column, cell, TRUE); + g_object_set(cell, "editable", FALSE, NULL); + + /* remove 'text' here — we set text exclusively in text_cell_renderer_func */ + gtk_tree_view_column_set_attributes(primary_column, cell, + "editable-set", PLACES_SIDEBAR_COLUMN_BOOKMARK, + "disk-full-percent", PLACES_SIDEBAR_COLUMN_DF_PERCENT, + "show-disk-full-percent", PLACES_SIDEBAR_COLUMN_SHOW_DF, + NULL); + + /* set cell data func, which considers TREE_NAME and NAME */ + gtk_tree_view_column_set_cell_data_func(primary_column, cell, text_cell_renderer_func, sidebar, NULL); + + g_signal_connect(cell, "edited", G_CALLBACK(bookmarks_edited), sidebar); + g_signal_connect(cell, "editing-canceled", G_CALLBACK(bookmarks_editing_canceled), sidebar); + + gtk_tree_view_append_column(tree_view, primary_column); + /* === Eject Column === */ + sidebar->eject_column = gtk_tree_view_column_new(); + gtk_tree_view_column_set_sizing(sidebar->eject_column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); + gtk_tree_view_column_set_min_width(sidebar->eject_column, EJECT_COLUMN_MIN_WIDTH); + gtk_tree_view_column_set_max_width(sidebar->eject_column, EJECT_COLUMN_MAX_WIDTH); + + /* Eject Icon Renderer */ + cell = gtk_cell_renderer_pixbuf_new(); + sidebar->eject_icon_cell_renderer = cell; + g_object_set(cell, + "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, + "yalign", 0.8, + "width", menu_icon_pixels, + NULL); + gtk_tree_view_column_pack_start(sidebar->eject_column, cell, FALSE); + gtk_tree_view_column_set_attributes(sidebar->eject_column, cell, + "visible", PLACES_SIDEBAR_COLUMN_EJECT, + "icon-name", PLACES_SIDEBAR_COLUMN_EJECT_ICON, + "stock-size", PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, + NULL); + + gtk_tree_view_append_column(tree_view, sidebar->eject_column); + +} + +static void +hidden_files_mode_changed_callback (NemoWindow *window, + NemoPlacesSidebar *sidebar) +{ + update_filtering_from_preferences (sidebar); +} - if (type == PLACES_HEADING) { - g_object_set (cell, - "visible", FALSE, - "xpad", 0, - "ypad", 0, - NULL); +static void +notify_clipboard_info (NemoClipboardMonitor *monitor, + NemoClipboardInfo *info, + NemoPlacesSidebar *sidebar) +{ + if (info != NULL && info->cut) { + fm_tree_model_set_highlight_for_files ( FM_TREE_MODEL (sidebar->store), info->files); } else { - g_object_set (cell, - "visible", TRUE, - "ypad", 3, - NULL); + fm_tree_model_set_highlight_for_files ( FM_TREE_MODEL (sidebar->store), NULL); } } static void -heading_cell_renderer_func (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) +refresh_highlight (NemoPlacesSidebar *sidebar) { - PlaceType type; + NemoClipboardMonitor *monitor; + NemoClipboardInfo *info; - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); + monitor = nemo_clipboard_monitor_get (); + info = nemo_clipboard_monitor_get_clipboard_info (monitor); + + notify_clipboard_info (monitor, info, sidebar); +} + + + + + + +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 (type == PLACES_HEADING) { - g_object_set (cell, - "visible", TRUE, - NULL); + if (file_a == file_b) { + result = 0; + } else if (file_a == NULL) { + result = -1; + } else if (file_b == NULL) { + result = +1; } else { - g_object_set (cell, - "visible", FALSE, - NULL); + result = nemo_file_compare_for_sort (file_a, file_b, + NEMO_FILE_SORT_BY_DISPLAY_NAME, + FALSE, //sidebar->sort_directories_first, + FALSE, //sidebar->sort_favorites_first, + FALSE, + NULL); + } + + nemo_file_unref (file_a); + nemo_file_unref (file_b); + + return result; +} + + + +static void +row_loaded_callback (GtkTreeModel *tree_model, + GtkTreeIter *iter, + NemoPlacesSidebar *sidebar) +{ + NemoFile *file, *tmp_file, *selection_file; + + if (sidebar->selection_location == NULL + || !sidebar->selecting + || iter->user_data == NULL || iter->stamp == 0) { + return; + } + + file = fm_tree_model_iter_get_file ( FM_TREE_MODEL (sidebar->store), 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 (sidebar->selection_location); + while (selection_file != NULL) { + if (file == selection_file) { + nemo_file_unref (file); + nemo_file_unref (selection_file); + + schedule_show_selection (sidebar); + return; + } + tmp_file = nemo_file_get_parent (selection_file); + nemo_file_unref (selection_file); + selection_file = tmp_file; } + nemo_file_unref (file); } + static gboolean row_visibility_function (GtkTreeModel *model, GtkTreeIter *iter, @@ -4103,12 +5543,19 @@ row_visibility_function (GtkTreeModel *model, return FALSE; } +static gint +get_icon_scale_callback (FMTreeModel *model, + NemoPlacesSidebar *sidebar) +{ + return gtk_widget_get_scale_factor (GTK_WIDGET (sidebar->tree_view)); +} + +//static GdkAtom copied_files_atom; + static void nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) { GtkTreeView *tree_view; - GtkTreeViewColumn *primary_column, *expander_column, *expander_pad_column; - GtkCellRenderer *cell; GtkTreeSelection *selection; GtkStyleContext *style_context; @@ -4117,17 +5564,27 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) "changed", G_CALLBACK (actions_changed), sidebar); + + sidebar->ui_manager = gtk_ui_manager_new (); sidebar->in_drag = FALSE; - sidebar->desktop_dnd_source_fs = NULL; - sidebar->desktop_dnd_can_delete_source = FALSE; + /* enables TreeView for files */ + sidebar->use_file_treeview = TRUE; - sidebar->volume_monitor = g_volume_monitor_get (); + sidebar->desktop_dnd_source_fs = NULL; + sidebar->desktop_dnd_can_delete_source = TRUE; sidebar->update_places_on_idle_id = 0; + sidebar->bookmarks_changed_id = 0; + + sidebar-> hidden_files_changed_id = 0; + + sidebar->volume_monitor = NULL; + + sidebar->my_computer_expanded = g_settings_get_boolean (nemo_window_state, NEMO_WINDOW_STATE_MY_COMPUTER_EXPANDED); sidebar->bookmarks_expanded = g_settings_get_boolean (nemo_window_state, @@ -4152,165 +5609,87 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) /* Make it easier for theme authors to style the sidebar */ gtk_style_context_add_class (style_context, "nemo-places-sidebar"); - /* tree view */ - tree_view = GTK_TREE_VIEW (nemo_places_tree_view_new ()); - - gtk_tree_view_set_headers_visible (tree_view, FALSE); - - primary_column = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ()); - gtk_tree_view_column_set_max_width (GTK_TREE_VIEW_COLUMN (primary_column), NEMO_ICON_SIZE_SMALLER); - gtk_tree_view_column_set_expand (primary_column, TRUE); - - /* initial padding */ - cell = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (primary_column, cell, FALSE); - - /* headings */ - cell = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (primary_column, cell, FALSE); - gtk_tree_view_column_set_attributes (primary_column, cell, - "text", PLACES_SIDEBAR_COLUMN_HEADING_TEXT, - NULL); - g_object_set (cell, - "weight", PANGO_WEIGHT_BOLD, - "weight-set", TRUE, - "ypad", 0, - "xpad", 0, - NULL); - gtk_tree_view_column_set_cell_data_func (primary_column, cell, - heading_cell_renderer_func, - sidebar, NULL); - - /* icon padding */ - cell = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (primary_column, cell, FALSE); - gtk_tree_view_column_set_cell_data_func (primary_column, cell, - padding_cell_renderer_func, - sidebar, NULL); - - /* icon renderer */ - cell = gtk_cell_renderer_pixbuf_new (); - g_object_set (cell, - "follow-state", TRUE, - NULL); - gtk_tree_view_column_pack_start (primary_column, cell, FALSE); - gtk_tree_view_column_set_attributes (primary_column, cell, - "gicon", PLACES_SIDEBAR_COLUMN_GICON, - NULL); - gtk_tree_view_column_set_cell_data_func (primary_column, cell, - icon_cell_renderer_func, - sidebar, NULL); - - /* normal text renderer */ - cell = sidebar->editable_renderer = nemo_cell_renderer_disk_new (); - NEMO_CELL_RENDERER_DISK (cell)->direction = gtk_widget_get_direction (GTK_WIDGET (tree_view)); - gtk_tree_view_column_pack_start (primary_column, cell, TRUE); - g_object_set (G_OBJECT (cell), "editable", FALSE, NULL); - gtk_tree_view_column_set_attributes (primary_column, cell, - "text", PLACES_SIDEBAR_COLUMN_NAME, - "editable-set", PLACES_SIDEBAR_COLUMN_BOOKMARK, - "disk-full-percent", PLACES_SIDEBAR_COLUMN_DF_PERCENT, - "show-disk-full-percent", PLACES_SIDEBAR_COLUMN_SHOW_DF, - NULL); - g_object_set (cell, - "ellipsize", PANGO_ELLIPSIZE_END, - "ellipsize-set", TRUE, - NULL); - g_signal_connect (cell, "edited", - G_CALLBACK (bookmarks_edited), sidebar); - g_signal_connect (cell, "editing-canceled", - G_CALLBACK (bookmarks_editing_canceled), sidebar); - - /* eject column */ - sidebar->eject_column = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ()); - gtk_tree_view_column_set_sizing (sidebar->eject_column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); - gtk_tree_view_column_set_min_width (sidebar->eject_column, EJECT_COLUMN_MIN_WIDTH); - gtk_tree_view_column_set_max_width (sidebar->eject_column, EJECT_COLUMN_MAX_WIDTH); - - cell = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (sidebar->eject_column, cell, FALSE); - g_object_set (cell, "width", 5, NULL); - - /* eject icon renderer */ - cell = gtk_cell_renderer_pixbuf_new (); - sidebar->eject_icon_cell_renderer = cell; - g_object_set (cell, - "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, - "yalign", 0.8, - "width", menu_icon_pixels, - NULL); - gtk_tree_view_column_pack_start (sidebar->eject_column, cell, FALSE); - gtk_tree_view_column_set_attributes (sidebar->eject_column, cell, - "visible", PLACES_SIDEBAR_COLUMN_EJECT, - "icon-name", PLACES_SIDEBAR_COLUMN_EJECT_ICON, - "stock-size", PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, - NULL); - - /* eject icon trailing padding (adjusts to always avoid overlay-scrollbars) */ - gboolean overlay_scrolling; - GtkSettings *gtksettings = gtk_settings_get_default (); - g_object_get (gtksettings, - "gtk-overlay-scrolling", &overlay_scrolling, - NULL); - - cell = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (sidebar->eject_column, cell, FALSE); - gtk_tree_view_column_set_attributes (sidebar->eject_column, cell, - "visible", PLACES_SIDEBAR_COLUMN_EJECT, - NULL); - - if (overlay_scrolling) { - GtkWidget *vscrollbar = gtk_scrolled_window_get_vscrollbar (GTK_SCROLLED_WINDOW (sidebar)); - gint nat_width; - - gtk_widget_get_preferred_width (vscrollbar, NULL, &nat_width); - g_object_set (cell, "width", nat_width, NULL); - } else { - g_object_set (cell, "width", 2, NULL); - } + if(sidebar->use_file_treeview) + { + sidebar->store = fm_tree_model_new(); + GType model_types[PLACES_SIDEBAR_COLUMN_COUNT] = { + G_TYPE_STRING, // FM_TREE_MODEL_DISPLAY_NAME_COLUMN + g_icon_get_type(), // FM_TREE_MODEL_CLOSED_ICON_COLUMN + g_icon_get_type(), // FM_TREE_MODEL_OPEN_ICON_COLUMN + PANGO_TYPE_STYLE, // FM_TREE_MODEL_FONT_STYLE_COLUMN + G_TYPE_INT, // FM_TREE_MODEL_TEXT_WEIGHT_COLUMN + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_ROW_TYPE + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_URI + G_TYPE_DRIVE, // PLACES_SIDEBAR_COLUMN_DRIVE + G_TYPE_VOLUME, // PLACES_SIDEBAR_COLUMN_VOLUME + G_TYPE_MOUNT, // PLACES_SIDEBAR_COLUMN_MOUNT + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_INDEX + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_EJECT + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_NO_EJECT + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_BOOKMARK + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_TOOLTIP + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_EJECT_ICON + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_SECTION_TYPE + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_HEADING_TEXT + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_DF_PERCENT + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_SHOW_DF + G_TYPE_STRING // PLACES_SIDEBAR_COLUMN_TREE_NAME + }; + fm_tree_model_set_column_types((FMTreeModel*)(sidebar->store), PLACES_SIDEBAR_COLUMN_COUNT, model_types); + + tree_view = GTK_TREE_VIEW (nemo_places_tree_view_new ()); + + gtk_tree_view_set_headers_visible (tree_view, FALSE); + + sidebar->sort_model = (GtkTreeModel*)GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (sidebar->store))); + + gtk_tree_view_set_search_column (GTK_TREE_VIEW (tree_view), PLACES_SIDEBAR_COLUMN_NAME); + + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (sidebar->sort_model), + compare_rows, sidebar, NULL); + + gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (sidebar->sort_model)); - expander_pad_column = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new()); - gtk_tree_view_column_set_sizing (expander_pad_column, GTK_TREE_VIEW_COLUMN_FIXED); - gtk_tree_view_column_set_fixed_width (expander_pad_column, EXPANDER_PAD_COLUMN_WIDTH); + } else { + /* tree view */ + tree_view = GTK_TREE_VIEW (nemo_places_tree_view_new ()); - expander_column = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ()); - gtk_tree_view_column_set_sizing (expander_column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_set_headers_visible (tree_view, FALSE); - gint expander_size; - gtk_widget_style_get (GTK_WIDGET (tree_view), "expander-size", &expander_size, NULL); - gtk_tree_view_column_set_fixed_width (expander_column, expander_size); + sidebar->store = nemo_shortcuts_model_new (sidebar); - gtk_tree_view_append_column (tree_view, expander_pad_column); - gtk_tree_view_append_column (tree_view, expander_column); - gtk_tree_view_append_column (tree_view, primary_column); - gtk_tree_view_append_column (tree_view, sidebar->eject_column); + sidebar->sort_model = (GtkTreeModel*)GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (sidebar->store))); - gtk_tree_view_set_expander_column (tree_view, expander_column); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (tree_view), PLACES_SIDEBAR_COLUMN_NAME); - sidebar->store = nemo_shortcuts_model_new (sidebar); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sidebar->store), - GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, - GTK_SORT_ASCENDING); + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sidebar->store), + PLACES_SIDEBAR_COLUMN_TREE_NAME, + GTK_SORT_ASCENDING); - sidebar->store_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sidebar->store), NULL); + sidebar->store_filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(sidebar->sort_model), NULL); - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (sidebar->store_filter), - (GtkTreeModelFilterVisibleFunc) row_visibility_function, - sidebar, NULL); + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(sidebar->store_filter), + (GtkTreeModelFilterVisibleFunc) row_visibility_function, + sidebar, NULL); - gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (sidebar->store_filter)); + gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (sidebar->store_filter)); + } - gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view)); - gtk_widget_show (GTK_WIDGET (tree_view)); + sidebar->tree_view = tree_view; + gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view)); + gtk_widget_show (GTK_WIDGET (tree_view)); gtk_widget_show (GTK_WIDGET (sidebar)); - sidebar->tree_view = tree_view; + + nemo_places_tree_sidebar_renderer_init (sidebar, tree_view); gtk_tree_view_set_search_column (tree_view, PLACES_SIDEBAR_COLUMN_NAME); selection = gtk_tree_view_get_selection (tree_view); gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE); + gtk_tree_selection_set_select_function (selection, tree_selection_func, sidebar, @@ -4321,10 +5700,13 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) nemo_shortcuts_source_targets, G_N_ELEMENTS (nemo_shortcuts_source_targets), GDK_ACTION_MOVE); - gtk_drag_dest_set (GTK_WIDGET (tree_view), - 0, - nemo_shortcuts_drop_targets, G_N_ELEMENTS (nemo_shortcuts_drop_targets), - GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); + + gtk_tree_view_enable_model_drag_dest( + GTK_TREE_VIEW(tree_view), + nemo_shortcuts_drop_targets, + G_N_ELEMENTS(nemo_shortcuts_drop_targets), + GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK + ); g_signal_connect (tree_view, "key-press-event", G_CALLBACK (bookmarks_key_press_event_cb), sidebar); @@ -4361,6 +5743,12 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) G_CALLBACK (query_tooltip_callback), sidebar, 0); gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE); + g_signal_connect(sidebar->tree_view, "focus-in-event", + G_CALLBACK(on_tree_focus_in), NULL); + + g_signal_connect(sidebar->tree_view, "focus-out-event", + G_CALLBACK(on_tree_focus_out), NULL); + g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_DESKTOP_IS_HOME_DIR, G_CALLBACK(desktop_setting_changed_callback), sidebar); @@ -4382,6 +5770,18 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) "changed", G_CALLBACK (favorites_changed_cb), sidebar); + + if(sidebar->use_file_treeview) { + fm_tree_model_set_custom_row_draggable_func (sidebar->store, nemo_places_sidebar_row_draggable, sidebar); + + g_signal_connect_object (sidebar->store, "row_loaded", + G_CALLBACK (row_loaded_callback), + sidebar, G_CONNECT_AFTER); + + g_signal_connect_object (sidebar->store, "get-icon-scale", + G_CALLBACK (get_icon_scale_callback), sidebar, 0); + } + } static void @@ -4390,12 +5790,25 @@ nemo_places_sidebar_dispose (GObject *object) NemoPlacesSidebar *sidebar; sidebar = NEMO_PLACES_SIDEBAR (object); + tree_model_remove_all_nodes(sidebar); - sidebar->window = NULL; - sidebar->tree_view = NULL; + g_clear_object (&sidebar->desktop_dnd_source_fs); + + if (sidebar->expand_timeout_source > 0) { + g_source_remove(sidebar->expand_timeout_source); + sidebar->expand_timeout_source = 0; + } - g_free (sidebar->uri); - sidebar->uri = NULL; + if(sidebar->tree_view != NULL) + { + g_signal_handlers_disconnect_by_data(sidebar->tree_view, sidebar); + gtk_tree_view_set_model(GTK_TREE_VIEW(sidebar->tree_view), NULL); + gtk_widget_destroy(GTK_WIDGET(sidebar->tree_view)); + sidebar->tree_view = NULL; + } + + g_clear_object (&sidebar->ui_manager); + sidebar->ui_manager = NULL; free_drag_data (sidebar); @@ -4412,15 +5825,16 @@ nemo_places_sidebar_dispose (GObject *object) sidebar->actions_changed_id); sidebar->actions_changed_id = 0; } - g_clear_object (&sidebar->action_manager); if (sidebar->update_places_on_idle_id != 0) { g_source_remove (sidebar->update_places_on_idle_id); sidebar->update_places_on_idle_id = 0; } - - g_clear_object (&sidebar->store); + if (sidebar->hidden_files_changed_id != 0 && sidebar->window) { + g_signal_handler_disconnect(sidebar->window, sidebar->hidden_files_changed_id); + sidebar->hidden_files_changed_id = 0; + } g_clear_pointer (&sidebar->top_bookend_uri, g_free); g_clear_pointer (&sidebar->bottom_bookend_uri, g_free); @@ -4428,18 +5842,22 @@ nemo_places_sidebar_dispose (GObject *object) if (sidebar->go_to_after_mount_slot) { g_object_remove_weak_pointer (G_OBJECT (sidebar->go_to_after_mount_slot), (gpointer *) &sidebar->go_to_after_mount_slot); - sidebar->go_to_after_mount_slot = NULL; + g_clear_object (&sidebar->go_to_after_mount_slot); } g_signal_handlers_disconnect_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); + g_signal_handlers_disconnect_by_func (nemo_preferences, + reset_menu, + sidebar); + g_signal_handlers_disconnect_by_func (nemo_preferences, desktop_setting_changed_callback, sidebar); - g_signal_handlers_disconnect_by_func (gnome_background_preferences, + g_signal_handlers_disconnect_by_func (nemo_desktop_preferences, desktop_setting_changed_callback, sidebar); @@ -4473,10 +5891,30 @@ nemo_places_sidebar_dispose (GObject *object) g_clear_object (&sidebar->volume_monitor); } - + if(sidebar->selection_location) { + g_free (sidebar->selection_location); + sidebar->selection_location = NULL; + } + sidebar->window = NULL; G_OBJECT_CLASS (nemo_places_sidebar_parent_class)->dispose (object); } +static void +nemo_places_tree_sidebar_finalize (GObject *object) +{ + NemoPlacesSidebar *sidebar; + + sidebar = NEMO_PLACES_SIDEBAR (object); + + if(sidebar) { + if (sidebar->store_filter) g_clear_object(&sidebar->store_filter); + if(sidebar->sort_model) g_object_unref (sidebar->sort_model); + if(sidebar->store) g_clear_object(&sidebar->store); + } + + G_OBJECT_CLASS (nemo_places_sidebar_parent_class)->finalize (object); +} + static void nemo_places_sidebar_class_init (NemoPlacesSidebarClass *class) { @@ -4484,6 +5922,7 @@ nemo_places_sidebar_class_init (NemoPlacesSidebarClass *class) GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); oclass->dispose = nemo_places_sidebar_dispose; + oclass->finalize = nemo_places_tree_sidebar_finalize; widget_class->style_set = nemo_places_sidebar_style_set; widget_class->focus = nemo_places_sidebar_focus; @@ -4492,6 +5931,7 @@ nemo_places_sidebar_class_init (NemoPlacesSidebarClass *class) EJECT_ICON_SIZE_NOT_HOVERED = gtk_icon_size_register ("menu-icon-size-small", menu_icon_pixels - EJECT_ICON_SIZE_REDUCTION, menu_icon_pixels - EJECT_ICON_SIZE_REDUCTION); + copied_files_atom = gdk_atom_intern ("x-special/gnome-copied-files", FALSE); } static gboolean @@ -4514,7 +5954,8 @@ update_places_on_idle (NemoPlacesSidebar *sidebar) sidebar->update_places_on_idle_id = g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc) update_places_on_idle_callback, - sidebar, NULL); + g_object_ref(sidebar), + (GDestroyNotify) g_object_unref); } static void @@ -4526,10 +5967,19 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, sidebar->window = window; + + g_signal_connect_object (window, "loading_uri", + G_CALLBACK (loading_uri_callback), sidebar, 0); + + if(sidebar->use_file_treeview) { + sidebar->hidden_files_changed_id = + g_signal_connect_object (window, "hidden-files-mode-changed", + G_CALLBACK (hidden_files_mode_changed_callback), sidebar, 0); + } slot = nemo_window_get_active_slot (window); sidebar->bookmarks = nemo_bookmark_list_get_default (); - sidebar->uri = nemo_window_slot_get_current_uri (slot); + sidebar->selection_location = nemo_window_slot_get_current_uri (slot); breakpoint = g_settings_get_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT); @@ -4547,9 +5997,7 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, G_CALLBACK (update_places_on_idle), sidebar); - g_signal_connect_object (window, "loading_uri", - G_CALLBACK (loading_uri_callback), - sidebar, 0); + if (sidebar->volume_monitor == NULL) sidebar->volume_monitor = g_volume_monitor_get (); g_signal_connect_object (sidebar->volume_monitor, "volume_added", G_CALLBACK (volume_added_callback), sidebar, 0); @@ -4572,7 +6020,7 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_ALWAYS_USE_BROWSER, G_CALLBACK (reset_menu), sidebar); - update_places (sidebar); +// update_places (sidebar); } static void @@ -4582,7 +6030,7 @@ nemo_places_sidebar_style_set (GtkWidget *widget, NemoPlacesSidebar *sidebar; sidebar = NEMO_PLACES_SIDEBAR (widget); - update_places (sidebar); + if(sidebar->tree_view) update_places (sidebar); } GtkWidget * @@ -4648,24 +6096,28 @@ nemo_shortcuts_model_new (NemoPlacesSidebar *sidebar) { NemoShortcutsModel *model; GType model_types[PLACES_SIDEBAR_COLUMN_COUNT] = { - G_TYPE_INT, - G_TYPE_STRING, - G_TYPE_DRIVE, - G_TYPE_VOLUME, - G_TYPE_MOUNT, - G_TYPE_STRING, - G_TYPE_ICON, - G_TYPE_INT, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, - G_TYPE_STRING, - G_TYPE_STRING, - G_TYPE_INT, - G_TYPE_INT, - G_TYPE_STRING, - G_TYPE_INT, - G_TYPE_BOOLEAN + G_TYPE_STRING, // FM_TREE_MODEL_DISPLAY_NAME_COLUMN + g_icon_get_type(), // FM_TREE_MODEL_CLOSED_ICON_COLUMN + g_icon_get_type(), // FM_TREE_MODEL_OPEN_ICON_COLUMN + PANGO_TYPE_STYLE, // FM_TREE_MODEL_FONT_STYLE_COLUMN + G_TYPE_INT, // FM_TREE_MODEL_TEXT_WEIGHT_COLUMN + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_ROW_TYPE + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_URI + G_TYPE_DRIVE, // PLACES_SIDEBAR_COLUMN_DRIVE + G_TYPE_VOLUME, // PLACES_SIDEBAR_COLUMN_VOLUME + G_TYPE_MOUNT, // PLACES_SIDEBAR_COLUMN_MOUNT + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_INDEX + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_EJECT + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_NO_EJECT + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_BOOKMARK + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_TOOLTIP + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_EJECT_ICON + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_SECTION_TYPE + G_TYPE_STRING, // PLACES_SIDEBAR_COLUMN_HEADING_TEXT + G_TYPE_INT, // PLACES_SIDEBAR_COLUMN_DF_PERCENT + G_TYPE_BOOLEAN, // PLACES_SIDEBAR_COLUMN_SHOW_DF + G_TYPE_STRING // PLACES_SIDEBAR_COLUMN_TREE_NAME }; model = g_object_new (NEMO_TYPE_SHORTCUTS_MODEL, NULL); @@ -4677,3 +6129,28 @@ nemo_shortcuts_model_new (NemoPlacesSidebar *sidebar) return GTK_TREE_STORE (model); } + +static gboolean +nemo_places_sidebar_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path, + gpointer user_data) +{ + GtkTreeModel *model; + GtkTreeIter iter; + PlaceType place_type; + SectionType section_type; + + model = GTK_TREE_MODEL (drag_source); + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + -1); + + if (place_type != PLACES_HEADING && + (section_type == SECTION_XDG_BOOKMARKS || section_type == SECTION_BOOKMARKS)) + return TRUE; + + return FALSE; +} diff --git a/src/nemo-properties-window.c b/src/nemo-properties-window.c index 7ec1e12e49..1c5339695c 100644 --- a/src/nemo-properties-window.c +++ b/src/nemo-properties-window.c @@ -4719,7 +4719,6 @@ get_pending_key (GList *file_list) GList *l; GList *uris; GString *key; - char *ret; uris = NULL; for (l = file_list; l != NULL; l = l->next) { @@ -4735,10 +4734,7 @@ get_pending_key (GList *file_list) g_list_free_full (uris, g_free); - ret = key->str; - g_string_free (key, FALSE); - - return ret; + return g_string_free (key, FALSE); } static StartupData * diff --git a/src/nemo-query-editor.c b/src/nemo-query-editor.c index 330bcda8d9..56c02be751 100644 --- a/src/nemo-query-editor.c +++ b/src/nemo-query-editor.c @@ -398,15 +398,17 @@ nemo_query_editor_init (NemoQueryEditor *editor) editor); priv->file_regex_toggle = GTK_WIDGET (gtk_builder_get_object (builder, "file_search_regex_toggle")); - gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->file_regex_toggle), - g_settings_get_boolean (nemo_search_preferences, - NEMO_PREFERENCES_SEARCH_FILES_REGEX)); - - g_signal_connect (priv->file_regex_toggle, - "toggled", - G_CALLBACK (file_regex_button_toggled_cb), - editor); - + if(priv->file_regex_toggle != NULL) + { + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->file_regex_toggle), + g_settings_get_boolean (nemo_search_preferences, + NEMO_PREFERENCES_SEARCH_FILES_REGEX)); + + g_signal_connect (priv->file_regex_toggle, + "toggled", + G_CALLBACK (file_regex_button_toggled_cb), + editor); + } priv->content_entry = GTK_WIDGET (gtk_builder_get_object (builder, "content_search_entry")); g_signal_connect (priv->content_entry, "activate", diff --git a/src/nemo-tree-sidebar-model.c b/src/nemo-tree-sidebar-model.c index 92fdfcec73..e55bd4c1c8 100644 --- a/src/nemo-tree-sidebar-model.c +++ b/src/nemo-tree-sidebar-model.c @@ -42,6 +42,7 @@ #include #include + enum { ROW_LOADED, GET_ICON_SCALE, @@ -57,6 +58,8 @@ typedef gboolean (* FilePredicate) (NemoFile *); * is the TreeNode pointer to the parent. */ +#define ISROOTNODE(node) (node->parent == NULL || node->parent->isheadnode) + typedef struct TreeNode TreeNode; typedef struct FMTreeModelRoot FMTreeModelRoot; @@ -70,6 +73,9 @@ struct TreeNode { GMount *mount; GIcon *closed_icon; GIcon *open_icon; + PangoStyle font_style; + int text_weight; + gboolean text_weight_override; FMTreeModelRoot *root; @@ -81,16 +87,19 @@ struct TreeNode { int dummy_child_ref_count; int all_children_ref_count; guint icon_scale; - NemoDirectory *directory; guint done_loading_id; guint files_added_id; guint files_changed_id; TreeNode *first_child; + gboolean isheadnode; + GValue *extra_values; + int num_extra_values; /* misc. flags */ guint done_loading : 1; + guint is_empty : 1; guint force_has_dummy : 1; guint inserted : 1; guint pinned : 1; @@ -100,6 +109,7 @@ struct TreeNode { struct FMTreeModelDetails { int stamp; + TreeNode *head_root_node; // New top-level root node TreeNode *root_node; guint monitoring_update_idle_id; @@ -108,6 +118,12 @@ struct FMTreeModelDetails { gboolean show_only_directories; GList *highlighted_files; + + GType *column_types; + int num_columns; + + gboolean (*custom_row_draggable_func)(GtkTreeDragSource *drag_source, GtkTreePath *path, gpointer user_data); + gpointer custom_row_draggable_data; // Custom data for the callback }; struct FMTreeModelRoot { @@ -130,10 +146,17 @@ static void destroy_node_without_reporting (FMTreeModel *model, TreeNode *node); static void report_node_contents_changed (FMTreeModel *model, TreeNode *node); +static GtkTreePath *fm_tree_model_get_path(GtkTreeModel *model, GtkTreeIter *iter); +static int fm_tree_model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter); +static void fm_tree_model_drag_source_init (GtkTreeDragSourceIface *iface); G_DEFINE_TYPE_WITH_CODE (FMTreeModel, fm_tree_model, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, - fm_tree_model_tree_model_init)); + fm_tree_model_tree_model_init) + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + fm_tree_model_drag_source_init) + +); static GtkTreeModelFlags fm_tree_model_get_flags (GtkTreeModel *tree_model) @@ -156,15 +179,23 @@ fm_tree_model_get_icon_scale (GtkTreeModel *model) return retval; } +static void +object_unref_if_not_NULL (gpointer object) +{ + if (object == NULL) { + return; + } + g_object_unref (object); +} + static FMTreeModelRoot * tree_model_root_new (FMTreeModel *model) { FMTreeModelRoot *root; - root = g_new0 (FMTreeModelRoot, 1); root->model = model; - root->file_to_node_map = g_hash_table_new (NULL, NULL); - root->icon_scale = fm_tree_model_get_icon_scale (GTK_TREE_MODEL (model)); + root->file_to_node_map = g_hash_table_new (g_direct_hash, g_direct_equal); + root->icon_scale = fm_tree_model_get_icon_scale (GTK_TREE_MODEL (model)); return root; } @@ -175,9 +206,12 @@ tree_node_new (NemoFile *file, FMTreeModelRoot *root) TreeNode *node; node = g_new0 (TreeNode, 1); + node->isheadnode = FALSE; node->file = nemo_file_ref (file); node->root = root; - node->icon_scale = root->icon_scale; + node->icon_scale = root->icon_scale; + node->extra_values = NULL; + node->num_extra_values = 0; return node; } @@ -212,52 +246,75 @@ tree_node_unparent (FMTreeModel *model, TreeNode *node) node->root = NULL; } +static void +tree_model_root_free (FMTreeModelRoot *root) +{ + if (!root) return; + if (root->file_to_node_map) { + g_hash_table_destroy (root->file_to_node_map); + root->file_to_node_map = NULL; + } + g_free (root); +} + static void tree_node_destroy (FMTreeModel *model, TreeNode *node) { g_assert (node->first_child == NULL); g_assert (node->ref_count == 0); + if (node->root && node->root->file_to_node_map && node->file) { + g_hash_table_remove(node->root->file_to_node_map, node->file); + } + tree_node_unparent (model, node); - g_object_unref (node->file); - g_free (node->display_name); - g_clear_object (&node->icon); - g_clear_object (&node->closed_icon); - g_clear_object (&node->open_icon); + object_unref_if_not_NULL (node->file); node->file=NULL; + g_free (node->display_name); node->display_name=NULL; + object_unref_if_not_NULL (node->icon); node->icon = NULL; + object_unref_if_not_NULL (node->closed_icon); node->closed_icon=NULL; + object_unref_if_not_NULL (node->open_icon); node->open_icon=NULL; g_assert (node->done_loading_id == 0); g_assert (node->files_added_id == 0); g_assert (node->files_changed_id == 0); nemo_directory_unref (node->directory); - + if (node->extra_values) { + for (int i = 0; i < node->num_extra_values; i++) { + g_value_unset(&node->extra_values[i]); + } + g_free(node->extra_values); + node->extra_values = NULL; + node->num_extra_values = 0; + } g_free (node); } static void tree_node_parent (TreeNode *node, TreeNode *parent) { - TreeNode *first_child; - - g_assert (parent != NULL); - g_assert (node->parent == NULL); - g_assert (node->prev == NULL); - g_assert (node->next == NULL); - - first_child = parent->first_child; - - node->parent = parent; - node->root = parent->root; - node->next = first_child; - - if (first_child != NULL) { - g_assert (first_child->prev == NULL); - first_child->prev = node; - } - - parent->first_child = node; + g_assert (parent != NULL); + g_assert (node->parent == NULL); + g_assert (node->prev == NULL); + g_assert (node->next == NULL); + + node->parent = parent; + node->root = parent->root; + node->parent->is_empty = FALSE; + + if (parent->first_child == NULL) { + parent->first_child = node; + } else { + TreeNode *last_node = parent->first_child; + while (last_node->next != NULL) { + last_node = last_node->next; + } + last_node->next = node; + node->prev = last_node; + } } + static GIcon * get_menu_icon_for_file (TreeNode *node, NemoFile *file, @@ -266,11 +323,12 @@ get_menu_icon_for_file (TreeNode *node, NemoFile *parent_file; GIcon *gicon, *emblem_icon, *emblemed_icon; GEmblem *emblem; - int size; +// int size; GList *emblem_icons, *l; - size = nemo_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU); - gicon = G_ICON (nemo_file_get_icon_pixbuf (file, size, TRUE, node->icon_scale, flags)); + //size = nemo_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU); + //gicon = G_ICON (nemo_file_get_icon_pixbuf (file, size, TRUE, node->icon_scale, flags)); + gicon = nemo_file_get_gicon(file, flags); parent_file = NULL; @@ -295,15 +353,17 @@ get_menu_icon_for_file (TreeNode *node, g_list_free_full (emblem_icons, g_object_unref); - return gicon; + if (gicon) + return g_object_ref (gicon); + return NULL; } static GIcon * tree_node_get_icon (TreeNode *node, NemoFileIconFlags flags) { - if (node->parent == NULL) { - return node->icon; + if (ISROOTNODE(node)) { + return node->icon ? g_object_ref(node->icon) : NULL; } return get_menu_icon_for_file (node, node->file, flags); } @@ -313,19 +373,27 @@ tree_node_update_icon (TreeNode *node, GIcon **icon_storage, NemoFileIconFlags flags) { - GIcon *icon; + GIcon *new_icon = tree_node_get_icon (node, flags); /* liefert eine new ref (oder NULL) */ - if (*icon_storage == NULL) { - return FALSE; - } - icon = tree_node_get_icon (node, flags); - if (icon == *icon_storage) { - g_object_unref (icon); - return FALSE; - } - g_object_unref (*icon_storage); - *icon_storage = icon; - return TRUE; + /* If both NULL -> no change */ + if (new_icon == NULL && *icon_storage == NULL) { + return FALSE; + } + + /* pointer-equality is fine as heuristic (same object pointer) */ + if (new_icon == *icon_storage) { + if (new_icon) + g_object_unref (new_icon); /* we got a ref from get_icon, drop it */ + return FALSE; + } + + /* replace: unref old storage, store new ref */ + if (*icon_storage) { + g_object_unref (*icon_storage); + } + *icon_storage = new_icon; /* take ownership */ + + return TRUE; } static gboolean @@ -349,7 +417,7 @@ tree_node_update_display_name (TreeNode *node) return FALSE; } /* don't update root node display names */ - if (node->parent == NULL) { + if (ISROOTNODE(node)) { return FALSE; } display_name = nemo_file_get_display_name (node->file); @@ -358,7 +426,7 @@ tree_node_update_display_name (TreeNode *node) return FALSE; } g_free (node->display_name); - node->display_name = NULL; + node->display_name = display_name; return TRUE; } @@ -424,12 +492,39 @@ tree_node_get_display_name (TreeNode *node) static gboolean tree_node_has_dummy_child (TreeNode *node) { - return (node->directory != NULL - && (!node->done_loading - || node->first_child == NULL - || node->force_has_dummy)) || - /* Roots always have dummy nodes if directory isn't loaded yet */ - (node->directory == NULL && node->parent == NULL); +#if 0 + return (node != NULL && !node->isheadnode && + ((node->directory != NULL && (!node->done_loading || node->first_child == NULL || node->force_has_dummy)) || + (node->directory == NULL && ISROOTNODE(node))) ); +#else + // same code as above + if (!node) return FALSE; + + + // 1) node is a directory + if (node->directory != NULL) { + // contains a dummy if: + // - not loading, or + // - no child exists or + // - a dummy is forced + + if (!node->done_loading || node->first_child == NULL || node->force_has_dummy) { + return TRUE; + } + } + + // head_root_node has file == NULL; we don't want him to be used not loaded + if (node->isheadnode) + return FALSE; + + // 2) node is a root node without directory + if (node->directory == NULL && (ISROOTNODE(node))) { + return TRUE; + } + + return FALSE; + +#endif } static int @@ -492,7 +587,11 @@ make_iter_for_dummy_row (TreeNode *parent, GtkTreeIter *iter, int stamp) static TreeNode * get_node_from_file (FMTreeModelRoot *root, NemoFile *file) { - return g_hash_table_lookup (root->file_to_node_map, file); + if (root == NULL || root->file_to_node_map == NULL || file == NULL) { + return NULL; + } + + return g_hash_table_lookup(root->file_to_node_map, file); } static TreeNode * @@ -507,6 +606,73 @@ get_parent_node_from_file (FMTreeModelRoot *root, NemoFile *file) return parent_node; } +static void +tree_node_init_extra_columns (TreeNode *node, FMTreeModel *model) +{ + int base = FM_TREE_MODEL_NUM_COLUMNS; + int extra = model->details->num_columns - base; + + if (extra <= 0) { + node->extra_values = NULL; + return; + } + // free if extra_values already exists + if (node->extra_values) { + for (int i = 0; i < node->num_extra_values; i++) { + g_value_unset(&node->extra_values[i]); + } + g_free(node->extra_values); + node->extra_values = NULL; + node->num_extra_values = 0; + } + + node->extra_values = g_new0(GValue, extra); + node->num_extra_values = extra; + + for (int i = 0; i < extra; i++) { + g_value_init(&node->extra_values[i], + model->details->column_types[base + i]); + } +} + +static void +tree_node_init_extra_columns_recursive(TreeNode *node, FMTreeModel *model) +{ + for (TreeNode *n = node; n; n = n->next) { + tree_node_init_extra_columns(n, model); + if (n->first_child) + tree_node_init_extra_columns_recursive(n->first_child, model); + } +} + +void +fm_tree_model_set_column_types(FMTreeModel *model, int new_count, const GType *types) +{ + g_return_if_fail(FM_IS_TREE_MODEL(model)); + g_return_if_fail(types != NULL); + g_return_if_fail(new_count > 0); + + // Free old columns if they exist + if (model->details->column_types) + g_free(model->details->column_types); + + // Allocate new columns (correct) + model->details->column_types = g_new0(GType, new_count); + + memcpy(model->details->column_types, types, sizeof(GType) * new_count); + + model->details->num_columns = new_count; + + /* EXTEND EXISTING NODES */ + for (TreeNode *n = model->details->head_root_node; n; n = n->next) + tree_node_init_extra_columns_recursive(n, model); + + for (TreeNode *r = model->details->root_node; r; r = r->next) + tree_node_init_extra_columns_recursive(r, model); + +} + + static TreeNode * create_node_for_file (FMTreeModelRoot *root, NemoFile *file) { @@ -515,6 +681,7 @@ create_node_for_file (FMTreeModelRoot *root, NemoFile *file) g_assert (get_node_from_file (root, file) == NULL); node = tree_node_new (file, root); g_hash_table_insert (root->file_to_node_map, node->file, node); + tree_node_init_extra_columns(node, root->model); return node; } @@ -588,9 +755,19 @@ abandon_dummy_row_ref_count (FMTreeModel *model, TreeNode *node) static void report_row_inserted (FMTreeModel *model, GtkTreeIter *iter) { + if (iter == NULL || iter->stamp != model->details->stamp) { + g_warning("Invalid iterator"); + return; + } + GtkTreePath *path; path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter); + if (path == NULL) { + g_warning("Failed to get path for iterator"); + return; + } + gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter); gtk_tree_path_free (path); } @@ -620,8 +797,17 @@ get_node_path (FMTreeModel *model, TreeNode *node) { GtkTreeIter iter; - make_iter_for_node (node, &iter, model->details->stamp); - return gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter); + if (node == NULL) { + g_warning("Node is NULL"); + return NULL; + } + + if (!make_iter_for_node (node, &iter, model->details->stamp)) { + g_warning("Failed to make iterator for node"); + return NULL; + } + + return fm_tree_model_get_path(GTK_TREE_MODEL(model), &iter); } static void @@ -667,10 +853,10 @@ report_node_inserted (FMTreeModel *model, TreeNode *node) gboolean add_child = FALSE; - if (node->directory != NULL) { + if (node->directory != NULL) { guint count; if (nemo_file_get_directory_item_count (node->file, &count, NULL)) { - add_child = count > 0 || node->parent == NULL; + add_child = count > 0 || ISROOTNODE(node); } else { add_child = TRUE; } @@ -747,11 +933,15 @@ destroy_children_without_reporting (FMTreeModel *model, TreeNode *parent) static void destroy_node_without_reporting (FMTreeModel *model, TreeNode *node) { + if (node == NULL) { + g_warning("Node is NULL"); + return; + } abandon_node_ref_count (model, node); stop_monitoring_directory (model, node); node->inserted = FALSE; destroy_children_without_reporting (model, node); - g_hash_table_remove (node->root->file_to_node_map, node->file); + tree_node_destroy (model, node); } @@ -809,6 +999,12 @@ static void destroy_by_function (FMTreeModel *model, FilePredicate f) { TreeNode *node; + TreeNode *head; + for (head = model->details->head_root_node; head != NULL; head = head->next) { + for (node = head->first_child; node != NULL; node = node->next) { + destroy_children_by_function (model, node, f); + } + } for (node = model->details->root_node; node != NULL; node = node->next) { destroy_children_by_function (model, node, f); } @@ -822,10 +1018,10 @@ update_node_without_reporting (FMTreeModel *model, TreeNode *node) changed = FALSE; if (node->directory == NULL && - (nemo_file_is_directory (node->file) || node->parent == NULL)) { + (nemo_file_is_directory(node->file) || ISROOTNODE(node))) { node->directory = nemo_directory_get_for_file (node->file); } else if (node->directory != NULL && - !(nemo_file_is_directory (node->file) || node->parent == NULL)) { + !(nemo_file_is_directory (node->file) || ISROOTNODE(node))) { stop_monitoring_directory (model, node); destroy_children (model, node); nemo_directory_unref (node->directory); @@ -912,10 +1108,17 @@ should_show_file (FMTreeModel *model, NemoFile *file) if (should && nemo_file_is_gone (file)) { should = FALSE; } - + TreeNode *head; + for (head = model->details->head_root_node; head != NULL; head = head->next) { + for (node = head->first_child; node != NULL; node = node->next) { + if (!should && node != NULL && file == node->file) { + return TRUE; + } + } + } for (node = model->details->root_node; node != NULL; node = node->next) { if (!should && node != NULL && file == node->file) { - should = TRUE; + return TRUE; } } @@ -979,7 +1182,6 @@ process_file_change (FMTreeModelRoot *root, update_node (root->model, node); return; } - if (!should_show_file (root->model, file)) { return; } @@ -1058,6 +1260,23 @@ done_loading_callback (NemoDirectory *directory, return; } set_done_loading (root->model, node, TRUE); + + // Check if the node has no children + if (node->first_child == NULL) { + // Inform GTK that the node has no children + if (tree_node_has_dummy_child(node)) { + GtkTreeIter dummy_iter; + make_iter_for_dummy_row(node, &dummy_iter, root->model->details->stamp); + GtkTreePath *dummy_path = gtk_tree_model_get_path(GTK_TREE_MODEL(root->model), &dummy_iter); + gtk_tree_model_row_deleted(GTK_TREE_MODEL(root->model), dummy_path); + gtk_tree_path_free(dummy_path); + } + node->is_empty = TRUE; + update_node(root->model, node); + make_iter_for_node(node, &iter, root->model->details->stamp); + report_row_has_child_toggled(root->model, &iter); + } + nemo_file_unref (file); make_iter_for_node (node, &iter, root->model->details->stamp); @@ -1112,34 +1331,25 @@ start_monitoring_directory (FMTreeModel *model, TreeNode *node) attributes, files_changed_callback, node->root); } + static int fm_tree_model_get_n_columns (GtkTreeModel *model) { - return FM_TREE_MODEL_NUM_COLUMNS; + FMTreeModel *fm_model = FM_TREE_MODEL(model); + return fm_model->details->num_columns; } static GType -fm_tree_model_get_column_type (GtkTreeModel *model, int index) -{ - switch (index) { - case FM_TREE_MODEL_DISPLAY_NAME_COLUMN: - return G_TYPE_STRING; - case FM_TREE_MODEL_CLOSED_ICON_COLUMN: - return G_TYPE_ICON; - case FM_TREE_MODEL_OPEN_ICON_COLUMN: - return G_TYPE_ICON; - case FM_TREE_MODEL_FONT_STYLE_COLUMN: - return PANGO_TYPE_STYLE; - case FM_TREE_MODEL_TEXT_WEIGHT_COLUMN: - return G_TYPE_INT; - default: - g_assert_not_reached (); - } +fm_tree_model_get_column_type(GtkTreeModel *model, int index) { + FMTreeModel *fm_model = FM_TREE_MODEL(model); - return G_TYPE_INVALID; + if (index < 0 || index >= fm_model->details->num_columns) { + return G_TYPE_INVALID; + } + return fm_model->details->column_types[index]; } -static gboolean +gboolean iter_is_valid (FMTreeModel *model, const GtkTreeIter *iter) { TreeNode *node, *parent; @@ -1152,7 +1362,7 @@ iter_is_valid (FMTreeModel *model, const GtkTreeIter *iter) parent = iter->user_data2; if (node == NULL) { if (parent != NULL) { - if (!NEMO_IS_FILE (parent->file)) { + if (!parent->isheadnode && !NEMO_IS_FILE (parent->file)) { return FALSE; } if (!tree_node_has_dummy_child (parent)) { @@ -1160,6 +1370,9 @@ iter_is_valid (FMTreeModel *model, const GtkTreeIter *iter) } } } else { + if (node->isheadnode) { + return TRUE; + } if (!NEMO_IS_FILE (node->file)) { return FALSE; } @@ -1198,50 +1411,175 @@ fm_tree_model_get_iter (GtkTreeModel *model, GtkTreeIter *iter, GtkTreePath *pat return TRUE; } + +static int +get_top_level_index(FMTreeModel *model, TreeNode *node) +{ + int i = 0; + for (TreeNode *n = model->details->head_root_node; n != NULL; n = n->next, i++) { + if (n == node) return i; + } + for (TreeNode *n = model->details->root_node; n != NULL; n = n->next) { + if (n == node) return i; + if(n->parent == NULL) i++; + } + return -1; // Node not found +} + static GtkTreePath * fm_tree_model_get_path (GtkTreeModel *model, GtkTreeIter *iter) { FMTreeModel *tree_model; - TreeNode *node, *parent, *cnode; + TreeNode *node, *parent; GtkTreePath *path; GtkTreeIter parent_iter; - int i; g_return_val_if_fail (FM_IS_TREE_MODEL (model), NULL); tree_model = FM_TREE_MODEL (model); g_return_val_if_fail (iter_is_valid (tree_model, iter), NULL); node = iter->user_data; + + // if the iterator is a dummy node if (node == NULL) { parent = iter->user_data2; if (parent == NULL) { return gtk_tree_path_new (); } - } else { - parent = node->parent; - if (parent == NULL) { - i = 0; - for (cnode = tree_model->details->root_node; cnode != node; cnode = cnode->next) { - i++; - } - path = gtk_tree_path_new (); - gtk_tree_path_append_index (path, i); - return path; - } + } else { + parent = node->parent; + if (parent == NULL) { + int index = get_top_level_index(tree_model, node); + path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, index); + return path; + } + } + if (parent == NULL) { + g_warning("Failed parent==NULL"); } + /* Recursively get path of parent */ parent_iter.stamp = iter->stamp; parent_iter.user_data = parent; parent_iter.user_data2 = NULL; parent_iter.user_data3 = NULL; path = fm_tree_model_get_path (model, &parent_iter); + if (path == NULL) { + g_warning("Failed to get path for parent node"); + return NULL; + } gtk_tree_path_append_index (path, tree_node_get_child_index (parent, node)); return path; } +void +fm_tree_model_set (FMTreeModel *model, GtkTreeIter *iter, ...) +{ + TreeNode *node; + va_list args; + int column; + + g_return_if_fail(FM_IS_TREE_MODEL(model)); + g_return_if_fail(iter_is_valid(model, iter)); + + node = iter->user_data; + if (!node) + return; + + va_start(args, iter); + + while ((column = va_arg(args, int)) != -1) { + switch (column) { + + case FM_TREE_MODEL_DISPLAY_NAME_COLUMN: { + const char *str = va_arg(args, const char *); + g_free(node->display_name); + node->display_name = g_strdup(str); + break; + } + + case FM_TREE_MODEL_CLOSED_ICON_COLUMN: { + GIcon *icon = va_arg(args, GIcon *); + if (node->closed_icon) + g_object_unref(node->closed_icon); + node->closed_icon = icon ? g_object_ref(icon) : NULL; + break; + } + + case FM_TREE_MODEL_OPEN_ICON_COLUMN: { + GIcon *icon = va_arg(args, GIcon *); + if (node->open_icon) + g_object_unref(node->open_icon); + node->open_icon = icon ? g_object_ref(icon) : NULL; + break; + } + + case FM_TREE_MODEL_FONT_STYLE_COLUMN: + node->font_style = va_arg(args, PangoStyle); + break; + + case FM_TREE_MODEL_TEXT_WEIGHT_COLUMN: + node->text_weight = va_arg(args, int); + node->text_weight_override = TRUE; + break; + + default: { + int dynamic_index = column - FM_TREE_MODEL_NUM_COLUMNS; + if (dynamic_index >= 0 && dynamic_index < node->num_extra_values) { + GValue *dst = &node->extra_values[dynamic_index]; + GType type = G_VALUE_TYPE(dst); + + /* Initialize dst if not already done */ + if (G_VALUE_TYPE(dst) == 0) + g_value_init(dst, type); + + /* Set value from va_arg depending on type */ + if (type == G_TYPE_STRING) { + const char *str = va_arg(args, const char *); + g_value_set_string(dst, str); + } else if (type == G_TYPE_INT) { + int v = va_arg(args, int); + g_value_set_int(dst, v); + } else if (type == G_TYPE_BOOLEAN) { + gboolean b = va_arg(args, int); + g_value_set_boolean(dst, b); + } else if (g_type_is_a(type, G_TYPE_OBJECT)) { + GObject *obj = va_arg(args, GObject *); + g_value_set_object(dst, obj ? G_OBJECT(obj) : NULL); + } else { + g_warning("fm_tree_model_set: unsupported dynamic column type %s", + g_type_name(type)); + /* Consume arg anyway to correctly advance va_list */ + (void)va_arg(args, void *); + } + } else { + g_warning("fm_tree_model_set: invalid column %d", column); + } + break; + } + } + } + + va_end(args); + + /* Update TreeView */ + GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), iter); + gtk_tree_model_row_changed(GTK_TREE_MODEL(model), path, iter); + gtk_tree_path_free(path); +} + +gboolean isDummyNode(GtkTreeIter *iter) +{ + if (!iter->user_data) { + return TRUE; + } + return FALSE; +} + static void fm_tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, int column, GValue *value) { @@ -1264,12 +1602,12 @@ fm_tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, int column, GVa } break; case FM_TREE_MODEL_CLOSED_ICON_COLUMN: - g_value_init (value, G_TYPE_ICON); - g_value_set_object (value, node == NULL ? NULL : tree_node_get_closed_icon (node)); - break; - case FM_TREE_MODEL_OPEN_ICON_COLUMN: - g_value_init (value, G_TYPE_ICON); - g_value_set_object (value, node == NULL ? NULL : tree_node_get_open_icon (node)); + g_value_init (value, G_TYPE_ICON); + g_value_set_object (value, node == NULL ? NULL : tree_node_get_closed_icon (node)); + break; + case FM_TREE_MODEL_OPEN_ICON_COLUMN: + g_value_init (value, G_TYPE_ICON); + g_value_set_object (value, node == NULL ? NULL : tree_node_get_open_icon (node)); break; case FM_TREE_MODEL_FONT_STYLE_COLUMN: g_value_init (value, PANGO_TYPE_STYLE); @@ -1279,27 +1617,46 @@ fm_tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter, int column, GVa g_value_set_enum (value, PANGO_STYLE_NORMAL); } break; - case FM_TREE_MODEL_TEXT_WEIGHT_COLUMN: - g_value_init (value, G_TYPE_INT); + case FM_TREE_MODEL_TEXT_WEIGHT_COLUMN: + g_value_init (value, G_TYPE_INT); - if (node != NULL) { - if (node->fav_unavailable) { - g_value_set_int (value, UNAVAILABLE_TEXT_WEIGHT); - } - else - if (node->pinned) { - g_value_set_int (value, PINNED_TEXT_WEIGHT); - } - else { - g_value_set_int (value, NORMAL_TEXT_WEIGHT); - } - } else { - g_value_set_int (value, NORMAL_TEXT_WEIGHT); - } + if (node != NULL) { + if (node->fav_unavailable) { + g_value_set_int (value, UNAVAILABLE_TEXT_WEIGHT); + } + else + if (node->pinned) { + g_value_set_int (value, PINNED_TEXT_WEIGHT); + } + else { + g_value_set_int (value, NORMAL_TEXT_WEIGHT); + } + } else { + g_value_set_int (value, NORMAL_TEXT_WEIGHT); + } - break; + break; default: - g_assert_not_reached (); + if (!node) { + // dummy node + FMTreeModel *m=(FMTreeModel *)model; + if (column >= 0 && column < m->details->num_columns) { + g_value_init(value, m->details->column_types[column]); + return; + } + return; + } else { + /* --- Dynamic columns --- */ + int dynamic_index = column - FM_TREE_MODEL_NUM_COLUMNS; + if (dynamic_index >= 0 && dynamic_index < node->num_extra_values) { + GValue *val = &node->extra_values[dynamic_index]; + g_value_init(value, G_VALUE_TYPE(val)); + g_value_copy(val, value); + return; + } + } + g_warning("fm_tree_model_get_value: invalid column %d", column); + g_value_init(value, G_TYPE_INVALID); } } @@ -1329,9 +1686,17 @@ fm_tree_model_iter_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter TreeNode *parent; g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); - g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), parent_iter), FALSE); + if(parent_iter == NULL) { + FMTreeModel *tree_model = FM_TREE_MODEL(model); + parent = tree_model->details->head_root_node; + if(!parent)parent = tree_model->details->root_node; + return make_iter_for_node(parent, iter, tree_model->details->stamp); + } else { + g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), parent_iter), FALSE); + parent = parent_iter->user_data; + } + - parent = parent_iter->user_data; if (parent == NULL) { return make_iter_invalid (iter); } @@ -1370,14 +1735,11 @@ fm_tree_model_iter_has_child (GtkTreeModel *model, GtkTreeIter *iter) g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter), FALSE); node = iter->user_data; + if(!node) return FALSE; - has_child = node != NULL && (node->directory != NULL || node->parent == NULL); + if(node->is_empty) return FALSE; -#if 0 - g_warning ("Node '%s' %s", - node && node->file ? nemo_file_get_uri (node->file) : "no name", - has_child ? "has child" : "no child"); -#endif + has_child = node != NULL && (node->directory != NULL || ISROOTNODE(node)); return has_child; } @@ -1385,14 +1747,25 @@ fm_tree_model_iter_has_child (GtkTreeModel *model, GtkTreeIter *iter) static int fm_tree_model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter) { - TreeNode *parent, *node; + FMTreeModel *tree_model; + TreeNode *parent=NULL, *node; int n; g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); g_return_val_if_fail (iter == NULL || iter_is_valid (FM_TREE_MODEL (model), iter), FALSE); + tree_model = FM_TREE_MODEL(model); + if (iter == NULL) { - return 1; + // If no iterator is given, we are at the top level + int count = 0; + for (node = tree_model->details->head_root_node; node != NULL; node = node->next) { + count++; + } + for (node = tree_model->details->root_node; node != NULL; node = node->next) { + count++; + } + return count; } parent = iter->user_data; @@ -1400,6 +1773,19 @@ fm_tree_model_iter_n_children (GtkTreeModel *model, GtkTreeIter *iter) return 0; } + // Check if the parent is a head node + TreeNode *head_node; + for (head_node = tree_model->details->head_root_node; head_node != NULL; head_node = head_node->next) { + if (parent == head_node) { + n = 0; + for (node = head_node->first_child; node != NULL; node = node->next) { + n++; + } + return n; + } + } + + // Default case: number of children of the parent n = tree_node_has_dummy_child (parent) ? 1 : 0; for (node = parent->first_child; node != NULL; node = node->next) { n++; @@ -1414,7 +1800,7 @@ fm_tree_model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, { FMTreeModel *tree_model; TreeNode *parent, *node; - int i; + int i=0; g_return_val_if_fail (FM_IS_TREE_MODEL (model), FALSE); g_return_val_if_fail (parent_iter == NULL @@ -1423,27 +1809,54 @@ fm_tree_model_iter_nth_child (GtkTreeModel *model, GtkTreeIter *iter, tree_model = FM_TREE_MODEL (model); if (parent_iter == NULL) { - node = tree_model->details->root_node; - for (i = 0; i < n && node != NULL; i++, node = node->next); - return make_iter_for_node (node, iter, - tree_model->details->stamp); + // If no parent iterator is given, we are at the top level + if(tree_model->details->head_root_node) { + // Check the head nodes + for (node = tree_model->details->head_root_node; node != NULL; node = node->next, i++) { + if (i == n) { + return make_iter_for_node(node, iter, tree_model->details->stamp); + } + } + } else { + // Only regular root nodes + for (node = tree_model->details->root_node; node != NULL; node = node->next, i++) { + if (i == n) { + return make_iter_for_node(node, iter, tree_model->details->stamp); + } + } + } + return make_iter_invalid(iter); } parent = parent_iter->user_data; if (parent == NULL) { return make_iter_invalid (iter); } - - i = tree_node_has_dummy_child (parent) ? 1 : 0; - if (n == 0 && i == 1) { - return make_iter_for_dummy_row (parent, iter, parent_iter->stamp); + // Check if the parent is a head node + TreeNode *head_node; + for (head_node = tree_model->details->head_root_node; head_node != NULL; head_node = head_node->next) { + if (parent == head_node) { + for (node = head_node->first_child; node != NULL; node = node->next, i++) { + if (i == n) { + return make_iter_for_node(node, iter, parent_iter->stamp); + } + } + return make_iter_invalid(iter); + } } - for (node = parent->first_child; i != n; i++, node = node->next) { - if (node == NULL) { - return make_iter_invalid (iter); + // Check if the parent is a dummy node + if (tree_node_has_dummy_child(parent)) { + if (n == 0) { + return make_iter_for_dummy_row(parent, iter, parent_iter->stamp); + } + n--; + } + // Default case: Check the children of the parent + for (node = parent->first_child; node != NULL; node = node->next, i++) { + if (i == n) { + return make_iter_for_node(node, iter, parent_iter->stamp); } } - return make_iter_for_node (node, iter, parent_iter->stamp); } @@ -1471,9 +1884,19 @@ update_monitoring_idle_callback (gpointer callback_data) model = FM_TREE_MODEL (callback_data); model->details->monitoring_update_idle_id = 0; - for (node = model->details->root_node; node != NULL; node = node->next) { - update_monitoring (model, node); - } + + if(model->details->head_root_node) { + TreeNode *head; + for (head = model->details->head_root_node; head != NULL; head = head->next) { + for (node = head->first_child; node != NULL; node = node->next) { + update_monitoring (model, node); + } + } + } //else { + for (node = model->details->root_node; node != NULL; node = node->next) { + update_monitoring (model, node); + } +// } return FALSE; } @@ -1502,8 +1925,17 @@ stop_monitoring (FMTreeModel *model) { TreeNode *node; - for (node = model->details->root_node; node != NULL; node = node->next) { - stop_monitoring_directory_and_children (model, node); + if(model->details->head_root_node) { + TreeNode *head; + for (head = model->details->head_root_node; head != NULL; head = head->next) { + for (node = head->first_child; node != NULL; node = node->next) { + stop_monitoring_directory_and_children (model, node); + } + } + } else { + for (node = model->details->root_node; node != NULL; node = node->next) { + stop_monitoring_directory_and_children (model, node); + } } } @@ -1582,13 +2014,170 @@ fm_tree_model_unref_node (GtkTreeModel *model, GtkTreeIter *iter) } } +gboolean +fm_tree_model_append_head_root_node(FMTreeModel *model, const char *nodeName, GtkTreeIter *iter) +{ + if (!FM_IS_TREE_MODEL(model) || nodeName == NULL || iter == NULL) { + if(iter)make_iter_invalid(iter); + return FALSE; + } + + /* Create node */ + TreeNode *node = g_new0 (TreeNode, 1); + node->ref_count = 1; + node->display_name = g_strdup(nodeName); + node->file = NULL; + node->parent = NULL; + node->root = NULL; + node->isheadnode = TRUE; /*defines head nodes just for organization purpose (2nd level is possible)*/ + node->inserted = FALSE; /* default */ + tree_node_init_extra_columns(node, model); + + /* Append into linked list of head nodes */ + if (model->details->head_root_node == NULL) { + model->details->head_root_node = node; + } else { + TreeNode *last_head_node; + for (last_head_node = model->details->head_root_node; + last_head_node->next != NULL; + last_head_node = last_head_node->next) {} + last_head_node->next = node; + node->prev = last_head_node; + } + + /* --- Notify GTK: build child-path (index among top-level entries) --- */ + + /* Compute the top-level index for the new node. + Important: if your top-level sequence is head_nodes followed by root_node list, + this index is just the position in the head list (0..n-1). */ + int index = 0; + for (TreeNode *t = model->details->head_root_node; t != NULL; t = t->next) { + if (t == node) + break; + index++; + } + + /* Create a one-element path [index] */ + GtkTreePath *path = gtk_tree_path_new(); + gtk_tree_path_append_index(path, index); + + /* Make a valid iter for the node (this sets iter->stamp etc.) */ + if (!make_iter_for_node(node, iter, model->details->stamp)) { + /* defensive: shouldn't happen */ + gtk_tree_path_free(path); + tree_node_destroy (model, node); + make_iter_invalid(iter); + return FALSE;; + } + + /* Mark node inserted in your model state (if your code relies on this) */ + node->inserted = TRUE; + + /* Emit the row-inserted signal on the child model; + the GtkTreeModelSort above will hear this and update itself. */ + gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, iter); + + gtk_tree_path_free(path); + + return TRUE; +} + +gboolean +fm_tree_model_append_child_node(FMTreeModel *model, + GtkTreeIter *parent_iter, + GtkTreeIter *new_iter) +{ + + /* Default: return invalid if something goes wrong */ + + g_return_val_if_fail(new_iter !=NULL, FALSE); + g_return_val_if_fail(parent_iter != NULL, FALSE); + + TreeNode *parent = parent_iter->user_data; + if (parent == NULL) { + g_warning("fm_tree_model_append_child_node: invalid parent_iter"); + return FALSE; + } + + /* --- Create new child --- */ + TreeNode *node = g_new0(TreeNode, 1); + node->ref_count = 1; + node->display_name = NULL; + node->file = NULL; + node->root = NULL; + node->parent = parent; + node->isheadnode = TRUE; + node->inserted = FALSE; + tree_node_init_extra_columns(node, model); + + /* --- Insert into child list --- */ + if (parent->first_child == NULL) { + parent->first_child = node; + } else { + TreeNode *last = parent->first_child; + while (last->next) + last = last->next; + last->next = node; + node->prev = last; + } + + /* --- Determine index among siblings --- */ + int index = 0; + for (TreeNode *t = parent->first_child; t != NULL; t = t->next) { + if (t == node) + break; + index++; + } + + /* --- TreePath of the parent node + index --- */ + GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), parent_iter); + gtk_tree_path_append_index(path, index); + + /* --- Create iterator --- */ + if (!make_iter_for_node(node, new_iter, model->details->stamp)) { + /* defensive: shouldn't happen */ + tree_node_destroy (model, node); + gtk_tree_path_free(path); + g_free(node); + return FALSE; + } + + /* --- Mark as inserted and send signal --- */ + node->inserted = TRUE; + gtk_tree_model_row_inserted(GTK_TREE_MODEL(model), path, new_iter); + + /* --- Notify the TreeView that the parent now has children after insertion --- */ + GtkTreePath *parent_path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), parent_iter); + gtk_tree_model_row_has_child_toggled(GTK_TREE_MODEL(model), parent_path, parent_iter); + + + gtk_tree_path_free(parent_path); + gtk_tree_path_free(path); + return TRUE; +} + + void fm_tree_model_add_root_uri (FMTreeModel *model, const char *root_uri, const char *display_name, GIcon *icon, GMount *mount) { - NemoFile *file; + GtkTreeIter child_iter; + fm_tree_model_add_root_uri_head(model, root_uri, NULL, &child_iter, display_name, icon, mount); +} + + +gboolean +fm_tree_model_add_root_uri_head (FMTreeModel *model, const char *root_uri, GtkTreeIter *parent_iter, GtkTreeIter *child_iter, + const char *display_name, GIcon *icon, GMount *mount) +{ + NemoFile *file=NULL; TreeNode *node, *cnode; FMTreeModelRoot *newroot; + if (!FM_IS_TREE_MODEL(model) || root_uri == NULL || child_iter == NULL) { + if(child_iter)make_iter_invalid(child_iter); + return FALSE; + } + file = nemo_file_get_by_uri (root_uri); newroot = tree_model_root_new (model); @@ -1600,19 +2189,58 @@ fm_tree_model_add_root_uri (FMTreeModel *model, const char *root_uri, const char } newroot->root_node = node; node->parent = NULL; - if (model->details->root_node == NULL) { - model->details->root_node = node; + + if(parent_iter) { + /* Add the new node as a child of the head_root_node */ + TreeNode *head_node = parent_iter->user_data; + if (!head_node) { + g_warning("head_root_node not found in model->details->head_root_node!"); + g_free(node->display_name); + g_object_unref(node->icon); + if (mount) { + g_object_unref(node->mount); + } + tree_model_root_free (newroot); + nemo_file_unref(file); + return FALSE; + } + tree_node_parent (node, head_node); + /* Explicitly set the root pointer of the node to newroot */ + node->root = newroot; + + GtkTreeIter parent_iter; + if (!make_iter_for_node(head_node, &parent_iter, model->details->stamp)) return FALSE; + // GtkTreePath *parent_path = gtk_tree_path_new(); + int index = get_top_level_index(model, head_node); + GtkTreePath *parent_path = gtk_tree_path_new_from_indices(index, -1); + gtk_tree_model_row_has_child_toggled(GTK_TREE_MODEL(model), + parent_path, + &parent_iter); + gtk_tree_path_free(parent_path); + } else { - /* append it */ - for (cnode = model->details->root_node; cnode->next != NULL; cnode = cnode->next); - cnode->next = node; - node->prev = cnode; + + if (model->details->root_node == NULL) { + model->details->root_node = node; + } else { + /* append it */ + for (cnode = model->details->root_node; cnode->next != NULL; cnode = cnode->next); + cnode->next = node; + node->prev = cnode; + } } nemo_file_unref (file); update_node_without_reporting (model, node); report_node_inserted (model, node); + /* Make a valid iter for the node (this sets iter->stamp etc.) */ + if (!make_iter_for_node(node, child_iter, model->details->stamp)) { + /* defensive: shouldn't happen */ + make_iter_invalid(child_iter); + return FALSE;; + } + return TRUE; } GMount * @@ -1629,26 +2257,64 @@ fm_tree_model_get_mount_for_root_node_file (FMTreeModel *model, NemoFile *file) if (node) { return node->mount; } + TreeNode *head; + for (head = model->details->head_root_node; head != NULL; head = head->next) { + for (node = head->first_child; node != NULL; node = node->next) { + if (file == node->file) { + return node->mount; + } + } + } return NULL; } - -void -fm_tree_model_remove_root_uri (FMTreeModel *model, const char *uri) +static void +fm_tree_model_free_node(FMTreeModel *model, TreeNode *node) { - TreeNode *node; GtkTreePath *path; FMTreeModelRoot *root; - NemoFile *file; + if (node) { + /* remove the node */ - file = nemo_file_get_by_uri (uri); - for (node = model->details->root_node; node != NULL; node = node->next) { - if (file == node->file) { - break; + if (node->mount) { + g_object_unref (node->mount); + node->mount = NULL; + } + + if(node->file)nemo_file_monitor_remove (node->file, model); + path = get_node_path (model, node); + + /* Report row_deleted before actually deleting */ + gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path); + gtk_tree_path_free (path); + + if (node->prev) { + node->prev->next = node->next; + } + if (node->next) { + node->next->prev = node->prev; + } + + if (node == model->details->root_node) { + model->details->root_node = node->next; + } + if (node == model->details->head_root_node) { + model->details->head_root_node = node->next; } - } - nemo_file_unref (file); + /* destroy the root identifier */ + root = node->root; + destroy_node_without_reporting (model, node); + if(root) { + if(root->file_to_node_map)g_hash_table_destroy (root->file_to_node_map); + g_free (root); + } + } +} +static void +fm_tree_model_free_top_node(FMTreeModel *model, TreeNode *node) +{ + GtkTreePath *path; if (node) { /* remove the node */ @@ -1657,7 +2323,6 @@ fm_tree_model_remove_root_uri (FMTreeModel *model, const char *uri) node->mount = NULL; } - nemo_file_monitor_remove (node->file, model); path = get_node_path (model, node); /* Report row_deleted before actually deleting */ @@ -1673,15 +2338,67 @@ fm_tree_model_remove_root_uri (FMTreeModel *model, const char *uri) if (node == model->details->root_node) { model->details->root_node = node->next; } + if (node == model->details->head_root_node) { + model->details->head_root_node = node->next; + } /* destroy the root identifier */ - root = node->root; destroy_node_without_reporting (model, node); - g_hash_table_destroy (root->file_to_node_map); - g_free (root); } } +void +fm_tree_model_remove_all_nodes (FMTreeModel *model) +{ + TreeNode *head_node; + if(model->details->head_root_node) { + for (head_node = model->details->head_root_node; head_node != NULL; head_node = head_node->next) { + while(head_node->first_child) { + if (!head_node->first_child->isheadnode) { + fm_tree_model_free_node (model, head_node->first_child); + } else { + fm_tree_model_free_top_node (model, model->details->head_root_node); + } + } + } + } + while(model->details->root_node) { + fm_tree_model_free_node (model, model->details->root_node); + } + while(model->details->head_root_node) { + fm_tree_model_free_top_node (model, model->details->head_root_node); + } +} +void +fm_tree_model_remove_root_uri (FMTreeModel *model, const char *uri) +{ + TreeNode *node, *foundnode=NULL; + NemoFile *file; + + file = nemo_file_get_by_uri (uri); + if(model->details->head_root_node) { + TreeNode *head_node; + for (head_node = model->details->head_root_node; head_node != NULL; head_node = head_node->next) { + for (node = head_node->first_child; node != NULL; node = node->next) { + if (file == node->file) { + foundnode = node; + break; + } + } + } + } else { + for (node = model->details->root_node; node != NULL; node = node->next) { + if (file == node->file) { + foundnode = node; + break; + } + } + } + nemo_file_unref (file); + node = foundnode; + fm_tree_model_free_node (model, node); +} + FMTreeModel * fm_tree_model_new (void) { @@ -1689,6 +2406,17 @@ fm_tree_model_new (void) model = g_object_new (FM_TYPE_TREE_MODEL, NULL); + + model->details->column_types = g_new(GType, FM_TREE_MODEL_NUM_COLUMNS); + model->details->column_types[FM_TREE_MODEL_DISPLAY_NAME_COLUMN] = G_TYPE_STRING; + model->details->column_types[FM_TREE_MODEL_CLOSED_ICON_COLUMN] = g_icon_get_type(); + model->details->column_types[FM_TREE_MODEL_OPEN_ICON_COLUMN] = g_icon_get_type(); + model->details->column_types[FM_TREE_MODEL_FONT_STYLE_COLUMN] = PANGO_TYPE_STYLE; + model->details->column_types[FM_TREE_MODEL_TEXT_WEIGHT_COLUMN] = G_TYPE_INT; + // Weitere Spaltentypen hier + + model->details->num_columns = FM_TREE_MODEL_NUM_COLUMNS; + return model; } @@ -1742,7 +2470,7 @@ fm_tree_model_iter_get_file (FMTreeModel *model, GtkTreeIter *iter) TreeNode *node; g_return_val_if_fail (FM_IS_TREE_MODEL (model), NULL); - g_return_val_if_fail (iter_is_valid (FM_TREE_MODEL (model), iter), NULL); + if (!iter_is_valid (FM_TREE_MODEL (model), iter)) return NULL; node = iter->user_data; return node == NULL ? NULL : nemo_file_ref (node->file); @@ -1755,33 +2483,45 @@ fm_tree_model_iter_compare_roots (FMTreeModel *model, GtkTreeIter *iter_a, GtkTreeIter *iter_b) { - TreeNode *a, *b, *n; + TreeNode *a, *b, *n; - g_return_val_if_fail (FM_IS_TREE_MODEL (model), 0); - g_return_val_if_fail (iter_is_valid (model, iter_a), 0); - g_return_val_if_fail (iter_is_valid (model, iter_b), 0); + g_return_val_if_fail(FM_IS_TREE_MODEL(model), 0); + g_return_val_if_fail(iter_is_valid(model, iter_a), 0); + g_return_val_if_fail(iter_is_valid(model, iter_b), 0); - a = iter_a->user_data; - b = iter_b->user_data; + a = iter_a->user_data; + b = iter_b->user_data; - g_assert (a != NULL && a->parent == NULL); - g_assert (b != NULL && b->parent == NULL); + if (a == b) { + return 0; + } - if (a == b) { - return 0; - } + /* Administrative nodes come first */ + TreeNode *head; + for (head = model->details->head_root_node; head != NULL; head = head->next) { + if (a == head) return -1; + if (b == head) return 1; - for (n = model->details->root_node; n != NULL; n = n->next) { - if (n == a) { - return -1; - } - if (n == b) { - return 1; - } - } - g_assert_not_reached (); + + /* Check children of this administrative node */ + TreeNode *child; + for (child = head->first_child; child != NULL; child = child->next) { + if (a == child) return -1; + if (b == child) return 1; + } + } + /* Optional: normal root nodes */ + for (n = model->details->root_node; n != NULL; n = n->next) { + if (a == n) return -1; + if (b == n) return 1; + } + /* Should never be reached now */ + g_warning("fm_tree_model_iter_compare_roots: unexpected nodes a=%p b=%p", a, b); + return 0; } + + gboolean fm_tree_model_iter_is_root (FMTreeModel *model, GtkTreeIter *iter) { @@ -1792,9 +2532,14 @@ fm_tree_model_iter_is_root (FMTreeModel *model, GtkTreeIter *iter) node = iter->user_data; if (node == NULL) { return FALSE; - } else { - return (node->parent == NULL); } + if (node->parent && node->parent->isheadnode) { + return TRUE; + } + if (node->parent == NULL) + return TRUE; + return FALSE; + } gboolean @@ -1816,6 +2561,15 @@ fm_tree_model_file_get_iter (FMTreeModel *model, return make_iter_for_node (node, iter, model->details->stamp); } } + TreeNode *head_node; + for (head_node = model->details->head_root_node; head_node != NULL; head_node = head_node->next) { + for (root_node = head_node->first_child; root_node != NULL; root_node = root_node->next) { + node = get_node_from_file (root_node->root, file); + if (node != NULL) { + return make_iter_for_node (node, iter, model->details->stamp); + } + } + } return FALSE; } @@ -1833,10 +2587,21 @@ do_update_node (NemoFile *file, } } + if (node == NULL) { + TreeNode *head, *cnode; + for (head = model->details->head_root_node; head != NULL; head = head->next) { + for (cnode = head->first_child; cnode != NULL; cnode = cnode->next) { + node = get_node_from_file (cnode->root, file); + if (node != NULL) { + break; + } + } + } + return; + } if (node == NULL) { return; } - update_node (model, node); } @@ -1883,6 +2648,11 @@ fm_tree_model_finalize (GObject *object) model = FM_TREE_MODEL (object); + if (model->details->column_types) { + g_free(model->details->column_types); + model->details->column_types = NULL; + } + for (root_node = model->details->root_node; root_node != NULL; root_node = next_root) { next_root = root_node->next; root = root_node->root; @@ -1890,6 +2660,12 @@ fm_tree_model_finalize (GObject *object) g_hash_table_destroy (root->file_to_node_map); g_free (root); } + TreeNode *head, *next_node; + for (head = model->details->head_root_node; head != NULL; head = next_node) { + next_node = head->next; + g_free(head); + } + if (model->details->monitoring_update_idle_id != 0) { g_source_remove (model->details->monitoring_update_idle_id); @@ -1898,7 +2674,6 @@ fm_tree_model_finalize (GObject *object) if (model->details->highlighted_files != NULL) { nemo_file_list_free (model->details->highlighted_files); } - g_free (model->details); G_OBJECT_CLASS (fm_tree_model_parent_class)->finalize (object); @@ -1947,3 +2722,72 @@ fm_tree_model_tree_model_init (GtkTreeModelIface *iface) iface->unref_node = fm_tree_model_unref_node; } +static gboolean +fm_tree_model_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + FMTreeModel *model = FM_TREE_MODEL(drag_source); + GtkTreeIter iter; + + // Standard logic: Check if the path is valid + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path)) { + return FALSE; + } + + // If a custom function is registered, call it + if (model->details->custom_row_draggable_func != NULL) { + return model->details->custom_row_draggable_func( + drag_source, + path, + model->details->custom_row_draggable_data + ); + } + + return TRUE; +} + +void +fm_tree_model_set_custom_row_draggable_func( + FMTreeModel *model, + gboolean (*func)(GtkTreeDragSource *drag_source, GtkTreePath *path, gpointer user_data), + gpointer user_data +) { + g_return_if_fail(FM_IS_TREE_MODEL(model)); + model->details->custom_row_draggable_func = func; + model->details->custom_row_draggable_data = user_data; +} + + +static gboolean +fm_tree_model_drag_data_get(GtkTreeDragSource *drag_source, + GtkTreePath *path, + GtkSelectionData *selection_data) +{ + FMTreeModel *model = FM_TREE_MODEL(drag_source); + GtkTreeIter iter; + TreeNode *node; + + if (!gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path)) + return FALSE; + + node = iter.user_data; + if (!node) return FALSE; + + // send the URI as text + if (node->file) { + char *uri = nemo_file_get_uri(node->file); + gtk_selection_data_set_text(selection_data, uri, -1); + g_free(uri); + } + + return TRUE; +} + + +static void +fm_tree_model_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = fm_tree_model_row_draggable; + iface->drag_data_get = fm_tree_model_drag_data_get; +} + diff --git a/src/nemo-tree-sidebar-model.h b/src/nemo-tree-sidebar-model.h index 028287969a..283362bfbe 100644 --- a/src/nemo-tree-sidebar-model.h +++ b/src/nemo-tree-sidebar-model.h @@ -24,8 +24,8 @@ /* fm-tree-model.h - Model for the tree view */ -#ifndef FM_TREE_MODEL_H -#define FM_TREE_MODEL_H +#ifndef FM_TREE_SIDEBAR_MODEL_H +#define FM_TREE_SIDEBAR_MODEL_H #include #include @@ -79,7 +79,24 @@ void fm_tree_model_add_root_uri (FMTreeModel *model, const char *root_uri, const char *display_name, GIcon *icon, - GMount *mount); + GMount *mount); // optional else NULL +gboolean fm_tree_model_add_root_uri_head (FMTreeModel *model, const char *root_uri, + GtkTreeIter *parent_iter, GtkTreeIter *child_iter, + const char *display_name, GIcon *icon, GMount *mount); +gboolean fm_tree_model_append_head_root_node (FMTreeModel *model, const char *nodeName, GtkTreeIter *iter); +void fm_tree_model_set (FMTreeModel *model, GtkTreeIter *iter, ...); +void fm_tree_model_set_column_types (FMTreeModel *model, int new_count, const GType *types); +void fm_tree_model_remove_all_nodes (FMTreeModel *model); +gboolean iter_is_valid (FMTreeModel *model, const GtkTreeIter *iter); +gboolean fm_tree_model_append_child_node(FMTreeModel *model, + GtkTreeIter *parent_iter, + GtkTreeIter *iter); +void fm_tree_model_set_custom_row_draggable_func(FMTreeModel *model, + gboolean (*func)(GtkTreeDragSource *drag_source, GtkTreePath *path, gpointer user_data), + gpointer user_data); +gboolean fm_tree_model_drag_default_action (FMTreeModel *model, + const gchar *uri, + GdkDragAction *action); void fm_tree_model_remove_root_uri (FMTreeModel *model, const char *root_uri); gboolean fm_tree_model_iter_is_root (FMTreeModel *model, @@ -91,6 +108,7 @@ gboolean fm_tree_model_file_get_iter (FMTreeModel *model, GtkTreeIter *iter, NemoFile *file, GtkTreeIter *currentIter); +gboolean isDummyNode(GtkTreeIter *iter); GMount * fm_tree_model_get_mount_for_root_node_file (FMTreeModel *model, @@ -98,4 +116,5 @@ GMount * fm_tree_model_get_mount_for_root_node_file void fm_tree_model_set_highlight_for_files (FMTreeModel *model, GList *files); -#endif /* FM_TREE_MODEL_H */ \ No newline at end of file +#endif /* FM_TREE_SIDEBAR_MODEL_H */ +