1 Commits

Author SHA1 Message Date
05ee0dbdb5 Reduce needed client restarts for settings changes. 2026-07-16 08:48:33 -06:00
8 changed files with 532 additions and 63 deletions

View File

@@ -307,6 +307,109 @@ chanview_set_impl (chanview *cv, int type)
cv->func_focus (cv->focused); cv->func_focus (cv->focused);
} }
void
chanview_set_sorted (chanview *cv, gboolean sorted)
{
cv->sorted = sorted ? 1 : 0;
}
void
chanview_set_use_icons (chanview *cv, gboolean use_icons)
{
cv->use_icons = use_icons ? 1 : 0;
}
void
chanview_set_trunc_len (chanview *cv, int trunc_len)
{
cv->trunc_len = trunc_len;
}
typedef struct
{
chan *ch;
int old_pos;
} chanview_sort_entry;
/* re-sort the session rows below one parent with cb_compare, leaving
* utility rows (tag != 0) in place; mirrors chanview_insert_sorted() */
static void
chanview_resort_children (chanview *cv, GtkTreeIter *parent)
{
GtkTreeModel *model = GTK_TREE_MODEL (cv->store);
GtkTreeIter iter;
chanview_sort_entry *entries;
int *positions;
gint *new_order;
chan *ch;
int n, i, j, count;
n = gtk_tree_model_iter_n_children (model, parent);
if (n < 2)
return;
entries = g_new0 (chanview_sort_entry, n);
positions = g_new0 (int, n);
new_order = g_new0 (gint, n);
count = 0;
for (i = 0; i < n; i++)
{
new_order[i] = i;
if (!gtk_tree_model_iter_nth_child (model, &iter, parent, i))
continue;
gtk_tree_model_get (model, &iter, COL_CHAN, &ch, -1);
if (ch && ch->tag == 0 && ch->userdata)
{
entries[count].ch = ch;
entries[count].old_pos = i;
positions[count] = i;
count++;
}
}
/* stable insertion sort; tab groups stay small so this is cheap */
for (i = 1; i < count; i++)
{
chanview_sort_entry key = entries[i];
j = i - 1;
while (j >= 0 && cv->cb_compare (entries[j].ch->userdata, key.ch->userdata) > 0)
{
entries[j + 1] = entries[j];
j--;
}
entries[j + 1] = key;
}
for (i = 0; i < count; i++)
new_order[positions[i]] = entries[i].old_pos;
gtk_tree_store_reorder (cv->store, parent, new_order);
g_free (entries);
g_free (positions);
g_free (new_order);
}
/* top-level rows keep their order (insertion never sorts them either);
* only the children within each family are sorted */
void
chanview_resort (chanview *cv)
{
GtkTreeIter iter;
if (!cv->cb_compare)
return;
if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter))
{
do
chanview_resort_children (cv, &iter);
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter));
}
}
static void static void
chanview_free_ch (chanview *cv, GtkTreeIter *iter) chanview_free_ch (chanview *cv, GtkTreeIter *iter)
{ {

View File

@@ -34,6 +34,10 @@ void chanview_set_callbacks (chanview *cv,
gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *), gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *),
int (*cb_compare) (void *a, void *b)); int (*cb_compare) (void *a, void *b));
void chanview_set_impl (chanview *cv, int type); void chanview_set_impl (chanview *cv, int type);
void chanview_set_sorted (chanview *cv, gboolean sorted);
void chanview_set_use_icons (chanview *cv, gboolean use_icons);
void chanview_set_trunc_len (chanview *cv, int trunc_len);
void chanview_resort (chanview *cv);
chan *chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon); chan *chanview_add (chanview *cv, char *name, void *family, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon);
int chanview_get_size (chanview *cv); int chanview_get_size (chanview *cv);
GtkWidget *chanview_get_box (chanview *cv); GtkWidget *chanview_get_box (chanview *cv);

View File

