X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtksettings.c;h=1ec5ade207b300148caa60e888a7044aa8a86e20;hb=f72dd13a36526c2e811cf24609bdc577266b5a42;hp=6a4e4823c70a8d7a8c8e9a54f2026713b510834b;hpb=c09cc89317d222e54e98d4e2e9f2792de13897ec;p=~andy%2Fgtk diff --git a/gtk/gtksettings.c b/gtk/gtksettings.c index 6a4e4823c..1ec5ade20 100644 --- a/gtk/gtksettings.c +++ b/gtk/gtksettings.c @@ -16,7 +16,9 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include +#define PANGO_ENABLE_BACKEND /* for pango_fc_font_map_cache_clear() */ + +#include "config.h" #include @@ -30,14 +32,20 @@ #ifdef GDK_WINDOWING_X11 #include "x11/gdkx.h" +#include #endif +#define DEFAULT_TIMEOUT_INITIAL 200 +#define DEFAULT_TIMEOUT_REPEAT 20 +#define DEFAULT_TIMEOUT_EXPAND 500 + typedef struct _GtkSettingsValuePrivate GtkSettingsValuePrivate; typedef enum { GTK_SETTINGS_SOURCE_DEFAULT, GTK_SETTINGS_SOURCE_RC_FILE, + GTK_SETTINGS_SOURCE_XSETTING, GTK_SETTINGS_SOURCE_APPLICATION } GtkSettingsSource; @@ -59,9 +67,11 @@ enum { PROP_DOUBLE_CLICK_DISTANCE, PROP_CURSOR_BLINK, PROP_CURSOR_BLINK_TIME, + PROP_CURSOR_BLINK_TIMEOUT, PROP_SPLIT_CURSOR, PROP_THEME_NAME, PROP_ICON_THEME_NAME, + PROP_FALLBACK_ICON_THEME, PROP_KEY_THEME_NAME, PROP_MENU_BAR_ACCEL, PROP_DND_DRAG_THRESHOLD, @@ -77,13 +87,40 @@ enum { PROP_CURSOR_THEME_NAME, PROP_CURSOR_THEME_SIZE, #endif - PROP_ALTERNATIVE_BUTTON_ORDER + PROP_ALTERNATIVE_BUTTON_ORDER, + PROP_ALTERNATIVE_SORT_ARROWS, + PROP_SHOW_INPUT_METHOD_MENU, + PROP_SHOW_UNICODE_MENU, + PROP_TIMEOUT_INITIAL, + PROP_TIMEOUT_REPEAT, + PROP_TIMEOUT_EXPAND, + PROP_COLOR_SCHEME, + PROP_ENABLE_ANIMATIONS, + PROP_TOUCHSCREEN_MODE, + PROP_TOOLTIP_TIMEOUT, + PROP_TOOLTIP_BROWSE_TIMEOUT, + PROP_TOOLTIP_BROWSE_MODE_TIMEOUT, + PROP_KEYNAV_CURSOR_ONLY, + PROP_KEYNAV_WRAP_AROUND, + PROP_ERROR_BELL, + PROP_COLOR_HASH, + PROP_FILE_CHOOSER_BACKEND, + PROP_PRINT_BACKENDS, + PROP_PRINT_PREVIEW_COMMAND, + PROP_ENABLE_MNEMONICS, + PROP_ENABLE_ACCELS, + PROP_RECENT_FILES_LIMIT, + PROP_IM_MODULE, + PROP_RECENT_FILES_MAX_AGE, + PROP_FONTCONFIG_TIMESTAMP, + PROP_SOUND_THEME_NAME, + PROP_ENABLE_INPUT_FEEDBACK_SOUNDS, + PROP_ENABLE_EVENT_SOUNDS, + PROP_ENABLE_TOOLTIPS }; /* --- prototypes --- */ -static void gtk_settings_init (GtkSettings *settings); -static void gtk_settings_class_init (GtkSettingsClass *class); static void gtk_settings_finalize (GObject *object); static void gtk_settings_get_property (GObject *object, guint property_id, @@ -105,45 +142,26 @@ static void settings_update_modules (GtkSettings *setting static void settings_update_cursor_theme (GtkSettings *settings); static void settings_update_resolution (GtkSettings *settings); static void settings_update_font_options (GtkSettings *settings); +static gboolean settings_update_fontconfig (GtkSettings *settings); #endif +static void settings_update_color_scheme (GtkSettings *settings); +static void merge_color_scheme (GtkSettings *settings, + const GValue *value, + GtkSettingsSource source); +static gchar *get_color_scheme (GtkSettings *settings); +static GHashTable *get_color_hash (GtkSettings *settings); /* --- variables --- */ -static gpointer parent_class = NULL; static GQuark quark_property_parser = 0; static GSList *object_list = NULL; static guint class_n_properties = 0; -/* --- functions --- */ -GType -gtk_settings_get_type (void) -{ - static GType settings_type = 0; - - if (!settings_type) - { - static const GTypeInfo settings_info = - { - sizeof (GtkSettingsClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_settings_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkSettings), - 0, /* n_preallocs */ - (GInstanceInitFunc) gtk_settings_init, - }; - - settings_type = g_type_register_static (G_TYPE_OBJECT, g_intern_static_string ("GtkSettings"), - &settings_info, 0); - } - - return settings_type; -} +G_DEFINE_TYPE (GtkSettings, gtk_settings, G_TYPE_OBJECT) +/* --- functions --- */ static void gtk_settings_init (GtkSettings *settings) { @@ -186,15 +204,12 @@ gtk_settings_class_init (GtkSettingsClass *class) GObjectClass *gobject_class = G_OBJECT_CLASS (class); guint result; - parent_class = g_type_class_peek_parent (class); - gobject_class->finalize = gtk_settings_finalize; gobject_class->get_property = gtk_settings_get_property; gobject_class->set_property = gtk_settings_set_property; gobject_class->notify = gtk_settings_notify; quark_property_parser = g_quark_from_static_string ("gtk-rc-property-parser"); - result = settings_install_property_parser (class, g_param_spec_int ("gtk-double-click-time", P_("Double Click Time"), @@ -211,6 +226,15 @@ gtk_settings_class_init (GtkSettingsClass *class) GTK_PARAM_READWRITE), NULL); g_assert (result == PROP_DOUBLE_CLICK_DISTANCE); + + /** + * GtkSettings:gtk-cursor-blink: + * + * Whether the cursor should blink. + * + * Also see the #GtkSettings:gtk-cursor-blink-timeout setting, + * which allows more flexible control over cursor blinking. + */ result = settings_install_property_parser (class, g_param_spec_boolean ("gtk-cursor-blink", P_("Cursor Blink"), @@ -222,11 +246,31 @@ gtk_settings_class_init (GtkSettingsClass *class) result = settings_install_property_parser (class, g_param_spec_int ("gtk-cursor-blink-time", P_("Cursor Blink Time"), - P_("Length of the cursor blink cycle, in milleseconds"), + P_("Length of the cursor blink cycle, in milliseconds"), 100, G_MAXINT, 1200, GTK_PARAM_READWRITE), NULL); g_assert (result == PROP_CURSOR_BLINK_TIME); + + /** + * GtkSettings:gtk-cursor-blink-timeout: + * + * Time after which the cursor stops blinking, in seconds. + * The timer is reset after each user interaction. + * + * Setting this to zero has the same effect as setting + * #GtkSettings:gtk-cursor-blink to %FALSE. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-cursor-blink-timeout", + P_("Cursor Blink Timeout"), + P_("Time after which the cursor stops blinking, in seconds"), + 1, G_MAXINT, G_MAXINT, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_CURSOR_BLINK_TIMEOUT); result = settings_install_property_parser (class, g_param_spec_boolean ("gtk-split-cursor", P_("Split Cursor"), @@ -243,6 +287,7 @@ gtk_settings_class_init (GtkSettingsClass *class) GTK_PARAM_READWRITE), NULL); g_assert (result == PROP_THEME_NAME); + result = settings_install_property_parser (class, g_param_spec_string ("gtk-icon-theme-name", P_("Icon Theme Name"), @@ -251,6 +296,15 @@ gtk_settings_class_init (GtkSettingsClass *class) GTK_PARAM_READWRITE), NULL); g_assert (result == PROP_ICON_THEME_NAME); + + result = settings_install_property_parser (class, + g_param_spec_string ("gtk-fallback-icon-theme", + P_("Fallback Icon Theme Name"), + P_("Name of a icon theme to fall back to"), + NULL, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_FALLBACK_ICON_THEME); result = settings_install_property_parser (class, g_param_spec_string ("gtk-key-theme-name", @@ -288,6 +342,20 @@ gtk_settings_class_init (GtkSettingsClass *class) NULL); g_assert (result == PROP_FONT_NAME); + /** + * GtkSettings:gtk-icon-sizes: + * + * A list of icon sizes. The list is separated by colons, and + * item has the form: + * + * size-name = width , height + * + * E.g. "gtk-menu=16,16:gtk-button=20,20:gtk-dialog=48,48". + * GTK+ itself use the following named icon sizes: gtk-menu, + * gtk-button, gtk-small-toolbar, gtk-large-toolbar, gtk-dnd, + * gtk-dialog. Applications can register their own named icon + * sizes with gtk_icon_size_register(). + */ result = settings_install_property_parser (class, g_param_spec_string ("gtk-icon-sizes", P_("Icon Sizes"), @@ -360,7 +428,7 @@ gtk_settings_class_init (GtkSettingsClass *class) result = settings_install_property_parser (class, g_param_spec_string ("gtk-cursor-theme-name", P_("Cursor theme name"), - P_("Name of the cursor theme to use"), + P_("Name of the cursor theme to use, or NULL to use the default theme"), NULL, GTK_PARAM_READWRITE), NULL); @@ -369,8 +437,8 @@ gtk_settings_class_init (GtkSettingsClass *class) result = settings_install_property_parser (class, g_param_spec_int ("gtk-cursor-theme-size", P_("Cursor theme size"), - P_("Size to use for cursors"), - 0, 128, 24, + P_("Size to use for cursors, or 0 to use the default size"), + 0, 128, 0, GTK_PARAM_READWRITE), NULL); @@ -385,6 +453,510 @@ gtk_settings_class_init (GtkSettingsClass *class) GTK_PARAM_READWRITE), NULL); g_assert (result == PROP_ALTERNATIVE_BUTTON_ORDER); + + /** + * GtkSettings:gtk-alternative-sort-arrows: + * + * Controls the direction of the sort indicators in sorted list and tree + * views. By default an arrow pointing down means the column is sorted + * in ascending order. When set to %TRUE, this order will be inverted. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-alternative-sort-arrows", + P_("Alternative sort indicator direction"), + P_("Whether the direction of the sort indicators in list and tree views is inverted compared to the default (where down means ascending)"), + FALSE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_ALTERNATIVE_SORT_ARROWS); + + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-show-input-method-menu", + P_("Show the 'Input Methods' menu"), + P_("Whether the context menus of entries and text views should offer to change the input method"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_SHOW_INPUT_METHOD_MENU); + + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-show-unicode-menu", + P_("Show the 'Insert Unicode Control Character' menu"), + P_("Whether the context menus of entries and text views should offer to insert control characters"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_SHOW_UNICODE_MENU); + + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-timeout-initial", + P_("Start timeout"), + P_("Starting value for timeouts, when button is pressed"), + 0, G_MAXINT, DEFAULT_TIMEOUT_INITIAL, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_TIMEOUT_INITIAL); + + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-timeout-repeat", + P_("Repeat timeout"), + P_("Repeat value for timeouts, when button is pressed"), + 0, G_MAXINT, DEFAULT_TIMEOUT_REPEAT, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_TIMEOUT_REPEAT); + + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-timeout-expand", + P_("Expand timeout"), + P_("Expand value for timeouts, when a widget is expanding a new region"), + 0, G_MAXINT, DEFAULT_TIMEOUT_EXPAND, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_TIMEOUT_EXPAND); + + /** + * GtkSettings:gtk-color-scheme: + * + * A palette of named colors for use in themes. The format of the string is + * + * name1: color1 + * name2: color2 + * ... + * + * Color names must be acceptable as identifiers in the + * gtkrc syntax, and + * color specifications must be in the format accepted by + * gdk_color_parse(). + * + * Note that due to the way the color tables from different sources are + * merged, color specifications will be converted to hexadecimal form + * when getting this property. + * + * Starting with GTK+ 2.12, the entries can alternatively be separated + * by ';' instead of newlines: + * + * name1: color1; name2: color2; ... + * + * + * Since: 2.10 + */ + result = settings_install_property_parser (class, + g_param_spec_string ("gtk-color-scheme", + P_("Color scheme"), + P_("A palette of named colors for use in themes"), + "", + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_COLOR_SCHEME); + + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-enable-animations", + P_("Enable Animations"), + P_("Whether to enable toolkit-wide animations."), + TRUE, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_ENABLE_ANIMATIONS); + + /** + * GtkSettings:gtk-touchscreen-mode: + * + * When %TRUE, there are no motion notify events delivered on this screen, + * and widgets can't use the pointer hovering them for any essential + * functionality. + * + * Since: 2.10 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-touchscreen-mode", + P_("Enable Touchscreen Mode"), + P_("When TRUE, there are no motion notify events delivered on this screen"), + FALSE, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_TOUCHSCREEN_MODE); + + /** + * GtkSettings:gtk-tooltip-timeout: + * + * Time, in milliseconds, after which a tooltip could appear if the + * cursor is hovering on top of a widget. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-tooltip-timeout", + P_("Tooltip timeout"), + P_("Timeout before tooltip is shown"), + 0, G_MAXINT, + 500, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_TOOLTIP_TIMEOUT); + + /** + * GtkSettings:gtk-tooltip-browse-timeout: + * + * Controls the time after which tooltips will appear when + * browse mode is enabled, in milliseconds. + * + * Browse mode is enabled when the mouse pointer moves off an object + * where a tooltip was currently being displayed. If the mouse pointer + * hits another object before the browse mode timeout expires (see + * #GtkSettings:gtk-tooltip-browse-mode-timeout), it will take the + * amount of milliseconds specified by this setting to popup the tooltip + * for the new object. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-tooltip-browse-timeout", + P_("Tooltip browse timeout"), + P_("Timeout before tooltip is shown when browse mode is enabled"), + 0, G_MAXINT, + 60, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_TOOLTIP_BROWSE_TIMEOUT); + + /** + * GtkSettings:gtk-tooltip-browse-mode-timeout: + * + * Amount of time, in milliseconds, after which the browse mode + * will be disabled. + * + * See #GtkSettings:gtk-tooltip-browse-timeout for more information + * about browse mode. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-tooltip-browse-mode-timeout", + P_("Tooltip browse mode timeout"), + P_("Timeout after which browse mode is disabled"), + 0, G_MAXINT, + 500, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_TOOLTIP_BROWSE_MODE_TIMEOUT); + + /** + * GtkSettings:gtk-keynav-cursor-only: + * + * When %TRUE, keyboard navigation should be able to reach all widgets + * by using the cursor keys only. Tab, Shift etc. keys can't be expected + * to be present on the used input device. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-keynav-cursor-only", + P_("Keynav Cursor Only"), + P_("When TRUE, there are only cursor keys available to navigate widgets"), + FALSE, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_KEYNAV_CURSOR_ONLY); + + /** + * GtkSettings:gtk-keynav-wrap-around: + * + * When %TRUE, some widgets will wrap around when doing keyboard + * navigation, such as menus, menubars and notebooks. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-keynav-wrap-around", + P_("Keynav Wrap Around"), + P_("Whether to wrap around when keyboard-navigating widgets"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_KEYNAV_WRAP_AROUND); + + /** + * GtkSettings:gtk-error-bell: + * + * When %TRUE, keyboard navigation and other input-related errors + * will cause a beep. Since the error bell is implemented using + * gdk_window_beep(), the windowing system may offer ways to + * configure the error bell in many ways, such as flashing the + * window or similar visual effects. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-error-bell", + P_("Error Bell"), + P_("When TRUE, keyboard navigation and other errors will cause a beep"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_ERROR_BELL); + + /** + * GtkSettings:color-hash: + * + * Holds a hash table representation of the #GtkSettings:gtk-color-scheme + * setting, mapping color names to #GdkColors. + * + * Since: 2.10 + */ + result = settings_install_property_parser (class, + g_param_spec_boxed ("color-hash", + P_("Color Hash"), + P_("A hash table representation of the color scheme."), + G_TYPE_HASH_TABLE, + GTK_PARAM_READABLE), + NULL); + g_assert (result == PROP_COLOR_HASH); + + result = settings_install_property_parser (class, + g_param_spec_string ("gtk-file-chooser-backend", + P_("Default file chooser backend"), + P_("Name of the GtkFileChooser backend to use by default"), + NULL, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_FILE_CHOOSER_BACKEND); + + /** + * GtkSettings:gtk-print-backends: + * + * A comma-separated list of print backends to use in the print + * dialog. Available print backends depend on the GTK+ installation, + * and may include "pdf", "cups" or "lpr". + * + * Since: 2.10 + */ + result = settings_install_property_parser (class, + g_param_spec_string ("gtk-print-backends", + P_("Default print backend"), + P_("List of the GtkPrintBackend backends to use by default"), + GTK_PRINT_BACKENDS, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_PRINT_BACKENDS); + + /** + * GtkSettings:gtk-print-preview-command: + * + * A command to run for displaying the print preview. The command + * should contain a %f placeholder, which will get replaced by + * the path to the pdf file. The command may also contain a %s + * placeholder, which will get replaced by the path to a file + * containing the print settings in the format produced by + * gtk_print_settings_to_file(). + * + * The preview application is responsible for removing the pdf file + * and the print settings file when it is done. + * + * Since: 2.10 + */ + result = settings_install_property_parser (class, + g_param_spec_string ("gtk-print-preview-command", + P_("Default command to run when displaying a print preview"), + P_("Command to run when displaying a print preview"), + GTK_PRINT_PREVIEW_COMMAND, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_PRINT_PREVIEW_COMMAND); + + /** + * GtkSettings:gtk-enable-mnemonics: + * + * Whether labels and menu items should have visible mnemonics which + * can be activated. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-enable-mnemonics", + P_("Enable Mnemonics"), + P_("Whether labels should have mnemonics"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_ENABLE_MNEMONICS); + + /** + * GtkSettings:gtk-enable-accels: + * + * Whether menu items should have visible accelerators which can be + * activated. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-enable-accels", + P_("Enable Accelerators"), + P_("Whether menu items should have accelerators"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_ENABLE_ACCELS); + + /** + * GtkSettings:gtk-recent-files-limit: + * + * The number of recently used files that should be displayed by default by + * #GtkRecentChooser implementations and by the #GtkFileChooser. A value of + * -1 means every recently used file stored. + * + * Since: 2.12 + */ + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-recent-files-limit", + P_("Recent Files Limit"), + P_("Number of recently used files"), + -1, G_MAXINT, + 50, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_RECENT_FILES_LIMIT); + + /** + * GtkSettings:gtk-im-module: + * + * Which IM module should be used by default. + */ + result = settings_install_property_parser (class, + g_param_spec_string ("gtk-im-module", + P_("Default IM module"), + P_("Which IM module should be used by default"), + NULL, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_IM_MODULE); + + /** + * GtkSettings:gtk-recent-files-max-age: + * + * The maximum age, in days, of the items inside the recently used + * resources list. Items older than this setting will be excised + * from the list. If set to 0, the list will always be empty; if + * set to -1, no item will be removed. + * + * Since: 2.14 + */ + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-recent-files-max-age", + P_("Recent Files Max Age"), + P_("Maximum age of recently used files, in days"), + -1, G_MAXINT, + 30, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_RECENT_FILES_MAX_AGE); + + result = settings_install_property_parser (class, + g_param_spec_int ("gtk-fontconfig-timestamp", + P_("Fontconfig configuration timestamp"), + P_("Timestamp of current fontconfig configuration"), + G_MININT, G_MAXINT, 0, + GTK_PARAM_READWRITE), + NULL); + + g_assert (result == PROP_FONTCONFIG_TIMESTAMP); + + /** + * GtkSettings:gtk-sound-theme-name: + * + * The XDG sound theme to use for event sounds. + * + * See the Sound Theme spec + * for more information on event sounds and sound themes. + * + * GTK+ itself does not support event sounds, you have to use a loadable + * module like the one that comes with libcanberra. + * + * Since: 2.14 + */ + result = settings_install_property_parser (class, + g_param_spec_string ("gtk-sound-theme-name", + P_("Sound Theme Name"), + P_("XDG sound theme name"), + "freedesktop", + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_SOUND_THEME_NAME); + + /** + * GtkSettings:gtk-enable-input-feedback-sounds: + * + * Whether to play event sounds as feedback to user input. + * + * See the Sound Theme spec + * for more information on event sounds and sound themes. + * + * GTK+ itself does not support event sounds, you have to use a loadable + * module like the one that comes with libcanberra. + * + * Since: 2.14 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-enable-input-feedback-sounds", + /* Translators: this means sounds that are played as feedback to user input */ + P_("Audible Input Feedback"), + P_("Whether to play event sounds as feedback to user input"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_ENABLE_INPUT_FEEDBACK_SOUNDS); + + /** + * GtkSettings:gtk-enable-event-sounds: + * + * Whether to play any event sounds at all. + * + * See the Sound Theme spec + * for more information on event sounds and sound themes. + * + * GTK+ itself does not support event sounds, you have to use a loadable + * module like the one that comes with libcanberra. + * + * Since: 2.14 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-enable-event-sounds", + P_("Enable Event Sounds"), + P_("Whether to play any event sounds at all"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_ENABLE_EVENT_SOUNDS); + + /** + * GtkSettings:gtk-enable-tooltips: + * + * Whether tooltips should be shown on widgets. + * + * Since: 2.14 + */ + result = settings_install_property_parser (class, + g_param_spec_boolean ("gtk-enable-tooltips", + P_("Enable Tooltips"), + P_("Whether tooltips should be shown on widgets"), + TRUE, + GTK_PARAM_READWRITE), + NULL); + g_assert (result == PROP_ENABLE_TOOLTIPS); } static void @@ -395,13 +967,15 @@ gtk_settings_finalize (GObject *object) object_list = g_slist_remove (object_list, settings); + _gtk_rc_context_destroy (settings); + for (i = 0; i < class_n_properties; i++) g_value_unset (&settings->property_values[i].value); g_free (settings->property_values); - + g_datalist_clear (&settings->queued_settings); - G_OBJECT_CLASS (parent_class)->finalize (object); + G_OBJECT_CLASS (gtk_settings_parent_class)->finalize (object); } /** @@ -426,7 +1000,8 @@ gtk_settings_get_for_screen (GdkScreen *screen) { settings = g_object_new (GTK_TYPE_SETTINGS, NULL); settings->screen = screen; - g_object_set_data (G_OBJECT (screen), "gtk-settings", settings); + g_object_set_data_full (G_OBJECT (screen), I_("gtk-settings"), + settings, g_object_unref); gtk_rc_reparse_all_for_settings (settings, TRUE); settings_update_double_click (settings); @@ -435,6 +1010,7 @@ gtk_settings_get_for_screen (GdkScreen *screen) settings_update_resolution (settings); settings_update_font_options (settings); #endif + settings_update_color_scheme (settings); } return settings; @@ -470,6 +1046,9 @@ gtk_settings_set_property (GObject *object, g_value_copy (value, &settings->property_values[property_id - 1].value); settings->property_values[property_id - 1].source = GTK_SETTINGS_SOURCE_APPLICATION; + + if (pspec->param_id == PROP_COLOR_SCHEME) + merge_color_scheme (settings, value, GTK_SETTINGS_SOURCE_APPLICATION); } static void @@ -482,6 +1061,18 @@ gtk_settings_get_property (GObject *object, GType value_type = G_VALUE_TYPE (value); GType fundamental_type = G_TYPE_FUNDAMENTAL (value_type); + /* handle internal properties */ + switch (property_id) + { + case PROP_COLOR_HASH: + g_value_set_boxed (value, get_color_hash (settings)); + return; + case PROP_COLOR_SCHEME: + g_value_take_string (value, get_color_scheme (settings)); + return; + default: ; + } + /* For enums and strings, we need to get the value as a string, * not as an int, since we support using names/nicks as the setting * value. @@ -494,7 +1085,7 @@ gtk_settings_get_property (GObject *object, if (settings->property_values[property_id - 1].source == GTK_SETTINGS_SOURCE_APPLICATION || !gdk_screen_get_setting (settings->screen, pspec->name, value)) g_value_copy (&settings->property_values[property_id - 1].value, value); - else + else g_param_value_validate (pspec, value); } else @@ -517,7 +1108,6 @@ gtk_settings_get_property (GObject *object, GtkRcPropertyParser parser = (GtkRcPropertyParser) g_param_spec_get_qdata (pspec, quark_property_parser); g_value_init (&gstring_value, G_TYPE_GSTRING); - g_value_take_boxed (&gstring_value, g_string_new (g_value_get_string (&val))); @@ -561,6 +1151,9 @@ gtk_settings_notify (GObject *object, case PROP_DOUBLE_CLICK_DISTANCE: settings_update_double_click (settings); break; + case PROP_COLOR_SCHEME: + settings_update_color_scheme (settings); + break; #ifdef GDK_WINDOWING_X11 case PROP_XFT_DPI: settings_update_resolution (settings); @@ -577,6 +1170,10 @@ gtk_settings_notify (GObject *object, settings_update_font_options (settings); gtk_rc_reset_styles (GTK_SETTINGS (object)); break; + case PROP_FONTCONFIG_TIMESTAMP: + if (settings_update_fontconfig (settings)) + gtk_rc_reset_styles (GTK_SETTINGS (object)); + break; case PROP_CURSOR_THEME_NAME: case PROP_CURSOR_THEME_SIZE: settings_update_cursor_theme (settings); @@ -664,18 +1261,23 @@ apply_queued_setting (GtkSettings *data, if (_gtk_settings_parse_convert (parser, &qvalue->public.value, pspec, &tmp_value)) { + if (pspec->param_id == PROP_COLOR_SCHEME) + merge_color_scheme (data, &tmp_value, qvalue->source); + if (data->property_values[pspec->param_id - 1].source <= qvalue->source) { - g_object_set_property (G_OBJECT (data), pspec->name, &tmp_value); + g_value_copy (&tmp_value, &data->property_values[pspec->param_id - 1].value); data->property_values[pspec->param_id - 1].source = qvalue->source; + g_object_notify (G_OBJECT (data), g_param_spec_get_name (pspec)); } + } else { gchar *debug = g_strdup_value_contents (&qvalue->public.value); g_message ("%s: failed to retrieve property `%s' of type `%s' from rc file value \"%s\" of type `%s'", - qvalue->public.origin, + qvalue->public.origin ? qvalue->public.origin : "(for origin information, set GTK_DEBUG)", pspec->name, g_type_name (G_PARAM_SPEC_VALUE_TYPE (pspec)), debug, @@ -705,6 +1307,12 @@ settings_install_property_parser (GtkSettingsClass *class, case G_TYPE_DOUBLE: case G_TYPE_STRING: break; + case G_TYPE_BOXED: + if (strcmp (g_param_spec_get_name (pspec), "color-hash") == 0) + { + break; + } + /* fall through */ default: if (!parser) { @@ -772,23 +1380,33 @@ _gtk_rc_property_parser_from_type (GType type) void gtk_settings_install_property (GParamSpec *pspec) { + static GtkSettingsClass *klass = NULL; + GtkRcPropertyParser parser; g_return_if_fail (G_IS_PARAM_SPEC (pspec)); + if (! klass) + klass = g_type_class_ref (GTK_TYPE_SETTINGS); + parser = _gtk_rc_property_parser_from_type (G_PARAM_SPEC_VALUE_TYPE (pspec)); - settings_install_property_parser (gtk_type_class (GTK_TYPE_SETTINGS), pspec, parser); + settings_install_property_parser (klass, pspec, parser); } void gtk_settings_install_property_parser (GParamSpec *pspec, GtkRcPropertyParser parser) { + static GtkSettingsClass *klass = NULL; + g_return_if_fail (G_IS_PARAM_SPEC (pspec)); g_return_if_fail (parser != NULL); - - settings_install_property_parser (gtk_type_class (GTK_TYPE_SETTINGS), pspec, parser); + + if (! klass) + klass = g_type_class_ref (GTK_TYPE_SETTINGS); + + settings_install_property_parser (klass, pspec, parser); } static void @@ -798,7 +1416,7 @@ free_value (gpointer data) g_value_unset (&qvalue->public.value); g_free (qvalue->public.origin); - g_free (qvalue); + g_slice_free (GtkSettingsValuePrivate, qvalue); } static void @@ -822,14 +1440,14 @@ gtk_settings_set_property_value_internal (GtkSettings *settings, } name = g_strdup (prop_name); - g_strcanon (name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-'); + g_strcanon (name, G_CSET_DIGITS "-" G_CSET_a_2_z G_CSET_A_2_Z, '-'); name_quark = g_quark_from_string (name); g_free (name); qvalue = g_datalist_id_get_data (&settings->queued_settings, name_quark); if (!qvalue) { - qvalue = g_new0 (GtkSettingsValuePrivate, 1); + qvalue = g_slice_new0 (GtkSettingsValuePrivate); g_datalist_id_set_data_full (&settings->queued_settings, name_quark, qvalue, free_value); } else @@ -854,7 +1472,6 @@ gtk_settings_set_property_value (GtkSettings *settings, g_return_if_fail (GTK_SETTINGS (settings)); g_return_if_fail (prop_name != NULL); g_return_if_fail (new_value != NULL); - g_return_if_fail (new_value->origin != NULL); gtk_settings_set_property_value_internal (settings, prop_name, new_value, GTK_SETTINGS_SOURCE_APPLICATION); @@ -868,7 +1485,6 @@ _gtk_settings_set_property_value_from_rc (GtkSettings *settings, g_return_if_fail (GTK_SETTINGS (settings)); g_return_if_fail (prop_name != NULL); g_return_if_fail (new_value != NULL); - g_return_if_fail (new_value->origin != NULL); gtk_settings_set_property_value_internal (settings, prop_name, new_value, GTK_SETTINGS_SOURCE_RC_FILE); @@ -885,7 +1501,6 @@ gtk_settings_set_string_property (GtkSettings *settings, g_return_if_fail (GTK_SETTINGS (settings)); g_return_if_fail (name != NULL); g_return_if_fail (v_string != NULL); - g_return_if_fail (origin != NULL); svalue.origin = (gchar*) origin; g_value_init (&svalue.value, G_TYPE_STRING); @@ -904,7 +1519,6 @@ gtk_settings_set_long_property (GtkSettings *settings, g_return_if_fail (GTK_SETTINGS (settings)); g_return_if_fail (name != NULL); - g_return_if_fail (origin != NULL); svalue.origin = (gchar*) origin; g_value_init (&svalue.value, G_TYPE_LONG); @@ -923,7 +1537,6 @@ gtk_settings_set_double_property (GtkSettings *settings, g_return_if_fail (GTK_SETTINGS (settings)); g_return_if_fail (name != NULL); - g_return_if_fail (origin != NULL); svalue.origin = (gchar*) origin; g_value_init (&svalue.value, G_TYPE_DOUBLE); @@ -1265,10 +1878,29 @@ gtk_rc_property_parse_border (const GParamSpec *pspec, void _gtk_settings_handle_event (GdkEventSetting *event) { - GtkSettings *settings = gtk_settings_get_for_screen (gdk_drawable_get_screen (event->window)); - - if (g_object_class_find_property (G_OBJECT_GET_CLASS (settings), event->name)) - g_object_notify (G_OBJECT (settings), event->name); + GtkSettings *settings; + GParamSpec *pspec; + guint property_id; + + settings = gtk_settings_get_for_screen (gdk_drawable_get_screen (event->window)); + pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (settings), event->name); + + if (pspec) + { + property_id = pspec->param_id; + + if (property_id == PROP_COLOR_SCHEME) + { + GValue value = { 0, }; + + g_value_init (&value, G_TYPE_STRING); + if (!gdk_screen_get_setting (settings->screen, pspec->name, &value)) + g_value_set_static_string (&value, ""); + merge_color_scheme (settings, &value, GTK_SETTINGS_SOURCE_XSETTING); + g_value_unset (&value); + } + g_object_notify (G_OBJECT (settings), pspec->name); + } } static void @@ -1303,6 +1935,8 @@ _gtk_settings_reset_rc_values (GtkSettings *settings) g_datalist_id_remove_data (&settings->queued_settings, key_id); } + g_slist_free (to_reset); + /* Now reset the active settings */ pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), NULL); @@ -1380,13 +2014,11 @@ settings_update_font_options (GtkSettings *settings) { gint hinting; gchar *hint_style_str; - cairo_hint_style_t hint_style = CAIRO_HINT_STYLE_DEFAULT; + cairo_hint_style_t hint_style = CAIRO_HINT_STYLE_NONE; gint antialias; - cairo_antialias_t antialias_mode = CAIRO_ANTIALIAS_DEFAULT; + cairo_antialias_t antialias_mode = CAIRO_ANTIALIAS_GRAY; gchar *rgba_str; cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; - gint dpi_int; - double dpi; cairo_font_options_t *options; g_object_get (settings, @@ -1394,15 +2026,11 @@ settings_update_font_options (GtkSettings *settings) "gtk-xft-hinting", &hinting, "gtk-xft-hintstyle", &hint_style_str, "gtk-xft-rgba", &rgba_str, - "gtk-xft-dpi", &dpi_int, NULL); - if (dpi_int > 0) - dpi = dpi_int / 1024.; - else - dpi = -1.; - options = cairo_font_options_create (); + + cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); if (hinting >= 0 && !hinting) { @@ -1420,8 +2048,7 @@ settings_update_font_options (GtkSettings *settings) hint_style = CAIRO_HINT_STYLE_FULL; } - if (hint_style_str) - g_free (hint_style_str); + g_free (hint_style_str); cairo_font_options_set_hint_style (options, hint_style); @@ -1455,6 +2082,44 @@ settings_update_font_options (GtkSettings *settings) cairo_font_options_destroy (options); } +#ifdef GDK_WINDOWING_X11 +static gboolean +settings_update_fontconfig (GtkSettings *settings) +{ + static guint last_update_timestamp; + static gboolean last_update_needed; + + guint timestamp; + + g_object_get (settings, + "gtk-fontconfig-timestamp", ×tamp, + NULL); + + /* if timestamp is the same as last_update_timestamp, we already have + * updated fontconig on this timestamp (another screen requested it perhaps?), + * just return the cached result.*/ + + if (timestamp != last_update_timestamp) + { + PangoFontMap *fontmap = pango_cairo_font_map_get_default (); + gboolean update_needed = FALSE; + + /* bug 547680 */ + if (PANGO_IS_FC_FONT_MAP (fontmap) && !FcConfigUptoDate (NULL)) + { + pango_fc_font_map_cache_clear (PANGO_FC_FONT_MAP (fontmap)); + if (FcInitReinitialize ()) + update_needed = TRUE; + } + + last_update_timestamp = timestamp; + last_update_needed = update_needed; + } + + return last_update_needed; +} +#endif /* GDK_WINDOWING_X11 */ + static void settings_update_resolution (GtkSettings *settings) { @@ -1474,5 +2139,266 @@ settings_update_resolution (GtkSettings *settings) } #endif +typedef struct { + GHashTable *color_hash; + GHashTable *tables[GTK_SETTINGS_SOURCE_APPLICATION + 1]; + gchar *lastentry[GTK_SETTINGS_SOURCE_APPLICATION + 1]; +} ColorSchemeData; + +static void +color_scheme_data_free (ColorSchemeData *data) +{ + gint i; + + g_hash_table_unref (data->color_hash); + + for (i = 0; i <= GTK_SETTINGS_SOURCE_APPLICATION; i++) + { + if (data->tables[i]) + g_hash_table_unref (data->tables[i]); + g_free (data->lastentry[i]); + } + + g_slice_free (ColorSchemeData, data); +} + +static void +settings_update_color_scheme (GtkSettings *settings) +{ + if (!g_object_get_data (G_OBJECT (settings), "gtk-color-scheme")) + { + ColorSchemeData *data; + GValue value = { 0, }; + + data = g_slice_new0 (ColorSchemeData); + data->color_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) gdk_color_free); + g_object_set_data_full (G_OBJECT (settings), "gtk-color-scheme", + data, (GDestroyNotify) color_scheme_data_free); + + g_value_init (&value, G_TYPE_STRING); + if (gdk_screen_get_setting (settings->screen, "gtk-color-scheme", &value)) + { + merge_color_scheme (settings, &value, GTK_SETTINGS_SOURCE_XSETTING); + g_value_unset (&value); + } + } +} + +static gboolean +add_color_to_hash (gchar *name, + GdkColor *color, + GHashTable *target) +{ + GdkColor *old; + + old = g_hash_table_lookup (target, name); + if (!old || !gdk_color_equal (old, color)) + { + g_hash_table_insert (target, g_strdup (name), gdk_color_copy (color)); + + return TRUE; + } + + return FALSE; +} + +static gboolean +add_colors_to_hash_from_string (GHashTable *hash, + const gchar *colors) +{ + gchar *s, *p, *name; + GdkColor color; + gboolean changed = FALSE; + gchar *copy; + + copy = g_strdup (colors); + s = copy; + while (s && *s) + { + name = s; + p = strchr (s, ':'); + if (p) + { + *p = '\0'; + p++; + } + else + break; + + while (*p == ' ') + p++; + + s = p; + while (*s) + { + if (*s == '\n' || *s == ';') + { + *s = '\0'; + s++; + break; + } + s++; + } + + if (gdk_color_parse (p, &color)) + changed |= add_color_to_hash (name, &color, hash); + } + + g_free (copy); + + return changed; +} + +static gboolean +update_color_hash (ColorSchemeData *data, + const gchar *str, + GtkSettingsSource source) +{ + gboolean changed = FALSE; + gint i; + GHashTable *old_hash; + + if ((str == NULL || *str == '\0') && + (data->lastentry[source] == NULL || data->lastentry[source][0] == '\0')) + return FALSE; + + if (str && data->lastentry[source] && strcmp (str, data->lastentry[source]) == 0) + return FALSE; + + /* For the RC_FILE source we merge the values rather than over-writing + * them, since multiple rc files might define independent sets of colors + */ + if ((source != GTK_SETTINGS_SOURCE_RC_FILE) && + data->tables[source] && g_hash_table_size (data->tables[source]) > 0) + { + g_hash_table_unref (data->tables[source]); + data->tables[source] = NULL; + changed = TRUE; /* We can't rely on the code below since str might be "" */ + } + + if (data->tables[source] == NULL) + data->tables[source] = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + (GDestroyNotify) gdk_color_free); + + g_free (data->lastentry[source]); + data->lastentry[source] = g_strdup (str); + + changed |= add_colors_to_hash_from_string (data->tables[source], str); + + if (!changed) + return FALSE; + + /* Rebuild the merged hash table. */ + old_hash = data->color_hash; + data->color_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, + (GDestroyNotify) gdk_color_free); + for (i = 0; i <= GTK_SETTINGS_SOURCE_APPLICATION; i++) + { + if (data->tables[i]) + g_hash_table_foreach (data->tables[i], (GHFunc) add_color_to_hash, + data->color_hash); + } + + if (old_hash) + { + /* now check if the merged hash has changed */ + changed = FALSE; + if (g_hash_table_size (old_hash) != g_hash_table_size (data->color_hash)) + changed = TRUE; + else + { + GHashTableIter iter; + gpointer key, value, new_value; + + g_hash_table_iter_init (&iter, old_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + new_value = g_hash_table_lookup (data->color_hash, key); + if (!new_value || !gdk_color_equal (value, new_value)) + { + changed = TRUE; + break; + } + } + } + + g_hash_table_unref (old_hash); + } + else + changed = TRUE; + + return changed; +} + +static void +merge_color_scheme (GtkSettings *settings, + const GValue *value, + GtkSettingsSource source) +{ + ColorSchemeData *data; + const gchar *colors; + + g_object_freeze_notify (G_OBJECT (settings)); + + colors = g_value_get_string (value); + + settings_update_color_scheme (settings); + + data = (ColorSchemeData *) g_object_get_data (G_OBJECT (settings), + "gtk-color-scheme"); + + if (update_color_hash (data, colors, source)) + g_object_notify (G_OBJECT (settings), "color-hash"); + + g_object_thaw_notify (G_OBJECT (settings)); +} + +static GHashTable * +get_color_hash (GtkSettings *settings) +{ + ColorSchemeData *data; + + settings_update_color_scheme (settings); + + data = (ColorSchemeData *)g_object_get_data (G_OBJECT (settings), + "gtk-color-scheme"); + + return data->color_hash; +} + +static void +append_color_scheme (gpointer key, + gpointer value, + gpointer data) +{ + gchar *name = (gchar *)key; + GdkColor *color = (GdkColor *)value; + GString *string = (GString *)data; + + g_string_append_printf (string, "%s: #%04x%04x%04x\n", + name, color->red, color->green, color->blue); +} + +static gchar * +get_color_scheme (GtkSettings *settings) +{ + ColorSchemeData *data; + GString *string; + + settings_update_color_scheme (settings); + + data = (ColorSchemeData *) g_object_get_data (G_OBJECT (settings), + "gtk-color-scheme"); + + string = g_string_new (""); + + g_hash_table_foreach (data->color_hash, append_color_scheme, string); + + return g_string_free (string, FALSE); +} + + #define __GTK_SETTINGS_C__ #include "gtkaliasdef.c"