/* ZoiteChat
 * Copyright (C) 1998-2010 Peter Zelezny.
 * Copyright (C) 2009-2013 Berke Viktor.
 * Copyright (C) 2026 deepend-tildeclub.
 *
 * 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 St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "../fe-gtk.h"
#include "theme-manager.h"

#include <gtk/gtk.h>
#include <string.h>

#include "theme-application.h"
#include "theme-policy.h"
#include "theme-runtime.h"
#include "theme-access.h"
#include "theme-css.h"
#include "theme-gtk3.h"
#include "../gtkutil.h"
#include "../maingui.h"
#include "../setup.h"
#include "../../common/zoitechat.h"
#include "../../common/zoitechatc.h"

void theme_runtime_reset_mode_colors (gboolean dark_mode);

typedef struct
{
	guint id;
	char *component_id;
	ThemeChangedCallback callback;
	gpointer userdata;
} ThemeListener;

static GHashTable *theme_manager_listeners;
static guint theme_manager_next_listener_id = 1;
static guint theme_manager_setup_listener_id;
static const char theme_manager_window_destroy_handler_key[] = "theme-manager-window-destroy-handler";
static const char theme_manager_window_csd_headerbar_key[] = "theme-manager-window-csd-headerbar";

typedef struct
{
	gboolean initialized;
	gboolean resolved_dark_preference;
	char gtk3_theme_id[sizeof prefs.hex_gui_gtk3_theme];
	int gtk3_variant;
	char *gtk_theme_name;
} ThemeManagerAutoRefreshCache;

static ThemeManagerAutoRefreshCache theme_manager_auto_refresh_cache;

static void
theme_manager_auto_refresh_cache_reset (void)
{
	g_free (theme_manager_auto_refresh_cache.gtk_theme_name);
	memset (&theme_manager_auto_refresh_cache, 0, sizeof (theme_manager_auto_refresh_cache));
}

static void
theme_manager_auto_refresh_cache_store (gboolean resolved_dark_preference, char *gtk_theme_name)
{
	theme_manager_auto_refresh_cache.initialized = TRUE;
	theme_manager_auto_refresh_cache.resolved_dark_preference = resolved_dark_preference;
	theme_manager_auto_refresh_cache.gtk3_variant = prefs.hex_gui_gtk3_variant;
	g_strlcpy (theme_manager_auto_refresh_cache.gtk3_theme_id,
		   prefs.hex_gui_gtk3_theme,
		   sizeof (theme_manager_auto_refresh_cache.gtk3_theme_id));
	g_free (theme_manager_auto_refresh_cache.gtk_theme_name);
	theme_manager_auto_refresh_cache.gtk_theme_name = gtk_theme_name;
}

static char *
theme_manager_dup_gtk_theme_name (void)
{
	GtkSettings *settings = gtk_settings_get_default ();
	char *gtk_theme_name = NULL;

	if (settings)
		g_object_get (settings, "gtk-theme-name", &gtk_theme_name, NULL);
	return gtk_theme_name;
}

/* Record the state the application started up with so that the first queued
 * auto refresh only reacts to real changes; startup already applied the
 * current theme, and consumers such as the tray are not ready yet. */
static void
theme_manager_auto_refresh_cache_prime (void)
{
	theme_manager_auto_refresh_cache_store (theme_policy_system_prefers_dark (),
						theme_manager_dup_gtk_theme_name ());
}

static void theme_manager_apply_platform_window_theme (GtkWidget *window);

static void
theme_manager_apply_to_toplevel_windows (void)
{
	GList *toplevels;
	GList *iter;

	toplevels = gtk_window_list_toplevels ();
	for (iter = toplevels; iter != NULL; iter = iter->next)
	{
		GtkWidget *window = GTK_WIDGET (iter->data);

		if (!GTK_IS_WINDOW (window) || gtk_widget_get_mapped (window) == FALSE)
			continue;

		theme_manager_apply_platform_window_theme (window);
	}
	g_list_free (toplevels);
}

static void
theme_listener_free (gpointer data)
{
	ThemeListener *listener = data;

	if (!listener)
		return;

	g_free (listener->component_id);
	g_free (listener);
}

static void
theme_manager_setup_apply_listener (const ThemeChangedEvent *event, gpointer userdata)
{
	(void) userdata;
	theme_manager_dispatch_setup_apply (event);
}

