diff --git a/src/common/userlist.c b/src/common/userlist.c index 7c6c3e4f..f546d5e9 100644 --- a/src/common/userlist.c +++ b/src/common/userlist.c @@ -317,6 +317,7 @@ userlist_change (struct session *sess, char *oldname, char *newname) tree_insert (sess->usertree, user); fe_userlist_insert (sess, user, FALSE); + fe_userlist_rehash (sess, user); return 1; } diff --git a/src/fe-gtk/chanview-tabs.c b/src/fe-gtk/chanview-tabs.c index bea4946a..3d364ad4 100644 --- a/src/fe-gtk/chanview-tabs.c +++ b/src/fe-gtk/chanview-tabs.c @@ -75,20 +75,6 @@ cv_tabs_get_viewport_size (GdkWindow *parent_win, gboolean vertical) return viewport_size; } -/* - * GtkViewports request at least as much space as their children do. - * If we don't intervene here, the GtkViewport will be granted its - * request, even at the expense of resizing the top-level window. - */ -static void -cv_tabs_sizerequest (GtkWidget *viewport, GtkRequisition *requisition, chanview *cv) -{ - if (!cv->vertical) - requisition->width = 1; - else - requisition->height = 1; -} - static void cv_tabs_sizealloc (GtkWidget *widget, GtkAllocation *allocation, chanview *cv) { @@ -415,8 +401,10 @@ cv_tabs_init (chanview *cv) viewport = gtk_viewport_new (0, 0); gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE); - g_signal_connect (G_OBJECT (viewport), "size_request", - G_CALLBACK (cv_tabs_sizerequest), cv); + if (cv->vertical) + gtk_widget_set_size_request (viewport, -1, 1); + else + gtk_widget_set_size_request (viewport, 1, -1); g_signal_connect (G_OBJECT (viewport), "scroll_event", G_CALLBACK (tab_scroll_cb), cv); gtk_box_pack_start (GTK_BOX (outer), viewport, 1, 1, 0); diff --git a/src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c b/src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c index 6f6c8a94..36863658 100644 --- a/src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c +++ b/src/fe-gtk/theme/tests/test-theme-preferences-gtk3-populate.c @@ -95,6 +95,7 @@ theme_manager_attach_window (GtkWidget *window) (void)window; } + char * zoitechat_gtk3_theme_service_get_user_themes_dir (void) { @@ -268,6 +269,37 @@ test_removed_selected_theme_commits_fallback_and_applies (void) gtk_widget_destroy (page); } + +static void +test_unset_theme_keeps_system_default_without_apply (void) +{ + GtkWidget *page; + struct zoitechatprefs setup_prefs; + + if (!gtk_available) + { + g_test_message ("GTK display not available"); + return; + } + + memset (&setup_prefs, 0, sizeof (setup_prefs)); + memset (&prefs, 0, sizeof (prefs)); + removed_selected = FALSE; + apply_current_calls = 0; + applied_theme_id[0] = '\0'; + prefs.hex_gui_gtk3_variant = THEME_GTK3_VARIANT_FOLLOW_SYSTEM; + + page = theme_preferences_create_page (NULL, &setup_prefs, NULL); + + g_assert_cmpstr (prefs.hex_gui_gtk3_theme, ==, ""); + g_assert_cmpstr (setup_prefs.hex_gui_gtk3_theme, ==, ""); + g_assert_cmpint (prefs.hex_gui_gtk3_variant, ==, THEME_GTK3_VARIANT_FOLLOW_SYSTEM); + g_assert_cmpint (setup_prefs.hex_gui_gtk3_variant, ==, 0); + g_assert_cmpint (apply_current_calls, ==, 0); + + gtk_widget_destroy (page); +} + int main (int argc, char **argv) { @@ -275,5 +307,7 @@ main (int argc, char **argv) gtk_available = gtk_init_check (&argc, &argv); g_test_add_func ("/theme/preferences/gtk3_removed_selection_applies_fallback", test_removed_selected_theme_commits_fallback_and_applies); + g_test_add_func ("/theme/preferences/gtk3_unset_keeps_system_default", + test_unset_theme_keeps_system_default_without_apply); return g_test_run (); } diff --git a/src/fe-gtk/theme/theme-manager.c b/src/fe-gtk/theme/theme-manager.c index 23eda211..1885d626 100644 --- a/src/fe-gtk/theme/theme-manager.c +++ b/src/fe-gtk/theme/theme-manager.c @@ -102,6 +102,7 @@ theme_manager_synthesize_preference_reasons (const struct zoitechatprefs *old_pr reasons |= THEME_CHANGED_REASON_IDENTD; if (color_change || old_prefs->hex_gui_ulist_color != new_prefs->hex_gui_ulist_color || + old_prefs->hex_text_color_nicks != new_prefs->hex_text_color_nicks || old_prefs->hex_away_size_max != new_prefs->hex_away_size_max || old_prefs->hex_away_track != new_prefs->hex_away_track) reasons |= THEME_CHANGED_REASON_USERLIST; @@ -578,7 +579,7 @@ theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_de behavior.font_desc = font_desc; behavior.apply_background = TRUE; - behavior.apply_foreground = TRUE; + behavior.apply_foreground = (prefs.hex_gui_ulist_color || prefs.hex_text_color_nicks) ? FALSE : TRUE; return behavior; } diff --git a/src/fe-gtk/theme/theme-preferences.c b/src/fe-gtk/theme/theme-preferences.c index 8051e33a..c5f41ba2 100644 --- a/src/fe-gtk/theme/theme-preferences.c +++ b/src/fe-gtk/theme/theme-preferences.c @@ -10,7 +10,6 @@ #include "../gtkutil.h" #include "../../common/fe.h" #include "../../common/util.h" -#include "../../common/cfgfiles.h" #include "../../common/gtk3-theme-service.h" #include "theme-gtk3.h" #include "theme-manager.h" @@ -740,6 +739,186 @@ theme_preferences_manage_colors_cb (GtkWidget *button, gpointer user_data) theme_manager_save_preferences (); } +static void +theme_preferences_show_import_error (GtkWidget *button, const char *message) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (button)), + GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", + message); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); +} + +static gboolean +theme_preferences_parse_cfg_color (const char *cfg, + const char *key, + guint16 *red, + guint16 *green, + guint16 *blue) +{ + const char *line; + size_t key_len; + + if (!cfg || !key || !red || !green || !blue) + return FALSE; + + key_len = strlen (key); + line = cfg; + + while (*line) + { + const char *line_end; + const char *p; + + while (*line == '\n' || *line == '\r') + line++; + if (!*line) + break; + + line_end = strchr (line, '\n'); + if (!line_end) + line_end = line + strlen (line); + + p = line; + while (p < line_end && g_ascii_isspace (*p)) + p++; + + if ((size_t) (line_end - p) > key_len && + strncmp (p, key, key_len) == 0) + { + unsigned int r; + unsigned int g; + unsigned int b; + + p += key_len; + while (p < line_end && g_ascii_isspace (*p)) + p++; + if (p < line_end && *p == '=') + p++; + while (p < line_end && g_ascii_isspace (*p)) + p++; + + if (sscanf (p, "%x %x %x", &r, &g, &b) == 3) + { + *red = (guint16) CLAMP (r, 0, 0xffff); + *green = (guint16) CLAMP (g, 0, 0xffff); + *blue = (guint16) CLAMP (b, 0, 0xffff); + return TRUE; + } + } + + line = line_end; + } + + return FALSE; +} + +static gboolean +theme_preferences_read_import_color (const char *cfg, + ThemeSemanticToken token, + GdkRGBA *rgba) +{ + static const char *token_names[] = { + "mirc_0", "mirc_1", "mirc_2", "mirc_3", "mirc_4", "mirc_5", "mirc_6", "mirc_7", + "mirc_8", "mirc_9", "mirc_10", "mirc_11", "mirc_12", "mirc_13", "mirc_14", "mirc_15", + "mirc_16", "mirc_17", "mirc_18", "mirc_19", "mirc_20", "mirc_21", "mirc_22", "mirc_23", + "mirc_24", "mirc_25", "mirc_26", "mirc_27", "mirc_28", "mirc_29", "mirc_30", "mirc_31", + "selection_foreground", "selection_background", "text_foreground", "text_background", "marker", + "tab_new_data", "tab_highlight", "tab_new_message", "tab_away", "spell" + }; + char key[256]; + guint16 red; + guint16 green; + guint16 blue; + int legacy_key; + + if (token < 0 || token >= THEME_TOKEN_COUNT) + return FALSE; + + g_snprintf (key, sizeof key, "theme.mode.light.token.%s", token_names[token]); + if (!theme_preferences_parse_cfg_color (cfg, key, &red, &green, &blue)) + { + legacy_key = token < 32 ? token : (token - 32) + 256; + g_snprintf (key, sizeof key, "color_%d", legacy_key); + if (!theme_preferences_parse_cfg_color (cfg, key, &red, &green, &blue)) + return FALSE; + } + + rgba->red = red / 65535.0; + rgba->green = green / 65535.0; + rgba->blue = blue / 65535.0; + rgba->alpha = 1.0; + return TRUE; +} + +static void +theme_preferences_import_colors_conf_cb (GtkWidget *button, gpointer user_data) +{ + gboolean *color_change_flag = user_data; + GtkWidget *dialog; + char *path; + char *cfg; + GError *error = NULL; + gboolean any_imported = FALSE; + gboolean old_changed = FALSE; + ThemeSemanticToken token; + + if (color_change_flag) + old_changed = *color_change_flag; + + dialog = gtk_file_chooser_dialog_new (_("Import colors.conf colors"), + GTK_WINDOW (gtk_widget_get_toplevel (button)), + GTK_FILE_CHOOSER_ACTION_OPEN, + _("_Cancel"), GTK_RESPONSE_CANCEL, + _("_Import"), GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), TRUE); + gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (dialog), FALSE); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT) + { + gtk_widget_destroy (dialog); + return; + } + + path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + gtk_widget_destroy (dialog); + if (!path) + return; + + if (!g_file_get_contents (path, &cfg, NULL, &error)) + { + theme_preferences_show_import_error (button, _("Failed to read colors.conf file.")); + g_clear_error (&error); + g_free (path); + return; + } + + for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++) + { + GdkRGBA rgba; + + if (!theme_preferences_read_import_color (cfg, token, &rgba)) + continue; + + theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token, &rgba, color_change_flag); + any_imported = TRUE; + } + + if (!any_imported) + theme_preferences_show_import_error (button, _("No importable colors were found in that colors.conf file.")); + else if (color_change_flag && *color_change_flag != old_changed) + theme_manager_save_preferences (); + + g_free (cfg); + g_free (path); +} + static void theme_preferences_create_color_button (GtkWidget *table, ThemeSemanticToken token, @@ -1103,6 +1282,40 @@ theme_preferences_load_thumbnail (const char *path) return scaled; } +static int +theme_preferences_gtk3_find_system_theme_index (GPtrArray *themes) +{ + GtkSettings *settings; + char *system_theme = NULL; + guint i; + int found = -1; + + settings = gtk_settings_get_default (); + if (!settings || !themes) + return -1; + + g_object_get (G_OBJECT (settings), "gtk-theme-name", &system_theme, NULL); + if (!system_theme || system_theme[0] == '\0') + { + g_free (system_theme); + return -1; + } + + for (i = 0; i < themes->len; i++) + { + ZoitechatGtk3Theme *theme = g_ptr_array_index (themes, i); + + if (theme && g_strcmp0 (theme->id, system_theme) == 0) + { + found = (int) i; + break; + } + } + + g_free (system_theme); + return found; +} + static void theme_preferences_populate_gtk3 (theme_preferences_ui *ui) { @@ -1112,6 +1325,7 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui) GtkTreeIter iter; int active = -1; gboolean removed_selected_theme = FALSE; + gboolean using_system_default = prefs.hex_gui_gtk3_theme[0] == '\0'; gboolean should_apply = FALSE; char *final_id; ThemeGtk3Variant final_variant = THEME_GTK3_VARIANT_PREFER_LIGHT; @@ -1144,12 +1358,14 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui) g_object_unref (thumbnail); g_free (label); } + if (active < 0 && using_system_default) + active = theme_preferences_gtk3_find_system_theme_index (themes); if (active >= 0) gtk_combo_box_set_active (GTK_COMBO_BOX (ui->gtk3_combo), active); else if (themes->len > 0) { gtk_combo_box_set_active (GTK_COMBO_BOX (ui->gtk3_combo), 0); - if (prefs.hex_gui_gtk3_theme[0] != '\0') + if (!using_system_default) removed_selected_theme = TRUE; } else if (prefs.hex_gui_gtk3_theme[0] != '\0') @@ -1162,19 +1378,22 @@ theme_preferences_populate_gtk3 (theme_preferences_ui *ui) if (final_id) { final_variant = theme_gtk3_variant_for_theme (final_id); - should_apply = g_strcmp0 (prefs.hex_gui_gtk3_theme, final_id) != 0 - || prefs.hex_gui_gtk3_variant != final_variant - || removed_selected_theme; - g_strlcpy (prefs.hex_gui_gtk3_theme, final_id, sizeof (prefs.hex_gui_gtk3_theme)); - if (ui->setup_prefs) - g_strlcpy (ui->setup_prefs->hex_gui_gtk3_theme, - final_id, - sizeof (ui->setup_prefs->hex_gui_gtk3_theme)); + if (!using_system_default || removed_selected_theme) + { + should_apply = g_strcmp0 (prefs.hex_gui_gtk3_theme, final_id) != 0 + || prefs.hex_gui_gtk3_variant != final_variant + || removed_selected_theme; + g_strlcpy (prefs.hex_gui_gtk3_theme, final_id, sizeof (prefs.hex_gui_gtk3_theme)); + if (ui->setup_prefs) + g_strlcpy (ui->setup_prefs->hex_gui_gtk3_theme, + final_id, + sizeof (ui->setup_prefs->hex_gui_gtk3_theme)); + prefs.hex_gui_gtk3_variant = final_variant; + if (ui->setup_prefs) + ui->setup_prefs->hex_gui_gtk3_variant = final_variant; + } g_free (final_id); } - prefs.hex_gui_gtk3_variant = final_variant; - if (ui->setup_prefs) - ui->setup_prefs->hex_gui_gtk3_variant = final_variant; if (should_apply && !theme_gtk3_apply_current (&error)) { @@ -1290,6 +1509,7 @@ theme_preferences_create_page (GtkWindow *parent, GtkWidget *colors_frame; GtkWidget *colors_box; GtkWidget *manage_colors_button; + GtkWidget *import_colors_button; GtkWidget *gtk3_frame; GtkWidget *gtk3_grid; GtkWidget *gtk3_button; @@ -1320,6 +1540,12 @@ theme_preferences_create_page (GtkWindow *parent, g_signal_connect (G_OBJECT (manage_colors_button), "clicked", G_CALLBACK (theme_preferences_manage_colors_cb), color_change_flag); + import_colors_button = gtk_button_new_with_label (_("Import colors.conf colors…")); + gtk_widget_set_halign (import_colors_button, GTK_ALIGN_START); + gtk_box_pack_start (GTK_BOX (colors_box), import_colors_button, FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (import_colors_button), "clicked", + G_CALLBACK (theme_preferences_import_colors_conf_cb), color_change_flag); + gtk3_frame = gtk_frame_new (_("GTK3 Theme")); gtk_box_pack_start (GTK_BOX (box), gtk3_frame, FALSE, FALSE, 0); gtk3_grid = gtk_grid_new (); diff --git a/src/fe-gtk/userlistgui.c b/src/fe-gtk/userlistgui.c index 7e9cdece..445265a9 100644 --- a/src/fe-gtk/userlistgui.c +++ b/src/fe-gtk/userlistgui.c @@ -429,7 +429,7 @@ fe_userlist_rehash (session *sess, struct User *user) nick_token = THEME_TOKEN_TAB_AWAY; have_nick_token = TRUE; } - else if (prefs.hex_gui_ulist_color) + else if (prefs.hex_gui_ulist_color || prefs.hex_text_color_nicks) { int mirc_index = text_color_of (user->nick); @@ -461,7 +461,7 @@ fe_userlist_insert (session *sess, struct User *newuser, gboolean sel) nick_token = THEME_TOKEN_TAB_AWAY; have_nick_token = TRUE; } - else if (prefs.hex_gui_ulist_color) + else if (prefs.hex_gui_ulist_color || prefs.hex_text_color_nicks) { int mirc_index = text_color_of (newuser->nick);