diff options
Diffstat (limited to '')
-rw-r--r-- | src/profile-editor.c | 1444 |
1 files changed, 1444 insertions, 0 deletions
diff --git a/src/profile-editor.c b/src/profile-editor.c new file mode 100644 index 0000000..4098f90 --- /dev/null +++ b/src/profile-editor.c @@ -0,0 +1,1444 @@ +/* + * Copyright © 2002 Havoc Pennington + * Copyright © 2002 Mathias Hasselmann + * Copyright © 2008, 2011, 2017 Christian Persch + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <string.h> +#include <math.h> + +#include <glib.h> +#include <glib/gi18n.h> +#include <gio/gio.h> + +#include "terminal-app.h" +#include "terminal-enums.h" +#include "profile-editor.h" +#include "terminal-prefs.h" +#include "terminal-schemas.h" +#include "terminal-type-builtins.h" +#include "terminal-util.h" +#include "terminal-profiles-list.h" +#include "terminal-libgsystem.h" + + +/* Wrapper around g_signal_connect that maintains a list of the + * handlers installed, and can disconnect them all. */ +typedef struct { + gpointer instance; + gulong handler_id; +} ProfilePrefsSignal; + +static void +profile_prefs_register_signal_handler (gpointer instance, + gulong handler_id) +{ + ProfilePrefsSignal sig; + sig.instance = instance; + sig.handler_id = handler_id; + g_array_append_val (the_pref_data->profile_signals, sig); +} + +static gulong +profile_prefs_signal_connect (gpointer instance, + const gchar *detailed_signal, + GCallback c_handler, + gpointer data) +{ + gulong handler_id = g_signal_connect(instance, detailed_signal, c_handler, data); + profile_prefs_register_signal_handler (instance, handler_id); + return handler_id; +} + +static void +profile_prefs_signal_handlers_disconnect_all (void) +{ + for (guint i = 0; i < the_pref_data->profile_signals->len; i++) { + ProfilePrefsSignal *sig = &g_array_index (the_pref_data->profile_signals, ProfilePrefsSignal, i); + g_signal_handler_disconnect (sig->instance, sig->handler_id); + } + g_array_set_size (the_pref_data->profile_signals, 0); +} + + +/* Wrappers around g_settings_bind and friends that maintain a list of the + * bindings installed, and can unbind them all. */ +typedef struct { + gpointer object; + char *property; +} ProfilePrefsBinding; + +static void +profile_prefs_register_settings_binding (gpointer object, + const char *property) +{ + ProfilePrefsBinding bind; + bind.object = object; + bind.property = g_strdup (property); + g_array_append_val (the_pref_data->profile_bindings, bind); +} + +static void +profile_prefs_settings_bind (GSettings *settings, + const gchar *key, + gpointer object, + const gchar *property, + GSettingsBindFlags flags) +{ + profile_prefs_register_settings_binding (object, property); + g_settings_bind (settings, key, object, property, flags); +} + +static void +profile_prefs_settings_bind_with_mapping (GSettings *settings, + const gchar *key, + gpointer object, + const gchar *property, + GSettingsBindFlags flags, + GSettingsBindGetMapping get_mapping, + GSettingsBindSetMapping set_mapping, + gpointer user_data, + GDestroyNotify destroy) +{ + profile_prefs_register_settings_binding (object, property); + g_settings_bind_with_mapping (settings, key, object, property, flags, get_mapping, set_mapping, user_data, destroy); +} + +static void +profile_prefs_settings_bind_writable (GSettings *settings, + const gchar *key, + gpointer object, + const gchar *property, + gboolean inverted) +{ + profile_prefs_register_settings_binding (object, property); + g_settings_bind_writable (settings, key, object, property, inverted); +} + +static void +profile_prefs_settings_unbind_all (void) +{ + for (guint i = 0; i < the_pref_data->profile_bindings->len; i++) { + ProfilePrefsBinding *bind = &g_array_index (the_pref_data->profile_bindings, ProfilePrefsBinding, i); + g_settings_unbind (bind->object, bind->property); + g_free (bind->property); + } + g_array_set_size (the_pref_data->profile_bindings, 0); +} + + +typedef struct _TerminalColorScheme TerminalColorScheme; + +struct _TerminalColorScheme +{ + const char *name; + const GdkRGBA foreground; + const GdkRGBA background; +}; + +#define COLOR(r, g, b) { .red = (r) / 255.0, .green = (g) / 255.0, .blue = (b) / 255.0, .alpha = 1.0 } + +static const TerminalColorScheme color_schemes[] = { + { N_("Black on light yellow"), + COLOR (0x00, 0x00, 0x00), + COLOR (0xff, 0xff, 0xdd) + }, + { N_("Black on white"), + COLOR (0x00, 0x00, 0x00), + COLOR (0xff, 0xff, 0xff) + }, + { N_("Gray on black"), + COLOR (0xaa, 0xaa, 0xaa), + COLOR (0x00, 0x00, 0x00) + }, + { N_("Green on black"), + COLOR (0x00, 0xff, 0x00), + COLOR (0x00, 0x00, 0x00) + }, + { N_("White on black"), + COLOR (0xff, 0xff, 0xff), + COLOR (0x00, 0x00, 0x00) + }, + /* Translators: "GNOME" is the name of a colour scheme, "light" can be translated */ + { N_("GNOME light"), + COLOR (0x17, 0x14, 0x21), /* Palette entry 0 */ + COLOR (0xff, 0xff, 0xff) /* Palette entry 15 */ + }, + /* Translators: "GNOME" is the name of a colour scheme, "dark" can be translated */ + { N_("GNOME dark"), + COLOR (0xd0, 0xcf, 0xcc), /* Palette entry 7 */ + COLOR (0x17, 0x14, 0x21) /* Palette entry 0 */ + }, + /* Translators: "Tango" is the name of a colour scheme, "light" can be translated */ + { N_("Tango light"), + COLOR (0x2e, 0x34, 0x36), + COLOR (0xee, 0xee, 0xec) + }, + /* Translators: "Tango" is the name of a colour scheme, "dark" can be translated */ + { N_("Tango dark"), + COLOR (0xd3, 0xd7, 0xcf), + COLOR (0x2e, 0x34, 0x36) + }, + /* Translators: "Solarized" is the name of a colour scheme, "light" can be translated */ + { N_("Solarized light"), + COLOR (0x65, 0x7b, 0x83), /* 11: base00 */ + COLOR (0xfd, 0xf6, 0xe3) /* 15: base3 */ + }, + /* Translators: "Solarized" is the name of a colour scheme, "dark" can be translated */ + { N_("Solarized dark"), + COLOR (0x83, 0x94, 0x96), /* 12: base0 */ + COLOR (0x00, 0x2b, 0x36) /* 8: base03 */ + }, +}; + +#define TERMINAL_PALETTE_SIZE (16) + +enum +{ + TERMINAL_PALETTE_GNOME = 0, + TERMINAL_PALETTE_TANGO = 1, + TERMINAL_PALETTE_LINUX = 2, + TERMINAL_PALETTE_XTERM = 3, + TERMINAL_PALETTE_RXVT = 4, + TERMINAL_PALETTE_SOLARIZED = 5, + TERMINAL_PALETTE_N_BUILTINS +}; + +static const GdkRGBA terminal_palettes[TERMINAL_PALETTE_N_BUILTINS][TERMINAL_PALETTE_SIZE] = +{ + /* Based on GNOME 3.32 palette: https://developer.gnome.org/hig/stable/icon-design.html.en#palette */ + { + COLOR (0x17, 0x14, 0x21), /* Blend of Dark 4 and Black */ + COLOR (0xc0, 0x1c, 0x28), /* Red 4 */ + COLOR (0x26, 0xa2, 0x69), /* Green 5 */ + COLOR (0xa2, 0x73, 0x4c), /* Blend of Brown 2 and Brown 3 */ + COLOR (0x12, 0x48, 0x8b), /* Blend of Blue 5 and Dark 4 */ + COLOR (0xa3, 0x47, 0xba), /* Purple 3 */ + COLOR (0x2a, 0xa1, 0xb3), /* Linear addition Blue 5 + Green 5, darkened slightly */ + COLOR (0xd0, 0xcf, 0xcc), /* Blend of Light 3 and Light 4 */ + COLOR (0x5e, 0x5c, 0x64), /* Dark 2 */ + COLOR (0xf6, 0x61, 0x51), /* Red 1 */ + COLOR (0x33, 0xd1, 0x7a), /* Green 3 */ + COLOR (0xe9, 0xad, 0x0c), /* Blend of Yellow 4 and Yellow 5 */ + COLOR (0x2a, 0x7b, 0xde), /* Blend of Blue 3 and Blue 4 */ + COLOR (0xc0, 0x61, 0xcb), /* Purple 2 */ + COLOR (0x33, 0xc7, 0xde), /* Linear addition Blue 4 + Green 4, darkened slightly */ + COLOR (0xff, 0xff, 0xff) /* Light 1 */ + }, + + /* Tango palette */ + { + COLOR (0x2e, 0x34, 0x36), + COLOR (0xcc, 0x00, 0x00), + COLOR (0x4e, 0x9a, 0x06), + COLOR (0xc4, 0xa0, 0x00), + COLOR (0x34, 0x65, 0xa4), + COLOR (0x75, 0x50, 0x7b), + COLOR (0x06, 0x98, 0x9a), + COLOR (0xd3, 0xd7, 0xcf), + COLOR (0x55, 0x57, 0x53), + COLOR (0xef, 0x29, 0x29), + COLOR (0x8a, 0xe2, 0x34), + COLOR (0xfc, 0xe9, 0x4f), + COLOR (0x72, 0x9f, 0xcf), + COLOR (0xad, 0x7f, 0xa8), + COLOR (0x34, 0xe2, 0xe2), + COLOR (0xee, 0xee, 0xec) + }, + + /* Linux palette */ + { + COLOR (0x00, 0x00, 0x00), + COLOR (0xaa, 0x00, 0x00), + COLOR (0x00, 0xaa, 0x00), + COLOR (0xaa, 0x55, 0x00), + COLOR (0x00, 0x00, 0xaa), + COLOR (0xaa, 0x00, 0xaa), + COLOR (0x00, 0xaa, 0xaa), + COLOR (0xaa, 0xaa, 0xaa), + COLOR (0x55, 0x55, 0x55), + COLOR (0xff, 0x55, 0x55), + COLOR (0x55, 0xff, 0x55), + COLOR (0xff, 0xff, 0x55), + COLOR (0x55, 0x55, 0xff), + COLOR (0xff, 0x55, 0xff), + COLOR (0x55, 0xff, 0xff), + COLOR (0xff, 0xff, 0xff) + }, + + /* XTerm palette */ + { + COLOR (0x00, 0x00, 0x00), + COLOR (0xcd, 0x00, 0x00), + COLOR (0x00, 0xcd, 0x00), + COLOR (0xcd, 0xcd, 0x00), + COLOR (0x00, 0x00, 0xee), + COLOR (0xcd, 0x00, 0xcd), + COLOR (0x00, 0xcd, 0xcd), + COLOR (0xe5, 0xe5, 0xe5), + COLOR (0x7f, 0x7f, 0x7f), + COLOR (0xff, 0x00, 0x00), + COLOR (0x00, 0xff, 0x00), + COLOR (0xff, 0xff, 0x00), + COLOR (0x5c, 0x5c, 0xff), + COLOR (0xff, 0x00, 0xff), + COLOR (0x00, 0xff, 0xff), + COLOR (0xff, 0xff, 0xff) + }, + + /* RXVT palette */ + { + COLOR (0x00, 0x00, 0x00), + COLOR (0xcd, 0x00, 0x00), + COLOR (0x00, 0xcd, 0x00), + COLOR (0xcd, 0xcd, 0x00), + COLOR (0x00, 0x00, 0xcd), + COLOR (0xcd, 0x00, 0xcd), + COLOR (0x00, 0xcd, 0xcd), + COLOR (0xfa, 0xeb, 0xd7), + COLOR (0x40, 0x40, 0x40), + COLOR (0xff, 0x00, 0x00), + COLOR (0x00, 0xff, 0x00), + COLOR (0xff, 0xff, 0x00), + COLOR (0x00, 0x00, 0xff), + COLOR (0xff, 0x00, 0xff), + COLOR (0x00, 0xff, 0xff), + COLOR (0xff, 0xff, 0xff) + }, + + /* Solarized palette (1.0.0beta2): http://ethanschoonover.com/solarized */ + { + COLOR (0x07, 0x36, 0x42), /* 0: base02 */ + COLOR (0xdc, 0x32, 0x2f), /* 1: red */ + COLOR (0x85, 0x99, 0x00), /* 2: green */ + COLOR (0xb5, 0x89, 0x00), /* 3: yellow */ + COLOR (0x26, 0x8b, 0xd2), /* 4: blue */ + COLOR (0xd3, 0x36, 0x82), /* 5: magenta */ + COLOR (0x2a, 0xa1, 0x98), /* 6: cyan */ + COLOR (0xee, 0xe8, 0xd5), /* 7: base2 */ + COLOR (0x00, 0x2b, 0x36), /* 8: base03 */ + COLOR (0xcb, 0x4b, 0x16), /* 9: orange */ + COLOR (0x58, 0x6e, 0x75), /* 10: base01 */ + COLOR (0x65, 0x7b, 0x83), /* 11: base00 */ + COLOR (0x83, 0x94, 0x96), /* 12: base0 */ + COLOR (0x6c, 0x71, 0xc4), /* 13: violet */ + COLOR (0x93, 0xa1, 0xa1), /* 14: base1 */ + COLOR (0xfd, 0xf6, 0xe3) /* 15: base3 */ + }, +}; + +#undef COLOR + +static void profile_colors_notify_scheme_combo_cb (GSettings *profile, + const char *key, + GtkComboBox *combo); + +static void profile_palette_notify_scheme_combo_cb (GSettings *profile, + const char *key, + GtkComboBox *combo); + +static void profile_palette_notify_colorpickers_cb (GSettings *profile, + const char *key, + gpointer user_data); + +static void profile_notify_encoding_combo_cb (GSettings *profile, + const char *key, + GtkComboBox *combo); + +enum { + ENCODINGS_COL_ID, + ENCODINGS_COL_TEXT +}; + +/* gdk_rgba_equal is too strict! */ +static gboolean +rgba_equal (const GdkRGBA *a, + const GdkRGBA *b) +{ + gdouble dr, dg, db; + + dr = a->red - b->red; + dg = a->green - b->green; + db = a->blue - b->blue; + + return (dr * dr + dg * dg + db * db) < 1e-4; +} + +static gboolean +palette_cmp (const GdkRGBA *ca, + const GdkRGBA *cb) +{ + guint i; + + for (i = 0; i < TERMINAL_PALETTE_SIZE; ++i) + if (!rgba_equal (&ca[i], &cb[i])) + return FALSE; + + return TRUE; +} + +static gboolean +palette_is_builtin (const GdkRGBA *colors, + gsize n_colors, + guint *n) +{ + guint i; + + if (n_colors != TERMINAL_PALETTE_SIZE) + return FALSE; + + for (i = 0; i < TERMINAL_PALETTE_N_BUILTINS; ++i) + { + if (palette_cmp (colors, terminal_palettes[i])) + { + *n = i; + return TRUE; + } + } + + return FALSE; +} + +static void +modify_palette_entry (GSettings *profile, + guint i, + const GdkRGBA *color) +{ + gs_free GdkRGBA *colors; + gsize n_colors; + + /* FIXMEchpe: this can be optimised, don't really need to parse the colours! */ + + colors = terminal_g_settings_get_rgba_palette (profile, TERMINAL_PROFILE_PALETTE_KEY, &n_colors); + + if (i < n_colors) + { + colors[i] = *color; + terminal_g_settings_set_rgba_palette (profile, TERMINAL_PROFILE_PALETTE_KEY, + colors, n_colors); + } +} + +static void +color_scheme_combo_changed_cb (GtkWidget *combo, + GParamSpec *pspec, + GSettings *profile) +{ + guint i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); + + if (i < G_N_ELEMENTS (color_schemes)) + { + g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); + terminal_g_settings_set_rgba (profile, TERMINAL_PROFILE_FOREGROUND_COLOR_KEY, &color_schemes[i].foreground); + terminal_g_settings_set_rgba (profile, TERMINAL_PROFILE_BACKGROUND_COLOR_KEY, &color_schemes[i].background); + g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); + } + else + { + /* "custom" selected, no change */ + } +} + +static void +profile_colors_notify_scheme_combo_cb (GSettings *profile, + const char *key, + GtkComboBox *combo) +{ + GdkRGBA fg, bg; + guint i; + + terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_FOREGROUND_COLOR_KEY, &fg); + terminal_g_settings_get_rgba (profile, TERMINAL_PROFILE_BACKGROUND_COLOR_KEY, &bg); + + for (i = 0; i < G_N_ELEMENTS (color_schemes); ++i) + { + if (rgba_equal (&fg, &color_schemes[i].foreground) && + rgba_equal (&bg, &color_schemes[i].background)) + break; + } + /* If we didn't find a match, then we get the last combo box item which is "custom" */ + + g_signal_handlers_block_by_func (combo, G_CALLBACK (color_scheme_combo_changed_cb), profile); + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), i); + g_signal_handlers_unblock_by_func (combo, G_CALLBACK (color_scheme_combo_changed_cb), profile); +} + +static void +palette_scheme_combo_changed_cb (GtkComboBox *combo, + GParamSpec *pspec, + GSettings *profile) +{ + int i; + + i = gtk_combo_box_get_active (GTK_COMBO_BOX (combo)); + + g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); + if (i < TERMINAL_PALETTE_N_BUILTINS) + terminal_g_settings_set_rgba_palette (profile, TERMINAL_PROFILE_PALETTE_KEY, + terminal_palettes[i], TERMINAL_PALETTE_SIZE); + else + { + /* "custom" selected, no change */ + } + g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_colors_notify_scheme_combo_cb), combo); +} + +static void +profile_palette_notify_scheme_combo_cb (GSettings *profile, + const char *key, + GtkComboBox *combo) +{ + gs_free GdkRGBA *colors; + gsize n_colors; + guint i; + + colors = terminal_g_settings_get_rgba_palette (profile, TERMINAL_PROFILE_PALETTE_KEY, &n_colors); + if (!palette_is_builtin (colors, n_colors, &i)) + /* If we didn't find a match, then we want the last combo + * box item which is "custom" + */ + i = TERMINAL_PALETTE_N_BUILTINS; + + g_signal_handlers_block_by_func (combo, G_CALLBACK (palette_scheme_combo_changed_cb), profile); + gtk_combo_box_set_active (combo, i); + g_signal_handlers_unblock_by_func (combo, G_CALLBACK (palette_scheme_combo_changed_cb), profile); +} + +static void +palette_color_notify_cb (GtkColorButton *button, + GParamSpec *pspec, + GSettings *profile) +{ + GdkRGBA color; + guint i; + + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (button), &color); + i = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (button), "palette-entry-index")); + + g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_palette_notify_colorpickers_cb), NULL); + modify_palette_entry (profile, i, &color); + g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_palette_notify_colorpickers_cb), NULL); +} + +static void +profile_palette_notify_colorpickers_cb (GSettings *profile, + const char *key, + gpointer user_data) +{ + GtkWidget *w; + GtkBuilder *builder = the_pref_data->builder; + gs_free GdkRGBA *colors; + gsize n_colors, i; + + g_assert (strcmp (key, TERMINAL_PROFILE_PALETTE_KEY) == 0); + + colors = terminal_g_settings_get_rgba_palette (profile, TERMINAL_PROFILE_PALETTE_KEY, &n_colors); + + n_colors = MIN (n_colors, TERMINAL_PALETTE_SIZE); + for (i = 0; i < n_colors; i++) + { + char name[32]; + + g_snprintf (name, sizeof (name), "palette-colorpicker-%" G_GSIZE_FORMAT, i); + w = (GtkWidget *) gtk_builder_get_object (builder, name); + + g_signal_handlers_block_by_func (w, G_CALLBACK (palette_color_notify_cb), profile); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (w), &colors[i]); + g_signal_handlers_unblock_by_func (w, G_CALLBACK (palette_color_notify_cb), profile); + } +} + +static void +custom_command_entry_changed_cb (GtkEntry *entry) +{ + const char *command; + gs_free_error GError *error = NULL; + + command = gtk_entry_get_text (entry); + + if (command[0] == '\0' || + g_shell_parse_argv (command, NULL, NULL, &error)) + { + gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, NULL); + } + else + { + gs_free char *tooltip; + + gtk_entry_set_icon_from_icon_name (entry, GTK_ENTRY_ICON_SECONDARY, "dialog-warning"); + + tooltip = g_strdup_printf (_("Error parsing command: %s"), error->message); + gtk_entry_set_icon_tooltip_text (entry, GTK_ENTRY_ICON_SECONDARY, tooltip); + } +} + +static void +default_size_reset_cb (GtkWidget *button, + GSettings *profile) +{ + g_settings_reset (profile, TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS_KEY); + g_settings_reset (profile, TERMINAL_PROFILE_DEFAULT_SIZE_ROWS_KEY); +} + +static void +cell_scale_reset_cb (GtkWidget *button, + GSettings *profile) +{ + g_settings_reset (profile, TERMINAL_PROFILE_CELL_HEIGHT_SCALE_KEY); + g_settings_reset (profile, TERMINAL_PROFILE_CELL_WIDTH_SCALE_KEY); +} + +static void +reset_compat_defaults_cb (GtkWidget *button, + GSettings *profile) +{ + g_settings_reset (profile, TERMINAL_PROFILE_DELETE_BINDING_KEY); + g_settings_reset (profile, TERMINAL_PROFILE_BACKSPACE_BINDING_KEY); + g_settings_reset (profile, TERMINAL_PROFILE_ENCODING_KEY); + g_settings_reset (profile, TERMINAL_PROFILE_CJK_UTF8_AMBIGUOUS_WIDTH_KEY); + g_settings_reset (profile, TERMINAL_PROFILE_ENABLE_SIXEL_KEY); +} + +static gboolean +tree_model_id_to_iter_recurse (GtkTreeModel *model, + int id_column, + const char *active_id, + GtkTreeIter *iter, + GtkTreeIter *result_iter) +{ + do { + /* Descend the tree */ + GtkTreeIter child_iter; + if (gtk_tree_model_iter_children(model, &child_iter, iter) && + tree_model_id_to_iter_recurse (model, id_column, active_id, &child_iter, result_iter)) + return TRUE; + + gs_free char *id = NULL; + gtk_tree_model_get (model, iter, id_column, &id, -1); + if (g_strcmp0 (id, active_id) == 0) { + *result_iter = *iter; + return TRUE; + } + } while (gtk_tree_model_iter_next (model, iter)); + + return FALSE; +} + +static gboolean +tree_model_id_to_iter (GtkTreeModel *model, + int id_column, + const char *active_id, + GtkTreeIter *iter) +{ + GtkTreeIter first_iter; + + return gtk_tree_model_get_iter_first(model, &first_iter) && + tree_model_id_to_iter_recurse(model, id_column, active_id, &first_iter, iter); +} + +static void +profile_encoding_combo_changed_cb (GtkComboBox *combo, + GSettings *profile) +{ + GtkTreeIter iter; + + if (!gtk_combo_box_get_active_iter(combo, &iter)) + return; + + gs_free char *encoding = NULL; + gtk_tree_model_get(gtk_combo_box_get_model(combo), + &iter, + ENCODINGS_COL_ID, &encoding, + -1); + if (encoding == NULL) + return; + + g_signal_handlers_block_by_func (profile, G_CALLBACK (profile_notify_encoding_combo_cb), combo); + g_settings_set_string(profile, TERMINAL_PROFILE_ENCODING_KEY, encoding); + g_signal_handlers_unblock_by_func (profile, G_CALLBACK (profile_notify_encoding_combo_cb), combo); +} + +static void +profile_notify_encoding_combo_cb (GSettings *profile, + const char *key, + GtkComboBox *combo) +{ + gs_free char *encoding = NULL; + g_settings_get(profile, key, "s", &encoding); + + g_signal_handlers_block_by_func (combo, G_CALLBACK (profile_encoding_combo_changed_cb), profile); + + GtkTreeIter iter; + if (tree_model_id_to_iter(gtk_combo_box_get_model(combo), + ENCODINGS_COL_ID, + encoding, + &iter)) { + gtk_combo_box_set_active_iter(combo, &iter); + } else { + gtk_combo_box_set_active(combo, -1); + } + + g_signal_handlers_unblock_by_func (combo, G_CALLBACK (profile_encoding_combo_changed_cb), profile); +} + +/* + * initialize widgets + */ + +static void +init_color_scheme_menu (GtkWidget *widget) +{ + GtkCellRenderer *renderer; + GtkTreeIter iter; + gs_unref_object GtkListStore *store; + guint i; + + store = gtk_list_store_new (1, G_TYPE_STRING); + for (i = 0; i < G_N_ELEMENTS (color_schemes); ++i) + gtk_list_store_insert_with_values (store, &iter, -1, + 0, _(color_schemes[i].name), + -1); + gtk_list_store_insert_with_values (store, &iter, -1, + 0, _("Custom"), + -1); + + gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store)); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (widget), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (widget), renderer, "text", 0, NULL); +} + +typedef enum { + GROUP_UTF8, + GROUP_CJKV, + GROUP_OBSOLETE, + LAST_GROUP +} EncodingGroup; + +typedef struct { + const char *charset; + const char *name; + EncodingGroup group; +} EncodingEntry; + +/* These MUST be sorted by charset so that bsearch can work! */ +static const EncodingEntry encodings[] = { + { "ARMSCII-8", N_("Armenian"), GROUP_OBSOLETE }, + { "BIG5", N_("Chinese Traditional"), GROUP_CJKV }, + { "BIG5-HKSCS", N_("Chinese Traditional"), GROUP_CJKV }, + { "CP866", N_("Cyrillic/Russian"), GROUP_OBSOLETE }, + { "EUC-JP", N_("Japanese"), GROUP_CJKV }, + { "EUC-KR", N_("Korean"), GROUP_CJKV }, + { "EUC-TW", N_("Chinese Traditional"), GROUP_CJKV }, + { "GB18030", N_("Chinese Simplified"), GROUP_CJKV }, + { "GB2312", N_("Chinese Simplified"), GROUP_CJKV }, + { "GBK", N_("Chinese Simplified"), GROUP_CJKV }, + { "GEORGIAN-PS", N_("Georgian"), GROUP_OBSOLETE }, + { "IBM850", N_("Western"), GROUP_OBSOLETE }, + { "IBM852", N_("Central European"), GROUP_OBSOLETE }, + { "IBM855", N_("Cyrillic"), GROUP_OBSOLETE }, + { "IBM857", N_("Turkish"), GROUP_OBSOLETE }, + { "IBM862", N_("Hebrew"), GROUP_OBSOLETE }, + { "IBM864", N_("Arabic"), GROUP_OBSOLETE }, + { "ISO-2022-JP", N_("Japanese"), GROUP_CJKV }, + { "ISO-2022-KR", N_("Korean"), GROUP_CJKV }, + { "ISO-8859-1", N_("Western"), GROUP_OBSOLETE }, + { "ISO-8859-10", N_("Nordic"), GROUP_OBSOLETE }, + { "ISO-8859-13", N_("Baltic"), GROUP_OBSOLETE }, + { "ISO-8859-14", N_("Celtic"), GROUP_OBSOLETE }, + { "ISO-8859-15", N_("Western"), GROUP_OBSOLETE }, + { "ISO-8859-16", N_("Romanian"), GROUP_OBSOLETE }, + { "ISO-8859-2", N_("Central European"), GROUP_OBSOLETE }, + { "ISO-8859-3", N_("South European"), GROUP_OBSOLETE }, + { "ISO-8859-4", N_("Baltic"), GROUP_OBSOLETE }, + { "ISO-8859-5", N_("Cyrillic"), GROUP_OBSOLETE }, + { "ISO-8859-6", N_("Arabic"), GROUP_OBSOLETE }, + { "ISO-8859-7", N_("Greek"), GROUP_OBSOLETE }, + { "ISO-8859-8", N_("Hebrew Visual"), GROUP_OBSOLETE }, + { "ISO-8859-8-I", N_("Hebrew"), GROUP_OBSOLETE }, + { "ISO-8859-9", N_("Turkish"), GROUP_OBSOLETE }, + { "ISO-IR-111", N_("Cyrillic"), GROUP_OBSOLETE }, + { "KOI8-R", N_("Cyrillic"), GROUP_OBSOLETE }, + { "KOI8-U", N_("Cyrillic/Ukrainian"), GROUP_OBSOLETE }, + { "MAC-CYRILLIC", N_("Cyrillic"), GROUP_OBSOLETE }, + { "MAC_ARABIC", N_("Arabic"), GROUP_OBSOLETE }, + { "MAC_CE", N_("Central European"), GROUP_OBSOLETE }, + { "MAC_CROATIAN", N_("Croatian"), GROUP_OBSOLETE }, + { "MAC_GREEK", N_("Greek"), GROUP_OBSOLETE }, + { "MAC_HEBREW", N_("Hebrew"), GROUP_OBSOLETE }, + { "MAC_ROMAN", N_("Western"), GROUP_OBSOLETE }, + { "MAC_ROMANIAN", N_("Romanian"), GROUP_OBSOLETE }, + { "MAC_TURKISH", N_("Turkish"), GROUP_OBSOLETE }, + { "MAC_UKRAINIAN", N_("Cyrillic/Ukrainian"), GROUP_OBSOLETE }, + { "SHIFT_JIS", N_("Japanese"), GROUP_CJKV }, + { "TIS-620", N_("Thai"), GROUP_OBSOLETE }, + { "UHC", N_("Korean"), GROUP_CJKV }, + { "UTF-8", N_("Unicode"), GROUP_UTF8 }, + { "WINDOWS-1250", N_("Central European"), GROUP_OBSOLETE }, + { "WINDOWS-1251", N_("Cyrillic"), GROUP_OBSOLETE }, + { "WINDOWS-1252", N_("Western"), GROUP_OBSOLETE }, + { "WINDOWS-1253", N_("Greek"), GROUP_OBSOLETE }, + { "WINDOWS-1254", N_("Turkish"), GROUP_OBSOLETE }, + { "WINDOWS-1255", N_("Hebrew"), GROUP_OBSOLETE}, + { "WINDOWS-1256", N_("Arabic"), GROUP_OBSOLETE }, + { "WINDOWS-1257", N_("Baltic"), GROUP_OBSOLETE }, + { "WINDOWS-1258", N_("Vietnamese"), GROUP_OBSOLETE }, +}; + +static const struct { + EncodingGroup group; + const char *name; +} encodings_group_names[] = { + { GROUP_UTF8, N_("Unicode") }, + { GROUP_CJKV, N_("Legacy CJK Encodings") }, + { GROUP_OBSOLETE, N_("Obsolete Encodings") }, +}; + +#define EM_DASH "—" + +static void +append_encodings_for_group (GtkTreeStore *store, + EncodingGroup group, + gboolean submenu) +{ + GtkTreeIter parent_iter; + + if (submenu) { + gtk_tree_store_insert_with_values (store, + &parent_iter, + NULL, + -1, + ENCODINGS_COL_ID, NULL, + ENCODINGS_COL_TEXT, _(encodings_group_names[group].name), + -1); + } + + for (guint i = 0; i < G_N_ELEMENTS (encodings); i++) { + if (encodings[i].group != group) + continue; + + gs_free char *name = g_strdup_printf ("%s " EM_DASH " %s", + _(encodings[i].name), encodings[i].charset); + + GtkTreeIter iter; + gtk_tree_store_insert_with_values (store, + &iter, + submenu ? &parent_iter : NULL, + -1, + ENCODINGS_COL_ID, encodings[i].charset, + ENCODINGS_COL_TEXT, name, + -1); + } +} + +static GtkTreeStore * +encodings_tree_store_new (void) +{ + GtkTreeStore *store = gtk_tree_store_new (2, G_TYPE_STRING, G_TYPE_STRING); + + append_encodings_for_group(store, GROUP_UTF8, FALSE); /* UTF-8 in main menu */ + append_encodings_for_group(store, GROUP_CJKV, TRUE); + append_encodings_for_group(store, GROUP_OBSOLETE, TRUE); + + return store; +} + +static void +init_encodings_combo (GtkWidget *widget) +{ + gs_unref_object GtkTreeStore *store = encodings_tree_store_new (); + gtk_combo_box_set_model (GTK_COMBO_BOX (widget), GTK_TREE_MODEL (store)); +} + +static gboolean +s_to_rgba (GValue *value, + GVariant *variant, + gpointer user_data) +{ + const char *s; + GdkRGBA color; + + g_variant_get (variant, "&s", &s); + if (!gdk_rgba_parse (&color, s)) + return FALSE; + + color.alpha = 1.0; + g_value_set_boxed (value, &color); + return TRUE; +} + +static GVariant * +rgba_to_s (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) +{ + GdkRGBA *color; + gs_free char *s = NULL; + + color = g_value_get_boxed (value); + if (color == NULL) + return NULL; + + s = gdk_rgba_to_string (color); + return g_variant_new_string (s); +} + +static gboolean +string_to_enum (GValue *value, + GVariant *variant, + gpointer user_data) +{ + GType (* get_type) (void) = user_data; + GEnumClass *klass; + GEnumValue *eval = NULL; + const char *s; + guint i; + + g_variant_get (variant, "&s", &s); + + klass = g_type_class_ref (get_type ()); + for (i = 0; i < klass->n_values; ++i) { + if (strcmp (klass->values[i].value_nick, s) != 0) + continue; + + eval = &klass->values[i]; + break; + } + + if (eval) + g_value_set_int (value, eval->value); + + g_type_class_unref (klass); + + return eval != NULL; +} + +static GVariant * +enum_to_string (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) +{ + GType (* get_type) (void) = user_data; + GEnumClass *klass; + GEnumValue *eval = NULL; + int val; + guint i; + GVariant *variant = NULL; + + val = g_value_get_int (value); + + klass = g_type_class_ref (get_type ()); + for (i = 0; i < klass->n_values; ++i) { + if (klass->values[i].value != val) + continue; + + eval = &klass->values[i]; + break; + } + + if (eval) + variant = g_variant_new_string (eval->value_nick); + + g_type_class_unref (klass); + + return variant; +} + +static gboolean +scrollbar_policy_to_bool (GValue *value, + GVariant *variant, + gpointer user_data) +{ + const char *str; + + g_variant_get (variant, "&s", &str); + g_value_set_boolean (value, g_str_equal (str, "always")); + + return TRUE; +} + +static GVariant * +bool_to_scrollbar_policy (const GValue *value, + const GVariantType *expected_type, + gpointer user_data) +{ + return g_variant_new_string (g_value_get_boolean (value) ? "always" : "never"); +} + +static gboolean +monospace_filter (const PangoFontFamily *family, + const PangoFontFace *face, + gpointer data) +{ + return pango_font_family_is_monospace ((PangoFontFamily *) family); +} + +/* Called once per Preferences window, to initialize stuff that doesn't depend on the profile being edited */ +void +profile_prefs_init (void) +{ + GtkWidget *w; + GtkBuilder *builder = the_pref_data->builder; + char *text; + + the_pref_data->profile_signals = g_array_new (FALSE, FALSE, sizeof (ProfilePrefsSignal)); + the_pref_data->profile_bindings = g_array_new (FALSE, FALSE, sizeof (ProfilePrefsBinding)); + + w = (GtkWidget *) gtk_builder_get_object (builder, "color-scheme-combobox"); + init_color_scheme_menu (w); + + w = (GtkWidget *) gtk_builder_get_object (builder, "encoding-combobox"); + init_encodings_combo (w); + + /* Translators: Appears as: [numeric entry] × width */ + text = g_strdup_printf ("× %s", _("width")); + gtk_label_set_text ((GtkLabel *) gtk_builder_get_object (builder, "cell-width-scale-label"), + text); + g_free (text); + /* Translators: Appears as: [numeric entry] × height */ + text = g_strdup_printf ("× %s", _("height")); + gtk_label_set_text ((GtkLabel *) gtk_builder_get_object (builder, "cell-height-scale-label"), + text); + g_free (text); +} + +/* Called each time the user switches away from a profile, so it's no longer being edited */ +void +profile_prefs_unload (void) +{ + profile_prefs_signal_handlers_disconnect_all (); + profile_prefs_settings_unbind_all (); +} + +/* Called each time the user selects a new profile to edit */ +void +profile_prefs_load (const char *uuid, GSettings *profile) +{ + GtkWidget *w; + GtkBuilder *builder = the_pref_data->builder; + guint i; + + profile_prefs_unload (); + + gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (builder, "profile-uuid")), + uuid); + + profile_prefs_signal_connect (gtk_builder_get_object (builder, "default-size-reset-button"), + "clicked", + G_CALLBACK (default_size_reset_cb), + profile); + profile_prefs_signal_connect (gtk_builder_get_object (builder, "cell-scale-reset-button"), + "clicked", + G_CALLBACK (cell_scale_reset_cb), + profile); + + /* Hook up the palette colorpickers and combo box */ + + for (i = 0; i < TERMINAL_PALETTE_SIZE; ++i) + { + char name[32]; + char *text; + + g_snprintf (name, sizeof (name), "palette-colorpicker-%u", i); + w = (GtkWidget *) gtk_builder_get_object (builder, name); + + g_object_set_data (G_OBJECT (w), "palette-entry-index", GUINT_TO_POINTER (i)); + + text = g_strdup_printf (_("Choose Palette Color %u"), i); + gtk_color_button_set_title (GTK_COLOR_BUTTON (w), text); + g_free (text); + + text = g_strdup_printf (_("Palette entry %u"), i); + gtk_widget_set_tooltip_text (w, text); + g_free (text); + + profile_prefs_signal_connect (w, "notify::rgba", + G_CALLBACK (palette_color_notify_cb), + profile); + } + + profile_palette_notify_colorpickers_cb (profile, TERMINAL_PROFILE_PALETTE_KEY, NULL); + profile_prefs_signal_connect (profile, "changed::" TERMINAL_PROFILE_PALETTE_KEY, + G_CALLBACK (profile_palette_notify_colorpickers_cb), + NULL); + + w = (GtkWidget *) gtk_builder_get_object (builder, "palette-combobox"); + profile_prefs_signal_connect (w, "notify::active", + G_CALLBACK (palette_scheme_combo_changed_cb), + profile); + + profile_palette_notify_scheme_combo_cb (profile, TERMINAL_PROFILE_PALETTE_KEY, GTK_COMBO_BOX (w)); + profile_prefs_signal_connect (profile, "changed::" TERMINAL_PROFILE_PALETTE_KEY, + G_CALLBACK (profile_palette_notify_scheme_combo_cb), + w); + + /* Hook up the color scheme pickers and combo box */ + w = (GtkWidget *) gtk_builder_get_object (builder, "color-scheme-combobox"); + profile_prefs_signal_connect (w, "notify::active", + G_CALLBACK (color_scheme_combo_changed_cb), + profile); + + profile_colors_notify_scheme_combo_cb (profile, NULL, GTK_COMBO_BOX (w)); + profile_prefs_signal_connect (profile, "changed::" TERMINAL_PROFILE_FOREGROUND_COLOR_KEY, + G_CALLBACK (profile_colors_notify_scheme_combo_cb), + w); + profile_prefs_signal_connect (profile, "changed::" TERMINAL_PROFILE_BACKGROUND_COLOR_KEY, + G_CALLBACK (profile_colors_notify_scheme_combo_cb), + w); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "custom-command-entry")); + custom_command_entry_changed_cb (GTK_ENTRY (w)); + profile_prefs_signal_connect (w, "changed", + G_CALLBACK (custom_command_entry_changed_cb), NULL); + + profile_prefs_signal_connect (gtk_builder_get_object (builder, "reset-compat-defaults-button"), + "clicked", + G_CALLBACK (reset_compat_defaults_cb), + profile); + + profile_prefs_settings_bind_with_mapping (profile, + TERMINAL_PROFILE_BACKGROUND_COLOR_KEY, + gtk_builder_get_object (builder, + "background-colorpicker"), + "rgba", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) s_to_rgba, + (GSettingsBindSetMapping) rgba_to_s, + NULL, NULL); + + profile_prefs_settings_bind_with_mapping (profile, + TERMINAL_PROFILE_BACKSPACE_BINDING_KEY, + gtk_builder_get_object (builder, + "backspace-binding-combobox"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) string_to_enum, + (GSettingsBindSetMapping) enum_to_string, + vte_erase_binding_get_type, NULL); + profile_prefs_settings_bind (profile, + TERMINAL_PROFILE_BOLD_IS_BRIGHT_KEY, + gtk_builder_get_object (builder, "bold-is-bright-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG_KEY, + gtk_builder_get_object (builder, + "bold-color-checkbutton"), + "active", + G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_INVERT_BOOLEAN | + G_SETTINGS_BIND_SET); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "bold-colorpicker")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_BOLD_COLOR_SAME_AS_FG_KEY, + w, + "sensitive", + G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_INVERT_BOOLEAN | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_BOLD_COLOR_KEY, + w, + "rgba", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_NO_SENSITIVITY, + (GSettingsBindGetMapping) s_to_rgba, + (GSettingsBindSetMapping) rgba_to_s, + NULL, NULL); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "cell-height-scale-spinbutton")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_CELL_HEIGHT_SCALE_KEY, + gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)), + "value", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "cell-width-scale-spinbutton")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_CELL_WIDTH_SCALE_KEY, + gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)), + "value", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_CURSOR_COLORS_SET_KEY, + gtk_builder_get_object (builder, + "cursor-colors-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "cursor-foreground-colorpicker")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_CURSOR_COLORS_SET_KEY, + w, + "sensitive", + G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_CURSOR_FOREGROUND_COLOR_KEY, + w, + "rgba", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_NO_SENSITIVITY, + (GSettingsBindGetMapping) s_to_rgba, + (GSettingsBindSetMapping) rgba_to_s, + NULL, NULL); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "cursor-background-colorpicker")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_CURSOR_COLORS_SET_KEY, + w, + "sensitive", + G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_CURSOR_BACKGROUND_COLOR_KEY, + w, + "rgba", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_NO_SENSITIVITY, + (GSettingsBindGetMapping) s_to_rgba, + (GSettingsBindSetMapping) rgba_to_s, + NULL, NULL); + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_HIGHLIGHT_COLORS_SET_KEY, + gtk_builder_get_object (builder, + "highlight-colors-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "highlight-foreground-colorpicker")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_HIGHLIGHT_COLORS_SET_KEY, + w, + "sensitive", + G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_HIGHLIGHT_FOREGROUND_COLOR_KEY, + w, + "rgba", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_NO_SENSITIVITY, + (GSettingsBindGetMapping) s_to_rgba, + (GSettingsBindSetMapping) rgba_to_s, + NULL, NULL); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "highlight-background-colorpicker")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_HIGHLIGHT_COLORS_SET_KEY, + w, + "sensitive", + G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_HIGHLIGHT_BACKGROUND_COLOR_KEY, + w, + "rgba", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | G_SETTINGS_BIND_NO_SENSITIVITY, + (GSettingsBindGetMapping) s_to_rgba, + (GSettingsBindSetMapping) rgba_to_s, + NULL, NULL); + + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_CURSOR_SHAPE_KEY, + gtk_builder_get_object (builder, + "cursor-shape-combobox"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) string_to_enum, + (GSettingsBindSetMapping) enum_to_string, + vte_cursor_shape_get_type, NULL); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_CURSOR_BLINK_MODE_KEY, + gtk_builder_get_object (builder, + "cursor-blink-mode-combobox"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) string_to_enum, + (GSettingsBindSetMapping) enum_to_string, + vte_cursor_blink_mode_get_type, NULL); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_TEXT_BLINK_MODE_KEY, + gtk_builder_get_object (builder, + "text-blink-mode-combobox"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) string_to_enum, + (GSettingsBindSetMapping) enum_to_string, + vte_text_blink_mode_get_type, NULL); + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_CUSTOM_COMMAND_KEY, + gtk_builder_get_object (builder, + "custom-command-entry"), + "text", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "default-size-columns-spinbutton")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_DEFAULT_SIZE_COLUMNS_KEY, + gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)), + "value", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "default-size-rows-spinbutton")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_DEFAULT_SIZE_ROWS_KEY, + gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)), + "value", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_DELETE_BINDING_KEY, + gtk_builder_get_object (builder, + "delete-binding-combobox"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) string_to_enum, + (GSettingsBindSetMapping) enum_to_string, + vte_erase_binding_get_type, NULL); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_EXIT_ACTION_KEY, + gtk_builder_get_object (builder, + "exit-action-combobox"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) string_to_enum, + (GSettingsBindSetMapping) enum_to_string, + terminal_exit_action_get_type, NULL); + w = (GtkWidget*) gtk_builder_get_object (builder, "font-selector"); + gtk_font_chooser_set_filter_func (GTK_FONT_CHOOSER (w), monospace_filter, NULL, NULL); +#if GTK_CHECK_VERSION (3, 24, 0) + gtk_font_chooser_set_level (GTK_FONT_CHOOSER (w), GTK_FONT_CHOOSER_LEVEL_FAMILY | + GTK_FONT_CHOOSER_LEVEL_SIZE); +#endif + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_FONT_KEY, + w, + "font-name", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + profile_prefs_settings_bind_with_mapping (profile, + TERMINAL_PROFILE_FOREGROUND_COLOR_KEY, + gtk_builder_get_object (builder, + "foreground-colorpicker"), + "rgba", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) s_to_rgba, + (GSettingsBindSetMapping) rgba_to_s, + NULL, NULL); + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_LOGIN_SHELL_KEY, + gtk_builder_get_object (builder, + "login-shell-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + w = GTK_WIDGET (gtk_builder_get_object (builder, "scrollback-lines-spinbutton")); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_SCROLLBACK_LINES_KEY, + gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (w)), + "value", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED_KEY, + gtk_builder_get_object (builder, + "scrollback-limited-checkbutton"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | + G_SETTINGS_BIND_INVERT_BOOLEAN); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_SCROLLBACK_UNLIMITED_KEY, + gtk_builder_get_object (builder, + "scrollback-box"), + "sensitive", + G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_INVERT_BOOLEAN | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind_with_mapping (profile, + TERMINAL_PROFILE_SCROLLBAR_POLICY_KEY, + gtk_builder_get_object (builder, + "scrollbar-checkbutton"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) scrollbar_policy_to_bool, + (GSettingsBindSetMapping) bool_to_scrollbar_policy, + NULL, NULL); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_SCROLL_ON_KEYSTROKE_KEY, + gtk_builder_get_object (builder, + "scroll-on-keystroke-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_SCROLL_ON_OUTPUT_KEY, + gtk_builder_get_object (builder, + "scroll-on-output-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_USE_SYSTEM_FONT_KEY, + gtk_builder_get_object (builder, + "custom-font-checkbutton"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET | + G_SETTINGS_BIND_INVERT_BOOLEAN); + + w = (GtkWidget *) gtk_builder_get_object (builder, "preserve-working-directory-combobox"); + profile_prefs_settings_bind_with_mapping (profile, TERMINAL_PROFILE_PRESERVE_WORKING_DIRECTORY_KEY, w, + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET, + (GSettingsBindGetMapping) string_to_enum, + (GSettingsBindSetMapping) enum_to_string, + terminal_preserve_working_directory_get_type, NULL); + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY, + gtk_builder_get_object (builder, + "use-custom-command-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_USE_THEME_COLORS_KEY, + gtk_builder_get_object (builder, + "use-theme-colors-checkbutton"), + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_AUDIBLE_BELL_KEY, + gtk_builder_get_object (builder, "bell-checkbutton"), + "active", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + profile_prefs_settings_bind (profile, + TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY, + gtk_builder_get_object (builder, "custom-command-entry-label"), + "sensitive", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind (profile, + TERMINAL_PROFILE_USE_CUSTOM_COMMAND_KEY, + gtk_builder_get_object (builder, "custom-command-entry"), + "sensitive", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind (profile, + TERMINAL_PROFILE_USE_SYSTEM_FONT_KEY, + gtk_builder_get_object (builder, "font-selector"), + "sensitive", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_INVERT_BOOLEAN | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind (profile, + TERMINAL_PROFILE_USE_THEME_COLORS_KEY, + gtk_builder_get_object (builder, "colors-box"), + "sensitive", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_INVERT_BOOLEAN | + G_SETTINGS_BIND_NO_SENSITIVITY); + profile_prefs_settings_bind_writable (profile, + TERMINAL_PROFILE_PALETTE_KEY, + gtk_builder_get_object (builder, "palette-box"), + "sensitive", + FALSE); + + /* Compatibility options */ + w = (GtkWidget *) gtk_builder_get_object (builder, "encoding-combobox"); + profile_prefs_signal_connect (w, "changed", + G_CALLBACK (profile_encoding_combo_changed_cb), + profile); + + profile_notify_encoding_combo_cb (profile, TERMINAL_PROFILE_ENCODING_KEY, GTK_COMBO_BOX (w)); + profile_prefs_signal_connect (profile, "changed::" TERMINAL_PROFILE_ENCODING_KEY, + G_CALLBACK (profile_notify_encoding_combo_cb), + w); + + w = (GtkWidget *) gtk_builder_get_object (builder, "cjk-ambiguous-width-combobox"); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_CJK_UTF8_AMBIGUOUS_WIDTH_KEY, + w, + "active-id", + G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + + w = (GtkWidget *) gtk_builder_get_object (builder, "enable-sixel-checkbutton"); + profile_prefs_settings_bind (profile, TERMINAL_PROFILE_ENABLE_SIXEL_KEY, w, + "active", G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET); + gtk_widget_set_visible (w, (vte_get_feature_flags() & VTE_FEATURE_FLAG_SIXEL) != 0); +} + +/* Called once per Preferences window, to destroy stuff that doesn't depend on the profile being edited */ +void +profile_prefs_destroy (void) +{ + profile_prefs_unload (); + + g_array_free (the_pref_data->profile_signals, TRUE); + g_array_free (the_pref_data->profile_bindings, TRUE); +} |