static ThemeChangedReason
theme_manager_synthesize_preference_reasons (const struct zoitechatprefs *old_prefs,
					      const struct zoitechatprefs *new_prefs,
					      gboolean color_change)
{
	ThemeChangedReason reasons = THEME_CHANGED_REASON_NONE;

	if (!old_prefs || !new_prefs)
		return reasons;

	if (strcmp (old_prefs->hex_text_background, new_prefs->hex_text_background) != 0)
		reasons |= THEME_CHANGED_REASON_PIXMAP;
	if (old_prefs->hex_gui_tab_dots != new_prefs->hex_gui_tab_dots ||
	    old_prefs->hex_gui_tab_layout != new_prefs->hex_gui_tab_layout)
		reasons |= THEME_CHANGED_REASON_LAYOUT;
	if (old_prefs->hex_identd_server != new_prefs->hex_identd_server ||
	    old_prefs->hex_identd_port != new_prefs->hex_identd_port)
		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;

	if (reasons != THEME_CHANGED_REASON_NONE)
		reasons |= THEME_CHANGED_REASON_WIDGET_STYLE;

	return reasons;
}

static gboolean
theme_manager_should_refresh_gtk3 (void)
{
	return prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM;
}

static void
theme_manager_auto_dark_mode_changed (GtkSettings *settings, GParamSpec *pspec, gpointer data)
{
	gboolean color_change = FALSE;
	gboolean should_refresh_gtk3;
	gboolean gtk3_refresh;
	gboolean follows_system_theme;
	gboolean resolved_dark_preference;
	char *gtk_theme_name = NULL;
	static gboolean in_handler = FALSE;

	(void) settings;
	(void) pspec;
	(void) data;

	resolved_dark_preference = theme_policy_system_prefers_dark ();
	gtk3_refresh = theme_manager_should_refresh_gtk3 ();
	follows_system_theme = prefs.hex_gui_gtk3_theme[0] == '\0';
	should_refresh_gtk3 = gtk3_refresh || prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO;

	gtk_theme_name = theme_manager_dup_gtk_theme_name ();

	if (theme_manager_auto_refresh_cache.initialized &&
	    theme_manager_auto_refresh_cache.resolved_dark_preference == resolved_dark_preference &&
	    theme_manager_auto_refresh_cache.gtk3_variant == prefs.hex_gui_gtk3_variant &&
	    g_strcmp0 (theme_manager_auto_refresh_cache.gtk3_theme_id, prefs.hex_gui_gtk3_theme) == 0 &&
	    g_strcmp0 (theme_manager_auto_refresh_cache.gtk_theme_name, gtk_theme_name) == 0)
	{
		g_free (gtk_theme_name);
		return;
	}

	theme_manager_auto_refresh_cache_store (resolved_dark_preference, gtk_theme_name);

	if (prefs.hex_gui_dark_mode != ZOITECHAT_DARK_MODE_AUTO && !gtk3_refresh && !follows_system_theme)
		return;
	if (in_handler)
		return;

	in_handler = TRUE;

	if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO || follows_system_theme)
	{
		theme_manager_commit_preferences (prefs.hex_gui_dark_mode, &color_change);
		if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
			fe_set_auto_dark_mode_state (resolved_dark_preference);
	}

	if (should_refresh_gtk3)
		theme_gtk3_apply_current (NULL);

	if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO || follows_system_theme)
	{
		/* The system theme feeds the mapped palette (chat area, user
		 * list, channel tree), so consumers must re-resolve colors. */
		theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_MODE);
	}

	in_handler = FALSE;
}

static guint theme_manager_auto_refresh_source = 0;
static ThemeManagerIdleAddFunc theme_manager_idle_add_func = g_idle_add;

static gboolean
theme_manager_run_auto_refresh (gpointer data)
{
	theme_manager_auto_refresh_source = 0;
	theme_manager_auto_dark_mode_changed (NULL, NULL, data);
	return G_SOURCE_REMOVE;
}

