2026-03-02 19:42:48 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
2026-03-09 14:21:20 -06:00
|
|
|
#include "../../common/zoitechat.h"
|
|
|
|
|
#include "../../common/zoitechatc.h"
|
|
|
|
|
|
2026-03-02 19:42:48 -07:00
|
|
|
#include <gio/gio.h>
|
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
|
|
|
|
|
|
#include "../gtkutil.h"
|
|
|
|
|
#include "../../common/fe.h"
|
|
|
|
|
#include "../../common/util.h"
|
2026-03-04 23:28:01 -07:00
|
|
|
#include "../../common/gtk3-theme-service.h"
|
|
|
|
|
#include "theme-gtk3.h"
|
2026-03-02 19:42:48 -07:00
|
|
|
#include "theme-manager.h"
|
|
|
|
|
#include "theme-preferences.h"
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GtkWindow *parent;
|
2026-03-04 23:28:01 -07:00
|
|
|
GtkWidget *gtk3_combo;
|
|
|
|
|
GtkWidget *gtk3_remove;
|
|
|
|
|
gboolean gtk3_populating;
|
|
|
|
|
struct zoitechatprefs *setup_prefs;
|
2026-03-02 19:42:48 -07:00
|
|
|
} theme_preferences_ui;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *button;
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
gboolean *color_change_flag;
|
2026-03-04 23:28:01 -07:00
|
|
|
gpointer manager_ui;
|
2026-03-02 19:42:48 -07:00
|
|
|
} theme_color_dialog_data;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
2026-03-04 23:28:01 -07:00
|
|
|
GtkWidget *row;
|
|
|
|
|
GtkWidget *button;
|
|
|
|
|
GtkWidget *entry;
|
|
|
|
|
GtkWidget *preview;
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
char *search_text;
|
|
|
|
|
gboolean *color_change_flag;
|
|
|
|
|
GtkWindow *parent;
|
|
|
|
|
gpointer manager_ui;
|
|
|
|
|
} theme_color_manager_row;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_row *row;
|
|
|
|
|
GdkRGBA original;
|
|
|
|
|
gboolean has_original;
|
|
|
|
|
} theme_manager_live_picker_data;
|
|
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
GPtrArray *rows;
|
|
|
|
|
GtkWidget *search_entry;
|
|
|
|
|
gboolean *color_change_flag;
|
|
|
|
|
GtkWidget *preview_window;
|
|
|
|
|
GtkWidget *preview_chat;
|
|
|
|
|
GtkWidget *preview_selected;
|
|
|
|
|
GtkWidget *preview_marker;
|
|
|
|
|
GtkWidget *preview_tab_new_data;
|
|
|
|
|
GtkWidget *preview_tab_new_message;
|
|
|
|
|
GtkWidget *preview_tab_highlight;
|
|
|
|
|
GtkWidget *preview_tab_away;
|
|
|
|
|
GtkWidget *preview_spell;
|
|
|
|
|
} theme_color_manager_ui;
|
|
|
|
|
|
|
|
|
|
#define COLOR_MANAGER_RESPONSE_RESET 1
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
gboolean active;
|
|
|
|
|
gboolean changed;
|
|
|
|
|
gboolean snapshot_valid[THEME_TOKEN_COUNT];
|
|
|
|
|
gboolean staged_valid[THEME_TOKEN_COUNT];
|
|
|
|
|
GdkRGBA snapshot[THEME_TOKEN_COUNT];
|
|
|
|
|
GdkRGBA staged[THEME_TOKEN_COUNT];
|
|
|
|
|
} theme_preferences_stage_state;
|
|
|
|
|
|
|
|
|
|
static theme_preferences_stage_state theme_preferences_stage;
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
theme_preferences_staged_get_color (ThemeSemanticToken token, GdkRGBA *rgba)
|
|
|
|
|
{
|
|
|
|
|
if (token < 0 || token >= THEME_TOKEN_COUNT || !rgba)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
if (theme_preferences_stage.active && theme_preferences_stage.staged_valid[token])
|
|
|
|
|
{
|
|
|
|
|
*rgba = theme_preferences_stage.staged[token];
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return theme_get_color (token, rgba);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_stage_recompute_changed (void)
|
|
|
|
|
{
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
|
|
|
|
|
theme_preferences_stage.changed = FALSE;
|
|
|
|
|
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
|
|
|
|
{
|
|
|
|
|
if (!theme_preferences_stage.snapshot_valid[token] || !theme_preferences_stage.staged_valid[token])
|
|
|
|
|
continue;
|
|
|
|
|
if (!gdk_rgba_equal (&theme_preferences_stage.snapshot[token], &theme_preferences_stage.staged[token]))
|
|
|
|
|
{
|
|
|
|
|
theme_preferences_stage.changed = TRUE;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_stage_sync_runtime_to_snapshot (void)
|
|
|
|
|
{
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
|
|
|
|
|
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
|
|
|
|
{
|
|
|
|
|
if (theme_preferences_stage.snapshot_valid[token])
|
|
|
|
|
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token,
|
|
|
|
|
&theme_preferences_stage.snapshot[token], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_stage_sync_runtime_to_staged (void)
|
|
|
|
|
{
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
|
|
|
|
|
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
|
|
|
|
{
|
|
|
|
|
if (theme_preferences_stage.staged_valid[token])
|
|
|
|
|
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token,
|
|
|
|
|
&theme_preferences_stage.staged[token], NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_staged_set_color (ThemeSemanticToken token, const GdkRGBA *rgba,
|
|
|
|
|
gboolean *color_change_flag, gboolean live_preview)
|
|
|
|
|
{
|
2026-03-17 09:53:56 -06:00
|
|
|
const GdkRGBA *preview_color = rgba;
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
if (token < 0 || token >= THEME_TOKEN_COUNT || !rgba)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (theme_preferences_stage.active)
|
|
|
|
|
{
|
|
|
|
|
theme_preferences_stage.staged[token] = *rgba;
|
|
|
|
|
theme_preferences_stage.staged_valid[token] = TRUE;
|
|
|
|
|
theme_preferences_stage_recompute_changed ();
|
|
|
|
|
if (color_change_flag)
|
|
|
|
|
*color_change_flag = theme_preferences_stage.changed;
|
2026-03-17 09:53:56 -06:00
|
|
|
|
|
|
|
|
preview_color = &theme_preferences_stage.staged[token];
|
2026-03-17 09:43:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (live_preview)
|
2026-03-17 09:53:56 -06:00
|
|
|
theme_manager_set_token_color (ZOITECHAT_DARK_MODE_LIGHT, token, preview_color, NULL);
|
2026-03-17 09:43:50 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
theme_preferences_stage_begin (void)
|
|
|
|
|
{
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
|
|
|
|
|
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
|
|
|
|
|
theme_preferences_stage.active = TRUE;
|
|
|
|
|
|
|
|
|
|
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
|
|
|
|
{
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
|
|
|
|
|
if (!theme_preferences_staged_get_color (token, &rgba))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
theme_preferences_stage.snapshot[token] = rgba;
|
|
|
|
|
theme_preferences_stage.staged[token] = rgba;
|
|
|
|
|
theme_preferences_stage.snapshot_valid[token] = TRUE;
|
|
|
|
|
theme_preferences_stage.staged_valid[token] = TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2026-03-17 11:01:40 -06:00
|
|
|
theme_preferences_stage_apply (void)
|
2026-03-17 09:43:50 -06:00
|
|
|
{
|
|
|
|
|
if (!theme_preferences_stage.active)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
theme_preferences_stage_sync_runtime_to_staged ();
|
2026-03-17 11:01:40 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
theme_preferences_stage_commit (void)
|
|
|
|
|
{
|
|
|
|
|
if (!theme_preferences_stage.active)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
theme_preferences_stage_apply ();
|
2026-03-17 09:43:50 -06:00
|
|
|
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
theme_preferences_stage_discard (void)
|
|
|
|
|
{
|
|
|
|
|
if (!theme_preferences_stage.active)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
theme_preferences_stage_sync_runtime_to_snapshot ();
|
|
|
|
|
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 21:55:36 -06:00
|
|
|
static void
|
|
|
|
|
theme_preferences_show_import_error (GtkWidget *button, const char *message);
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_row_free (gpointer data)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_row *row = data;
|
|
|
|
|
|
|
|
|
|
if (!row)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
g_free (row->search_text);
|
|
|
|
|
g_free (row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_ui_free (gpointer data)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_ui *ui = data;
|
|
|
|
|
|
|
|
|
|
if (!ui)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (ui->rows)
|
|
|
|
|
g_ptr_array_unref (ui->rows);
|
|
|
|
|
g_free (ui);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_update_preview (theme_color_manager_ui *ui)
|
|
|
|
|
{
|
|
|
|
|
GdkRGBA text_fg;
|
|
|
|
|
GdkRGBA text_bg;
|
|
|
|
|
GdkRGBA sel_fg;
|
|
|
|
|
GdkRGBA sel_bg;
|
|
|
|
|
GdkRGBA marker;
|
|
|
|
|
GdkRGBA tab_new_data;
|
|
|
|
|
GdkRGBA tab_new_message;
|
|
|
|
|
GdkRGBA tab_highlight;
|
|
|
|
|
GdkRGBA tab_away;
|
|
|
|
|
GdkRGBA spell;
|
|
|
|
|
GtkWidget *label;
|
|
|
|
|
|
|
|
|
|
if (!ui)
|
|
|
|
|
return;
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
if (!theme_preferences_staged_get_color (THEME_TOKEN_TEXT_FOREGROUND, &text_fg)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_TEXT_BACKGROUND, &text_bg)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_SELECTION_FOREGROUND, &sel_fg)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_SELECTION_BACKGROUND, &sel_bg)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_MARKER, &marker)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_NEW_DATA, &tab_new_data)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_NEW_MESSAGE, &tab_new_message)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_HIGHLIGHT, &tab_highlight)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_TAB_AWAY, &tab_away)
|
|
|
|
|
|| !theme_preferences_staged_get_color (THEME_TOKEN_SPELL, &spell))
|
2026-03-04 23:28:01 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_window, &text_bg, &text_fg, NULL);
|
|
|
|
|
gtkutil_apply_palette (ui->preview_chat, &text_bg, &text_fg, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_chat), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &text_fg, NULL);
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_selected, &sel_bg, &sel_fg, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_selected), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &sel_fg, NULL);
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_marker, &marker, &text_fg, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_marker), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &text_fg, NULL);
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_tab_new_data, &tab_new_data, &text_fg, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_tab_new_data), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &text_fg, NULL);
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_tab_new_message, &tab_new_message, &text_fg, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_tab_new_message), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &text_fg, NULL);
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_tab_highlight, &tab_highlight, &text_fg, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_tab_highlight), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &text_fg, NULL);
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_tab_away, &tab_away, &text_fg, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_tab_away), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &text_fg, NULL);
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (ui->preview_spell, &text_bg, &spell, NULL);
|
|
|
|
|
label = g_object_get_data (G_OBJECT (ui->preview_spell), "zoitechat-preview-label");
|
|
|
|
|
if (GTK_IS_WIDGET (label))
|
|
|
|
|
gtkutil_apply_palette (label, NULL, &spell, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkWidget *
|
|
|
|
|
theme_preferences_manager_preview_item_new (const char *text)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *box;
|
|
|
|
|
GtkWidget *label;
|
|
|
|
|
|
|
|
|
|
box = gtk_event_box_new ();
|
|
|
|
|
gtk_event_box_set_visible_window (GTK_EVENT_BOX (box), TRUE);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 3);
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (text);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (box), label);
|
|
|
|
|
g_object_set_data (G_OBJECT (box), "zoitechat-preview-label", label);
|
|
|
|
|
|
|
|
|
|
return box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkWidget *
|
|
|
|
|
theme_preferences_manager_create_preview (theme_color_manager_ui *ui)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *frame;
|
|
|
|
|
GtkWidget *vbox;
|
|
|
|
|
GtkWidget *header;
|
|
|
|
|
GtkWidget *chat_box;
|
|
|
|
|
GtkWidget *tabs_box;
|
|
|
|
|
GtkWidget *label;
|
|
|
|
|
|
|
|
|
|
frame = gtk_frame_new (_("Live preview"));
|
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (frame), vbox);
|
|
|
|
|
|
|
|
|
|
ui->preview_window = gtk_event_box_new ();
|
|
|
|
|
gtk_event_box_set_visible_window (GTK_EVENT_BOX (ui->preview_window), TRUE);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (ui->preview_window), 8);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), ui->preview_window, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
chat_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (ui->preview_window), chat_box);
|
|
|
|
|
|
|
|
|
|
header = gtk_label_new (_("#zoitechat-preview"));
|
|
|
|
|
gtk_widget_set_halign (header, GTK_ALIGN_START);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (chat_box), header, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_chat = theme_preferences_manager_preview_item_new (_("<alice> Example chat message"));
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (chat_box), ui->preview_chat, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_selected = theme_preferences_manager_preview_item_new (_("Selected text example"));
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (chat_box), ui->preview_selected, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_marker = theme_preferences_manager_preview_item_new (_("Marker line"));
|
|
|
|
|
gtk_widget_set_hexpand (ui->preview_marker, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (chat_box), ui->preview_marker, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_spell = theme_preferences_manager_preview_item_new (_("mispelled wrd"));
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (chat_box), ui->preview_spell, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (_("Tab states"));
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
tabs_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), tabs_box, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_tab_new_data = theme_preferences_manager_preview_item_new (_("New data"));
|
|
|
|
|
gtk_widget_set_hexpand (ui->preview_tab_new_data, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (tabs_box), ui->preview_tab_new_data, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_tab_new_message = theme_preferences_manager_preview_item_new (_("New message"));
|
|
|
|
|
gtk_widget_set_hexpand (ui->preview_tab_new_message, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (tabs_box), ui->preview_tab_new_message, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_tab_highlight = theme_preferences_manager_preview_item_new (_("Highlight"));
|
|
|
|
|
gtk_widget_set_hexpand (ui->preview_tab_highlight, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (tabs_box), ui->preview_tab_highlight, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
ui->preview_tab_away = theme_preferences_manager_preview_item_new (_("Away"));
|
|
|
|
|
gtk_widget_set_hexpand (ui->preview_tab_away, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (tabs_box), ui->preview_tab_away, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
theme_preferences_manager_update_preview (ui);
|
|
|
|
|
|
|
|
|
|
return frame;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
GTK3_THEME_COL_ID = 0,
|
|
|
|
|
GTK3_THEME_COL_LABEL,
|
|
|
|
|
GTK3_THEME_COL_SOURCE,
|
|
|
|
|
GTK3_THEME_COL_THUMBNAIL,
|
|
|
|
|
GTK3_THEME_COL_COUNT
|
|
|
|
|
};
|
2026-03-02 19:42:48 -07:00
|
|
|
|
|
|
|
|
#define LABEL_INDENT 12
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_show_message (theme_preferences_ui *ui, GtkMessageType message_type, const char *primary)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *dialog;
|
|
|
|
|
|
|
|
|
|
dialog = gtk_message_dialog_new (ui->parent,
|
|
|
|
|
GTK_DIALOG_MODAL,
|
|
|
|
|
message_type,
|
|
|
|
|
GTK_BUTTONS_OK,
|
|
|
|
|
"%s",
|
|
|
|
|
primary);
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_manager_attach_window (dialog);
|
2026-03-02 19:42:48 -07:00
|
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_color_button_apply (GtkWidget *button, const GdkRGBA *color)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *target = g_object_get_data (G_OBJECT (button), "zoitechat-color-box");
|
|
|
|
|
GtkWidget *apply_widget = GTK_IS_WIDGET (target) ? target : button;
|
|
|
|
|
|
|
|
|
|
gtkutil_apply_palette (apply_widget, color, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
if (apply_widget != button)
|
|
|
|
|
gtkutil_apply_palette (button, color, NULL, NULL);
|
|
|
|
|
|
|
|
|
|
gtk_widget_queue_draw (button);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_color_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_color_dialog_data *data = user_data;
|
|
|
|
|
|
|
|
|
|
if (response_id == GTK_RESPONSE_OK)
|
|
|
|
|
{
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
|
|
|
|
|
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
2026-03-17 09:43:50 -06:00
|
|
|
theme_preferences_staged_set_color (data->token,
|
|
|
|
|
&rgba,
|
|
|
|
|
data->color_change_flag,
|
|
|
|
|
TRUE);
|
2026-03-02 19:42:48 -07:00
|
|
|
theme_preferences_color_button_apply (data->button, &rgba);
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_manager_update_preview ((theme_color_manager_ui *) data->manager_ui);
|
2026-03-02 19:42:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
|
|
|
g_free (data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_color_cb (GtkWidget *button, gpointer userdata)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *dialog;
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
theme_color_dialog_data *data;
|
|
|
|
|
|
|
|
|
|
token = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (button), "zoitechat-theme-token"));
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
if (!theme_preferences_staged_get_color (token, &rgba))
|
2026-03-02 19:42:48 -07:00
|
|
|
return;
|
|
|
|
|
dialog = gtk_color_chooser_dialog_new (_("Select color"), GTK_WINDOW (userdata));
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_manager_attach_window (dialog);
|
2026-03-02 19:42:48 -07:00
|
|
|
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
|
|
|
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
|
|
|
|
|
|
|
|
|
data = g_new0 (theme_color_dialog_data, 1);
|
|
|
|
|
data->button = button;
|
|
|
|
|
data->token = token;
|
|
|
|
|
data->color_change_flag = g_object_get_data (G_OBJECT (button), "zoitechat-theme-color-change");
|
2026-03-04 23:28:01 -07:00
|
|
|
data->manager_ui = g_object_get_data (G_OBJECT (button), "zoitechat-theme-color-manager-ui");
|
2026-03-02 19:42:48 -07:00
|
|
|
g_signal_connect (dialog, "response", G_CALLBACK (theme_preferences_color_response_cb), data);
|
|
|
|
|
gtk_widget_show (dialog);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
static char *
|
|
|
|
|
theme_preferences_format_hex (const GdkRGBA *color)
|
|
|
|
|
{
|
|
|
|
|
return g_strdup_printf ("#%02X%02X%02X",
|
|
|
|
|
(guint) CLAMP (color->red * 255.0 + 0.5, 0.0, 255.0),
|
|
|
|
|
(guint) CLAMP (color->green * 255.0 + 0.5, 0.0, 255.0),
|
|
|
|
|
(guint) CLAMP (color->blue * 255.0 + 0.5, 0.0, 255.0));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
theme_preferences_token_display_name (ThemeSemanticToken token)
|
|
|
|
|
{
|
|
|
|
|
if (token >= THEME_TOKEN_MIRC_0 && token <= THEME_TOKEN_MIRC_15)
|
|
|
|
|
return g_strdup_printf (_("mIRC color %d"), token - THEME_TOKEN_MIRC_0);
|
|
|
|
|
|
|
|
|
|
if (token >= THEME_TOKEN_MIRC_16 && token <= THEME_TOKEN_MIRC_31)
|
|
|
|
|
return g_strdup_printf (_("Local color %d"), token - THEME_TOKEN_MIRC_0);
|
|
|
|
|
|
|
|
|
|
switch (token)
|
|
|
|
|
{
|
|
|
|
|
case THEME_TOKEN_SELECTION_FOREGROUND:
|
|
|
|
|
return g_strdup (_("Selected text foreground"));
|
|
|
|
|
case THEME_TOKEN_SELECTION_BACKGROUND:
|
|
|
|
|
return g_strdup (_("Selected text background"));
|
|
|
|
|
case THEME_TOKEN_TEXT_FOREGROUND:
|
|
|
|
|
return g_strdup (_("Text foreground"));
|
|
|
|
|
case THEME_TOKEN_TEXT_BACKGROUND:
|
|
|
|
|
return g_strdup (_("Text background"));
|
|
|
|
|
case THEME_TOKEN_MARKER:
|
|
|
|
|
return g_strdup (_("Marker line"));
|
|
|
|
|
case THEME_TOKEN_TAB_NEW_DATA:
|
|
|
|
|
return g_strdup (_("Tab: new data"));
|
|
|
|
|
case THEME_TOKEN_TAB_HIGHLIGHT:
|
|
|
|
|
return g_strdup (_("Tab: highlight"));
|
|
|
|
|
case THEME_TOKEN_TAB_NEW_MESSAGE:
|
|
|
|
|
return g_strdup (_("Tab: new message"));
|
|
|
|
|
case THEME_TOKEN_TAB_AWAY:
|
|
|
|
|
return g_strdup (_("Tab: away"));
|
|
|
|
|
case THEME_TOKEN_SPELL:
|
|
|
|
|
return g_strdup (_("Spell checker"));
|
|
|
|
|
default:
|
|
|
|
|
return g_strdup (_("Unknown color"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_row_apply (theme_color_manager_row *row, const GdkRGBA *rgba)
|
|
|
|
|
{
|
|
|
|
|
char *hex;
|
|
|
|
|
|
|
|
|
|
theme_preferences_color_button_apply (row->button, rgba);
|
|
|
|
|
gtkutil_apply_palette (row->preview, rgba, NULL, NULL);
|
|
|
|
|
hex = theme_preferences_format_hex (rgba);
|
|
|
|
|
gtk_entry_set_text (GTK_ENTRY (row->entry), hex);
|
|
|
|
|
g_free (hex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_row_commit (theme_color_manager_row *row, const GdkRGBA *rgba)
|
|
|
|
|
{
|
2026-03-17 09:43:50 -06:00
|
|
|
theme_preferences_staged_set_color (row->token, rgba, row->color_change_flag, TRUE);
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_manager_row_apply (row, rgba);
|
|
|
|
|
theme_preferences_manager_update_preview ((theme_color_manager_ui *) row->manager_ui);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_entry_commit (theme_color_manager_row *row)
|
|
|
|
|
{
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
const char *text = gtk_entry_get_text (GTK_ENTRY (row->entry));
|
|
|
|
|
|
|
|
|
|
if (!gdk_rgba_parse (&rgba, text))
|
|
|
|
|
{
|
2026-03-17 09:43:50 -06:00
|
|
|
if (theme_preferences_staged_get_color (row->token, &rgba))
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_manager_row_apply (row, &rgba);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
theme_preferences_manager_row_commit (row, &rgba);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_entry_activate_cb (GtkEntry *entry, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
(void) entry;
|
|
|
|
|
theme_preferences_manager_entry_commit ((theme_color_manager_row *) user_data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
theme_preferences_manager_entry_focus_out_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
(void) widget;
|
|
|
|
|
(void) event;
|
|
|
|
|
theme_preferences_manager_entry_commit ((theme_color_manager_row *) user_data);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_picker_notify_rgba_cb (GObject *object, GParamSpec *pspec, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_manager_live_picker_data *data = user_data;
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
|
|
|
|
|
(void) pspec;
|
|
|
|
|
if (!data || !data->row)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (object), &rgba);
|
|
|
|
|
theme_preferences_manager_row_commit (data->row, &rgba);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_picker_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_manager_live_picker_data *data = user_data;
|
|
|
|
|
|
|
|
|
|
if (data && data->row && data->has_original && response_id != GTK_RESPONSE_OK)
|
|
|
|
|
theme_preferences_manager_row_commit (data->row, &data->original);
|
|
|
|
|
|
|
|
|
|
gtk_widget_destroy (GTK_WIDGET (dialog));
|
|
|
|
|
g_free (data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_pick_cb (GtkWidget *button, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_row *row = user_data;
|
|
|
|
|
GtkWidget *dialog;
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
theme_manager_live_picker_data *data;
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
if (!theme_preferences_staged_get_color (row->token, &rgba))
|
2026-03-04 23:28:01 -07:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
dialog = gtk_color_chooser_dialog_new (_("Select color"), row->parent);
|
|
|
|
|
theme_manager_attach_window (dialog);
|
|
|
|
|
gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (dialog), &rgba);
|
|
|
|
|
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
|
|
|
|
|
|
|
|
|
|
data = g_new0 (theme_manager_live_picker_data, 1);
|
|
|
|
|
data->row = row;
|
|
|
|
|
data->original = rgba;
|
|
|
|
|
data->has_original = TRUE;
|
|
|
|
|
|
|
|
|
|
g_signal_connect (G_OBJECT (dialog), "notify::rgba",
|
|
|
|
|
G_CALLBACK (theme_preferences_manager_picker_notify_rgba_cb), data);
|
|
|
|
|
g_signal_connect (G_OBJECT (dialog), "response",
|
|
|
|
|
G_CALLBACK (theme_preferences_manager_picker_response_cb), data);
|
|
|
|
|
gtk_widget_show (dialog);
|
|
|
|
|
(void) button;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
theme_preferences_manager_row_matches (theme_color_manager_row *row, const char *needle)
|
|
|
|
|
{
|
|
|
|
|
if (!needle || !needle[0])
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
return strstr (row->search_text, needle) != NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_search_changed_cb (GtkEditable *editable, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_ui *ui = user_data;
|
|
|
|
|
char *needle_lower;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
needle_lower = g_utf8_strdown (gtk_entry_get_text (GTK_ENTRY (editable)), -1);
|
|
|
|
|
for (i = 0; i < ui->rows->len; i++)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_row *row = g_ptr_array_index (ui->rows, i);
|
|
|
|
|
gtk_widget_set_visible (row->row, theme_preferences_manager_row_matches (row, needle_lower));
|
|
|
|
|
}
|
|
|
|
|
g_free (needle_lower);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_refresh_rows (theme_color_manager_ui *ui)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
if (!ui || !ui->rows)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < ui->rows->len; i++)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_row *row = g_ptr_array_index (ui->rows, i);
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
if (theme_preferences_staged_get_color (row->token, &rgba))
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_manager_row_apply (row, &rgba);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
theme_preferences_manager_update_preview (ui);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manager_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_ui *ui = user_data;
|
|
|
|
|
|
|
|
|
|
if (response_id != COLOR_MANAGER_RESPONSE_RESET)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
gboolean changed = FALSE;
|
|
|
|
|
|
|
|
|
|
theme_manager_reset_mode_colors (ZOITECHAT_DARK_MODE_LIGHT, &changed);
|
2026-03-17 09:43:50 -06:00
|
|
|
if (theme_preferences_stage.active)
|
|
|
|
|
{
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
|
|
|
|
|
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
|
|
|
|
{
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
|
|
|
|
|
if (!theme_get_color (token, &rgba))
|
|
|
|
|
continue;
|
|
|
|
|
theme_preferences_stage.staged[token] = rgba;
|
|
|
|
|
theme_preferences_stage.staged_valid[token] = TRUE;
|
|
|
|
|
}
|
|
|
|
|
theme_preferences_stage_recompute_changed ();
|
|
|
|
|
if (ui->color_change_flag)
|
|
|
|
|
*ui->color_change_flag = theme_preferences_stage.changed;
|
|
|
|
|
}
|
|
|
|
|
else if (ui->color_change_flag)
|
2026-03-04 23:28:01 -07:00
|
|
|
*ui->color_change_flag = *ui->color_change_flag || changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
theme_preferences_manager_refresh_rows (ui);
|
|
|
|
|
g_signal_stop_emission_by_name (dialog, "response");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkWidget *
|
|
|
|
|
theme_preferences_create_color_manager_dialog (GtkWindow *parent, gboolean *color_change_flag)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *dialog;
|
|
|
|
|
GtkWidget *content;
|
|
|
|
|
GtkWidget *vbox;
|
|
|
|
|
GtkWidget *content_hbox;
|
|
|
|
|
GtkWidget *left_box;
|
|
|
|
|
GtkWidget *search;
|
|
|
|
|
GtkWidget *scroller;
|
|
|
|
|
GtkWidget *list;
|
|
|
|
|
GtkWidget *preview_frame;
|
|
|
|
|
theme_color_manager_ui *ui;
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
|
|
|
|
|
dialog = gtk_dialog_new_with_buttons (_("Manage client colors"),
|
|
|
|
|
parent,
|
|
|
|
|
GTK_DIALOG_MODAL,
|
|
|
|
|
_("_Reset to GTK3 defaults"),
|
|
|
|
|
COLOR_MANAGER_RESPONSE_RESET,
|
|
|
|
|
_("_Close"),
|
|
|
|
|
GTK_RESPONSE_CLOSE,
|
|
|
|
|
NULL);
|
|
|
|
|
theme_manager_attach_window (dialog);
|
|
|
|
|
gtk_window_set_default_size (GTK_WINDOW (dialog), 760, 560);
|
|
|
|
|
|
|
|
|
|
ui = g_new0 (theme_color_manager_ui, 1);
|
|
|
|
|
ui->rows = g_ptr_array_new_with_free_func (theme_preferences_manager_row_free);
|
|
|
|
|
ui->color_change_flag = color_change_flag;
|
|
|
|
|
g_object_set_data_full (G_OBJECT (dialog), "zoitechat-theme-color-manager", ui, theme_preferences_manager_ui_free);
|
|
|
|
|
g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (theme_preferences_manager_dialog_response_cb), ui);
|
|
|
|
|
|
|
|
|
|
content = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
|
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
|
|
|
|
|
gtk_widget_set_hexpand (vbox, TRUE);
|
|
|
|
|
gtk_widget_set_vexpand (vbox, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (content), vbox, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
content_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
|
|
|
|
|
gtk_widget_set_hexpand (content_hbox, TRUE);
|
|
|
|
|
gtk_widget_set_vexpand (content_hbox, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (vbox), content_hbox, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
left_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 8);
|
|
|
|
|
gtk_widget_set_hexpand (left_box, TRUE);
|
|
|
|
|
gtk_widget_set_vexpand (left_box, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (content_hbox), left_box, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
search = gtk_search_entry_new ();
|
|
|
|
|
gtk_entry_set_placeholder_text (GTK_ENTRY (search), _("Search colors by name"));
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (left_box), search, FALSE, FALSE, 0);
|
|
|
|
|
ui->search_entry = search;
|
|
|
|
|
|
|
|
|
|
scroller = gtk_scrolled_window_new (NULL, NULL);
|
|
|
|
|
gtk_widget_set_hexpand (scroller, TRUE);
|
|
|
|
|
gtk_widget_set_vexpand (scroller, TRUE);
|
|
|
|
|
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (left_box), scroller, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
list = gtk_list_box_new ();
|
|
|
|
|
gtk_widget_set_hexpand (list, TRUE);
|
|
|
|
|
gtk_widget_set_vexpand (list, TRUE);
|
|
|
|
|
gtk_list_box_set_selection_mode (GTK_LIST_BOX (list), GTK_SELECTION_NONE);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (scroller), list);
|
|
|
|
|
|
|
|
|
|
preview_frame = theme_preferences_manager_create_preview (ui);
|
|
|
|
|
gtk_widget_set_size_request (preview_frame, 300, -1);
|
|
|
|
|
gtk_widget_set_hexpand (preview_frame, FALSE);
|
|
|
|
|
gtk_widget_set_vexpand (preview_frame, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (content_hbox), preview_frame, FALSE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
|
|
|
|
|
{
|
|
|
|
|
theme_color_manager_row *row;
|
|
|
|
|
GtkWidget *list_row;
|
|
|
|
|
GtkWidget *hbox;
|
|
|
|
|
GtkWidget *name;
|
|
|
|
|
GtkWidget *preview;
|
|
|
|
|
GtkWidget *button;
|
|
|
|
|
GtkWidget *entry;
|
|
|
|
|
GdkRGBA rgba;
|
|
|
|
|
char *display;
|
|
|
|
|
char *search_text;
|
|
|
|
|
char *token_code;
|
|
|
|
|
|
|
|
|
|
list_row = gtk_list_box_row_new ();
|
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (list_row), hbox);
|
|
|
|
|
|
|
|
|
|
display = theme_preferences_token_display_name (token);
|
|
|
|
|
name = gtk_label_new (display);
|
|
|
|
|
gtk_widget_set_halign (name, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_hexpand (name, TRUE);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), name, TRUE, TRUE, 0);
|
|
|
|
|
|
|
|
|
|
preview = gtk_label_new (_("Preview"));
|
|
|
|
|
gtk_widget_set_size_request (preview, 90, -1);
|
|
|
|
|
gtk_widget_set_halign (preview, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), preview, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
button = gtk_button_new_with_label (_("Choose…"));
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
entry = gtk_entry_new ();
|
|
|
|
|
gtk_entry_set_width_chars (GTK_ENTRY (entry), 9);
|
|
|
|
|
gtk_entry_set_max_length (GTK_ENTRY (entry), 9);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
row = g_new0 (theme_color_manager_row, 1);
|
|
|
|
|
row->row = list_row;
|
|
|
|
|
row->button = button;
|
|
|
|
|
row->entry = entry;
|
|
|
|
|
row->preview = preview;
|
|
|
|
|
row->token = token;
|
|
|
|
|
row->color_change_flag = color_change_flag;
|
|
|
|
|
row->parent = GTK_WINDOW (dialog);
|
|
|
|
|
row->manager_ui = ui;
|
|
|
|
|
|
|
|
|
|
token_code = g_strdup_printf ("token_%d", token);
|
|
|
|
|
search_text = g_strconcat (display, " ", token_code, NULL);
|
|
|
|
|
row->search_text = g_utf8_strdown (search_text, -1);
|
|
|
|
|
g_free (token_code);
|
|
|
|
|
g_free (search_text);
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
if (theme_preferences_staged_get_color (token, &rgba))
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_manager_row_apply (row, &rgba);
|
|
|
|
|
|
|
|
|
|
g_signal_connect (G_OBJECT (button), "clicked",
|
|
|
|
|
G_CALLBACK (theme_preferences_manager_pick_cb), row);
|
|
|
|
|
g_object_set_data (G_OBJECT (button), "zoitechat-theme-color-manager-ui", ui);
|
|
|
|
|
g_signal_connect (G_OBJECT (entry), "activate",
|
|
|
|
|
G_CALLBACK (theme_preferences_manager_entry_activate_cb), row);
|
|
|
|
|
g_signal_connect (G_OBJECT (entry), "focus-out-event",
|
|
|
|
|
G_CALLBACK (theme_preferences_manager_entry_focus_out_cb), row);
|
|
|
|
|
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (list), list_row);
|
|
|
|
|
g_ptr_array_add (ui->rows, row);
|
|
|
|
|
g_free (display);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_signal_connect (G_OBJECT (search), "changed",
|
|
|
|
|
G_CALLBACK (theme_preferences_manager_search_changed_cb), ui);
|
|
|
|
|
|
|
|
|
|
theme_preferences_manager_update_preview (ui);
|
|
|
|
|
|
|
|
|
|
gtk_widget_show_all (dialog);
|
|
|
|
|
return dialog;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_manage_colors_cb (GtkWidget *button, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
gboolean *color_change_flag = user_data;
|
|
|
|
|
GtkWidget *dialog;
|
|
|
|
|
|
|
|
|
|
dialog = theme_preferences_create_color_manager_dialog (GTK_WINDOW (gtk_widget_get_toplevel (button)),
|
|
|
|
|
color_change_flag);
|
|
|
|
|
gtk_dialog_run (GTK_DIALOG (dialog));
|
|
|
|
|
gtk_widget_destroy (dialog);
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
if (color_change_flag)
|
|
|
|
|
*color_change_flag = theme_preferences_stage.active ? theme_preferences_stage.changed : *color_change_flag;
|
2026-03-04 23:28:01 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-12 14:51:10 -06:00
|
|
|
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;
|
|
|
|
|
ThemeSemanticToken token;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2026-03-17 09:43:50 -06:00
|
|
|
theme_preferences_staged_set_color (token, &rgba, color_change_flag, TRUE);
|
2026-03-12 14:51:10 -06:00
|
|
|
any_imported = TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!any_imported)
|
|
|
|
|
theme_preferences_show_import_error (button, _("No importable colors were found in that colors.conf file."));
|
2026-03-17 09:43:50 -06:00
|
|
|
else if (color_change_flag)
|
|
|
|
|
*color_change_flag = theme_preferences_stage.active ? theme_preferences_stage.changed : *color_change_flag;
|
2026-03-12 14:51:10 -06:00
|
|
|
|
|
|
|
|
g_free (cfg);
|
|
|
|
|
g_free (path);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 19:42:48 -07:00
|
|
|
static void
|
|
|
|
|
theme_preferences_create_color_button (GtkWidget *table,
|
|
|
|
|
ThemeSemanticToken token,
|
|
|
|
|
int row,
|
|
|
|
|
int col,
|
|
|
|
|
GtkWindow *parent,
|
|
|
|
|
gboolean *color_change_flag)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *but;
|
|
|
|
|
GtkWidget *label;
|
|
|
|
|
GtkWidget *box;
|
|
|
|
|
char buf[64];
|
|
|
|
|
GdkRGBA color;
|
|
|
|
|
|
|
|
|
|
if (token > THEME_TOKEN_MIRC_31)
|
|
|
|
|
strcpy (buf, "<span size=\"x-small\">  </span>");
|
|
|
|
|
else if (token < 10)
|
|
|
|
|
sprintf (buf, "<span size=\"x-small\"> %d</span>", token);
|
|
|
|
|
else
|
|
|
|
|
sprintf (buf, "<span size=\"x-small\">%d</span>", token);
|
|
|
|
|
|
|
|
|
|
but = gtk_button_new ();
|
|
|
|
|
label = gtk_label_new (" ");
|
|
|
|
|
gtk_label_set_markup (GTK_LABEL (label), buf);
|
|
|
|
|
box = gtk_event_box_new ();
|
|
|
|
|
gtk_event_box_set_visible_window (GTK_EVENT_BOX (box), TRUE);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (box), label);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (but), box);
|
|
|
|
|
gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_set_valign (box, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_show (label);
|
|
|
|
|
gtk_widget_show (box);
|
|
|
|
|
g_object_set_data (G_OBJECT (but), "zoitechat-color", (gpointer)1);
|
|
|
|
|
g_object_set_data (G_OBJECT (but), "zoitechat-color-box", box);
|
|
|
|
|
g_object_set_data (G_OBJECT (but), "zoitechat-theme-token", GINT_TO_POINTER (token));
|
|
|
|
|
g_object_set_data (G_OBJECT (but), "zoitechat-theme-color-change", color_change_flag);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (table), but, col, row, 1, 1);
|
|
|
|
|
g_signal_connect (G_OBJECT (but), "clicked", G_CALLBACK (theme_preferences_color_cb), parent);
|
2026-03-17 09:43:50 -06:00
|
|
|
if (theme_preferences_staged_get_color (token, &color))
|
2026-03-02 19:42:48 -07:00
|
|
|
theme_preferences_color_button_apply (but, &color);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_create_header (GtkWidget *table, int row, const char *labeltext)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *label;
|
|
|
|
|
char buf[128];
|
|
|
|
|
|
|
|
|
|
if (row == 0)
|
|
|
|
|
g_snprintf (buf, sizeof (buf), "<b>%s</b>", _(labeltext));
|
|
|
|
|
else
|
|
|
|
|
g_snprintf (buf, sizeof (buf), "\n<b>%s</b>", _(labeltext));
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (NULL);
|
|
|
|
|
gtk_label_set_markup (GTK_LABEL (label), buf);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (table), label, 0, row, 4, 1);
|
|
|
|
|
gtk_widget_set_margin_bottom (label, 5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_create_other_color_l (GtkWidget *tab,
|
|
|
|
|
const char *text,
|
|
|
|
|
ThemeSemanticToken token,
|
|
|
|
|
int row,
|
|
|
|
|
GtkWindow *parent,
|
|
|
|
|
gboolean *color_change_flag)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *label;
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (text);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (tab), label, 2, row, 1, 1);
|
|
|
|
|
theme_preferences_create_color_button (tab, token, row, 3, parent, color_change_flag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_create_other_color_r (GtkWidget *tab,
|
|
|
|
|
const char *text,
|
|
|
|
|
ThemeSemanticToken token,
|
|
|
|
|
int row,
|
|
|
|
|
GtkWindow *parent,
|
|
|
|
|
gboolean *color_change_flag)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *label;
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (text);
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (tab), label, 5, row, 4, 1);
|
|
|
|
|
theme_preferences_create_color_button (tab, token, row, 9, parent, color_change_flag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_strip_toggle_cb (GtkToggleButton *toggle, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
int *field = user_data;
|
|
|
|
|
|
|
|
|
|
*field = gtk_toggle_button_get_active (toggle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_create_strip_toggle (GtkWidget *tab,
|
|
|
|
|
int row,
|
|
|
|
|
const char *text,
|
|
|
|
|
int *field)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *toggle;
|
|
|
|
|
|
|
|
|
|
toggle = gtk_check_button_new_with_label (text);
|
|
|
|
|
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), *field);
|
|
|
|
|
g_signal_connect (G_OBJECT (toggle), "toggled",
|
|
|
|
|
G_CALLBACK (theme_preferences_strip_toggle_cb), field);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (tab), toggle, 2, row, 1, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GtkWidget *
|
|
|
|
|
theme_preferences_create_color_page (GtkWindow *parent,
|
|
|
|
|
struct zoitechatprefs *setup_prefs,
|
|
|
|
|
gboolean *color_change_flag)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *tab;
|
|
|
|
|
GtkWidget *box;
|
|
|
|
|
GtkWidget *label;
|
2026-03-04 23:28:01 -07:00
|
|
|
GtkWidget *manage_colors_button;
|
2026-03-02 19:42:48 -07:00
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 6);
|
|
|
|
|
|
|
|
|
|
tab = gtk_grid_new ();
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (tab), 6);
|
|
|
|
|
gtk_grid_set_row_spacing (GTK_GRID (tab), 2);
|
|
|
|
|
gtk_grid_set_column_spacing (GTK_GRID (tab), 3);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (box), tab);
|
|
|
|
|
|
|
|
|
|
theme_preferences_create_header (tab, 0, N_("Text Colors"));
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (_("mIRC colors:"));
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (tab), label, 2, 1, 1, 1);
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
|
theme_preferences_create_color_button (tab,
|
|
|
|
|
THEME_TOKEN_MIRC_0 + i,
|
|
|
|
|
1,
|
|
|
|
|
i + 3,
|
|
|
|
|
parent,
|
|
|
|
|
color_change_flag);
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (_("Local colors:"));
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
|
|
|
|
|
gtk_widget_set_margin_start (label, LABEL_INDENT);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (tab), label, 2, 2, 1, 1);
|
|
|
|
|
|
|
|
|
|
for (i = 16; i < 32; i++)
|
|
|
|
|
theme_preferences_create_color_button (tab,
|
|
|
|
|
THEME_TOKEN_MIRC_0 + i,
|
|
|
|
|
2,
|
|
|
|
|
(i + 3) - 16,
|
|
|
|
|
parent,
|
|
|
|
|
color_change_flag);
|
|
|
|
|
|
|
|
|
|
theme_preferences_create_other_color_l (tab, _("Foreground:"), THEME_TOKEN_TEXT_FOREGROUND, 3,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_other_color_r (tab, _("Background:"), THEME_TOKEN_TEXT_BACKGROUND, 3,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
|
|
|
|
|
theme_preferences_create_header (tab, 5, N_("Selected Text"));
|
|
|
|
|
theme_preferences_create_other_color_l (tab, _("Foreground:"), THEME_TOKEN_SELECTION_FOREGROUND, 6,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_other_color_r (tab, _("Background:"), THEME_TOKEN_SELECTION_BACKGROUND, 6,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
|
|
|
|
|
theme_preferences_create_header (tab, 8, N_("Interface Colors"));
|
|
|
|
|
theme_preferences_create_other_color_l (tab, _("New data:"), THEME_TOKEN_TAB_NEW_DATA, 9,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_other_color_r (tab, _("Marker line:"), THEME_TOKEN_MARKER, 9,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_other_color_l (tab, _("New message:"), THEME_TOKEN_TAB_NEW_MESSAGE, 10,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_other_color_r (tab, _("Away user:"), THEME_TOKEN_TAB_AWAY, 10,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_other_color_l (tab, _("Highlight:"), THEME_TOKEN_TAB_HIGHLIGHT, 11,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_other_color_r (tab, _("Spell checker:"), THEME_TOKEN_SPELL, 11,
|
|
|
|
|
parent, color_change_flag);
|
|
|
|
|
theme_preferences_create_header (tab, 15, N_("Color Stripping"));
|
|
|
|
|
theme_preferences_create_strip_toggle (tab, 16, _("Messages"), &setup_prefs->hex_text_stripcolor_msg);
|
|
|
|
|
theme_preferences_create_strip_toggle (tab, 17, _("Scrollback"), &setup_prefs->hex_text_stripcolor_replay);
|
|
|
|
|
theme_preferences_create_strip_toggle (tab, 18, _("Topic"), &setup_prefs->hex_text_stripcolor_topic);
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
manage_colors_button = gtk_button_new_with_label (_("Manage all client colors…"));
|
|
|
|
|
gtk_widget_set_halign (manage_colors_button, GTK_ALIGN_START);
|
|
|
|
|
gtk_widget_set_margin_start (manage_colors_button, LABEL_INDENT);
|
|
|
|
|
gtk_widget_set_margin_top (manage_colors_button, 10);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (box), manage_colors_button, FALSE, FALSE, 0);
|
|
|
|
|
g_signal_connect (G_OBJECT (manage_colors_button), "clicked",
|
|
|
|
|
G_CALLBACK (theme_preferences_manage_colors_cb), color_change_flag);
|
2026-03-02 19:42:48 -07:00
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
return box;
|
2026-03-02 19:42:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_open_gtk3_folder_cb (GtkWidget *button, gpointer user_data)
|
2026-03-02 19:42:48 -07:00
|
|
|
{
|
|
|
|
|
char *themes_dir;
|
|
|
|
|
|
2026-03-14 20:01:58 -06:00
|
|
|
(void)user_data;
|
2026-03-02 19:42:48 -07:00
|
|
|
(void)button;
|
2026-03-04 23:28:01 -07:00
|
|
|
themes_dir = zoitechat_gtk3_theme_service_get_user_themes_dir ();
|
2026-03-02 19:42:48 -07:00
|
|
|
g_mkdir_with_parents (themes_dir, 0700);
|
|
|
|
|
fe_open_url (themes_dir);
|
|
|
|
|
g_free (themes_dir);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
static char *
|
|
|
|
|
theme_preferences_gtk3_active_id (theme_preferences_ui *ui)
|
|
|
|
|
{
|
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
GtkTreeModel *model;
|
|
|
|
|
char *id = NULL;
|
|
|
|
|
|
|
|
|
|
if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (ui->gtk3_combo), &iter))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->gtk3_combo));
|
|
|
|
|
gtk_tree_model_get (model, &iter, GTK3_THEME_COL_ID, &id, -1);
|
|
|
|
|
return id;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 19:42:48 -07:00
|
|
|
static void
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_gtk3_sync_remove_state (theme_preferences_ui *ui)
|
2026-03-02 19:42:48 -07:00
|
|
|
{
|
2026-03-04 23:28:01 -07:00
|
|
|
GtkTreeIter iter;
|
|
|
|
|
GtkTreeModel *model;
|
|
|
|
|
int source = -1;
|
2026-03-02 19:42:48 -07:00
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
if (!gtk_combo_box_get_active_iter (GTK_COMBO_BOX (ui->gtk3_combo), &iter))
|
|
|
|
|
{
|
|
|
|
|
gtk_widget_set_sensitive (ui->gtk3_remove, FALSE);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
model = gtk_combo_box_get_model (GTK_COMBO_BOX (ui->gtk3_combo));
|
|
|
|
|
gtk_tree_model_get (model, &iter, GTK3_THEME_COL_SOURCE, &source, -1);
|
|
|
|
|
gtk_widget_set_sensitive (ui->gtk3_remove, source == ZOITECHAT_GTK3_THEME_SOURCE_USER);
|
2026-03-02 19:42:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_gtk3_changed_cb (GtkComboBox *combo, gpointer user_data)
|
2026-03-02 19:42:48 -07:00
|
|
|
{
|
|
|
|
|
theme_preferences_ui *ui = user_data;
|
2026-03-04 23:28:01 -07:00
|
|
|
char *id;
|
|
|
|
|
gboolean selection_changed;
|
|
|
|
|
ThemeGtk3Variant variant;
|
2026-03-02 19:42:48 -07:00
|
|
|
GError *error = NULL;
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
(void) combo;
|
|
|
|
|
theme_preferences_gtk3_sync_remove_state (ui);
|
|
|
|
|
if (ui->gtk3_populating)
|
2026-03-02 19:42:48 -07:00
|
|
|
return;
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
id = theme_preferences_gtk3_active_id (ui);
|
|
|
|
|
if (!id)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
variant = theme_gtk3_variant_for_theme (id);
|
|
|
|
|
selection_changed = g_strcmp0 (prefs.hex_gui_gtk3_theme, id) != 0
|
|
|
|
|
|| prefs.hex_gui_gtk3_variant != variant;
|
|
|
|
|
g_strlcpy (prefs.hex_gui_gtk3_theme, id, sizeof (prefs.hex_gui_gtk3_theme));
|
|
|
|
|
prefs.hex_gui_gtk3_variant = variant;
|
2026-03-02 19:42:48 -07:00
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
if (ui->setup_prefs)
|
2026-03-02 19:42:48 -07:00
|
|
|
{
|
2026-03-04 23:28:01 -07:00
|
|
|
g_strlcpy (ui->setup_prefs->hex_gui_gtk3_theme, id, sizeof (ui->setup_prefs->hex_gui_gtk3_theme));
|
|
|
|
|
ui->setup_prefs->hex_gui_gtk3_variant = prefs.hex_gui_gtk3_variant;
|
2026-03-02 19:42:48 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
if (selection_changed && !theme_gtk3_apply_current (&error))
|
2026-03-02 19:42:48 -07:00
|
|
|
{
|
|
|
|
|
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
2026-03-04 23:28:01 -07:00
|
|
|
error ? error->message : _("Failed to apply GTK3 theme."));
|
2026-03-02 19:42:48 -07:00
|
|
|
g_clear_error (&error);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
g_free (id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GdkPixbuf *
|
|
|
|
|
theme_preferences_load_thumbnail (const char *path)
|
|
|
|
|
{
|
|
|
|
|
char *data = NULL;
|
|
|
|
|
gsize length = 0;
|
|
|
|
|
GdkPixbufLoader *loader;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
GdkPixbuf *pixbuf;
|
|
|
|
|
GdkPixbuf *scaled;
|
|
|
|
|
int width;
|
|
|
|
|
int height;
|
|
|
|
|
|
|
|
|
|
if (!path || !g_file_get_contents (path, &data, &length, &error))
|
|
|
|
|
{
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loader = gdk_pixbuf_loader_new ();
|
|
|
|
|
if (!gdk_pixbuf_loader_write (loader, (const guchar *) data, length, &error))
|
|
|
|
|
{
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
g_object_unref (loader);
|
|
|
|
|
g_free (data);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_free (data);
|
|
|
|
|
|
|
|
|
|
if (!gdk_pixbuf_loader_close (loader, &error))
|
|
|
|
|
{
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
g_object_unref (loader);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
|
|
|
|
|
if (!pixbuf)
|
|
|
|
|
{
|
|
|
|
|
g_object_unref (loader);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
width = gdk_pixbuf_get_width (pixbuf);
|
|
|
|
|
height = gdk_pixbuf_get_height (pixbuf);
|
|
|
|
|
if (width > 48 || height > 48)
|
|
|
|
|
scaled = gdk_pixbuf_scale_simple (pixbuf, 48, 48, GDK_INTERP_BILINEAR);
|
|
|
|
|
else
|
|
|
|
|
scaled = gdk_pixbuf_copy (pixbuf);
|
|
|
|
|
|
|
|
|
|
g_object_unref (loader);
|
|
|
|
|
return scaled;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 14:51:10 -06:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
static void
|
|
|
|
|
theme_preferences_populate_gtk3 (theme_preferences_ui *ui)
|
|
|
|
|
{
|
|
|
|
|
GPtrArray *themes;
|
|
|
|
|
guint i;
|
|
|
|
|
GtkTreeStore *store;
|
|
|
|
|
GtkTreeIter iter;
|
|
|
|
|
int active = -1;
|
|
|
|
|
gboolean removed_selected_theme = FALSE;
|
2026-03-12 14:51:10 -06:00
|
|
|
gboolean using_system_default = prefs.hex_gui_gtk3_theme[0] == '\0';
|
2026-03-04 23:28:01 -07:00
|
|
|
gboolean should_apply = FALSE;
|
|
|
|
|
char *final_id;
|
|
|
|
|
ThemeGtk3Variant final_variant = THEME_GTK3_VARIANT_PREFER_LIGHT;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
|
|
store = GTK_TREE_STORE (gtk_combo_box_get_model (GTK_COMBO_BOX (ui->gtk3_combo)));
|
|
|
|
|
ui->gtk3_populating = TRUE;
|
|
|
|
|
gtk_tree_store_clear (store);
|
|
|
|
|
themes = zoitechat_gtk3_theme_service_discover ();
|
|
|
|
|
for (i = 0; i < themes->len; i++)
|
|
|
|
|
{
|
|
|
|
|
ZoitechatGtk3Theme *theme = g_ptr_array_index (themes, i);
|
|
|
|
|
char *label = g_strdup_printf ("%s (%s)", theme->display_name,
|
|
|
|
|
theme->source == ZOITECHAT_GTK3_THEME_SOURCE_USER ? _("user") : _("system"));
|
|
|
|
|
GdkPixbuf *thumbnail = NULL;
|
|
|
|
|
|
|
|
|
|
if (theme->thumbnail_path && g_file_test (theme->thumbnail_path, G_FILE_TEST_IS_REGULAR))
|
|
|
|
|
thumbnail = theme_preferences_load_thumbnail (theme->thumbnail_path);
|
|
|
|
|
|
|
|
|
|
gtk_tree_store_append (store, &iter, NULL);
|
|
|
|
|
gtk_tree_store_set (store, &iter,
|
|
|
|
|
GTK3_THEME_COL_ID, theme->id,
|
|
|
|
|
GTK3_THEME_COL_LABEL, label,
|
|
|
|
|
GTK3_THEME_COL_SOURCE, theme->source,
|
|
|
|
|
GTK3_THEME_COL_THUMBNAIL, thumbnail,
|
|
|
|
|
-1);
|
|
|
|
|
if (g_strcmp0 (prefs.hex_gui_gtk3_theme, theme->id) == 0)
|
|
|
|
|
active = i;
|
|
|
|
|
if (thumbnail)
|
|
|
|
|
g_object_unref (thumbnail);
|
|
|
|
|
g_free (label);
|
|
|
|
|
}
|
2026-03-12 14:51:10 -06:00
|
|
|
if (active < 0 && using_system_default)
|
|
|
|
|
active = theme_preferences_gtk3_find_system_theme_index (themes);
|
2026-03-04 23:28:01 -07:00
|
|
|
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);
|
2026-03-12 14:51:10 -06:00
|
|
|
if (!using_system_default)
|
2026-03-04 23:28:01 -07:00
|
|
|
removed_selected_theme = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (prefs.hex_gui_gtk3_theme[0] != '\0')
|
|
|
|
|
removed_selected_theme = TRUE;
|
|
|
|
|
gtk_widget_set_sensitive (ui->gtk3_combo, themes->len > 0);
|
|
|
|
|
theme_preferences_gtk3_sync_remove_state (ui);
|
|
|
|
|
ui->gtk3_populating = FALSE;
|
|
|
|
|
|
|
|
|
|
final_id = theme_preferences_gtk3_active_id (ui);
|
|
|
|
|
if (final_id)
|
|
|
|
|
{
|
|
|
|
|
final_variant = theme_gtk3_variant_for_theme (final_id);
|
2026-03-12 14:51:10 -06:00
|
|
|
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;
|
|
|
|
|
}
|
2026-03-04 23:28:01 -07:00
|
|
|
g_free (final_id);
|
|
|
|
|
}
|
2026-03-02 19:42:48 -07:00
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
if (should_apply && !theme_gtk3_apply_current (&error))
|
|
|
|
|
{
|
|
|
|
|
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
|
|
|
|
error ? error->message : _("Failed to apply GTK3 theme."));
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
}
|
2026-03-02 19:42:48 -07:00
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
g_ptr_array_unref (themes);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 10:45:29 -06:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
static void
|
|
|
|
|
theme_preferences_gtk3_import_cb (GtkWidget *button, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_preferences_ui *ui = user_data;
|
2026-03-16 10:45:29 -06:00
|
|
|
GtkFileChooserNative *dialog;
|
2026-03-04 23:28:01 -07:00
|
|
|
GtkFileFilter *filter;
|
|
|
|
|
char *path;
|
|
|
|
|
|
|
|
|
|
(void)button;
|
2026-03-16 10:45:29 -06:00
|
|
|
dialog = gtk_file_chooser_native_new (_("Import GTK3 Theme"), ui->parent,
|
2026-03-04 23:28:01 -07:00
|
|
|
GTK_FILE_CHOOSER_ACTION_OPEN,
|
2026-03-16 10:45:29 -06:00
|
|
|
_("_Import"),
|
|
|
|
|
_("_Cancel"));
|
2026-03-04 23:28:01 -07:00
|
|
|
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 ();
|
|
|
|
|
gtk_file_filter_set_name (filter, _("Theme archives (*.zip, *.tar, *.tar.xz, *.tgz, *.tar.gz, *.tar.bz2)"));
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.zip");
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.tar");
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.tar.xz");
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.txz");
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.tar.gz");
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.tgz");
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.tar.bz2");
|
|
|
|
|
gtk_file_filter_add_pattern (filter, "*.tbz");
|
|
|
|
|
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dialog), filter);
|
|
|
|
|
|
2026-03-16 10:45:29 -06:00
|
|
|
if (gtk_native_dialog_run (GTK_NATIVE_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
|
2026-03-04 23:28:01 -07:00
|
|
|
{
|
2026-03-16 10:45:29 -06:00
|
|
|
g_object_unref (dialog);
|
2026-03-04 23:28:01 -07:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-16 10:45:29 -06:00
|
|
|
path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
|
|
|
|
|
g_object_unref (dialog);
|
|
|
|
|
theme_preferences_gtk3_import_path (ui, path);
|
2026-03-04 23:28:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
theme_preferences_gtk3_remove_cb (GtkWidget *button, gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
theme_preferences_ui *ui = user_data;
|
|
|
|
|
char *id;
|
|
|
|
|
GError *error = NULL;
|
|
|
|
|
|
|
|
|
|
(void)button;
|
|
|
|
|
id = theme_preferences_gtk3_active_id (ui);
|
|
|
|
|
if (!id)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!zoitechat_gtk3_theme_service_remove_user_theme (id, &error))
|
|
|
|
|
theme_preferences_show_message (ui, GTK_MESSAGE_ERROR,
|
|
|
|
|
error ? error->message : _("Failed to remove GTK3 theme."));
|
|
|
|
|
g_clear_error (&error);
|
|
|
|
|
g_free (id);
|
|
|
|
|
theme_preferences_populate_gtk3 (ui);
|
2026-03-02 19:42:48 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GtkWidget *
|
2026-03-04 23:28:01 -07:00
|
|
|
theme_preferences_create_page (GtkWindow *parent,
|
|
|
|
|
struct zoitechatprefs *setup_prefs,
|
|
|
|
|
gboolean *color_change_flag)
|
2026-03-02 19:42:48 -07:00
|
|
|
{
|
|
|
|
|
theme_preferences_ui *ui;
|
|
|
|
|
GtkWidget *box;
|
|
|
|
|
GtkWidget *label;
|
2026-03-04 23:28:01 -07:00
|
|
|
GtkWidget *colors_frame;
|
|
|
|
|
GtkWidget *colors_box;
|
|
|
|
|
GtkWidget *manage_colors_button;
|
2026-03-12 14:51:10 -06:00
|
|
|
GtkWidget *import_colors_button;
|
2026-03-04 23:28:01 -07:00
|
|
|
GtkWidget *gtk3_frame;
|
|
|
|
|
GtkWidget *gtk3_grid;
|
|
|
|
|
GtkWidget *gtk3_button;
|
|
|
|
|
GtkTreeStore *gtk3_store;
|
|
|
|
|
GtkCellRenderer *renderer;
|
2026-03-02 19:42:48 -07:00
|
|
|
|
|
|
|
|
ui = g_new0 (theme_preferences_ui, 1);
|
|
|
|
|
ui->parent = parent;
|
2026-03-04 23:28:01 -07:00
|
|
|
ui->setup_prefs = setup_prefs;
|
2026-03-02 19:42:48 -07:00
|
|
|
|
|
|
|
|
box = gtkutil_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 6);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (box), 6);
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
colors_frame = gtk_frame_new (_("Colors"));
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (box), colors_frame, FALSE, FALSE, 0);
|
|
|
|
|
colors_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (colors_box), 6);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (colors_frame), colors_box);
|
2026-03-02 19:42:48 -07:00
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
label = gtk_label_new (_("GTK3 theme colors are used by default. Open the color manager to set custom colors."));
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (colors_box), label, FALSE, FALSE, 0);
|
|
|
|
|
|
|
|
|
|
manage_colors_button = gtk_button_new_with_label (_("Manage all client colors…"));
|
|
|
|
|
gtk_widget_set_halign (manage_colors_button, GTK_ALIGN_START);
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (colors_box), manage_colors_button, FALSE, FALSE, 0);
|
|
|
|
|
g_signal_connect (G_OBJECT (manage_colors_button), "clicked",
|
|
|
|
|
G_CALLBACK (theme_preferences_manage_colors_cb), color_change_flag);
|
|
|
|
|
|
2026-03-12 14:51:10 -06:00
|
|
|
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);
|
|
|
|
|
|
2026-03-04 23:28:01 -07:00
|
|
|
gtk3_frame = gtk_frame_new (_("GTK3 Theme"));
|
|
|
|
|
gtk_box_pack_start (GTK_BOX (box), gtk3_frame, FALSE, FALSE, 0);
|
|
|
|
|
gtk3_grid = gtk_grid_new ();
|
|
|
|
|
gtk_container_set_border_width (GTK_CONTAINER (gtk3_grid), 6);
|
|
|
|
|
gtk_grid_set_row_spacing (GTK_GRID (gtk3_grid), 6);
|
|
|
|
|
gtk_grid_set_column_spacing (GTK_GRID (gtk3_grid), 6);
|
|
|
|
|
gtk_container_add (GTK_CONTAINER (gtk3_frame), gtk3_grid);
|
|
|
|
|
|
|
|
|
|
label = gtk_label_new (_("GTK3 theme:"));
|
|
|
|
|
gtk_widget_set_halign (label, GTK_ALIGN_START);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (gtk3_grid), label, 0, 0, 1, 1);
|
|
|
|
|
gtk3_store = gtk_tree_store_new (GTK3_THEME_COL_COUNT,
|
|
|
|
|
G_TYPE_STRING,
|
|
|
|
|
G_TYPE_STRING,
|
|
|
|
|
G_TYPE_INT,
|
|
|
|
|
GDK_TYPE_PIXBUF);
|
|
|
|
|
ui->gtk3_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (gtk3_store));
|
|
|
|
|
g_object_unref (gtk3_store);
|
|
|
|
|
renderer = gtk_cell_renderer_pixbuf_new ();
|
|
|
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, FALSE);
|
|
|
|
|
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, "pixbuf", GTK3_THEME_COL_THUMBNAIL);
|
|
|
|
|
renderer = gtk_cell_renderer_text_new ();
|
|
|
|
|
gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, TRUE);
|
|
|
|
|
gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (ui->gtk3_combo), renderer, "text", GTK3_THEME_COL_LABEL);
|
|
|
|
|
gtk_grid_attach (GTK_GRID (gtk3_grid), ui->gtk3_combo, 1, 0, 3, 1);
|
|
|
|
|
g_signal_connect (G_OBJECT (ui->gtk3_combo), "changed", G_CALLBACK (theme_preferences_gtk3_changed_cb), ui);
|
|
|
|
|
|
|
|
|
|
gtk3_button = gtk_button_new_with_mnemonic (_("Import"));
|
|
|
|
|
gtk_grid_attach (GTK_GRID (gtk3_grid), gtk3_button, 0, 1, 1, 1);
|
|
|
|
|
g_signal_connect (G_OBJECT (gtk3_button), "clicked", G_CALLBACK (theme_preferences_gtk3_import_cb), ui);
|
|
|
|
|
|
|
|
|
|
ui->gtk3_remove = gtk_button_new_with_mnemonic (_("Remove"));
|
|
|
|
|
gtk_grid_attach (GTK_GRID (gtk3_grid), ui->gtk3_remove, 1, 1, 1, 1);
|
|
|
|
|
g_signal_connect (G_OBJECT (ui->gtk3_remove), "clicked", G_CALLBACK (theme_preferences_gtk3_remove_cb), ui);
|
|
|
|
|
|
|
|
|
|
gtk3_button = gtk_button_new_with_mnemonic (_("Open theme folder"));
|
|
|
|
|
gtk_grid_attach (GTK_GRID (gtk3_grid), gtk3_button, 2, 1, 2, 1);
|
|
|
|
|
g_signal_connect (G_OBJECT (gtk3_button), "clicked", G_CALLBACK (theme_preferences_open_gtk3_folder_cb), ui);
|
|
|
|
|
|
|
|
|
|
theme_preferences_populate_gtk3 (ui);
|
2026-03-02 19:42:48 -07:00
|
|
|
|
|
|
|
|
g_object_set_data_full (G_OBJECT (box), "theme-preferences-ui", ui, g_free);
|
|
|
|
|
|
|
|
|
|
return box;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
theme_preferences_apply_to_session (session_gui *gui, InputStyle *input_style)
|
|
|
|
|
{
|
|
|
|
|
if (gui->user_tree)
|
|
|
|
|
{
|
|
|
|
|
theme_manager_apply_userlist_style (gui->user_tree,
|
|
|
|
|
theme_manager_get_userlist_palette_behavior (input_style ? input_style->font_desc : NULL));
|
|
|
|
|
}
|
|
|
|
|
}
|