aboutsummaryrefslogtreecommitdiff
path: root/fastiv-sidebar.c
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2021-12-18 06:38:30 +0100
committerPřemysl Eric Janouch <p@janouch.name>2021-12-18 07:04:01 +0100
commitb78010ccb1e6663ced40d212c3239b07b0065365 (patch)
tree8cac8917563c4e3a87f690fd0c09d6391077f2a0 /fastiv-sidebar.c
parentc136c089fab3e5aff6ef8339f8aea93cf142d1c7 (diff)
downloadfiv-b78010ccb1e6663ced40d212c3239b07b0065365.tar.gz
fiv-b78010ccb1e6663ced40d212c3239b07b0065365.tar.xz
fiv-b78010ccb1e6663ced40d212c3239b07b0065365.zip
Adopt shorter identifiers
Also, slightly reformat the source code according to clang-format.
Diffstat (limited to 'fastiv-sidebar.c')
-rw-r--r--fastiv-sidebar.c433
1 files changed, 0 insertions, 433 deletions
diff --git a/fastiv-sidebar.c b/fastiv-sidebar.c
deleted file mode 100644
index ecfec3e..0000000
--- a/fastiv-sidebar.c
+++ /dev/null
@@ -1,433 +0,0 @@
-//
-// fastiv-sidebar.c: molesting GtkPlacesSidebar
-//
-// Copyright (c) 2021, Přemysl Eric Janouch <p@janouch.name>
-//
-// Permission to use, copy, modify, and/or distribute this software for any
-// purpose with or without fee is hereby granted.
-//
-// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
-// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-//
-
-#include <gtk/gtk.h>
-
-#include "fastiv-io.h" // fastiv_io_filecmp
-#include "fastiv-sidebar.h"
-
-struct _FastivSidebar {
- GtkScrolledWindow parent_instance;
- GtkPlacesSidebar *places;
- GtkWidget *toolbar;
- GtkWidget *listbox;
- GFile *location;
-};
-
-G_DEFINE_TYPE(FastivSidebar, fastiv_sidebar, GTK_TYPE_SCROLLED_WINDOW)
-
-G_DEFINE_QUARK(fastiv-sidebar-location-quark, fastiv_sidebar_location)
-
-enum {
- OPEN_LOCATION,
- LAST_SIGNAL,
-};
-
-// Globals are, sadly, the canonical way of storing signal numbers.
-static guint sidebar_signals[LAST_SIGNAL];
-
-static void
-fastiv_sidebar_dispose(GObject *gobject)
-{
- FastivSidebar *self = FASTIV_SIDEBAR(gobject);
- g_clear_object(&self->location);
-
- G_OBJECT_CLASS(fastiv_sidebar_parent_class)->dispose(gobject);
-}
-
-static void
-fastiv_sidebar_class_init(FastivSidebarClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS(klass);
- object_class->dispose = fastiv_sidebar_dispose;
-
- // You're giving me no choice, Adwaita.
- // Your style is hardcoded to match against the class' CSS name.
- // And I need replicate the internal widget structure.
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- gtk_widget_class_set_css_name(widget_class, "placessidebar");
-
- // TODO(p): Consider a return value, and using it.
- sidebar_signals[OPEN_LOCATION] =
- g_signal_new("open_location", G_TYPE_FROM_CLASS(klass), 0, 0,
- NULL, NULL, NULL, G_TYPE_NONE,
- 2, G_TYPE_FILE, GTK_TYPE_PLACES_OPEN_FLAGS);
-}
-
-static gboolean
-on_rowlabel_query_tooltip(GtkWidget *widget,
- G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y,
- G_GNUC_UNUSED gboolean keyboard_tooltip, GtkTooltip *tooltip)
-{
- GtkLabel *label = GTK_LABEL(widget);
- if (!pango_layout_is_ellipsized(gtk_label_get_layout(label)))
- return FALSE;
-
- gtk_tooltip_set_text(tooltip, gtk_label_get_text(label));
- return TRUE;
-}
-
-static GtkWidget *
-create_row(GFile *file, const char *icon_name)
-{
- // TODO(p): Handle errors better.
- GFileInfo *info =
- g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
- G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL, NULL);
- if (!info)
- return NULL;
-
- const char *name = g_file_info_get_display_name(info);
- GtkWidget *rowbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
-
- GtkWidget *rowimage =
- gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_MENU);
- gtk_style_context_add_class(
- gtk_widget_get_style_context(rowimage), "sidebar-icon");
- gtk_container_add(GTK_CONTAINER(rowbox), rowimage);
-
- GtkWidget *rowlabel = gtk_label_new(name);
- gtk_label_set_ellipsize(GTK_LABEL(rowlabel), PANGO_ELLIPSIZE_END);
- gtk_widget_set_has_tooltip(rowlabel, TRUE);
- g_signal_connect(rowlabel, "query-tooltip",
- G_CALLBACK(on_rowlabel_query_tooltip), NULL);
- gtk_style_context_add_class(
- gtk_widget_get_style_context(rowlabel), "sidebar-label");
- gtk_container_add(GTK_CONTAINER(rowbox), rowlabel);
-
- GtkWidget *revealer = gtk_revealer_new();
- gtk_revealer_set_reveal_child(
- GTK_REVEALER(revealer), TRUE);
- gtk_revealer_set_transition_type(
- GTK_REVEALER(revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
- gtk_container_add(GTK_CONTAINER(revealer), rowbox);
-
- GtkWidget *row = gtk_list_box_row_new();
- g_object_set_qdata_full(G_OBJECT(row), fastiv_sidebar_location_quark(),
- g_object_ref(file), (GDestroyNotify) g_object_unref);
- gtk_container_add(GTK_CONTAINER(row), revealer);
- gtk_widget_show_all(row);
- return row;
-}
-
-static gint
-listbox_compare(
- GtkListBoxRow *row1, GtkListBoxRow *row2, G_GNUC_UNUSED gpointer user_data)
-{
- return fastiv_io_filecmp(
- g_object_get_qdata(G_OBJECT(row1), fastiv_sidebar_location_quark()),
- g_object_get_qdata(G_OBJECT(row2), fastiv_sidebar_location_quark()));
-}
-
-static void
-update_location(FastivSidebar *self, GFile *location)
-{
- if (location) {
- g_clear_object(&self->location);
- self->location = g_object_ref(location);
- }
-
- gtk_places_sidebar_set_location(self->places, self->location);
- gtk_container_foreach(GTK_CONTAINER(self->listbox),
- (GtkCallback) gtk_widget_destroy, NULL);
- g_return_if_fail(self->location != NULL);
-
- GFile *iter = g_object_ref(self->location);
- while (TRUE) {
- GFile *parent = g_file_get_parent(iter);
- g_object_unref(iter);
- if (!(iter = parent))
- break;
-
- gtk_list_box_prepend(GTK_LIST_BOX(self->listbox),
- create_row(parent, "go-up-symbolic"));
- }
-
- // Other options are "folder-{visiting,open}-symbolic", though the former
- // is mildly inappropriate (means: open in another window).
- gtk_container_add(GTK_CONTAINER(self->listbox),
- create_row(self->location, "circle-filled-symbolic"));
-
- GFileEnumerator *enumerator = g_file_enumerate_children(self->location,
- G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME
- "," G_FILE_ATTRIBUTE_STANDARD_NAME
- "," G_FILE_ATTRIBUTE_STANDARD_TYPE
- "," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
- G_FILE_QUERY_INFO_NONE, NULL, NULL);
- if (!enumerator)
- return;
-
- // TODO(p): gtk_list_box_set_filter_func(), or even use a model,
- // which could be shared with FastivBrowser.
- while (TRUE) {
- GFileInfo *info = NULL;
- GFile *child = NULL;
- if (!g_file_enumerator_iterate(enumerator, &info, &child, NULL, NULL) ||
- !info)
- break;
-
- if (g_file_info_get_file_type(info) == G_FILE_TYPE_DIRECTORY &&
- !g_file_info_get_is_hidden(info))
- gtk_container_add(GTK_CONTAINER(self->listbox),
- create_row(child, "go-down-symbolic"));
- }
- g_object_unref(enumerator);
-}
-
-static void
-on_open_breadcrumb(
- G_GNUC_UNUSED GtkListBox *listbox, GtkListBoxRow *row, gpointer user_data)
-{
- FastivSidebar *self = FASTIV_SIDEBAR(user_data);
- GFile *location =
- g_object_get_qdata(G_OBJECT(row), fastiv_sidebar_location_quark());
- g_signal_emit(self, sidebar_signals[OPEN_LOCATION], 0,
- location, GTK_PLACES_OPEN_NORMAL);
-}
-
-static void
-on_open_location(G_GNUC_UNUSED GtkPlacesSidebar *sidebar, GFile *location,
- GtkPlacesOpenFlags flags, gpointer user_data)
-{
- FastivSidebar *self = FASTIV_SIDEBAR(user_data);
- g_signal_emit(self, sidebar_signals[OPEN_LOCATION], 0, location, flags);
-
- // Deselect the item in GtkPlacesSidebar, if unsuccessful.
- update_location(self, NULL);
-}
-
-static void
-complete_path(GFile *location, GtkListStore *model)
-{
- // TODO(p): Do not enter directories unless followed by '/'.
- // This information has already been stripped from `location`.
- GFile *parent = G_FILE_TYPE_DIRECTORY ==
- g_file_query_file_type(location, G_FILE_QUERY_INFO_NONE, NULL)
- ? g_object_ref(location)
- : g_file_get_parent(location);
- if (!parent)
- return;
-
- GFileEnumerator *enumerator = g_file_enumerate_children(parent,
- G_FILE_ATTRIBUTE_STANDARD_NAME
- "," G_FILE_ATTRIBUTE_STANDARD_TYPE
- "," G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
- G_FILE_QUERY_INFO_NONE, NULL, NULL);
- if (!enumerator)
- goto fail_enumerator;
-
- while (TRUE) {
- GFileInfo *info = NULL;
- GFile *child = NULL;
- if (!g_file_enumerator_iterate(enumerator, &info, &child, NULL, NULL) ||
- !info)
- break;
-
- if (g_file_info_get_file_type(info) != G_FILE_TYPE_DIRECTORY ||
- g_file_info_get_is_hidden(info))
- continue;
-
- char *parse_name = g_file_get_parse_name(child);
- if (!g_str_has_suffix(parse_name, G_DIR_SEPARATOR_S)) {
- char *save = parse_name;
- parse_name = g_strdup_printf("%s%c", parse_name, G_DIR_SEPARATOR);
- g_free(save);
- }
- gtk_list_store_insert_with_values(model, NULL, -1, 0, parse_name, -1);
- g_free(parse_name);
- }
-
- g_object_unref(enumerator);
-fail_enumerator:
- g_object_unref(parent);
-}
-
-static GFile *
-resolve_location(FastivSidebar *self, const char *text)
-{
- // Relative paths produce invalid GFile objects with this function.
- // And even if they didn't, we have our own root for them.
- GFile *file = g_file_parse_name(text);
- if (g_uri_is_valid(text, G_URI_FLAGS_PARSE_RELAXED, NULL) ||
- g_file_peek_path(file))
- return file;
-
- GFile *absolute =
- g_file_get_child_for_display_name(self->location, text, NULL);
- if (!absolute)
- return file;
-
- g_object_unref(file);
- return absolute;
-}
-
-static void
-on_enter_location_changed(GtkEntry *entry, gpointer user_data)
-{
- FastivSidebar *self = FASTIV_SIDEBAR(user_data);
- const char *text = gtk_entry_get_text(entry);
- GFile *location = resolve_location(self, text);
-
- // Don't touch the network anywhere around here, URIs are a no-no.
- GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(entry));
- if (!g_file_peek_path(location) || g_file_query_exists(location, NULL))
- gtk_style_context_remove_class(style, GTK_STYLE_CLASS_WARNING);
- else
- gtk_style_context_add_class(style, GTK_STYLE_CLASS_WARNING);
-
- // XXX: For some reason, this jumps around with longer lists.
- GtkEntryCompletion *completion = gtk_entry_get_completion(entry);
- GtkTreeModel *model = gtk_entry_completion_get_model(completion);
- gtk_list_store_clear(GTK_LIST_STORE(model));
- if (g_file_peek_path(location))
- complete_path(location, GTK_LIST_STORE(model));
- g_object_unref(location);
-}
-
-static void
-on_show_enter_location(G_GNUC_UNUSED GtkPlacesSidebar *sidebar,
- G_GNUC_UNUSED gpointer user_data)
-{
- FastivSidebar *self = FASTIV_SIDEBAR(user_data);
- GtkWidget *dialog = gtk_dialog_new_with_buttons("Enter location",
- GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(self))),
- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL |
- GTK_DIALOG_USE_HEADER_BAR,
- "_Open", GTK_RESPONSE_ACCEPT, "_Cancel", GTK_RESPONSE_CANCEL, NULL);
-
- GtkListStore *model = gtk_list_store_new(1, G_TYPE_STRING);
- gtk_tree_sortable_set_sort_column_id(
- GTK_TREE_SORTABLE(model), 0, GTK_SORT_ASCENDING);
-
- GtkEntryCompletion *completion = gtk_entry_completion_new();
- gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(model));
- gtk_entry_completion_set_text_column(completion, 0);
- // TODO(p): Complete ~ paths so that they start with ~, then we can filter.
- gtk_entry_completion_set_match_func(
- completion, (GtkEntryCompletionMatchFunc) gtk_true, NULL, NULL);
- g_object_unref(model);
-
- GtkWidget *entry = gtk_entry_new();
- gtk_entry_set_completion(GTK_ENTRY(entry), completion);
- gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
- g_signal_connect(entry, "changed",
- G_CALLBACK(on_enter_location_changed), self);
-
- GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
- gtk_container_add(GTK_CONTAINER(content), entry);
- gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
- gtk_window_set_skip_taskbar_hint(GTK_WINDOW(dialog), TRUE);
- gtk_window_set_default_size(GTK_WINDOW(dialog), 800, -1);
-
- GdkGeometry geometry = {.max_width = G_MAXSHORT, .max_height = -1};
- gtk_window_set_geometry_hints(
- GTK_WINDOW(dialog), NULL, &geometry, GDK_HINT_MAX_SIZE);
- gtk_widget_show_all(dialog);
-
- if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
- const char *text = gtk_entry_get_text(GTK_ENTRY(entry));
- GFile *location = resolve_location(self, text);
- g_signal_emit(self, sidebar_signals[OPEN_LOCATION], 0,
- location, GTK_PLACES_OPEN_NORMAL);
- g_object_unref(location);
- }
- gtk_widget_destroy(dialog);
- g_object_unref(completion);
-
- // Deselect the item in GtkPlacesSidebar, if unsuccessful.
- update_location(self, NULL);
-}
-
-static void
-fastiv_sidebar_init(FastivSidebar *self)
-{
- // TODO(p): Transplant functionality from the shitty GtkPlacesSidebar.
- // We cannot reasonably place any new items within its own GtkListBox,
- // so we need to replicate the style hierarchy to some extent.
- self->places = GTK_PLACES_SIDEBAR(gtk_places_sidebar_new());
- gtk_places_sidebar_set_show_recent(self->places, FALSE);
- gtk_places_sidebar_set_show_trash(self->places, FALSE);
- gtk_places_sidebar_set_open_flags(self->places,
- GTK_PLACES_OPEN_NORMAL | GTK_PLACES_OPEN_NEW_WINDOW);
- g_signal_connect(self->places, "open-location",
- G_CALLBACK(on_open_location), self);
-
- gtk_places_sidebar_set_show_enter_location(self->places, TRUE);
- g_signal_connect(self->places, "show-enter-location",
- G_CALLBACK(on_show_enter_location), self);
- gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(self->places),
- GTK_POLICY_NEVER, GTK_POLICY_NEVER);
-
- // None of GtkActionBar, GtkToolbar, .inline-toolbar is appropriate.
- // It is either side-favouring borders or excess button padding.
- self->toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
- gtk_style_context_add_class(
- gtk_widget_get_style_context(self->toolbar), GTK_STYLE_CLASS_TOOLBAR);
-
- self->listbox = gtk_list_box_new();
- gtk_list_box_set_selection_mode(
- GTK_LIST_BOX(self->listbox), GTK_SELECTION_NONE);
- g_signal_connect(self->listbox, "row-activated",
- G_CALLBACK(on_open_breadcrumb), self);
- gtk_list_box_set_sort_func(
- GTK_LIST_BOX(self->listbox), listbox_compare, self, NULL);
-
- // Fill up what would otherwise be wasted space,
- // as it is in the examples of Nautilus and Thunar.
- GtkWidget *superbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
- gtk_container_add(
- GTK_CONTAINER(superbox), GTK_WIDGET(self->places));
- gtk_container_add(
- GTK_CONTAINER(superbox), gtk_separator_new(GTK_ORIENTATION_VERTICAL));
- gtk_container_add(
- GTK_CONTAINER(superbox), self->toolbar);
- gtk_container_add(
- GTK_CONTAINER(superbox), gtk_separator_new(GTK_ORIENTATION_VERTICAL));
- gtk_container_add(
- GTK_CONTAINER(superbox), self->listbox);
- gtk_container_add(GTK_CONTAINER(self), superbox);
-
- gtk_scrolled_window_set_policy(
- GTK_SCROLLED_WINDOW(self), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
- gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(self)),
- GTK_STYLE_CLASS_SIDEBAR);
- gtk_style_context_add_class(gtk_widget_get_style_context(GTK_WIDGET(self)),
- "fastiv");
-}
-
-// --- Public interface --------------------------------------------------------
-
-void
-fastiv_sidebar_set_location(FastivSidebar *self, GFile *location)
-{
- g_return_if_fail(FASTIV_IS_SIDEBAR(self));
- update_location(self, location);
-}
-
-void
-fastiv_sidebar_show_enter_location(FastivSidebar *self)
-{
- g_return_if_fail(FASTIV_IS_SIDEBAR(self));
- g_signal_emit_by_name(self->places, "show-enter-location");
-}
-
-GtkBox *
-fastiv_sidebar_get_toolbar(FastivSidebar *self)
-{
- g_return_val_if_fail(FASTIV_IS_SIDEBAR(self), NULL);
- return GTK_BOX(self->toolbar);
-}