static void
theme_manager_queue_auto_refresh (GtkSettings *settings, GParamSpec *pspec, gpointer data)
{
	guint source_id;

	(void) settings;
	(void) pspec;

	if (theme_manager_auto_refresh_source != 0)
		return;

	theme_manager_auto_refresh_source = 1;
	source_id = theme_manager_idle_add_func (theme_manager_run_auto_refresh, data);
	/* An injected idle-add may run the refresh synchronously and already
	 * have cleared the pending marker; only keep the id while pending. */
	if (theme_manager_auto_refresh_source != 0)
		theme_manager_auto_refresh_source = source_id;
}

void
theme_manager_init (void)
{
	static gboolean settings_signals_connected = FALSE;
	GtkSettings *settings;

	if (!theme_manager_listeners)
		theme_manager_listeners = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
									 theme_listener_free);

	if (!theme_manager_setup_listener_id)
		theme_manager_setup_listener_id = theme_listener_register ("setup.apply", theme_manager_setup_apply_listener, NULL);

	theme_policy_init ();
	theme_policy_set_appearance_changed_callback (theme_manager_refresh_auto_mode);

	settings = gtk_settings_get_default ();
	if (settings && !settings_signals_connected)
	{
		g_signal_connect (settings, "notify::gtk-theme-name",
				  G_CALLBACK (theme_manager_queue_auto_refresh), NULL);
		g_signal_connect (settings, "notify::gtk-application-prefer-dark-theme",
				  G_CALLBACK (theme_manager_queue_auto_refresh), NULL);
		settings_signals_connected = TRUE;
	}

	fe_set_auto_dark_mode_state (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO ?
				     theme_policy_system_prefers_dark () : FALSE);
	theme_application_apply_mode (prefs.hex_gui_dark_mode, NULL);
	theme_gtk3_init ();
	zoitechat_set_theme_post_apply_callback (theme_manager_handle_theme_applied);
	theme_manager_auto_refresh_cache_prime ();
}

gboolean
theme_manager_apply_mode (unsigned int mode, gboolean *palette_changed)
{
	return theme_application_apply_mode (mode, palette_changed);
}

void
theme_manager_set_mode (unsigned int mode, gboolean *palette_changed)
{
	theme_application_apply_mode (mode, palette_changed);
}

void
theme_manager_set_token_color (unsigned int mode, ThemeSemanticToken token, const GdkRGBA *color, gboolean *palette_changed)
{
	gboolean changed = FALSE;

	if (!color)
		return;

	(void) mode;
	theme_runtime_user_set_color (token, color);

	theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, &changed);
	if (palette_changed)
		*palette_changed = changed;

	if (changed)
		theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);

	theme_application_reload_input_style ();
}

void
theme_manager_reset_mode_colors (unsigned int mode, gboolean *palette_changed)
{
	gboolean changed;

	(void) mode;
	theme_runtime_reset_mode_colors (FALSE);
	theme_runtime_apply_mode (ZOITECHAT_DARK_MODE_LIGHT, &changed);
	changed = TRUE;
	if (palette_changed)
		*palette_changed = changed;
	theme_manager_dispatch_changed (THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST);

	theme_application_reload_input_style ();
}

void
theme_manager_commit_preferences (unsigned int old_mode, gboolean *color_change)
{
	gboolean palette_changed = FALSE;

	theme_application_apply_mode (prefs.hex_gui_dark_mode, &palette_changed);
	if (color_change && (prefs.hex_gui_dark_mode != old_mode || palette_changed))
		*color_change = TRUE;

	if (prefs.hex_gui_dark_mode == ZOITECHAT_DARK_MODE_AUTO)
		fe_set_auto_dark_mode_state (theme_policy_is_dark_mode_active (ZOITECHAT_DARK_MODE_AUTO));
}

gboolean
theme_manager_save_preferences (void)
{
	return theme_runtime_save ();
}

gboolean
theme_changed_event_has_reason (const ThemeChangedEvent *event, ThemeChangedReason reason)
{
	if (!event)
		return FALSE;

	return (event->reasons & reason) != 0;
}

void
theme_manager_apply_and_dispatch (unsigned int mode, ThemeChangedReason reasons, gboolean *palette_changed)
{
	theme_application_apply_mode (mode, palette_changed);
	theme_manager_dispatch_changed (reasons);
}

