From 0f5dfb147efb924e85b2cec519576f3cbcf649b4 Mon Sep 17 00:00:00 2001 From: Mike Buchholz Date: Mon, 16 Mar 2026 10:45:29 -0600 Subject: [PATCH] fix: native GTK theme import, proper file:// paths --- src/fe-gtk/chanlist.c | 111 +++++++++++++++++++++------ src/fe-gtk/fe-gtk.c | 53 +++++++++++-- src/fe-gtk/maingui.c | 2 +- src/fe-gtk/sexy-spell-entry.c | 36 ++++++--- src/fe-gtk/theme/theme-preferences.c | 68 ++++++---------- 5 files changed, 184 insertions(+), 86 deletions(-) diff --git a/src/fe-gtk/chanlist.c b/src/fe-gtk/chanlist.c index 3fc1218e..394d0f2d 100644 --- a/src/fe-gtk/chanlist.c +++ b/src/fe-gtk/chanlist.c @@ -508,36 +508,72 @@ chanlist_match_topic_button_toggled (GtkWidget * wid, server *serv) serv->gui->chanlist_match_wants_topic = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)); } +static GSList * +chanlist_get_selection (server *serv, int column) +{ + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list)); + GtkTreeModel *model = GET_MODEL (serv); + GList *rows; + GList *cur; + GSList *result = NULL; + + rows = gtk_tree_selection_get_selected_rows (sel, &model); + for (cur = rows; cur != NULL; cur = cur->next) + { + GtkTreeIter iter; + char *value; + + if (gtk_tree_model_get_iter (model, &iter, (GtkTreePath *)cur->data)) + { + gtk_tree_model_get (model, &iter, column, &value, -1); + result = g_slist_prepend (result, value); + } + } + + g_list_free_full (rows, (GDestroyNotify) gtk_tree_path_free); + return g_slist_reverse (result); +} + static char * chanlist_get_selected (server *serv, gboolean get_topic) { - char *chan; - GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list)); - GtkTreeModel *model; - GtkTreeIter iter; + GSList *selection = chanlist_get_selection (serv, get_topic ? COL_TOPIC : COL_CHANNEL); + char *value; - if (!gtk_tree_selection_get_selected (sel, &model, &iter)) + if (!selection) return NULL; - gtk_tree_model_get (model, &iter, get_topic ? COL_TOPIC : COL_CHANNEL, &chan, -1); - return chan; + value = selection->data; + selection->data = NULL; + g_slist_free_full (selection, g_free); + return value; } static void chanlist_join (GtkWidget * wid, server *serv) { char tbuf[CHANLEN + 6]; - char *chan = chanlist_get_selected (serv, FALSE); - if (chan) + GSList *selection; + GSList *item; + gboolean joined = FALSE; + + selection = chanlist_get_selection (serv, COL_CHANNEL); + for (item = selection; item != NULL; item = item->next) { - if (serv->connected && (strcmp (chan, "*") != 0)) + char *chan = item->data; + + if (serv->connected && strcmp (chan, "*") != 0) { g_snprintf (tbuf, sizeof (tbuf), "join %s", chan); handle_command (serv->server_session, tbuf, FALSE); - } else - gdk_display_beep (gdk_display_get_default ()); - g_free (chan); + joined = TRUE; + } } + + if (!joined && selection) + gdk_display_beep (gdk_display_get_default ()); + + g_slist_free_full (selection, g_free); } static void @@ -656,23 +692,47 @@ chanlist_menu_destroy (GtkWidget *menu, gpointer userdata) static void chanlist_copychannel (GtkWidget *item, server *serv) { - char *chan = chanlist_get_selected (serv, FALSE); - if (chan) + GSList *selection = chanlist_get_selection (serv, COL_CHANNEL); + GSList *cur; + GString *text; + + if (!selection) + return; + + text = g_string_new (""); + for (cur = selection; cur != NULL; cur = cur->next) { - gtkutil_copy_to_clipboard (item, NULL, chan); - g_free (chan); + if (text->len) + g_string_append_c (text, '\n'); + g_string_append (text, (char *)cur->data); } + + gtkutil_copy_to_clipboard (item, NULL, text->str); + g_string_free (text, TRUE); + g_slist_free_full (selection, g_free); } static void chanlist_copytopic (GtkWidget *item, server *serv) { - char *topic = chanlist_get_selected (serv, TRUE); - if (topic) + GSList *selection = chanlist_get_selection (serv, COL_TOPIC); + GSList *cur; + GString *text; + + if (!selection) + return; + + text = g_string_new (""); + for (cur = selection; cur != NULL; cur = cur->next) { - gtkutil_copy_to_clipboard (item, NULL, topic); - g_free (topic); + if (text->len) + g_string_append_c (text, '\n'); + g_string_append (text, (char *)cur->data); } + + gtkutil_copy_to_clipboard (item, NULL, text->str); + g_string_free (text, TRUE); + g_slist_free_full (selection, g_free); } static gboolean @@ -689,10 +749,12 @@ chanlist_button_cb (GtkTreeView *tree, GdkEventButton *event, server *serv) if (!gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0)) return FALSE; - /* select what they right-clicked on */ sel = gtk_tree_view_get_selection (tree); - gtk_tree_selection_unselect_all (sel); - gtk_tree_selection_select_path (sel, path); + if (!gtk_tree_selection_path_is_selected (sel, path)) + { + gtk_tree_selection_unselect_all (sel); + gtk_tree_selection_select_path (sel, path); + } gtk_tree_path_free (path); menu = gtk_menu_new (); @@ -879,6 +941,7 @@ chanlist_opengui (server *serv, int do_refresh) chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE); chanlist_add_column (view, COL_TOPIC, 50, _("Topic"), FALSE); gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (view), GTK_TREE_VIEW_GRID_LINES_HORIZONTAL); + gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (view)), GTK_SELECTION_MULTIPLE); /* this is a speed up, but no horizontal scrollbar :( */ /*gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);*/ gtk_widget_show (view); diff --git a/src/fe-gtk/fe-gtk.c b/src/fe-gtk/fe-gtk.c index 989fea20..5576877d 100644 --- a/src/fe-gtk/fe-gtk.c +++ b/src/fe-gtk/fe-gtk.c @@ -1339,22 +1339,61 @@ fe_open_url_inner (const char *url) g_free (escaped_url); } +static gboolean +fe_open_url_is_local_path (const char *url) +{ + if (g_path_is_absolute (url) || g_file_test (url, G_FILE_TEST_EXISTS)) + return TRUE; + +#ifdef WIN32 + if (g_ascii_isalpha (url[0]) && url[1] == ':' && + (url[2] == '\\' || url[2] == '/')) + return TRUE; + + if (url[0] == '\\' && url[1] == '\\') + return TRUE; +#endif + + return FALSE; +} + void fe_open_url (const char *url) { int url_type = url_check_word (url); char *uri; + char *path; + char *path_uri; + + if (fe_open_url_is_local_path (url)) + { + path = g_canonicalize_filename (url, NULL); + path_uri = g_filename_to_uri (path, NULL, NULL); + g_free (path); + + if (path_uri) + { + fe_open_url_inner (path_uri); + g_free (path_uri); + return; + } + } /* gvfs likes file:// */ if (url_type == WORD_PATH) { -#ifndef WIN32 - uri = g_strconcat ("file://", url, NULL); - fe_open_url_inner (uri); - g_free (uri); -#else - fe_open_url_inner (url); -#endif + path = g_canonicalize_filename (url, NULL); + path_uri = g_filename_to_uri (path, NULL, NULL); + g_free (path); + if (path_uri) + { + fe_open_url_inner (path_uri); + g_free (path_uri); + } + else + { + fe_open_url_inner (url); + } } /* IPv6 addr. Add http:// */ else if (url_type == WORD_HOST6) diff --git a/src/fe-gtk/maingui.c b/src/fe-gtk/maingui.c index 97e26c26..d28f1ce6 100644 --- a/src/fe-gtk/maingui.c +++ b/src/fe-gtk/maingui.c @@ -670,7 +670,7 @@ mg_spellcheck_cb (SexySpellEntry *entry, gchar *word, gpointer data) { /* This can cause freezes on long words, nicks arn't very long anyway. */ if (strlen (word) > 20) - return TRUE; + return FALSE; /* Ignore anything we think is a valid url */ if (url_check_word (word) != 0) diff --git a/src/fe-gtk/sexy-spell-entry.c b/src/fe-gtk/sexy-spell-entry.c index aeff36ea..56c36d38 100644 --- a/src/fe-gtk/sexy-spell-entry.c +++ b/src/fe-gtk/sexy-spell-entry.c @@ -964,6 +964,9 @@ default_word_check(SexySpellEntry *entry, const gchar *word) /* We only want to check words */ return FALSE; } + + if (g_utf8_strlen (word, -1) > 20) + return FALSE; for (li = entry->priv->dict_list; li; li = g_slist_next (li)) { struct EnchantDict *dict = (struct EnchantDict *) li->data; if (enchant_dict_check(dict, word, strlen(word)) == 0) { @@ -1161,13 +1164,34 @@ check_color: } } +static gboolean +attr_list_has_attrs (PangoAttrList *attrs) +{ + PangoAttrIterator *it; + GSList *list; + gboolean has = FALSE; + + if (!attrs) + return FALSE; + + it = pango_attr_list_get_iterator (attrs); + if (!it) + return FALSE; + + list = pango_attr_iterator_get_attrs (it); + has = (list != NULL); + g_slist_free_full (list, (GDestroyNotify) pango_attribute_destroy); + pango_attr_iterator_destroy (it); + + return has; +} + static void sexy_spell_entry_recheck_all(SexySpellEntry *entry) { GdkRectangle rect; GtkAllocation allocation; GtkWidget *widget = GTK_WIDGET(entry); - PangoLayout *layout; int length, i, text_len; const char *text; @@ -1196,8 +1220,7 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry) } } - layout = gtk_entry_get_layout(GTK_ENTRY(entry)); - pango_layout_set_attributes(layout, entry->priv->attr_list); + gtk_entry_set_attributes (GTK_ENTRY (entry), attr_list_has_attrs (entry->priv->attr_list) ? entry->priv->attr_list : NULL); if (gtk_widget_get_realized (GTK_WIDGET(entry))) { @@ -1213,13 +1236,6 @@ sexy_spell_entry_recheck_all(SexySpellEntry *entry) static gboolean sexy_spell_entry_draw(GtkWidget *widget, cairo_t *cr) { - SexySpellEntry *entry = SEXY_SPELL_ENTRY(widget); - GtkEntry *gtk_entry = GTK_ENTRY(widget); - PangoLayout *layout; - - layout = gtk_entry_get_layout(gtk_entry); - pango_layout_set_attributes(layout, entry->priv->attr_list); - return GTK_WIDGET_CLASS(parent_class)->draw (widget, cr); } diff --git a/src/fe-gtk/theme/theme-preferences.c b/src/fe-gtk/theme/theme-preferences.c index f85ae26e..c91e712c 100644 --- a/src/fe-gtk/theme/theme-preferences.c +++ b/src/fe-gtk/theme/theme-preferences.c @@ -1392,26 +1392,34 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui) g_ptr_array_unref (themes); } +static void +theme_preferences_gtk3_import_path (theme_preferences_ui *ui, char *path) +{ + char *id = NULL; + GError *error = NULL; + + if (!zoitechat_gtk3_theme_service_import (path, &id, &error)) + theme_preferences_show_message (ui, GTK_MESSAGE_ERROR, + error ? error->message : _("Failed to import GTK3 theme.")); + g_clear_error (&error); + g_free (id); + g_free (path); + theme_preferences_populate_gtk3 (ui); +} + static void theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data) { theme_preferences_ui *ui = user_data; - GtkWidget *dialog; + GtkFileChooserNative *dialog; GtkFileFilter *filter; - GtkWidget *folder_dialog; char *path; - char *id = NULL; - GError *error = NULL; - gint response; (void)button; - dialog = gtk_file_chooser_dialog_new (_("Import GTK3 Theme"), ui->parent, + dialog = gtk_file_chooser_native_new (_("Import GTK3 Theme"), ui->parent, GTK_FILE_CHOOSER_ACTION_OPEN, - _("Import _Folder"), 1, - _("_Cancel"), GTK_RESPONSE_CANCEL, - _("_Import"), GTK_RESPONSE_ACCEPT, - NULL); - theme_manager_attach_window (dialog); + _("_Import"), + _("_Cancel")); gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE); filter = gtk_file_filter_new (); @@ -1426,43 +1434,15 @@ theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data) gtk_file_filter_add_pattern (filter, "*.tbz"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter); - response = gtk_dialog_run (GTK_DIALOG (dialog)); - if (response == 1) + if (gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT) { - gtk_widget_destroy (dialog); - folder_dialog = gtk_file_chooser_dialog_new (_("Import GTK3 Theme Folder"), ui->parent, - GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, - _("_Cancel"), GTK_RESPONSE_CANCEL, - _("_Import"), GTK_RESPONSE_ACCEPT, - NULL); - theme_manager_attach_window (folder_dialog); - gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (folder_dialog), TRUE); - if (gtk_dialog_run (GTK_DIALOG (folder_dialog)) != GTK_RESPONSE_ACCEPT) - { - gtk_widget_destroy (folder_dialog); - return; - } - - path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (folder_dialog)); - gtk_widget_destroy (folder_dialog); - } - else if (response == GTK_RESPONSE_ACCEPT) - { - path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - gtk_widget_destroy (dialog); - } - else - { - gtk_widget_destroy (dialog); + g_object_unref (dialog); return; } - if (!zoitechat_gtk3_theme_service_import (path, &id, &error)) - theme_preferences_show_message (ui, GTK_MESSAGE_ERROR, - error ? error->message : _("Failed to import GTK3 theme.")); - g_clear_error (&error); - g_free (path); - theme_preferences_populate_gtk3 (ui); + path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + g_object_unref (dialog); + theme_preferences_gtk3_import_path (ui, path); } static void