@@ -569,6 +569,18 @@ mg_create_tab_colors (void)
away_list = mg_attr_list_create (&gui_palette[THEME_TOKEN_TAB_AWAY], FALSE); away_list = mg_attr_list_create (&gui_palette[THEME_TOKEN_TAB_AWAY], FALSE);
} }
static PangoAttrList *
mg_attr_list_for_tab_state (int state)
{
if (state & TAB_STATE_NEW_HILIGHT)
return nickseen_list;
if (state & TAB_STATE_NEW_MSG)
return newmsg_list;
if (state & TAB_STATE_NEW_DATA)
return newdata_list;
return plain_list;
}
static void static void
set_window_urgency (GtkWidget *win, gboolean set) set_window_urgency (GtkWidget *win, gboolean set)
{ {
@@ -714,7 +726,10 @@ mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away)
{ {
if (gui->op_xpm) if (gui->op_xpm)
{ {
if (pix == gtk_image_get_pixbuf (GTK_IMAGE (gui->op_xpm))) /* no change? */ /* no change? (the pref check makes sure a live settings
change can still remove the icon) */
if (pix == gtk_image_get_pixbuf (GTK_IMAGE (gui->op_xpm)) &&
prefs.hex_gui_input_icon)
{ {
mg_set_myself_away (gui, away); mg_set_myself_away (gui, away);
return; return;
@@ -3596,6 +3611,48 @@ mg_create_topicbar (session *sess, GtkWidget *box)
mg_create_chanmodebuttons (gui, bbox); mg_create_chanmodebuttons (gui, bbox);
} }
/* re-apply the multi-line topic and inline mode button preferences to an
* already built topic bar (see mg_create_topicbar () for the layout) */
static void
mg_update_topicbar_layout (session_gui *gui)
{
GtkWidget *topic = gui->topic_entry;
GtkWidget *vbox = gui->topic_bar;
GtkWidget *topic_scroll;
GtkWidget *hbox;
GtkWidget *mode_hbox;
GtkWidget *desired_parent;
GtkWidget *current_parent;
gboolean wrap;
if (!topic || !GTK_IS_TEXT_VIEW (topic) || !vbox || !gui->topicbutton_box)
return;
wrap = prefs.hex_gui_topicbar_multiline && !prefs.hex_gui_mode_buttons_inline;
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (topic),
wrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE);
topic_scroll = gtk_widget_get_parent (topic);
hbox = topic_scroll ? gtk_widget_get_parent (topic_scroll) : NULL;
mode_hbox = gtk_widget_get_parent (gui->topicbutton_box);
if (mode_hbox && hbox)
{
desired_parent = prefs.hex_gui_mode_buttons_inline ? hbox : vbox;
current_parent = gtk_widget_get_parent (mode_hbox);
if (current_parent && current_parent != desired_parent)
{
g_object_ref (mode_hbox);
gtk_container_remove (GTK_CONTAINER (current_parent), mode_hbox);
gtk_box_pack_start (GTK_BOX (desired_parent), mode_hbox, 0, 0, 0);
g_object_unref (mode_hbox);
}
}
mg_topicbar_update_height (topic);
}
/* check if a word is clickable */ /* check if a word is clickable */
static int static int
@@ -4004,8 +4061,12 @@ mg_create_userlist (session_gui *gui, GtkWidget *box)
gtk_widget_set_margin_end (gui->namelistinfo, 0); gtk_widget_set_margin_end (gui->namelistinfo, 0);
gtk_widget_set_hexpand (gui->namelistinfo, TRUE); gtk_widget_set_hexpand (gui->namelistinfo, TRUE);
gtk_widget_set_halign (gui->namelistinfo, GTK_ALIGN_FILL); gtk_widget_set_halign (gui->namelistinfo, GTK_ALIGN_FILL);
/* always pack it so the preference can be toggled without a
restart; visibility is controlled explicitly */
gtk_widget_set_no_show_all (gui->namelistinfo, TRUE);
gtk_box_pack_start (GTK_BOX (vbox), gui->namelistinfo, 0, 0, 0);
if (prefs.hex_gui_ulist_count) if (prefs.hex_gui_ulist_count)
gtk_box_pack_start (GTK_BOX (vbox), gui->namelistinfo, 0, 0, 0); gtk_widget_show (gui->namelistinfo);
gui->user_tree = ulist = userlist_create (vbox); gui->user_tree = ulist = userlist_create (vbox);
@@ -5233,6 +5294,10 @@ mg_apply_setup (void)
sess = list->data; sess = list->data;
gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text); gtk_xtext_set_time_stamp (sess->res->buffer, prefs.hex_stamp_text);
((xtext_buffer *)sess->res->buffer)->needs_recalc = TRUE; ((xtext_buffer *)sess->res->buffer)->needs_recalc = TRUE;
/* the attribute lists were just recreated, so re-apply the
color matching each tab's activity state */
if (sess->res->tab)
chan_set_color (sess->res->tab, mg_attr_list_for_tab_state (sess->tab_state));
if (!sess->gui->is_tab || !done_main) if (!sess->gui->is_tab || !done_main)
mg_place_userlist_and_chanview (sess->gui); mg_place_userlist_and_chanview (sess->gui);
if (sess->gui->is_tab) if (sess->gui->is_tab)
@@ -5275,6 +5340,49 @@ fe_buttons_update (session *sess)
gtk_widget_hide (sess->gui->button_box); gtk_widget_hide (sess->gui->button_box);
} }
/* tab label for a session that has no joined channel: the channel we are
* waiting to join in parentheses, or "<none>". tbuf must hold CHANLEN+6. */
static void
mg_tab_pending_name (session *sess, char *tbuf)
{
if (sess->waitchannel[0])
{
if (prefs.hex_gui_tab_trunc > 2 && g_utf8_strlen (sess->waitchannel, -1) > prefs.hex_gui_tab_trunc)
{
/* truncate long channel names */
tbuf[0] = '(';
strcpy (tbuf + 1, sess->waitchannel);
g_utf8_offset_to_pointer(tbuf, prefs.hex_gui_tab_trunc)[0] = 0;
strcat (tbuf, "..)");
} else
{
sprintf (tbuf, "(%s)", sess->waitchannel);
}
}
else
strcpy (tbuf, _("<none>"));
}
/* re-apply the tab label truncation preference to a session's tab */
static void
mg_retrunc_tab (session *sess)
{
char tbuf[CHANLEN+6];
if (!sess->res->tab)
return;
if (sess->channel[0])
chan_rename (sess->res->tab, sess->channel, prefs.hex_gui_tab_trunc);
else
{
mg_tab_pending_name (sess, tbuf);
chan_rename (sess->res->tab, tbuf, prefs.hex_gui_tab_trunc);
}
}
void void
fe_clear_channel (session *sess) fe_clear_channel (session *sess)
{ {
@@ -5283,22 +5391,7 @@ fe_clear_channel (session *sess)
if (sess->gui->is_tab) if (sess->gui->is_tab)
{ {
if (sess->waitchannel[0]) mg_tab_pending_name (sess, tbuf);
{
if (prefs.hex_gui_tab_trunc > 2 && g_utf8_strlen (sess->waitchannel, -1) > prefs.hex_gui_tab_trunc)
{
/* truncate long channel names */
tbuf[0] = '(';
strcpy (tbuf + 1, sess->waitchannel);
g_utf8_offset_to_pointer(tbuf, prefs.hex_gui_tab_trunc)[0] = 0;
strcat (tbuf, "..)");
} else
{
sprintf (tbuf, "(%s)", sess->waitchannel);
}
}
else
strcpy (tbuf, _("<none>"));
chan_rename (sess->res->tab, tbuf, prefs.hex_gui_tab_trunc); chan_rename (sess->res->tab, tbuf, prefs.hex_gui_tab_trunc);
} }
@@ -5422,6 +5515,110 @@ fe_set_channel (session *sess)
chan_rename (sess->res->tab, sess->channel, prefs.hex_gui_tab_trunc); chan_rename (sess->res->tab, sess->channel, prefs.hex_gui_tab_trunc);
} }
static void
mg_apply_live_prefs_to_gui (session_gui *gui, session *sess, const mg_live_prefs *changes)
{
if (changes->meters)
mg_update_meters (gui);
if (changes->ulist_columns && gui->user_tree)
userlist_rebuild_columns (gui->user_tree);
if (changes->ulist_count && gui->namelistinfo)
{
if (prefs.hex_gui_ulist_count)
gtk_widget_show (gui->namelistinfo);
else
gtk_widget_hide (gui->namelistinfo);
}
if (changes->input_box && gui->nick_box)
{
if (prefs.hex_gui_input_nick)
gtk_widget_show (gui->nick_box);
else
gtk_widget_hide (gui->nick_box);
}
/* refresh the access icon; re-filling the user list rows can
clear it as a side effect, so restore it here too */
if ((changes->input_box || changes->ulist_rows) && sess && is_session (sess))
{
if (sess->type == SESS_DIALOG)
mg_set_access_icon (gui, NULL, sess->server->is_away);
else
mg_set_access_icon (gui, get_user_icon (sess->server, sess->me), sess->server->is_away);
}
if (changes->topic_bar)
mg_update_topicbar_layout (gui);
if (changes->transparency && gui->window)
gtk_widget_set_opacity (gui->window, (prefs.hex_gui_transparency / 255.));
}
/* apply preference changes that used to require a restart. called by
* setup.c after the new prefs have been committed. */
void
mg_apply_live_prefs (const mg_live_prefs *changes)
{
GSList *list;
session *sess;
int done_main = FALSE;
if (!changes)
return;
/* the tab bar / channel tree lives in the shared tab window */
if (mg_gui && mg_gui->chanview)
{
if (changes->tab_resort)
{
chanview_set_sorted (mg_gui->chanview, prefs.hex_gui_tab_sort);
chanview_resort (mg_gui->chanview);
}
if (changes->tab_trunc)
chanview_set_trunc_len (mg_gui->chanview, prefs.hex_gui_tab_trunc);
if (changes->chanview || changes->tab_resort)
{
chanview_set_use_icons (mg_gui->chanview,
prefs.hex_gui_tab_icons && (pix_tree_channel || pix_tree_dialog ||
pix_tree_server || pix_tree_util));
/* rebuild the tab bar / tree widgets from the model */
chanview_set_impl (mg_gui->chanview, prefs.hex_gui_tab_layout);
}
}
list = sess_list;
while (list)
{
sess = list->data;
if (changes->tab_trunc)
mg_retrunc_tab (sess);
if (changes->ulist_sort)
userlist_apply_sort (sess);
if (changes->ulist_rows)
userlist_refill (sess);
if (sess->gui->is_tab)
{
/* only apply to the shared tab window once, using the
front-most session for session-dependent bits */
if (!done_main)
{
done_main = TRUE;
mg_apply_live_prefs_to_gui (sess->gui, current_tab, changes);
}
}
else
mg_apply_live_prefs_to_gui (sess->gui, sess, changes);
list = list->next;
}
}
void void
fe_set_typing (session *sess, const char *nick, const char *state) fe_set_typing (session *sess, const char *nick, const char *state)
{ {

View File

@@ -37,6 +37,24 @@ GtkWidget *mg_create_generic_tab (char *name, char *title, int force_toplevel, i
void mg_set_title (GtkWidget *button, char *title); void mg_set_title (GtkWidget *button, char *title);
void mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away); void mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away);
void mg_apply_setup (void); void mg_apply_setup (void);
/* which parts of the GUI need a live rebuild after a preferences change */
typedef struct
{
unsigned int chanview:1; /* rebuild the tab bar / channel tree */
unsigned int tab_resort:1; /* re-sort channel tabs */
unsigned int tab_trunc:1; /* re-truncate tab labels */
unsigned int ulist_columns:1; /* rebuild user list columns */
unsigned int ulist_rows:1; /* re-fill user list rows */
unsigned int ulist_sort:1; /* re-apply user list sort order */
unsigned int ulist_count:1; /* user count label visibility */
unsigned int meters:1; /* rebuild lag/throttle meters */
unsigned int input_box:1; /* nick box visibility and access icon */
unsigned int topic_bar:1; /* topic wrap mode and mode button placement */
unsigned int transparency:1; /* main window opacity */
} mg_live_prefs;
void mg_apply_live_prefs (const mg_live_prefs *changes);
void mg_apply_session_font_prefs (session_gui *gui); void mg_apply_session_font_prefs (session_gui *gui);
void mg_close_sess (session *); void mg_close_sess (session *);
void mg_tab_close (session *sess); void mg_tab_close (session *sess);

View File

@@ -1045,28 +1045,30 @@ setup_create_spin (GtkWidget *table, int row, const setting *set)
return wid; return wid;
} }
static guint setup_trans_apply_tag = 0;
/* the window opacity the dialog opened with, so cancelling can undo the
* live preview done by the slider */
static int setup_transparency_snapshot;
static gint static gint
setup_apply_trans (int *tag) setup_apply_trans (gpointer userdata)
{ {
prefs.hex_gui_transparency = setup_prefs.hex_gui_transparency; prefs.hex_gui_transparency = setup_prefs.hex_gui_transparency;
gtk_widget_set_opacity (current_sess->gui->window, if (current_sess)
(prefs.hex_gui_transparency / 255.)); gtk_widget_set_opacity (current_sess->gui->window,
(prefs.hex_gui_transparency / 255.));
*tag = 0; setup_trans_apply_tag = 0;
return 0; return 0;
} }
static void static void
setup_hscale_cb (GtkRange *wid, const setting *set) setup_hscale_cb (GtkRange *wid, const setting *set)
{ {
static int tag = 0;
setup_set_int (&setup_prefs, set, (int) gtk_range_get_value (wid)); setup_set_int (&setup_prefs, set, (int) gtk_range_get_value (wid));
if (tag == 0) if (setup_trans_apply_tag == 0)
{ setup_trans_apply_tag = g_idle_add ((GSourceFunc) setup_apply_trans, NULL);
tag = g_idle_add ((GSourceFunc) setup_apply_trans, &tag);
}
} }
static void static void
@@ -1091,7 +1093,8 @@ setup_create_hscale (GtkWidget *table, int row, const setting *set)
#ifndef WIN32 /* Windows always supports this */ #ifndef WIN32 /* Windows always supports this */
/* Only used for transparency currently */ /* Only used for transparency currently */
if (!gdk_screen_is_composited (gtk_widget_get_screen (current_sess->gui->window))) if (current_sess &&
!gdk_screen_is_composited (gtk_widget_get_screen (current_sess->gui->window)))
gtk_widget_set_sensitive (wid, FALSE); gtk_widget_set_sensitive (wid, FALSE);
#endif #endif
} }
@@ -2293,7 +2296,9 @@ setup_apply (struct zoitechatprefs *pr)
ThemeChangedEvent event; ThemeChangedEvent event;
struct zoitechatprefs old_prefs = prefs; struct zoitechatprefs old_prefs = prefs;
int old_dark_mode = prefs.hex_gui_dark_mode; int old_dark_mode = prefs.hex_gui_dark_mode;
mg_live_prefs live_changes;
memset (&live_changes, 0, sizeof (live_changes));
#define DIFF(a) (pr->a != prefs.a) #define DIFF(a) (pr->a != prefs.a)
@@ -2301,40 +2306,49 @@ setup_apply (struct zoitechatprefs *pr)
if (DIFF (hex_gui_lang)) if (DIFF (hex_gui_lang))
noapply = TRUE; noapply = TRUE;
#endif #endif
if (DIFF (hex_gui_compact)) /* server tabs are only created when a connection is opened, so
noapply = TRUE; existing connections keep their current tabs until a restart;
if (DIFF (hex_gui_input_icon)) the channel tree indentation is still re-applied below */
noapply = TRUE;
if (DIFF (hex_gui_input_nick))
noapply = TRUE;
if (DIFF (hex_gui_lagometer))
noapply = TRUE;
if (DIFF (hex_gui_mode_buttons_inline))
noapply = TRUE;
if (DIFF (hex_gui_tab_icons))
noapply = TRUE;
if (DIFF (hex_gui_tab_closebuttons))
noapply = TRUE;
if (DIFF (hex_gui_tab_server)) if (DIFF (hex_gui_tab_server))
{
noapply = TRUE; noapply = TRUE;
if (DIFF (hex_gui_tab_small)) live_changes.chanview = TRUE;
noapply = TRUE; }
if (DIFF (hex_gui_compact))
{
live_changes.chanview = TRUE;
live_changes.ulist_columns = TRUE;
}
if (DIFF (hex_gui_input_icon) || DIFF (hex_gui_input_nick))
live_changes.input_box = TRUE;
if (DIFF (hex_gui_lagometer) || DIFF (hex_gui_throttlemeter))
live_changes.meters = TRUE;
if (DIFF (hex_gui_mode_buttons_inline) || DIFF (hex_gui_topicbar_multiline))
live_changes.topic_bar = TRUE;
if (DIFF (hex_gui_tab_icons) || DIFF (hex_gui_tab_closebuttons) ||
DIFF (hex_gui_tab_small))
live_changes.chanview = TRUE;
if (DIFF (hex_gui_tab_sort)) if (DIFF (hex_gui_tab_sort))
noapply = TRUE; live_changes.tab_resort = TRUE;
if (DIFF (hex_gui_tab_trunc)) if (DIFF (hex_gui_tab_trunc))
noapply = TRUE; live_changes.tab_trunc = TRUE;
if (DIFF (hex_gui_throttlemeter))
noapply = TRUE;
if (DIFF (hex_gui_topicbar_multiline))
noapply = TRUE;
if (DIFF (hex_gui_ulist_count)) if (DIFF (hex_gui_ulist_count))
noapply = TRUE; live_changes.ulist_count = TRUE;
if (DIFF (hex_gui_ulist_icons)) if (DIFF (hex_gui_ulist_icons))
noapply = TRUE; live_changes.ulist_rows = TRUE;
if (DIFF (hex_gui_ulist_show_hosts)) if (DIFF (hex_gui_ulist_show_hosts))
noapply = TRUE; {
live_changes.ulist_columns = TRUE;
live_changes.ulist_rows = TRUE; /* hostnames may be stale */
}
if (DIFF (hex_gui_ulist_sort)) if (DIFF (hex_gui_ulist_sort))
noapply = TRUE; live_changes.ulist_sort = TRUE;
/* compare against the pre-dialog value: the slider previews the
opacity live on the current window only, so OK still needs to
apply it to the other windows */
if (pr->hex_gui_transparency != setup_transparency_snapshot)
live_changes.transparency = TRUE;
if ((pr->hex_gui_tab_pos == 5 || pr->hex_gui_tab_pos == 6) && if ((pr->hex_gui_tab_pos == 5 || pr->hex_gui_tab_pos == 6) &&
pr->hex_gui_tab_layout == 2 && pr->hex_gui_tab_pos != prefs.hex_gui_tab_pos) pr->hex_gui_tab_layout == 2 && pr->hex_gui_tab_pos != prefs.hex_gui_tab_pos)
@@ -2378,6 +2392,8 @@ setup_apply (struct zoitechatprefs *pr)
theme_manager_dispatch_setup_apply (&event); theme_manager_dispatch_setup_apply (&event);
mg_apply_live_prefs (&live_changes);
if (noapply) if (noapply)
fe_message (_("Some settings were changed that require a" fe_message (_("Some settings were changed that require a"
" restart to take full effect."), FE_MSG_WARN); " restart to take full effect."), FE_MSG_WARN);
@@ -2410,6 +2426,9 @@ setup_ok_cb (GtkWidget *but, GtkWidget *win)
if (save_result.success) if (save_result.success)
{ {
theme_preferences_stage_commit (); theme_preferences_stage_commit ();
/* the previewed values were accepted, so the close handler
must not revert them */
setup_transparency_snapshot = prefs.hex_gui_transparency;
gtk_widget_destroy (win); gtk_widget_destroy (win);
return; return;
} }
@@ -2472,6 +2491,24 @@ setup_close_cb (GtkWidget *win, GtkWidget **swin)
{ {
*swin = NULL; *swin = NULL;
/* drop any pending transparency preview and undo the previewed
opacity when the dialog is cancelled; after OK the snapshot
was updated so this is a no-op */
if (setup_trans_apply_tag)
{
g_source_remove (setup_trans_apply_tag);
setup_trans_apply_tag = 0;
}
if (prefs.hex_gui_transparency != setup_transparency_snapshot)
{
mg_live_prefs revert_changes;
prefs.hex_gui_transparency = setup_transparency_snapshot;
memset (&revert_changes, 0, sizeof (revert_changes));
revert_changes.transparency = TRUE;
mg_apply_live_prefs (&revert_changes);
}
theme_preferences_stage_discard (); theme_preferences_stage_discard ();
if (font_dialog) if (font_dialog)
@@ -2493,6 +2530,7 @@ setup_open (void)
memcpy (&setup_prefs, &prefs, sizeof (prefs)); memcpy (&setup_prefs, &prefs, sizeof (prefs));
color_change = FALSE; color_change = FALSE;
setup_transparency_snapshot = prefs.hex_gui_transparency;
theme_preferences_stage_begin (); theme_preferences_stage_begin ();
setup_window = setup_window_open (); setup_window = setup_window_open ();

View File

@@ -106,6 +106,11 @@ typedef struct
gboolean staged_dirty[THEME_TOKEN_COUNT]; gboolean staged_dirty[THEME_TOKEN_COUNT];
GdkRGBA snapshot[THEME_TOKEN_COUNT]; GdkRGBA snapshot[THEME_TOKEN_COUNT];
GdkRGBA staged[THEME_TOKEN_COUNT]; GdkRGBA staged[THEME_TOKEN_COUNT];
/* the GTK3 theme selection is previewed live from the combo, so
* remember what was active when the dialog opened in order to
* restore it when the dialog is cancelled */
char gtk3_theme_snapshot[sizeof ((struct zoitechatprefs *) 0)->hex_gui_gtk3_theme];
int gtk3_variant_snapshot;
} theme_preferences_stage_state; } theme_preferences_stage_state;
static theme_preferences_stage_state theme_preferences_stage; static theme_preferences_stage_state theme_preferences_stage;
@@ -204,6 +209,11 @@ theme_preferences_stage_begin (void)
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage)); memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
theme_preferences_stage.active = TRUE; theme_preferences_stage.active = TRUE;
g_strlcpy (theme_preferences_stage.gtk3_theme_snapshot,
prefs.hex_gui_gtk3_theme,
sizeof (theme_preferences_stage.gtk3_theme_snapshot));
theme_preferences_stage.gtk3_variant_snapshot = prefs.hex_gui_gtk3_variant;
for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++) for (token = THEME_TOKEN_MIRC_0; token < THEME_TOKEN_COUNT; token++)
{ {
GdkRGBA rgba; GdkRGBA rgba;
@@ -237,12 +247,36 @@ theme_preferences_stage_commit (void)
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage)); memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
} }
/* undo a live GTK3 theme preview: put the selection that was active when
* the dialog opened back and re-apply it */
static void
theme_preferences_stage_restore_gtk3 (void)
{
if (g_strcmp0 (prefs.hex_gui_gtk3_theme, theme_preferences_stage.gtk3_theme_snapshot) == 0 &&
prefs.hex_gui_gtk3_variant == theme_preferences_stage.gtk3_variant_snapshot)
return;
g_strlcpy (prefs.hex_gui_gtk3_theme,
theme_preferences_stage.gtk3_theme_snapshot,
sizeof (prefs.hex_gui_gtk3_theme));
prefs.hex_gui_gtk3_variant = theme_preferences_stage.gtk3_variant_snapshot;
theme_gtk3_apply_current (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);
}
void void
theme_preferences_stage_discard (void) theme_preferences_stage_discard (void)
{ {
if (!theme_preferences_stage.active) if (!theme_preferences_stage.active)
return; return;
/* restore the GTK3 theme first so the color snapshot below wins */
theme_preferences_stage_restore_gtk3 ();
theme_preferences_stage_sync_runtime_to_snapshot (); theme_preferences_stage_sync_runtime_to_snapshot ();
memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage)); memset (&theme_preferences_stage, 0, sizeof (theme_preferences_stage));
} }