void
theme_manager_dispatch_changed (ThemeChangedReason reasons)
{
	GHashTableIter iter;
	gpointer key;
	gpointer value;
	ThemeChangedEvent event;

	event.reasons = reasons;

	if ((reasons & (THEME_CHANGED_REASON_MODE |
			    THEME_CHANGED_REASON_THEME_PACK |
			    THEME_CHANGED_REASON_WIDGET_STYLE)) != 0)
	{
		theme_manager_apply_to_toplevel_windows ();
	}

	if (!theme_manager_listeners)
		return;

	g_hash_table_iter_init (&iter, theme_manager_listeners);
	while (g_hash_table_iter_next (&iter, &key, &value))
	{
		ThemeListener *listener = value;

		if (listener->callback)
			listener->callback (&event, listener->userdata);
	}
}

guint
theme_listener_register (const char *component_id, ThemeChangedCallback callback, gpointer userdata)
{
	ThemeListener *listener;
	guint id;

	if (!callback)
		return 0;

	if (!theme_manager_listeners)
		theme_manager_listeners = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
								     theme_listener_free);

	id = theme_manager_next_listener_id++;
	if (theme_manager_next_listener_id == 0)
		theme_manager_next_listener_id = 1;

	listener = g_new0 (ThemeListener, 1);
	listener->id = id;
	listener->component_id = g_strdup (component_id ? component_id : "theme.listener");
	listener->callback = callback;
	listener->userdata = userdata;

	g_hash_table_insert (theme_manager_listeners, GUINT_TO_POINTER (id), listener);

	return id;
}

void
theme_listener_unregister (guint listener_id)
{
	if (!theme_manager_listeners || listener_id == 0)
		return;

	g_hash_table_remove (theme_manager_listeners, GUINT_TO_POINTER (listener_id));
}

void
theme_manager_handle_theme_applied (void)
{
	theme_gtk3_invalidate_provider_cache ();
	if (prefs.hex_gui_gtk3_theme[0])
		theme_gtk3_refresh (prefs.hex_gui_gtk3_theme, (ThemeGtk3Variant) prefs.hex_gui_gtk3_variant, NULL);
	theme_application_apply_mode (prefs.hex_gui_dark_mode, NULL);
	theme_manager_dispatch_changed (THEME_CHANGED_REASON_THEME_PACK | THEME_CHANGED_REASON_PALETTE | THEME_CHANGED_REASON_WIDGET_STYLE | THEME_CHANGED_REASON_USERLIST | THEME_CHANGED_REASON_MODE);
}


static gboolean
theme_manager_is_kde_wayland (void)
{
	const char *wayland_display;
	const char *desktop;
	char *desktop_lower;
	gboolean is_kde;

	wayland_display = g_getenv ("WAYLAND_DISPLAY");
	if (!wayland_display || !wayland_display[0])
		return FALSE;

	desktop = g_getenv ("XDG_CURRENT_DESKTOP");
	if (!desktop || !desktop[0])
		desktop = g_getenv ("XDG_SESSION_DESKTOP");
	if (!desktop || !desktop[0])
		return FALSE;

	desktop_lower = g_ascii_strdown (desktop, -1);
	is_kde = strstr (desktop_lower, "kde") != NULL || strstr (desktop_lower, "plasma") != NULL;
	g_free (desktop_lower);
	return is_kde;
}

static gint
theme_manager_get_system_headerbar_height (void)
{
	GtkWidget *probe;
	gint minimum;
	gint natural;

	probe = gtk_header_bar_new ();
	gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (probe), TRUE);
	gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (probe), "menu:minimize,maximize,close");
	gtk_widget_show (probe);
	gtk_widget_get_preferred_height (probe, &minimum, &natural);
	gtk_widget_destroy (probe);
	if (natural > 0)
		return natural;
	return minimum;
}

