From fb73370acd10a5789938e247aab4a0f1efffe8c9 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Tue, 7 Oct 2025 02:41:45 +0200 Subject: [PATCH 01/19] elemintion of compile warnings found memory leak in nemo-places-sidebar.c in nemo_places_sidebar_dispose --- libnemo-private/nemo-action.c | 14 +++----------- libnemo-private/nemo-search-engine-advanced.c | 4 ++-- search-helpers/nemo-mso-to-txt.c | 2 +- src/nemo-places-sidebar.c | 9 ++++++++- src/nemo-properties-window.c | 6 +----- 5 files changed, 15 insertions(+), 20 deletions(-) diff --git a/libnemo-private/nemo-action.c b/libnemo-private/nemo-action.c index 671cfc215..7113ebad1 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-search-engine-advanced.c b/libnemo-private/nemo-search-engine-advanced.c index 8f16c5aa1..c05d98bfb 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 fcbdb4a75..e1cbc4bd3 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-places-sidebar.c b/src/nemo-places-sidebar.c index 323130b43..8374d0719 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -4399,6 +4399,9 @@ nemo_places_sidebar_dispose (GObject *object) g_free (sidebar->uri); sidebar->uri = NULL; + g_clear_object (&sidebar->ui_manager); + sidebar->ui_manager = NULL; + free_drag_data (sidebar); g_clear_handle_id (&sidebar->actions_changed_idle_id, g_source_remove); @@ -4437,11 +4440,15 @@ nemo_places_sidebar_dispose (GObject *object) 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); diff --git a/src/nemo-properties-window.c b/src/nemo-properties-window.c index 7ec1e12e4..1c5339695 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 * From af71f0b1b8455facb5a14275c84c0a7f629e3e75 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 11 Oct 2025 02:44:07 +0200 Subject: [PATCH 02/19] Extend Sidebar Places with TreeView for fi --- src/nemo-file-management-properties.c | 7 +- src/nemo-places-sidebar.c | 780 +++- src/nemo-places-tree-sidebar.c | 5008 +++++++++++++++++++++++++ src/nemo-places-tree-sidebar.h | 56 + src/nemo-query-editor.c | 20 +- 5 files changed, 5670 insertions(+), 201 deletions(-) create mode 100644 src/nemo-places-tree-sidebar.c create mode 100644 src/nemo-places-tree-sidebar.h diff --git a/src/nemo-file-management-properties.c b/src/nemo-file-management-properties.c index 00b83f3a2..d97bc4e08 100644 --- a/src/nemo-file-management-properties.c +++ b/src/nemo-file-management-properties.c @@ -557,8 +557,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 54945d6cd..dbe8ff546 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -87,6 +87,9 @@ typedef struct { GtkTreeViewColumn *eject_column; GtkCellRenderer *eject_icon_cell_renderer; GtkCellRenderer *editable_renderer; + GtkCellRenderer *icon_cell_renderer; + GtkCellRenderer *padding_cell_renderer; + GtkCellRenderer *heading_cell_renderer; char *uri; GtkTreeStore *store; GtkTreeModel *store_filter; @@ -149,6 +152,9 @@ typedef struct { guint popup_menu_action_index; guint update_places_on_idle_id; + guint hidden_files_changed_id; // filter hidden files/directories + gboolean use_file_treeview; // switch file treeview functionality on/off + } NemoPlacesSidebar; typedef struct { @@ -182,7 +188,9 @@ 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_TREE_LAZY, // identify lazy loading dummies + PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, // set if childs are already known in treenode PLACES_SIDEBAR_COLUMN_COUNT }; @@ -192,6 +200,7 @@ typedef enum { PLACES_MOUNTED_VOLUME, PLACES_BOOKMARK, PLACES_HEADING, + PLACES_TREE_FOLDER, } PlaceType; typedef enum { @@ -419,6 +428,234 @@ free_place_info (PlaceInfo *info) g_free (info); } +// insert dummy node "Loading..." +static void +add_children_lazy (NemoPlacesSidebar *sidebar, + GtkTreeIter *parent_iter) +{ + GtkTreeIter dummy; + gtk_tree_store_append(sidebar->store, &dummy, parent_iter); + gtk_tree_store_set(sidebar->store, &dummy, + PLACES_SIDEBAR_COLUMN_TREE_LAZY, TRUE, + PLACES_SIDEBAR_COLUMN_TREE_NAME, _("Loading..."), + -1); +} + +// insert dummy childr node if a foulder exists +static void +add_directory_children_lazy(NemoPlacesSidebar *sidebar, + GtkTreeIter *parent_iter, + const char *uri) +{ + GFile *dir = g_file_new_for_uri(uri); + GFileEnumerator *enumerator = g_file_enumerate_children( + dir, + G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, + NULL); + + if (enumerator) { + GFileInfo *info; + while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { + if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { + const char *name = g_file_info_get_name(info); + GFile *child_dir = g_file_get_child(dir, name); + char *child_uri = g_file_get_uri(child_dir); + add_children_lazy(sidebar, parent_iter); + g_free(child_uri); + g_object_unref(child_dir); + break; + } + g_object_unref(info); + } + g_file_enumerator_close(enumerator, NULL, NULL); + g_object_unref(enumerator); + } + + g_object_unref(dir); +} + +// check if childrean are already loaded +static gboolean +children_loaded (GtkTreeModel *model, GtkTreeIter *iter) +{ + gboolean loaded = FALSE; + gtk_tree_model_get(model, iter, + PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, &loaded, + -1); + return loaded; +} + +// insert real children nodes +static void +add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *uri) +{ + gboolean has_dummy = FALSE; + GtkTreeIter child; + GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store); + + if (children_loaded(model, parent_iter)) + return; + + // prove if dummy nodes exist + if (gtk_tree_model_iter_children(model, &child, parent_iter)) { + do { + gboolean is_lazy = FALSE; + gtk_tree_model_get(model, &child, + PLACES_SIDEBAR_COLUMN_TREE_LAZY, &is_lazy, + -1); + if (is_lazy) { + has_dummy = TRUE; + break; // Nur einen Dummy entfernen + } + } while (gtk_tree_model_iter_next(model, &child)); + // Wenn schon echte Kinder existieren, nichts weiter tun + if (!has_dummy) { + return; + } + } + GFile *dir = g_file_new_for_uri(uri); + GFileEnumerator *enumerator = g_file_enumerate_children( + dir, + G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, + NULL); + + if (enumerator) { + GFileInfo *info; + while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { + if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { + const char *name = g_file_info_get_name(info); + GFile *child_dir = g_file_get_child(dir, name); + char *child_uri = NULL; + + GFileInfo *nodeinfo = g_file_query_info(child_dir, + "standard::target-uri", + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (nodeinfo) { + child_uri = g_strdup(g_file_info_get_attribute_string(nodeinfo, "standard::target-uri")); + g_object_unref(nodeinfo); + } + if(child_uri==NULL) + { + child_uri = g_file_get_uri(child_dir); + } + GtkTreeIter child_iter; + gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); + gtk_tree_store_set(GTK_TREE_STORE(model), &child_iter, + PLACES_SIDEBAR_COLUMN_TREE_NAME, name, + PLACES_SIDEBAR_COLUMN_URI, child_uri, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, + PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), + PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, + -1); + + add_directory_children_lazy(sidebar, &child_iter, child_uri); + + g_free(child_uri); + g_object_unref(child_dir); + } + g_object_unref(info); + } + g_file_enumerator_close(enumerator, NULL, NULL); + } + // remove summy child node + if (gtk_tree_model_iter_children(model, &child, parent_iter)) { + do { + gboolean is_lazy = FALSE; + gtk_tree_model_get(model, &child, + PLACES_SIDEBAR_COLUMN_TREE_LAZY, &is_lazy, + -1); + if (is_lazy) { + gtk_tree_store_remove(GTK_TREE_STORE(model), &child); + } + } while (gtk_tree_model_iter_next(model, &child)); + } + g_object_unref(enumerator); + g_object_unref(dir); + + gtk_tree_store_set(GTK_TREE_STORE(model), parent_iter, + PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, TRUE, + -1); +} + +typedef struct _LazyLoadData { + NemoPlacesSidebar *sidebar; + GtkTreePath *parent_path; + char *uri; +} LazyLoadData; + +static gboolean +add_children_idle(gpointer user_data) +{ + LazyLoadData *data = user_data; + GtkTreeIter parent_iter; + + GtkTreeModel *store = GTK_TREE_MODEL(data->sidebar->store); + + if (!gtk_tree_model_get_iter(store, &parent_iter, data->parent_path)) { + g_warning("nemo-places-sidebar.c->[add_children_idle] Could not get iter for path (node may have been removed)"); + g_free(data->uri); + gtk_tree_path_free(data->parent_path); + g_free(data); + return FALSE; + } + + add_directory_children(data->sidebar, &parent_iter, data->uri); + + g_free(data->uri); + gtk_tree_path_free(data->parent_path); + g_free(data); + + return FALSE; +} + + +static void +test_expand_row_cb(GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data) +{ + NemoPlacesSidebar *sidebar = user_data; + GtkTreeModel *filter_model = gtk_tree_view_get_model(tree_view); + char *uri = NULL; + + gtk_tree_model_get(filter_model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + if (!uri) { + g_warning("[expand_cb] No URI found for expanded row"); + return; + } + LazyLoadData *load_data = g_new0(LazyLoadData, 1); + load_data->sidebar = sidebar; + load_data->uri = g_strdup(uri); + + GtkTreePath *store_path = NULL; + + /* path conversion: filter → store */ + if (GTK_IS_TREE_MODEL_FILTER(filter_model)) { + GtkTreeIter store_iter; + gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter_model), + &store_iter, + iter); + store_path = gtk_tree_model_get_path(GTK_TREE_MODEL(sidebar->store), &store_iter); + g_debug("[expand_cb] iter converted to store path via filter"); + } else { + store_path = gtk_tree_path_copy(path); + g_debug("[expand_cb] model is direct store, path copied"); + } + + load_data->parent_path = store_path; + + /* start asynchron */ + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, add_children_idle, load_data, NULL); + g_free(uri); +} + static GtkTreeIter add_place (NemoPlacesSidebar *sidebar, PlaceType place_type, @@ -478,6 +715,21 @@ add_place (NemoPlacesSidebar *sidebar, PLACES_SIDEBAR_COLUMN_SHOW_DF, show_df_percent, -1); + // add children + if (uri != NULL && sidebar->use_file_treeview) { + switch(section_type) { + case SECTION_NETWORK: + if(place_type != PLACES_BUILT_IN) + add_children_lazy(sidebar, &iter); + break; + case SECTION_DEVICES: + add_children_lazy(sidebar, &iter); + break; + default: + add_directory_children_lazy(sidebar, &iter, uri); + break; + } + } g_clear_object (&gicon); return cat_iter; @@ -1433,18 +1685,127 @@ desktop_setting_changed_callback (gpointer user_data) update_places (sidebar); } + +static gboolean +find_uri_recursive (GtkTreeModel *model, + GtkTreeIter *parent, /* NULL = Top-Level */ + const char *location, + GtkTreePath **out_path, + NemoPlacesSidebar *sidebar) +{ + GtkTreeIter child; + gboolean valid_child; + gchar *uri = NULL; + gchar *tree_name = NULL; + + /* iterate children of `parent` (or top-level when parent == NULL) */ + valid_child = gtk_tree_model_iter_children (model, &child, parent); + while (valid_child) { + gtk_tree_model_get (model, &child, + PLACES_SIDEBAR_COLUMN_URI, &uri, + PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_name, + -1); + + /* Lazy-Loading / "(loading)"-Dummy prüfen */ + if (tree_name && g_strcmp0(tree_name, "(loading)") == 0) { + if (uri) { + /* -> Direkt echte Kinder laden */ + GtkTreeIter store_iter; + gtk_tree_model_filter_convert_iter_to_child_iter( + GTK_TREE_MODEL_FILTER(sidebar->store_filter), + &store_iter, + &child + ); + + /* Lädt synchron echte Kinder in sidebar->store */ + add_directory_children(sidebar, &store_iter, uri); + + /* Jetzt kann die Kindliste aktualisiert werden */ + g_free(tree_name); + g_free(uri); + uri = NULL; + tree_name = NULL; + + /* Neu starten der Kinderliste für dieses Parent */ + valid_child = gtk_tree_model_iter_children (model, &child, parent); + continue; + } + } + + /* Treffer gefunden? */ + if (uri && g_strcmp0(uri, location) == 0) { + *out_path = gtk_tree_model_get_path(model, &child); + g_free(tree_name); + g_free(uri); + return TRUE; + } + + /* Rekursiv tiefer gehen */ + GtkTreePath *subpath = NULL; + if (find_uri_recursive(model, &child, location, &subpath, sidebar)) { + *out_path = subpath; + g_free(tree_name); + g_free(uri); + return TRUE; + } + + /* Aufräumen */ + g_free(tree_name); + g_free(uri); + uri = NULL; + tree_name = NULL; + + valid_child = gtk_tree_model_iter_next(model, &child); + } + + return FALSE; +} + 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; + char *uri=NULL; + GtkTreePath *path = NULL; gboolean found = FALSE; - if (strcmp (sidebar->uri, location) != 0) { + if (g_strcmp0 (sidebar->uri, location) == 0) return; + if(sidebar->use_file_treeview) + { + /* with use_file_treeview a recursiv search is necessary*/ + 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); + + GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); + + /* 🔍 search for uri recursiv in tree */ + if (find_uri_recursive(model, NULL, location, &path, sidebar)) { + + /* Nur Elternpfade expandieren (nicht den Zielknoten selbst) */ + GtkTreePath *parent_path = gtk_tree_path_copy(path); + if (gtk_tree_path_get_depth(parent_path) > 1) { + gtk_tree_path_up(parent_path); + gtk_tree_view_expand_to_path(sidebar->tree_view, parent_path); + } + + /* select target node */ + gtk_tree_selection_select_path(selection, path); + + /* scroll to target node */ + gtk_tree_view_scroll_to_cell(sidebar->tree_view, path, NULL, FALSE, 0.5, 0.5); + + gtk_tree_path_free(parent_path); + gtk_tree_path_free(path); + } + } else { g_free (sidebar->uri); sidebar->uri = g_strdup (location); @@ -1455,6 +1816,7 @@ loading_uri_callback (NemoWindow *window, &iter_cat); while (valid_cat) { + if (uri) g_free(uri); valid_child = gtk_tree_model_iter_children (GTK_TREE_MODEL (sidebar->store_filter), &iter_child, &iter_cat); @@ -3812,6 +4174,9 @@ query_tooltip_callback (GtkWidget *widget, tooltip_markup = g_strdup (_("Stop")); } } else { + if (type == PLACES_TREE_FOLDER) { + return FALSE; + } gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_TOOLTIP, &tooltip_markup, @@ -3848,18 +4213,26 @@ update_expanded_state (GtkTreeView *tree_view, gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &type, -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); + + 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; } } @@ -4007,9 +4380,11 @@ icon_cell_renderer_func (GtkTreeViewColumn *column, gpointer user_data) { PlaceType type; + GIcon *gicon = NULL; gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_GICON, &gicon, -1); if (type == PLACES_HEADING) { @@ -4019,10 +4394,12 @@ icon_cell_renderer_func (GtkTreeViewColumn *column, } else { g_object_set (cell, "visible", TRUE, - "xpad", 3, - "ypad", 2, + "gicon", gicon, NULL); } + + if (gicon) + g_object_unref(gicon); } static void @@ -4032,50 +4409,156 @@ padding_cell_renderer_func (GtkTreeViewColumn *column, GtkTreeIter *iter, gpointer user_data) { - PlaceType type; - - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); + GtkTreePath *path = gtk_tree_model_get_path(model, iter); + int depth = gtk_tree_path_get_depth(path); /* Root = 1 */ + gtk_tree_path_free(path); - if (type == PLACES_HEADING) { - g_object_set (cell, - "visible", FALSE, - "xpad", 0, - "ypad", 0, - NULL); - } else { - g_object_set (cell, - "visible", TRUE, - "ypad", 3, - NULL); - } + g_object_set(cell, + "width", 3 + (depth - 1) * 0, /* optional: leicht anpassen */ + "visible", TRUE, + NULL); } + static void -heading_cell_renderer_func (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - PlaceType type; +text_cell_renderer_func(GtkTreeViewColumn *column, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + gchar *tree_text = NULL; + gchar *name_text = NULL; + gchar *heading_text = NULL; + gint row_type = 0; + gboolean lazy = FALSE; + + /* Lese beide Text-Spalten + Row-Type */ + gtk_tree_model_get(model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, + PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_text, + PLACES_SIDEBAR_COLUMN_NAME, &name_text, + PLACES_SIDEBAR_COLUMN_TREE_LAZY, &lazy, + -1); + + if (row_type == PLACES_HEADING) { + /* Für Headings benutze HEADING_TEXT (bold) */ + g_free(tree_text); + 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 { + /* Normale Zeilen: bevorzugt TREE_NAME, fallback auf NAME */ + const gchar *display = ""; + if (tree_text != NULL && *tree_text != '\0') + display = tree_text; + else if (name_text != NULL && *name_text != '\0') + display = name_text; + + /* Standardgewicht */ + g_object_set(renderer, + "weight-set", FALSE, + NULL); + + g_object_set(renderer, "text", display, NULL); + + g_free(tree_text); + g_free(name_text); + } +} - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); - if (type == PLACES_HEADING) { - g_object_set (cell, - "visible", TRUE, - NULL); - } else { - g_object_set (cell, - "visible", FALSE, - NULL); - } +static void +nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView *tree_view) +{ + GtkCellRenderer *cell; + GtkTreeViewColumn *primary_column; + + /* === Primäre Spalte === */ + 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); + + /* Entferne 'text' hier — wir setzen Text ausschließlich 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); + + /* Setze cell data func, die jetzt TREE_NAME und NAME berücksichtigt */ + 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); + + + /* === Expander Column entfällt, GTK rendert automatisch in der ersten Spalte === */ +} + +static void +hidden_files_mode_changed_callback (NemoWindow *window, + NemoPlacesSidebar *sidebar) +{ + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); } + static gboolean row_visibility_function (GtkTreeModel *model, GtkTreeIter *iter, @@ -4090,6 +4573,18 @@ row_visibility_function (GtkTreeModel *model, PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, -1); + if (type == PLACES_TREE_FOLDER) { + NemoWindowShowHiddenFilesMode mode = nemo_window_get_hidden_files_mode (sidebar->window); + // --- 2. Versteckte Verzeichnisse ausblenden --- + if ((mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE)) return TRUE; + gchar *name = NULL; + gboolean visible = TRUE; + gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_TREE_NAME, &name, -1); + if (name && name[0] == '.') // beginnt mit Punkt → versteckt + visible = FALSE; + g_free(name); + return visible; + } if (type != PLACES_HEADING || section_type != SECTION_BOOKMARKS) return TRUE; @@ -4107,8 +4602,6 @@ 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; @@ -4121,6 +4614,9 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) sidebar->in_drag = FALSE; + /* enables TreeView for files */ + sidebar->use_file_treeview = TRUE; + sidebar->desktop_dnd_source_fs = NULL; sidebar->desktop_dnd_can_delete_source = FALSE; @@ -4128,6 +4624,10 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) sidebar->update_places_on_idle_id = 0; + sidebar->bookmarks_changed_id = 0; + + sidebar-> hidden_files_changed_id = 0; + 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, @@ -4157,140 +4657,11 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) 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); - } - - 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); - - expander_column = GTK_TREE_VIEW_COLUMN (gtk_tree_view_column_new ()); - gtk_tree_view_column_set_sizing (expander_column, GTK_TREE_VIEW_COLUMN_FIXED); - - 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); - - 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); - - gtk_tree_view_set_expander_column (tree_view, expander_column); + nemo_places_tree_sidebar_renderer_init (sidebar, tree_view); 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, + PLACES_SIDEBAR_COLUMN_TREE_NAME, GTK_SORT_ASCENDING); sidebar->store_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sidebar->store), NULL); @@ -4361,6 +4732,11 @@ 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(tree_view, + "test-expand-row", + G_CALLBACK(test_expand_row_cb), + sidebar); + g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_DESKTOP_IS_HOME_DIR, G_CALLBACK(desktop_setting_changed_callback), sidebar); @@ -4391,8 +4767,23 @@ nemo_places_sidebar_dispose (GObject *object) sidebar = NEMO_PLACES_SIDEBAR (object); - sidebar->window = NULL; - sidebar->tree_view = NULL; + if(sidebar->tree_view != NULL) + { + g_signal_handlers_disconnect_by_func(GTK_WIDGET(sidebar->tree_view), + G_CALLBACK (query_tooltip_callback), + sidebar); + + sidebar->tree_view = NULL; + } + + if (sidebar->window!=NULL) { + if(sidebar->hidden_files_changed_id != 0) { + g_signal_handler_disconnect (sidebar->window, + sidebar->hidden_files_changed_id); + sidebar->hidden_files_changed_id = 0; + } + sidebar->window = NULL; + } g_free (sidebar->uri); sidebar->uri = NULL; @@ -4579,6 +4970,9 @@ 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); + sidebar->hidden_files_changed_id = + g_signal_connect_object (window, "hidden-files-mode-changed", + G_CALLBACK (hidden_files_mode_changed_callback), sidebar, 0); update_places (sidebar); } @@ -4672,7 +5066,10 @@ nemo_shortcuts_model_new (NemoPlacesSidebar *sidebar) G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT, - G_TYPE_BOOLEAN + G_TYPE_BOOLEAN, + G_TYPE_STRING, + G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN }; model = g_object_new (NEMO_TYPE_SHORTCUTS_MODEL, NULL); @@ -4684,3 +5081,4 @@ nemo_shortcuts_model_new (NemoPlacesSidebar *sidebar) return GTK_TREE_STORE (model); } + diff --git a/src/nemo-places-tree-sidebar.c b/src/nemo-places-tree-sidebar.c new file mode 100644 index 000000000..a9efd323f --- /dev/null +++ b/src/nemo-places-tree-sidebar.c @@ -0,0 +1,5008 @@ +/* -*- Mode: C; indent-tabs-mode: f; c-basic-offset: 4; tab-width: 4 -*- */ + +/* + * Nemo + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, MA 02110-1335, USA. + * + * Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk) + * Cosimo Cecchi + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "nemo-actions.h" +#include "nemo-application.h" +#include "nemo-bookmark-list.h" +#include "nemo-places-tree-sidebar.h" +#include "nemo-properties-window.h" +#include "nemo-window.h" +#include "nemo-window-slot.h" + +#define DEBUG_FLAG NEMO_DEBUG_PLACES +#include + +#define EXPANDER_PAD_COLUMN_WIDTH 4 +#define EJECT_COLUMN_MIN_WIDTH 22 +#define EJECT_COLUMN_MAX_WIDTH 60 +#define DRAG_EXPAND_CATEGORY_DELAY 500 +#define EJECT_PAD_COLUMN_WIDTH 14 + +static gint EJECT_ICON_SIZE_NOT_HOVERED = 0; +static gint EJECT_ICON_SIZE_HOVERED = GTK_ICON_SIZE_MENU; +static gint menu_icon_pixels = 16; +#define EJECT_ICON_SIZE_REDUCTION 4 + +#if (!GLIB_CHECK_VERSION(2,50,0)) +#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; + GtkCellRenderer *icon_cell_renderer; + GtkCellRenderer *padding_cell_renderer; + GtkCellRenderer *heading_cell_renderer; + char *uri; + GtkTreeStore *store; + GtkTreeModel *store_filter; + + NemoWindow *window; + NemoBookmarkList *bookmarks; + GVolumeMonitor *volume_monitor; + + NemoActionManager *action_manager; + gulong actions_changed_id; + guint actions_changed_idle_id; + + GtkUIManager *ui_manager; + + GtkActionGroup *bookmark_action_group; + guint bookmark_action_group_merge_id; + GtkActionGroup *action_action_group; + guint action_action_group_merge_id; + + gboolean actions_need_update; + + gboolean devices_header_added; + gboolean bookmarks_header_added; + + /* DnD */ + GList *drag_list; + gboolean drag_data_received; + int drag_data_info; + gboolean drop_occured; + gboolean in_drag; + gchar *desktop_dnd_source_fs; + gboolean desktop_dnd_can_delete_source; + + GtkWidget *popup_menu; + + /* volume mounting - delayed open process */ + gboolean mounting; + NemoWindowSlot *go_to_after_mount_slot; + NemoWindowOpenFlags go_to_after_mount_flags; + + guint bookmarks_changed_id; + + gboolean my_computer_expanded; + gboolean bookmarks_expanded; + gboolean devices_expanded; + gboolean network_expanded; + + gboolean updating_sidebar; + + /* Store the positions of the last + * entry prior to bookmarks, and + * the first entry after bookmarks + * to allow drag and drop creation + * of new bookmarks */ + gchar *top_bookend_uri; + gchar *bottom_bookend_uri; + + gint bookmark_breakpoint; + guint expand_timeout_source; + guint popup_menu_action_index; + guint update_places_on_idle_id; + + guint hidden_files_changed_id; + +} NemoPlacesTreeSidebar; + +typedef struct { + GtkScrolledWindowClass parent; +} NemoPlacesTreeSidebarClass; + +typedef struct { + GObject parent; +} NemoPlacesTreeSidebarProvider; + +typedef struct { + GObjectClass parent; +} NemoPlacesTreeSidebarProviderClass; + +typedef struct TreeNode TreeNode; +struct TreeNode { + /* part of this node for the file itself */ + int ref_count; + + NemoFile *file; + char *display_name; + GIcon *icon; + GMount *mount; + GIcon *closed_icon; + GIcon *open_icon; + + GtkTreeIter *root; + + TreeNode *parent; + TreeNode *next; + TreeNode *prev; + + /* part of the node used only for directories */ + 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; + + /* misc. flags */ + guint done_loading : 1; + guint force_has_dummy : 1; + guint inserted : 1; + guint pinned : 1; + guint fav_unavailable : 1; +}; + +enum { + 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, + PLACES_SIDEBAR_COLUMN_BOOKMARK, + PLACES_SIDEBAR_COLUMN_TOOLTIP, + PLACES_SIDEBAR_COLUMN_EJECT_ICON, + PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, + 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 only + PLACES_SIDEBAR_COLUMN_COUNT +}; + +typedef enum { + PLACES_BUILT_IN, + PLACES_XDG_DIR, + PLACES_MOUNTED_VOLUME, + PLACES_BOOKMARK, + PLACES_HEADING, + PLACES_TREE_FOLDER, +} PlaceType; + +typedef enum { + SECTION_COMPUTER, + SECTION_XDG_BOOKMARKS, + SECTION_BOOKMARKS, + SECTION_DEVICES, + SECTION_NETWORK, +} SectionType; + +enum { + POSITION_UPPER, + POSITION_MIDDLE, + POSITION_LOWER +}; + +static void open_selected_bookmark (NemoPlacesTreeSidebar *sidebar, + GtkTreeModel *model, + GtkTreeIter *iter, + NemoWindowOpenFlags flags); +static void nemo_places_tree_sidebar_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static gboolean eject_or_unmount_bookmark (NemoPlacesTreeSidebar *sidebar, + GtkTreePath *path); +static gboolean eject_or_unmount_selection (NemoPlacesTreeSidebar *sidebar); +static void check_unmount_and_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_unmount, + gboolean *show_eject); + +static void update_places (NemoPlacesTreeSidebar *sidebar); +static void update_places_on_idle (NemoPlacesTreeSidebar *sidebar); +static void rebuild_menu (NemoPlacesTreeSidebar *sidebar); +static void actions_changed (gpointer user_data); +/* Identifiers for target types */ +enum { + GTK_TREE_MODEL_ROW, + TEXT_URI_LIST +}; + +/* Target types for dragging from the shortcuts list */ +static const GtkTargetEntry nemo_shortcuts_source_targets[] = { + { (char *)"GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW } +}; + +/* Target types for dropping into the shortcuts list */ +static const GtkTargetEntry nemo_shortcuts_drop_targets [] = { + { (char *)"GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }, + { (char *)"text/uri-list", 0, TEXT_URI_LIST } +}; + +/* Drag and drop interface declarations */ +typedef struct { + GtkTreeStore parent; + + NemoPlacesTreeSidebar *sidebar; +} NemoShortcutsTreeModel; + +typedef struct { + GtkTreeStoreClass parent_class; +} NemoShortcutsTreeModelClass; + +#define NEMO_TYPE_SHORTCUTS_TREE_MODEL (_nemo_shortcuts_tree_model_get_type ()) +#define NEMO_SHORTCUTS_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_SHORTCUTS_MODEL_TYPE, NemoShortcutsTreeModel)) + +GType _nemo_shortcuts_tree_model_get_type (void); +static void _nemo_shortcuts_tree_model_drag_source_init (GtkTreeDragSourceIface *iface); +G_DEFINE_TYPE_WITH_CODE (NemoShortcutsTreeModel, _nemo_shortcuts_tree_model, GTK_TYPE_TREE_STORE, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, + _nemo_shortcuts_tree_model_drag_source_init)); +static GtkTreeStore *nemo_shortcuts_tree_model_new (NemoPlacesTreeSidebar *sidebar); + +G_DEFINE_TYPE (NemoPlacesTreeSidebar, nemo_places_tree_sidebar, GTK_TYPE_SCROLLED_WINDOW); + +static void +breakpoint_changed_cb (NemoPlacesTreeSidebar *sidebar) +{ + sidebar->bookmark_breakpoint = g_settings_get_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT); + update_places (sidebar); +} + +static void +increment_bookmark_breakpoint (NemoPlacesTreeSidebar *sidebar) +{ + g_signal_handlers_block_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); + + sidebar->bookmark_breakpoint ++; + g_settings_set_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, sidebar->bookmark_breakpoint); + + g_signal_handlers_unblock_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); +} + +static void +decrement_bookmark_breakpoint (NemoPlacesTreeSidebar *sidebar) +{ + g_signal_handlers_block_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); + + sidebar->bookmark_breakpoint --; + g_settings_set_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, sidebar->bookmark_breakpoint); + + g_signal_handlers_unblock_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); +} + +static gboolean +should_show_desktop (void) +{ + return g_settings_get_boolean (nemo_desktop_preferences, NEMO_PREFERENCES_SHOW_DESKTOP) && + !g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_DESKTOP_IS_HOME_DIR); +} + +static gboolean +is_built_in_bookmark (NemoFile *file) +{ + if (nemo_file_is_home (file)) { + return TRUE; + } + + if (nemo_file_is_desktop_directory (file) && should_show_desktop ()) + return TRUE; + else + return FALSE; +} + +static GtkTreeIter +add_heading (NemoPlacesTreeSidebar *sidebar, + SectionType section_type, + const gchar *title) +{ + GtkTreeIter cat_iter; + + 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; +} + +static GtkTreeIter +check_heading_for_devices (NemoPlacesTreeSidebar *sidebar, + SectionType section_type, + GtkTreeIter cat_iter) +{ + if (section_type == SECTION_DEVICES) { + if (!sidebar->devices_header_added) { + cat_iter = add_heading (sidebar, SECTION_DEVICES, + _("Devices")); + sidebar->devices_header_added = TRUE; + } + } + return cat_iter; +} + +typedef struct { + PlaceType place_type; + SectionType section_type; + gchar *name; + gchar *icon_name; + gchar *uri; + GDrive *drive; + GVolume *volume; + GMount *mount; + gint index; + gchar *tooltip; + gint df_percent; + gboolean show_df_percent; +} PlaceInfo; + +static gint +sort_places_func (gconstpointer a, + gconstpointer b) +{ + return g_utf8_collate (((PlaceInfo *) a)->name, ((PlaceInfo *) b)->name); +} + +static PlaceInfo * +new_place_info (PlaceType place_type, + SectionType section_type, + gchar *name, + gchar *icon_name, + gchar *uri, + GDrive *drive, + GVolume *volume, + GMount *mount, + gint index, + gchar *tooltip, + gint df_percent, + gboolean show_df_percent) +{ + PlaceInfo *info = g_new0 (PlaceInfo, 1); + + info->place_type = place_type; + info->section_type = section_type; + info->name = g_utf8_make_valid (name, -1); + info->icon_name = g_strdup (icon_name); + info->uri = (g_strdup (uri)); + info->drive = drive ? g_object_ref (drive) : NULL; + info->volume = volume ? g_object_ref (volume) : NULL; + info->mount = mount ? g_object_ref (mount) : NULL; + info->index = index; + info->tooltip = g_strdup (tooltip); + info->df_percent = df_percent; + info->show_df_percent = show_df_percent; + + return info; +} + +static void +free_place_info (PlaceInfo *info) +{ + g_free (info->name); + g_free (info->icon_name); + g_free (info->uri); + g_clear_object (&info->drive); + g_clear_object (&info->volume); + g_clear_object (&info->mount); + g_free (info->tooltip); + + g_free (info); +} + +static void +add_children_lazy(NemoPlacesTreeSidebar *sidebar, + GtkTreeIter *parent_iter) +{ + GtkTreeIter dummy; + gtk_tree_store_append(sidebar->store, &dummy, + parent_iter); + gtk_tree_store_set(sidebar->store, + &dummy, PLACES_SIDEBAR_COLUMN_TREE_NAME, _("(loading)"), -1); +} + + +static void +add_directory_children_lazy(NemoPlacesTreeSidebar *sidebar, + GtkTreeIter *parent_iter, const char *uri) +{ + + GFile *dir = g_file_new_for_uri(uri); + GFileEnumerator *enumerator = g_file_enumerate_children( + dir, + G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, + NULL); + + if (enumerator) { + GFileInfo *info; + while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { + if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { + const char *name = g_file_info_get_name(info); + GFile *child_dir = g_file_get_child(dir, name); + char *child_uri = g_file_get_uri(child_dir); + add_children_lazy(sidebar, parent_iter); + g_free(child_uri); + g_object_unref(child_dir); + break; + } + g_object_unref(info); + } + g_file_enumerator_close(enumerator, NULL, NULL); + g_object_unref(enumerator); + } + + g_object_unref(dir); +} + +static void +add_directory_children(NemoPlacesTreeSidebar *sidebar, GtkTreeIter *parent_iter, const char *uri) +{ + GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store); + + GtkTreeIter child; + if (gtk_tree_model_iter_children(model, &child, parent_iter)) { + // Es gibt mindestens ein Kind + gchar *name = NULL; + gtk_tree_model_get(model, &child, PLACES_SIDEBAR_COLUMN_TREE_NAME, &name, -1); + + if (g_strcmp0(name, "(loading)") == 0) { + // Entferne nur den Dummy + gtk_tree_store_remove(GTK_TREE_STORE(model), &child); + } else { + // Echte Kinder schon vorhanden → nichts tun + g_free(name); + return; + } + g_free(name); + } + + // --- Enumerate real children --- + GFile *dir = g_file_new_for_uri(uri); + GFileEnumerator *enumerator = g_file_enumerate_children( + dir, + G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, + NULL); + + if (enumerator) { + GFileInfo *info; + while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { + if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { + const char *name = g_file_info_get_name(info); + GFile *child_dir = g_file_get_child(dir, name); + char *child_uri = NULL; + + GFileInfo *nodeinfo = g_file_query_info(child_dir, + "standard::target-uri", + G_FILE_QUERY_INFO_NONE, + NULL, NULL); + + if (nodeinfo) { + child_uri = g_strdup(g_file_info_get_attribute_string(nodeinfo, "standard::target-uri")); + g_object_unref(nodeinfo); + } + if(child_uri==NULL) + { + child_uri = g_file_get_uri(child_dir); + } + GtkTreeIter child_iter; + gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); + gtk_tree_store_set( + GTK_TREE_STORE(model), &child_iter, + PLACES_SIDEBAR_COLUMN_TREE_NAME, name, + PLACES_SIDEBAR_COLUMN_URI, child_uri, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, + PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), + -1); + + add_directory_children_lazy(sidebar, &child_iter, child_uri); + + g_free(child_uri); + g_object_unref(child_dir); + } + g_object_unref(info); + } + g_file_enumerator_close(enumerator, NULL, NULL); + } + + g_object_unref(enumerator); + g_object_unref(dir); +} + + + +static GtkTreeIter +add_place (NemoPlacesTreeSidebar *sidebar, + PlaceType place_type, + SectionType section_type, + const char *name, + const char *icon_name, + const char *uri, + GDrive *drive, + GVolume *volume, + GMount *mount, + int index, + const char *tooltip, + int df_percent, + gboolean show_df_percent, + GtkTreeIter cat_iter) +{ + GtkTreeIter iter; + GIcon *gicon; + gboolean show_eject, show_unmount; + gboolean show_eject_button; + + cat_iter = check_heading_for_devices (sidebar, section_type, cat_iter); + + check_unmount_and_eject (mount, volume, drive, + &show_unmount, &show_eject); + + if (show_unmount || show_eject) { + g_assert (place_type != PLACES_BOOKMARK); + } + + if (mount == NULL) { + show_eject_button = FALSE; + } else { + show_eject_button = (show_unmount || show_eject); + } + + 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, + 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_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); + + // add children + if (uri != NULL) + switch(section_type) { + case SECTION_NETWORK: + case SECTION_DEVICES: + add_children_lazy(sidebar, &iter); + break; + default: + add_directory_children_lazy(sidebar, &iter, uri); + break; + } + g_clear_object (&gicon); + + return cat_iter; +} + +typedef struct { + const gchar *location; + const gchar *last_uri; + NemoPlacesTreeSidebar *sidebar; + GtkTreePath *path; +} RestoreLocationData; + +static gboolean +restore_selection_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + RestoreLocationData *data = user_data; + gchar *uri; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + if (g_strcmp0 (uri, data->last_uri) == 0 || + g_strcmp0 (uri, data->location) == 0) { + data->path = gtk_tree_path_copy (path); + } + + g_free (uri); + + return (data->path != NULL); +} + +static gboolean +restore_expand_state_foreach (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + PlaceType place_type; + SectionType section_type; + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + 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) { + if (section_type == SECTION_COMPUTER) { + if (sidebar->my_computer_expanded) + gtk_tree_view_expand_to_path (sidebar->tree_view, path); + else + gtk_tree_view_collapse_row (sidebar->tree_view, path); + } else if (section_type == SECTION_BOOKMARKS) { + if (sidebar->bookmarks_expanded) + gtk_tree_view_expand_to_path (sidebar->tree_view, path); + else + gtk_tree_view_collapse_row (sidebar->tree_view, path); + } else if (section_type == SECTION_DEVICES) { + if (sidebar->devices_expanded) + gtk_tree_view_expand_to_path (sidebar->tree_view, path); + else + gtk_tree_view_collapse_row (sidebar->tree_view, path); + } else if (section_type == SECTION_NETWORK) { + if (sidebar->network_expanded) + gtk_tree_view_expand_to_path (sidebar->tree_view, path); + else + gtk_tree_view_collapse_row (sidebar->tree_view, path); + } + } + return FALSE; +} + +static void +restore_expand_state (NemoPlacesTreeSidebar *sidebar) +{ + gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store_filter), + restore_expand_state_foreach, sidebar); +} + + +static void expand_or_collapse_category (NemoPlacesTreeSidebar *sidebar, + SectionType section_type, gboolean expand) +{ + switch (section_type) { + case SECTION_COMPUTER: + sidebar->my_computer_expanded = expand; + break; + case SECTION_BOOKMARKS: + sidebar->bookmarks_expanded = expand; + break; + case SECTION_DEVICES: + sidebar->devices_expanded = expand; + break; + case SECTION_NETWORK: + sidebar->network_expanded = expand; + break; + case SECTION_XDG_BOOKMARKS: + default: + break; + } + + restore_expand_state (sidebar); +} + +static void +sidebar_update_restore_selection (NemoPlacesTreeSidebar *sidebar, + const gchar *location, + const gchar *last_uri) +{ + RestoreLocationData data; + GtkTreeSelection *selection; + + data.location = location; + data.last_uri = last_uri; + data.sidebar = sidebar; + data.path = NULL; + + gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store_filter), + restore_selection_foreach, &data); + + if (data.path != NULL) { + selection = gtk_tree_view_get_selection (sidebar->tree_view); + gtk_tree_selection_select_path (selection, data.path); + gtk_tree_path_free (data.path); + } +} + +static gint +get_disk_full (GFile *file, gchar **tooltip_info) +{ + GFileInfo *info; + GError *error; + guint64 k_used, k_total, k_free; + gint df_percent; + float fraction; + int prefix; + gchar *size_string; + gchar *out_string; + + error = NULL; + df_percent = -1; + out_string = NULL; + + info = g_file_query_filesystem_info (file, + "filesystem::*", + NULL, + &error); + + if (info != NULL) { + k_used = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED); + k_total = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); + k_free = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); + + if (k_total > 0) { + fraction = ((float) k_used / (float) k_total) * 100.0; + + df_percent = (gint) rintf(fraction); + + prefix = nemo_global_preferences_get_size_prefix_preference (); + size_string = g_format_size_full (k_free, prefix); + + out_string = g_strdup_printf (_("Free space: %s"), size_string); + + g_free (size_string); + } + + g_object_unref (info); + } + + if (error != NULL) { + g_warning ("Couldn't get disk full info for: %s", error->message); + g_clear_error (&error); + } + + if (out_string == NULL) { + out_string = g_strdup (" "); + } + + *tooltip_info = out_string; + + return df_percent; +} + +static gboolean +home_on_different_fs (const gchar *home_uri) +{ + GFile *home = g_file_new_for_uri (home_uri); + GFile *root = g_file_new_for_uri ("file:///"); + GFileInfo *home_info, *root_info; + const gchar *home_id, *root_id; + gboolean res; + + res = FALSE; + home_info = g_file_query_info (home, + G_FILE_ATTRIBUTE_ID_FILESYSTEM, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, NULL); + root_info = g_file_query_info (root, + G_FILE_ATTRIBUTE_ID_FILESYSTEM, + G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, + NULL, NULL); + + if (home_info && root_info) { + home_id = g_file_info_get_attribute_string (home_info, G_FILE_ATTRIBUTE_ID_FILESYSTEM); + root_id = g_file_info_get_attribute_string (root_info, G_FILE_ATTRIBUTE_ID_FILESYSTEM); + res = g_strcmp0 (home_id, root_id) != 0; + g_object_unref (home_info); + g_object_unref (root_info); + } else { + if (home_info) + g_object_unref (home_info); + if (root_info) + g_object_unref (root_info); + } + g_object_unref (home); + g_object_unref (root); + return res; +} + +static gchar * +get_icon_name (const gchar *uri) +{ + NemoFile *file = nemo_file_get_by_uri (uri); + gchar *icon_name; + + icon_name = nemo_file_get_control_icon_name (file); + nemo_file_unref (file); + + return icon_name; +} + +static void +update_places (NemoPlacesTreeSidebar *sidebar) +{ + NemoBookmark *bookmark; + GtkTreeSelection *selection; + GtkTreeIter last_iter, cat_iter; + GtkTreeModel *model; + GVolumeMonitor *volume_monitor; + GList *mounts, *l, *ll; + GMount *mount; + GList *drives; + GDrive *drive; + GList *volumes; + GVolume *volume; + int bookmark_count, bookmark_index; + char *location, *mount_uri, *name, *desktop_path, *last_uri, *identifier; + const gchar *bookmark_name; + gchar *icon; + GFile *root, *df_file; + NemoWindowSlot *slot; + char *tooltip; + gchar *tooltip_info; + GList *network_mounts, *network_volumes; + gint full; + + DEBUG ("Updating places sidebar"); + + sidebar->updating_sidebar = TRUE; + + model = NULL; + 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); + } + gtk_tree_store_clear (sidebar->store); + + sidebar->devices_header_added = FALSE; + sidebar->bookmarks_header_added = FALSE; + + slot = nemo_window_get_active_slot (sidebar->window); + location = nemo_window_slot_get_current_uri (slot); + + network_mounts = network_volumes = NULL; + volume_monitor = sidebar->volume_monitor; + + cat_iter = add_heading (sidebar, SECTION_COMPUTER, + _("My Computer")); + /* add built in bookmarks */ + + /* home folder */ + mount_uri = nemo_get_home_directory_uri (); + icon = get_icon_name (mount_uri); + + df_file = g_file_new_for_uri (mount_uri); + full = get_disk_full (df_file, &tooltip_info); + g_clear_object (&df_file); + + tooltip = g_strdup_printf (_("Open your personal folder\n%s"), tooltip_info); + g_free (tooltip_info); + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Home"), icon, + mount_uri, NULL, NULL, NULL, 0, + tooltip, + full, home_on_different_fs (mount_uri) && full > -1, + cat_iter); + g_free (icon); + sidebar->top_bookend_uri = g_strdup (mount_uri); + g_free (mount_uri); + g_free (tooltip); + + if (should_show_desktop ()) { + /* desktop */ + desktop_path = nemo_get_desktop_directory (); + mount_uri = g_filename_to_uri (desktop_path, NULL, NULL); + icon = get_icon_name (mount_uri); + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Desktop"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Open the contents of your desktop in a folder"), 0, FALSE, + cat_iter); + g_free (icon); + g_free (sidebar->top_bookend_uri); + sidebar->top_bookend_uri = g_strdup (mount_uri); + g_free (mount_uri); + g_free (desktop_path); + } + + /* add bookmarks */ + bookmark_count = nemo_bookmark_list_length (sidebar->bookmarks); + /* in certain situations (i.e. removed a bookmark), the breakpoint is smaller than + * the number of bookmarks - make sure to fix this before iterating through a list of them. + * We don't overwrite the stored breakpoint because the bookmark list could simply be reloading, + * and we want the original number still when we update places again. + */ + gint temp_breakpoint = sidebar->bookmark_breakpoint; + + if (temp_breakpoint < 0 || + temp_breakpoint > bookmark_count) { + temp_breakpoint = bookmark_count; + } + + for (bookmark_index = 0; bookmark_index < temp_breakpoint; ++bookmark_index) { + bookmark = nemo_bookmark_list_item_at (sidebar->bookmarks, bookmark_index); + + root = nemo_bookmark_get_location (bookmark); + + bookmark_name = nemo_bookmark_get_name (bookmark); + icon = nemo_bookmark_get_icon_name (bookmark); + mount_uri = nemo_bookmark_get_uri (bookmark); + tooltip = g_file_get_parse_name (root); + + cat_iter = add_place (sidebar, PLACES_BOOKMARK, + SECTION_XDG_BOOKMARKS, + bookmark_name, icon, mount_uri, + NULL, NULL, NULL, bookmark_index, + tooltip, 0, FALSE, + cat_iter); + g_object_unref (root); + g_free (icon); + g_free (mount_uri); + g_free (tooltip); + } + + if (eel_vfs_supports_uri_scheme ("favorites")) { + gint n = xapp_favorites_get_n_favorites (xapp_favorites_get_default ()); + + if (n > 0) { + mount_uri = (char *)"favorites:///"; /* No need to strdup */ + icon = "xapp-user-favorites-symbolic"; + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Favorites"), icon, mount_uri, + NULL, NULL, NULL, 0, + _("Favorite files"), 0, FALSE, cat_iter); + + sidebar->bottom_bookend_uri = g_strdup (mount_uri); + } + } + + gboolean recent_enabled; + recent_enabled = g_settings_get_boolean (cinnamon_privacy_preferences, + NEMO_PREFERENCES_RECENT_ENABLED); + + if (recent_enabled && eel_vfs_supports_uri_scheme ("recent")) { + mount_uri = (char *)"recent:///"; /* No need to strdup */ + icon = NEMO_ICON_SYMBOLIC_FOLDER_RECENT; + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Recent"), icon, mount_uri, + NULL, NULL, NULL, 0, + _("Recent files"), 0, FALSE, cat_iter); + + if (sidebar->bottom_bookend_uri == NULL) { + sidebar->bottom_bookend_uri = g_strdup (mount_uri); + } + } + + /* file system root */ + mount_uri = (char *)"file:///"; /* No need to strdup */ + icon = NEMO_ICON_SYMBOLIC_FILESYSTEM; + + df_file = g_file_new_for_uri (mount_uri); + full = get_disk_full (df_file, &tooltip_info); + g_clear_object (&df_file); + + tooltip = g_strdup_printf (_("Open the contents of the File System\n%s"), tooltip_info); + g_free (tooltip_info); + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("File System"), icon, + mount_uri, NULL, NULL, NULL, 0, + tooltip, + full, full > -1, + cat_iter); + g_free (tooltip); + + if (sidebar->bottom_bookend_uri == NULL) { + sidebar->bottom_bookend_uri = g_strdup (mount_uri); + } + + if (eel_vfs_supports_uri_scheme("trash")) { + mount_uri = (char *)"trash:///"; /* No need to strdup */ + icon = nemo_trash_monitor_get_symbolic_icon_name (); + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_COMPUTER, + _("Trash"), icon, mount_uri, + NULL, NULL, NULL, 0, + _("Open the trash"), 0, FALSE, + cat_iter); + g_free (icon); + } + + cat_iter = add_heading (sidebar, SECTION_BOOKMARKS, + _("Bookmarks")); + + while (bookmark_index < bookmark_count) { + bookmark = nemo_bookmark_list_item_at (sidebar->bookmarks, bookmark_index); + + root = nemo_bookmark_get_location (bookmark); + + bookmark_name = nemo_bookmark_get_name (bookmark); + icon = nemo_bookmark_get_icon_name (bookmark); + mount_uri = nemo_bookmark_get_uri (bookmark); + tooltip = g_file_get_parse_name (root); + + cat_iter = add_place (sidebar, PLACES_BOOKMARK, + SECTION_BOOKMARKS, + bookmark_name, icon, mount_uri, + NULL, NULL, NULL, bookmark_index, + tooltip, 0, FALSE, + cat_iter); + g_object_unref (root); + g_free (icon); + g_free (mount_uri); + g_free (tooltip); + ++bookmark_index; + } + + GList *place_infos = NULL; + PlaceInfo *place_info; + + /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ + mounts = g_volume_monitor_get_mounts (volume_monitor); + + for (l = mounts; l != NULL; l = l->next) { + mount = l->data; + if (g_mount_is_shadowed (mount)) { + g_object_unref (mount); + continue; + } + volume = g_mount_get_volume (mount); + if (volume != NULL) { + g_object_unref (volume); + g_object_unref (mount); + continue; + } + root = g_mount_get_default_location (mount); + + if (!g_file_is_native (root)) { + gboolean really_network = TRUE; + gchar *path = g_file_get_path (root); + if (!path) { + network_mounts = g_list_prepend (network_mounts, mount); + g_object_unref (root); + continue; + } + gchar *escaped1 = g_uri_unescape_string (path, ""); + gchar *escaped2 = g_uri_unescape_string (escaped1, ""); + gchar *ptr = g_strrstr (escaped2, "file://"); + if (ptr != NULL) { + GFile *actual_file = g_file_new_for_uri (ptr); + if (g_file_is_native(actual_file)) { + really_network = FALSE; + } + g_object_unref(actual_file); + } + g_free (path); + g_free (escaped1); + g_free (escaped2); + if (really_network) { + network_mounts = g_list_prepend (network_mounts, mount); + g_object_unref (root); + continue; + } + } + + icon = nemo_get_mount_icon_name (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + tooltip = g_file_get_parse_name (root); + place_info = new_place_info (PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, mount_uri, + NULL, NULL, mount, 0, tooltip, 0, FALSE); + place_infos = g_list_prepend (place_infos, place_info); + g_object_unref (root); + g_object_unref (mount); + g_free (icon); + g_free (name); + g_free (mount_uri); + g_free (tooltip); + + } + g_list_free (mounts); + + /* first go through all connected drives */ + drives = g_volume_monitor_get_connected_drives (volume_monitor); + + for (l = drives; l != NULL; l = l->next) { + drive = l->data; + + volumes = g_drive_get_volumes (drive); + if (volumes != NULL) { + for (ll = volumes; ll != NULL; ll = ll->next) { + volume = ll->data; + identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); + + if (g_strcmp0 (identifier, "network") == 0) { + g_free (identifier); + network_volumes = g_list_prepend (network_volumes, volume); + continue; + } + g_free (identifier); + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + gchar *full_display_name, *volume_id; + /* Show mounted volume in the sidebar */ + icon = nemo_get_mount_icon_name (mount); + root = g_mount_get_default_location (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + + volume_id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); + full_display_name = g_file_get_parse_name (root); + + df_file = g_file_new_for_uri (mount_uri); + full = get_disk_full (df_file, &tooltip_info); + g_clear_object (&df_file); + + tooltip = g_strdup_printf (_("%s (%s)\n%s"), + full_display_name, + volume_id, + tooltip_info); + g_free (tooltip_info); + place_info = new_place_info (PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, mount_uri, + drive, volume, mount, 0, tooltip, full, full > -1); + place_infos = g_list_prepend (place_infos, place_info); + g_object_unref (root); + g_object_unref (mount); + g_free (icon); + g_free (tooltip); + g_free (name); + g_free (mount_uri); + g_free (volume_id); + g_free (full_display_name); + } else { + /* Do show the unmounted volumes in the sidebar; + * this is so the user can mount it (in case automounting + * is off). + * + * Also, even if automounting is enabled, this gives a visual + * cue that the user should remember to yank out the media if + * he just unmounted it. + */ + gchar *volume_id; + icon = nemo_get_volume_icon_name (volume); + name = g_volume_get_name (volume); + + volume_id = g_volume_get_identifier (volume, + G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); + tooltip = g_strdup_printf (_("Mount and open %s (%s)"), name, volume_id); + + place_info = new_place_info (PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, NULL, + drive, volume, NULL, 0, tooltip, 0, FALSE); + place_infos = g_list_prepend (place_infos, place_info); + + g_free (icon); + g_free (name); + g_free (tooltip); + g_free (volume_id); + } + g_object_unref (volume); + } + g_list_free (volumes); + } else { + if (g_drive_is_removable (drive) && !g_drive_is_media_check_automatic (drive)) { + /* If the drive has no mountable volumes and we cannot detect media change.. we + * display the drive in the sidebar so the user can manually poll the drive by + * right clicking and selecting "Rescan..." + * + * This is mainly for drives like floppies where media detection doesn't + * work.. but it's also for human beings who like to turn off media detection + * in the OS to save battery juice. + */ + icon = nemo_get_drive_icon_name (drive); + name = g_drive_get_name (drive); + tooltip = g_strdup_printf (_("Mount and open %s"), name); + + place_info = new_place_info (PLACES_BUILT_IN, + SECTION_DEVICES, + name, icon, NULL, + drive, NULL, NULL, 0, tooltip, 0, FALSE); + place_infos = g_list_prepend (place_infos, place_info); + + g_free (icon); + g_free (tooltip); + g_free (name); + } + } + g_object_unref (drive); + } + g_list_free (drives); + + /* add all volumes that is not associated with a drive */ + volumes = g_volume_monitor_get_volumes (volume_monitor); + for (l = volumes; l != NULL; l = l->next) { + volume = l->data; + drive = g_volume_get_drive (volume); + if (drive != NULL) { + g_object_unref (volume); + g_object_unref (drive); + continue; + } + + identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); + + if (g_strcmp0 (identifier, "network") == 0) { + g_free (identifier); + network_volumes = g_list_prepend (network_volumes, volume); + continue; + } + g_free (identifier); + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + g_autofree gchar *parse_name = NULL; + icon = nemo_get_mount_icon_name (mount); + root = g_mount_get_default_location (mount); + mount_uri = g_file_get_uri (root); + + df_file = g_file_new_for_uri (mount_uri); + full = get_disk_full (df_file, &tooltip_info); + g_clear_object (&df_file); + + parse_name = g_file_get_parse_name (root); + tooltip = g_strdup_printf (_("%s\n%s"), parse_name, tooltip_info); + + g_free (tooltip_info); + g_object_unref (root); + name = g_mount_get_name (mount); + + place_info = new_place_info (PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, mount_uri, + NULL, volume, mount, 0, tooltip, full, full > -1); + place_infos = g_list_prepend (place_infos, place_info); + + g_object_unref (mount); + g_free (icon); + g_free (name); + g_free (tooltip); + g_free (mount_uri); + } else { + /* see comment above in why we add an icon for an unmounted mountable volume */ + icon = nemo_get_volume_icon_name (volume); + name = g_volume_get_name (volume); + + place_info = new_place_info (PLACES_MOUNTED_VOLUME, + SECTION_DEVICES, + name, icon, NULL, + NULL, volume, NULL, 0, name, 0, FALSE); + place_infos = g_list_prepend (place_infos, place_info); + + g_free (icon); + g_free (name); + } + g_object_unref (volume); + } + g_list_free (volumes); + + place_infos = g_list_sort (place_infos, (GCompareFunc) sort_places_func); + + for (l = place_infos; l != NULL; l = l->next) { + PlaceInfo *info = (PlaceInfo *) l->data; + + cat_iter = add_place (sidebar, + info->place_type, + info->section_type, + info->name, + info->icon_name, + info->uri, + info->drive, + info->volume, + info->mount, + info->index, + info->tooltip, + info->df_percent, + info->show_df_percent, + cat_iter); + + free_place_info (info); + } + + g_list_free (place_infos); + + /* network */ + cat_iter = add_heading (sidebar, SECTION_NETWORK, + _("Network")); + + network_volumes = g_list_reverse (network_volumes); + for (l = network_volumes; l != NULL; l = l->next) { + volume = l->data; + mount = g_volume_get_mount (volume); + + if (mount != NULL) { + network_mounts = g_list_prepend (network_mounts, mount); + continue; + } else { + icon = nemo_get_volume_icon_name (volume); + name = g_volume_get_name (volume); + tooltip = g_strdup_printf (_("Mount and open %s"), name); + + cat_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_NETWORK, + name, icon, NULL, + NULL, volume, NULL, 0, tooltip, 0, FALSE, + cat_iter); + g_free (icon); + g_free (name); + g_free (tooltip); + } + } + + g_list_free_full (network_volumes, g_object_unref); + + network_mounts = g_list_reverse (network_mounts); + for (l = network_mounts; l != NULL; l = l->next) { + mount = l->data; + root = g_mount_get_default_location (mount); + icon = nemo_get_mount_icon_name (mount); + mount_uri = g_file_get_uri (root); + name = g_mount_get_name (mount); + tooltip = g_file_get_parse_name (root); + cat_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, + SECTION_NETWORK, + name, icon, mount_uri, + NULL, NULL, mount, 0, tooltip, 0, FALSE, + cat_iter); + g_object_unref (root); + g_free (icon); + g_free (name); + g_free (mount_uri); + g_free (tooltip); + } + + g_list_free_full (network_mounts, g_object_unref); + + /* network:// */ + mount_uri = (char *)"network:///"; /* No need to strdup */ + icon = NEMO_ICON_SYMBOLIC_NETWORK; + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_NETWORK, + _("Network"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Browse the contents of the network"), 0, FALSE, + cat_iter); + + /* restore selection */ + restore_expand_state (sidebar); + sidebar_update_restore_selection (sidebar, location, last_uri); + + actions_changed (sidebar); + + sidebar->updating_sidebar = FALSE; + + g_free (location); + g_free (last_uri); +} + +static void +mount_added_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +mount_removed_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +mount_changed_callback (GVolumeMonitor *volume_monitor, + GMount *mount, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +volume_added_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +volume_removed_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +volume_changed_callback (GVolumeMonitor *volume_monitor, + GVolume *volume, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +drive_disconnected_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +drive_connected_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static void +drive_changed_callback (GVolumeMonitor *volume_monitor, + GDrive *drive, + NemoPlacesTreeSidebar *sidebar) +{ + update_places_on_idle (sidebar); +} + +static gboolean +over_eject_button (NemoPlacesTreeSidebar *sidebar, + gint x, + gint y, + GtkTreePath **path) +{ + GtkTreeViewColumn *column; + int width, col_x_offset, cell_x_offset; + gboolean show_eject; + GtkTreeIter iter; + GtkTreeModel *model; + + *path = NULL; + model = gtk_tree_view_get_model (sidebar->tree_view); + + if (gtk_tree_view_get_path_at_pos (sidebar->tree_view, + x, y, + path, &column, &col_x_offset, NULL)) { + + gtk_tree_model_get_iter (model, &iter, *path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_EJECT, &show_eject, + -1); + + if (!show_eject) { + goto out; + } + + if (column == sidebar->eject_column) { + gtk_tree_view_column_cell_set_cell_data (column, model, &iter, FALSE, FALSE); + + gtk_tree_view_column_cell_get_position (column, + sidebar->eject_icon_cell_renderer, + &cell_x_offset, &width); + if ((col_x_offset >= cell_x_offset) && (col_x_offset < cell_x_offset + width)) { + return TRUE; + } + } + } + + out: + if (*path != NULL) { + gtk_tree_path_free (*path); + *path = NULL; + } + + return FALSE; +} + +static gboolean +clicked_eject_button (NemoPlacesTreeSidebar *sidebar, + GtkTreePath **path) +{ + GdkEvent *event; + + event = gtk_get_current_event (); + + if (event) { + GdkEventButton *button_event = (GdkEventButton *) event; + if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) && + over_eject_button (sidebar, button_event->x, button_event->y, path)) { + return TRUE; + } + } + + return FALSE; +} + +static void +desktop_setting_changed_callback (gpointer user_data) +{ + NemoPlacesTreeSidebar *sidebar; + + sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + update_places (sidebar); +} + + +static gboolean +find_uri_recursive (GtkTreeModel *model, + GtkTreeIter *parent, /* NULL = Top-Level */ + const char *location, + GtkTreePath **out_path, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter child; + gboolean valid_child; + gchar *uri = NULL; + gchar *tree_name = NULL; + + /* iterate children of `parent` (or top-level when parent == NULL) */ + valid_child = gtk_tree_model_iter_children (model, &child, parent); + while (valid_child) { + gtk_tree_model_get (model, &child, + PLACES_SIDEBAR_COLUMN_URI, &uri, + PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_name, + -1); + + /* Lazy-Loading / "(loading)"-Dummy prüfen */ + if (tree_name && g_strcmp0(tree_name, "(loading)") == 0) { + if (uri) { + /* -> Direkt echte Kinder laden */ + GtkTreeIter store_iter; + gtk_tree_model_filter_convert_iter_to_child_iter( + GTK_TREE_MODEL_FILTER(sidebar->store_filter), + &store_iter, + &child + ); + + /* Lädt synchron echte Kinder in sidebar->store */ + add_directory_children(sidebar, &store_iter, uri); + + /* Jetzt kann die Kindliste aktualisiert werden */ + g_free(tree_name); + g_free(uri); + uri = NULL; + tree_name = NULL; + + /* Neu starten der Kinderliste für dieses Parent */ + valid_child = gtk_tree_model_iter_children (model, &child, parent); + continue; + } + } + + /* Treffer gefunden? */ + if (uri && g_strcmp0(uri, location) == 0) { + *out_path = gtk_tree_model_get_path(model, &child); + g_free(tree_name); + g_free(uri); + return TRUE; + } + + /* Rekursiv tiefer gehen */ + GtkTreePath *subpath = NULL; + if (find_uri_recursive(model, &child, location, &subpath, sidebar)) { + *out_path = subpath; + g_free(tree_name); + g_free(uri); + return TRUE; + } + + /* Aufräumen */ + g_free(tree_name); + g_free(uri); + uri = NULL; + tree_name = NULL; + + valid_child = gtk_tree_model_iter_next(model, &child); + } + + return FALSE; +} + +static void +loading_uri_callback (NemoWindow *window, + char *location, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeSelection *selection; + GtkTreePath *path = NULL; + + /* if location changes */ + if (g_strcmp0 (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); + + GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); + + /* 🔍 search for uri recursiv in tree */ + if (find_uri_recursive(model, NULL, location, &path, sidebar)) { + + /* Nur Elternpfade expandieren (nicht den Zielknoten selbst) */ + GtkTreePath *parent_path = gtk_tree_path_copy(path); + if (gtk_tree_path_get_depth(parent_path) > 1) { + gtk_tree_path_up(parent_path); + gtk_tree_view_expand_to_path(sidebar->tree_view, parent_path); + } + + /* select target node */ + gtk_tree_selection_select_path(selection, path); + + /* scroll to target node */ + gtk_tree_view_scroll_to_cell(sidebar->tree_view, path, NULL, FALSE, 0.5, 0.5); + + gtk_tree_path_free(parent_path); + gtk_tree_path_free(path); + } + } +} + +typedef struct { + NemoPlacesTreeSidebar *sidebar; + GdkRectangle rect; + SectionType section_type; +} CategoryExpandPayload; + +static gboolean +pointer_is_still_in_cell (gint x, + gint y, + GdkRectangle rect) +{ + gint max_x = rect.x + rect.width; + gint max_y = rect.y + rect.height; + if ((x >= rect.x && x <= max_x) && + (y >= rect.y && y <= max_y)) { + return TRUE; + } else { + return FALSE; + } +} + +static gboolean +maybe_expand_category (gpointer data) +{ + CategoryExpandPayload *payload = (CategoryExpandPayload *) data; + GdkDeviceManager *manager; + GdkDevice *pointer; + GdkWindow *window; + int x, y; + g_assert (GTK_IS_WIDGET (payload->sidebar->tree_view)); + window = gtk_widget_get_window (GTK_WIDGET (payload->sidebar->tree_view)); + + manager = gdk_display_get_device_manager (gtk_widget_get_display (GTK_WIDGET (payload->sidebar->tree_view))); + pointer = gdk_device_manager_get_client_pointer (manager); + gdk_window_get_device_position (window, pointer, + &x, &y, NULL); + if (pointer_is_still_in_cell (x, y, payload->rect)) { + expand_or_collapse_category (payload->sidebar, payload->section_type, TRUE); + } + + g_source_remove (payload->sidebar->expand_timeout_source); + payload->sidebar->expand_timeout_source = 0; + return FALSE; +} + + +static gboolean +cat_is_expanded (NemoPlacesTreeSidebar *sidebar, + SectionType section_type) +{ + switch (section_type) { + case SECTION_COMPUTER: + return sidebar->my_computer_expanded; + case SECTION_BOOKMARKS: + return sidebar->bookmarks_expanded; + case SECTION_DEVICES: + return sidebar->devices_expanded; + case SECTION_NETWORK: + return sidebar->network_expanded; + case SECTION_XDG_BOOKMARKS: + default: + return TRUE; + } +} + + +static GtkTreeViewDropPosition +get_drag_type (NemoPlacesTreeSidebar *sidebar, + gchar *drop_target_uri, + GdkRectangle rect, + int y, + SectionType section_type) +{ + gint zone; + gint upper_bound = rect.y + (rect.height / 4); + gint lower_bound = rect.y + rect.height - (rect.height / 4); + + if (y <= upper_bound) { + zone = POSITION_UPPER; + } else if (y > upper_bound && y < lower_bound) { + return GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + } else { + zone = POSITION_LOWER; + } + + if (g_strcmp0 (drop_target_uri, sidebar->top_bookend_uri) == 0 && + zone == POSITION_LOWER) { + return GTK_TREE_VIEW_DROP_AFTER; + } else if (g_strcmp0 (drop_target_uri, sidebar->bottom_bookend_uri) == 0 && + zone == POSITION_UPPER) { + return GTK_TREE_VIEW_DROP_BEFORE; + } + + if ((section_type == SECTION_XDG_BOOKMARKS || section_type == SECTION_BOOKMARKS) + && zone == POSITION_UPPER) { + return GTK_TREE_VIEW_DROP_BEFORE; + } else if ((section_type == SECTION_XDG_BOOKMARKS || section_type == SECTION_BOOKMARKS) + && zone == POSITION_LOWER) { + return GTK_TREE_VIEW_DROP_AFTER; + } else { + /* or else you want to drag items INTO the existing bookmarks */ + return GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; + } +} + + +/* Computes the appropriate row and position for dropping */ +static gboolean +compute_drop_position (GtkTreeView *tree_view, + int x, + int y, + GtkTreePath **path, + GtkTreeViewDropPosition *pos, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeModel *model; + GtkTreeIter iter; + PlaceType place_type; + SectionType section_type; + gchar *drop_target_uri = NULL; + + if (!gtk_tree_view_get_dest_row_at_pos (tree_view, + x, y, + path, pos)) { + return FALSE; + } + model = gtk_tree_view_get_model (tree_view); + + 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, + PLACES_SIDEBAR_COLUMN_URI, &drop_target_uri, + -1); + if (!cat_is_expanded (sidebar, section_type) && place_type == PLACES_HEADING) { + if (sidebar->expand_timeout_source > 0) { + goto fail; + } + CategoryExpandPayload *payload; + GtkTreeViewColumn *column; + column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 2); + payload = g_new0 (CategoryExpandPayload, 1); + payload->sidebar = sidebar; + gtk_tree_view_get_cell_area (tree_view, + *path, + column, + &payload->rect); + payload->section_type = section_type; + sidebar->expand_timeout_source = g_timeout_add_full (G_PRIORITY_DEFAULT, + DRAG_EXPAND_CATEGORY_DELAY, + (GSourceFunc) maybe_expand_category, + payload, + (GDestroyNotify) g_free); + goto fail; + } else if (place_type == PLACES_HEADING) { + if (section_type == SECTION_BOOKMARKS && + (int)nemo_bookmark_list_length (sidebar->bookmarks) == sidebar->bookmark_breakpoint) { + *pos = GTK_TREE_VIEW_DROP_AFTER; + g_free (drop_target_uri); + return TRUE; + } else { + goto fail; + } + } + + if (section_type != SECTION_XDG_BOOKMARKS && + section_type != SECTION_BOOKMARKS && + sidebar->drag_data_received && + sidebar->drag_data_info == GTK_TREE_MODEL_ROW && + g_strcmp0 (drop_target_uri, sidebar->top_bookend_uri) != 0) { + /* don't allow dropping bookmarks into non-bookmark areas */ + + goto fail; + } + + if (g_strcmp0 (drop_target_uri, "recent:///") == 0) { + goto fail; + } + + GdkRectangle rect; + GtkTreeViewColumn *column; + column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 1); + gtk_tree_view_get_cell_area (tree_view, + *path, + column, + &rect); + + *pos = get_drag_type (sidebar, drop_target_uri, rect, y, section_type); + + if (*pos != GTK_TREE_VIEW_DROP_BEFORE && + sidebar->drag_data_received && + sidebar->drag_data_info == GTK_TREE_MODEL_ROW) { + /* bookmark rows are never dragged into other bookmark rows */ + *pos = GTK_TREE_VIEW_DROP_AFTER; + } + + g_free (drop_target_uri); + return TRUE; + +fail: + g_free (drop_target_uri); + gtk_tree_path_free (*path); + *path = NULL; + return FALSE; +} + +static gboolean +get_drag_data (GtkTreeView *tree_view, + GdkDragContext *context, + unsigned int time) +{ + GdkAtom target; + + target = gtk_drag_dest_find_target (GTK_WIDGET (tree_view), + context, + NULL); + + if (target == GDK_NONE) { + return FALSE; + } + + gtk_drag_get_data (GTK_WIDGET (tree_view), + context, target, time); + + return TRUE; +} + +static void +free_drag_data (NemoPlacesTreeSidebar *sidebar) +{ + sidebar->drag_data_received = FALSE; + + if (sidebar->drag_list) { + nemo_drag_destroy_selection_list (sidebar->drag_list); + sidebar->drag_list = NULL; + } + + g_clear_pointer (&sidebar->desktop_dnd_source_fs, g_free); + sidebar->desktop_dnd_can_delete_source = FALSE; +} + +static gboolean +can_accept_file_as_bookmark (NemoFile *file) +{ + return (nemo_file_is_directory (file) && + !is_built_in_bookmark (file)); +} + +static gboolean +can_accept_items_as_bookmarks (const GList *items) +{ + int max; + char *uri; + NemoFile *file; + + /* Iterate through selection checking if item will get accepted as a bookmark. + * If more than 100 items selected, return an over-optimistic result. + */ + for (max = 100; items != NULL && max >= 0; items = items->next, max--) { + uri = ((NemoDragSelectionItem *)items->data)->uri; + file = nemo_file_get_by_uri (uri); + if (!can_accept_file_as_bookmark (file)) { + nemo_file_unref (file); + return FALSE; + } + nemo_file_unref (file); + } + + return TRUE; +} + +static gboolean +drag_motion_callback (GtkTreeView *tree_view, + GdkDragContext *context, + int x, + int y, + unsigned int time, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + int action = 0; + GtkTreeIter iter; + char *uri = NULL; + gboolean res; + + if (!sidebar->drag_data_received) { + if (!get_drag_data (tree_view, context, time)) { + return FALSE; + } + } + + if (!sidebar->in_drag) { + sidebar->in_drag = TRUE; + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); + } + + path = NULL; + res = compute_drop_position (tree_view, x, y, &path, &pos, sidebar); + + if (!res) { + gtk_tree_view_set_drag_dest_row (tree_view, NULL, GTK_TREE_VIEW_DROP_BEFORE); + goto out; + } + + if (pos == GTK_TREE_VIEW_DROP_BEFORE || + pos == GTK_TREE_VIEW_DROP_AFTER ) { + if (sidebar->drag_data_received && + sidebar->drag_data_info == GTK_TREE_MODEL_ROW) { + action = GDK_ACTION_MOVE; + } else if (can_accept_items_as_bookmarks (sidebar->drag_list)) { + action = GDK_ACTION_COPY; + } else { + action = 0; + } + } else { + if (sidebar->drag_list == NULL) { + action = 0; + } else { + gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store_filter), + &iter, path); + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), + &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + nemo_drag_default_drop_action_for_icons (context, + uri, + sidebar->drag_list, + &action, + &sidebar->desktop_dnd_source_fs, + &sidebar->desktop_dnd_can_delete_source); + g_free (uri); + } + } + + if (action != 0) { + gtk_tree_view_set_drag_dest_row (tree_view, path, pos); + } + + if (path != NULL) { + gtk_tree_path_free (path); + } + + out: + g_signal_stop_emission_by_name (tree_view, "drag-motion"); + + if (action != 0) { + gdk_drag_status (context, action, time); + } else { + gdk_drag_status (context, 0, time); + } + + return TRUE; +} + +static void +drag_leave_callback (GtkTreeView *tree_view, + GdkDragContext *context, + unsigned int time, + NemoPlacesTreeSidebar *sidebar) +{ + free_drag_data (sidebar); + gtk_tree_view_set_drag_dest_row (tree_view, NULL, GTK_TREE_VIEW_DROP_BEFORE); + g_signal_stop_emission_by_name (tree_view, "drag-leave"); +} + +/* Parses a "text/uri-list" string and inserts its URIs as bookmarks */ +static void +bookmarks_drop_uris (NemoPlacesTreeSidebar *sidebar, + GtkSelectionData *selection_data, + int position, + SectionType section_type) +{ + NemoBookmark *bookmark; + NemoFile *file; + char *uri; + char **uris; + int i; + GFile *location; + + uris = gtk_selection_data_get_uris (selection_data); + if (!uris) + return; + + for (i = 0; uris[i]; i++) { + uri = uris[i]; + file = nemo_file_get_by_uri (uri); + + if (!can_accept_file_as_bookmark (file)) { + nemo_file_unref (file); + continue; + } + + uri = nemo_file_get_drop_target_uri (file); + location = g_file_new_for_uri (uri); + nemo_file_unref (file); + + bookmark = nemo_bookmark_new (location, NULL, NULL, NULL); + + if (!nemo_bookmark_list_contains (sidebar->bookmarks, bookmark)) { + if (position < sidebar->bookmark_breakpoint || + (position == sidebar->bookmark_breakpoint && (section_type == SECTION_XDG_BOOKMARKS || + section_type == SECTION_COMPUTER))) { + increment_bookmark_breakpoint (sidebar); + } + nemo_bookmark_list_insert_item (sidebar->bookmarks, bookmark, position++); + } + + g_object_unref (location); + g_object_unref (bookmark); + g_free (uri); + } + + g_strfreev (uris); +} + +static GList * +uri_list_from_selection (GList *selection) +{ + NemoDragSelectionItem *item; + GList *ret; + GList *l; + + ret = NULL; + for (l = selection; l != NULL; l = l->next) { + item = l->data; + ret = g_list_prepend (ret, item->uri); + } + + return g_list_reverse (ret); +} + +static GList* +build_selection_list (const char *data) +{ + NemoDragSelectionItem *item; + GList *result; + char **uris; + char *uri; + int i; + + uris = g_uri_list_extract_uris (data); + + result = NULL; + for (i = 0; uris[i]; i++) { + uri = uris[i]; + item = nemo_drag_selection_item_new (); + item->uri = g_strdup (uri); + item->got_icon_position = FALSE; + result = g_list_prepend (result, item); + } + + g_strfreev (uris); + + return g_list_reverse (result); +} + +static gboolean +get_selected_iter (NemoPlacesTreeSidebar *sidebar, + GtkTreeIter *iter) +{ + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (sidebar->tree_view); + + return gtk_tree_selection_get_selected (selection, NULL, iter); +} + +static void +update_bookmark_breakpoint (NemoPlacesTreeSidebar *sidebar, + SectionType old_type, + SectionType new_type) +{ + if (old_type != new_type) { + if (old_type == SECTION_XDG_BOOKMARKS && new_type != SECTION_COMPUTER) + decrement_bookmark_breakpoint (sidebar); + else if (old_type == SECTION_BOOKMARKS) + increment_bookmark_breakpoint (sidebar); + } +} + +/* Reorders the selected bookmark to the specified position */ +static void +reorder_bookmarks (NemoPlacesTreeSidebar *sidebar, + int new_position, + SectionType new_section_type) +{ + GtkTreeIter iter; + PlaceType type; + SectionType old_section_type; + int old_position; + + /* Get the selected path */ + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &old_section_type, + PLACES_SIDEBAR_COLUMN_INDEX, &old_position, + -1); + + if (type != PLACES_BOOKMARK || + old_position < 0 || + old_position >= (int)nemo_bookmark_list_length (sidebar->bookmarks)) { + return; + } + + update_bookmark_breakpoint (sidebar, old_section_type, new_section_type); + + nemo_bookmark_list_move_item (sidebar->bookmarks, old_position, + new_position); + + if (old_position == new_position) + update_places (sidebar); +} + +static gboolean +idle_hide_bookmarks (gpointer user_data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + if (sidebar->in_drag) { + sidebar->in_drag = FALSE; + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); + } + + return FALSE; +} + +/* Callback triggered when drag data is received on the sidebar */ +static void +drag_data_received_callback (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + GtkSelectionData *selection_data, + unsigned int info, + unsigned int time, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeView *tree_view = NULL; + GtkTreePath *tree_path = NULL; + GtkTreeViewDropPosition tree_pos; + GtkTreeIter iter; + int position = 0; + GtkTreeModel *model = NULL; + char *drop_uri = NULL; + GList *selection_list = NULL; + GList *uris = NULL; + PlaceType place_type; + SectionType section_type; + gboolean success = FALSE; + + tree_view = GTK_TREE_VIEW (widget); + + /* If this is the first time receiving drag data for this drag operation */ + if (!sidebar->drag_data_received) { + /* If the drag contains URIs and is of type TEXT_URI_LIST */ + if (gtk_selection_data_get_target (selection_data) != GDK_NONE && + info == TEXT_URI_LIST) { + /* Build a list of dragged items */ + sidebar->drag_list = build_selection_list ((const gchar *) gtk_selection_data_get_data (selection_data)); + } else { + sidebar->drag_list = NULL; + } + sidebar->drag_data_received = TRUE; + sidebar->drag_data_info = info; + } + + /* Stop further emission of the "drag-data-received" signal */ + g_signal_stop_emission_by_name (widget, "drag-data-received"); + + /* If no drop has occurred yet, do nothing further */ + if (!sidebar->drop_occured) { + return; + } + + /* Compute the drop position in the tree view */ + success = compute_drop_position (tree_view, x, y, &tree_path, &tree_pos, sidebar); + if (!success) { + goto out; + } + + success = FALSE; + + /* Handle drops on specific tree rows */ + if (tree_pos == GTK_TREE_VIEW_DROP_BEFORE || + tree_pos == GTK_TREE_VIEW_DROP_AFTER) { + model = gtk_tree_view_get_model (tree_view); + if (!gtk_tree_model_get_iter (model, &iter, tree_path)) { + goto out; + } + + /* Extract information about the target row */ + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + PLACES_SIDEBAR_COLUMN_INDEX, &position, + -1); + + /* Adjust insertion index based on section type and drop position */ + if (section_type == SECTION_COMPUTER && tree_pos == GTK_TREE_VIEW_DROP_BEFORE) { + position = nemo_bookmark_list_length(sidebar->bookmarks); + } else if (section_type == SECTION_BOOKMARKS && position == -1) { + /* Check for (temporarily) visible Bookmarks heading, only drop-reactive when + * it has no children, so we can assume that (bookmark_breakpoint == bookmark_count) + */ + position = nemo_bookmark_list_length (sidebar->bookmarks); + } else if (tree_pos == GTK_TREE_VIEW_DROP_AFTER && + (section_type == SECTION_XDG_BOOKMARKS || section_type == SECTION_BOOKMARKS)) { + position++; + } + + /* Handle different types of drag data */ + switch (info) { + case TEXT_URI_LIST: + bookmarks_drop_uris (sidebar, selection_data, position, section_type); + success = TRUE; + break; + case GTK_TREE_MODEL_ROW: + reorder_bookmarks (sidebar, position, section_type); + success = TRUE; + break; + default: + g_assert_not_reached (); + break; + } + } else { + GdkDragAction real_action; + + /* file transfer requested */ + /* Handle drag data targeting folders or special areas (not rows) */ + real_action = gdk_drag_context_get_selected_action (context); + + if (real_action == GDK_ACTION_ASK) { + real_action = + nemo_drag_drop_action_ask (GTK_WIDGET (tree_view), + gdk_drag_context_get_actions (context)); + } + + if (real_action > 0) { + model = gtk_tree_view_get_model (tree_view); + + gtk_tree_model_get_iter (model, &iter, tree_path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_URI, &drop_uri, + -1); + + switch (info) { + case TEXT_URI_LIST: + /* Handle file/folder copy or "favorite" marking */ + selection_list = build_selection_list ((const gchar *) gtk_selection_data_get_data (selection_data)); + uris = uri_list_from_selection (selection_list); + + GList *l; + + if (g_strcmp0 (drop_uri, "favorites:///") == 0) { + /* Mark dragged items as favorites */ + for (l = uris; l != NULL; l = l->next) { + gchar *uri = (gchar *) l->data; + NemoFile *source_file = nemo_file_get_by_uri (uri); + nemo_file_set_is_favorite (source_file, TRUE); + nemo_file_unref (source_file); + } + } else { + /* Perform copy/move operations for other targets */ + nemo_file_operations_copy_move (uris, NULL, drop_uri, + real_action, GTK_WIDGET (tree_view), + NULL, NULL); + } + + nemo_drag_destroy_selection_list (selection_list); + g_list_free (uris); + success = TRUE; + break; + case GTK_TREE_MODEL_ROW: + success = FALSE; + break; + default: + g_assert_not_reached (); + break; + } + + g_free (drop_uri); + } + } + +out: + /* Cleanup after drag operation */ + sidebar->drop_occured = FALSE; + free_drag_data (sidebar); + gtk_drag_finish (context, success, FALSE, time); + gtk_tree_path_free (tree_path); + + /* Schedule a delayed hiding of bookmarks panel if needed */ + g_timeout_add (250, (GSourceFunc) idle_hide_bookmarks, sidebar); +} + +/* Callback triggered when a drop occurs on the sidebar */ +static gboolean +drag_drop_callback (GtkTreeView *tree_view, + GdkDragContext *context, + int x, + int y, + unsigned int time, + NemoPlacesTreeSidebar *sidebar) +{ + gboolean retval = FALSE; + sidebar->drop_occured = TRUE; + retval = get_drag_data (tree_view, context, time); + /* Stop further emission of the "drag-drop" signal */ + g_signal_stop_emission_by_name (tree_view, "drag-drop"); + return retval; +} + +static void +check_unmount_and_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_unmount, + gboolean *show_eject) +{ + *show_unmount = FALSE; + *show_eject = FALSE; + + if (drive != NULL) { + *show_eject = g_drive_can_eject (drive); + } + + if (volume != NULL) { + *show_eject |= g_volume_can_eject (volume); + } + if (mount != NULL) { + *show_eject |= g_mount_can_eject (mount); + *show_unmount = g_mount_can_unmount (mount) && !*show_eject; + } +} + +static void +check_visibility (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_mount, + gboolean *show_unmount, + gboolean *show_eject, + gboolean *show_rescan, + gboolean *show_start, + gboolean *show_stop) +{ + *show_mount = FALSE; + *show_rescan = FALSE; + *show_start = FALSE; + *show_stop = FALSE; + + check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject); + + if (drive != NULL) { + if (g_drive_is_removable (drive) && + !g_drive_is_media_check_automatic (drive) && + g_drive_can_poll_for_media (drive)) + *show_rescan = TRUE; + + *show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive); + *show_stop = g_drive_can_stop (drive); + + if (*show_stop) + *show_unmount = FALSE; + } + + if (volume != NULL) { + if (mount == NULL) + *show_mount = g_volume_can_mount (volume); + } +} + +static void +set_action_visible (GtkActionGroup *action_group, + const gchar *name, + gboolean visible) +{ + GtkAction *action; + + action = gtk_action_group_get_action (action_group, name); + gtk_action_set_visible (action, visible); +} + +// Updates the state (enabled/disabled/visible) of menu items in the sidebar +static void +update_menu_states (NemoPlacesTreeSidebar *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; + } + + // Determine the currently selected item in the sidebar + 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); + } + + if (uri) { + NemoFile *file = nemo_file_get_by_uri (uri); + NemoFile *parent = nemo_file_get_parent (file); + + GList *selection = g_list_prepend (NULL, file); + + // Update action manager to reflect available actions for this selection + 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); + } + + // Show/hide bookmark-related actions + 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 ()); + + // Determine mount/eject/start/stop visibility for drives + check_visibility (mount, volume, drive, + &show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop); + + /* 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); + + /* 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); + } + + // Set visibility for volume actions + 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; + + start_action = gtk_action_group_get_action (sidebar->bookmark_action_group, NEMO_ACTION_START_VOLUME); + stop_action = gtk_action_group_get_action (sidebar->bookmark_action_group, NEMO_ACTION_STOP_VOLUME); + + gtk_action_set_label (start_action, _("_Start")); + gtk_action_set_label (stop_action, _("_Stop")); + if ((show_start || show_stop) && drive != NULL) { + switch (g_drive_get_start_stop_type (drive)) { + case G_DRIVE_START_STOP_TYPE_SHUTDOWN: + /* start() for type G_DRIVE_START_STOP_TYPE_SHUTDOWN is normally not used */ + gtk_action_set_label (start_action, _("_Power On")); + gtk_action_set_label (stop_action, _("_Safely Remove Drive")); + break; + case G_DRIVE_START_STOP_TYPE_NETWORK: + gtk_action_set_label (start_action, _("_Connect Drive")); + gtk_action_set_label (stop_action, _("_Disconnect Drive")); + break; + case G_DRIVE_START_STOP_TYPE_MULTIDISK: + gtk_action_set_label (start_action, _("_Start Multi-disk Device")); + gtk_action_set_label (stop_action, _("_Stop Multi-disk Device")); + break; + case G_DRIVE_START_STOP_TYPE_PASSWORD: + /* stop() for type G_DRIVE_START_STOP_TYPE_PASSWORD is normally not used */ + gtk_action_set_label (start_action, _("_Unlock Drive")); + gtk_action_set_label (stop_action, _("_Lock Drive")); + break; + + default: + case G_DRIVE_START_STOP_TYPE_UNKNOWN: + /* uses defaults set above */ + break; + } + } + + g_clear_object (&drive); + g_clear_object (&volume); + g_clear_object (&mount); +} + +/* Callback used when the selection in the shortcuts tree changes */ +static void +bookmarks_selection_changed_cb (GtkTreeSelection *selection, + NemoPlacesTreeSidebar *sidebar) +{ + update_menu_states (sidebar); +} + +static void +volume_mounted_cb (GVolume *volume, + gboolean success, + GObject *user_data) +{ + GMount *mount; + NemoPlacesTreeSidebar *sidebar; + GFile *location; + + sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + sidebar->mounting = FALSE; + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + location = g_mount_get_default_location (mount); + + if (sidebar->go_to_after_mount_slot != NULL) { + if ((sidebar->go_to_after_mount_flags & NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) { + nemo_window_slot_open_location (sidebar->go_to_after_mount_slot, location, + sidebar->go_to_after_mount_flags); + } else { + NemoWindow *new, *cur; + + cur = NEMO_WINDOW (sidebar->window); + new = nemo_application_create_window (nemo_application_get_singleton (), + gtk_window_get_screen (GTK_WINDOW (cur))); + nemo_window_go_to (new, location); + } + } + + g_object_unref (G_OBJECT (location)); + g_object_unref (G_OBJECT (mount)); + } + + 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; + } +} + +static void +drive_start_from_bookmark_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + char *primary; + char *name; + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to start %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +open_selected_bookmark (NemoPlacesTreeSidebar *sidebar, + GtkTreeModel *model, + GtkTreeIter *iter, + NemoWindowOpenFlags flags) +{ + NemoWindowSlot *slot; + GFile *location; + char *uri; + + if (!iter) { + return; + } + + gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + + if (uri != NULL) { + DEBUG ("Activating bookmark %s", uri); + + location = g_file_new_for_uri (uri); + /* Navigate to the clicked location */ + if ((flags & NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) { + slot = nemo_window_get_active_slot (sidebar->window); + nemo_window_slot_open_location (slot, location, flags); + } else { + NemoWindow *cur, *new; + + cur = NEMO_WINDOW (sidebar->window); + new = nemo_application_create_window (nemo_application_get_singleton (), + gtk_window_get_screen (GTK_WINDOW (cur))); + nemo_window_go_to (new, location); + } + g_object_unref (location); + g_free (uri); + + } else { + GDrive *drive; + GVolume *volume; + NemoWindowSlot *slt; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + + if (volume != NULL && !sidebar->mounting) { + sidebar->mounting = TRUE; + + g_assert (sidebar->go_to_after_mount_slot == NULL); + + slt = nemo_window_get_active_slot (sidebar->window); + sidebar->go_to_after_mount_slot = slt; + g_object_add_weak_pointer (G_OBJECT (sidebar->go_to_after_mount_slot), + (gpointer *) &sidebar->go_to_after_mount_slot); + + sidebar->go_to_after_mount_flags = flags | NEMO_WINDOW_OPEN_FLAG_MOUNT; + + nemo_file_operations_mount_volume_full (NULL, volume, + volume_mounted_cb, + G_OBJECT (sidebar)); + } else if (volume == NULL && drive != NULL && + (g_drive_can_start (drive) || g_drive_can_start_degraded (drive))) { + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_from_bookmark_cb, NULL); + g_object_unref (mount_op); + } + + if (drive != NULL) + g_object_unref (drive); + if (volume != NULL) + g_object_unref (volume); + } +} + +static void +open_shortcut_from_menu (NemoPlacesTreeSidebar *sidebar, + NemoWindowOpenFlags flags) +{ + GtkTreeModel *model; + GtkTreeIter iter; + GtkTreePath *path = NULL; + + model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); + + if (path != NULL && gtk_tree_model_get_iter (model, &iter, path)) { + open_selected_bookmark (sidebar, model, &iter, flags); + } + + gtk_tree_path_free (path); +} + +static void +open_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, 0); +} + +static void +open_shortcut_in_new_window_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW); +} + +static void +open_shortcut_in_new_tab_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + open_shortcut_from_menu (sidebar, NEMO_WINDOW_OPEN_FLAG_NEW_TAB); +} + +/* Add bookmark for the selected item */ +static void +add_bookmark (NemoPlacesTreeSidebar *sidebar) +{ + 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); + + if (uri == NULL) { + return; + } + + location = g_file_new_for_uri (uri); + bookmark = nemo_bookmark_new (location, NULL, NULL, NULL); + + if (!nemo_bookmark_list_contains (sidebar->bookmarks, bookmark)) { + nemo_bookmark_list_append (sidebar->bookmarks, bookmark); + } + + g_object_unref (location); + g_object_unref (bookmark); + g_free (uri); + } +} + +static void +add_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + add_bookmark (sidebar); +} + +/* Rename the selected bookmark */ +static void +rename_selected_bookmark (NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GtkTreePath *path; + GtkTreeViewColumn *column; + PlaceType type; + + if (get_selected_iter (sidebar, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type != PLACES_BOOKMARK) { + 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); + 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); + gtk_tree_path_free (path); + } +} + +static void +rename_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + rename_selected_bookmark (sidebar); +} + +/* Removes the selected bookmarks */ +static void +remove_selected_bookmarks (NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + PlaceType type; + int index; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + + if (type != PLACES_BOOKMARK) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_INDEX, &index, + -1); + + if (index < sidebar->bookmark_breakpoint) + decrement_bookmark_breakpoint (sidebar); + + nemo_bookmark_list_delete_item_at (sidebar->bookmarks, index); +} + +static void +remove_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + remove_selected_bookmarks (sidebar); +} + +static void +mount_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GVolume *volume; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + + if (volume != NULL) { + nemo_file_operations_mount_volume (NULL, volume); + g_object_unref (volume); + } +} + +static void +unmount_done (gpointer data) +{ + NemoWindow *window; + + window = data; + g_object_unref (window); +} + +static void +show_unmount_progress_cb (GMountOperation *op, + const gchar *message, + gint64 time_left, + gint64 bytes_left, + gpointer user_data) +{ + NemoApplication *app = NEMO_APPLICATION (g_application_get_default ()); + + if (bytes_left == 0) { + nemo_application_notify_unmount_done (app, message); + } else { + nemo_application_notify_unmount_show (app, message); + } +} + +static void +show_unmount_progress_aborted_cb (GMountOperation *op, + gpointer user_data) +{ + NemoApplication *app = NEMO_APPLICATION (g_application_get_default ()); + nemo_application_notify_unmount_done (app, NULL); +} + +static GMountOperation * +get_unmount_operation (NemoPlacesTreeSidebar *sidebar) +{ + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + g_signal_connect (mount_op, "show-unmount-progress", + G_CALLBACK (show_unmount_progress_cb), sidebar); + g_signal_connect (mount_op, "aborted", + G_CALLBACK (show_unmount_progress_aborted_cb), sidebar); + + return mount_op; +} + +static void +do_unmount (GMount *mount, + NemoPlacesTreeSidebar *sidebar) +{ + GMountOperation *mount_op; + + if (mount != NULL) { + mount_op = get_unmount_operation (sidebar); + nemo_file_operations_unmount_mount_full (NULL, mount, mount_op, FALSE, TRUE, + unmount_done, + g_object_ref (sidebar->window)); + g_object_unref (mount_op); + } +} + +static void +do_unmount_selection (NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GMount *mount; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + -1); + + if (mount != NULL) { + do_unmount (mount, sidebar); + g_object_unref (mount); + } +} + +static void +unmount_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + do_unmount_selection (sidebar); +} + +static void +handle_mount_unmount_failure (const gchar *primary, + GError *error) +{ + const gchar *message = NULL; + + if (error && error->code == G_IO_ERROR_FAILED_HANDLED) { + return; + } + + if (error) { + message = error->message; + } + + eel_show_error_dialog (primary, + message, + NULL); +} + +static void +drive_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NemoWindow *window; + GError *error; + + window = user_data; + g_object_unref (window); + + error = NULL; + if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error)) { + char *name, *primary; + + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + + handle_mount_unmount_failure (primary, error); + + g_free (name); + g_free (primary); + g_clear_error (&error); + } +} + +static void +volume_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NemoWindow *window; + GError *error; + + window = user_data; + g_object_unref (window); + + error = NULL; + if (!g_volume_eject_with_operation_finish (G_VOLUME (source_object), res, &error)) { + char *name, *primary; + + name = g_volume_get_name (G_VOLUME (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + + handle_mount_unmount_failure (primary, error); + + g_free (name); + g_free (primary); + g_clear_error (&error); + } +} + +static void +mount_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NemoWindow *window; + GError *error; + + window = user_data; + g_object_unref (window); + + error = NULL; + if (!g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error)) { + char *name, *primary; + + name = g_mount_get_name (G_MOUNT (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + + handle_mount_unmount_failure (primary, error); + + g_free (name); + g_free (primary); + g_clear_error (&error); + } +} + +static void +do_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + NemoPlacesTreeSidebar *sidebar) +{ + GMountOperation *mount_op = get_unmount_operation (sidebar); + + if (mount != NULL) { + g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb, + g_object_ref (sidebar->window)); + } else if (volume != NULL) { + g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb, + g_object_ref (sidebar->window)); + } else if (drive != NULL) { + g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb, + g_object_ref (sidebar->window)); + } + g_object_unref (mount_op); +} + +static void +eject_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GMount *mount; + GVolume *volume; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + do_eject (mount, volume, drive, sidebar); + + g_clear_object (&mount); + g_clear_object (&volume); + g_clear_object (&drive); +} + +static gboolean +eject_or_unmount_bookmark (NemoPlacesTreeSidebar *sidebar, + GtkTreePath *path) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gboolean can_unmount, can_eject; + GMount *mount; + GVolume *volume; + GDrive *drive; + gboolean ret; + + model = GTK_TREE_MODEL (sidebar->store_filter); + + if (!path) { + return FALSE; + } + if (!gtk_tree_model_get_iter (model, &iter, path)) { + return FALSE; + } + + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + ret = FALSE; + + check_unmount_and_eject (mount, volume, drive, &can_unmount, &can_eject); + /* if we can eject, it has priority over unmount */ + if (can_eject) { + do_eject (mount, volume, drive, sidebar); + ret = TRUE; + } else if (can_unmount) { + do_unmount (mount, sidebar); + ret = TRUE; + } + + g_clear_object (&mount); + g_clear_object (&volume); + g_clear_object (&drive); + + return ret; +} + +static gboolean +eject_or_unmount_selection (NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GtkTreePath *path; + gboolean ret; + + if (!get_selected_iter (sidebar, &iter)) { + return FALSE; + } + + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iter); + if (path == NULL) { + return FALSE; + } + + ret = eject_or_unmount_bookmark (sidebar, path); + + gtk_tree_path_free (path); + + return ret; +} + +static void +drive_poll_for_media_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { + char *name, *primary; + + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to poll %s for media changes"), name); + + handle_mount_unmount_failure (primary, error); + + g_free (name); + g_free (primary); + g_clear_error (&error); + } +} + +static void +rescan_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) { + g_drive_poll_for_media (drive, NULL, drive_poll_for_media_cb, NULL); + g_object_unref (drive); + } +} + +static void +drive_start_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + + error = NULL; + + if (!g_drive_start_finish (G_DRIVE (source_object), res, &error)) { + char *name, *primary; + + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to start %s"), name); + + handle_mount_unmount_failure (primary, error); + + g_free (name); + g_free (primary); + g_clear_error (&error); + } +} + +static void +start_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) { + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); + + g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_cb, NULL); + + g_object_unref (mount_op); + g_object_unref (drive); + } +} + +static void +drive_stop_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + NemoWindow *window; + GError *error; + + window = user_data; + g_object_unref (window); + + error = NULL; + + if (!g_drive_stop_finish (G_DRIVE (source_object), res, &error)) { + char *name, *primary; + + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to stop %s"), name); + + handle_mount_unmount_failure (primary, error); + + g_free (name); + g_free (primary); + g_clear_error (&error); + } +} + +static void +stop_shortcut_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeIter iter; + GDrive *drive; + + if (!get_selected_iter (sidebar, &iter)) { + return; + } + + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + -1); + + if (drive != NULL) { + GMountOperation *mount_op = get_unmount_operation (sidebar); + g_drive_stop (drive, G_MOUNT_UNMOUNT_NONE, mount_op, NULL, drive_stop_cb, + g_object_ref (sidebar->window)); + g_object_unref (mount_op); + g_object_unref (drive); + } +} + +static void +empty_trash_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + nemo_file_operations_empty_trash (GTK_WIDGET (sidebar->window)); +} + +static gboolean +find_prev_or_next_row (NemoPlacesTreeSidebar *sidebar, + GtkTreeIter *iter, + gboolean go_up) +{ + GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); + gboolean res; + int place_type; + + if (go_up) { + res = gtk_tree_model_iter_previous (model, iter); + } else { + res = gtk_tree_model_iter_next (model, iter); + } + + if (res) { + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + -1); + if (place_type == PLACES_HEADING) { + if (go_up) { + res = gtk_tree_model_iter_previous (model, iter); + } else { + res = gtk_tree_model_iter_next (model, iter); + } + } + } + + return res; +} + +static gboolean +find_prev_row (NemoPlacesTreeSidebar *sidebar, GtkTreeIter *iter) +{ + return find_prev_or_next_row (sidebar, iter, TRUE); +} + +static gboolean +find_next_row (NemoPlacesTreeSidebar *sidebar, GtkTreeIter *iter) +{ + return find_prev_or_next_row (sidebar, iter, FALSE); +} + +static void +properties_cb (GtkAction *item, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeModel *model; + GtkTreePath *path = NULL; + GtkTreeIter iter; + GList *list; + NemoFile *file; + char *uri; + + model = gtk_tree_view_get_model (sidebar->tree_view); + gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); + + if (path == NULL || !gtk_tree_model_get_iter (model, &iter, path)) { + gtk_tree_path_free (path); + return; + } + + gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); + + if (uri != NULL) { + + file = nemo_file_get_by_uri (uri); + list = g_list_prepend (NULL, nemo_file_ref (file)); + + nemo_properties_window_present (list, GTK_WIDGET (sidebar), NULL); + + nemo_file_list_free (list); + g_free (uri); + } + + gtk_tree_path_free (path); +} + +static gboolean +nemo_places_tree_sidebar_focus (GtkWidget *widget, + GtkDirectionType direction) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (widget); + GtkTreePath *path; + GtkTreeIter iter, child_iter; + gboolean res; + + 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); + res = find_next_row (sidebar, &child_iter); + if (res) { + path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iter); + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + gtk_tree_path_free (path); + } + } + + return GTK_WIDGET_CLASS (nemo_places_tree_sidebar_parent_class)->focus (widget, direction); +} + +/* Handler for GtkWidget::key-press-event on the shortcuts list */ +static gboolean +bookmarks_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + NemoPlacesTreeSidebar *sidebar) +{ + 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; + } + } + + 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; + } + + return FALSE; +} + +static void +run_action_callback (GtkAction *action, gpointer user_data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + gchar *uri = NULL; + GtkTreeIter iter; + + if (get_selected_iter (sidebar, &iter)) { + gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + } + + if (!uri) { + return; + } + + NemoFile *file = nemo_file_get_by_uri (uri); + NemoFile *parent = nemo_file_get_parent (file); + GList *selection = g_list_prepend (NULL, file); + + nemo_action_activate (NEMO_ACTION (action), selection, parent, GTK_WINDOW (sidebar->window)); + + nemo_file_list_free (selection); + nemo_file_unref (parent); + + g_free (uri); +} + +#if GTK_CHECK_VERSION (3, 24, 8) +static void +moved_to_rect_cb (GdkWindow *window, + const GdkRectangle *flipped_rect, + const GdkRectangle *final_rect, + gboolean flipped_x, + gboolean flipped_y, + GtkMenu *menu) +{ + g_signal_emit_by_name (menu, + "popped-up", + 0, + flipped_rect, + final_rect, + flipped_x, + flipped_y); + + // Don't let the emission run in gtkmenu.c + g_signal_stop_emission_by_name (window, "moved-to-rect"); +} + +static void +popup_menu_realized (GtkWidget *menu, + gpointer user_data) +{ + GdkWindow *toplevel; + + toplevel = gtk_widget_get_window (gtk_widget_get_toplevel (menu)); + + g_signal_handlers_disconnect_by_func (toplevel, moved_to_rect_cb, menu); + + g_signal_connect (toplevel, "moved-to-rect", G_CALLBACK (moved_to_rect_cb), + menu); +} +#endif + +static void +bookmarks_popup_menu (NemoPlacesTreeSidebar *sidebar, + GdkEventButton *event) +{ + update_menu_states (sidebar); + eel_pop_up_context_menu (GTK_MENU(sidebar->popup_menu), + (GdkEvent *) event, + GTK_WIDGET (sidebar)); +} + +/* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */ +static gboolean +bookmarks_popup_menu_cb (GtkWidget *widget, + NemoPlacesTreeSidebar *sidebar) +{ + bookmarks_popup_menu (sidebar, NULL); + return TRUE; +} + +static void +reset_menu (NemoPlacesTreeSidebar *sidebar) +{ + sidebar->actions_need_update = TRUE; + rebuild_menu (sidebar); +} + +static gboolean +actions_changed_idle_cb (gpointer user_data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + reset_menu (sidebar); + + sidebar->actions_changed_idle_id = 0; + return G_SOURCE_REMOVE; +} + +static void +actions_changed (gpointer user_data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + g_clear_handle_id (&sidebar->actions_changed_idle_id, g_source_remove); + sidebar->actions_changed_idle_id = g_idle_add (actions_changed_idle_cb, sidebar); +} + +static void +add_action_to_ui (NemoActionManager *manager, + GtkAction *action, + GtkUIManagerItemType type, + const gchar *path, + const gchar *accelerator, + gpointer user_data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + static const gchar *roots[] = { + "/selection/PlacesSidebarActionsPlaceholder", + NULL, + }; + + nemo_action_manager_add_action_ui (manager, + sidebar->ui_manager, + action, + path, + accelerator, + sidebar->action_action_group, + sidebar->action_action_group_merge_id, + roots, + type, + G_CALLBACK (run_action_callback), + sidebar); +} + +static void +clear_ui (NemoPlacesTreeSidebar *sidebar) +{ + + nemo_ui_unmerge_ui (sidebar->ui_manager, + &sidebar->bookmark_action_group_merge_id, + &sidebar->bookmark_action_group); + + nemo_ui_unmerge_ui (sidebar->ui_manager, + &sidebar->action_action_group_merge_id, + &sidebar->action_action_group); + +} + +static const GtkActionEntry bookmark_action_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_ADD_BOOKMARK, NULL, N_("_Add Bookmark"), NULL, NULL, G_CALLBACK (add_shortcut_cb) }, + { NEMO_ACTION_SIDEBAR_REMOVE, "list-remove-symbolic", N_("Remove"), NULL, NULL, G_CALLBACK (remove_shortcut_cb) }, + { NEMO_ACTION_RENAME, NULL, N_("_Rename..."), NULL, NULL, G_CALLBACK (rename_shortcut_cb) }, + { NEMO_ACTION_MOUNT_VOLUME, NULL, N_("_Mount"), NULL, NULL, G_CALLBACK (mount_shortcut_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_SIDEBAR_DETECT_MEDIA, NULL, N_("_Detect Media"), NULL, NULL, G_CALLBACK (rescan_shortcut_cb) }, + { NEMO_ACTION_START_VOLUME, NULL, N_("_Start"), NULL, NULL, G_CALLBACK (start_shortcut_cb) }, + { NEMO_ACTION_STOP_VOLUME, NULL, N_("_Stop"), NULL, NULL, G_CALLBACK (stop_shortcut_cb) }, + { NEMO_ACTION_EMPTY_TRASH_CONDITIONAL, NULL, N_("_Empty _Trash"), NULL, NULL, G_CALLBACK (empty_trash_cb) }, + { NEMO_ACTION_PROPERTIES, NULL, N_("_Properties"), NULL, NULL, G_CALLBACK (properties_cb) }, +}; + +static void +rebuild_menu (NemoPlacesTreeSidebar *sidebar) +{ + if (!gtk_widget_get_realized (GTK_WIDGET (sidebar))) { + return; + } + + if (!sidebar->actions_need_update) { + return; + } + + clear_ui (sidebar); + + nemo_ui_prepare_merge_ui (sidebar->ui_manager, + "NemoPlacesTreeSidebarBookmarkActions", + &sidebar->bookmark_action_group_merge_id, + &sidebar->bookmark_action_group); + + nemo_ui_prepare_merge_ui (sidebar->ui_manager, + "NemoPlacesTreeSidebarActionActions", + &sidebar->action_action_group_merge_id, + &sidebar->action_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); + + gtk_action_group_add_actions (sidebar->bookmark_action_group, + bookmark_action_entries, + G_N_ELEMENTS (bookmark_action_entries), + sidebar); + + nemo_action_manager_iterate_actions (sidebar->action_manager, + (NemoActionManagerIterFunc) add_action_to_ui, + sidebar); + + if (sidebar->popup_menu == NULL) { + GtkWidget *menu = gtk_ui_manager_get_widget (sidebar->ui_manager, "/selection"); + gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (GTK_WIDGET (sidebar->window))); + sidebar->popup_menu = menu; + +#if GTK_CHECK_VERSION (3, 24, 8) + g_signal_connect (sidebar->popup_menu, "realize", + G_CALLBACK (popup_menu_realized), + sidebar); + gtk_widget_realize (sidebar->popup_menu); +#endif + + gtk_widget_show (menu); + } + + sidebar->actions_need_update = FALSE; +} + +static gboolean +bookmarks_button_release_event_cb (GtkWidget *widget, + GdkEventButton *event, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeView *tree_view; + gboolean res; + + path = NULL; + + if (event->type != GDK_BUTTON_RELEASE) { + return TRUE; + } + + if (clicked_eject_button (sidebar, &path)) { + eject_or_unmount_bookmark (sidebar, path); + gtk_tree_path_free (path); + + return FALSE; + } + + tree_view = GTK_TREE_VIEW (widget); + model = gtk_tree_view_get_model (tree_view); + + if (event->button == 1) { + + if (event->window != gtk_tree_view_get_bin_window (tree_view)) { + return FALSE; + } + + res = gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, + &path, NULL, NULL, NULL); + + if (!res) { + return FALSE; + } + + gtk_tree_model_get_iter (model, &iter, path); + + open_selected_bookmark (sidebar, model, &iter, 0); + + gtk_tree_path_free (path); + } + + return FALSE; +} + +static gboolean +bookmarks_button_press_event_cb (GtkWidget *widget, + GdkEventButton *event, + NemoPlacesTreeSidebar *sidebar) + +{ + GtkTreeModel *model; + GtkTreeView *tree_view; + GtkTreeIter iter; + GtkTreePath *path = NULL; + gboolean retval = FALSE; + PlaceType row_type; + + if (event->type != GDK_BUTTON_PRESS) { + /* ignore multiple clicks */ + return TRUE; + } + + tree_view = GTK_TREE_VIEW (widget); + model = gtk_tree_view_get_model (tree_view); + gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, + &path, NULL, NULL, NULL); + + if (path == NULL || !gtk_tree_model_get_iter (model, &iter, path)) { + return FALSE; + } + + if (event->button == 3) { + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, + -1); + + if (row_type != PLACES_HEADING) { + bookmarks_popup_menu (sidebar, event); + } + } else if (event->button == 2) { + NemoWindowOpenFlags flags = 0; + + if (g_settings_get_boolean (nemo_preferences, + NEMO_PREFERENCES_ALWAYS_USE_BROWSER)) { + flags = (event->state & GDK_CONTROL_MASK) ? + NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW : + NEMO_WINDOW_OPEN_FLAG_NEW_TAB; + } else { + flags = NEMO_WINDOW_OPEN_FLAG_CLOSE_BEHIND; + } + + open_selected_bookmark (sidebar, model, &iter, flags); + retval = TRUE; + } + + gtk_tree_path_free (path); + + return retval; +} + +typedef struct { + NemoPlacesTreeSidebar *sidebar; + GtkTreePath *hovered_path; +} ClearHoverData; + +static gboolean +clear_eject_hover (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + ClearHoverData *hdata = data; + gint size; + gboolean can_eject = FALSE; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_EJECT, &can_eject, + -1); + + if (can_eject && hdata->hovered_path != NULL && gtk_tree_path_compare (path, hdata->hovered_path) == 0) { + size = EJECT_ICON_SIZE_HOVERED; + + } else { + size = EJECT_ICON_SIZE_NOT_HOVERED; + } + + gtk_tree_store_set (hdata->sidebar->store, iter, + PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, size, + -1); + + return FALSE; +} + +static gboolean +motion_notify_cb (GtkWidget *widget, + GdkEventMotion *event, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeModel *model; + GtkTreePath *path = NULL; + GtkTreePath *store_path = NULL; + gboolean editing; + + if (event->type != GDK_MOTION_NOTIFY) { + return TRUE; + } + + g_object_get (sidebar->editable_renderer, "editing", &editing, NULL); + if (editing) { + return GDK_EVENT_PROPAGATE; + } + + 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); + } + + ClearHoverData data = { sidebar, store_path }; + gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store), (GtkTreeModelForeachFunc) clear_eject_hover, &data); + + if (store_path != NULL) { + gtk_tree_path_free (store_path); + } + + gtk_tree_path_free (path); + + return FALSE; +} + +static gboolean +leave_notify_cb (GtkWidget *widget, + GdkEventCrossing *event, + NemoPlacesTreeSidebar *sidebar) +{ + gboolean editing; + + if (event->type != GDK_LEAVE_NOTIFY) { + return TRUE; + } + + g_object_get (sidebar->editable_renderer, "editing", &editing, NULL); + if (editing) { + return GDK_EVENT_PROPAGATE; + } + + ClearHoverData data = { sidebar, NULL }; + gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store), (GtkTreeModelForeachFunc) clear_eject_hover, &data); + + return FALSE; +} + + +static gboolean +query_tooltip_callback (GtkWidget *widget, + gint x, + gint y, + gboolean kb_mode, + GtkTooltip *tooltip, + gpointer user_data) +{ + GtkTreeIter iter; + GtkTreePath *path = NULL; + GtkTreeModel *model; + + if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y, + kb_mode, + &model, &path, &iter)) { + gboolean can_eject; + gint icon_size, type; + + gtk_tree_model_get (model, + &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_EJECT, &can_eject, + // HACK: If we store a bool 'hovered' we still need to have an + // icon size column or else make a cell_data_func to render the icon + // manually. This is simpler. + PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, &icon_size, + -1); + + if (type == PLACES_HEADING) { + gtk_tree_path_free (path); + return FALSE; + } + + g_autofree gchar *tooltip_markup = NULL; + + if (can_eject && icon_size == EJECT_ICON_SIZE_HOVERED) { + g_autoptr(GMount) mount = NULL; + g_autoptr(GDrive) drive = NULL; + g_autoptr(GVolume) volume = NULL; + + gtk_tree_model_get (model, + &iter, + PLACES_SIDEBAR_COLUMN_MOUNT, &mount, + PLACES_SIDEBAR_COLUMN_DRIVE, &drive, + PLACES_SIDEBAR_COLUMN_VOLUME, &volume, + -1); + if (mount != NULL) { + tooltip_markup = g_strdup (_("Unmount")); + } + else + if (drive != NULL) { + tooltip_markup = g_strdup (_("Eject")); + } + else + if (volume != NULL) { + tooltip_markup = g_strdup (_("Stop")); + } + } else { + if (type == PLACES_TREE_FOLDER) { + return FALSE; + } + gtk_tree_model_get (model, + &iter, + PLACES_SIDEBAR_COLUMN_TOOLTIP, &tooltip_markup, + -1); + } + + gtk_tooltip_set_markup (tooltip, tooltip_markup); + gtk_tree_view_set_tooltip_cell (GTK_TREE_VIEW (widget), tooltip, path, NULL, NULL); + + gtk_tree_path_free (path); + return TRUE; + } + + return FALSE; +} + + +static void +update_expanded_state (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data, + gboolean expanded) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + + if (sidebar->updating_sidebar) + return; + + SectionType type; + 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, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &type, + -1); + + 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: + g_print("Unbekannter SectionType: %d\n", type); + return; + } +} + +static void +row_collapsed_cb (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data) +{ + update_expanded_state (tree_view, + iter, + path, + user_data, + FALSE); +} + + +static void +row_expanded_cb (GtkTreeView *tree_view, + GtkTreeIter *iter, + GtkTreePath *path, + gpointer user_data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR(user_data); + + GtkTreeModel *filter_model = gtk_tree_view_get_model(tree_view); // Filter + char *uri = NULL; + + gtk_tree_model_get(filter_model, iter, + PLACES_SIDEBAR_COLUMN_URI, &uri, + -1); + GtkTreeIter store_iter; + gtk_tree_model_filter_convert_iter_to_child_iter( + GTK_TREE_MODEL_FILTER(filter_model), + &store_iter, + iter + ); + // Store-Iter ist jetzt gültig für sidebar->store + GtkTreePath *copy_path = gtk_tree_model_get_path(filter_model, iter); + + if (uri) { + add_directory_children(sidebar, &store_iter, uri); + g_free(uri); + } + + gtk_tree_view_expand_row(tree_view, path, FALSE); + + GtkTreeIter fresh_iter; + if (gtk_tree_model_get_iter(filter_model, &fresh_iter, copy_path)) { + update_expanded_state(tree_view, &fresh_iter, copy_path, user_data, TRUE); + } + gtk_tree_path_free(copy_path); +} + +static void +row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + GtkTreeIter iter; + SectionType section_type; + PlaceType place_type; + + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); + GtkTreeModel *model = gtk_tree_view_get_model (tree_view); + gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, + -1); + if (place_type == PLACES_HEADING) { + if (section_type == SECTION_COMPUTER) { + sidebar->my_computer_expanded = !sidebar->my_computer_expanded; + } else if (section_type == SECTION_BOOKMARKS) { + sidebar->bookmarks_expanded = !sidebar->bookmarks_expanded; + } else if (section_type == SECTION_DEVICES) { + sidebar->devices_expanded = !sidebar->devices_expanded; + } else if (section_type == SECTION_NETWORK) { + sidebar->network_expanded = !sidebar->network_expanded; + } + restore_expand_state (sidebar); + } +} + +static void +bookmarks_edited (GtkCellRenderer *cell, + gchar *path_string, + gchar *new_text, + NemoPlacesTreeSidebar *sidebar) +{ + GtkTreePath *path; + GtkTreeIter iter; + NemoBookmark *bookmark; + int index; + + 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, + PLACES_SIDEBAR_COLUMN_INDEX, &index, + -1); + gtk_tree_path_free (path); + bookmark = nemo_bookmark_list_item_at (sidebar->bookmarks, index); + + if (bookmark != NULL) { + nemo_bookmark_set_custom_name (bookmark, new_text); + } +} + +static void +bookmarks_editing_canceled (GtkCellRenderer *cell, + NemoPlacesTreeSidebar *sidebar) +{ + g_object_set (cell, "editable", FALSE, NULL); +} + +static void +trash_state_changed_cb (NemoTrashMonitor *trash_monitor, + gboolean state, + gpointer data) +{ + NemoPlacesTreeSidebar *sidebar; + + sidebar = NEMO_PLACES_TREE_SIDEBAR (data); + + /* The trash icon changed, update the sidebar */ + update_places (sidebar); + + reset_menu (sidebar); +} + +static void +favorites_changed_cb (gpointer data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (data); + + update_places (sidebar); +} + +static gboolean +tree_selection_func (GtkTreeSelection *selection, + GtkTreeModel *model, + GtkTreePath *path, + gboolean path_currently_selected, + gpointer user_data) +{ + GtkTreeIter iter; + PlaceType row_type; + + gtk_tree_model_get_iter (model, &iter, path); + gtk_tree_model_get (model, &iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, + -1); + + if (row_type == PLACES_HEADING) { + return FALSE; + } + + return TRUE; +} + +static void +icon_cell_renderer_func (GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + PlaceType type; + GIcon *gicon = NULL; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + PLACES_SIDEBAR_COLUMN_GICON, &gicon, + -1); + + if (type == PLACES_HEADING) { + g_object_set (cell, + "visible", FALSE, + NULL); + } else { + g_object_set (cell, + "visible", TRUE, + "gicon", gicon, + NULL); + } + + if (gicon) + g_object_unref(gicon); +} + +static void +padding_cell_renderer_func(GtkTreeViewColumn *column, + GtkCellRenderer *cell, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkTreePath *path = gtk_tree_model_get_path(model, iter); + int depth = gtk_tree_path_get_depth(path); /* Root = 1 */ + gtk_tree_path_free(path); + + /* Minimaler Abstand zwischen Expander und Icon */ + 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 *tree_text = NULL; + gchar *name_text = NULL; + gchar *heading_text = NULL; + gint row_type = 0; + + /* Lese beide Text-Spalten + Row-Type */ + gtk_tree_model_get(model, iter, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, + PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_text, + PLACES_SIDEBAR_COLUMN_NAME, &name_text, + -1); + + if (row_type == PLACES_HEADING) { + /* Für Headings benutze HEADING_TEXT (bold) */ + g_free(tree_text); + 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 { + /* Normale Zeilen: bevorzugt TREE_NAME, fallback auf NAME */ + const gchar *display = ""; + if (tree_text != NULL && *tree_text != '\0') + display = tree_text; + else if (name_text != NULL && *name_text != '\0') + display = name_text; + + /* Standardgewicht */ + g_object_set(renderer, + "weight-set", FALSE, + NULL); + + g_object_set(renderer, "text", display, NULL); + + g_free(tree_text); + g_free(name_text); + } +} + + +static void +nemo_places_tree_sidebar_renderer_init(NemoPlacesTreeSidebar *sidebar, GtkTreeView *tree_view) +{ + GtkCellRenderer *cell; + GtkTreeViewColumn *primary_column; + + /* === Primäre Spalte === */ + 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); + + /* Entferne 'text' hier — wir setzen Text ausschließlich 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); + + /* Setze cell data func, die jetzt TREE_NAME und NAME berücksichtigt */ + 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); + + /* === Expander Column entfällt, GTK rendert automatisch in der ersten Spalte === */ +} + +static void +hidden_files_mode_changed_callback (NemoWindow *window, + NemoPlacesTreeSidebar *sidebar) +{ + gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); +} + + +static gboolean +row_visibility_function (GtkTreeModel *model, + GtkTreeIter *iter, + gpointer data) +{ + NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR(data); + + SectionType section_type; + PlaceType type; + + gtk_tree_model_get (model, iter, + PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, + -1); + if (type == PLACES_TREE_FOLDER) { + NemoWindowShowHiddenFilesMode mode = nemo_window_get_hidden_files_mode (sidebar->window); + // --- 2. Versteckte Verzeichnisse ausblenden --- + if ((mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE)) return TRUE; + gchar *name = NULL; + gboolean visible = TRUE; + gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_TREE_NAME, &name, -1); + if (name && name[0] == '.') // beginnt mit Punkt → versteckt + visible = FALSE; + g_free(name); + return visible; + } + + // --- 1. Heading-Filter --- + if (type != PLACES_HEADING || section_type != SECTION_BOOKMARKS) + return TRUE; + + if (sidebar->in_drag) + return TRUE; + + if ((int)nemo_bookmark_list_length (sidebar->bookmarks) > sidebar->bookmark_breakpoint) + return TRUE; + + return FALSE; +} + + +static void +nemo_places_tree_sidebar_init (NemoPlacesTreeSidebar *sidebar) +{ + GtkTreeView *tree_view; + GtkTreeSelection *selection; + GtkStyleContext *style_context; + + sidebar->action_manager = nemo_action_manager_new (); + sidebar->actions_changed_id = g_signal_connect_swapped (sidebar->action_manager, + "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; + + sidebar->volume_monitor = g_volume_monitor_get (); + + sidebar->update_places_on_idle_id = 0; + + sidebar->bookmarks_changed_id = 0; + + sidebar-> hidden_files_changed_id = 0; + + 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, + NEMO_WINDOW_STATE_BOOKMARKS_EXPANDED); + sidebar->devices_expanded = g_settings_get_boolean (nemo_window_state, + NEMO_WINDOW_STATE_DEVICES_EXPANDED); + sidebar->network_expanded = g_settings_get_boolean (nemo_window_state, + NEMO_WINDOW_STATE_NETWORK_EXPANDED); + + gtk_widget_set_size_request (GTK_WIDGET (sidebar), 140, -1); + + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); + gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN); + + style_context = gtk_widget_get_style_context (GTK_WIDGET (sidebar)); + gtk_style_context_set_junction_sides (style_context, GTK_JUNCTION_RIGHT | GTK_JUNCTION_LEFT); + + /* 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); + + nemo_places_tree_sidebar_renderer_init (sidebar, tree_view); + + sidebar->store = nemo_shortcuts_tree_model_new (sidebar); + 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); + + 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_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; + + 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, + NULL); + + gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree_view), + GDK_BUTTON1_MASK, + 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); + + g_signal_connect (tree_view, "key-press-event", + G_CALLBACK (bookmarks_key_press_event_cb), sidebar); + + g_signal_connect (tree_view, "drag-motion", + G_CALLBACK (drag_motion_callback), sidebar); + g_signal_connect (tree_view, "drag-leave", + G_CALLBACK (drag_leave_callback), sidebar); + g_signal_connect (tree_view, "drag-data-received", + G_CALLBACK (drag_data_received_callback), sidebar); + g_signal_connect (tree_view, "drag-drop", + G_CALLBACK (drag_drop_callback), sidebar); + + g_signal_connect (selection, "changed", + G_CALLBACK (bookmarks_selection_changed_cb), sidebar); + g_signal_connect (tree_view, "popup-menu", + G_CALLBACK (bookmarks_popup_menu_cb), sidebar); + g_signal_connect (tree_view, "button-press-event", + G_CALLBACK (bookmarks_button_press_event_cb), sidebar); + g_signal_connect (tree_view, "button-release-event", + G_CALLBACK (bookmarks_button_release_event_cb), sidebar); + g_signal_connect (tree_view, "row-expanded", + G_CALLBACK (row_expanded_cb), sidebar); + g_signal_connect (tree_view, "row-collapsed", + G_CALLBACK (row_collapsed_cb), sidebar); + g_signal_connect (tree_view, "row-activated", + G_CALLBACK (row_activated_cb), sidebar); + g_signal_connect (tree_view, "motion-notify-event", + G_CALLBACK (motion_notify_cb), sidebar); + g_signal_connect (tree_view, "leave-notify-event", + G_CALLBACK (leave_notify_cb), sidebar); + + g_signal_connect_object (GTK_WIDGET (tree_view), "query-tooltip", + G_CALLBACK (query_tooltip_callback), sidebar, 0); + gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE); + + g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_DESKTOP_IS_HOME_DIR, + G_CALLBACK(desktop_setting_changed_callback), + sidebar); + + g_signal_connect_swapped (nemo_desktop_preferences, "changed::" NEMO_PREFERENCES_SHOW_DESKTOP, + G_CALLBACK(desktop_setting_changed_callback), + sidebar); + + g_signal_connect_swapped (cinnamon_privacy_preferences, "changed::" NEMO_PREFERENCES_RECENT_ENABLED, + G_CALLBACK(desktop_setting_changed_callback), + sidebar); + + g_signal_connect_object (nemo_trash_monitor_get (), + "trash_state_changed", + G_CALLBACK (trash_state_changed_cb), + sidebar, 0); + + g_signal_connect_swapped (xapp_favorites_get_default (), + "changed", + G_CALLBACK (favorites_changed_cb), + sidebar); +} + +static void +nemo_places_tree_sidebar_dispose (GObject *object) +{ + NemoPlacesTreeSidebar *sidebar; + + sidebar = NEMO_PLACES_TREE_SIDEBAR (object); + + if(sidebar->tree_view != NULL) + { + g_signal_handlers_disconnect_by_func(GTK_WIDGET(sidebar->tree_view), + G_CALLBACK (query_tooltip_callback), + sidebar); + + sidebar->tree_view = NULL; + } + + if (sidebar->window!=NULL) { + if(sidebar->hidden_files_changed_id != 0) { + g_signal_handler_disconnect (sidebar->window, + sidebar->hidden_files_changed_id); + sidebar->hidden_files_changed_id = 0; + } + sidebar->window = NULL; + } + + g_free (sidebar->uri); + sidebar->uri = NULL; + + g_clear_object (&sidebar->ui_manager); + sidebar->ui_manager = NULL; + + free_drag_data (sidebar); + + g_clear_handle_id (&sidebar->actions_changed_idle_id, g_source_remove); + + if (sidebar->bookmarks_changed_id != 0) { + g_signal_handler_disconnect (sidebar->bookmarks, + sidebar->bookmarks_changed_id); + sidebar->bookmarks_changed_id = 0; + } + + if (sidebar->actions_changed_id != 0) { + g_signal_handler_disconnect (sidebar->action_manager, + 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); + + g_clear_pointer (&sidebar->top_bookend_uri, g_free); + g_clear_pointer (&sidebar->bottom_bookend_uri, g_free); + + 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_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 (nemo_desktop_preferences, + desktop_setting_changed_callback, + sidebar); + + g_signal_handlers_disconnect_by_func (cinnamon_privacy_preferences, + desktop_setting_changed_callback, + sidebar); + + g_signal_handlers_disconnect_by_func (xapp_favorites_get_default (), + favorites_changed_cb, + sidebar); + + if (sidebar->volume_monitor != NULL) { + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + volume_added_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + volume_removed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + volume_changed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + mount_added_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + mount_removed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + mount_changed_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + drive_disconnected_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + drive_connected_callback, sidebar); + g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, + drive_changed_callback, sidebar); + + g_clear_object (&sidebar->volume_monitor); + } + + G_OBJECT_CLASS (nemo_places_tree_sidebar_parent_class)->dispose (object); +} + +static void +nemo_places_tree_sidebar_class_init (NemoPlacesTreeSidebarClass *class) +{ + GObjectClass *oclass = G_OBJECT_CLASS (class); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); + + oclass->dispose = nemo_places_tree_sidebar_dispose; + + widget_class->style_set = nemo_places_tree_sidebar_style_set; + widget_class->focus = nemo_places_tree_sidebar_focus; + + gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &menu_icon_pixels, NULL); + 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); +} + +static gboolean +update_places_on_idle_callback (NemoPlacesTreeSidebar *sidebar) +{ + sidebar->update_places_on_idle_id = 0; + + update_places (sidebar); + + return FALSE; +} + +static void +update_places_on_idle (NemoPlacesTreeSidebar *sidebar) +{ + if (sidebar->update_places_on_idle_id != 0) { + g_source_remove (sidebar->update_places_on_idle_id); + sidebar->update_places_on_idle_id = 0; + } + + sidebar->update_places_on_idle_id = g_idle_add_full (G_PRIORITY_LOW, + (GSourceFunc) update_places_on_idle_callback, + sidebar, NULL); +} + +static void +nemo_places_tree_sidebar_set_parent_window (NemoPlacesTreeSidebar *sidebar, + NemoWindow *window) +{ + NemoWindowSlot *slot; + gint breakpoint; + + sidebar->window = window; + + slot = nemo_window_get_active_slot (window); + + sidebar->bookmarks = nemo_bookmark_list_get_default (); + sidebar->uri = nemo_window_slot_get_current_uri (slot); + + breakpoint = g_settings_get_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT); + + if (breakpoint < 0) { // Default gsettings value is -1 (which translates to 'not previously set') + breakpoint = nemo_bookmark_list_length (sidebar->bookmarks); + g_settings_set_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, breakpoint); + } + + sidebar->bookmark_breakpoint = breakpoint; + g_signal_connect_swapped (nemo_window_state, "changed::" NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, + G_CALLBACK (breakpoint_changed_cb), sidebar); + + sidebar->bookmarks_changed_id = + g_signal_connect_swapped (sidebar->bookmarks, "changed", + G_CALLBACK (update_places_on_idle), + sidebar); + + g_signal_connect_object (window, "loading_uri", + G_CALLBACK (loading_uri_callback), + sidebar, 0); + + g_signal_connect_object (sidebar->volume_monitor, "volume_added", + G_CALLBACK (volume_added_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "volume_removed", + G_CALLBACK (volume_removed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "volume_changed", + G_CALLBACK (volume_changed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_added", + G_CALLBACK (mount_added_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_removed", + G_CALLBACK (mount_removed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "mount_changed", + G_CALLBACK (mount_changed_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_disconnected", + G_CALLBACK (drive_disconnected_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_connected", + G_CALLBACK (drive_connected_callback), sidebar, 0); + g_signal_connect_object (sidebar->volume_monitor, "drive_changed", + G_CALLBACK (drive_changed_callback), sidebar, 0); + + g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_ALWAYS_USE_BROWSER, + G_CALLBACK (reset_menu), sidebar); + sidebar->hidden_files_changed_id = + g_signal_connect_object (window, "hidden-files-mode-changed", + G_CALLBACK (hidden_files_mode_changed_callback), sidebar, 0); + update_places (sidebar); +} + +static void +nemo_places_tree_sidebar_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + NemoPlacesTreeSidebar *sidebar; + + sidebar = NEMO_PLACES_TREE_SIDEBAR (widget); + update_places (sidebar); +} + +GtkWidget * +nemo_places_tree_sidebar_new (NemoWindow *window) +{ + NemoPlacesTreeSidebar *sidebar; + + sidebar = g_object_new (NEMO_TYPE_PLACES_TREE_SIDEBAR, NULL); + nemo_places_tree_sidebar_set_parent_window (sidebar, window); + + return GTK_WIDGET (sidebar); +} + + +/* Drag and drop interfaces */ + +/* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */ + +static gboolean +nemo_shortcuts_model_row_draggable (GtkTreeDragSource *drag_source, + GtkTreePath *path) +{ + 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; +} + +static void +_nemo_shortcuts_tree_model_class_init (NemoShortcutsTreeModelClass *class) +{ + +} + +static void +_nemo_shortcuts_tree_model_init (NemoShortcutsTreeModel *model) +{ + model->sidebar = NULL; +} + +static void +_nemo_shortcuts_tree_model_drag_source_init (GtkTreeDragSourceIface *iface) +{ + iface->row_draggable = nemo_shortcuts_model_row_draggable; +} + +static GtkTreeStore * +nemo_shortcuts_tree_model_new (NemoPlacesTreeSidebar *sidebar) +{ + NemoShortcutsTreeModel *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 + }; + + model = g_object_new (NEMO_TYPE_SHORTCUTS_TREE_MODEL, NULL); + model->sidebar = sidebar; + + gtk_tree_store_set_column_types (GTK_TREE_STORE (model), + PLACES_SIDEBAR_COLUMN_COUNT, + model_types); + + return GTK_TREE_STORE (model); +} + diff --git a/src/nemo-places-tree-sidebar.h b/src/nemo-places-tree-sidebar.h new file mode 100644 index 000000000..a292bf63a --- /dev/null +++ b/src/nemo-places-tree-sidebar.h @@ -0,0 +1,56 @@ + + +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* + * Copyright (C) 2000, 2001 Eazel, Inc + * Copyright (C) 2002 Anders Carlsson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, + * Boston, MA 02110-1335, USA. + * + * Authors: Klaus-Dieter Menk + */ + +/* fm-tree-view.h - tree view. */ + +#ifndef NEMO_PLACES_TREE_SIDEBAR_H +#define NEMO_PLACES_TREE_SIDEBAR_H + +#include "nemo-window.h" + +#include + +#define NEMO_PLACES_TREE_SIDEBAR_ID "placestree" + +#define NEMO_TYPE_PLACES_TREE_SIDEBAR nemo_places_tree_sidebar_get_type() +#define NEMO_PLACES_TREE_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_TYPE_PLACES_TREE_SIDEBAR, NemoPlacesTreeSidebar)) +#define NEMO_PLACES_TREE_SIDEBAR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), NEMO_TYPE_PLACES_TREE_SIDEBAR, NemoPlacesTreeSidebarClass)) +#define NEMO_IS_PLACES_TREE_SIDEBAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NEMO_TYPE_PLACES_TREE_SIDEBAR)) +#define NEMO_IS_PLACES_TREE_SIDEBAR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), NEMO_TYPE_PLACES_TREE_SIDEBAR)) +#define NEMO_PLACES_TREE_SIDEBAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), NEMO_TYPE_PLACES_TREE_SIDEBAR, NemoPlacesTreeSidebarClass)) + + +GType nemo_places_tree_sidebar_get_type (void); +GtkWidget * nemo_places_tree_sidebar_new (NemoWindow *window); + +#endif /* NEMO_PLACES_TREE_SIDEBAR_H */ + + diff --git a/src/nemo-query-editor.c b/src/nemo-query-editor.c index 330bcda8d..56c02be75 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", From d754475321d680a0324ae234ddcd9c2de1a8a883 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 11 Oct 2025 04:03:09 +0200 Subject: [PATCH 03/19] Fix sidebar warnings, remove old tree-sidebar files, update .gitignore --- src/nemo-places-sidebar.c | 2 + src/nemo-places-tree-sidebar.c | 5008 -------------------------------- src/nemo-places-tree-sidebar.h | 56 - 3 files changed, 2 insertions(+), 5064 deletions(-) delete mode 100644 src/nemo-places-tree-sidebar.c delete mode 100644 src/nemo-places-tree-sidebar.h diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index dbe8ff546..dbfef3050 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -605,6 +605,8 @@ add_children_idle(gpointer user_data) return FALSE; } + gtk_tree_view_expand_row(data->sidebar->tree_view, data->parent_path, FALSE); + add_directory_children(data->sidebar, &parent_iter, data->uri); g_free(data->uri); diff --git a/src/nemo-places-tree-sidebar.c b/src/nemo-places-tree-sidebar.c deleted file mode 100644 index a9efd323f..000000000 --- a/src/nemo-places-tree-sidebar.c +++ /dev/null @@ -1,5008 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: f; c-basic-offset: 4; tab-width: 4 -*- */ - -/* - * Nemo - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Suite 500, MA 02110-1335, USA. - * - * Authors : Mr Jamie McCracken (jamiemcc at blueyonder dot co dot uk) - * Cosimo Cecchi - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "nemo-actions.h" -#include "nemo-application.h" -#include "nemo-bookmark-list.h" -#include "nemo-places-tree-sidebar.h" -#include "nemo-properties-window.h" -#include "nemo-window.h" -#include "nemo-window-slot.h" - -#define DEBUG_FLAG NEMO_DEBUG_PLACES -#include - -#define EXPANDER_PAD_COLUMN_WIDTH 4 -#define EJECT_COLUMN_MIN_WIDTH 22 -#define EJECT_COLUMN_MAX_WIDTH 60 -#define DRAG_EXPAND_CATEGORY_DELAY 500 -#define EJECT_PAD_COLUMN_WIDTH 14 - -static gint EJECT_ICON_SIZE_NOT_HOVERED = 0; -static gint EJECT_ICON_SIZE_HOVERED = GTK_ICON_SIZE_MENU; -static gint menu_icon_pixels = 16; -#define EJECT_ICON_SIZE_REDUCTION 4 - -#if (!GLIB_CHECK_VERSION(2,50,0)) -#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; - GtkCellRenderer *icon_cell_renderer; - GtkCellRenderer *padding_cell_renderer; - GtkCellRenderer *heading_cell_renderer; - char *uri; - GtkTreeStore *store; - GtkTreeModel *store_filter; - - NemoWindow *window; - NemoBookmarkList *bookmarks; - GVolumeMonitor *volume_monitor; - - NemoActionManager *action_manager; - gulong actions_changed_id; - guint actions_changed_idle_id; - - GtkUIManager *ui_manager; - - GtkActionGroup *bookmark_action_group; - guint bookmark_action_group_merge_id; - GtkActionGroup *action_action_group; - guint action_action_group_merge_id; - - gboolean actions_need_update; - - gboolean devices_header_added; - gboolean bookmarks_header_added; - - /* DnD */ - GList *drag_list; - gboolean drag_data_received; - int drag_data_info; - gboolean drop_occured; - gboolean in_drag; - gchar *desktop_dnd_source_fs; - gboolean desktop_dnd_can_delete_source; - - GtkWidget *popup_menu; - - /* volume mounting - delayed open process */ - gboolean mounting; - NemoWindowSlot *go_to_after_mount_slot; - NemoWindowOpenFlags go_to_after_mount_flags; - - guint bookmarks_changed_id; - - gboolean my_computer_expanded; - gboolean bookmarks_expanded; - gboolean devices_expanded; - gboolean network_expanded; - - gboolean updating_sidebar; - - /* Store the positions of the last - * entry prior to bookmarks, and - * the first entry after bookmarks - * to allow drag and drop creation - * of new bookmarks */ - gchar *top_bookend_uri; - gchar *bottom_bookend_uri; - - gint bookmark_breakpoint; - guint expand_timeout_source; - guint popup_menu_action_index; - guint update_places_on_idle_id; - - guint hidden_files_changed_id; - -} NemoPlacesTreeSidebar; - -typedef struct { - GtkScrolledWindowClass parent; -} NemoPlacesTreeSidebarClass; - -typedef struct { - GObject parent; -} NemoPlacesTreeSidebarProvider; - -typedef struct { - GObjectClass parent; -} NemoPlacesTreeSidebarProviderClass; - -typedef struct TreeNode TreeNode; -struct TreeNode { - /* part of this node for the file itself */ - int ref_count; - - NemoFile *file; - char *display_name; - GIcon *icon; - GMount *mount; - GIcon *closed_icon; - GIcon *open_icon; - - GtkTreeIter *root; - - TreeNode *parent; - TreeNode *next; - TreeNode *prev; - - /* part of the node used only for directories */ - 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; - - /* misc. flags */ - guint done_loading : 1; - guint force_has_dummy : 1; - guint inserted : 1; - guint pinned : 1; - guint fav_unavailable : 1; -}; - -enum { - 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, - PLACES_SIDEBAR_COLUMN_BOOKMARK, - PLACES_SIDEBAR_COLUMN_TOOLTIP, - PLACES_SIDEBAR_COLUMN_EJECT_ICON, - PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, - 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 only - PLACES_SIDEBAR_COLUMN_COUNT -}; - -typedef enum { - PLACES_BUILT_IN, - PLACES_XDG_DIR, - PLACES_MOUNTED_VOLUME, - PLACES_BOOKMARK, - PLACES_HEADING, - PLACES_TREE_FOLDER, -} PlaceType; - -typedef enum { - SECTION_COMPUTER, - SECTION_XDG_BOOKMARKS, - SECTION_BOOKMARKS, - SECTION_DEVICES, - SECTION_NETWORK, -} SectionType; - -enum { - POSITION_UPPER, - POSITION_MIDDLE, - POSITION_LOWER -}; - -static void open_selected_bookmark (NemoPlacesTreeSidebar *sidebar, - GtkTreeModel *model, - GtkTreeIter *iter, - NemoWindowOpenFlags flags); -static void nemo_places_tree_sidebar_style_set (GtkWidget *widget, - GtkStyle *previous_style); -static gboolean eject_or_unmount_bookmark (NemoPlacesTreeSidebar *sidebar, - GtkTreePath *path); -static gboolean eject_or_unmount_selection (NemoPlacesTreeSidebar *sidebar); -static void check_unmount_and_eject (GMount *mount, - GVolume *volume, - GDrive *drive, - gboolean *show_unmount, - gboolean *show_eject); - -static void update_places (NemoPlacesTreeSidebar *sidebar); -static void update_places_on_idle (NemoPlacesTreeSidebar *sidebar); -static void rebuild_menu (NemoPlacesTreeSidebar *sidebar); -static void actions_changed (gpointer user_data); -/* Identifiers for target types */ -enum { - GTK_TREE_MODEL_ROW, - TEXT_URI_LIST -}; - -/* Target types for dragging from the shortcuts list */ -static const GtkTargetEntry nemo_shortcuts_source_targets[] = { - { (char *)"GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW } -}; - -/* Target types for dropping into the shortcuts list */ -static const GtkTargetEntry nemo_shortcuts_drop_targets [] = { - { (char *)"GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, GTK_TREE_MODEL_ROW }, - { (char *)"text/uri-list", 0, TEXT_URI_LIST } -}; - -/* Drag and drop interface declarations */ -typedef struct { - GtkTreeStore parent; - - NemoPlacesTreeSidebar *sidebar; -} NemoShortcutsTreeModel; - -typedef struct { - GtkTreeStoreClass parent_class; -} NemoShortcutsTreeModelClass; - -#define NEMO_TYPE_SHORTCUTS_TREE_MODEL (_nemo_shortcuts_tree_model_get_type ()) -#define NEMO_SHORTCUTS_TREE_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_SHORTCUTS_MODEL_TYPE, NemoShortcutsTreeModel)) - -GType _nemo_shortcuts_tree_model_get_type (void); -static void _nemo_shortcuts_tree_model_drag_source_init (GtkTreeDragSourceIface *iface); -G_DEFINE_TYPE_WITH_CODE (NemoShortcutsTreeModel, _nemo_shortcuts_tree_model, GTK_TYPE_TREE_STORE, - G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, - _nemo_shortcuts_tree_model_drag_source_init)); -static GtkTreeStore *nemo_shortcuts_tree_model_new (NemoPlacesTreeSidebar *sidebar); - -G_DEFINE_TYPE (NemoPlacesTreeSidebar, nemo_places_tree_sidebar, GTK_TYPE_SCROLLED_WINDOW); - -static void -breakpoint_changed_cb (NemoPlacesTreeSidebar *sidebar) -{ - sidebar->bookmark_breakpoint = g_settings_get_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT); - update_places (sidebar); -} - -static void -increment_bookmark_breakpoint (NemoPlacesTreeSidebar *sidebar) -{ - g_signal_handlers_block_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); - - sidebar->bookmark_breakpoint ++; - g_settings_set_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, sidebar->bookmark_breakpoint); - - g_signal_handlers_unblock_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); -} - -static void -decrement_bookmark_breakpoint (NemoPlacesTreeSidebar *sidebar) -{ - g_signal_handlers_block_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); - - sidebar->bookmark_breakpoint --; - g_settings_set_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, sidebar->bookmark_breakpoint); - - g_signal_handlers_unblock_by_func (nemo_window_state, breakpoint_changed_cb, sidebar); -} - -static gboolean -should_show_desktop (void) -{ - return g_settings_get_boolean (nemo_desktop_preferences, NEMO_PREFERENCES_SHOW_DESKTOP) && - !g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_DESKTOP_IS_HOME_DIR); -} - -static gboolean -is_built_in_bookmark (NemoFile *file) -{ - if (nemo_file_is_home (file)) { - return TRUE; - } - - if (nemo_file_is_desktop_directory (file) && should_show_desktop ()) - return TRUE; - else - return FALSE; -} - -static GtkTreeIter -add_heading (NemoPlacesTreeSidebar *sidebar, - SectionType section_type, - const gchar *title) -{ - GtkTreeIter cat_iter; - - 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; -} - -static GtkTreeIter -check_heading_for_devices (NemoPlacesTreeSidebar *sidebar, - SectionType section_type, - GtkTreeIter cat_iter) -{ - if (section_type == SECTION_DEVICES) { - if (!sidebar->devices_header_added) { - cat_iter = add_heading (sidebar, SECTION_DEVICES, - _("Devices")); - sidebar->devices_header_added = TRUE; - } - } - return cat_iter; -} - -typedef struct { - PlaceType place_type; - SectionType section_type; - gchar *name; - gchar *icon_name; - gchar *uri; - GDrive *drive; - GVolume *volume; - GMount *mount; - gint index; - gchar *tooltip; - gint df_percent; - gboolean show_df_percent; -} PlaceInfo; - -static gint -sort_places_func (gconstpointer a, - gconstpointer b) -{ - return g_utf8_collate (((PlaceInfo *) a)->name, ((PlaceInfo *) b)->name); -} - -static PlaceInfo * -new_place_info (PlaceType place_type, - SectionType section_type, - gchar *name, - gchar *icon_name, - gchar *uri, - GDrive *drive, - GVolume *volume, - GMount *mount, - gint index, - gchar *tooltip, - gint df_percent, - gboolean show_df_percent) -{ - PlaceInfo *info = g_new0 (PlaceInfo, 1); - - info->place_type = place_type; - info->section_type = section_type; - info->name = g_utf8_make_valid (name, -1); - info->icon_name = g_strdup (icon_name); - info->uri = (g_strdup (uri)); - info->drive = drive ? g_object_ref (drive) : NULL; - info->volume = volume ? g_object_ref (volume) : NULL; - info->mount = mount ? g_object_ref (mount) : NULL; - info->index = index; - info->tooltip = g_strdup (tooltip); - info->df_percent = df_percent; - info->show_df_percent = show_df_percent; - - return info; -} - -static void -free_place_info (PlaceInfo *info) -{ - g_free (info->name); - g_free (info->icon_name); - g_free (info->uri); - g_clear_object (&info->drive); - g_clear_object (&info->volume); - g_clear_object (&info->mount); - g_free (info->tooltip); - - g_free (info); -} - -static void -add_children_lazy(NemoPlacesTreeSidebar *sidebar, - GtkTreeIter *parent_iter) -{ - GtkTreeIter dummy; - gtk_tree_store_append(sidebar->store, &dummy, - parent_iter); - gtk_tree_store_set(sidebar->store, - &dummy, PLACES_SIDEBAR_COLUMN_TREE_NAME, _("(loading)"), -1); -} - - -static void -add_directory_children_lazy(NemoPlacesTreeSidebar *sidebar, - GtkTreeIter *parent_iter, const char *uri) -{ - - GFile *dir = g_file_new_for_uri(uri); - GFileEnumerator *enumerator = g_file_enumerate_children( - dir, - G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, - NULL); - - if (enumerator) { - GFileInfo *info; - while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { - if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { - const char *name = g_file_info_get_name(info); - GFile *child_dir = g_file_get_child(dir, name); - char *child_uri = g_file_get_uri(child_dir); - add_children_lazy(sidebar, parent_iter); - g_free(child_uri); - g_object_unref(child_dir); - break; - } - g_object_unref(info); - } - g_file_enumerator_close(enumerator, NULL, NULL); - g_object_unref(enumerator); - } - - g_object_unref(dir); -} - -static void -add_directory_children(NemoPlacesTreeSidebar *sidebar, GtkTreeIter *parent_iter, const char *uri) -{ - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store); - - GtkTreeIter child; - if (gtk_tree_model_iter_children(model, &child, parent_iter)) { - // Es gibt mindestens ein Kind - gchar *name = NULL; - gtk_tree_model_get(model, &child, PLACES_SIDEBAR_COLUMN_TREE_NAME, &name, -1); - - if (g_strcmp0(name, "(loading)") == 0) { - // Entferne nur den Dummy - gtk_tree_store_remove(GTK_TREE_STORE(model), &child); - } else { - // Echte Kinder schon vorhanden → nichts tun - g_free(name); - return; - } - g_free(name); - } - - // --- Enumerate real children --- - GFile *dir = g_file_new_for_uri(uri); - GFileEnumerator *enumerator = g_file_enumerate_children( - dir, - G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, - NULL); - - if (enumerator) { - GFileInfo *info; - while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { - if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { - const char *name = g_file_info_get_name(info); - GFile *child_dir = g_file_get_child(dir, name); - char *child_uri = NULL; - - GFileInfo *nodeinfo = g_file_query_info(child_dir, - "standard::target-uri", - G_FILE_QUERY_INFO_NONE, - NULL, NULL); - - if (nodeinfo) { - child_uri = g_strdup(g_file_info_get_attribute_string(nodeinfo, "standard::target-uri")); - g_object_unref(nodeinfo); - } - if(child_uri==NULL) - { - child_uri = g_file_get_uri(child_dir); - } - GtkTreeIter child_iter; - gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); - gtk_tree_store_set( - GTK_TREE_STORE(model), &child_iter, - PLACES_SIDEBAR_COLUMN_TREE_NAME, name, - PLACES_SIDEBAR_COLUMN_URI, child_uri, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, - PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), - -1); - - add_directory_children_lazy(sidebar, &child_iter, child_uri); - - g_free(child_uri); - g_object_unref(child_dir); - } - g_object_unref(info); - } - g_file_enumerator_close(enumerator, NULL, NULL); - } - - g_object_unref(enumerator); - g_object_unref(dir); -} - - - -static GtkTreeIter -add_place (NemoPlacesTreeSidebar *sidebar, - PlaceType place_type, - SectionType section_type, - const char *name, - const char *icon_name, - const char *uri, - GDrive *drive, - GVolume *volume, - GMount *mount, - int index, - const char *tooltip, - int df_percent, - gboolean show_df_percent, - GtkTreeIter cat_iter) -{ - GtkTreeIter iter; - GIcon *gicon; - gboolean show_eject, show_unmount; - gboolean show_eject_button; - - cat_iter = check_heading_for_devices (sidebar, section_type, cat_iter); - - check_unmount_and_eject (mount, volume, drive, - &show_unmount, &show_eject); - - if (show_unmount || show_eject) { - g_assert (place_type != PLACES_BOOKMARK); - } - - if (mount == NULL) { - show_eject_button = FALSE; - } else { - show_eject_button = (show_unmount || show_eject); - } - - 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, - 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_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); - - // add children - if (uri != NULL) - switch(section_type) { - case SECTION_NETWORK: - case SECTION_DEVICES: - add_children_lazy(sidebar, &iter); - break; - default: - add_directory_children_lazy(sidebar, &iter, uri); - break; - } - g_clear_object (&gicon); - - return cat_iter; -} - -typedef struct { - const gchar *location; - const gchar *last_uri; - NemoPlacesTreeSidebar *sidebar; - GtkTreePath *path; -} RestoreLocationData; - -static gboolean -restore_selection_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - RestoreLocationData *data = user_data; - gchar *uri; - - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_URI, &uri, - -1); - if (g_strcmp0 (uri, data->last_uri) == 0 || - g_strcmp0 (uri, data->location) == 0) { - data->path = gtk_tree_path_copy (path); - } - - g_free (uri); - - return (data->path != NULL); -} - -static gboolean -restore_expand_state_foreach (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer user_data) -{ - PlaceType place_type; - SectionType section_type; - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - 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) { - if (section_type == SECTION_COMPUTER) { - if (sidebar->my_computer_expanded) - gtk_tree_view_expand_to_path (sidebar->tree_view, path); - else - gtk_tree_view_collapse_row (sidebar->tree_view, path); - } else if (section_type == SECTION_BOOKMARKS) { - if (sidebar->bookmarks_expanded) - gtk_tree_view_expand_to_path (sidebar->tree_view, path); - else - gtk_tree_view_collapse_row (sidebar->tree_view, path); - } else if (section_type == SECTION_DEVICES) { - if (sidebar->devices_expanded) - gtk_tree_view_expand_to_path (sidebar->tree_view, path); - else - gtk_tree_view_collapse_row (sidebar->tree_view, path); - } else if (section_type == SECTION_NETWORK) { - if (sidebar->network_expanded) - gtk_tree_view_expand_to_path (sidebar->tree_view, path); - else - gtk_tree_view_collapse_row (sidebar->tree_view, path); - } - } - return FALSE; -} - -static void -restore_expand_state (NemoPlacesTreeSidebar *sidebar) -{ - gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store_filter), - restore_expand_state_foreach, sidebar); -} - - -static void expand_or_collapse_category (NemoPlacesTreeSidebar *sidebar, - SectionType section_type, gboolean expand) -{ - switch (section_type) { - case SECTION_COMPUTER: - sidebar->my_computer_expanded = expand; - break; - case SECTION_BOOKMARKS: - sidebar->bookmarks_expanded = expand; - break; - case SECTION_DEVICES: - sidebar->devices_expanded = expand; - break; - case SECTION_NETWORK: - sidebar->network_expanded = expand; - break; - case SECTION_XDG_BOOKMARKS: - default: - break; - } - - restore_expand_state (sidebar); -} - -static void -sidebar_update_restore_selection (NemoPlacesTreeSidebar *sidebar, - const gchar *location, - const gchar *last_uri) -{ - RestoreLocationData data; - GtkTreeSelection *selection; - - data.location = location; - data.last_uri = last_uri; - data.sidebar = sidebar; - data.path = NULL; - - gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store_filter), - restore_selection_foreach, &data); - - if (data.path != NULL) { - selection = gtk_tree_view_get_selection (sidebar->tree_view); - gtk_tree_selection_select_path (selection, data.path); - gtk_tree_path_free (data.path); - } -} - -static gint -get_disk_full (GFile *file, gchar **tooltip_info) -{ - GFileInfo *info; - GError *error; - guint64 k_used, k_total, k_free; - gint df_percent; - float fraction; - int prefix; - gchar *size_string; - gchar *out_string; - - error = NULL; - df_percent = -1; - out_string = NULL; - - info = g_file_query_filesystem_info (file, - "filesystem::*", - NULL, - &error); - - if (info != NULL) { - k_used = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USED); - k_total = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE); - k_free = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); - - if (k_total > 0) { - fraction = ((float) k_used / (float) k_total) * 100.0; - - df_percent = (gint) rintf(fraction); - - prefix = nemo_global_preferences_get_size_prefix_preference (); - size_string = g_format_size_full (k_free, prefix); - - out_string = g_strdup_printf (_("Free space: %s"), size_string); - - g_free (size_string); - } - - g_object_unref (info); - } - - if (error != NULL) { - g_warning ("Couldn't get disk full info for: %s", error->message); - g_clear_error (&error); - } - - if (out_string == NULL) { - out_string = g_strdup (" "); - } - - *tooltip_info = out_string; - - return df_percent; -} - -static gboolean -home_on_different_fs (const gchar *home_uri) -{ - GFile *home = g_file_new_for_uri (home_uri); - GFile *root = g_file_new_for_uri ("file:///"); - GFileInfo *home_info, *root_info; - const gchar *home_id, *root_id; - gboolean res; - - res = FALSE; - home_info = g_file_query_info (home, - G_FILE_ATTRIBUTE_ID_FILESYSTEM, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, NULL); - root_info = g_file_query_info (root, - G_FILE_ATTRIBUTE_ID_FILESYSTEM, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, NULL); - - if (home_info && root_info) { - home_id = g_file_info_get_attribute_string (home_info, G_FILE_ATTRIBUTE_ID_FILESYSTEM); - root_id = g_file_info_get_attribute_string (root_info, G_FILE_ATTRIBUTE_ID_FILESYSTEM); - res = g_strcmp0 (home_id, root_id) != 0; - g_object_unref (home_info); - g_object_unref (root_info); - } else { - if (home_info) - g_object_unref (home_info); - if (root_info) - g_object_unref (root_info); - } - g_object_unref (home); - g_object_unref (root); - return res; -} - -static gchar * -get_icon_name (const gchar *uri) -{ - NemoFile *file = nemo_file_get_by_uri (uri); - gchar *icon_name; - - icon_name = nemo_file_get_control_icon_name (file); - nemo_file_unref (file); - - return icon_name; -} - -static void -update_places (NemoPlacesTreeSidebar *sidebar) -{ - NemoBookmark *bookmark; - GtkTreeSelection *selection; - GtkTreeIter last_iter, cat_iter; - GtkTreeModel *model; - GVolumeMonitor *volume_monitor; - GList *mounts, *l, *ll; - GMount *mount; - GList *drives; - GDrive *drive; - GList *volumes; - GVolume *volume; - int bookmark_count, bookmark_index; - char *location, *mount_uri, *name, *desktop_path, *last_uri, *identifier; - const gchar *bookmark_name; - gchar *icon; - GFile *root, *df_file; - NemoWindowSlot *slot; - char *tooltip; - gchar *tooltip_info; - GList *network_mounts, *network_volumes; - gint full; - - DEBUG ("Updating places sidebar"); - - sidebar->updating_sidebar = TRUE; - - model = NULL; - 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); - } - gtk_tree_store_clear (sidebar->store); - - sidebar->devices_header_added = FALSE; - sidebar->bookmarks_header_added = FALSE; - - slot = nemo_window_get_active_slot (sidebar->window); - location = nemo_window_slot_get_current_uri (slot); - - network_mounts = network_volumes = NULL; - volume_monitor = sidebar->volume_monitor; - - cat_iter = add_heading (sidebar, SECTION_COMPUTER, - _("My Computer")); - /* add built in bookmarks */ - - /* home folder */ - mount_uri = nemo_get_home_directory_uri (); - icon = get_icon_name (mount_uri); - - df_file = g_file_new_for_uri (mount_uri); - full = get_disk_full (df_file, &tooltip_info); - g_clear_object (&df_file); - - tooltip = g_strdup_printf (_("Open your personal folder\n%s"), tooltip_info); - g_free (tooltip_info); - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_COMPUTER, - _("Home"), icon, - mount_uri, NULL, NULL, NULL, 0, - tooltip, - full, home_on_different_fs (mount_uri) && full > -1, - cat_iter); - g_free (icon); - sidebar->top_bookend_uri = g_strdup (mount_uri); - g_free (mount_uri); - g_free (tooltip); - - if (should_show_desktop ()) { - /* desktop */ - desktop_path = nemo_get_desktop_directory (); - mount_uri = g_filename_to_uri (desktop_path, NULL, NULL); - icon = get_icon_name (mount_uri); - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_COMPUTER, - _("Desktop"), icon, - mount_uri, NULL, NULL, NULL, 0, - _("Open the contents of your desktop in a folder"), 0, FALSE, - cat_iter); - g_free (icon); - g_free (sidebar->top_bookend_uri); - sidebar->top_bookend_uri = g_strdup (mount_uri); - g_free (mount_uri); - g_free (desktop_path); - } - - /* add bookmarks */ - bookmark_count = nemo_bookmark_list_length (sidebar->bookmarks); - /* in certain situations (i.e. removed a bookmark), the breakpoint is smaller than - * the number of bookmarks - make sure to fix this before iterating through a list of them. - * We don't overwrite the stored breakpoint because the bookmark list could simply be reloading, - * and we want the original number still when we update places again. - */ - gint temp_breakpoint = sidebar->bookmark_breakpoint; - - if (temp_breakpoint < 0 || - temp_breakpoint > bookmark_count) { - temp_breakpoint = bookmark_count; - } - - for (bookmark_index = 0; bookmark_index < temp_breakpoint; ++bookmark_index) { - bookmark = nemo_bookmark_list_item_at (sidebar->bookmarks, bookmark_index); - - root = nemo_bookmark_get_location (bookmark); - - bookmark_name = nemo_bookmark_get_name (bookmark); - icon = nemo_bookmark_get_icon_name (bookmark); - mount_uri = nemo_bookmark_get_uri (bookmark); - tooltip = g_file_get_parse_name (root); - - cat_iter = add_place (sidebar, PLACES_BOOKMARK, - SECTION_XDG_BOOKMARKS, - bookmark_name, icon, mount_uri, - NULL, NULL, NULL, bookmark_index, - tooltip, 0, FALSE, - cat_iter); - g_object_unref (root); - g_free (icon); - g_free (mount_uri); - g_free (tooltip); - } - - if (eel_vfs_supports_uri_scheme ("favorites")) { - gint n = xapp_favorites_get_n_favorites (xapp_favorites_get_default ()); - - if (n > 0) { - mount_uri = (char *)"favorites:///"; /* No need to strdup */ - icon = "xapp-user-favorites-symbolic"; - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_COMPUTER, - _("Favorites"), icon, mount_uri, - NULL, NULL, NULL, 0, - _("Favorite files"), 0, FALSE, cat_iter); - - sidebar->bottom_bookend_uri = g_strdup (mount_uri); - } - } - - gboolean recent_enabled; - recent_enabled = g_settings_get_boolean (cinnamon_privacy_preferences, - NEMO_PREFERENCES_RECENT_ENABLED); - - if (recent_enabled && eel_vfs_supports_uri_scheme ("recent")) { - mount_uri = (char *)"recent:///"; /* No need to strdup */ - icon = NEMO_ICON_SYMBOLIC_FOLDER_RECENT; - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_COMPUTER, - _("Recent"), icon, mount_uri, - NULL, NULL, NULL, 0, - _("Recent files"), 0, FALSE, cat_iter); - - if (sidebar->bottom_bookend_uri == NULL) { - sidebar->bottom_bookend_uri = g_strdup (mount_uri); - } - } - - /* file system root */ - mount_uri = (char *)"file:///"; /* No need to strdup */ - icon = NEMO_ICON_SYMBOLIC_FILESYSTEM; - - df_file = g_file_new_for_uri (mount_uri); - full = get_disk_full (df_file, &tooltip_info); - g_clear_object (&df_file); - - tooltip = g_strdup_printf (_("Open the contents of the File System\n%s"), tooltip_info); - g_free (tooltip_info); - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_COMPUTER, - _("File System"), icon, - mount_uri, NULL, NULL, NULL, 0, - tooltip, - full, full > -1, - cat_iter); - g_free (tooltip); - - if (sidebar->bottom_bookend_uri == NULL) { - sidebar->bottom_bookend_uri = g_strdup (mount_uri); - } - - if (eel_vfs_supports_uri_scheme("trash")) { - mount_uri = (char *)"trash:///"; /* No need to strdup */ - icon = nemo_trash_monitor_get_symbolic_icon_name (); - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_COMPUTER, - _("Trash"), icon, mount_uri, - NULL, NULL, NULL, 0, - _("Open the trash"), 0, FALSE, - cat_iter); - g_free (icon); - } - - cat_iter = add_heading (sidebar, SECTION_BOOKMARKS, - _("Bookmarks")); - - while (bookmark_index < bookmark_count) { - bookmark = nemo_bookmark_list_item_at (sidebar->bookmarks, bookmark_index); - - root = nemo_bookmark_get_location (bookmark); - - bookmark_name = nemo_bookmark_get_name (bookmark); - icon = nemo_bookmark_get_icon_name (bookmark); - mount_uri = nemo_bookmark_get_uri (bookmark); - tooltip = g_file_get_parse_name (root); - - cat_iter = add_place (sidebar, PLACES_BOOKMARK, - SECTION_BOOKMARKS, - bookmark_name, icon, mount_uri, - NULL, NULL, NULL, bookmark_index, - tooltip, 0, FALSE, - cat_iter); - g_object_unref (root); - g_free (icon); - g_free (mount_uri); - g_free (tooltip); - ++bookmark_index; - } - - GList *place_infos = NULL; - PlaceInfo *place_info; - - /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ - mounts = g_volume_monitor_get_mounts (volume_monitor); - - for (l = mounts; l != NULL; l = l->next) { - mount = l->data; - if (g_mount_is_shadowed (mount)) { - g_object_unref (mount); - continue; - } - volume = g_mount_get_volume (mount); - if (volume != NULL) { - g_object_unref (volume); - g_object_unref (mount); - continue; - } - root = g_mount_get_default_location (mount); - - if (!g_file_is_native (root)) { - gboolean really_network = TRUE; - gchar *path = g_file_get_path (root); - if (!path) { - network_mounts = g_list_prepend (network_mounts, mount); - g_object_unref (root); - continue; - } - gchar *escaped1 = g_uri_unescape_string (path, ""); - gchar *escaped2 = g_uri_unescape_string (escaped1, ""); - gchar *ptr = g_strrstr (escaped2, "file://"); - if (ptr != NULL) { - GFile *actual_file = g_file_new_for_uri (ptr); - if (g_file_is_native(actual_file)) { - really_network = FALSE; - } - g_object_unref(actual_file); - } - g_free (path); - g_free (escaped1); - g_free (escaped2); - if (really_network) { - network_mounts = g_list_prepend (network_mounts, mount); - g_object_unref (root); - continue; - } - } - - icon = nemo_get_mount_icon_name (mount); - mount_uri = g_file_get_uri (root); - name = g_mount_get_name (mount); - tooltip = g_file_get_parse_name (root); - place_info = new_place_info (PLACES_MOUNTED_VOLUME, - SECTION_DEVICES, - name, icon, mount_uri, - NULL, NULL, mount, 0, tooltip, 0, FALSE); - place_infos = g_list_prepend (place_infos, place_info); - g_object_unref (root); - g_object_unref (mount); - g_free (icon); - g_free (name); - g_free (mount_uri); - g_free (tooltip); - - } - g_list_free (mounts); - - /* first go through all connected drives */ - drives = g_volume_monitor_get_connected_drives (volume_monitor); - - for (l = drives; l != NULL; l = l->next) { - drive = l->data; - - volumes = g_drive_get_volumes (drive); - if (volumes != NULL) { - for (ll = volumes; ll != NULL; ll = ll->next) { - volume = ll->data; - identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); - - if (g_strcmp0 (identifier, "network") == 0) { - g_free (identifier); - network_volumes = g_list_prepend (network_volumes, volume); - continue; - } - g_free (identifier); - - mount = g_volume_get_mount (volume); - if (mount != NULL) { - gchar *full_display_name, *volume_id; - /* Show mounted volume in the sidebar */ - icon = nemo_get_mount_icon_name (mount); - root = g_mount_get_default_location (mount); - mount_uri = g_file_get_uri (root); - name = g_mount_get_name (mount); - - volume_id = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); - full_display_name = g_file_get_parse_name (root); - - df_file = g_file_new_for_uri (mount_uri); - full = get_disk_full (df_file, &tooltip_info); - g_clear_object (&df_file); - - tooltip = g_strdup_printf (_("%s (%s)\n%s"), - full_display_name, - volume_id, - tooltip_info); - g_free (tooltip_info); - place_info = new_place_info (PLACES_MOUNTED_VOLUME, - SECTION_DEVICES, - name, icon, mount_uri, - drive, volume, mount, 0, tooltip, full, full > -1); - place_infos = g_list_prepend (place_infos, place_info); - g_object_unref (root); - g_object_unref (mount); - g_free (icon); - g_free (tooltip); - g_free (name); - g_free (mount_uri); - g_free (volume_id); - g_free (full_display_name); - } else { - /* Do show the unmounted volumes in the sidebar; - * this is so the user can mount it (in case automounting - * is off). - * - * Also, even if automounting is enabled, this gives a visual - * cue that the user should remember to yank out the media if - * he just unmounted it. - */ - gchar *volume_id; - icon = nemo_get_volume_icon_name (volume); - name = g_volume_get_name (volume); - - volume_id = g_volume_get_identifier (volume, - G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); - tooltip = g_strdup_printf (_("Mount and open %s (%s)"), name, volume_id); - - place_info = new_place_info (PLACES_MOUNTED_VOLUME, - SECTION_DEVICES, - name, icon, NULL, - drive, volume, NULL, 0, tooltip, 0, FALSE); - place_infos = g_list_prepend (place_infos, place_info); - - g_free (icon); - g_free (name); - g_free (tooltip); - g_free (volume_id); - } - g_object_unref (volume); - } - g_list_free (volumes); - } else { - if (g_drive_is_removable (drive) && !g_drive_is_media_check_automatic (drive)) { - /* If the drive has no mountable volumes and we cannot detect media change.. we - * display the drive in the sidebar so the user can manually poll the drive by - * right clicking and selecting "Rescan..." - * - * This is mainly for drives like floppies where media detection doesn't - * work.. but it's also for human beings who like to turn off media detection - * in the OS to save battery juice. - */ - icon = nemo_get_drive_icon_name (drive); - name = g_drive_get_name (drive); - tooltip = g_strdup_printf (_("Mount and open %s"), name); - - place_info = new_place_info (PLACES_BUILT_IN, - SECTION_DEVICES, - name, icon, NULL, - drive, NULL, NULL, 0, tooltip, 0, FALSE); - place_infos = g_list_prepend (place_infos, place_info); - - g_free (icon); - g_free (tooltip); - g_free (name); - } - } - g_object_unref (drive); - } - g_list_free (drives); - - /* add all volumes that is not associated with a drive */ - volumes = g_volume_monitor_get_volumes (volume_monitor); - for (l = volumes; l != NULL; l = l->next) { - volume = l->data; - drive = g_volume_get_drive (volume); - if (drive != NULL) { - g_object_unref (volume); - g_object_unref (drive); - continue; - } - - identifier = g_volume_get_identifier (volume, G_VOLUME_IDENTIFIER_KIND_CLASS); - - if (g_strcmp0 (identifier, "network") == 0) { - g_free (identifier); - network_volumes = g_list_prepend (network_volumes, volume); - continue; - } - g_free (identifier); - - mount = g_volume_get_mount (volume); - if (mount != NULL) { - g_autofree gchar *parse_name = NULL; - icon = nemo_get_mount_icon_name (mount); - root = g_mount_get_default_location (mount); - mount_uri = g_file_get_uri (root); - - df_file = g_file_new_for_uri (mount_uri); - full = get_disk_full (df_file, &tooltip_info); - g_clear_object (&df_file); - - parse_name = g_file_get_parse_name (root); - tooltip = g_strdup_printf (_("%s\n%s"), parse_name, tooltip_info); - - g_free (tooltip_info); - g_object_unref (root); - name = g_mount_get_name (mount); - - place_info = new_place_info (PLACES_MOUNTED_VOLUME, - SECTION_DEVICES, - name, icon, mount_uri, - NULL, volume, mount, 0, tooltip, full, full > -1); - place_infos = g_list_prepend (place_infos, place_info); - - g_object_unref (mount); - g_free (icon); - g_free (name); - g_free (tooltip); - g_free (mount_uri); - } else { - /* see comment above in why we add an icon for an unmounted mountable volume */ - icon = nemo_get_volume_icon_name (volume); - name = g_volume_get_name (volume); - - place_info = new_place_info (PLACES_MOUNTED_VOLUME, - SECTION_DEVICES, - name, icon, NULL, - NULL, volume, NULL, 0, name, 0, FALSE); - place_infos = g_list_prepend (place_infos, place_info); - - g_free (icon); - g_free (name); - } - g_object_unref (volume); - } - g_list_free (volumes); - - place_infos = g_list_sort (place_infos, (GCompareFunc) sort_places_func); - - for (l = place_infos; l != NULL; l = l->next) { - PlaceInfo *info = (PlaceInfo *) l->data; - - cat_iter = add_place (sidebar, - info->place_type, - info->section_type, - info->name, - info->icon_name, - info->uri, - info->drive, - info->volume, - info->mount, - info->index, - info->tooltip, - info->df_percent, - info->show_df_percent, - cat_iter); - - free_place_info (info); - } - - g_list_free (place_infos); - - /* network */ - cat_iter = add_heading (sidebar, SECTION_NETWORK, - _("Network")); - - network_volumes = g_list_reverse (network_volumes); - for (l = network_volumes; l != NULL; l = l->next) { - volume = l->data; - mount = g_volume_get_mount (volume); - - if (mount != NULL) { - network_mounts = g_list_prepend (network_mounts, mount); - continue; - } else { - icon = nemo_get_volume_icon_name (volume); - name = g_volume_get_name (volume); - tooltip = g_strdup_printf (_("Mount and open %s"), name); - - cat_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, - SECTION_NETWORK, - name, icon, NULL, - NULL, volume, NULL, 0, tooltip, 0, FALSE, - cat_iter); - g_free (icon); - g_free (name); - g_free (tooltip); - } - } - - g_list_free_full (network_volumes, g_object_unref); - - network_mounts = g_list_reverse (network_mounts); - for (l = network_mounts; l != NULL; l = l->next) { - mount = l->data; - root = g_mount_get_default_location (mount); - icon = nemo_get_mount_icon_name (mount); - mount_uri = g_file_get_uri (root); - name = g_mount_get_name (mount); - tooltip = g_file_get_parse_name (root); - cat_iter = add_place (sidebar, PLACES_MOUNTED_VOLUME, - SECTION_NETWORK, - name, icon, mount_uri, - NULL, NULL, mount, 0, tooltip, 0, FALSE, - cat_iter); - g_object_unref (root); - g_free (icon); - g_free (name); - g_free (mount_uri); - g_free (tooltip); - } - - g_list_free_full (network_mounts, g_object_unref); - - /* network:// */ - mount_uri = (char *)"network:///"; /* No need to strdup */ - icon = NEMO_ICON_SYMBOLIC_NETWORK; - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_NETWORK, - _("Network"), icon, - mount_uri, NULL, NULL, NULL, 0, - _("Browse the contents of the network"), 0, FALSE, - cat_iter); - - /* restore selection */ - restore_expand_state (sidebar); - sidebar_update_restore_selection (sidebar, location, last_uri); - - actions_changed (sidebar); - - sidebar->updating_sidebar = FALSE; - - g_free (location); - g_free (last_uri); -} - -static void -mount_added_callback (GVolumeMonitor *volume_monitor, - GMount *mount, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -mount_removed_callback (GVolumeMonitor *volume_monitor, - GMount *mount, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -mount_changed_callback (GVolumeMonitor *volume_monitor, - GMount *mount, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -volume_added_callback (GVolumeMonitor *volume_monitor, - GVolume *volume, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -volume_removed_callback (GVolumeMonitor *volume_monitor, - GVolume *volume, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -volume_changed_callback (GVolumeMonitor *volume_monitor, - GVolume *volume, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -drive_disconnected_callback (GVolumeMonitor *volume_monitor, - GDrive *drive, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -drive_connected_callback (GVolumeMonitor *volume_monitor, - GDrive *drive, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static void -drive_changed_callback (GVolumeMonitor *volume_monitor, - GDrive *drive, - NemoPlacesTreeSidebar *sidebar) -{ - update_places_on_idle (sidebar); -} - -static gboolean -over_eject_button (NemoPlacesTreeSidebar *sidebar, - gint x, - gint y, - GtkTreePath **path) -{ - GtkTreeViewColumn *column; - int width, col_x_offset, cell_x_offset; - gboolean show_eject; - GtkTreeIter iter; - GtkTreeModel *model; - - *path = NULL; - model = gtk_tree_view_get_model (sidebar->tree_view); - - if (gtk_tree_view_get_path_at_pos (sidebar->tree_view, - x, y, - path, &column, &col_x_offset, NULL)) { - - gtk_tree_model_get_iter (model, &iter, *path); - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_EJECT, &show_eject, - -1); - - if (!show_eject) { - goto out; - } - - if (column == sidebar->eject_column) { - gtk_tree_view_column_cell_set_cell_data (column, model, &iter, FALSE, FALSE); - - gtk_tree_view_column_cell_get_position (column, - sidebar->eject_icon_cell_renderer, - &cell_x_offset, &width); - if ((col_x_offset >= cell_x_offset) && (col_x_offset < cell_x_offset + width)) { - return TRUE; - } - } - } - - out: - if (*path != NULL) { - gtk_tree_path_free (*path); - *path = NULL; - } - - return FALSE; -} - -static gboolean -clicked_eject_button (NemoPlacesTreeSidebar *sidebar, - GtkTreePath **path) -{ - GdkEvent *event; - - event = gtk_get_current_event (); - - if (event) { - GdkEventButton *button_event = (GdkEventButton *) event; - if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) && - over_eject_button (sidebar, button_event->x, button_event->y, path)) { - return TRUE; - } - } - - return FALSE; -} - -static void -desktop_setting_changed_callback (gpointer user_data) -{ - NemoPlacesTreeSidebar *sidebar; - - sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - update_places (sidebar); -} - - -static gboolean -find_uri_recursive (GtkTreeModel *model, - GtkTreeIter *parent, /* NULL = Top-Level */ - const char *location, - GtkTreePath **out_path, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter child; - gboolean valid_child; - gchar *uri = NULL; - gchar *tree_name = NULL; - - /* iterate children of `parent` (or top-level when parent == NULL) */ - valid_child = gtk_tree_model_iter_children (model, &child, parent); - while (valid_child) { - gtk_tree_model_get (model, &child, - PLACES_SIDEBAR_COLUMN_URI, &uri, - PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_name, - -1); - - /* Lazy-Loading / "(loading)"-Dummy prüfen */ - if (tree_name && g_strcmp0(tree_name, "(loading)") == 0) { - if (uri) { - /* -> Direkt echte Kinder laden */ - GtkTreeIter store_iter; - gtk_tree_model_filter_convert_iter_to_child_iter( - GTK_TREE_MODEL_FILTER(sidebar->store_filter), - &store_iter, - &child - ); - - /* Lädt synchron echte Kinder in sidebar->store */ - add_directory_children(sidebar, &store_iter, uri); - - /* Jetzt kann die Kindliste aktualisiert werden */ - g_free(tree_name); - g_free(uri); - uri = NULL; - tree_name = NULL; - - /* Neu starten der Kinderliste für dieses Parent */ - valid_child = gtk_tree_model_iter_children (model, &child, parent); - continue; - } - } - - /* Treffer gefunden? */ - if (uri && g_strcmp0(uri, location) == 0) { - *out_path = gtk_tree_model_get_path(model, &child); - g_free(tree_name); - g_free(uri); - return TRUE; - } - - /* Rekursiv tiefer gehen */ - GtkTreePath *subpath = NULL; - if (find_uri_recursive(model, &child, location, &subpath, sidebar)) { - *out_path = subpath; - g_free(tree_name); - g_free(uri); - return TRUE; - } - - /* Aufräumen */ - g_free(tree_name); - g_free(uri); - uri = NULL; - tree_name = NULL; - - valid_child = gtk_tree_model_iter_next(model, &child); - } - - return FALSE; -} - -static void -loading_uri_callback (NemoWindow *window, - char *location, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeSelection *selection; - GtkTreePath *path = NULL; - - /* if location changes */ - if (g_strcmp0 (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); - - GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); - - /* 🔍 search for uri recursiv in tree */ - if (find_uri_recursive(model, NULL, location, &path, sidebar)) { - - /* Nur Elternpfade expandieren (nicht den Zielknoten selbst) */ - GtkTreePath *parent_path = gtk_tree_path_copy(path); - if (gtk_tree_path_get_depth(parent_path) > 1) { - gtk_tree_path_up(parent_path); - gtk_tree_view_expand_to_path(sidebar->tree_view, parent_path); - } - - /* select target node */ - gtk_tree_selection_select_path(selection, path); - - /* scroll to target node */ - gtk_tree_view_scroll_to_cell(sidebar->tree_view, path, NULL, FALSE, 0.5, 0.5); - - gtk_tree_path_free(parent_path); - gtk_tree_path_free(path); - } - } -} - -typedef struct { - NemoPlacesTreeSidebar *sidebar; - GdkRectangle rect; - SectionType section_type; -} CategoryExpandPayload; - -static gboolean -pointer_is_still_in_cell (gint x, - gint y, - GdkRectangle rect) -{ - gint max_x = rect.x + rect.width; - gint max_y = rect.y + rect.height; - if ((x >= rect.x && x <= max_x) && - (y >= rect.y && y <= max_y)) { - return TRUE; - } else { - return FALSE; - } -} - -static gboolean -maybe_expand_category (gpointer data) -{ - CategoryExpandPayload *payload = (CategoryExpandPayload *) data; - GdkDeviceManager *manager; - GdkDevice *pointer; - GdkWindow *window; - int x, y; - g_assert (GTK_IS_WIDGET (payload->sidebar->tree_view)); - window = gtk_widget_get_window (GTK_WIDGET (payload->sidebar->tree_view)); - - manager = gdk_display_get_device_manager (gtk_widget_get_display (GTK_WIDGET (payload->sidebar->tree_view))); - pointer = gdk_device_manager_get_client_pointer (manager); - gdk_window_get_device_position (window, pointer, - &x, &y, NULL); - if (pointer_is_still_in_cell (x, y, payload->rect)) { - expand_or_collapse_category (payload->sidebar, payload->section_type, TRUE); - } - - g_source_remove (payload->sidebar->expand_timeout_source); - payload->sidebar->expand_timeout_source = 0; - return FALSE; -} - - -static gboolean -cat_is_expanded (NemoPlacesTreeSidebar *sidebar, - SectionType section_type) -{ - switch (section_type) { - case SECTION_COMPUTER: - return sidebar->my_computer_expanded; - case SECTION_BOOKMARKS: - return sidebar->bookmarks_expanded; - case SECTION_DEVICES: - return sidebar->devices_expanded; - case SECTION_NETWORK: - return sidebar->network_expanded; - case SECTION_XDG_BOOKMARKS: - default: - return TRUE; - } -} - - -static GtkTreeViewDropPosition -get_drag_type (NemoPlacesTreeSidebar *sidebar, - gchar *drop_target_uri, - GdkRectangle rect, - int y, - SectionType section_type) -{ - gint zone; - gint upper_bound = rect.y + (rect.height / 4); - gint lower_bound = rect.y + rect.height - (rect.height / 4); - - if (y <= upper_bound) { - zone = POSITION_UPPER; - } else if (y > upper_bound && y < lower_bound) { - return GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; - } else { - zone = POSITION_LOWER; - } - - if (g_strcmp0 (drop_target_uri, sidebar->top_bookend_uri) == 0 && - zone == POSITION_LOWER) { - return GTK_TREE_VIEW_DROP_AFTER; - } else if (g_strcmp0 (drop_target_uri, sidebar->bottom_bookend_uri) == 0 && - zone == POSITION_UPPER) { - return GTK_TREE_VIEW_DROP_BEFORE; - } - - if ((section_type == SECTION_XDG_BOOKMARKS || section_type == SECTION_BOOKMARKS) - && zone == POSITION_UPPER) { - return GTK_TREE_VIEW_DROP_BEFORE; - } else if ((section_type == SECTION_XDG_BOOKMARKS || section_type == SECTION_BOOKMARKS) - && zone == POSITION_LOWER) { - return GTK_TREE_VIEW_DROP_AFTER; - } else { - /* or else you want to drag items INTO the existing bookmarks */ - return GTK_TREE_VIEW_DROP_INTO_OR_BEFORE; - } -} - - -/* Computes the appropriate row and position for dropping */ -static gboolean -compute_drop_position (GtkTreeView *tree_view, - int x, - int y, - GtkTreePath **path, - GtkTreeViewDropPosition *pos, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeModel *model; - GtkTreeIter iter; - PlaceType place_type; - SectionType section_type; - gchar *drop_target_uri = NULL; - - if (!gtk_tree_view_get_dest_row_at_pos (tree_view, - x, y, - path, pos)) { - return FALSE; - } - model = gtk_tree_view_get_model (tree_view); - - 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, - PLACES_SIDEBAR_COLUMN_URI, &drop_target_uri, - -1); - if (!cat_is_expanded (sidebar, section_type) && place_type == PLACES_HEADING) { - if (sidebar->expand_timeout_source > 0) { - goto fail; - } - CategoryExpandPayload *payload; - GtkTreeViewColumn *column; - column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 2); - payload = g_new0 (CategoryExpandPayload, 1); - payload->sidebar = sidebar; - gtk_tree_view_get_cell_area (tree_view, - *path, - column, - &payload->rect); - payload->section_type = section_type; - sidebar->expand_timeout_source = g_timeout_add_full (G_PRIORITY_DEFAULT, - DRAG_EXPAND_CATEGORY_DELAY, - (GSourceFunc) maybe_expand_category, - payload, - (GDestroyNotify) g_free); - goto fail; - } else if (place_type == PLACES_HEADING) { - if (section_type == SECTION_BOOKMARKS && - (int)nemo_bookmark_list_length (sidebar->bookmarks) == sidebar->bookmark_breakpoint) { - *pos = GTK_TREE_VIEW_DROP_AFTER; - g_free (drop_target_uri); - return TRUE; - } else { - goto fail; - } - } - - if (section_type != SECTION_XDG_BOOKMARKS && - section_type != SECTION_BOOKMARKS && - sidebar->drag_data_received && - sidebar->drag_data_info == GTK_TREE_MODEL_ROW && - g_strcmp0 (drop_target_uri, sidebar->top_bookend_uri) != 0) { - /* don't allow dropping bookmarks into non-bookmark areas */ - - goto fail; - } - - if (g_strcmp0 (drop_target_uri, "recent:///") == 0) { - goto fail; - } - - GdkRectangle rect; - GtkTreeViewColumn *column; - column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 1); - gtk_tree_view_get_cell_area (tree_view, - *path, - column, - &rect); - - *pos = get_drag_type (sidebar, drop_target_uri, rect, y, section_type); - - if (*pos != GTK_TREE_VIEW_DROP_BEFORE && - sidebar->drag_data_received && - sidebar->drag_data_info == GTK_TREE_MODEL_ROW) { - /* bookmark rows are never dragged into other bookmark rows */ - *pos = GTK_TREE_VIEW_DROP_AFTER; - } - - g_free (drop_target_uri); - return TRUE; - -fail: - g_free (drop_target_uri); - gtk_tree_path_free (*path); - *path = NULL; - return FALSE; -} - -static gboolean -get_drag_data (GtkTreeView *tree_view, - GdkDragContext *context, - unsigned int time) -{ - GdkAtom target; - - target = gtk_drag_dest_find_target (GTK_WIDGET (tree_view), - context, - NULL); - - if (target == GDK_NONE) { - return FALSE; - } - - gtk_drag_get_data (GTK_WIDGET (tree_view), - context, target, time); - - return TRUE; -} - -static void -free_drag_data (NemoPlacesTreeSidebar *sidebar) -{ - sidebar->drag_data_received = FALSE; - - if (sidebar->drag_list) { - nemo_drag_destroy_selection_list (sidebar->drag_list); - sidebar->drag_list = NULL; - } - - g_clear_pointer (&sidebar->desktop_dnd_source_fs, g_free); - sidebar->desktop_dnd_can_delete_source = FALSE; -} - -static gboolean -can_accept_file_as_bookmark (NemoFile *file) -{ - return (nemo_file_is_directory (file) && - !is_built_in_bookmark (file)); -} - -static gboolean -can_accept_items_as_bookmarks (const GList *items) -{ - int max; - char *uri; - NemoFile *file; - - /* Iterate through selection checking if item will get accepted as a bookmark. - * If more than 100 items selected, return an over-optimistic result. - */ - for (max = 100; items != NULL && max >= 0; items = items->next, max--) { - uri = ((NemoDragSelectionItem *)items->data)->uri; - file = nemo_file_get_by_uri (uri); - if (!can_accept_file_as_bookmark (file)) { - nemo_file_unref (file); - return FALSE; - } - nemo_file_unref (file); - } - - return TRUE; -} - -static gboolean -drag_motion_callback (GtkTreeView *tree_view, - GdkDragContext *context, - int x, - int y, - unsigned int time, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreePath *path = NULL; - GtkTreeViewDropPosition pos; - int action = 0; - GtkTreeIter iter; - char *uri = NULL; - gboolean res; - - if (!sidebar->drag_data_received) { - if (!get_drag_data (tree_view, context, time)) { - return FALSE; - } - } - - if (!sidebar->in_drag) { - sidebar->in_drag = TRUE; - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); - } - - path = NULL; - res = compute_drop_position (tree_view, x, y, &path, &pos, sidebar); - - if (!res) { - gtk_tree_view_set_drag_dest_row (tree_view, NULL, GTK_TREE_VIEW_DROP_BEFORE); - goto out; - } - - if (pos == GTK_TREE_VIEW_DROP_BEFORE || - pos == GTK_TREE_VIEW_DROP_AFTER ) { - if (sidebar->drag_data_received && - sidebar->drag_data_info == GTK_TREE_MODEL_ROW) { - action = GDK_ACTION_MOVE; - } else if (can_accept_items_as_bookmarks (sidebar->drag_list)) { - action = GDK_ACTION_COPY; - } else { - action = 0; - } - } else { - if (sidebar->drag_list == NULL) { - action = 0; - } else { - gtk_tree_model_get_iter (GTK_TREE_MODEL (sidebar->store_filter), - &iter, path); - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), - &iter, - PLACES_SIDEBAR_COLUMN_URI, &uri, - -1); - nemo_drag_default_drop_action_for_icons (context, - uri, - sidebar->drag_list, - &action, - &sidebar->desktop_dnd_source_fs, - &sidebar->desktop_dnd_can_delete_source); - g_free (uri); - } - } - - if (action != 0) { - gtk_tree_view_set_drag_dest_row (tree_view, path, pos); - } - - if (path != NULL) { - gtk_tree_path_free (path); - } - - out: - g_signal_stop_emission_by_name (tree_view, "drag-motion"); - - if (action != 0) { - gdk_drag_status (context, action, time); - } else { - gdk_drag_status (context, 0, time); - } - - return TRUE; -} - -static void -drag_leave_callback (GtkTreeView *tree_view, - GdkDragContext *context, - unsigned int time, - NemoPlacesTreeSidebar *sidebar) -{ - free_drag_data (sidebar); - gtk_tree_view_set_drag_dest_row (tree_view, NULL, GTK_TREE_VIEW_DROP_BEFORE); - g_signal_stop_emission_by_name (tree_view, "drag-leave"); -} - -/* Parses a "text/uri-list" string and inserts its URIs as bookmarks */ -static void -bookmarks_drop_uris (NemoPlacesTreeSidebar *sidebar, - GtkSelectionData *selection_data, - int position, - SectionType section_type) -{ - NemoBookmark *bookmark; - NemoFile *file; - char *uri; - char **uris; - int i; - GFile *location; - - uris = gtk_selection_data_get_uris (selection_data); - if (!uris) - return; - - for (i = 0; uris[i]; i++) { - uri = uris[i]; - file = nemo_file_get_by_uri (uri); - - if (!can_accept_file_as_bookmark (file)) { - nemo_file_unref (file); - continue; - } - - uri = nemo_file_get_drop_target_uri (file); - location = g_file_new_for_uri (uri); - nemo_file_unref (file); - - bookmark = nemo_bookmark_new (location, NULL, NULL, NULL); - - if (!nemo_bookmark_list_contains (sidebar->bookmarks, bookmark)) { - if (position < sidebar->bookmark_breakpoint || - (position == sidebar->bookmark_breakpoint && (section_type == SECTION_XDG_BOOKMARKS || - section_type == SECTION_COMPUTER))) { - increment_bookmark_breakpoint (sidebar); - } - nemo_bookmark_list_insert_item (sidebar->bookmarks, bookmark, position++); - } - - g_object_unref (location); - g_object_unref (bookmark); - g_free (uri); - } - - g_strfreev (uris); -} - -static GList * -uri_list_from_selection (GList *selection) -{ - NemoDragSelectionItem *item; - GList *ret; - GList *l; - - ret = NULL; - for (l = selection; l != NULL; l = l->next) { - item = l->data; - ret = g_list_prepend (ret, item->uri); - } - - return g_list_reverse (ret); -} - -static GList* -build_selection_list (const char *data) -{ - NemoDragSelectionItem *item; - GList *result; - char **uris; - char *uri; - int i; - - uris = g_uri_list_extract_uris (data); - - result = NULL; - for (i = 0; uris[i]; i++) { - uri = uris[i]; - item = nemo_drag_selection_item_new (); - item->uri = g_strdup (uri); - item->got_icon_position = FALSE; - result = g_list_prepend (result, item); - } - - g_strfreev (uris); - - return g_list_reverse (result); -} - -static gboolean -get_selected_iter (NemoPlacesTreeSidebar *sidebar, - GtkTreeIter *iter) -{ - GtkTreeSelection *selection; - - selection = gtk_tree_view_get_selection (sidebar->tree_view); - - return gtk_tree_selection_get_selected (selection, NULL, iter); -} - -static void -update_bookmark_breakpoint (NemoPlacesTreeSidebar *sidebar, - SectionType old_type, - SectionType new_type) -{ - if (old_type != new_type) { - if (old_type == SECTION_XDG_BOOKMARKS && new_type != SECTION_COMPUTER) - decrement_bookmark_breakpoint (sidebar); - else if (old_type == SECTION_BOOKMARKS) - increment_bookmark_breakpoint (sidebar); - } -} - -/* Reorders the selected bookmark to the specified position */ -static void -reorder_bookmarks (NemoPlacesTreeSidebar *sidebar, - int new_position, - SectionType new_section_type) -{ - GtkTreeIter iter; - PlaceType type; - SectionType old_section_type; - int old_position; - - /* Get the selected path */ - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &old_section_type, - PLACES_SIDEBAR_COLUMN_INDEX, &old_position, - -1); - - if (type != PLACES_BOOKMARK || - old_position < 0 || - old_position >= (int)nemo_bookmark_list_length (sidebar->bookmarks)) { - return; - } - - update_bookmark_breakpoint (sidebar, old_section_type, new_section_type); - - nemo_bookmark_list_move_item (sidebar->bookmarks, old_position, - new_position); - - if (old_position == new_position) - update_places (sidebar); -} - -static gboolean -idle_hide_bookmarks (gpointer user_data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - if (sidebar->in_drag) { - sidebar->in_drag = FALSE; - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); - } - - return FALSE; -} - -/* Callback triggered when drag data is received on the sidebar */ -static void -drag_data_received_callback (GtkWidget *widget, - GdkDragContext *context, - int x, - int y, - GtkSelectionData *selection_data, - unsigned int info, - unsigned int time, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeView *tree_view = NULL; - GtkTreePath *tree_path = NULL; - GtkTreeViewDropPosition tree_pos; - GtkTreeIter iter; - int position = 0; - GtkTreeModel *model = NULL; - char *drop_uri = NULL; - GList *selection_list = NULL; - GList *uris = NULL; - PlaceType place_type; - SectionType section_type; - gboolean success = FALSE; - - tree_view = GTK_TREE_VIEW (widget); - - /* If this is the first time receiving drag data for this drag operation */ - if (!sidebar->drag_data_received) { - /* If the drag contains URIs and is of type TEXT_URI_LIST */ - if (gtk_selection_data_get_target (selection_data) != GDK_NONE && - info == TEXT_URI_LIST) { - /* Build a list of dragged items */ - sidebar->drag_list = build_selection_list ((const gchar *) gtk_selection_data_get_data (selection_data)); - } else { - sidebar->drag_list = NULL; - } - sidebar->drag_data_received = TRUE; - sidebar->drag_data_info = info; - } - - /* Stop further emission of the "drag-data-received" signal */ - g_signal_stop_emission_by_name (widget, "drag-data-received"); - - /* If no drop has occurred yet, do nothing further */ - if (!sidebar->drop_occured) { - return; - } - - /* Compute the drop position in the tree view */ - success = compute_drop_position (tree_view, x, y, &tree_path, &tree_pos, sidebar); - if (!success) { - goto out; - } - - success = FALSE; - - /* Handle drops on specific tree rows */ - if (tree_pos == GTK_TREE_VIEW_DROP_BEFORE || - tree_pos == GTK_TREE_VIEW_DROP_AFTER) { - model = gtk_tree_view_get_model (tree_view); - if (!gtk_tree_model_get_iter (model, &iter, tree_path)) { - goto out; - } - - /* Extract information about the target row */ - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, - PLACES_SIDEBAR_COLUMN_INDEX, &position, - -1); - - /* Adjust insertion index based on section type and drop position */ - if (section_type == SECTION_COMPUTER && tree_pos == GTK_TREE_VIEW_DROP_BEFORE) { - position = nemo_bookmark_list_length(sidebar->bookmarks); - } else if (section_type == SECTION_BOOKMARKS && position == -1) { - /* Check for (temporarily) visible Bookmarks heading, only drop-reactive when - * it has no children, so we can assume that (bookmark_breakpoint == bookmark_count) - */ - position = nemo_bookmark_list_length (sidebar->bookmarks); - } else if (tree_pos == GTK_TREE_VIEW_DROP_AFTER && - (section_type == SECTION_XDG_BOOKMARKS || section_type == SECTION_BOOKMARKS)) { - position++; - } - - /* Handle different types of drag data */ - switch (info) { - case TEXT_URI_LIST: - bookmarks_drop_uris (sidebar, selection_data, position, section_type); - success = TRUE; - break; - case GTK_TREE_MODEL_ROW: - reorder_bookmarks (sidebar, position, section_type); - success = TRUE; - break; - default: - g_assert_not_reached (); - break; - } - } else { - GdkDragAction real_action; - - /* file transfer requested */ - /* Handle drag data targeting folders or special areas (not rows) */ - real_action = gdk_drag_context_get_selected_action (context); - - if (real_action == GDK_ACTION_ASK) { - real_action = - nemo_drag_drop_action_ask (GTK_WIDGET (tree_view), - gdk_drag_context_get_actions (context)); - } - - if (real_action > 0) { - model = gtk_tree_view_get_model (tree_view); - - gtk_tree_model_get_iter (model, &iter, tree_path); - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_URI, &drop_uri, - -1); - - switch (info) { - case TEXT_URI_LIST: - /* Handle file/folder copy or "favorite" marking */ - selection_list = build_selection_list ((const gchar *) gtk_selection_data_get_data (selection_data)); - uris = uri_list_from_selection (selection_list); - - GList *l; - - if (g_strcmp0 (drop_uri, "favorites:///") == 0) { - /* Mark dragged items as favorites */ - for (l = uris; l != NULL; l = l->next) { - gchar *uri = (gchar *) l->data; - NemoFile *source_file = nemo_file_get_by_uri (uri); - nemo_file_set_is_favorite (source_file, TRUE); - nemo_file_unref (source_file); - } - } else { - /* Perform copy/move operations for other targets */ - nemo_file_operations_copy_move (uris, NULL, drop_uri, - real_action, GTK_WIDGET (tree_view), - NULL, NULL); - } - - nemo_drag_destroy_selection_list (selection_list); - g_list_free (uris); - success = TRUE; - break; - case GTK_TREE_MODEL_ROW: - success = FALSE; - break; - default: - g_assert_not_reached (); - break; - } - - g_free (drop_uri); - } - } - -out: - /* Cleanup after drag operation */ - sidebar->drop_occured = FALSE; - free_drag_data (sidebar); - gtk_drag_finish (context, success, FALSE, time); - gtk_tree_path_free (tree_path); - - /* Schedule a delayed hiding of bookmarks panel if needed */ - g_timeout_add (250, (GSourceFunc) idle_hide_bookmarks, sidebar); -} - -/* Callback triggered when a drop occurs on the sidebar */ -static gboolean -drag_drop_callback (GtkTreeView *tree_view, - GdkDragContext *context, - int x, - int y, - unsigned int time, - NemoPlacesTreeSidebar *sidebar) -{ - gboolean retval = FALSE; - sidebar->drop_occured = TRUE; - retval = get_drag_data (tree_view, context, time); - /* Stop further emission of the "drag-drop" signal */ - g_signal_stop_emission_by_name (tree_view, "drag-drop"); - return retval; -} - -static void -check_unmount_and_eject (GMount *mount, - GVolume *volume, - GDrive *drive, - gboolean *show_unmount, - gboolean *show_eject) -{ - *show_unmount = FALSE; - *show_eject = FALSE; - - if (drive != NULL) { - *show_eject = g_drive_can_eject (drive); - } - - if (volume != NULL) { - *show_eject |= g_volume_can_eject (volume); - } - if (mount != NULL) { - *show_eject |= g_mount_can_eject (mount); - *show_unmount = g_mount_can_unmount (mount) && !*show_eject; - } -} - -static void -check_visibility (GMount *mount, - GVolume *volume, - GDrive *drive, - gboolean *show_mount, - gboolean *show_unmount, - gboolean *show_eject, - gboolean *show_rescan, - gboolean *show_start, - gboolean *show_stop) -{ - *show_mount = FALSE; - *show_rescan = FALSE; - *show_start = FALSE; - *show_stop = FALSE; - - check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject); - - if (drive != NULL) { - if (g_drive_is_removable (drive) && - !g_drive_is_media_check_automatic (drive) && - g_drive_can_poll_for_media (drive)) - *show_rescan = TRUE; - - *show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive); - *show_stop = g_drive_can_stop (drive); - - if (*show_stop) - *show_unmount = FALSE; - } - - if (volume != NULL) { - if (mount == NULL) - *show_mount = g_volume_can_mount (volume); - } -} - -static void -set_action_visible (GtkActionGroup *action_group, - const gchar *name, - gboolean visible) -{ - GtkAction *action; - - action = gtk_action_group_get_action (action_group, name); - gtk_action_set_visible (action, visible); -} - -// Updates the state (enabled/disabled/visible) of menu items in the sidebar -static void -update_menu_states (NemoPlacesTreeSidebar *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; - } - - // Determine the currently selected item in the sidebar - 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); - } - - if (uri) { - NemoFile *file = nemo_file_get_by_uri (uri); - NemoFile *parent = nemo_file_get_parent (file); - - GList *selection = g_list_prepend (NULL, file); - - // Update action manager to reflect available actions for this selection - 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); - } - - // Show/hide bookmark-related actions - 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 ()); - - // Determine mount/eject/start/stop visibility for drives - check_visibility (mount, volume, drive, - &show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop); - - /* 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); - - /* 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); - } - - // Set visibility for volume actions - 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; - - start_action = gtk_action_group_get_action (sidebar->bookmark_action_group, NEMO_ACTION_START_VOLUME); - stop_action = gtk_action_group_get_action (sidebar->bookmark_action_group, NEMO_ACTION_STOP_VOLUME); - - gtk_action_set_label (start_action, _("_Start")); - gtk_action_set_label (stop_action, _("_Stop")); - if ((show_start || show_stop) && drive != NULL) { - switch (g_drive_get_start_stop_type (drive)) { - case G_DRIVE_START_STOP_TYPE_SHUTDOWN: - /* start() for type G_DRIVE_START_STOP_TYPE_SHUTDOWN is normally not used */ - gtk_action_set_label (start_action, _("_Power On")); - gtk_action_set_label (stop_action, _("_Safely Remove Drive")); - break; - case G_DRIVE_START_STOP_TYPE_NETWORK: - gtk_action_set_label (start_action, _("_Connect Drive")); - gtk_action_set_label (stop_action, _("_Disconnect Drive")); - break; - case G_DRIVE_START_STOP_TYPE_MULTIDISK: - gtk_action_set_label (start_action, _("_Start Multi-disk Device")); - gtk_action_set_label (stop_action, _("_Stop Multi-disk Device")); - break; - case G_DRIVE_START_STOP_TYPE_PASSWORD: - /* stop() for type G_DRIVE_START_STOP_TYPE_PASSWORD is normally not used */ - gtk_action_set_label (start_action, _("_Unlock Drive")); - gtk_action_set_label (stop_action, _("_Lock Drive")); - break; - - default: - case G_DRIVE_START_STOP_TYPE_UNKNOWN: - /* uses defaults set above */ - break; - } - } - - g_clear_object (&drive); - g_clear_object (&volume); - g_clear_object (&mount); -} - -/* Callback used when the selection in the shortcuts tree changes */ -static void -bookmarks_selection_changed_cb (GtkTreeSelection *selection, - NemoPlacesTreeSidebar *sidebar) -{ - update_menu_states (sidebar); -} - -static void -volume_mounted_cb (GVolume *volume, - gboolean success, - GObject *user_data) -{ - GMount *mount; - NemoPlacesTreeSidebar *sidebar; - GFile *location; - - sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - sidebar->mounting = FALSE; - - mount = g_volume_get_mount (volume); - if (mount != NULL) { - location = g_mount_get_default_location (mount); - - if (sidebar->go_to_after_mount_slot != NULL) { - if ((sidebar->go_to_after_mount_flags & NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) { - nemo_window_slot_open_location (sidebar->go_to_after_mount_slot, location, - sidebar->go_to_after_mount_flags); - } else { - NemoWindow *new, *cur; - - cur = NEMO_WINDOW (sidebar->window); - new = nemo_application_create_window (nemo_application_get_singleton (), - gtk_window_get_screen (GTK_WINDOW (cur))); - nemo_window_go_to (new, location); - } - } - - g_object_unref (G_OBJECT (location)); - g_object_unref (G_OBJECT (mount)); - } - - 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; - } -} - -static void -drive_start_from_bookmark_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - char *primary; - char *name; - - error = NULL; - if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { - if (error->code != G_IO_ERROR_FAILED_HANDLED) { - name = g_drive_get_name (G_DRIVE (source_object)); - primary = g_strdup_printf (_("Unable to start %s"), name); - g_free (name); - eel_show_error_dialog (primary, - error->message, - NULL); - g_free (primary); - } - g_error_free (error); - } -} - -static void -open_selected_bookmark (NemoPlacesTreeSidebar *sidebar, - GtkTreeModel *model, - GtkTreeIter *iter, - NemoWindowOpenFlags flags) -{ - NemoWindowSlot *slot; - GFile *location; - char *uri; - - if (!iter) { - return; - } - - gtk_tree_model_get (model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); - - if (uri != NULL) { - DEBUG ("Activating bookmark %s", uri); - - location = g_file_new_for_uri (uri); - /* Navigate to the clicked location */ - if ((flags & NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW) == 0) { - slot = nemo_window_get_active_slot (sidebar->window); - nemo_window_slot_open_location (slot, location, flags); - } else { - NemoWindow *cur, *new; - - cur = NEMO_WINDOW (sidebar->window); - new = nemo_application_create_window (nemo_application_get_singleton (), - gtk_window_get_screen (GTK_WINDOW (cur))); - nemo_window_go_to (new, location); - } - g_object_unref (location); - g_free (uri); - - } else { - GDrive *drive; - GVolume *volume; - NemoWindowSlot *slt; - - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - PLACES_SIDEBAR_COLUMN_VOLUME, &volume, - -1); - - if (volume != NULL && !sidebar->mounting) { - sidebar->mounting = TRUE; - - g_assert (sidebar->go_to_after_mount_slot == NULL); - - slt = nemo_window_get_active_slot (sidebar->window); - sidebar->go_to_after_mount_slot = slt; - g_object_add_weak_pointer (G_OBJECT (sidebar->go_to_after_mount_slot), - (gpointer *) &sidebar->go_to_after_mount_slot); - - sidebar->go_to_after_mount_flags = flags | NEMO_WINDOW_OPEN_FLAG_MOUNT; - - nemo_file_operations_mount_volume_full (NULL, volume, - volume_mounted_cb, - G_OBJECT (sidebar)); - } else if (volume == NULL && drive != NULL && - (g_drive_can_start (drive) || g_drive_can_start_degraded (drive))) { - GMountOperation *mount_op; - - mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); - g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_from_bookmark_cb, NULL); - g_object_unref (mount_op); - } - - if (drive != NULL) - g_object_unref (drive); - if (volume != NULL) - g_object_unref (volume); - } -} - -static void -open_shortcut_from_menu (NemoPlacesTreeSidebar *sidebar, - NemoWindowOpenFlags flags) -{ - GtkTreeModel *model; - GtkTreeIter iter; - GtkTreePath *path = NULL; - - model = gtk_tree_view_get_model (sidebar->tree_view); - gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); - - if (path != NULL && gtk_tree_model_get_iter (model, &iter, path)) { - open_selected_bookmark (sidebar, model, &iter, flags); - } - - gtk_tree_path_free (path); -} - -static void -open_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - open_shortcut_from_menu (sidebar, 0); -} - -static void -open_shortcut_in_new_window_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - open_shortcut_from_menu (sidebar, NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW); -} - -static void -open_shortcut_in_new_tab_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - open_shortcut_from_menu (sidebar, NEMO_WINDOW_OPEN_FLAG_NEW_TAB); -} - -/* Add bookmark for the selected item */ -static void -add_bookmark (NemoPlacesTreeSidebar *sidebar) -{ - 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); - - if (uri == NULL) { - return; - } - - location = g_file_new_for_uri (uri); - bookmark = nemo_bookmark_new (location, NULL, NULL, NULL); - - if (!nemo_bookmark_list_contains (sidebar->bookmarks, bookmark)) { - nemo_bookmark_list_append (sidebar->bookmarks, bookmark); - } - - g_object_unref (location); - g_object_unref (bookmark); - g_free (uri); - } -} - -static void -add_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - add_bookmark (sidebar); -} - -/* Rename the selected bookmark */ -static void -rename_selected_bookmark (NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GtkTreePath *path; - GtkTreeViewColumn *column; - PlaceType type; - - if (get_selected_iter (sidebar, &iter)) { - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); - - if (type != PLACES_BOOKMARK) { - 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); - 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); - gtk_tree_path_free (path); - } -} - -static void -rename_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - rename_selected_bookmark (sidebar); -} - -/* Removes the selected bookmarks */ -static void -remove_selected_bookmarks (NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - PlaceType type; - int index; - - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); - - if (type != PLACES_BOOKMARK) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_INDEX, &index, - -1); - - if (index < sidebar->bookmark_breakpoint) - decrement_bookmark_breakpoint (sidebar); - - nemo_bookmark_list_delete_item_at (sidebar->bookmarks, index); -} - -static void -remove_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - remove_selected_bookmarks (sidebar); -} - -static void -mount_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GVolume *volume; - - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_VOLUME, &volume, - -1); - - if (volume != NULL) { - nemo_file_operations_mount_volume (NULL, volume); - g_object_unref (volume); - } -} - -static void -unmount_done (gpointer data) -{ - NemoWindow *window; - - window = data; - g_object_unref (window); -} - -static void -show_unmount_progress_cb (GMountOperation *op, - const gchar *message, - gint64 time_left, - gint64 bytes_left, - gpointer user_data) -{ - NemoApplication *app = NEMO_APPLICATION (g_application_get_default ()); - - if (bytes_left == 0) { - nemo_application_notify_unmount_done (app, message); - } else { - nemo_application_notify_unmount_show (app, message); - } -} - -static void -show_unmount_progress_aborted_cb (GMountOperation *op, - gpointer user_data) -{ - NemoApplication *app = NEMO_APPLICATION (g_application_get_default ()); - nemo_application_notify_unmount_done (app, NULL); -} - -static GMountOperation * -get_unmount_operation (NemoPlacesTreeSidebar *sidebar) -{ - GMountOperation *mount_op; - - mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); - g_signal_connect (mount_op, "show-unmount-progress", - G_CALLBACK (show_unmount_progress_cb), sidebar); - g_signal_connect (mount_op, "aborted", - G_CALLBACK (show_unmount_progress_aborted_cb), sidebar); - - return mount_op; -} - -static void -do_unmount (GMount *mount, - NemoPlacesTreeSidebar *sidebar) -{ - GMountOperation *mount_op; - - if (mount != NULL) { - mount_op = get_unmount_operation (sidebar); - nemo_file_operations_unmount_mount_full (NULL, mount, mount_op, FALSE, TRUE, - unmount_done, - g_object_ref (sidebar->window)); - g_object_unref (mount_op); - } -} - -static void -do_unmount_selection (NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GMount *mount; - - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_MOUNT, &mount, - -1); - - if (mount != NULL) { - do_unmount (mount, sidebar); - g_object_unref (mount); - } -} - -static void -unmount_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - do_unmount_selection (sidebar); -} - -static void -handle_mount_unmount_failure (const gchar *primary, - GError *error) -{ - const gchar *message = NULL; - - if (error && error->code == G_IO_ERROR_FAILED_HANDLED) { - return; - } - - if (error) { - message = error->message; - } - - eel_show_error_dialog (primary, - message, - NULL); -} - -static void -drive_eject_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - NemoWindow *window; - GError *error; - - window = user_data; - g_object_unref (window); - - error = NULL; - if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error)) { - char *name, *primary; - - name = g_drive_get_name (G_DRIVE (source_object)); - primary = g_strdup_printf (_("Unable to eject %s"), name); - - handle_mount_unmount_failure (primary, error); - - g_free (name); - g_free (primary); - g_clear_error (&error); - } -} - -static void -volume_eject_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - NemoWindow *window; - GError *error; - - window = user_data; - g_object_unref (window); - - error = NULL; - if (!g_volume_eject_with_operation_finish (G_VOLUME (source_object), res, &error)) { - char *name, *primary; - - name = g_volume_get_name (G_VOLUME (source_object)); - primary = g_strdup_printf (_("Unable to eject %s"), name); - - handle_mount_unmount_failure (primary, error); - - g_free (name); - g_free (primary); - g_clear_error (&error); - } -} - -static void -mount_eject_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - NemoWindow *window; - GError *error; - - window = user_data; - g_object_unref (window); - - error = NULL; - if (!g_mount_eject_with_operation_finish (G_MOUNT (source_object), res, &error)) { - char *name, *primary; - - name = g_mount_get_name (G_MOUNT (source_object)); - primary = g_strdup_printf (_("Unable to eject %s"), name); - - handle_mount_unmount_failure (primary, error); - - g_free (name); - g_free (primary); - g_clear_error (&error); - } -} - -static void -do_eject (GMount *mount, - GVolume *volume, - GDrive *drive, - NemoPlacesTreeSidebar *sidebar) -{ - GMountOperation *mount_op = get_unmount_operation (sidebar); - - if (mount != NULL) { - g_mount_eject_with_operation (mount, 0, mount_op, NULL, mount_eject_cb, - g_object_ref (sidebar->window)); - } else if (volume != NULL) { - g_volume_eject_with_operation (volume, 0, mount_op, NULL, volume_eject_cb, - g_object_ref (sidebar->window)); - } else if (drive != NULL) { - g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb, - g_object_ref (sidebar->window)); - } - g_object_unref (mount_op); -} - -static void -eject_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GMount *mount; - GVolume *volume; - GDrive *drive; - - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_MOUNT, &mount, - PLACES_SIDEBAR_COLUMN_VOLUME, &volume, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - -1); - - do_eject (mount, volume, drive, sidebar); - - g_clear_object (&mount); - g_clear_object (&volume); - g_clear_object (&drive); -} - -static gboolean -eject_or_unmount_bookmark (NemoPlacesTreeSidebar *sidebar, - GtkTreePath *path) -{ - GtkTreeModel *model; - GtkTreeIter iter; - gboolean can_unmount, can_eject; - GMount *mount; - GVolume *volume; - GDrive *drive; - gboolean ret; - - model = GTK_TREE_MODEL (sidebar->store_filter); - - if (!path) { - return FALSE; - } - if (!gtk_tree_model_get_iter (model, &iter, path)) { - return FALSE; - } - - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_MOUNT, &mount, - PLACES_SIDEBAR_COLUMN_VOLUME, &volume, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - -1); - - ret = FALSE; - - check_unmount_and_eject (mount, volume, drive, &can_unmount, &can_eject); - /* if we can eject, it has priority over unmount */ - if (can_eject) { - do_eject (mount, volume, drive, sidebar); - ret = TRUE; - } else if (can_unmount) { - do_unmount (mount, sidebar); - ret = TRUE; - } - - g_clear_object (&mount); - g_clear_object (&volume); - g_clear_object (&drive); - - return ret; -} - -static gboolean -eject_or_unmount_selection (NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GtkTreePath *path; - gboolean ret; - - if (!get_selected_iter (sidebar, &iter)) { - return FALSE; - } - - path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iter); - if (path == NULL) { - return FALSE; - } - - ret = eject_or_unmount_bookmark (sidebar, path); - - gtk_tree_path_free (path); - - return ret; -} - -static void -drive_poll_for_media_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - - error = NULL; - if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), res, &error)) { - char *name, *primary; - - name = g_drive_get_name (G_DRIVE (source_object)); - primary = g_strdup_printf (_("Unable to poll %s for media changes"), name); - - handle_mount_unmount_failure (primary, error); - - g_free (name); - g_free (primary); - g_clear_error (&error); - } -} - -static void -rescan_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GDrive *drive; - - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - -1); - - if (drive != NULL) { - g_drive_poll_for_media (drive, NULL, drive_poll_for_media_cb, NULL); - g_object_unref (drive); - } -} - -static void -drive_start_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - GError *error; - - error = NULL; - - if (!g_drive_start_finish (G_DRIVE (source_object), res, &error)) { - char *name, *primary; - - name = g_drive_get_name (G_DRIVE (source_object)); - primary = g_strdup_printf (_("Unable to start %s"), name); - - handle_mount_unmount_failure (primary, error); - - g_free (name); - g_free (primary); - g_clear_error (&error); - } -} - -static void -start_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GDrive *drive; - - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - -1); - - if (drive != NULL) { - GMountOperation *mount_op; - - mount_op = gtk_mount_operation_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)))); - - g_drive_start (drive, G_DRIVE_START_NONE, mount_op, NULL, drive_start_cb, NULL); - - g_object_unref (mount_op); - g_object_unref (drive); - } -} - -static void -drive_stop_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - NemoWindow *window; - GError *error; - - window = user_data; - g_object_unref (window); - - error = NULL; - - if (!g_drive_stop_finish (G_DRIVE (source_object), res, &error)) { - char *name, *primary; - - name = g_drive_get_name (G_DRIVE (source_object)); - primary = g_strdup_printf (_("Unable to stop %s"), name); - - handle_mount_unmount_failure (primary, error); - - g_free (name); - g_free (primary); - g_clear_error (&error); - } -} - -static void -stop_shortcut_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeIter iter; - GDrive *drive; - - if (!get_selected_iter (sidebar, &iter)) { - return; - } - - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - -1); - - if (drive != NULL) { - GMountOperation *mount_op = get_unmount_operation (sidebar); - g_drive_stop (drive, G_MOUNT_UNMOUNT_NONE, mount_op, NULL, drive_stop_cb, - g_object_ref (sidebar->window)); - g_object_unref (mount_op); - g_object_unref (drive); - } -} - -static void -empty_trash_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - nemo_file_operations_empty_trash (GTK_WIDGET (sidebar->window)); -} - -static gboolean -find_prev_or_next_row (NemoPlacesTreeSidebar *sidebar, - GtkTreeIter *iter, - gboolean go_up) -{ - GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); - gboolean res; - int place_type; - - if (go_up) { - res = gtk_tree_model_iter_previous (model, iter); - } else { - res = gtk_tree_model_iter_next (model, iter); - } - - if (res) { - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, - -1); - if (place_type == PLACES_HEADING) { - if (go_up) { - res = gtk_tree_model_iter_previous (model, iter); - } else { - res = gtk_tree_model_iter_next (model, iter); - } - } - } - - return res; -} - -static gboolean -find_prev_row (NemoPlacesTreeSidebar *sidebar, GtkTreeIter *iter) -{ - return find_prev_or_next_row (sidebar, iter, TRUE); -} - -static gboolean -find_next_row (NemoPlacesTreeSidebar *sidebar, GtkTreeIter *iter) -{ - return find_prev_or_next_row (sidebar, iter, FALSE); -} - -static void -properties_cb (GtkAction *item, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeModel *model; - GtkTreePath *path = NULL; - GtkTreeIter iter; - GList *list; - NemoFile *file; - char *uri; - - model = gtk_tree_view_get_model (sidebar->tree_view); - gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); - - if (path == NULL || !gtk_tree_model_get_iter (model, &iter, path)) { - gtk_tree_path_free (path); - return; - } - - gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); - - if (uri != NULL) { - - file = nemo_file_get_by_uri (uri); - list = g_list_prepend (NULL, nemo_file_ref (file)); - - nemo_properties_window_present (list, GTK_WIDGET (sidebar), NULL); - - nemo_file_list_free (list); - g_free (uri); - } - - gtk_tree_path_free (path); -} - -static gboolean -nemo_places_tree_sidebar_focus (GtkWidget *widget, - GtkDirectionType direction) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (widget); - GtkTreePath *path; - GtkTreeIter iter, child_iter; - gboolean res; - - 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); - res = find_next_row (sidebar, &child_iter); - if (res) { - path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iter); - gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); - gtk_tree_path_free (path); - } - } - - return GTK_WIDGET_CLASS (nemo_places_tree_sidebar_parent_class)->focus (widget, direction); -} - -/* Handler for GtkWidget::key-press-event on the shortcuts list */ -static gboolean -bookmarks_key_press_event_cb (GtkWidget *widget, - GdkEventKey *event, - NemoPlacesTreeSidebar *sidebar) -{ - 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; - } - } - - 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; - } - - return FALSE; -} - -static void -run_action_callback (GtkAction *action, gpointer user_data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - gchar *uri = NULL; - GtkTreeIter iter; - - if (get_selected_iter (sidebar, &iter)) { - gtk_tree_model_get (GTK_TREE_MODEL (sidebar->store_filter), &iter, - PLACES_SIDEBAR_COLUMN_URI, &uri, - -1); - } - - if (!uri) { - return; - } - - NemoFile *file = nemo_file_get_by_uri (uri); - NemoFile *parent = nemo_file_get_parent (file); - GList *selection = g_list_prepend (NULL, file); - - nemo_action_activate (NEMO_ACTION (action), selection, parent, GTK_WINDOW (sidebar->window)); - - nemo_file_list_free (selection); - nemo_file_unref (parent); - - g_free (uri); -} - -#if GTK_CHECK_VERSION (3, 24, 8) -static void -moved_to_rect_cb (GdkWindow *window, - const GdkRectangle *flipped_rect, - const GdkRectangle *final_rect, - gboolean flipped_x, - gboolean flipped_y, - GtkMenu *menu) -{ - g_signal_emit_by_name (menu, - "popped-up", - 0, - flipped_rect, - final_rect, - flipped_x, - flipped_y); - - // Don't let the emission run in gtkmenu.c - g_signal_stop_emission_by_name (window, "moved-to-rect"); -} - -static void -popup_menu_realized (GtkWidget *menu, - gpointer user_data) -{ - GdkWindow *toplevel; - - toplevel = gtk_widget_get_window (gtk_widget_get_toplevel (menu)); - - g_signal_handlers_disconnect_by_func (toplevel, moved_to_rect_cb, menu); - - g_signal_connect (toplevel, "moved-to-rect", G_CALLBACK (moved_to_rect_cb), - menu); -} -#endif - -static void -bookmarks_popup_menu (NemoPlacesTreeSidebar *sidebar, - GdkEventButton *event) -{ - update_menu_states (sidebar); - eel_pop_up_context_menu (GTK_MENU(sidebar->popup_menu), - (GdkEvent *) event, - GTK_WIDGET (sidebar)); -} - -/* Callback used for the GtkWidget::popup-menu signal of the shortcuts list */ -static gboolean -bookmarks_popup_menu_cb (GtkWidget *widget, - NemoPlacesTreeSidebar *sidebar) -{ - bookmarks_popup_menu (sidebar, NULL); - return TRUE; -} - -static void -reset_menu (NemoPlacesTreeSidebar *sidebar) -{ - sidebar->actions_need_update = TRUE; - rebuild_menu (sidebar); -} - -static gboolean -actions_changed_idle_cb (gpointer user_data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - reset_menu (sidebar); - - sidebar->actions_changed_idle_id = 0; - return G_SOURCE_REMOVE; -} - -static void -actions_changed (gpointer user_data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - g_clear_handle_id (&sidebar->actions_changed_idle_id, g_source_remove); - sidebar->actions_changed_idle_id = g_idle_add (actions_changed_idle_cb, sidebar); -} - -static void -add_action_to_ui (NemoActionManager *manager, - GtkAction *action, - GtkUIManagerItemType type, - const gchar *path, - const gchar *accelerator, - gpointer user_data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - static const gchar *roots[] = { - "/selection/PlacesSidebarActionsPlaceholder", - NULL, - }; - - nemo_action_manager_add_action_ui (manager, - sidebar->ui_manager, - action, - path, - accelerator, - sidebar->action_action_group, - sidebar->action_action_group_merge_id, - roots, - type, - G_CALLBACK (run_action_callback), - sidebar); -} - -static void -clear_ui (NemoPlacesTreeSidebar *sidebar) -{ - - nemo_ui_unmerge_ui (sidebar->ui_manager, - &sidebar->bookmark_action_group_merge_id, - &sidebar->bookmark_action_group); - - nemo_ui_unmerge_ui (sidebar->ui_manager, - &sidebar->action_action_group_merge_id, - &sidebar->action_action_group); - -} - -static const GtkActionEntry bookmark_action_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_ADD_BOOKMARK, NULL, N_("_Add Bookmark"), NULL, NULL, G_CALLBACK (add_shortcut_cb) }, - { NEMO_ACTION_SIDEBAR_REMOVE, "list-remove-symbolic", N_("Remove"), NULL, NULL, G_CALLBACK (remove_shortcut_cb) }, - { NEMO_ACTION_RENAME, NULL, N_("_Rename..."), NULL, NULL, G_CALLBACK (rename_shortcut_cb) }, - { NEMO_ACTION_MOUNT_VOLUME, NULL, N_("_Mount"), NULL, NULL, G_CALLBACK (mount_shortcut_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_SIDEBAR_DETECT_MEDIA, NULL, N_("_Detect Media"), NULL, NULL, G_CALLBACK (rescan_shortcut_cb) }, - { NEMO_ACTION_START_VOLUME, NULL, N_("_Start"), NULL, NULL, G_CALLBACK (start_shortcut_cb) }, - { NEMO_ACTION_STOP_VOLUME, NULL, N_("_Stop"), NULL, NULL, G_CALLBACK (stop_shortcut_cb) }, - { NEMO_ACTION_EMPTY_TRASH_CONDITIONAL, NULL, N_("_Empty _Trash"), NULL, NULL, G_CALLBACK (empty_trash_cb) }, - { NEMO_ACTION_PROPERTIES, NULL, N_("_Properties"), NULL, NULL, G_CALLBACK (properties_cb) }, -}; - -static void -rebuild_menu (NemoPlacesTreeSidebar *sidebar) -{ - if (!gtk_widget_get_realized (GTK_WIDGET (sidebar))) { - return; - } - - if (!sidebar->actions_need_update) { - return; - } - - clear_ui (sidebar); - - nemo_ui_prepare_merge_ui (sidebar->ui_manager, - "NemoPlacesTreeSidebarBookmarkActions", - &sidebar->bookmark_action_group_merge_id, - &sidebar->bookmark_action_group); - - nemo_ui_prepare_merge_ui (sidebar->ui_manager, - "NemoPlacesTreeSidebarActionActions", - &sidebar->action_action_group_merge_id, - &sidebar->action_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); - - gtk_action_group_add_actions (sidebar->bookmark_action_group, - bookmark_action_entries, - G_N_ELEMENTS (bookmark_action_entries), - sidebar); - - nemo_action_manager_iterate_actions (sidebar->action_manager, - (NemoActionManagerIterFunc) add_action_to_ui, - sidebar); - - if (sidebar->popup_menu == NULL) { - GtkWidget *menu = gtk_ui_manager_get_widget (sidebar->ui_manager, "/selection"); - gtk_menu_set_screen (GTK_MENU (menu), gtk_widget_get_screen (GTK_WIDGET (sidebar->window))); - sidebar->popup_menu = menu; - -#if GTK_CHECK_VERSION (3, 24, 8) - g_signal_connect (sidebar->popup_menu, "realize", - G_CALLBACK (popup_menu_realized), - sidebar); - gtk_widget_realize (sidebar->popup_menu); -#endif - - gtk_widget_show (menu); - } - - sidebar->actions_need_update = FALSE; -} - -static gboolean -bookmarks_button_release_event_cb (GtkWidget *widget, - GdkEventButton *event, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreePath *path; - GtkTreeIter iter; - GtkTreeModel *model; - GtkTreeView *tree_view; - gboolean res; - - path = NULL; - - if (event->type != GDK_BUTTON_RELEASE) { - return TRUE; - } - - if (clicked_eject_button (sidebar, &path)) { - eject_or_unmount_bookmark (sidebar, path); - gtk_tree_path_free (path); - - return FALSE; - } - - tree_view = GTK_TREE_VIEW (widget); - model = gtk_tree_view_get_model (tree_view); - - if (event->button == 1) { - - if (event->window != gtk_tree_view_get_bin_window (tree_view)) { - return FALSE; - } - - res = gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, - &path, NULL, NULL, NULL); - - if (!res) { - return FALSE; - } - - gtk_tree_model_get_iter (model, &iter, path); - - open_selected_bookmark (sidebar, model, &iter, 0); - - gtk_tree_path_free (path); - } - - return FALSE; -} - -static gboolean -bookmarks_button_press_event_cb (GtkWidget *widget, - GdkEventButton *event, - NemoPlacesTreeSidebar *sidebar) - -{ - GtkTreeModel *model; - GtkTreeView *tree_view; - GtkTreeIter iter; - GtkTreePath *path = NULL; - gboolean retval = FALSE; - PlaceType row_type; - - if (event->type != GDK_BUTTON_PRESS) { - /* ignore multiple clicks */ - return TRUE; - } - - tree_view = GTK_TREE_VIEW (widget); - model = gtk_tree_view_get_model (tree_view); - gtk_tree_view_get_path_at_pos (tree_view, (int) event->x, (int) event->y, - &path, NULL, NULL, NULL); - - if (path == NULL || !gtk_tree_model_get_iter (model, &iter, path)) { - return FALSE; - } - - if (event->button == 3) { - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, - -1); - - if (row_type != PLACES_HEADING) { - bookmarks_popup_menu (sidebar, event); - } - } else if (event->button == 2) { - NemoWindowOpenFlags flags = 0; - - if (g_settings_get_boolean (nemo_preferences, - NEMO_PREFERENCES_ALWAYS_USE_BROWSER)) { - flags = (event->state & GDK_CONTROL_MASK) ? - NEMO_WINDOW_OPEN_FLAG_NEW_WINDOW : - NEMO_WINDOW_OPEN_FLAG_NEW_TAB; - } else { - flags = NEMO_WINDOW_OPEN_FLAG_CLOSE_BEHIND; - } - - open_selected_bookmark (sidebar, model, &iter, flags); - retval = TRUE; - } - - gtk_tree_path_free (path); - - return retval; -} - -typedef struct { - NemoPlacesTreeSidebar *sidebar; - GtkTreePath *hovered_path; -} ClearHoverData; - -static gboolean -clear_eject_hover (GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - ClearHoverData *hdata = data; - gint size; - gboolean can_eject = FALSE; - - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_EJECT, &can_eject, - -1); - - if (can_eject && hdata->hovered_path != NULL && gtk_tree_path_compare (path, hdata->hovered_path) == 0) { - size = EJECT_ICON_SIZE_HOVERED; - - } else { - size = EJECT_ICON_SIZE_NOT_HOVERED; - } - - gtk_tree_store_set (hdata->sidebar->store, iter, - PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, size, - -1); - - return FALSE; -} - -static gboolean -motion_notify_cb (GtkWidget *widget, - GdkEventMotion *event, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeModel *model; - GtkTreePath *path = NULL; - GtkTreePath *store_path = NULL; - gboolean editing; - - if (event->type != GDK_MOTION_NOTIFY) { - return TRUE; - } - - g_object_get (sidebar->editable_renderer, "editing", &editing, NULL); - if (editing) { - return GDK_EVENT_PROPAGATE; - } - - 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); - } - - ClearHoverData data = { sidebar, store_path }; - gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store), (GtkTreeModelForeachFunc) clear_eject_hover, &data); - - if (store_path != NULL) { - gtk_tree_path_free (store_path); - } - - gtk_tree_path_free (path); - - return FALSE; -} - -static gboolean -leave_notify_cb (GtkWidget *widget, - GdkEventCrossing *event, - NemoPlacesTreeSidebar *sidebar) -{ - gboolean editing; - - if (event->type != GDK_LEAVE_NOTIFY) { - return TRUE; - } - - g_object_get (sidebar->editable_renderer, "editing", &editing, NULL); - if (editing) { - return GDK_EVENT_PROPAGATE; - } - - ClearHoverData data = { sidebar, NULL }; - gtk_tree_model_foreach (GTK_TREE_MODEL (sidebar->store), (GtkTreeModelForeachFunc) clear_eject_hover, &data); - - return FALSE; -} - - -static gboolean -query_tooltip_callback (GtkWidget *widget, - gint x, - gint y, - gboolean kb_mode, - GtkTooltip *tooltip, - gpointer user_data) -{ - GtkTreeIter iter; - GtkTreePath *path = NULL; - GtkTreeModel *model; - - if (gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (widget), &x, &y, - kb_mode, - &model, &path, &iter)) { - gboolean can_eject; - gint icon_size, type; - - gtk_tree_model_get (model, - &iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - PLACES_SIDEBAR_COLUMN_EJECT, &can_eject, - // HACK: If we store a bool 'hovered' we still need to have an - // icon size column or else make a cell_data_func to render the icon - // manually. This is simpler. - PLACES_SIDEBAR_COLUMN_EJECT_ICON_SIZE, &icon_size, - -1); - - if (type == PLACES_HEADING) { - gtk_tree_path_free (path); - return FALSE; - } - - g_autofree gchar *tooltip_markup = NULL; - - if (can_eject && icon_size == EJECT_ICON_SIZE_HOVERED) { - g_autoptr(GMount) mount = NULL; - g_autoptr(GDrive) drive = NULL; - g_autoptr(GVolume) volume = NULL; - - gtk_tree_model_get (model, - &iter, - PLACES_SIDEBAR_COLUMN_MOUNT, &mount, - PLACES_SIDEBAR_COLUMN_DRIVE, &drive, - PLACES_SIDEBAR_COLUMN_VOLUME, &volume, - -1); - if (mount != NULL) { - tooltip_markup = g_strdup (_("Unmount")); - } - else - if (drive != NULL) { - tooltip_markup = g_strdup (_("Eject")); - } - else - if (volume != NULL) { - tooltip_markup = g_strdup (_("Stop")); - } - } else { - if (type == PLACES_TREE_FOLDER) { - return FALSE; - } - gtk_tree_model_get (model, - &iter, - PLACES_SIDEBAR_COLUMN_TOOLTIP, &tooltip_markup, - -1); - } - - gtk_tooltip_set_markup (tooltip, tooltip_markup); - gtk_tree_view_set_tooltip_cell (GTK_TREE_VIEW (widget), tooltip, path, NULL, NULL); - - gtk_tree_path_free (path); - return TRUE; - } - - return FALSE; -} - - -static void -update_expanded_state (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path, - gpointer user_data, - gboolean expanded) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - - if (sidebar->updating_sidebar) - return; - - SectionType type; - 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, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, &type, - -1); - - 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: - g_print("Unbekannter SectionType: %d\n", type); - return; - } -} - -static void -row_collapsed_cb (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path, - gpointer user_data) -{ - update_expanded_state (tree_view, - iter, - path, - user_data, - FALSE); -} - - -static void -row_expanded_cb (GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path, - gpointer user_data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR(user_data); - - GtkTreeModel *filter_model = gtk_tree_view_get_model(tree_view); // Filter - char *uri = NULL; - - gtk_tree_model_get(filter_model, iter, - PLACES_SIDEBAR_COLUMN_URI, &uri, - -1); - GtkTreeIter store_iter; - gtk_tree_model_filter_convert_iter_to_child_iter( - GTK_TREE_MODEL_FILTER(filter_model), - &store_iter, - iter - ); - // Store-Iter ist jetzt gültig für sidebar->store - GtkTreePath *copy_path = gtk_tree_model_get_path(filter_model, iter); - - if (uri) { - add_directory_children(sidebar, &store_iter, uri); - g_free(uri); - } - - gtk_tree_view_expand_row(tree_view, path, FALSE); - - GtkTreeIter fresh_iter; - if (gtk_tree_model_get_iter(filter_model, &fresh_iter, copy_path)) { - update_expanded_state(tree_view, &fresh_iter, copy_path, user_data, TRUE); - } - gtk_tree_path_free(copy_path); -} - -static void -row_activated_cb (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gpointer user_data) -{ - GtkTreeIter iter; - SectionType section_type; - PlaceType place_type; - - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (user_data); - GtkTreeModel *model = gtk_tree_view_get_model (tree_view); - gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, - -1); - if (place_type == PLACES_HEADING) { - if (section_type == SECTION_COMPUTER) { - sidebar->my_computer_expanded = !sidebar->my_computer_expanded; - } else if (section_type == SECTION_BOOKMARKS) { - sidebar->bookmarks_expanded = !sidebar->bookmarks_expanded; - } else if (section_type == SECTION_DEVICES) { - sidebar->devices_expanded = !sidebar->devices_expanded; - } else if (section_type == SECTION_NETWORK) { - sidebar->network_expanded = !sidebar->network_expanded; - } - restore_expand_state (sidebar); - } -} - -static void -bookmarks_edited (GtkCellRenderer *cell, - gchar *path_string, - gchar *new_text, - NemoPlacesTreeSidebar *sidebar) -{ - GtkTreePath *path; - GtkTreeIter iter; - NemoBookmark *bookmark; - int index; - - 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, - PLACES_SIDEBAR_COLUMN_INDEX, &index, - -1); - gtk_tree_path_free (path); - bookmark = nemo_bookmark_list_item_at (sidebar->bookmarks, index); - - if (bookmark != NULL) { - nemo_bookmark_set_custom_name (bookmark, new_text); - } -} - -static void -bookmarks_editing_canceled (GtkCellRenderer *cell, - NemoPlacesTreeSidebar *sidebar) -{ - g_object_set (cell, "editable", FALSE, NULL); -} - -static void -trash_state_changed_cb (NemoTrashMonitor *trash_monitor, - gboolean state, - gpointer data) -{ - NemoPlacesTreeSidebar *sidebar; - - sidebar = NEMO_PLACES_TREE_SIDEBAR (data); - - /* The trash icon changed, update the sidebar */ - update_places (sidebar); - - reset_menu (sidebar); -} - -static void -favorites_changed_cb (gpointer data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR (data); - - update_places (sidebar); -} - -static gboolean -tree_selection_func (GtkTreeSelection *selection, - GtkTreeModel *model, - GtkTreePath *path, - gboolean path_currently_selected, - gpointer user_data) -{ - GtkTreeIter iter; - PlaceType row_type; - - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, - -1); - - if (row_type == PLACES_HEADING) { - return FALSE; - } - - return TRUE; -} - -static void -icon_cell_renderer_func (GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - PlaceType type; - GIcon *gicon = NULL; - - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - PLACES_SIDEBAR_COLUMN_GICON, &gicon, - -1); - - if (type == PLACES_HEADING) { - g_object_set (cell, - "visible", FALSE, - NULL); - } else { - g_object_set (cell, - "visible", TRUE, - "gicon", gicon, - NULL); - } - - if (gicon) - g_object_unref(gicon); -} - -static void -padding_cell_renderer_func(GtkTreeViewColumn *column, - GtkCellRenderer *cell, - GtkTreeModel *model, - GtkTreeIter *iter, - gpointer user_data) -{ - GtkTreePath *path = gtk_tree_model_get_path(model, iter); - int depth = gtk_tree_path_get_depth(path); /* Root = 1 */ - gtk_tree_path_free(path); - - /* Minimaler Abstand zwischen Expander und Icon */ - 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 *tree_text = NULL; - gchar *name_text = NULL; - gchar *heading_text = NULL; - gint row_type = 0; - - /* Lese beide Text-Spalten + Row-Type */ - gtk_tree_model_get(model, iter, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, - PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_text, - PLACES_SIDEBAR_COLUMN_NAME, &name_text, - -1); - - if (row_type == PLACES_HEADING) { - /* Für Headings benutze HEADING_TEXT (bold) */ - g_free(tree_text); - 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 { - /* Normale Zeilen: bevorzugt TREE_NAME, fallback auf NAME */ - const gchar *display = ""; - if (tree_text != NULL && *tree_text != '\0') - display = tree_text; - else if (name_text != NULL && *name_text != '\0') - display = name_text; - - /* Standardgewicht */ - g_object_set(renderer, - "weight-set", FALSE, - NULL); - - g_object_set(renderer, "text", display, NULL); - - g_free(tree_text); - g_free(name_text); - } -} - - -static void -nemo_places_tree_sidebar_renderer_init(NemoPlacesTreeSidebar *sidebar, GtkTreeView *tree_view) -{ - GtkCellRenderer *cell; - GtkTreeViewColumn *primary_column; - - /* === Primäre Spalte === */ - 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); - - /* Entferne 'text' hier — wir setzen Text ausschließlich 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); - - /* Setze cell data func, die jetzt TREE_NAME und NAME berücksichtigt */ - 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); - - /* === Expander Column entfällt, GTK rendert automatisch in der ersten Spalte === */ -} - -static void -hidden_files_mode_changed_callback (NemoWindow *window, - NemoPlacesTreeSidebar *sidebar) -{ - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); -} - - -static gboolean -row_visibility_function (GtkTreeModel *model, - GtkTreeIter *iter, - gpointer data) -{ - NemoPlacesTreeSidebar *sidebar = NEMO_PLACES_TREE_SIDEBAR(data); - - SectionType section_type; - PlaceType type; - - gtk_tree_model_get (model, iter, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, - -1); - if (type == PLACES_TREE_FOLDER) { - NemoWindowShowHiddenFilesMode mode = nemo_window_get_hidden_files_mode (sidebar->window); - // --- 2. Versteckte Verzeichnisse ausblenden --- - if ((mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE)) return TRUE; - gchar *name = NULL; - gboolean visible = TRUE; - gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_TREE_NAME, &name, -1); - if (name && name[0] == '.') // beginnt mit Punkt → versteckt - visible = FALSE; - g_free(name); - return visible; - } - - // --- 1. Heading-Filter --- - if (type != PLACES_HEADING || section_type != SECTION_BOOKMARKS) - return TRUE; - - if (sidebar->in_drag) - return TRUE; - - if ((int)nemo_bookmark_list_length (sidebar->bookmarks) > sidebar->bookmark_breakpoint) - return TRUE; - - return FALSE; -} - - -static void -nemo_places_tree_sidebar_init (NemoPlacesTreeSidebar *sidebar) -{ - GtkTreeView *tree_view; - GtkTreeSelection *selection; - GtkStyleContext *style_context; - - sidebar->action_manager = nemo_action_manager_new (); - sidebar->actions_changed_id = g_signal_connect_swapped (sidebar->action_manager, - "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; - - sidebar->volume_monitor = g_volume_monitor_get (); - - sidebar->update_places_on_idle_id = 0; - - sidebar->bookmarks_changed_id = 0; - - sidebar-> hidden_files_changed_id = 0; - - 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, - NEMO_WINDOW_STATE_BOOKMARKS_EXPANDED); - sidebar->devices_expanded = g_settings_get_boolean (nemo_window_state, - NEMO_WINDOW_STATE_DEVICES_EXPANDED); - sidebar->network_expanded = g_settings_get_boolean (nemo_window_state, - NEMO_WINDOW_STATE_NETWORK_EXPANDED); - - gtk_widget_set_size_request (GTK_WIDGET (sidebar), 140, -1); - - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sidebar), - GTK_POLICY_NEVER, - GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); - gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (sidebar), NULL); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sidebar), GTK_SHADOW_IN); - - style_context = gtk_widget_get_style_context (GTK_WIDGET (sidebar)); - gtk_style_context_set_junction_sides (style_context, GTK_JUNCTION_RIGHT | GTK_JUNCTION_LEFT); - - /* 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); - - nemo_places_tree_sidebar_renderer_init (sidebar, tree_view); - - sidebar->store = nemo_shortcuts_tree_model_new (sidebar); - 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); - - 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_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; - - 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, - NULL); - - gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree_view), - GDK_BUTTON1_MASK, - 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); - - g_signal_connect (tree_view, "key-press-event", - G_CALLBACK (bookmarks_key_press_event_cb), sidebar); - - g_signal_connect (tree_view, "drag-motion", - G_CALLBACK (drag_motion_callback), sidebar); - g_signal_connect (tree_view, "drag-leave", - G_CALLBACK (drag_leave_callback), sidebar); - g_signal_connect (tree_view, "drag-data-received", - G_CALLBACK (drag_data_received_callback), sidebar); - g_signal_connect (tree_view, "drag-drop", - G_CALLBACK (drag_drop_callback), sidebar); - - g_signal_connect (selection, "changed", - G_CALLBACK (bookmarks_selection_changed_cb), sidebar); - g_signal_connect (tree_view, "popup-menu", - G_CALLBACK (bookmarks_popup_menu_cb), sidebar); - g_signal_connect (tree_view, "button-press-event", - G_CALLBACK (bookmarks_button_press_event_cb), sidebar); - g_signal_connect (tree_view, "button-release-event", - G_CALLBACK (bookmarks_button_release_event_cb), sidebar); - g_signal_connect (tree_view, "row-expanded", - G_CALLBACK (row_expanded_cb), sidebar); - g_signal_connect (tree_view, "row-collapsed", - G_CALLBACK (row_collapsed_cb), sidebar); - g_signal_connect (tree_view, "row-activated", - G_CALLBACK (row_activated_cb), sidebar); - g_signal_connect (tree_view, "motion-notify-event", - G_CALLBACK (motion_notify_cb), sidebar); - g_signal_connect (tree_view, "leave-notify-event", - G_CALLBACK (leave_notify_cb), sidebar); - - g_signal_connect_object (GTK_WIDGET (tree_view), "query-tooltip", - G_CALLBACK (query_tooltip_callback), sidebar, 0); - gtk_widget_set_has_tooltip (GTK_WIDGET (tree_view), TRUE); - - g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_DESKTOP_IS_HOME_DIR, - G_CALLBACK(desktop_setting_changed_callback), - sidebar); - - g_signal_connect_swapped (nemo_desktop_preferences, "changed::" NEMO_PREFERENCES_SHOW_DESKTOP, - G_CALLBACK(desktop_setting_changed_callback), - sidebar); - - g_signal_connect_swapped (cinnamon_privacy_preferences, "changed::" NEMO_PREFERENCES_RECENT_ENABLED, - G_CALLBACK(desktop_setting_changed_callback), - sidebar); - - g_signal_connect_object (nemo_trash_monitor_get (), - "trash_state_changed", - G_CALLBACK (trash_state_changed_cb), - sidebar, 0); - - g_signal_connect_swapped (xapp_favorites_get_default (), - "changed", - G_CALLBACK (favorites_changed_cb), - sidebar); -} - -static void -nemo_places_tree_sidebar_dispose (GObject *object) -{ - NemoPlacesTreeSidebar *sidebar; - - sidebar = NEMO_PLACES_TREE_SIDEBAR (object); - - if(sidebar->tree_view != NULL) - { - g_signal_handlers_disconnect_by_func(GTK_WIDGET(sidebar->tree_view), - G_CALLBACK (query_tooltip_callback), - sidebar); - - sidebar->tree_view = NULL; - } - - if (sidebar->window!=NULL) { - if(sidebar->hidden_files_changed_id != 0) { - g_signal_handler_disconnect (sidebar->window, - sidebar->hidden_files_changed_id); - sidebar->hidden_files_changed_id = 0; - } - sidebar->window = NULL; - } - - g_free (sidebar->uri); - sidebar->uri = NULL; - - g_clear_object (&sidebar->ui_manager); - sidebar->ui_manager = NULL; - - free_drag_data (sidebar); - - g_clear_handle_id (&sidebar->actions_changed_idle_id, g_source_remove); - - if (sidebar->bookmarks_changed_id != 0) { - g_signal_handler_disconnect (sidebar->bookmarks, - sidebar->bookmarks_changed_id); - sidebar->bookmarks_changed_id = 0; - } - - if (sidebar->actions_changed_id != 0) { - g_signal_handler_disconnect (sidebar->action_manager, - 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); - - g_clear_pointer (&sidebar->top_bookend_uri, g_free); - g_clear_pointer (&sidebar->bottom_bookend_uri, g_free); - - 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_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 (nemo_desktop_preferences, - desktop_setting_changed_callback, - sidebar); - - g_signal_handlers_disconnect_by_func (cinnamon_privacy_preferences, - desktop_setting_changed_callback, - sidebar); - - g_signal_handlers_disconnect_by_func (xapp_favorites_get_default (), - favorites_changed_cb, - sidebar); - - if (sidebar->volume_monitor != NULL) { - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - volume_added_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - volume_removed_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - volume_changed_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - mount_added_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - mount_removed_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - mount_changed_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - drive_disconnected_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - drive_connected_callback, sidebar); - g_signal_handlers_disconnect_by_func (sidebar->volume_monitor, - drive_changed_callback, sidebar); - - g_clear_object (&sidebar->volume_monitor); - } - - G_OBJECT_CLASS (nemo_places_tree_sidebar_parent_class)->dispose (object); -} - -static void -nemo_places_tree_sidebar_class_init (NemoPlacesTreeSidebarClass *class) -{ - GObjectClass *oclass = G_OBJECT_CLASS (class); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); - - oclass->dispose = nemo_places_tree_sidebar_dispose; - - widget_class->style_set = nemo_places_tree_sidebar_style_set; - widget_class->focus = nemo_places_tree_sidebar_focus; - - gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &menu_icon_pixels, NULL); - 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); -} - -static gboolean -update_places_on_idle_callback (NemoPlacesTreeSidebar *sidebar) -{ - sidebar->update_places_on_idle_id = 0; - - update_places (sidebar); - - return FALSE; -} - -static void -update_places_on_idle (NemoPlacesTreeSidebar *sidebar) -{ - if (sidebar->update_places_on_idle_id != 0) { - g_source_remove (sidebar->update_places_on_idle_id); - sidebar->update_places_on_idle_id = 0; - } - - sidebar->update_places_on_idle_id = g_idle_add_full (G_PRIORITY_LOW, - (GSourceFunc) update_places_on_idle_callback, - sidebar, NULL); -} - -static void -nemo_places_tree_sidebar_set_parent_window (NemoPlacesTreeSidebar *sidebar, - NemoWindow *window) -{ - NemoWindowSlot *slot; - gint breakpoint; - - sidebar->window = window; - - slot = nemo_window_get_active_slot (window); - - sidebar->bookmarks = nemo_bookmark_list_get_default (); - sidebar->uri = nemo_window_slot_get_current_uri (slot); - - breakpoint = g_settings_get_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT); - - if (breakpoint < 0) { // Default gsettings value is -1 (which translates to 'not previously set') - breakpoint = nemo_bookmark_list_length (sidebar->bookmarks); - g_settings_set_int (nemo_window_state, NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, breakpoint); - } - - sidebar->bookmark_breakpoint = breakpoint; - g_signal_connect_swapped (nemo_window_state, "changed::" NEMO_PREFERENCES_SIDEBAR_BOOKMARK_BREAKPOINT, - G_CALLBACK (breakpoint_changed_cb), sidebar); - - sidebar->bookmarks_changed_id = - g_signal_connect_swapped (sidebar->bookmarks, "changed", - G_CALLBACK (update_places_on_idle), - sidebar); - - g_signal_connect_object (window, "loading_uri", - G_CALLBACK (loading_uri_callback), - sidebar, 0); - - g_signal_connect_object (sidebar->volume_monitor, "volume_added", - G_CALLBACK (volume_added_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "volume_removed", - G_CALLBACK (volume_removed_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "volume_changed", - G_CALLBACK (volume_changed_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "mount_added", - G_CALLBACK (mount_added_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "mount_removed", - G_CALLBACK (mount_removed_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "mount_changed", - G_CALLBACK (mount_changed_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "drive_disconnected", - G_CALLBACK (drive_disconnected_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "drive_connected", - G_CALLBACK (drive_connected_callback), sidebar, 0); - g_signal_connect_object (sidebar->volume_monitor, "drive_changed", - G_CALLBACK (drive_changed_callback), sidebar, 0); - - g_signal_connect_swapped (nemo_preferences, "changed::" NEMO_PREFERENCES_ALWAYS_USE_BROWSER, - G_CALLBACK (reset_menu), sidebar); - sidebar->hidden_files_changed_id = - g_signal_connect_object (window, "hidden-files-mode-changed", - G_CALLBACK (hidden_files_mode_changed_callback), sidebar, 0); - update_places (sidebar); -} - -static void -nemo_places_tree_sidebar_style_set (GtkWidget *widget, - GtkStyle *previous_style) -{ - NemoPlacesTreeSidebar *sidebar; - - sidebar = NEMO_PLACES_TREE_SIDEBAR (widget); - update_places (sidebar); -} - -GtkWidget * -nemo_places_tree_sidebar_new (NemoWindow *window) -{ - NemoPlacesTreeSidebar *sidebar; - - sidebar = g_object_new (NEMO_TYPE_PLACES_TREE_SIDEBAR, NULL); - nemo_places_tree_sidebar_set_parent_window (sidebar, window); - - return GTK_WIDGET (sidebar); -} - - -/* Drag and drop interfaces */ - -/* GtkTreeDragSource::row_draggable implementation for the shortcuts filter model */ - -static gboolean -nemo_shortcuts_model_row_draggable (GtkTreeDragSource *drag_source, - GtkTreePath *path) -{ - 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; -} - -static void -_nemo_shortcuts_tree_model_class_init (NemoShortcutsTreeModelClass *class) -{ - -} - -static void -_nemo_shortcuts_tree_model_init (NemoShortcutsTreeModel *model) -{ - model->sidebar = NULL; -} - -static void -_nemo_shortcuts_tree_model_drag_source_init (GtkTreeDragSourceIface *iface) -{ - iface->row_draggable = nemo_shortcuts_model_row_draggable; -} - -static GtkTreeStore * -nemo_shortcuts_tree_model_new (NemoPlacesTreeSidebar *sidebar) -{ - NemoShortcutsTreeModel *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 - }; - - model = g_object_new (NEMO_TYPE_SHORTCUTS_TREE_MODEL, NULL); - model->sidebar = sidebar; - - gtk_tree_store_set_column_types (GTK_TREE_STORE (model), - PLACES_SIDEBAR_COLUMN_COUNT, - model_types); - - return GTK_TREE_STORE (model); -} - diff --git a/src/nemo-places-tree-sidebar.h b/src/nemo-places-tree-sidebar.h deleted file mode 100644 index a292bf63a..000000000 --- a/src/nemo-places-tree-sidebar.h +++ /dev/null @@ -1,56 +0,0 @@ - - -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ - -/* - * Copyright (C) 2000, 2001 Eazel, Inc - * Copyright (C) 2002 Anders Carlsson - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street - Suite 500, - * Boston, MA 02110-1335, USA. - * - * Authors: Klaus-Dieter Menk - */ - -/* fm-tree-view.h - tree view. */ - -#ifndef NEMO_PLACES_TREE_SIDEBAR_H -#define NEMO_PLACES_TREE_SIDEBAR_H - -#include "nemo-window.h" - -#include - -#define NEMO_PLACES_TREE_SIDEBAR_ID "placestree" - -#define NEMO_TYPE_PLACES_TREE_SIDEBAR nemo_places_tree_sidebar_get_type() -#define NEMO_PLACES_TREE_SIDEBAR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), NEMO_TYPE_PLACES_TREE_SIDEBAR, NemoPlacesTreeSidebar)) -#define NEMO_PLACES_TREE_SIDEBAR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), NEMO_TYPE_PLACES_TREE_SIDEBAR, NemoPlacesTreeSidebarClass)) -#define NEMO_IS_PLACES_TREE_SIDEBAR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NEMO_TYPE_PLACES_TREE_SIDEBAR)) -#define NEMO_IS_PLACES_TREE_SIDEBAR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), NEMO_TYPE_PLACES_TREE_SIDEBAR)) -#define NEMO_PLACES_TREE_SIDEBAR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), NEMO_TYPE_PLACES_TREE_SIDEBAR, NemoPlacesTreeSidebarClass)) - - -GType nemo_places_tree_sidebar_get_type (void); -GtkWidget * nemo_places_tree_sidebar_new (NemoWindow *window); - -#endif /* NEMO_PLACES_TREE_SIDEBAR_H */ - - From 3c390175746e3f5bd58e08933951b53a119aa5a4 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 11 Oct 2025 04:16:46 +0200 Subject: [PATCH 04/19] Ignore TreeView folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2a64eee33..6a65214ed 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,4 @@ debian/files *.debhelper obj-x86_64-linux-gnu +nemo-places-treeview/ From 6701518c184e6694f4444033f61db388e3835cbe Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 11 Oct 2025 20:03:27 +0200 Subject: [PATCH 05/19] uri update from location entry in treeview --- src/nemo-places-sidebar.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index dbfef3050..ec6e5370e 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -597,6 +597,8 @@ add_children_idle(gpointer user_data) GtkTreeModel *store = GTK_TREE_MODEL(data->sidebar->store); + gtk_tree_view_expand_row(data->sidebar->tree_view, data->parent_path, TRUE); + if (!gtk_tree_model_get_iter(store, &parent_iter, data->parent_path)) { g_warning("nemo-places-sidebar.c->[add_children_idle] Could not get iter for path (node may have been removed)"); g_free(data->uri); @@ -605,7 +607,6 @@ add_children_idle(gpointer user_data) return FALSE; } - gtk_tree_view_expand_row(data->sidebar->tree_view, data->parent_path, FALSE); add_directory_children(data->sidebar, &parent_iter, data->uri); @@ -1737,6 +1738,7 @@ find_uri_recursive (GtkTreeModel *model, /* Treffer gefunden? */ if (uri && g_strcmp0(uri, location) == 0) { *out_path = gtk_tree_model_get_path(model, &child); + gtk_tree_view_expand_row(sidebar->tree_view, *out_path, TRUE); g_free(tree_name); g_free(uri); return TRUE; From d94a99e56d83e314dbfc4fbe3ec596d42855760a Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 11 Oct 2025 21:16:43 +0200 Subject: [PATCH 06/19] problem with expanding nodes in uri update --- src/nemo-places-sidebar.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index ec6e5370e..e0306e8c4 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -597,7 +597,7 @@ add_children_idle(gpointer user_data) GtkTreeModel *store = GTK_TREE_MODEL(data->sidebar->store); - gtk_tree_view_expand_row(data->sidebar->tree_view, data->parent_path, TRUE); +// gtk_tree_view_expand_row(data->sidebar->tree_view, data->parent_path, TRUE); if (!gtk_tree_model_get_iter(store, &parent_iter, data->parent_path)) { g_warning("nemo-places-sidebar.c->[add_children_idle] Could not get iter for path (node may have been removed)"); @@ -1700,17 +1700,20 @@ find_uri_recursive (GtkTreeModel *model, gboolean valid_child; gchar *uri = NULL; gchar *tree_name = NULL; + gboolean is_lazy=FALSE; /* iterate children of `parent` (or top-level when parent == NULL) */ valid_child = gtk_tree_model_iter_children (model, &child, parent); while (valid_child) { + is_lazy=FALSE; gtk_tree_model_get (model, &child, PLACES_SIDEBAR_COLUMN_URI, &uri, PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_name, + PLACES_SIDEBAR_COLUMN_TREE_LAZY, &is_lazy, -1); /* Lazy-Loading / "(loading)"-Dummy prüfen */ - if (tree_name && g_strcmp0(tree_name, "(loading)") == 0) { + if (is_lazy) { if (uri) { /* -> Direkt echte Kinder laden */ GtkTreeIter store_iter; @@ -1738,7 +1741,6 @@ find_uri_recursive (GtkTreeModel *model, /* Treffer gefunden? */ if (uri && g_strcmp0(uri, location) == 0) { *out_path = gtk_tree_model_get_path(model, &child); - gtk_tree_view_expand_row(sidebar->tree_view, *out_path, TRUE); g_free(tree_name); g_free(uri); return TRUE; From 162b99b05fe199a3a16d08f058e02c1cc34f6847 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 11 Oct 2025 22:36:12 +0200 Subject: [PATCH 07/19] comments --- src/nemo-places-sidebar.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index e0306e8c4..3a06822b0 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -630,7 +630,6 @@ test_expand_row_cb(GtkTreeView *tree_view, gtk_tree_model_get(filter_model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); if (!uri) { - g_warning("[expand_cb] No URI found for expanded row"); return; } LazyLoadData *load_data = g_new0(LazyLoadData, 1); @@ -646,10 +645,8 @@ test_expand_row_cb(GtkTreeView *tree_view, &store_iter, iter); store_path = gtk_tree_model_get_path(GTK_TREE_MODEL(sidebar->store), &store_iter); - g_debug("[expand_cb] iter converted to store path via filter"); } else { store_path = gtk_tree_path_copy(path); - g_debug("[expand_cb] model is direct store, path copied"); } load_data->parent_path = store_path; @@ -1723,22 +1720,22 @@ find_uri_recursive (GtkTreeModel *model, &child ); - /* Lädt synchron echte Kinder in sidebar->store */ + /* load synchron real childr in sidebar->store */ add_directory_children(sidebar, &store_iter, uri); - /* Jetzt kann die Kindliste aktualisiert werden */ + /* now the children list could be updated */ g_free(tree_name); g_free(uri); uri = NULL; tree_name = NULL; - /* Neu starten der Kinderliste für dieses Parent */ + /* New start for chlidren list for this parent */ valid_child = gtk_tree_model_iter_children (model, &child, parent); continue; } } - /* Treffer gefunden? */ + /* found hit? */ if (uri && g_strcmp0(uri, location) == 0) { *out_path = gtk_tree_model_get_path(model, &child); g_free(tree_name); @@ -1746,7 +1743,7 @@ find_uri_recursive (GtkTreeModel *model, return TRUE; } - /* Rekursiv tiefer gehen */ + /* go recursively deeper */ GtkTreePath *subpath = NULL; if (find_uri_recursive(model, &child, location, &subpath, sidebar)) { *out_path = subpath; @@ -1755,7 +1752,7 @@ find_uri_recursive (GtkTreeModel *model, return TRUE; } - /* Aufräumen */ + /* clean up */ g_free(tree_name); g_free(uri); uri = NULL; @@ -4448,7 +4445,7 @@ text_cell_renderer_func(GtkTreeViewColumn *column, -1); if (row_type == PLACES_HEADING) { - /* Für Headings benutze HEADING_TEXT (bold) */ + /* For Headings use HEADING_TEXT (bold) */ g_free(tree_text); g_free(name_text); @@ -4490,17 +4487,17 @@ nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView * GtkCellRenderer *cell; GtkTreeViewColumn *primary_column; - /* === Primäre Spalte === */ + /* === primary row === */ primary_column = gtk_tree_view_column_new(); gtk_tree_view_column_set_expand(primary_column, TRUE); - /* --- Padding Cell Renderer --- */ + /* --- 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 --- */ + /* --- icon cell renderer --- */ cell = gtk_cell_renderer_pixbuf_new(); g_object_set(cell, "xalign", 0.0, @@ -4510,20 +4507,20 @@ nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView * gtk_tree_view_column_set_cell_data_func(primary_column, cell, icon_cell_renderer_func, sidebar, NULL); sidebar->icon_cell_renderer = cell; - /* --- Text Renderer --- */ + /* --- 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); - /* Entferne 'text' hier — wir setzen Text ausschließlich in text_cell_renderer_func */ + /* 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); - /* Setze cell data func, die jetzt TREE_NAME und NAME berücksichtigt */ + /* 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); @@ -4554,7 +4551,7 @@ nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView * gtk_tree_view_append_column(tree_view, sidebar->eject_column); - /* === Expander Column entfällt, GTK rendert automatisch in der ersten Spalte === */ + /* === Expander Column contains, GTK rendert automaticaly in first row === */ } static void @@ -4581,12 +4578,12 @@ row_visibility_function (GtkTreeModel *model, -1); if (type == PLACES_TREE_FOLDER) { NemoWindowShowHiddenFilesMode mode = nemo_window_get_hidden_files_mode (sidebar->window); - // --- 2. Versteckte Verzeichnisse ausblenden --- + // --- 2. hide hidden folders --- if ((mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE)) return TRUE; gchar *name = NULL; gboolean visible = TRUE; gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_TREE_NAME, &name, -1); - if (name && name[0] == '.') // beginnt mit Punkt → versteckt + if (name && name[0] == '.') // starts with dot character → hide visible = FALSE; g_free(name); return visible; From 2ef5f0a0e7c92ef6539eba1000e961be0e780717 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Fri, 17 Oct 2025 00:48:14 +0200 Subject: [PATCH 08/19] Entered key handling and updates if local filesystem changes --- src/nemo-places-sidebar.c | 962 +++++++++++++++++++++++++++++++------- 1 file changed, 787 insertions(+), 175 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 3a06822b0..87d91759e 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -109,6 +109,8 @@ typedef struct { GtkActionGroup *action_action_group; guint action_action_group_merge_id; + gulong row_deleted_handler_id; + gboolean actions_need_update; gboolean devices_header_added; @@ -191,6 +193,7 @@ enum { PLACES_SIDEBAR_COLUMN_TREE_NAME, // used for search in file tree view only PLACES_SIDEBAR_COLUMN_TREE_LAZY, // identify lazy loading dummies PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, // set if childs are already known in treenode + PLACES_SIDEBAR_COLUMN_NODE_DATA, // extended for TreeNodeData pointer PLACES_SIDEBAR_COLUMN_COUNT }; @@ -217,6 +220,14 @@ enum { POSITION_LOWER }; +typedef struct { + NemoPlacesSidebar *sidebar; + char *uri; + NemoFile *file; // + GFileMonitor *monitor; + ulong dir_changed_handler; +} TreeNodeData; + static void open_selected_bookmark (NemoPlacesSidebar *sidebar, GtkTreeModel *model, GtkTreeIter *iter, @@ -236,6 +247,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 void nemo_places_sidebar_cleanup (GtkTreeStore *store); +static TreeNodeData* create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model); +static void tree_node_data_free (TreeNodeData *node_data); +static gboolean children_loaded (GtkTreeModel *model, GtkTreeIter *iter); +static gchar *get_icon_name (const gchar *uri); + /* Identifiers for target types */ enum { GTK_TREE_MODEL_ROW, @@ -417,13 +434,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); } @@ -441,39 +458,259 @@ add_children_lazy (NemoPlacesSidebar *sidebar, -1); } +static gboolean +find_iter_by_uri_recursive(GtkTreeModel *model, GtkTreeIter *iter, const char *uri, GtkTreeIter *out_iter) +{ + do { + char *this_uri = NULL; + gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_URI, &this_uri, -1); + + if (this_uri && g_strcmp0(this_uri, uri) == 0) { + if (out_iter) + *out_iter = *iter; + g_free(this_uri); + return TRUE; + } + g_free(this_uri); + + GtkTreeIter child; + if (gtk_tree_model_iter_children(model, &child, iter)) { + if (find_iter_by_uri_recursive(model, &child, uri, out_iter)) + return TRUE; + } + + } while (gtk_tree_model_iter_next(model, iter)); + + return FALSE; +} + +gboolean +find_iter_by_uri(GtkTreeModel *model, const char *uri, GtkTreeIter *out_iter) +{ + GtkTreeIter iter; + if (!gtk_tree_model_get_iter_first(model, &iter)) + return FALSE; + return find_iter_by_uri_recursive(model, &iter, uri, out_iter); +} + +static void +add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, + const char *uri); + + +gboolean +find_child_iter_by_uri_under_parent(GtkTreeModel *model, + GtkTreeIter *parent, + const char *uri, + GtkTreeIter *out_iter) +{ + GtkTreeIter child; + gboolean valid = gtk_tree_model_iter_children(model, &child, parent); + + while (valid) { + char *child_uri = NULL; + gtk_tree_model_get(model, &child, PLACES_SIDEBAR_COLUMN_URI, &child_uri, -1); + + if (child_uri && g_strcmp0(child_uri, uri) == 0) { + if (out_iter) + *out_iter = child; + + g_free(child_uri); + return TRUE; + } + + g_free(child_uri); + valid = gtk_tree_model_iter_next(model, &child); + } + + return FALSE; +} + +static void +on_gfile_monitor_changed(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + TreeNodeData *node_data = (TreeNodeData *)user_data; + GtkTreeStore *store = GTK_TREE_STORE(node_data->sidebar->store); + GtkTreeModel *model = GTK_TREE_MODEL(store); + GtkTreeIter parent_iter; + + if (!find_iter_by_uri(model, node_data->uri, &parent_iter)) + return; + + char *uri = g_file_get_uri(file); + char *other_uri = other_file ? g_file_get_uri(other_file) : NULL; + + switch (event_type) { + case G_FILE_MONITOR_EVENT_CREATED: + case G_FILE_MONITOR_EVENT_MOVED_IN: + if (g_file_query_file_type(file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_DIRECTORY) { + GtkTreeIter new_iter; + gtk_tree_store_append(store, &new_iter, &parent_iter); + + TreeNodeData *new_data = create_tree_node_data(node_data->sidebar, uri, model); + gtk_tree_store_set(store, &new_iter, + PLACES_SIDEBAR_COLUMN_TREE_NAME, g_file_get_basename(file), + PLACES_SIDEBAR_COLUMN_URI, uri, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, + PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), + PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, + PLACES_SIDEBAR_COLUMN_NODE_DATA, new_data, + -1); + add_directory_children_lazy(node_data->sidebar, &new_iter, uri); + g_message("Folder created: %s", uri); + } + break; + + case G_FILE_MONITOR_EVENT_DELETED: + case G_FILE_MONITOR_EVENT_MOVED_OUT: { + GtkTreeIter child_iter; + if (find_child_iter_by_uri_under_parent(model, &parent_iter, uri, &child_iter)) { + TreeNodeData *child_data = NULL; + gtk_tree_model_get(model, &child_iter, PLACES_SIDEBAR_COLUMN_NODE_DATA, &child_data, -1); + if (child_data) + tree_node_data_free(child_data); + gtk_tree_store_remove(store, &child_iter); + } + break; + } + + case G_FILE_MONITOR_EVENT_MOVED: + case G_FILE_MONITOR_EVENT_RENAMED: + case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + if (other_uri) { + GtkTreeIter child_iter; + if (find_child_iter_by_uri_under_parent(model, &parent_iter, uri, &child_iter)) { + gtk_tree_store_set(store, &child_iter, + PLACES_SIDEBAR_COLUMN_TREE_NAME, g_file_get_basename(other_file), + PLACES_SIDEBAR_COLUMN_URI, other_uri, + -1); + g_message("Folder renamed: %s -> %s", uri, other_uri); + } + } + break; + + default: + break; + } + + g_free(uri); + g_free(other_uri); +} + +static void +tree_node_data_free(TreeNodeData *node_data) +{ + if (!node_data) { + return; + } + + if (node_data->monitor) { + g_signal_handlers_disconnect_by_func(node_data->monitor, on_gfile_monitor_changed, node_data); + g_object_unref(node_data->monitor); + node_data->monitor = NULL; + } + + if (node_data->file) { + g_object_unref(node_data->file); + } + + if (node_data->uri) { + g_free(node_data->uri); + } + + node_data->sidebar = NULL; + + g_free(node_data); +} + + +static TreeNodeData* +create_tree_node_data(NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model) +{ + if(!uri) + return NULL; + // Erstelle ein neues TreeNodeData-Objekt + TreeNodeData *node_data = g_new0(TreeNodeData, 1); + node_data->sidebar = sidebar; + + node_data->uri = g_strdup(uri); + + // Erstelle ein NemoFile für die gegebene URI + GFile *gfile = g_file_new_for_uri(uri); + NemoFile *nemo_file = nemo_file_get(gfile); + + + if (!nemo_file) { + g_warning("Konnte NemoFile für URI '%s' nicht erstellen!", uri); + g_free(node_data); + g_object_unref(gfile); + return NULL; + } + + node_data->file = g_object_ref(nemo_file); + + GError *error = NULL; + GFileMonitor *monitor = g_file_monitor_directory(gfile, G_FILE_MONITOR_NONE, NULL, &error); + + if (monitor) { + g_signal_connect(monitor, "changed", G_CALLBACK(on_gfile_monitor_changed), node_data); + node_data->monitor = monitor; + } else { + node_data->monitor = NULL; + g_clear_error(&error); + } + + g_object_unref(gfile); + + return node_data; +} + // insert dummy childr node if a foulder exists static void -add_directory_children_lazy(NemoPlacesSidebar *sidebar, - GtkTreeIter *parent_iter, - const char *uri) +add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, + const char *uri) { GFile *dir = g_file_new_for_uri(uri); + if (!dir) + return; + + GError *error = NULL; GFileEnumerator *enumerator = g_file_enumerate_children( dir, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, - NULL); - - if (enumerator) { - GFileInfo *info; - while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { - if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { - const char *name = g_file_info_get_name(info); - GFile *child_dir = g_file_get_child(dir, name); - char *child_uri = g_file_get_uri(child_dir); - add_children_lazy(sidebar, parent_iter); - g_free(child_uri); - g_object_unref(child_dir); - break; - } + &error); + + if (!enumerator) { + g_clear_error(&error); + g_object_unref(dir); + return; + } + + GFileInfo *info; + while ((info = g_file_enumerator_next_file(enumerator, NULL, &error)) != NULL) { + if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { + add_children_lazy(sidebar, parent_iter); g_object_unref(info); + break; // insert only one dummy } - g_file_enumerator_close(enumerator, NULL, NULL); - g_object_unref(enumerator); + g_object_unref(info); } + if (error) { + g_warning("Error iterating directory '%s': %s", uri, error->message); + g_clear_error(&error); + } + + g_file_enumerator_close(enumerator, NULL, NULL); + g_object_unref(enumerator); g_object_unref(dir); + } // check if childrean are already loaded @@ -487,14 +724,17 @@ children_loaded (GtkTreeModel *model, GtkTreeIter *iter) return loaded; } + // insert real children nodes static void -add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *uri) +add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *parent_uri) { gboolean has_dummy = FALSE; GtkTreeIter child; GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store); + if(parent_uri == NULL) + return; if (children_loaded(model, parent_iter)) return; @@ -507,62 +747,91 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con -1); if (is_lazy) { has_dummy = TRUE; - break; // Nur einen Dummy entfernen + break; //found a summy node } } while (gtk_tree_model_iter_next(model, &child)); - // Wenn schon echte Kinder existieren, nichts weiter tun + // if already real children exist -> nothing to do if (!has_dummy) { return; } } - GFile *dir = g_file_new_for_uri(uri); + GtkTreeIter parent_iter_copy; + parent_iter_copy = *parent_iter; + + TreeNodeData *parent_node_data=NULL; + gtk_tree_model_get(model, parent_iter, + PLACES_SIDEBAR_COLUMN_NODE_DATA, &parent_node_data, + -1); + if(!parent_node_data) { + parent_node_data = create_tree_node_data(sidebar, parent_uri, model); + gtk_tree_store_set(GTK_TREE_STORE(model), &parent_iter_copy, + PLACES_SIDEBAR_COLUMN_NODE_DATA, parent_node_data, + -1); + } + + GFile *dir = g_file_new_for_uri(parent_uri); + GError *error = NULL; GFileEnumerator *enumerator = g_file_enumerate_children( dir, G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, - NULL); - - if (enumerator) { - GFileInfo *info; - while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { - if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { - const char *name = g_file_info_get_name(info); - GFile *child_dir = g_file_get_child(dir, name); - char *child_uri = NULL; - - GFileInfo *nodeinfo = g_file_query_info(child_dir, - "standard::target-uri", - G_FILE_QUERY_INFO_NONE, - NULL, NULL); - - if (nodeinfo) { - child_uri = g_strdup(g_file_info_get_attribute_string(nodeinfo, "standard::target-uri")); - g_object_unref(nodeinfo); - } - if(child_uri==NULL) - { - child_uri = g_file_get_uri(child_dir); - } - GtkTreeIter child_iter; - gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); - gtk_tree_store_set(GTK_TREE_STORE(model), &child_iter, - PLACES_SIDEBAR_COLUMN_TREE_NAME, name, - PLACES_SIDEBAR_COLUMN_URI, child_uri, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, - PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), - PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, - -1); + &error); - add_directory_children_lazy(sidebar, &child_iter, child_uri); + if (!enumerator) { + g_warning("Failed to enumerate directory '%s': %s", parent_uri, error ? error->message : "unknown"); + g_clear_error(&error); + g_object_unref(dir); + return; + } - g_free(child_uri); - g_object_unref(child_dir); + GFileInfo *info; + while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { + if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { + const char *name = g_file_info_get_name(info); + GFile *child_dir = g_file_get_child(dir, name); + NemoFile *child_file = nemo_file_get(child_dir); + + /* child_uri aus child_file holen */ + char *child_uri = NULL; + if (child_file) { + const char *u = nemo_file_get_uri(child_file); + if (u) child_uri = g_strdup(u); } - g_object_unref(info); + /* Wenn nemo_file_get fehlschlägt, fallback auf child_dir URI */ + if (!child_uri && child_dir) { + child_uri = g_file_get_uri(child_dir); + } + + if (child_uri) { + GtkTreeIter child_iter; + gchar *icon=get_icon_name(child_uri); + gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); + TreeNodeData *node_data = create_tree_node_data(sidebar, child_uri, model); + + gtk_tree_store_set(GTK_TREE_STORE(model), &child_iter, + PLACES_SIDEBAR_COLUMN_TREE_NAME, name, + PLACES_SIDEBAR_COLUMN_URI, child_uri, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, + PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), + PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, + PLACES_SIDEBAR_COLUMN_NODE_DATA, node_data, + -1); + + add_directory_children_lazy(sidebar, &child_iter, child_uri); + + g_free(child_uri); + g_free(icon); + } + if (child_file) + g_object_unref(child_file); + if (child_dir) + g_object_unref(child_dir); } - g_file_enumerator_close(enumerator, NULL, NULL); - } + g_object_unref(info); + } + + // remove summy child node if (gtk_tree_model_iter_children(model, &child, parent_iter)) { do { @@ -572,15 +841,19 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con -1); if (is_lazy) { gtk_tree_store_remove(GTK_TREE_STORE(model), &child); + if(!gtk_tree_model_iter_children(model, &child, parent_iter)) + break; } } while (gtk_tree_model_iter_next(model, &child)); } - g_object_unref(enumerator); - g_object_unref(dir); gtk_tree_store_set(GTK_TREE_STORE(model), parent_iter, PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, TRUE, -1); + + g_file_enumerator_close(enumerator, NULL, NULL); + g_object_unref(enumerator); + g_object_unref(dir); } typedef struct _LazyLoadData { @@ -589,6 +862,15 @@ typedef struct _LazyLoadData { char *uri; } LazyLoadData; +static void +free_lazy_load_data (LazyLoadData *data) +{ + if (!data) return; + if (data->parent_path) gtk_tree_path_free (data->parent_path); + if (data->uri) g_free(data->uri); + g_free (data); +} + static gboolean add_children_idle(gpointer user_data) { @@ -597,22 +879,15 @@ add_children_idle(gpointer user_data) GtkTreeModel *store = GTK_TREE_MODEL(data->sidebar->store); -// gtk_tree_view_expand_row(data->sidebar->tree_view, data->parent_path, TRUE); - if (!gtk_tree_model_get_iter(store, &parent_iter, data->parent_path)) { g_warning("nemo-places-sidebar.c->[add_children_idle] Could not get iter for path (node may have been removed)"); - g_free(data->uri); - gtk_tree_path_free(data->parent_path); - g_free(data); + free_lazy_load_data (data); return FALSE; } - add_directory_children(data->sidebar, &parent_iter, data->uri); - g_free(data->uri); - gtk_tree_path_free(data->parent_path); - g_free(data); + free_lazy_load_data (data); return FALSE; } @@ -696,12 +971,12 @@ add_place (NemoPlacesSidebar *sidebar, gtk_tree_store_append (sidebar->store, &iter, &cat_iter); gtk_tree_store_set (sidebar->store, &iter, - PLACES_SIDEBAR_COLUMN_GICON, gicon, + 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, @@ -730,7 +1005,7 @@ add_place (NemoPlacesSidebar *sidebar, break; } } - g_clear_object (&gicon); + g_object_unref(gicon); return cat_iter; } @@ -1005,7 +1280,7 @@ update_places (NemoPlacesSidebar *sidebar) &last_iter, PLACES_SIDEBAR_COLUMN_URI, &last_uri, -1); } - gtk_tree_store_clear (sidebar->store); + nemo_places_sidebar_cleanup(sidebar->store); sidebar->devices_header_added = FALSE; sidebar->bookmarks_header_added = FALSE; @@ -1672,6 +1947,7 @@ clicked_eject_button (NemoPlacesSidebar *sidebar, } } + gdk_event_free(event); return FALSE; } @@ -1777,19 +2053,16 @@ loading_uri_callback (NemoWindow *window, gboolean found = FALSE; if (g_strcmp0 (sidebar->uri, location) == 0) return; + g_free (sidebar->uri); + sidebar->uri = g_strdup (location); + selection = gtk_tree_view_get_selection (sidebar->tree_view); + gtk_tree_selection_unselect_all (selection); + if(sidebar->use_file_treeview) { - /* with use_file_treeview a recursiv search is necessary*/ - 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); - GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); - /* 🔍 search for uri recursiv in tree */ + /* search for uri recursiv in tree */ if (find_uri_recursive(model, NULL, location, &path, sidebar)) { /* Nur Elternpfade expandieren (nicht den Zielknoten selbst) */ @@ -1809,12 +2082,7 @@ loading_uri_callback (NemoWindow *window, gtk_tree_path_free(path); } } else { - 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); @@ -3522,6 +3790,60 @@ 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_MODEL(sidebar->store_filter); + GtkTreeIter child_iter; + gboolean res; + + GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), 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_MODEL(sidebar->store_filter); + GtkTreeIter parent_iter; + gboolean res=TRUE; + + GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), 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 (GTK_TREE_MODEL (sidebar->store_filter), 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, @@ -3529,34 +3851,209 @@ find_prev_or_next_row (NemoPlacesSidebar *sidebar, { GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); 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_MODEL(sidebar->store_filter); + 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 +goto_prev_row_or_last_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); + GtkTreeIter prev_iter; + + // Try to move to the previous sibling row + // 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); + + if (!expanded) { + // Not expanded → we stop here (iter already points to it) + gtk_tree_path_free(path); + return TRUE; + } + + // The previous row is expanded → go down to its last visible child + GtkTreeIter child_iter; + + // Start with the first child + if (!gtk_tree_model_iter_children(model, &child_iter, &prev_iter)) { + gtk_tree_path_free(path); + return TRUE; // Expanded but has no children + } + prev_iter = child_iter; + // Walk through all children until the last one + while (gtk_tree_model_iter_next(model, &child_iter)) { + prev_iter = child_iter; + } + + // Replace current iter with the last child + *iter = prev_iter; + gtk_tree_path_free(path); + return TRUE; +} + +static gboolean +iter_equal(const GtkTreeIter *a, const GtkTreeIter *b) +{ + return memcmp(a, b, sizeof(GtkTreeIter)) == 0; +} + +static gboolean +find_parent_or_next_sibling_row(NemoPlacesSidebar *sidebar, GtkTreeIter *iter) +{ + GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); + 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; + } + + // 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; // keine Kinder → eine Ebene nach oben + } + + do { + if (iter_equal(&child_iter, ¤t_iter)) { + // Wir sind am aktuellen Iter → nächstes Kind prüfen + if (gtk_tree_model_iter_next(model, &child_iter)) { + *iter = child_iter; + return TRUE; + } else { + break; // no more siblingsr → next higer level + } + } + } while (gtk_tree_model_iter_next(model, &child_iter)); + + // next sibling not found on this level→ one level higher + current_iter = parent_iter; + } +} + + +static gboolean +select_prev_or_next_node (NemoPlacesSidebar *sidebar, + GtkTreeIter *iter, + gboolean go_up) +{ + GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); + 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 (GTK_TREE_MODEL (sidebar->store_filter), &iterCopy); + + GtkTreeSelection *sel = gtk_tree_view_get_selection(sidebar->tree_view); + gtk_tree_selection_unselect_all(sel); // keine Selektion mehr + + + gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); + gtk_tree_path_free (path); + } + return res; + } static gboolean @@ -3632,68 +4129,69 @@ 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; + } + open_selected_bookmark (sidebar, GTK_TREE_MODEL (sidebar->store_filter), + &selected_iter, flags); + return TRUE; + 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; + } + } + case GDK_KEY_Up: + if ((event->state & modifiers) == 0) { + select_prev_or_next_node (sidebar, &selected_iter, TRUE); + return TRUE; + } + case GDK_KEY_F2: + if ((event->state & modifiers) == 0) { + rename_selected_bookmark (sidebar); + return TRUE; + } + case GDK_KEY_Left: + if ((event->state & modifiers) == 0) { + goto_parent_or_collapse(sidebar, &selected_iter); + return TRUE; + } + case GDK_KEY_Right: + if ((event->state & modifiers) == 0) { + select_first_child_row_or_expand(sidebar, &selected_iter); + return TRUE; + } + default: + return FALSE; } - return FALSE; } @@ -4436,7 +4934,6 @@ text_cell_renderer_func(GtkTreeViewColumn *column, gint row_type = 0; gboolean lazy = FALSE; - /* Lese beide Text-Spalten + Row-Type */ gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_text, @@ -4445,7 +4942,6 @@ text_cell_renderer_func(GtkTreeViewColumn *column, -1); if (row_type == PLACES_HEADING) { - /* For Headings use HEADING_TEXT (bold) */ g_free(tree_text); g_free(name_text); @@ -4461,14 +4957,12 @@ text_cell_renderer_func(GtkTreeViewColumn *column, g_object_set(renderer, "text", heading_text ? heading_text : "", NULL); g_free(heading_text); } else { - /* Normale Zeilen: bevorzugt TREE_NAME, fallback auf NAME */ const gchar *display = ""; if (tree_text != NULL && *tree_text != '\0') display = tree_text; else if (name_text != NULL && *name_text != '\0') display = name_text; - /* Standardgewicht */ g_object_set(renderer, "weight-set", FALSE, NULL); @@ -4481,6 +4975,96 @@ text_cell_renderer_func(GtkTreeViewColumn *column, } +static void +free_node_recursive(GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter child; + + /* process current node */ + TreeNodeData *node_data = NULL; + gtk_tree_model_get(model, iter, + PLACES_SIDEBAR_COLUMN_NODE_DATA, &node_data, + -1); + if (node_data) { + tree_node_data_free(node_data); + /* setze Pointer im Store auf NULL, damit keine dangling pointer verbleiben */ + if (GTK_IS_TREE_STORE(model)) { + gtk_tree_store_set(GTK_TREE_STORE(model), iter, + PLACES_SIDEBAR_COLUMN_NODE_DATA, NULL, + -1); + } + } + + /* process children recursively */ + if (gtk_tree_model_iter_children(model, &child, iter)) { + free_node_recursive(model, &child); + while (gtk_tree_model_iter_next(model, &child)) { + free_node_recursive(model, &child); + } + } +} + +static void +nemo_places_sidebar_cleanup(GtkTreeStore *store) +{ + if (!store) + return; + + // walk throug TreeNodes and freeTreeNodeData + GtkTreeIter iter; + GtkTreeModel *model = GTK_TREE_MODEL(store); + + if (gtk_tree_model_get_iter_first(model, &iter)) { + /* call rekursive all node_data (inc. children) */ + do { + free_node_recursive(model, &iter); + } while (gtk_tree_model_iter_next(model, &iter)); + } + gtk_tree_store_clear (store); +} + + +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; +} + +static gboolean +on_tree_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) +{ + set_treeview_style(GTK_TREE_VIEW(widget), FALSE); + return FALSE; +} + + static void nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView *tree_view) { @@ -4613,6 +5197,8 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) "changed", G_CALLBACK (actions_changed), sidebar); + + sidebar->ui_manager = gtk_ui_manager_new (); sidebar->in_drag = FALSE; @@ -4631,6 +5217,8 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) sidebar-> hidden_files_changed_id = 0; + sidebar->row_deleted_handler_id = 0; + 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, @@ -4740,6 +5328,12 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) G_CALLBACK(test_expand_row_cb), sidebar); + g_signal_connect(tree_view, "focus-in-event", + G_CALLBACK(on_tree_focus_in), NULL); + + g_signal_connect(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); @@ -4769,14 +5363,21 @@ nemo_places_sidebar_dispose (GObject *object) NemoPlacesSidebar *sidebar; sidebar = NEMO_PLACES_SIDEBAR (object); + nemo_places_sidebar_cleanup(sidebar->store); + + 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; + } if(sidebar->tree_view != NULL) { - g_signal_handlers_disconnect_by_func(GTK_WIDGET(sidebar->tree_view), - G_CALLBACK (query_tooltip_callback), - sidebar); - - 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; } if (sidebar->window!=NULL) { @@ -4788,9 +5389,6 @@ nemo_places_sidebar_dispose (GObject *object) sidebar->window = NULL; } - g_free (sidebar->uri); - sidebar->uri = NULL; - g_clear_object (&sidebar->ui_manager); sidebar->ui_manager = NULL; @@ -4809,15 +5407,21 @@ 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; } + 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; + } + if (sidebar->row_deleted_handler_id != 0) { + g_signal_handler_disconnect(sidebar->store, sidebar->row_deleted_handler_id); + sidebar->row_deleted_handler_id = 0; + } - g_clear_object (&sidebar->store); g_clear_pointer (&sidebar->top_bookend_uri, g_free); g_clear_pointer (&sidebar->bottom_bookend_uri, g_free); @@ -4825,7 +5429,7 @@ 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, @@ -4875,6 +5479,8 @@ nemo_places_sidebar_dispose (GObject *object) g_clear_object (&sidebar->volume_monitor); } + g_clear_object(&sidebar->store_filter); + g_clear_object(&sidebar->store); G_OBJECT_CLASS (nemo_places_sidebar_parent_class)->dispose (object); } @@ -4915,7 +5521,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 @@ -4951,6 +5558,10 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, g_signal_connect_object (window, "loading_uri", G_CALLBACK (loading_uri_callback), sidebar, 0); + if(sidebar->uri) { + g_free (sidebar->uri); + sidebar->uri = NULL; + } g_signal_connect_object (sidebar->volume_monitor, "volume_added", G_CALLBACK (volume_added_callback), sidebar, 0); @@ -4976,7 +5587,7 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, sidebar->hidden_files_changed_id = g_signal_connect_object (window, "hidden-files-mode-changed", G_CALLBACK (hidden_files_mode_changed_callback), sidebar, 0); - update_places (sidebar); +// update_places (sidebar); } static void @@ -5072,7 +5683,8 @@ nemo_shortcuts_model_new (NemoPlacesSidebar *sidebar) G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN + G_TYPE_BOOLEAN, + G_TYPE_POINTER // TreeNodeData* }; model = g_object_new (NEMO_TYPE_SHORTCUTS_MODEL, NULL); From ca1e26e5f4086246db1053cadfc2d41cf41b6d88 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 18 Oct 2025 19:06:24 +0200 Subject: [PATCH 09/19] poll for network directories --- src/nemo-places-sidebar.c | 230 +++++++++++++++++++++++++------------- 1 file changed, 152 insertions(+), 78 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 87d91759e..508138550 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -220,12 +220,20 @@ 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; ulong dir_changed_handler; + GIcon *icon; + GFile *dir; + guint poll_id; + GList *last_snapshot; } TreeNodeData; static void open_selected_bookmark (NemoPlacesSidebar *sidebar, @@ -248,10 +256,11 @@ static void update_places_on_idle (NemoPlacesSidebar *sideb static void rebuild_menu (NemoPlacesSidebar *sidebar); static void actions_changed (gpointer user_data); static void nemo_places_sidebar_cleanup (GtkTreeStore *store); -static TreeNodeData* create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model); +static TreeNodeData* create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model, GtkTreeIter *iter); static void tree_node_data_free (TreeNodeData *node_data); static gboolean children_loaded (GtkTreeModel *model, GtkTreeIter *iter); static gchar *get_icon_name (const gchar *uri); +static void free_node_recursive(GtkTreeModel *model, GtkTreeIter *iter); /* Identifiers for target types */ enum { @@ -458,41 +467,6 @@ add_children_lazy (NemoPlacesSidebar *sidebar, -1); } -static gboolean -find_iter_by_uri_recursive(GtkTreeModel *model, GtkTreeIter *iter, const char *uri, GtkTreeIter *out_iter) -{ - do { - char *this_uri = NULL; - gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_URI, &this_uri, -1); - - if (this_uri && g_strcmp0(this_uri, uri) == 0) { - if (out_iter) - *out_iter = *iter; - g_free(this_uri); - return TRUE; - } - g_free(this_uri); - - GtkTreeIter child; - if (gtk_tree_model_iter_children(model, &child, iter)) { - if (find_iter_by_uri_recursive(model, &child, uri, out_iter)) - return TRUE; - } - - } while (gtk_tree_model_iter_next(model, iter)); - - return FALSE; -} - -gboolean -find_iter_by_uri(GtkTreeModel *model, const char *uri, GtkTreeIter *out_iter) -{ - GtkTreeIter iter; - if (!gtk_tree_model_get_iter_first(model, &iter)) - return FALSE; - return find_iter_by_uri_recursive(model, &iter, uri, out_iter); -} - static void add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *uri); @@ -526,6 +500,26 @@ find_child_iter_by_uri_under_parent(GtkTreeModel *model, return FALSE; } +static void +add_new_child_node(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *name, const char *child_uri) +{ + GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store); + GtkTreeIter child_iter; + gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); + TreeNodeData *node_data = create_tree_node_data(sidebar, child_uri, model, &child_iter); + + gtk_tree_store_set(GTK_TREE_STORE(model), &child_iter, + PLACES_SIDEBAR_COLUMN_TREE_NAME, name, + PLACES_SIDEBAR_COLUMN_URI, child_uri, + PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, + PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), + PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, + PLACES_SIDEBAR_COLUMN_NODE_DATA, node_data, + -1); + + add_directory_children_lazy(sidebar, &child_iter, child_uri); +} + static void on_gfile_monitor_changed(GFileMonitor *monitor, GFile *file, @@ -538,8 +532,16 @@ on_gfile_monitor_changed(GFileMonitor *monitor, GtkTreeModel *model = GTK_TREE_MODEL(store); GtkTreeIter parent_iter; - if (!find_iter_by_uri(model, node_data->uri, &parent_iter)) - return; + if (!gtk_tree_row_reference_valid(node_data->tree_ref)) { + g_message("on_gfile_monitor_changed: node does not exist any more!"); + return; + } + GtkTreePath *path = gtk_tree_row_reference_get_path(node_data->tree_ref); + if (!gtk_tree_model_get_iter(model, &parent_iter, path)) { + g_warning("on_gfile_monitor_changed: could not extract node from path!"); + gtk_tree_path_free(path); + return; + } char *uri = g_file_get_uri(file); char *other_uri = other_file ? g_file_get_uri(other_file) : NULL; @@ -548,20 +550,7 @@ on_gfile_monitor_changed(GFileMonitor *monitor, case G_FILE_MONITOR_EVENT_CREATED: case G_FILE_MONITOR_EVENT_MOVED_IN: if (g_file_query_file_type(file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_DIRECTORY) { - GtkTreeIter new_iter; - gtk_tree_store_append(store, &new_iter, &parent_iter); - - TreeNodeData *new_data = create_tree_node_data(node_data->sidebar, uri, model); - gtk_tree_store_set(store, &new_iter, - PLACES_SIDEBAR_COLUMN_TREE_NAME, g_file_get_basename(file), - PLACES_SIDEBAR_COLUMN_URI, uri, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, - PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), - PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, - PLACES_SIDEBAR_COLUMN_NODE_DATA, new_data, - -1); - add_directory_children_lazy(node_data->sidebar, &new_iter, uri); - g_message("Folder created: %s", uri); + add_new_child_node(node_data->sidebar, &parent_iter, g_file_get_basename(file), uri); } break; @@ -569,15 +558,13 @@ on_gfile_monitor_changed(GFileMonitor *monitor, case G_FILE_MONITOR_EVENT_MOVED_OUT: { GtkTreeIter child_iter; if (find_child_iter_by_uri_under_parent(model, &parent_iter, uri, &child_iter)) { - TreeNodeData *child_data = NULL; - gtk_tree_model_get(model, &child_iter, PLACES_SIDEBAR_COLUMN_NODE_DATA, &child_data, -1); - if (child_data) - tree_node_data_free(child_data); + free_node_recursive(model, &child_iter); gtk_tree_store_remove(store, &child_iter); } break; } - +/* +* no need for this code becase events G_FILE_MONITOR_EVENT_DELETED and G_FILE_MONITOR_EVENT_CREATED are triggered later... case G_FILE_MONITOR_EVENT_MOVED: case G_FILE_MONITOR_EVENT_RENAMED: case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: @@ -592,7 +579,7 @@ on_gfile_monitor_changed(GFileMonitor *monitor, } } break; - +*/ default: break; } @@ -613,7 +600,13 @@ tree_node_data_free(TreeNodeData *node_data) g_object_unref(node_data->monitor); node_data->monitor = NULL; } - + if (node_data->poll_id) { + g_source_remove(node_data->poll_id); + node_data->poll_id = 0; + } + if (node_data->dir) { + g_object_unref(node_data->dir); + } if (node_data->file) { g_object_unref(node_data->file); } @@ -622,14 +615,101 @@ tree_node_data_free(TreeNodeData *node_data) g_free(node_data->uri); } + if (node_data->tree_ref) { + gtk_tree_row_reference_free(node_data->tree_ref); + node_data->tree_ref = NULL; + } + g_list_free_full(node_data->last_snapshot, g_free); node_data->sidebar = NULL; g_free(node_data); } +static GList* snapshot_directory(GFile *dir) { + GError *error = NULL; + GList *files = NULL; + GFileEnumerator *enumerator = g_file_enumerate_children( + dir, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + &error + ); + if (!enumerator) { + if (error) { + g_warning("Snapshot failed for %s: %s", + g_file_get_uri(dir), error->message); + g_clear_error(&error); + } + return NULL; + } + + GFileInfo *info; + while ((info = g_file_enumerator_next_file(enumerator, NULL, &error)) != NULL) { + const char *name = g_file_info_get_name(info); + files = g_list_prepend(files, g_strdup(name)); + g_object_unref(info); + } + g_file_enumerator_close(enumerator, NULL, NULL); + g_object_unref(enumerator); + + return g_list_reverse(files); // optional: sortiert +} + +static gboolean +poll_directory_changes(gpointer user_data) +{ + TreeNodeData *node_data = (TreeNodeData *)user_data; + if(!node_data->dir) { + return FALSE; + } + GList *current = snapshot_directory(node_data->dir); + + GList *added = NULL, *removed = NULL; + + for (GList *l = current; l; l = l->next) { + if (!g_list_find_custom(node_data->last_snapshot, l->data, (GCompareFunc)g_strcmp0)) + added = g_list_prepend(added, g_strdup(l->data)); + } + for (GList *l = node_data->last_snapshot; l; l = l->next) + { + if (!g_list_find_custom(current, l->data, (GCompareFunc)g_strcmp0)) + removed = g_list_prepend(removed, g_strdup(l->data)); + } + for (GList *l = added; l; l = l->next) { + GFile *child = g_file_get_child(node_data->dir, l->data); + on_gfile_monitor_changed( + NULL, // kein echter Monitor + child, // file + NULL, // other_file + G_FILE_MONITOR_EVENT_CREATED, + node_data); // user_data + g_object_unref(child); + } + + for (GList *l = removed; l; l = l->next) { + GFile *child = g_file_get_child(node_data->dir, l->data); + on_gfile_monitor_changed( + NULL, + child, + NULL, + G_FILE_MONITOR_EVENT_DELETED, + node_data); + g_object_unref(child); + } + + // Alte Snapshot freigeben + g_list_free_full(node_data->last_snapshot, g_free); + node_data->last_snapshot = current; + + g_list_free_full(added, g_free); + g_list_free_full(removed, g_free); + + return TRUE; // Wiederholen +} static TreeNodeData* -create_tree_node_data(NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model) +create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model, GtkTreeIter *iter) { if(!uri) return NULL; @@ -643,6 +723,11 @@ create_tree_node_data(NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel GFile *gfile = g_file_new_for_uri(uri); NemoFile *nemo_file = nemo_file_get(gfile); + node_data->dir = g_file_new_for_uri(node_data->uri); + + GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(sidebar->store), iter); + node_data->tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(sidebar->store), path); + gtk_tree_path_free(path); if (!nemo_file) { g_warning("Konnte NemoFile für URI '%s' nicht erstellen!", uri); @@ -653,6 +738,8 @@ create_tree_node_data(NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel node_data->file = g_object_ref(nemo_file); + node_data->poll_id = 0; + node_data->last_snapshot = NULL; GError *error = NULL; GFileMonitor *monitor = g_file_monitor_directory(gfile, G_FILE_MONITOR_NONE, NULL, &error); @@ -662,8 +749,11 @@ create_tree_node_data(NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel } else { node_data->monitor = NULL; g_clear_error(&error); + node_data->last_snapshot = snapshot_directory(node_data->dir); + node_data->poll_id = g_timeout_add(POLL_INTERVAL, poll_directory_changes, node_data); } + g_object_unref(gfile); return node_data; @@ -763,7 +853,7 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con PLACES_SIDEBAR_COLUMN_NODE_DATA, &parent_node_data, -1); if(!parent_node_data) { - parent_node_data = create_tree_node_data(sidebar, parent_uri, model); + parent_node_data = create_tree_node_data(sidebar, parent_uri, model, parent_iter); gtk_tree_store_set(GTK_TREE_STORE(model), &parent_iter_copy, PLACES_SIDEBAR_COLUMN_NODE_DATA, parent_node_data, -1); @@ -804,24 +894,8 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con } if (child_uri) { - GtkTreeIter child_iter; - gchar *icon=get_icon_name(child_uri); - gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); - TreeNodeData *node_data = create_tree_node_data(sidebar, child_uri, model); - - gtk_tree_store_set(GTK_TREE_STORE(model), &child_iter, - PLACES_SIDEBAR_COLUMN_TREE_NAME, name, - PLACES_SIDEBAR_COLUMN_URI, child_uri, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, - PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), - PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, - PLACES_SIDEBAR_COLUMN_NODE_DATA, node_data, - -1); - - add_directory_children_lazy(sidebar, &child_iter, child_uri); - + add_new_child_node(sidebar, parent_iter, name, child_uri); g_free(child_uri); - g_free(icon); } if (child_file) g_object_unref(child_file); From 1d978acc74a53480b11f4dc3ff146f3eecc417b0 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 18 Oct 2025 20:47:19 +0200 Subject: [PATCH 10/19] some changes for mor sable code --- src/nemo-places-sidebar.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 508138550..91558fb35 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -229,7 +229,6 @@ typedef struct { char *uri; NemoFile *file; // GFileMonitor *monitor; - ulong dir_changed_handler; GIcon *icon; GFile *dir; guint poll_id; @@ -596,7 +595,7 @@ tree_node_data_free(TreeNodeData *node_data) } if (node_data->monitor) { - g_signal_handlers_disconnect_by_func(node_data->monitor, on_gfile_monitor_changed, node_data); + g_file_monitor_cancel(node_data->monitor); g_object_unref(node_data->monitor); node_data->monitor = NULL; } @@ -620,6 +619,8 @@ tree_node_data_free(TreeNodeData *node_data) node_data->tree_ref = NULL; } g_list_free_full(node_data->last_snapshot, g_free); + node_data->last_snapshot =NULL; + node_data->sidebar = NULL; g_free(node_data); @@ -642,7 +643,7 @@ static GList* snapshot_directory(GFile *dir) { g_clear_error(&error); } return NULL; - } + } GFileInfo *info; while ((info = g_file_enumerator_next_file(enumerator, NULL, &error)) != NULL) { @@ -650,6 +651,12 @@ static GList* snapshot_directory(GFile *dir) { files = g_list_prepend(files, g_strdup(name)); g_object_unref(info); } + + if (error) { + g_warning("enumerator failed for %s: %s", + g_file_get_uri(dir), error->message); + g_clear_error(&error); + } g_file_enumerator_close(enumerator, NULL, NULL); g_object_unref(enumerator); @@ -894,9 +901,9 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con } if (child_uri) { - add_new_child_node(sidebar, parent_iter, name, child_uri); - g_free(child_uri); - } + add_new_child_node(sidebar, parent_iter, name, child_uri); + g_free(child_uri); + } if (child_file) g_object_unref(child_file); if (child_dir) From 84b090f8ba104c1d433ffadad8935565af9a35ec Mon Sep 17 00:00:00 2001 From: kdmenk Date: Tue, 21 Oct 2025 03:52:07 +0200 Subject: [PATCH 11/19] some issues with the current uri selection fixed --- src/nemo-places-sidebar.c | 256 +++++++++++++++++++++----------------- 1 file changed, 141 insertions(+), 115 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 91558fb35..bd60fcc80 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -2086,7 +2086,6 @@ find_uri_recursive (GtkTreeModel *model, uri = NULL; tree_name = NULL; - /* New start for chlidren list for this parent */ valid_child = gtk_tree_model_iter_children (model, &child, parent); continue; } @@ -2127,54 +2126,111 @@ loading_uri_callback (NemoWindow *window, NemoPlacesSidebar *sidebar) { GtkTreeSelection *selection; - GtkTreeIter iter_cat, iter_child; - gboolean valid_cat, valid_child; - char *uri=NULL; - GtkTreePath *path = NULL; gboolean found = FALSE; if (g_strcmp0 (sidebar->uri, location) == 0) return; - g_free (sidebar->uri); + + g_free (sidebar->uri); sidebar->uri = g_strdup (location); + selection = gtk_tree_view_get_selection (sidebar->tree_view); - gtk_tree_selection_unselect_all (selection); - if(sidebar->use_file_treeview) - { + if (sidebar->use_file_treeview) { GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); + GtkTreeIter current_iter; + GtkTreePath *current_path = NULL; + + if (gtk_tree_selection_get_selected(selection, NULL, ¤t_iter)) { + gchar *current_uri = NULL; + gtk_tree_model_get(model, ¤t_iter, + PLACES_SIDEBAR_COLUMN_URI, ¤t_uri, + -1); + + if (current_uri && g_strcmp0(current_uri, location) == 0) { + /* location already selected */ + g_free(current_uri); + return; + } + if (current_uri && g_str_has_prefix (location, current_uri)) { + GtkTreePath *iter_path = gtk_tree_model_get_path(model, ¤t_iter); + GtkTreePath *parent_path = gtk_tree_path_copy(iter_path); + gboolean parent_selected = FALSE; + + while (gtk_tree_path_get_depth (parent_path) > 0) { + GtkTreeIter parent_iter; + if (!gtk_tree_model_get_iter(model, &parent_iter, parent_path)) + break; + + char *parent_uri = NULL; + gtk_tree_model_get(model, &parent_iter, + PLACES_SIDEBAR_COLUMN_URI, &parent_uri, + -1); + + if (parent_uri && g_strcmp0(parent_uri, location) == 0) { + /* parent node found */ + gtk_tree_selection_select_path(selection, parent_path); + gtk_tree_view_expand_to_path(sidebar->tree_view, parent_path); + parent_selected = TRUE; + g_free(parent_uri); + break; + } + g_free(parent_uri); + gtk_tree_path_up(parent_path); + } + + gtk_tree_path_free (iter_path); + gtk_tree_path_free (parent_path); + + if (parent_selected) { + g_free(current_uri); + return; + } + } + current_path = gtk_tree_model_get_path(model, ¤t_iter); + g_free(current_uri); + } /* search for uri recursiv in tree */ - if (find_uri_recursive(model, NULL, location, &path, sidebar)) { + GtkTreePath *child_path = NULL; + if (find_uri_recursive (model, NULL, location, &child_path, sidebar)) { + GtkTreePath *parent_path = gtk_tree_path_copy(child_path); - /* Nur Elternpfade expandieren (nicht den Zielknoten selbst) */ - GtkTreePath *parent_path = gtk_tree_path_copy(path); if (gtk_tree_path_get_depth(parent_path) > 1) { gtk_tree_path_up(parent_path); gtk_tree_view_expand_to_path(sidebar->tree_view, parent_path); } /* select target node */ - gtk_tree_selection_select_path(selection, path); - + gtk_tree_selection_select_path(selection, child_path); /* scroll to target node */ - gtk_tree_view_scroll_to_cell(sidebar->tree_view, path, NULL, FALSE, 0.5, 0.5); + gtk_tree_view_scroll_to_cell(sidebar->tree_view, child_path, NULL, FALSE, 0.5, 0.5); gtk_tree_path_free(parent_path); - gtk_tree_path_free(path); + gtk_tree_path_free(child_path); } + + if (current_path) + gtk_tree_path_free(current_path); + } else { /* set selection if any place matches location */ - valid_cat = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store_filter), - &iter_cat); + gtk_tree_selection_unselect_all(selection); + + GtkTreeIter iter_cat, iter_child; + gboolean valid_cat = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store_filter), + &iter_cat); + char *uri = NULL; while (valid_cat) { - if (uri) g_free(uri); - valid_child = gtk_tree_model_iter_children (GTK_TREE_MODEL (sidebar->store_filter), - &iter_child, - &iter_cat); + if (uri) g_free(uri); + + gboolean 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, + 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) { @@ -2184,6 +2240,7 @@ loading_uri_callback (NemoWindow *window, break; } g_free (uri); + uri = NULL; } valid_child = gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store_filter), &iter_child); @@ -2192,8 +2249,8 @@ loading_uri_callback (NemoWindow *window, break; } valid_cat = gtk_tree_model_iter_next (GTK_TREE_MODEL (sidebar->store_filter), - &iter_cat); - } + &iter_cat); + } } } @@ -4079,7 +4136,7 @@ find_parent_or_next_sibling_row(NemoPlacesSidebar *sidebar, GtkTreeIter *iter) *iter = child_iter; return TRUE; } else { - break; // no more siblingsr → next higer level + break; // no mre siblings -> next higher level } } } while (gtk_tree_model_iter_next(model, &child_iter)); @@ -4127,9 +4184,7 @@ select_prev_or_next_node (NemoPlacesSidebar *sidebar, GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iterCopy); GtkTreeSelection *sel = gtk_tree_view_get_selection(sidebar->tree_view); - gtk_tree_selection_unselect_all(sel); // keine Selektion mehr - - + gtk_tree_selection_unselect_all(sel); gtk_tree_view_set_cursor (sidebar->tree_view, path, NULL, FALSE); gtk_tree_path_free (path); } @@ -4789,32 +4844,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); - - 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; + 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; + } } } @@ -4843,37 +4901,9 @@ row_expanded_cb (GtkTreeView *tree_view, path, user_data, TRUE); -} - -static void -row_activated_cb (GtkTreeView *tree_view, - GtkTreePath *path, - GtkTreeViewColumn *column, - gpointer user_data) -{ - GtkTreeIter iter; - SectionType section_type; - PlaceType place_type; - - NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (user_data); - GtkTreeModel *model = gtk_tree_view_get_model (tree_view); - gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path); - gtk_tree_model_get (model, &iter, - PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, &place_type, - -1); - if (place_type == PLACES_HEADING) { - if (section_type == SECTION_COMPUTER) { - sidebar->my_computer_expanded = !sidebar->my_computer_expanded; - } else if (section_type == SECTION_BOOKMARKS) { - sidebar->bookmarks_expanded = !sidebar->bookmarks_expanded; - } else if (section_type == SECTION_DEVICES) { - sidebar->devices_expanded = !sidebar->devices_expanded; - } else if (section_type == SECTION_NETWORK) { - sidebar->network_expanded = !sidebar->network_expanded; - } - restore_expand_state (sidebar); - } + GtkTreeSelection *sel = gtk_tree_view_get_selection(tree_view); + gtk_tree_selection_unselect_all(sel); + gtk_tree_selection_select_path(sel, path); } static void @@ -5057,24 +5087,30 @@ text_cell_renderer_func(GtkTreeViewColumn *column, static void -free_node_recursive(GtkTreeModel *model, GtkTreeIter *iter) +free_node_free_node_data(GtkTreeModel *model, GtkTreeIter *iter) { - GtkTreeIter child; - - /* process current node */ + /* process current node */ TreeNodeData *node_data = NULL; gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_NODE_DATA, &node_data, -1); if (node_data) { tree_node_data_free(node_data); - /* setze Pointer im Store auf NULL, damit keine dangling pointer verbleiben */ + /* set pointer in store to NULL to avoid dangling pointer */ if (GTK_IS_TREE_STORE(model)) { gtk_tree_store_set(GTK_TREE_STORE(model), iter, PLACES_SIDEBAR_COLUMN_NODE_DATA, NULL, -1); } } +} + +static void +free_node_recursive(GtkTreeModel *model, GtkTreeIter *iter) +{ + GtkTreeIter child; + + free_node_free_node_data(model, iter); /* process children recursively */ if (gtk_tree_model_iter_children(model, &child, iter)) { @@ -5215,8 +5251,6 @@ nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView * gtk_tree_view_append_column(tree_view, sidebar->eject_column); - - /* === Expander Column contains, GTK rendert automaticaly in first row === */ } static void @@ -5244,13 +5278,15 @@ row_visibility_function (GtkTreeModel *model, if (type == PLACES_TREE_FOLDER) { NemoWindowShowHiddenFilesMode mode = nemo_window_get_hidden_files_mode (sidebar->window); // --- 2. hide hidden folders --- - if ((mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE)) return TRUE; - gchar *name = NULL; - gboolean visible = TRUE; - gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_TREE_NAME, &name, -1); - if (name && name[0] == '.') // starts with dot character → hide - visible = FALSE; - g_free(name); + if ((mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE)) return TRUE; + + gboolean visible = TRUE; + TreeNodeData *node_data = NULL; + gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_NODE_DATA, &node_data, -1); + if(node_data) { + if (nemo_file_is_hidden_file(node_data->file)) + visible = FALSE; + } return visible; } @@ -5393,8 +5429,6 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) G_CALLBACK (row_expanded_cb), sidebar); g_signal_connect (tree_view, "row-collapsed", G_CALLBACK (row_collapsed_cb), sidebar); - g_signal_connect (tree_view, "row-activated", - G_CALLBACK (row_activated_cb), sidebar); g_signal_connect (tree_view, "motion-notify-event", G_CALLBACK (motion_notify_cb), sidebar); g_signal_connect (tree_view, "leave-notify-event", @@ -5461,14 +5495,11 @@ nemo_places_sidebar_dispose (GObject *object) sidebar->tree_view = NULL; } - if (sidebar->window!=NULL) { - if(sidebar->hidden_files_changed_id != 0) { - g_signal_handler_disconnect (sidebar->window, + if(sidebar->hidden_files_changed_id != 0) { + g_signal_handler_disconnect (sidebar->window, sidebar->hidden_files_changed_id); - sidebar->hidden_files_changed_id = 0; - } - sidebar->window = NULL; - } + sidebar->hidden_files_changed_id = 0; + } g_clear_object (&sidebar->ui_manager); sidebar->ui_manager = NULL; @@ -5493,10 +5524,6 @@ nemo_places_sidebar_dispose (GObject *object) if (sidebar->update_places_on_idle_id != 0) { g_source_remove (sidebar->update_places_on_idle_id); sidebar->update_places_on_idle_id = 0; - } - 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; } if (sidebar->row_deleted_handler_id != 0) { g_signal_handler_disconnect(sidebar->store, sidebar->row_deleted_handler_id); @@ -5559,7 +5586,11 @@ nemo_places_sidebar_dispose (GObject *object) g_clear_object (&sidebar->volume_monitor); } - + if(sidebar->uri) { + g_free (sidebar->uri); + sidebar->uri = NULL; + } + sidebar->window = NULL; g_clear_object(&sidebar->store_filter); g_clear_object(&sidebar->store); G_OBJECT_CLASS (nemo_places_sidebar_parent_class)->dispose (object); @@ -5639,10 +5670,6 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, g_signal_connect_object (window, "loading_uri", G_CALLBACK (loading_uri_callback), sidebar, 0); - if(sidebar->uri) { - g_free (sidebar->uri); - sidebar->uri = NULL; - } g_signal_connect_object (sidebar->volume_monitor, "volume_added", G_CALLBACK (volume_added_callback), sidebar, 0); @@ -5777,4 +5804,3 @@ nemo_shortcuts_model_new (NemoPlacesSidebar *sidebar) return GTK_TREE_STORE (model); } - From a5a0934ad5720553389bef59a4140b7a182ad513 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Thu, 23 Oct 2025 00:50:14 +0200 Subject: [PATCH 12/19] key up fixed --- src/nemo-places-sidebar.c | 120 ++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 43 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index bd60fcc80..ac64258e2 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -453,7 +453,7 @@ free_place_info (PlaceInfo *info) g_free (info); } -// insert dummy node "Loading..." +// Insert dummy node "Loading..." static void add_children_lazy (NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter) @@ -660,7 +660,7 @@ static GList* snapshot_directory(GFile *dir) { g_file_enumerator_close(enumerator, NULL, NULL); g_object_unref(enumerator); - return g_list_reverse(files); // optional: sortiert + return g_list_reverse(files); // Optional: sorted } static gboolean @@ -686,7 +686,7 @@ poll_directory_changes(gpointer user_data) for (GList *l = added; l; l = l->next) { GFile *child = g_file_get_child(node_data->dir, l->data); on_gfile_monitor_changed( - NULL, // kein echter Monitor + NULL, // not a real monitor child, // file NULL, // other_file G_FILE_MONITOR_EVENT_CREATED, @@ -705,7 +705,7 @@ poll_directory_changes(gpointer user_data) g_object_unref(child); } - // Alte Snapshot freigeben + // Free old snapshot g_list_free_full(node_data->last_snapshot, g_free); node_data->last_snapshot = current; @@ -720,13 +720,13 @@ create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel { if(!uri) return NULL; - // Erstelle ein neues TreeNodeData-Objekt + // Create a new TreeNodeData object TreeNodeData *node_data = g_new0(TreeNodeData, 1); node_data->sidebar = sidebar; node_data->uri = g_strdup(uri); - // Erstelle ein NemoFile für die gegebene URI + // Create a NemoFile for the given URI GFile *gfile = g_file_new_for_uri(uri); NemoFile *nemo_file = nemo_file_get(gfile); @@ -737,7 +737,7 @@ create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel gtk_tree_path_free(path); if (!nemo_file) { - g_warning("Konnte NemoFile für URI '%s' nicht erstellen!", uri); + g_warning("// Could not create NemoFile for URI '%s'!", uri); g_free(node_data); g_object_unref(gfile); return NULL; @@ -766,7 +766,7 @@ create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel return node_data; } -// insert dummy childr node if a foulder exists +// Insert dummy child node if a folder exists static void add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *uri) @@ -794,7 +794,7 @@ add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { add_children_lazy(sidebar, parent_iter); g_object_unref(info); - break; // insert only one dummy + break; // Insert only one dummy } g_object_unref(info); } @@ -810,7 +810,7 @@ add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter } -// check if childrean are already loaded +// Check if children are already loaded static gboolean children_loaded (GtkTreeModel *model, GtkTreeIter *iter) { @@ -822,7 +822,7 @@ children_loaded (GtkTreeModel *model, GtkTreeIter *iter) } -// insert real children nodes +// Insert real children nodes static void add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *parent_uri) { @@ -835,7 +835,7 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con if (children_loaded(model, parent_iter)) return; - // prove if dummy nodes exist + // Check if dummy nodes exist if (gtk_tree_model_iter_children(model, &child, parent_iter)) { do { gboolean is_lazy = FALSE; @@ -844,10 +844,10 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con -1); if (is_lazy) { has_dummy = TRUE; - break; //found a summy node + break; // Found a dummy node } } while (gtk_tree_model_iter_next(model, &child)); - // if already real children exist -> nothing to do + // If real children already exist → nothing to do if (!has_dummy) { return; } @@ -889,13 +889,13 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con GFile *child_dir = g_file_get_child(dir, name); NemoFile *child_file = nemo_file_get(child_dir); - /* child_uri aus child_file holen */ + // Get child_uri from child_file char *child_uri = NULL; if (child_file) { const char *u = nemo_file_get_uri(child_file); if (u) child_uri = g_strdup(u); } - /* Wenn nemo_file_get fehlschlägt, fallback auf child_dir URI */ + // If nemo_file_get fails, fall back to child_dir URI if (!child_uri && child_dir) { child_uri = g_file_get_uri(child_dir); } @@ -913,7 +913,7 @@ add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, con } - // remove summy child node + // Remove dummy child node if (gtk_tree_model_iter_children(model, &child, parent_iter)) { do { gboolean is_lazy = FALSE; @@ -2066,7 +2066,7 @@ find_uri_recursive (GtkTreeModel *model, PLACES_SIDEBAR_COLUMN_TREE_LAZY, &is_lazy, -1); - /* Lazy-Loading / "(loading)"-Dummy prüfen */ + // Check for lazy-loading / "(loading)" dummy if (is_lazy) { if (uri) { /* -> Direkt echte Kinder laden */ @@ -2091,7 +2091,7 @@ find_uri_recursive (GtkTreeModel *model, } } - /* found hit? */ + // Found a match? if (uri && g_strcmp0(uri, location) == 0) { *out_path = gtk_tree_model_get_path(model, &child); g_free(tree_name); @@ -2099,7 +2099,7 @@ find_uri_recursive (GtkTreeModel *model, return TRUE; } - /* go recursively deeper */ + // Go recursively deeper GtkTreePath *subpath = NULL; if (find_uri_recursive(model, &child, location, &subpath, sidebar)) { *out_path = subpath; @@ -2108,7 +2108,7 @@ find_uri_recursive (GtkTreeModel *model, return TRUE; } - /* clean up */ + // Clean up g_free(tree_name); g_free(uri); uri = NULL; @@ -3145,7 +3145,7 @@ update_menu_states (NemoPlacesSidebar *sidebar) default: case G_DRIVE_START_STOP_TYPE_UNKNOWN: - /* uses defaults set above */ + // Uses defaults set above break; } } @@ -4050,18 +4050,20 @@ goto_first_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) *iter = child_iter; return TRUE; } + + static gboolean goto_prev_row_or_last_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); GtkTreeIter prev_iter; + GtkTreeIter current_iter; + GtkTreeIter deepest_iter; // Try to move to the previous sibling row - // 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) @@ -4071,33 +4073,64 @@ goto_prev_row_or_last_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter // 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) - gtk_tree_path_free(path); return TRUE; } - // The previous row is expanded → go down to its last visible child - GtkTreeIter child_iter; + // 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 - if (!gtk_tree_model_iter_children(model, &child_iter, &prev_iter)) { - gtk_tree_path_free(path); - return TRUE; // Expanded but has no children + 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; } - prev_iter = child_iter; - // Walk through all children until the last one - while (gtk_tree_model_iter_next(model, &child_iter)) { - prev_iter = child_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 last child - *iter = prev_iter; - gtk_tree_path_free(path); + // Replace current iter with the deepest child + *iter = deepest_iter; return TRUE; } + static gboolean iter_equal(const GtkTreeIter *a, const GtkTreeIter *b) { @@ -4122,16 +4155,16 @@ find_parent_or_next_sibling_row(NemoPlacesSidebar *sidebar, GtkTreeIter *iter) return FALSE; } - // search for the next visible sibling beside the parent + // 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; // keine Kinder → eine Ebene nach oben + continue; // No children → one level up } do { if (iter_equal(&child_iter, ¤t_iter)) { - // Wir sind am aktuellen Iter → nächstes Kind prüfen + // We are at the current iterator → check next child if (gtk_tree_model_iter_next(model, &child_iter)) { *iter = child_iter; return TRUE; @@ -4141,7 +4174,7 @@ find_parent_or_next_sibling_row(NemoPlacesSidebar *sidebar, GtkTreeIter *iter) } } while (gtk_tree_model_iter_next(model, &child_iter)); - // next sibling not found on this level→ one level higher + // Next sibling not found on this level → one level higher current_iter = parent_iter; } } @@ -4168,7 +4201,7 @@ select_prev_or_next_node (NemoPlacesSidebar *sidebar, } else { res = goto_first_child_if_expanded (sidebar, &iterCopy); if(!res) { - /* go to next child in same row*/ + // go to next child in same row iterCopy = *iter; res = gtk_tree_model_iter_next (model, &iterCopy); if(!res) { @@ -4192,6 +4225,7 @@ select_prev_or_next_node (NemoPlacesSidebar *sidebar, } + static gboolean find_next_row (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { From 465113d777def9b27220e35493dc8930a1fe2855 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Sat, 15 Nov 2025 22:04:50 +0100 Subject: [PATCH 13/19] existing issue while some changed hash during call of g_hash_table_get_keys --- libnemo-private/nemo-directory.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/libnemo-private/nemo-directory.c b/libnemo-private/nemo-directory.c index 0a11ae58d..55ec9f0f4 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 From 0f3783f1bb208dbc56cc724e9e2d51a0e5795628 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Mon, 17 Nov 2025 11:59:13 +0100 Subject: [PATCH 14/19] Extended FMTreeModel for usage in NemoPlacesSidebar --- src/nemo-tree-sidebar-model.c | 1296 ++++++++++++++++++++++++++++----- src/nemo-tree-sidebar-model.h | 28 +- 2 files changed, 1120 insertions(+), 204 deletions(-) diff --git a/src/nemo-tree-sidebar-model.c b/src/nemo-tree-sidebar-model.c index 92fdfcec7..86b4854e5 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; } - } - nemo_file_unref (file); + 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; + } + + /* 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,9 +2561,70 @@ 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; } +gboolean +fm_tree_model_path_get_iter (FMTreeModel *model, GtkTreePath *path, GtkTreeIter *iter) +{ + g_return_val_if_fail(FM_IS_TREE_MODEL(model), FALSE); + g_return_val_if_fail(path != NULL, FALSE); + g_return_val_if_fail(iter != NULL, FALSE); + + // Get the indices from the path + gint *indices = gtk_tree_path_get_indices(path); + gint depth = gtk_tree_path_get_depth(path); + + // Check if the path is valid + if (depth <= 0 || indices == NULL) { + return FALSE; + } + + // Start with the root node + TreeNode *node = model->details->head_root_node; + + // Check the path to find the desired node + for (gint i = 0; i < depth; i++) { + gint index = indices[i]; + + // Check if the index is valid + if (node == NULL) { + return FALSE; + } + + // Check sibling nodes to find the correct node + for (gint j = 0; j < index; j++) { + if (node == NULL) { + return FALSE; + } + node = node->next; + } + + // If we didn't find the node, return FALSE + if (node == NULL) { + return FALSE; + } + + // Go to the child node if we are not at the end of the path + if (i < depth - 1) { + node = node->first_child; + } + } + + // Create the GtkTreeIter for the found node + return make_iter_for_node(node, iter, model->details->stamp); +} + + static void do_update_node (NemoFile *file, FMTreeModel *model) @@ -1833,10 +2639,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 +2700,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 +2712,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 +2726,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 +2774,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 028287969..d92ea198f 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,25 @@ 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 fm_tree_model_path_get_iter (FMTreeModel *model, GtkTreePath *path, GtkTreeIter *iter); +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 +109,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 +117,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 */ + From bb72d9903cb07c779a6c918963815878194c753d Mon Sep 17 00:00:00 2001 From: kdmenk Date: Tue, 18 Nov 2025 03:07:19 +0100 Subject: [PATCH 15/19] extending treeview in nemo-places-sidebar.c using FMTreeModel --- src/nemo-places-sidebar.c | 2773 +++++++++++++++++++++---------------- 1 file changed, 1549 insertions(+), 1224 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index ac64258e2..25e3a2bb8 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,6 +89,7 @@ static gint menu_icon_pixels = 16; #define g_drive_is_removable g_drive_is_media_removable #endif + typedef struct { GtkScrolledWindow parent; GtkTreeView *tree_view; @@ -90,8 +99,10 @@ typedef struct { GtkCellRenderer *icon_cell_renderer; GtkCellRenderer *padding_cell_renderer; GtkCellRenderer *heading_cell_renderer; - char *uri; - GtkTreeStore *store; + char *selection_location; + gboolean selecting; + void *store; + GtkTreeModel *sort_model; GtkTreeModel *store_filter; NemoWindow *window; @@ -109,7 +120,8 @@ typedef struct { GtkActionGroup *action_action_group; guint action_action_group_merge_id; - gulong row_deleted_handler_id; + GtkActionGroup *tv_action_group; + guint tv_action_group_merge_id; gboolean actions_need_update; @@ -155,8 +167,17 @@ typedef struct { 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 { @@ -172,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, @@ -191,19 +215,16 @@ enum { 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_TREE_LAZY, // identify lazy loading dummies - PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, // set if childs are already known in treenode - PLACES_SIDEBAR_COLUMN_NODE_DATA, // extended for TreeNodeData pointer PLACES_SIDEBAR_COLUMN_COUNT }; typedef enum { + PLACES_TREE_FOLDER, PLACES_BUILT_IN, PLACES_XDG_DIR, PLACES_MOUNTED_VOLUME, PLACES_BOOKMARK, PLACES_HEADING, - PLACES_TREE_FOLDER, } PlaceType; typedef enum { @@ -254,12 +275,11 @@ 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 void nemo_places_sidebar_cleanup (GtkTreeStore *store); -static TreeNodeData* create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model, GtkTreeIter *iter); -static void tree_node_data_free (TreeNodeData *node_data); -static gboolean children_loaded (GtkTreeModel *model, GtkTreeIter *iter); static gchar *get_icon_name (const gchar *uri); -static void free_node_recursive(GtkTreeModel *model, GtkTreeIter *iter); +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 { @@ -330,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; + + // Falls Filtermodell → zum Kindmodell weiterreichen + 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)); + } + + // Falls Sortmodell → noch einen Schritt tiefer + 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) { @@ -356,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; // Du musst eine ungültige Iter zurückgeben + } + 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; } @@ -453,565 +531,6 @@ free_place_info (PlaceInfo *info) g_free (info); } -// Insert dummy node "Loading..." -static void -add_children_lazy (NemoPlacesSidebar *sidebar, - GtkTreeIter *parent_iter) -{ - GtkTreeIter dummy; - gtk_tree_store_append(sidebar->store, &dummy, parent_iter); - gtk_tree_store_set(sidebar->store, &dummy, - PLACES_SIDEBAR_COLUMN_TREE_LAZY, TRUE, - PLACES_SIDEBAR_COLUMN_TREE_NAME, _("Loading..."), - -1); -} - -static void -add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, - const char *uri); - - -gboolean -find_child_iter_by_uri_under_parent(GtkTreeModel *model, - GtkTreeIter *parent, - const char *uri, - GtkTreeIter *out_iter) -{ - GtkTreeIter child; - gboolean valid = gtk_tree_model_iter_children(model, &child, parent); - - while (valid) { - char *child_uri = NULL; - gtk_tree_model_get(model, &child, PLACES_SIDEBAR_COLUMN_URI, &child_uri, -1); - - if (child_uri && g_strcmp0(child_uri, uri) == 0) { - if (out_iter) - *out_iter = child; - - g_free(child_uri); - return TRUE; - } - - g_free(child_uri); - valid = gtk_tree_model_iter_next(model, &child); - } - - return FALSE; -} - -static void -add_new_child_node(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *name, const char *child_uri) -{ - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store); - GtkTreeIter child_iter; - gtk_tree_store_append(GTK_TREE_STORE(model), &child_iter, parent_iter); - TreeNodeData *node_data = create_tree_node_data(sidebar, child_uri, model, &child_iter); - - gtk_tree_store_set(GTK_TREE_STORE(model), &child_iter, - PLACES_SIDEBAR_COLUMN_TREE_NAME, name, - PLACES_SIDEBAR_COLUMN_URI, child_uri, - PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_TREE_FOLDER, - PLACES_SIDEBAR_COLUMN_GICON, g_themed_icon_new("folder"), - PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, FALSE, - PLACES_SIDEBAR_COLUMN_NODE_DATA, node_data, - -1); - - add_directory_children_lazy(sidebar, &child_iter, child_uri); -} - -static void -on_gfile_monitor_changed(GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - TreeNodeData *node_data = (TreeNodeData *)user_data; - GtkTreeStore *store = GTK_TREE_STORE(node_data->sidebar->store); - GtkTreeModel *model = GTK_TREE_MODEL(store); - GtkTreeIter parent_iter; - - if (!gtk_tree_row_reference_valid(node_data->tree_ref)) { - g_message("on_gfile_monitor_changed: node does not exist any more!"); - return; - } - GtkTreePath *path = gtk_tree_row_reference_get_path(node_data->tree_ref); - if (!gtk_tree_model_get_iter(model, &parent_iter, path)) { - g_warning("on_gfile_monitor_changed: could not extract node from path!"); - gtk_tree_path_free(path); - return; - } - - char *uri = g_file_get_uri(file); - char *other_uri = other_file ? g_file_get_uri(other_file) : NULL; - - switch (event_type) { - case G_FILE_MONITOR_EVENT_CREATED: - case G_FILE_MONITOR_EVENT_MOVED_IN: - if (g_file_query_file_type(file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) == G_FILE_TYPE_DIRECTORY) { - add_new_child_node(node_data->sidebar, &parent_iter, g_file_get_basename(file), uri); - } - break; - - case G_FILE_MONITOR_EVENT_DELETED: - case G_FILE_MONITOR_EVENT_MOVED_OUT: { - GtkTreeIter child_iter; - if (find_child_iter_by_uri_under_parent(model, &parent_iter, uri, &child_iter)) { - free_node_recursive(model, &child_iter); - gtk_tree_store_remove(store, &child_iter); - } - break; - } -/* -* no need for this code becase events G_FILE_MONITOR_EVENT_DELETED and G_FILE_MONITOR_EVENT_CREATED are triggered later... - case G_FILE_MONITOR_EVENT_MOVED: - case G_FILE_MONITOR_EVENT_RENAMED: - case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: - if (other_uri) { - GtkTreeIter child_iter; - if (find_child_iter_by_uri_under_parent(model, &parent_iter, uri, &child_iter)) { - gtk_tree_store_set(store, &child_iter, - PLACES_SIDEBAR_COLUMN_TREE_NAME, g_file_get_basename(other_file), - PLACES_SIDEBAR_COLUMN_URI, other_uri, - -1); - g_message("Folder renamed: %s -> %s", uri, other_uri); - } - } - break; -*/ - default: - break; - } - - g_free(uri); - g_free(other_uri); -} - -static void -tree_node_data_free(TreeNodeData *node_data) -{ - if (!node_data) { - return; - } - - if (node_data->monitor) { - g_file_monitor_cancel(node_data->monitor); - g_object_unref(node_data->monitor); - node_data->monitor = NULL; - } - if (node_data->poll_id) { - g_source_remove(node_data->poll_id); - node_data->poll_id = 0; - } - if (node_data->dir) { - g_object_unref(node_data->dir); - } - if (node_data->file) { - g_object_unref(node_data->file); - } - - if (node_data->uri) { - g_free(node_data->uri); - } - - if (node_data->tree_ref) { - gtk_tree_row_reference_free(node_data->tree_ref); - node_data->tree_ref = NULL; - } - g_list_free_full(node_data->last_snapshot, g_free); - node_data->last_snapshot =NULL; - - node_data->sidebar = NULL; - - g_free(node_data); -} - -static GList* snapshot_directory(GFile *dir) { - GError *error = NULL; - GList *files = NULL; - GFileEnumerator *enumerator = g_file_enumerate_children( - dir, - G_FILE_ATTRIBUTE_STANDARD_NAME, - G_FILE_QUERY_INFO_NONE, - NULL, - &error - ); - if (!enumerator) { - if (error) { - g_warning("Snapshot failed for %s: %s", - g_file_get_uri(dir), error->message); - g_clear_error(&error); - } - return NULL; - } - - GFileInfo *info; - while ((info = g_file_enumerator_next_file(enumerator, NULL, &error)) != NULL) { - const char *name = g_file_info_get_name(info); - files = g_list_prepend(files, g_strdup(name)); - g_object_unref(info); - } - - if (error) { - g_warning("enumerator failed for %s: %s", - g_file_get_uri(dir), error->message); - g_clear_error(&error); - } - g_file_enumerator_close(enumerator, NULL, NULL); - g_object_unref(enumerator); - - return g_list_reverse(files); // Optional: sorted -} - -static gboolean -poll_directory_changes(gpointer user_data) -{ - TreeNodeData *node_data = (TreeNodeData *)user_data; - if(!node_data->dir) { - return FALSE; - } - GList *current = snapshot_directory(node_data->dir); - - GList *added = NULL, *removed = NULL; - - for (GList *l = current; l; l = l->next) { - if (!g_list_find_custom(node_data->last_snapshot, l->data, (GCompareFunc)g_strcmp0)) - added = g_list_prepend(added, g_strdup(l->data)); - } - for (GList *l = node_data->last_snapshot; l; l = l->next) - { - if (!g_list_find_custom(current, l->data, (GCompareFunc)g_strcmp0)) - removed = g_list_prepend(removed, g_strdup(l->data)); - } - for (GList *l = added; l; l = l->next) { - GFile *child = g_file_get_child(node_data->dir, l->data); - on_gfile_monitor_changed( - NULL, // not a real monitor - child, // file - NULL, // other_file - G_FILE_MONITOR_EVENT_CREATED, - node_data); // user_data - g_object_unref(child); - } - - for (GList *l = removed; l; l = l->next) { - GFile *child = g_file_get_child(node_data->dir, l->data); - on_gfile_monitor_changed( - NULL, - child, - NULL, - G_FILE_MONITOR_EVENT_DELETED, - node_data); - g_object_unref(child); - } - - // Free old snapshot - g_list_free_full(node_data->last_snapshot, g_free); - node_data->last_snapshot = current; - - g_list_free_full(added, g_free); - g_list_free_full(removed, g_free); - - return TRUE; // Wiederholen -} - -static TreeNodeData* -create_tree_node_data (NemoPlacesSidebar *sidebar, const char *uri, GtkTreeModel *model, GtkTreeIter *iter) -{ - if(!uri) - return NULL; - // Create a new TreeNodeData object - TreeNodeData *node_data = g_new0(TreeNodeData, 1); - node_data->sidebar = sidebar; - - node_data->uri = g_strdup(uri); - - // Create a NemoFile for the given URI - GFile *gfile = g_file_new_for_uri(uri); - NemoFile *nemo_file = nemo_file_get(gfile); - - node_data->dir = g_file_new_for_uri(node_data->uri); - - GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(sidebar->store), iter); - node_data->tree_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(sidebar->store), path); - gtk_tree_path_free(path); - - if (!nemo_file) { - g_warning("// Could not create NemoFile for URI '%s'!", uri); - g_free(node_data); - g_object_unref(gfile); - return NULL; - } - - node_data->file = g_object_ref(nemo_file); - - node_data->poll_id = 0; - node_data->last_snapshot = NULL; - GError *error = NULL; - GFileMonitor *monitor = g_file_monitor_directory(gfile, G_FILE_MONITOR_NONE, NULL, &error); - - if (monitor) { - g_signal_connect(monitor, "changed", G_CALLBACK(on_gfile_monitor_changed), node_data); - node_data->monitor = monitor; - } else { - node_data->monitor = NULL; - g_clear_error(&error); - node_data->last_snapshot = snapshot_directory(node_data->dir); - node_data->poll_id = g_timeout_add(POLL_INTERVAL, poll_directory_changes, node_data); - } - - - g_object_unref(gfile); - - return node_data; -} - -// Insert dummy child node if a folder exists -static void -add_directory_children_lazy(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, - const char *uri) -{ - GFile *dir = g_file_new_for_uri(uri); - if (!dir) - return; - - GError *error = NULL; - GFileEnumerator *enumerator = g_file_enumerate_children( - dir, - G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, - &error); - - if (!enumerator) { - g_clear_error(&error); - g_object_unref(dir); - return; - } - - GFileInfo *info; - while ((info = g_file_enumerator_next_file(enumerator, NULL, &error)) != NULL) { - if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { - add_children_lazy(sidebar, parent_iter); - g_object_unref(info); - break; // Insert only one dummy - } - g_object_unref(info); - } - - if (error) { - g_warning("Error iterating directory '%s': %s", uri, error->message); - g_clear_error(&error); - } - - g_file_enumerator_close(enumerator, NULL, NULL); - g_object_unref(enumerator); - g_object_unref(dir); - -} - -// Check if children are already loaded -static gboolean -children_loaded (GtkTreeModel *model, GtkTreeIter *iter) -{ - gboolean loaded = FALSE; - gtk_tree_model_get(model, iter, - PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, &loaded, - -1); - return loaded; -} - - -// Insert real children nodes -static void -add_directory_children(NemoPlacesSidebar *sidebar, GtkTreeIter *parent_iter, const char *parent_uri) -{ - gboolean has_dummy = FALSE; - GtkTreeIter child; - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store); - - if(parent_uri == NULL) - return; - if (children_loaded(model, parent_iter)) - return; - - // Check if dummy nodes exist - if (gtk_tree_model_iter_children(model, &child, parent_iter)) { - do { - gboolean is_lazy = FALSE; - gtk_tree_model_get(model, &child, - PLACES_SIDEBAR_COLUMN_TREE_LAZY, &is_lazy, - -1); - if (is_lazy) { - has_dummy = TRUE; - break; // Found a dummy node - } - } while (gtk_tree_model_iter_next(model, &child)); - // If real children already exist → nothing to do - if (!has_dummy) { - return; - } - } - GtkTreeIter parent_iter_copy; - parent_iter_copy = *parent_iter; - - TreeNodeData *parent_node_data=NULL; - gtk_tree_model_get(model, parent_iter, - PLACES_SIDEBAR_COLUMN_NODE_DATA, &parent_node_data, - -1); - if(!parent_node_data) { - parent_node_data = create_tree_node_data(sidebar, parent_uri, model, parent_iter); - gtk_tree_store_set(GTK_TREE_STORE(model), &parent_iter_copy, - PLACES_SIDEBAR_COLUMN_NODE_DATA, parent_node_data, - -1); - } - - GFile *dir = g_file_new_for_uri(parent_uri); - GError *error = NULL; - GFileEnumerator *enumerator = g_file_enumerate_children( - dir, - G_FILE_ATTRIBUTE_STANDARD_NAME "," G_FILE_ATTRIBUTE_STANDARD_TYPE "," G_FILE_ATTRIBUTE_STANDARD_ICON, - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, - &error); - - if (!enumerator) { - g_warning("Failed to enumerate directory '%s': %s", parent_uri, error ? error->message : "unknown"); - g_clear_error(&error); - g_object_unref(dir); - return; - } - - GFileInfo *info; - while ((info = g_file_enumerator_next_file(enumerator, NULL, NULL)) != NULL) { - if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY) { - const char *name = g_file_info_get_name(info); - GFile *child_dir = g_file_get_child(dir, name); - NemoFile *child_file = nemo_file_get(child_dir); - - // Get child_uri from child_file - char *child_uri = NULL; - if (child_file) { - const char *u = nemo_file_get_uri(child_file); - if (u) child_uri = g_strdup(u); - } - // If nemo_file_get fails, fall back to child_dir URI - if (!child_uri && child_dir) { - child_uri = g_file_get_uri(child_dir); - } - - if (child_uri) { - add_new_child_node(sidebar, parent_iter, name, child_uri); - g_free(child_uri); - } - if (child_file) - g_object_unref(child_file); - if (child_dir) - g_object_unref(child_dir); - } - g_object_unref(info); - } - - - // Remove dummy child node - if (gtk_tree_model_iter_children(model, &child, parent_iter)) { - do { - gboolean is_lazy = FALSE; - gtk_tree_model_get(model, &child, - PLACES_SIDEBAR_COLUMN_TREE_LAZY, &is_lazy, - -1); - if (is_lazy) { - gtk_tree_store_remove(GTK_TREE_STORE(model), &child); - if(!gtk_tree_model_iter_children(model, &child, parent_iter)) - break; - } - } while (gtk_tree_model_iter_next(model, &child)); - } - - gtk_tree_store_set(GTK_TREE_STORE(model), parent_iter, - PLACES_SIDEBAR_COLUMN_CHILDREN_LOADED, TRUE, - -1); - - g_file_enumerator_close(enumerator, NULL, NULL); - g_object_unref(enumerator); - g_object_unref(dir); -} - -typedef struct _LazyLoadData { - NemoPlacesSidebar *sidebar; - GtkTreePath *parent_path; - char *uri; -} LazyLoadData; - -static void -free_lazy_load_data (LazyLoadData *data) -{ - if (!data) return; - if (data->parent_path) gtk_tree_path_free (data->parent_path); - if (data->uri) g_free(data->uri); - g_free (data); -} - -static gboolean -add_children_idle(gpointer user_data) -{ - LazyLoadData *data = user_data; - GtkTreeIter parent_iter; - - GtkTreeModel *store = GTK_TREE_MODEL(data->sidebar->store); - - if (!gtk_tree_model_get_iter(store, &parent_iter, data->parent_path)) { - g_warning("nemo-places-sidebar.c->[add_children_idle] Could not get iter for path (node may have been removed)"); - free_lazy_load_data (data); - return FALSE; - } - - add_directory_children(data->sidebar, &parent_iter, data->uri); - - free_lazy_load_data (data); - - return FALSE; -} - - -static void -test_expand_row_cb(GtkTreeView *tree_view, - GtkTreeIter *iter, - GtkTreePath *path, - gpointer user_data) -{ - NemoPlacesSidebar *sidebar = user_data; - GtkTreeModel *filter_model = gtk_tree_view_get_model(tree_view); - char *uri = NULL; - - gtk_tree_model_get(filter_model, iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); - if (!uri) { - return; - } - LazyLoadData *load_data = g_new0(LazyLoadData, 1); - load_data->sidebar = sidebar; - load_data->uri = g_strdup(uri); - - GtkTreePath *store_path = NULL; - - /* path conversion: filter → store */ - if (GTK_IS_TREE_MODEL_FILTER(filter_model)) { - GtkTreeIter store_iter; - gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(filter_model), - &store_iter, - iter); - store_path = gtk_tree_model_get_path(GTK_TREE_MODEL(sidebar->store), &store_iter); - } else { - store_path = gtk_tree_path_copy(path); - } - - load_data->parent_path = store_path; - - /* start asynchron */ - g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, add_children_idle, load_data, NULL); - g_free(uri); -} - static GtkTreeIter add_place (NemoPlacesSidebar *sidebar, PlaceType place_type, @@ -1049,9 +568,14 @@ 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, + 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, @@ -1070,22 +594,29 @@ add_place (NemoPlacesSidebar *sidebar, PLACES_SIDEBAR_COLUMN_DF_PERCENT, df_percent, PLACES_SIDEBAR_COLUMN_SHOW_DF, show_df_percent, -1); - - // add children - if (uri != NULL && sidebar->use_file_treeview) { - switch(section_type) { - case SECTION_NETWORK: - if(place_type != PLACES_BUILT_IN) - add_children_lazy(sidebar, &iter); - break; - case SECTION_DEVICES: - add_children_lazy(sidebar, &iter); - break; - default: - add_directory_children_lazy(sidebar, &iter, uri); - break; - } + } 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_object_unref(gicon); return cat_iter; @@ -1105,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); @@ -1115,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); } @@ -1164,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); } @@ -1206,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) { @@ -1320,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; @@ -1349,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); + } } - nemo_places_sidebar_cleanup(sidebar->store); + tree_model_remove_all_nodes(sidebar); sidebar->devices_header_added = FALSE; sidebar->bookmarks_header_added = FALSE; @@ -1372,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 */ @@ -1594,7 +1172,6 @@ update_places (NemoPlacesSidebar *sidebar) continue; } } - g_object_unref (root); g_object_unref (mount); } @@ -1867,30 +1444,183 @@ update_places (NemoPlacesSidebar *sidebar) g_free (tooltip); } - g_list_free_full (network_mounts, g_object_unref); + g_list_free_full (network_mounts, g_object_unref); + + /* network:// */ + mount_uri = (char *)"network:///"; /* No need to strdup */ + icon = NEMO_ICON_SYMBOLIC_NETWORK; + cat_iter = add_place (sidebar, PLACES_BUILT_IN, + SECTION_NETWORK, + _("Network"), icon, + mount_uri, NULL, NULL, NULL, 0, + _("Browse the contents of the network"), 0, FALSE, + cat_iter); + + /* restore selection */ + restore_expand_state (sidebar); + sidebar_update_restore_selection (sidebar, location, last_uri); + + actions_changed (sidebar); + + sidebar->updating_sidebar = FALSE; + + g_free (location); + 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; + } + /* Der leere Pfad [] bezieht sich auf top_root_node oder Dummy – überspringen */ + 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; + } - /* network:// */ - mount_uri = (char *)"network:///"; /* No need to strdup */ - icon = NEMO_ICON_SYMBOLIC_NETWORK; - cat_iter = add_place (sidebar, PLACES_BUILT_IN, - SECTION_NETWORK, - _("Network"), icon, - mount_uri, NULL, NULL, NULL, 0, - _("Browse the contents of the network"), 0, FALSE, - cat_iter); + 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)); - /* restore selection */ - restore_expand_state (sidebar); - sidebar_update_restore_selection (sidebar, location, last_uri); + 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)); - actions_changed (sidebar); + 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; +} - sidebar->updating_sidebar = 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); - g_free (location); - g_free (last_uri); + 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, @@ -2043,82 +1773,7 @@ desktop_setting_changed_callback (gpointer user_data) } -static gboolean -find_uri_recursive (GtkTreeModel *model, - GtkTreeIter *parent, /* NULL = Top-Level */ - const char *location, - GtkTreePath **out_path, - NemoPlacesSidebar *sidebar) -{ - GtkTreeIter child; - gboolean valid_child; - gchar *uri = NULL; - gchar *tree_name = NULL; - gboolean is_lazy=FALSE; - - /* iterate children of `parent` (or top-level when parent == NULL) */ - valid_child = gtk_tree_model_iter_children (model, &child, parent); - while (valid_child) { - is_lazy=FALSE; - gtk_tree_model_get (model, &child, - PLACES_SIDEBAR_COLUMN_URI, &uri, - PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_name, - PLACES_SIDEBAR_COLUMN_TREE_LAZY, &is_lazy, - -1); - - // Check for lazy-loading / "(loading)" dummy - if (is_lazy) { - if (uri) { - /* -> Direkt echte Kinder laden */ - GtkTreeIter store_iter; - gtk_tree_model_filter_convert_iter_to_child_iter( - GTK_TREE_MODEL_FILTER(sidebar->store_filter), - &store_iter, - &child - ); - - /* load synchron real childr in sidebar->store */ - add_directory_children(sidebar, &store_iter, uri); - - /* now the children list could be updated */ - g_free(tree_name); - g_free(uri); - uri = NULL; - tree_name = NULL; - - valid_child = gtk_tree_model_iter_children (model, &child, parent); - continue; - } - } - - // Found a match? - if (uri && g_strcmp0(uri, location) == 0) { - *out_path = gtk_tree_model_get_path(model, &child); - g_free(tree_name); - g_free(uri); - return TRUE; - } - - // Go recursively deeper - GtkTreePath *subpath = NULL; - if (find_uri_recursive(model, &child, location, &subpath, sidebar)) { - *out_path = subpath; - g_free(tree_name); - g_free(uri); - return TRUE; - } - - // Clean up - g_free(tree_name); - g_free(uri); - uri = NULL; - tree_name = NULL; - - valid_child = gtk_tree_model_iter_next(model, &child); - } - return FALSE; -} static void loading_uri_callback (NemoWindow *window, @@ -2126,134 +1781,57 @@ loading_uri_callback (NemoWindow *window, 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 (g_strcmp0 (sidebar->uri, location) == 0) return; - - g_free (sidebar->uri); - sidebar->uri = g_strdup (location); - - selection = gtk_tree_view_get_selection (sidebar->tree_view); - - if (sidebar->use_file_treeview) { - GtkTreeModel *model = GTK_TREE_MODEL (sidebar->store_filter); - GtkTreeIter current_iter; - GtkTreePath *current_path = NULL; - - if (gtk_tree_selection_get_selected(selection, NULL, ¤t_iter)) { - gchar *current_uri = NULL; - gtk_tree_model_get(model, ¤t_iter, - PLACES_SIDEBAR_COLUMN_URI, ¤t_uri, - -1); - - if (current_uri && g_strcmp0(current_uri, location) == 0) { - /* location already selected */ - g_free(current_uri); - return; - } - if (current_uri && g_str_has_prefix (location, current_uri)) { - GtkTreePath *iter_path = gtk_tree_model_get_path(model, ¤t_iter); - GtkTreePath *parent_path = gtk_tree_path_copy(iter_path); - gboolean parent_selected = FALSE; - - while (gtk_tree_path_get_depth (parent_path) > 0) { - GtkTreeIter parent_iter; - if (!gtk_tree_model_get_iter(model, &parent_iter, parent_path)) - break; - - char *parent_uri = NULL; - gtk_tree_model_get(model, &parent_iter, - PLACES_SIDEBAR_COLUMN_URI, &parent_uri, - -1); - - if (parent_uri && g_strcmp0(parent_uri, location) == 0) { - /* parent node found */ - gtk_tree_selection_select_path(selection, parent_path); - gtk_tree_view_expand_to_path(sidebar->tree_view, parent_path); - parent_selected = TRUE; - g_free(parent_uri); - break; - } - - g_free(parent_uri); - gtk_tree_path_up(parent_path); - } - - gtk_tree_path_free (iter_path); - gtk_tree_path_free (parent_path); - - if (parent_selected) { - g_free(current_uri); - return; - } - } - current_path = gtk_tree_model_get_path(model, ¤t_iter); - g_free(current_uri); - } - /* search for uri recursiv in tree */ - GtkTreePath *child_path = NULL; - if (find_uri_recursive (model, NULL, location, &child_path, sidebar)) { - GtkTreePath *parent_path = gtk_tree_path_copy(child_path); - - if (gtk_tree_path_get_depth(parent_path) > 1) { - gtk_tree_path_up(parent_path); - gtk_tree_view_expand_to_path(sidebar->tree_view, parent_path); - } - - /* select target node */ - gtk_tree_selection_select_path(selection, child_path); - /* scroll to target node */ - gtk_tree_view_scroll_to_cell(sidebar->tree_view, child_path, NULL, FALSE, 0.5, 0.5); - - gtk_tree_path_free(parent_path); - gtk_tree_path_free(child_path); - } - - if (current_path) - gtk_tree_path_free(current_path); - + if(sidebar->use_file_treeview) { + schedule_select_and_show_location (sidebar, location); } else { - /* set selection if any place matches location */ - gtk_tree_selection_unselect_all(selection); - - GtkTreeIter iter_cat, iter_child; - gboolean valid_cat = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (sidebar->store_filter), - &iter_cat); - char *uri = NULL; - - while (valid_cat) { - if (uri) g_free(uri); - - gboolean 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); - uri = NULL; - } - 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 (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; @@ -2388,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, @@ -2435,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, @@ -2549,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; @@ -2574,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, @@ -2723,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, @@ -2748,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, @@ -2780,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; @@ -2892,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)); @@ -2935,93 +2544,414 @@ drag_data_received_callback (GtkWidget *widget, gtk_drag_finish (context, success, FALSE, time); gtk_tree_path_free (tree_path); - g_timeout_add (250, (GSourceFunc) idle_hide_bookmarks, sidebar); + g_timeout_add (250, (GSourceFunc) idle_hide_bookmarks, sidebar); +} + +static gboolean +drag_drop_callback (GtkTreeView *tree_view, + GdkDragContext *context, + int x, + int y, + unsigned int time, + NemoPlacesSidebar *sidebar) +{ + gboolean retval = FALSE; + sidebar->drop_occured = TRUE; + retval = get_drag_data (tree_view, context, time); + g_signal_stop_emission_by_name (tree_view, "drag-drop"); + return retval; +} + +static void +check_unmount_and_eject (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_unmount, + gboolean *show_eject) +{ + *show_unmount = FALSE; + *show_eject = FALSE; + + if (drive != NULL) { + *show_eject = g_drive_can_eject (drive); + } + + if (volume != NULL) { + *show_eject |= g_volume_can_eject (volume); + } + if (mount != NULL) { + *show_eject |= g_mount_can_eject (mount); + *show_unmount = g_mount_can_unmount (mount) && !*show_eject; + } +} + +static void +check_visibility (GMount *mount, + GVolume *volume, + GDrive *drive, + gboolean *show_mount, + gboolean *show_unmount, + gboolean *show_eject, + gboolean *show_rescan, + gboolean *show_start, + gboolean *show_stop) +{ + *show_mount = FALSE; + *show_rescan = FALSE; + *show_start = FALSE; + *show_stop = FALSE; + + check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject); + + if (drive != NULL) { + if (g_drive_is_removable (drive) && + !g_drive_is_media_check_automatic (drive) && + g_drive_can_poll_for_media (drive)) + *show_rescan = TRUE; + + *show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive); + *show_stop = g_drive_can_stop (drive); + + if (*show_stop) + *show_unmount = FALSE; + } + + if (volume != NULL) { + if (mount == NULL) + *show_mount = g_volume_can_mount (volume); + } +} + +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, + gboolean visible) +{ + GtkAction *action; + + action = gtk_action_group_get_action (action_group, name); + gtk_action_set_visible (action, visible); +} + +static GdkAtom copied_files_atom; + +static void +clipboard_contents_received_callback (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) +{ + NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (data); + + 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); + } +} + + +static gboolean +is_parent_writable (NemoFile *file) +{ + NemoFile *parent; + gboolean result; + + parent = nemo_file_get_parent (file); + + /* No parent directory, return FALSE */ + if (parent == NULL) { + return FALSE; + } + + result = nemo_file_can_write (parent); + nemo_file_unref (parent); + + return result; +} + +static GtkWindow * +fm_tree_view_get_containing_window (NemoPlacesSidebar *sidebar) +{ + GtkWidget *window; + + g_assert (NEMO_PLACES_SIDEBAR (sidebar)); + + window = gtk_widget_get_ancestor (GTK_WIDGET (sidebar), GTK_TYPE_WINDOW); + if (window == NULL) { + return NULL; + } + + return GTK_WINDOW (window); +} + +static void +fm_tree_view_delete_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) +{ + GList *location_list; + + if (!g_settings_get_boolean (nemo_preferences, NEMO_PREFERENCES_ENABLE_DELETE)) { + return; + } + + location_list = g_list_prepend (NULL, + nemo_file_get_location (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 gboolean -drag_drop_callback (GtkTreeView *tree_view, - GdkDragContext *context, - int x, - int y, - unsigned int time, - NemoPlacesSidebar *sidebar) +static void +fm_tree_view_pin_unpin_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) { - gboolean retval = FALSE; - sidebar->drop_occured = TRUE; - retval = get_drag_data (tree_view, context, time); - g_signal_stop_emission_by_name (tree_view, "drag-drop"); - return retval; + nemo_file_set_pinning (sidebar->popup_file, + !nemo_file_get_pinning (sidebar->popup_file)); } static void -check_unmount_and_eject (GMount *mount, - GVolume *volume, - GDrive *drive, - gboolean *show_unmount, - gboolean *show_eject) +paste_clipboard_data (NemoPlacesSidebar *sidebar, + GtkSelectionData *selection_data, + char *destination_uri) { - *show_unmount = FALSE; - *show_eject = FALSE; + gboolean cut; + GList *item_uris; - if (drive != NULL) { - *show_eject = g_drive_can_eject (drive); - } + cut = FALSE; + item_uris = nemo_clipboard_get_uri_list_from_selection_data (selection_data, &cut, + copied_files_atom); - if (volume != NULL) { - *show_eject |= g_volume_can_eject (volume); - } - if (mount != NULL) { - *show_eject |= g_mount_can_eject (mount); - *show_unmount = g_mount_can_unmount (mount) && !*show_eject; + 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 -check_visibility (GMount *mount, - GVolume *volume, - GDrive *drive, - gboolean *show_mount, - gboolean *show_unmount, - gboolean *show_eject, - gboolean *show_rescan, - gboolean *show_start, - gboolean *show_stop) +paste_into_clipboard_received_callback (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + gpointer data) { - *show_mount = FALSE; - *show_rescan = FALSE; - *show_start = FALSE; - *show_stop = FALSE; + char *directory_uri; + NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (data); - check_unmount_and_eject (mount, volume, drive, show_unmount, show_eject); + directory_uri = nemo_file_get_uri (sidebar->popup_file); - if (drive != NULL) { - if (g_drive_is_removable (drive) && - !g_drive_is_media_check_automatic (drive) && - g_drive_can_poll_for_media (drive)) - *show_rescan = TRUE; + paste_clipboard_data (sidebar, selection_data, directory_uri); - *show_start = g_drive_can_start (drive) || g_drive_can_start_degraded (drive); - *show_stop = g_drive_can_stop (drive); + g_free (directory_uri); +} - if (*show_stop) - *show_unmount = FALSE; - } +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); +} - if (volume != NULL) { - if (mount == NULL) - *show_mount = g_volume_can_mount (volume); +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 -set_action_visible (GtkActionGroup *action_group, - const gchar *name, - gboolean visible) +fm_tree_view_cut_cb (GtkAction *action, + NemoPlacesSidebar *sidebar) { - GtkAction *action; + copy_or_cut_files (sidebar, TRUE); +} - action = gtk_action_group_get_action (action_group, name); - gtk_action_set_visible (action, visible); +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 @@ -3050,8 +2980,8 @@ update_menu_states (NemoPlacesSidebar *sidebar) return; } - 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_ROW_TYPE, &type, PLACES_SIDEBAR_COLUMN_DRIVE, &drive, PLACES_SIDEBAR_COLUMN_VOLUME, &volume, @@ -3059,9 +2989,15 @@ update_menu_states (NemoPlacesSidebar *sidebar) 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 = nemo_file_get_by_uri (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); @@ -3073,28 +3009,13 @@ update_menu_states (NemoPlacesSidebar *sidebar) TRUE, FALSE, GTK_WINDOW (sidebar->window)); - nemo_file_list_free (selection); + /* nemo_file_list_free (selection); internally unrefs file! */ + nemo_file_list_free (selection); 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 ()); - - check_visibility (mount, volume, drive, - &show_mount, &show_unmount, &show_eject, &show_rescan, &show_start, &show_stop); - - /* 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); - /* Only show properties for local mounts */ - show_properties = (mount != NULL); + //show_properties = (mount != NULL); if (mount != NULL) { location = g_mount_get_default_location (mount); directory = nemo_directory_get (location); @@ -3105,14 +3026,87 @@ update_menu_states (NemoPlacesSidebar *sidebar) 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; @@ -3156,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) @@ -3163,6 +3158,7 @@ bookmarks_selection_changed_cb (GtkTreeSelection *selection, update_menu_states (sidebar); } + static void volume_mounted_cb (GVolume *volume, gboolean success, @@ -3242,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); @@ -3269,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; @@ -3348,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; } @@ -3391,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); @@ -3401,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); @@ -3425,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); @@ -3437,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); @@ -3461,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); @@ -3543,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); @@ -3691,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, @@ -3720,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; @@ -3765,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; } @@ -3806,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); @@ -3850,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); @@ -3904,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); @@ -3931,11 +3943,11 @@ empty_trash_cb (GtkAction *item, static gboolean select_first_child_row_or_expand (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); GtkTreeIter child_iter; gboolean res; - GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), iter); + GtkTreePath *path = gtk_tree_model_get_path (model, iter); if (!path) return FALSE; gboolean expanded = gtk_tree_view_row_expanded(sidebar->tree_view, path); @@ -3956,11 +3968,11 @@ select_first_child_row_or_expand (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) static gboolean goto_parent_or_collapse (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); GtkTreeIter parent_iter; gboolean res=TRUE; - GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), iter); + GtkTreePath *path = gtk_tree_model_get_path (model, iter); if (!path) return FALSE; gboolean expanded = gtk_tree_view_row_expanded(sidebar->tree_view, path); @@ -3972,7 +3984,7 @@ goto_parent_or_collapse (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) if(res) { *iter = parent_iter; gtk_tree_path_free (path); - 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); } } @@ -3987,7 +3999,7 @@ 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; @@ -4021,7 +4033,7 @@ find_prev_or_next_row (NemoPlacesSidebar *sidebar, static gboolean goto_first_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); GtkTreePath *path; GtkTreeIter child_iter; @@ -4051,11 +4063,10 @@ goto_first_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) return TRUE; } - static gboolean goto_prev_row_or_last_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); GtkTreeIter prev_iter; GtkTreeIter current_iter; GtkTreeIter deepest_iter; @@ -4132,15 +4143,25 @@ goto_prev_row_or_last_child_if_expanded (NemoPlacesSidebar *sidebar, GtkTreeIter static gboolean -iter_equal(const GtkTreeIter *a, const GtkTreeIter *b) +iter_equal(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b) { - return memcmp(a, b, sizeof(GtkTreeIter)) == 0; + GtkTreePath *path_a = gtk_tree_model_get_path(model, a); + GtkTreePath *path_b = gtk_tree_model_get_path(model, b); + + gboolean equal = gtk_tree_path_compare(path_a, path_b) == 0; + + gtk_tree_path_free(path_a); + gtk_tree_path_free(path_b); + + return equal; } static gboolean find_parent_or_next_sibling_row(NemoPlacesSidebar *sidebar, GtkTreeIter *iter) { - GtkTreeModel *model = GTK_TREE_MODEL(sidebar->store_filter); + GtkTreeModel *model = gtk_tree_view_get_model (sidebar->tree_view); GtkTreeIter current_iter = *iter; while (TRUE) { @@ -4163,7 +4184,7 @@ find_parent_or_next_sibling_row(NemoPlacesSidebar *sidebar, GtkTreeIter *iter) } do { - if (iter_equal(&child_iter, ¤t_iter)) { + 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; @@ -4185,7 +4206,7 @@ select_prev_or_next_node (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 iterCopy; @@ -4213,13 +4234,13 @@ select_prev_or_next_node (NemoPlacesSidebar *sidebar, } } if (res) { - *iter = iterCopy; - GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (sidebar->store_filter), &iterCopy); + *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); + 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; @@ -4234,37 +4255,15 @@ find_next_row (NemoPlacesSidebar *sidebar, GtkTreeIter *iter) static void properties_cb (GtkAction *item, - NemoPlacesSidebar *sidebar) + NemoPlacesSidebar *sidebar) { - GtkTreeModel *model; - GtkTreePath *path = NULL; - GtkTreeIter iter; GList *list; - NemoFile *file; - char *uri; - - model = gtk_tree_view_get_model (sidebar->tree_view); - gtk_tree_view_get_cursor (sidebar->tree_view, &path, NULL); - - if (path == NULL || !gtk_tree_model_get_iter (model, &iter, path)) { - gtk_tree_path_free (path); - return; - } - - gtk_tree_model_get (model, &iter, PLACES_SIDEBAR_COLUMN_URI, &uri, -1); - - if (uri != NULL) { - - file = nemo_file_get_by_uri (uri); - list = g_list_prepend (NULL, nemo_file_ref (file)); - - nemo_properties_window_present (list, GTK_WIDGET (sidebar), NULL); + if(sidebar->popup_file == NULL) return; + list = g_list_prepend (NULL, nemo_file_ref (sidebar->popup_file)); - nemo_file_list_free (list); - g_free (uri); - } + nemo_properties_window_present (list, GTK_WIDGET (sidebar->tree_view), NULL); - gtk_tree_path_free (path); + nemo_file_list_free (list); } static gboolean @@ -4275,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); } @@ -4327,9 +4326,11 @@ bookmarks_key_press_event_cb (GtkWidget *widget, } 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), + 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); @@ -4339,26 +4340,31 @@ bookmarks_key_press_event_cb (GtkWidget *widget, 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; } @@ -4372,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); } @@ -4394,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, @@ -4434,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)); @@ -4516,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[] = { @@ -4535,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) { @@ -4558,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); @@ -4566,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); @@ -4575,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), @@ -4708,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); @@ -4719,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; } @@ -4749,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 }; @@ -4759,7 +4891,7 @@ motion_notify_cb (GtkWidget *widget, gtk_tree_path_free (store_path); } - gtk_tree_path_free (path); + return FALSE; } @@ -4845,7 +4977,7 @@ query_tooltip_callback (GtkWidget *widget, tooltip_markup = g_strdup (_("Stop")); } } else { - if (type == PLACES_TREE_FOLDER) { + if (type == PLACES_BUILT_IN) { return FALSE; } gtk_tree_model_get (model, @@ -4940,6 +5072,40 @@ row_expanded_cb (GtkTreeView *tree_view, gtk_tree_selection_select_path(sel, path); } +static void +row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer user_data) +{ + GtkTreeIter iter; + SectionType section_type; + PlaceType place_type; + + NemoPlacesSidebar *sidebar = NEMO_PLACES_SIDEBAR (user_data); + 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, + -1); + if (place_type == PLACES_HEADING) { + if (section_type == SECTION_COMPUTER) { + sidebar->my_computer_expanded = !sidebar->my_computer_expanded; + } else if (section_type == SECTION_BOOKMARKS) { + sidebar->bookmarks_expanded = !sidebar->bookmarks_expanded; + } else if (section_type == SECTION_DEVICES) { + sidebar->devices_expanded = !sidebar->devices_expanded; + } else if (section_type == SECTION_NETWORK) { + sidebar->network_expanded = !sidebar->network_expanded; + } + restore_expand_state (sidebar); + } +} + static void bookmarks_edited (GtkCellRenderer *cell, gchar *path_string, @@ -4954,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); @@ -5025,29 +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, - PLACES_SIDEBAR_COLUMN_GICON, &gicon, - -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, - "gicon", gicon, - 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; // Standardverhalten beibehalten +} - if (gicon) - g_object_unref(gicon); +static gboolean +on_tree_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) +{ + set_treeview_style(GTK_TREE_VIEW(widget), FALSE); + return FALSE; // Standardverhalten beibehalten } + static void padding_cell_renderer_func (GtkTreeViewColumn *column, GtkCellRenderer *cell, @@ -5073,21 +5278,17 @@ text_cell_renderer_func(GtkTreeViewColumn *column, GtkTreeIter *iter, gpointer user_data) { - gchar *tree_text = NULL; gchar *name_text = NULL; gchar *heading_text = NULL; gint row_type = 0; - gboolean lazy = FALSE; gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &row_type, - PLACES_SIDEBAR_COLUMN_TREE_NAME, &tree_text, PLACES_SIDEBAR_COLUMN_NAME, &name_text, - PLACES_SIDEBAR_COLUMN_TREE_LAZY, &lazy, -1); if (row_type == PLACES_HEADING) { - g_free(tree_text); + /* For Headings use HEADING_TEXT (bold) */ g_free(name_text); gtk_tree_model_get(model, iter, @@ -5103,9 +5304,7 @@ text_cell_renderer_func(GtkTreeViewColumn *column, g_free(heading_text); } else { const gchar *display = ""; - if (tree_text != NULL && *tree_text != '\0') - display = tree_text; - else if (name_text != NULL && *name_text != '\0') + if (name_text != NULL && *name_text != '\0') display = name_text; g_object_set(renderer, @@ -5114,107 +5313,15 @@ text_cell_renderer_func(GtkTreeViewColumn *column, g_object_set(renderer, "text", display, NULL); - g_free(tree_text); g_free(name_text); } } - -static void -free_node_free_node_data(GtkTreeModel *model, GtkTreeIter *iter) -{ - /* process current node */ - TreeNodeData *node_data = NULL; - gtk_tree_model_get(model, iter, - PLACES_SIDEBAR_COLUMN_NODE_DATA, &node_data, - -1); - if (node_data) { - tree_node_data_free(node_data); - /* set pointer in store to NULL to avoid dangling pointer */ - if (GTK_IS_TREE_STORE(model)) { - gtk_tree_store_set(GTK_TREE_STORE(model), iter, - PLACES_SIDEBAR_COLUMN_NODE_DATA, NULL, - -1); - } - } -} - -static void -free_node_recursive(GtkTreeModel *model, GtkTreeIter *iter) -{ - GtkTreeIter child; - - free_node_free_node_data(model, iter); - - /* process children recursively */ - if (gtk_tree_model_iter_children(model, &child, iter)) { - free_node_recursive(model, &child); - while (gtk_tree_model_iter_next(model, &child)) { - free_node_recursive(model, &child); - } - } -} - -static void -nemo_places_sidebar_cleanup(GtkTreeStore *store) -{ - if (!store) - return; - - // walk throug TreeNodes and freeTreeNodeData - GtkTreeIter iter; - GtkTreeModel *model = GTK_TREE_MODEL(store); - - if (gtk_tree_model_get_iter_first(model, &iter)) { - /* call rekursive all node_data (inc. children) */ - do { - free_node_recursive(model, &iter); - } while (gtk_tree_model_iter_next(model, &iter)); - } - gtk_tree_store_clear (store); -} - - -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; -} - -static gboolean -on_tree_focus_out(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) +enum { - set_treeview_style(GTK_TREE_VIEW(widget), FALSE); - return FALSE; -} - + PROP_DISK_FULL_PERCENTAGE = 1, + PROP_SHOW_DISK_FULL_PERCENTAGE = 2, +}; static void nemo_places_tree_sidebar_renderer_init(NemoPlacesSidebar *sidebar, GtkTreeView *tree_view) @@ -5291,7 +5398,121 @@ static void hidden_files_mode_changed_callback (NemoWindow *window, NemoPlacesSidebar *sidebar) { - gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (sidebar->store_filter)); + update_filtering_from_preferences (sidebar); +} + +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 { + fm_tree_model_set_highlight_for_files ( FM_TREE_MODEL (sidebar->store), NULL); + } +} + +static void +refresh_highlight (NemoPlacesSidebar *sidebar) +{ + NemoClipboardMonitor *monitor; + NemoClipboardInfo *info; + + 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 (file_a == file_b) { + result = 0; + } else if (file_a == NULL) { + result = -1; + } else if (file_b == NULL) { + result = +1; + } else { + result = nemo_file_compare_for_sort (file_a, file_b, + NEMO_FILE_SORT_BY_DISPLAY_NAME, + 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); } @@ -5309,20 +5530,6 @@ row_visibility_function (GtkTreeModel *model, PLACES_SIDEBAR_COLUMN_SECTION_TYPE, §ion_type, PLACES_SIDEBAR_COLUMN_ROW_TYPE, &type, -1); - if (type == PLACES_TREE_FOLDER) { - NemoWindowShowHiddenFilesMode mode = nemo_window_get_hidden_files_mode (sidebar->window); - // --- 2. hide hidden folders --- - if ((mode == NEMO_WINDOW_SHOW_HIDDEN_FILES_ENABLE)) return TRUE; - - gboolean visible = TRUE; - TreeNodeData *node_data = NULL; - gtk_tree_model_get(model, iter, PLACES_SIDEBAR_COLUMN_NODE_DATA, &node_data, -1); - if(node_data) { - if (nemo_file_is_hidden_file(node_data->file)) - visible = FALSE; - } - return visible; - } if (type != PLACES_HEADING || section_type != SECTION_BOOKMARKS) return TRUE; @@ -5336,6 +5543,15 @@ 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) { @@ -5358,7 +5574,7 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) sidebar->use_file_treeview = TRUE; sidebar->desktop_dnd_source_fs = NULL; - sidebar->desktop_dnd_can_delete_source = FALSE; + sidebar->desktop_dnd_can_delete_source = TRUE; sidebar->volume_monitor = g_volume_monitor_get (); @@ -5368,7 +5584,6 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) sidebar-> hidden_files_changed_id = 0; - sidebar->row_deleted_handler_id = 0; sidebar->my_computer_expanded = g_settings_get_boolean (nemo_window_state, NEMO_WINDOW_STATE_MY_COMPUTER_EXPANDED); @@ -5394,36 +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); + 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)); - nemo_places_tree_sidebar_renderer_init (sidebar, tree_view); + } else { + /* tree view */ + tree_view = GTK_TREE_VIEW (nemo_places_tree_view_new ()); - sidebar->store = nemo_shortcuts_model_new (sidebar); - gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (sidebar->store), - PLACES_SIDEBAR_COLUMN_TREE_NAME, - GTK_SORT_ASCENDING); + gtk_tree_view_set_headers_visible (tree_view, FALSE); - sidebar->store_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (sidebar->store), NULL); + sidebar->store = nemo_shortcuts_model_new (sidebar); - gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (sidebar->store_filter), - (GtkTreeModelFilterVisibleFunc) row_visibility_function, - sidebar, NULL); + sidebar->sort_model = (GtkTreeModel*)GTK_TREE_MODEL_SORT (gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (sidebar->store))); - gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (sidebar->store_filter)); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (tree_view), PLACES_SIDEBAR_COLUMN_NAME); - gtk_container_add (GTK_CONTAINER (sidebar), GTK_WIDGET (tree_view)); - gtk_widget_show (GTK_WIDGET (tree_view)); + 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->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_view_set_model (tree_view, GTK_TREE_MODEL (sidebar->store_filter)); + } - gtk_widget_show (GTK_WIDGET (sidebar)); 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)); + + 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, @@ -5434,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); @@ -5463,6 +5732,8 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) G_CALLBACK (row_expanded_cb), sidebar); g_signal_connect (tree_view, "row-collapsed", G_CALLBACK (row_collapsed_cb), sidebar); + g_signal_connect (tree_view, "row-activated", + G_CALLBACK (row_activated_cb), sidebar); g_signal_connect (tree_view, "motion-notify-event", G_CALLBACK (motion_notify_cb), sidebar); g_signal_connect (tree_view, "leave-notify-event", @@ -5472,15 +5743,10 @@ 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(tree_view, - "test-expand-row", - G_CALLBACK(test_expand_row_cb), - sidebar); - - g_signal_connect(tree_view, "focus-in-event", + g_signal_connect(sidebar->tree_view, "focus-in-event", G_CALLBACK(on_tree_focus_in), NULL); - g_signal_connect(tree_view, "focus-out-event", + 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, @@ -5504,6 +5770,21 @@ 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 (gtk_tree_view_get_selection (GTK_TREE_VIEW (sidebar->tree_view)), "changed", + // G_CALLBACK (selection_changed_callback), sidebar, 0); + + 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 @@ -5512,7 +5793,7 @@ nemo_places_sidebar_dispose (GObject *object) NemoPlacesSidebar *sidebar; sidebar = NEMO_PLACES_SIDEBAR (object); - nemo_places_sidebar_cleanup(sidebar->store); + tree_model_remove_all_nodes(sidebar); g_clear_object (&sidebar->desktop_dnd_source_fs); @@ -5559,12 +5840,11 @@ nemo_places_sidebar_dispose (GObject *object) g_source_remove (sidebar->update_places_on_idle_id); sidebar->update_places_on_idle_id = 0; } - if (sidebar->row_deleted_handler_id != 0) { - g_signal_handler_disconnect(sidebar->store, sidebar->row_deleted_handler_id); - sidebar->row_deleted_handler_id = 0; + 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); @@ -5620,16 +5900,30 @@ nemo_places_sidebar_dispose (GObject *object) g_clear_object (&sidebar->volume_monitor); } - if(sidebar->uri) { - g_free (sidebar->uri); - sidebar->uri = NULL; + if(sidebar->selection_location) { + g_free (sidebar->selection_location); + sidebar->selection_location = NULL; } sidebar->window = NULL; - g_clear_object(&sidebar->store_filter); - g_clear_object(&sidebar->store); 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) { @@ -5637,6 +5931,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; @@ -5645,6 +5940,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 @@ -5680,10 +5976,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); @@ -5701,9 +6006,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); + sidebar->volume_monitor = g_volume_monitor_get (); g_signal_connect_object (sidebar->volume_monitor, "volume_added", G_CALLBACK (volume_added_callback), sidebar, 0); @@ -5726,9 +6029,6 @@ 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); - sidebar->hidden_files_changed_id = - g_signal_connect_object (window, "hidden-files-mode-changed", - G_CALLBACK (hidden_files_mode_changed_callback), sidebar, 0); // update_places (sidebar); } @@ -5739,7 +6039,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 * @@ -5805,28 +6105,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, - G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, - G_TYPE_POINTER // TreeNodeData* + 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); @@ -5838,3 +6138,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; +} From ff3c84365602d89e2bf6b2ff8895855286600398 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Tue, 18 Nov 2025 03:27:35 +0100 Subject: [PATCH 16/19] Tree View implementation for file tree --- src/nemo-places-sidebar.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index a155afea7..2d9e5a240 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -359,14 +359,14 @@ get_fm_tree_model_iter (GtkTreeModel *model, GtkTreeIter tmp_iter = *iter; GtkTreeModel *child_model = model; - // Falls Filtermodell → zum Kindmodell weiterreichen + // 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)); } - // Falls Sortmodell → noch einen Schritt tiefer + // 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); @@ -423,7 +423,7 @@ add_heading (NemoPlacesSidebar *sidebar, 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; // Du musst eine ungültige Iter zurückgeben + return cat_iter; // You must return an invalid iter } fm_tree_model_set (sidebar->store, &cat_iter, PLACES_SIDEBAR_COLUMN_ROW_TYPE, PLACES_HEADING, @@ -1564,7 +1564,7 @@ show_selection_idle_callback (gpointer callback_data) g_warning("Cannot get child path from iter"); return FALSE; } - /* Der leere Pfad [] bezieht sich auf top_root_node oder Dummy – überspringen */ + // 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); @@ -5242,14 +5242,14 @@ static gboolean on_tree_focus_in(GtkWidget *widget, GdkEventFocus *event, gpointer user_data) { set_treeview_style(GTK_TREE_VIEW(widget), TRUE); - return FALSE; // Standardverhalten beibehalten + 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; // Standardverhalten beibehalten + return FALSE; // Keep default behavior } @@ -5774,9 +5774,6 @@ nemo_places_sidebar_init (NemoPlacesSidebar *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 (gtk_tree_view_get_selection (GTK_TREE_VIEW (sidebar->tree_view)), "changed", - // G_CALLBACK (selection_changed_callback), sidebar, 0); - g_signal_connect_object (sidebar->store, "row_loaded", G_CALLBACK (row_loaded_callback), sidebar, G_CONNECT_AFTER); From 6ae27778682a26f9fa7fd672aea447c971e04cf7 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Wed, 19 Nov 2025 17:08:43 +0100 Subject: [PATCH 17/19] removed not used function --- src/nemo-tree-sidebar-model.c | 52 ----------------------------------- src/nemo-tree-sidebar-model.h | 1 - 2 files changed, 53 deletions(-) diff --git a/src/nemo-tree-sidebar-model.c b/src/nemo-tree-sidebar-model.c index 86b4854e5..e55bd4c1c 100644 --- a/src/nemo-tree-sidebar-model.c +++ b/src/nemo-tree-sidebar-model.c @@ -2573,58 +2573,6 @@ fm_tree_model_file_get_iter (FMTreeModel *model, return FALSE; } -gboolean -fm_tree_model_path_get_iter (FMTreeModel *model, GtkTreePath *path, GtkTreeIter *iter) -{ - g_return_val_if_fail(FM_IS_TREE_MODEL(model), FALSE); - g_return_val_if_fail(path != NULL, FALSE); - g_return_val_if_fail(iter != NULL, FALSE); - - // Get the indices from the path - gint *indices = gtk_tree_path_get_indices(path); - gint depth = gtk_tree_path_get_depth(path); - - // Check if the path is valid - if (depth <= 0 || indices == NULL) { - return FALSE; - } - - // Start with the root node - TreeNode *node = model->details->head_root_node; - - // Check the path to find the desired node - for (gint i = 0; i < depth; i++) { - gint index = indices[i]; - - // Check if the index is valid - if (node == NULL) { - return FALSE; - } - - // Check sibling nodes to find the correct node - for (gint j = 0; j < index; j++) { - if (node == NULL) { - return FALSE; - } - node = node->next; - } - - // If we didn't find the node, return FALSE - if (node == NULL) { - return FALSE; - } - - // Go to the child node if we are not at the end of the path - if (i < depth - 1) { - node = node->first_child; - } - } - - // Create the GtkTreeIter for the found node - return make_iter_for_node(node, iter, model->details->stamp); -} - - static void do_update_node (NemoFile *file, FMTreeModel *model) diff --git a/src/nemo-tree-sidebar-model.h b/src/nemo-tree-sidebar-model.h index d92ea198f..283362bfb 100644 --- a/src/nemo-tree-sidebar-model.h +++ b/src/nemo-tree-sidebar-model.h @@ -87,7 +87,6 @@ gboolean fm_tree_model_append_head_root_node (FMTreeModel *model, const char *n 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 fm_tree_model_path_get_iter (FMTreeModel *model, GtkTreePath *path, GtkTreeIter *iter); gboolean iter_is_valid (FMTreeModel *model, const GtkTreeIter *iter); gboolean fm_tree_model_append_child_node(FMTreeModel *model, GtkTreeIter *parent_iter, From 75e6fcbf1625919d2d41825c089a9fd4dec969e8 Mon Sep 17 00:00:00 2001 From: kdmenk Date: Wed, 19 Nov 2025 17:43:24 +0100 Subject: [PATCH 18/19] leak in src/nemo-places-sidebar.c --- src/nemo-places-sidebar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 2d9e5a240..2045c7c76 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -5576,14 +5576,14 @@ nemo_places_sidebar_init (NemoPlacesSidebar *sidebar) sidebar->desktop_dnd_source_fs = NULL; sidebar->desktop_dnd_can_delete_source = TRUE; - sidebar->volume_monitor = g_volume_monitor_get (); - 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); @@ -6003,7 +6003,7 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, G_CALLBACK (update_places_on_idle), sidebar); - sidebar->volume_monitor = g_volume_monitor_get (); + 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); From 130fdab1a7222806a09e046101988300742d342c Mon Sep 17 00:00:00 2001 From: kdmenk Date: Thu, 20 Nov 2025 16:04:48 +0100 Subject: [PATCH 19/19] double g_signal_handler_disconnect(sidebar->window, sidebar->hidden_files_changed_id); --- src/nemo-places-sidebar.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/nemo-places-sidebar.c b/src/nemo-places-sidebar.c index 2045c7c76..ae6d1463e 100644 --- a/src/nemo-places-sidebar.c +++ b/src/nemo-places-sidebar.c @@ -5807,12 +5807,6 @@ nemo_places_sidebar_dispose (GObject *object) sidebar->tree_view = NULL; } - if(sidebar->hidden_files_changed_id != 0) { - g_signal_handler_disconnect (sidebar->window, - sidebar->hidden_files_changed_id); - sidebar->hidden_files_changed_id = 0; - } - g_clear_object (&sidebar->ui_manager); sidebar->ui_manager = NULL; @@ -6003,7 +5997,7 @@ nemo_places_sidebar_set_parent_window (NemoPlacesSidebar *sidebar, G_CALLBACK (update_places_on_idle), sidebar); - if (sidebar->volume_monitor != NULL) sidebar->volume_monitor = g_volume_monitor_get (); + 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);