View File

@@ -796,16 +796,12 @@ userlist_store_color (GtkListStore *store, GtkTreeIter *iter, ThemeSemanticToken
} }
} }
GtkListStore * static void
userlist_create_model (session *sess) userlist_model_set_sort (GtkListStore *store, session *sess)
{ {
GtkListStore *store;
GtkTreeIterCompareFunc cmp_func; GtkTreeIterCompareFunc cmp_func;
GtkSortType sort_type; GtkSortType sort_type;
store = gtk_list_store_new (6, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
switch (prefs.hex_gui_ulist_sort) switch (prefs.hex_gui_ulist_sort)
{ {
case 0: case 0:
@@ -825,18 +821,77 @@ userlist_create_model (session *sess)
sort_type = GTK_SORT_DESCENDING; sort_type = GTK_SORT_DESCENDING;
break; break;
default: default:
/* No sorting */ /* No sorting; rows keep their current order and new rows
go wherever they are inserted */
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), NULL, NULL, NULL); gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), NULL, NULL, NULL);
return store; return;
} }
gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), cmp_func, sess, NULL); gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE(store), cmp_func, sess, NULL);
gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store), gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(store),
GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, sort_type); GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, sort_type);
}
GtkListStore *
userlist_create_model (session *sess)
{
GtkListStore *store;
store = gtk_list_store_new (6, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING,
G_TYPE_STRING, G_TYPE_POINTER, THEME_GTK_COLOR_TYPE);
userlist_model_set_sort (store, sess);
return store; return store;
} }
void
userlist_apply_sort (session *sess)
{
if (!sess->res || !sess->res->user_model)
return;
userlist_model_set_sort (sess->res->user_model, sess);
}
/* remove and re-insert every row, so per-row content that depends on
* preferences (mode icons vs. text prefixes, hostnames) is rebuilt */
void
userlist_refill (session *sess)
{
GtkTreeModel *model;
GtkTreeIter iter;
GSList *users = NULL;
GSList *it;
struct User *user;
if (!sess->res || !sess->res->user_model)
return;
model = GTK_TREE_MODEL (sess->res->user_model);
if (gtk_tree_model_get_iter_first (model, &iter))
{
do
{
gtk_tree_model_get (model, &iter, COL_USER, &user, -1);
if (user)
users = g_slist_prepend (users, user);
}
while (gtk_tree_model_iter_next (model, &iter));
}
fe_userlist_clear (sess);
/* fe_userlist_insert() prepends, so inserting in reverse display
order restores the on-screen order for unsorted lists; sorted
models position the rows themselves */
for (it = users; it; it = it->next)
fe_userlist_insert (sess, it->data, FALSE);
g_slist_free (users);
}
static void static void
userlist_add_columns (GtkTreeView * treeview) userlist_add_columns (GtkTreeView * treeview)
{ {
@@ -906,6 +961,23 @@ userlist_add_columns (GtkTreeView * treeview)
} }
} }
void
userlist_rebuild_columns (GtkWidget *treeview)
{
GList *columns;
GList *it;
if (!treeview)
return;
columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (treeview));
for (it = columns; it; it = it->next)
gtk_tree_view_remove_column (GTK_TREE_VIEW (treeview), it->data);
g_list_free (columns);
userlist_add_columns (GTK_TREE_VIEW (treeview));
}
static gint static gint
userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata) userlist_click_cb (GtkWidget *widget, GdkEventButton *event, gpointer userdata)
{ {

View File

@@ -24,6 +24,9 @@ void userlist_set_value (GtkWidget *treeview, gfloat val);
gfloat userlist_get_value (GtkWidget *treeview); gfloat userlist_get_value (GtkWidget *treeview);
GtkWidget *userlist_create (GtkWidget *box); GtkWidget *userlist_create (GtkWidget *box);
GtkListStore *userlist_create_model (session *sess); GtkListStore *userlist_create_model (session *sess);
void userlist_apply_sort (session *sess);
void userlist_refill (session *sess);
void userlist_rebuild_columns (GtkWidget *treeview);
void userlist_show (session *sess); void userlist_show (session *sess);
void userlist_select (session *sess, char *name); void userlist_select (session *sess, char *name);
void fe_userlist_set_typing (session *sess, const char *nick, const char *state); void fe_userlist_set_typing (session *sess, const char *nick, const char *state);