static void
theme_manager_apply_wayland_kde_csd (GtkWidget *window)
{
	GtkWindow *gtk_window;
	GtkWidget *headerbar;
	gboolean enable_csd;

	if (!window || !GTK_IS_WINDOW (window))
		return;

	gtk_window = GTK_WINDOW (window);
	enable_csd = theme_gtk3_is_active () && theme_manager_is_kde_wayland ();
	headerbar = g_object_get_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key);

	if (enable_csd)
	{
		if (!headerbar)
		{
			GtkWidget *icon_image;
			GdkPixbuf *icon_pixbuf;

			if (gtk_widget_get_realized (window))
				return;

			headerbar = gtk_header_bar_new ();
			gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (headerbar), TRUE);
			gtk_header_bar_set_decoration_layout (GTK_HEADER_BAR (headerbar), "menu:minimize,maximize,close");
			g_object_set (G_OBJECT (headerbar), "spacing", 0, NULL);
			gtk_widget_set_size_request (headerbar, -1, theme_manager_get_system_headerbar_height ());
			icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.svg", 24, 24, TRUE, NULL);
			if (!icon_pixbuf)
				icon_pixbuf = gdk_pixbuf_new_from_resource_at_scale ("/icons/zoitechat.png", 24, 24, TRUE, NULL);
			icon_image = icon_pixbuf ? gtk_image_new_from_pixbuf (icon_pixbuf) : gtk_image_new_from_resource ("/icons/zoitechat.png");
			gtk_widget_set_margin_start (icon_image, 0);
			gtk_widget_set_margin_end (icon_image, 0);
			gtk_widget_set_margin_top (icon_image, 0);
			gtk_widget_set_margin_bottom (icon_image, 0);
			if (icon_pixbuf)
				g_object_unref (icon_pixbuf);
			gtk_header_bar_pack_start (GTK_HEADER_BAR (headerbar), icon_image);
			gtk_widget_show (icon_image);
			gtk_window_set_titlebar (gtk_window, headerbar);
			g_object_set_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key, headerbar);
		}
		gtk_header_bar_set_title (GTK_HEADER_BAR (headerbar), gtk_window_get_title (gtk_window));
		gtk_widget_show (headerbar);
		{
			GdkScreen *screen = gdk_screen_get_default ();
			if (screen)
				gtk_style_context_reset_widgets (screen);
		}
		return;
	}

	if (headerbar)
	{
		if (gtk_widget_get_realized (window))
			return;
		gtk_window_set_titlebar (gtk_window, NULL);
		g_object_set_data (G_OBJECT (window), theme_manager_window_csd_headerbar_key, NULL);
	}

	{
		GdkScreen *screen = gdk_screen_get_default ();
		if (screen)
			gtk_style_context_reset_widgets (screen);
	}
}

static void
theme_manager_apply_platform_window_theme (GtkWidget *window)
{
	GtkStyleContext *context;
	gboolean dark;

	if (!window)
		return;

	context = gtk_widget_get_style_context (window);
	if (theme_gtk3_is_active ())
	{
		dark = prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_PREFER_DARK;
		if (prefs.hex_gui_gtk3_variant == THEME_GTK3_VARIANT_FOLLOW_SYSTEM)
			dark = theme_policy_system_prefers_dark ();
	}
	else
		dark = theme_policy_is_dark_mode_active (prefs.hex_gui_dark_mode);
	if (context)
	{
		gtk_style_context_remove_class (context, "zoitechat-dark");
		gtk_style_context_remove_class (context, "zoitechat-light");
		gtk_style_context_add_class (context, dark ? "zoitechat-dark" : "zoitechat-light");
	}
#ifdef G_OS_WIN32
	fe_win32_apply_native_titlebar (window, dark);
#else
	theme_manager_apply_wayland_kde_csd (window);
#endif
}

static void
theme_manager_window_destroy_cb (GtkWidget *window, gpointer userdata)
{
	(void) userdata;
	g_object_set_data (G_OBJECT (window), theme_manager_window_destroy_handler_key, NULL);
}

void
theme_manager_apply_to_window (GtkWidget *window)
{
	if (!window)
		return;

	theme_manager_apply_platform_window_theme (window);
}

void
theme_manager_attach_window (GtkWidget *window)
{
	gulong *handler_id;

	if (!window)
		return;

	handler_id = g_object_get_data (G_OBJECT (window), theme_manager_window_destroy_handler_key);
	if (!handler_id)
	{
		handler_id = g_new (gulong, 1);
		*handler_id = g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (theme_manager_window_destroy_cb), NULL);
		g_object_set_data_full (G_OBJECT (window), theme_manager_window_destroy_handler_key, handler_id, g_free);
	}

	theme_manager_apply_to_window (window);
}

void
theme_manager_detach_window (GtkWidget *window)
{
	gulong *handler_id;

	if (!window)
		return;

	handler_id = g_object_get_data (G_OBJECT (window), theme_manager_window_destroy_handler_key);
	if (handler_id)
	{
		g_signal_handler_disconnect (G_OBJECT (window), *handler_id);
		g_object_set_data (G_OBJECT (window), theme_manager_window_destroy_handler_key, NULL);
	}
}

void
theme_manager_apply_palette_widget (GtkWidget *widget, const GdkRGBA *bg, const GdkRGBA *fg,
			       const PangoFontDescription *font_desc)
{
	theme_css_apply_palette_widget (widget, bg, fg, font_desc);
}

void
theme_manager_apply_entry_palette (GtkWidget *widget, const PangoFontDescription *font_desc)
{
	ThemeWidgetStyleValues style_values;

	if (!widget || !font_desc)
		return;

	theme_get_widget_style_values_for_widget (widget, &style_values);
	gtkutil_apply_palette (widget, &style_values.background, &style_values.foreground, font_desc);
}

ThemePaletteBehavior
theme_manager_get_userlist_palette_behavior (const PangoFontDescription *font_desc)
{
	ThemePaletteBehavior behavior;

	behavior.font_desc = font_desc;
	behavior.apply_background = TRUE;
	behavior.apply_foreground = (prefs.hex_gui_ulist_color || prefs.hex_text_color_nicks) ? FALSE : TRUE;

	return behavior;
}

ThemePaletteBehavior
theme_manager_get_channel_tree_palette_behavior (const PangoFontDescription *font_desc)
{
	ThemePaletteBehavior behavior;

	behavior.font_desc = font_desc;
	behavior.apply_background = TRUE;
	behavior.apply_foreground = TRUE;

	return behavior;
}

void
theme_manager_apply_userlist_palette (GtkWidget *widget, const PangoFontDescription *font_desc,
				       gboolean prefer_background, gboolean prefer_foreground)
{
	ThemePaletteBehavior behavior;

	behavior.font_desc = font_desc;
	behavior.apply_background = prefer_background;
	behavior.apply_foreground = prefer_foreground;
	theme_manager_apply_userlist_style (widget, behavior);
}

void
theme_manager_apply_userlist_style (GtkWidget *widget, ThemePaletteBehavior behavior)
{
	ThemeWidgetStyleValues style_values;
	const GdkRGBA *background = NULL;
	const GdkRGBA *foreground = NULL;

	if (!widget)
		return;

	theme_get_widget_style_values_for_widget (widget, &style_values);
	if (behavior.apply_background)
		background = &style_values.background;
	if (behavior.apply_foreground)
		foreground = &style_values.foreground;

	gtkutil_apply_palette (widget, background, foreground, behavior.font_desc);
}

void
theme_manager_apply_channel_tree_style (GtkWidget *widget, ThemePaletteBehavior behavior)
{
	theme_manager_apply_userlist_style (widget, behavior);
}

void
theme_manager_apply_input_style (gboolean enabled, const PangoFontDescription *font_desc)
{
	theme_css_reload_input_style (enabled, font_desc);
}

void
theme_manager_reload_input_style (void)
{
	theme_application_reload_input_style ();
}

void
theme_manager_refresh_auto_mode (void)
{
	theme_manager_queue_auto_refresh (NULL, NULL, NULL);
}

ThemeChangedEvent
theme_manager_on_preferences_changed (const struct zoitechatprefs *old_prefs,
				      const struct zoitechatprefs *new_prefs,
				      unsigned int old_mode,
				      gboolean *color_change)
{
	ThemeChangedEvent event;
	gboolean had_color_change = color_change && *color_change;

	theme_manager_commit_preferences (old_mode, color_change);
	event.reasons = theme_manager_synthesize_preference_reasons (old_prefs, new_prefs,
						     had_color_change || (color_change && *color_change));

	return event;
}

void
theme_manager_dispatch_setup_apply (const ThemeChangedEvent *event)
{
	if (!event)
		return;

	setup_apply_real (event);
}

void
theme_manager_set_idle_add_func (ThemeManagerIdleAddFunc idle_add_func)
{
	theme_manager_idle_add_func = idle_add_func ? idle_add_func : g_idle_add;
	theme_manager_auto_refresh_source = 0;
	theme_manager_auto_refresh_cache_reset ();
}
