diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:36:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 14:36:24 +0000 |
commit | 9b6d8e63db85c30007b463e91f91a791969fa83f (patch) | |
tree | 0899af51d73c1bf986f73ae39a03c4436083018a /panels/universal-access/zoom-options.c | |
parent | Initial commit. (diff) | |
download | gnome-control-center-9b6d8e63db85c30007b463e91f91a791969fa83f.tar.xz gnome-control-center-9b6d8e63db85c30007b463e91f91a791969fa83f.zip |
Adding upstream version 1:3.38.4.upstream/1%3.38.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'panels/universal-access/zoom-options.c')
-rw-r--r-- | panels/universal-access/zoom-options.c | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/panels/universal-access/zoom-options.c b/panels/universal-access/zoom-options.c new file mode 100644 index 0000000..c7aee19 --- /dev/null +++ b/panels/universal-access/zoom-options.c @@ -0,0 +1,540 @@ +/* + * Copyright 2011 Inclusive Design Research Centre, OCAD University. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2.1 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 Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: Joseph Scheuhammer <clown@alum.mit.edu> + */ + +#include "zoom-options.h" +#include <gdk/gdk.h> +#include <glib/gi18n.h> +#include <string.h> + +#define POSITION_MODEL_VALUE_COLUMN 2 +#define FONT_SCALE 1.25 + +struct _ZoomOptions +{ + GtkDialog parent; + + GSettings *settings; + GSettings *application_settings; + + GtkWidget *screen_position_combobox; + GtkWidget *follow_mouse_radio; + GtkWidget *screen_part_radio; + GtkWidget *centered_radio; + GtkWidget *push_radio; + GtkWidget *proportional_radio; + GtkWidget *extend_beyond_checkbox; + GtkWidget *brightness_slider; + GtkWidget *contrast_slider; + GtkWidget *crosshair_picker_color_button; + GtkWidget *magnifier_factor_spin; + GtkWidget *seeing_zoom_switch; + GtkWidget *crosshair_thickness_scale; + GtkWidget *grayscale_slider; + GtkWidget *crosshair_clip_checkbox; + GtkWidget *crosshair_length_slider; + GtkWidget *crosshair_enabled_switcher; + GtkWidget *inverse_enabled_switch; +}; + +G_DEFINE_TYPE (ZoomOptions, zoom_options, GTK_TYPE_DIALOG); + +static gchar *brightness_keys[] = { + "brightness-red", + "brightness-green", + "brightness-blue", + NULL +}; + +static gchar *contrast_keys[] = { + "contrast-red", + "contrast-green", + "contrast-blue", + NULL +}; + +static void set_enable_screen_part_ui (ZoomOptions *self); +static void scale_label (GtkBin *toggle, PangoAttrList *attrs); +static void xhairs_length_add_marks (ZoomOptions *self, GtkScale *scale); +static void effects_slider_set_value (GtkRange *slider, GSettings *settings); +static void brightness_slider_notify_cb (ZoomOptions *self, const gchar *key); +static void contrast_slider_notify_cb (ZoomOptions *self, const gchar *key); +static void effects_slider_changed (ZoomOptions *self, GtkRange *slider); + +static void +mouse_tracking_radio_toggled_cb (ZoomOptions *self, GtkWidget *widget) +{ + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + return; + + if (widget == self->centered_radio) + g_settings_set_string (self->settings, "mouse-tracking", "centered"); + else if (widget == self->proportional_radio) + g_settings_set_string (self->settings, "mouse-tracking", "proportional"); + else if (widget == self->push_radio) + g_settings_set_string (self->settings, "mouse-tracking", "push"); +} + +static void +mouse_tracking_notify_cb (ZoomOptions *self) +{ + g_autofree gchar *tracking = NULL; + + tracking = g_settings_get_string (self->settings, "mouse-tracking"); + if (g_strcmp0 (tracking, "centered") == 0) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->centered_radio), TRUE); + else if (g_strcmp0 (tracking, "proportional") == 0) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->proportional_radio), TRUE); + else + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->push_radio), TRUE); +} + +static void +init_screen_part_section (ZoomOptions *self, PangoAttrList *pango_attrs) +{ + gboolean lens_mode; + + /* Scale the labels of the toggles */ + scale_label (GTK_BIN (self->follow_mouse_radio), pango_attrs); + scale_label (GTK_BIN (self->screen_part_radio), pango_attrs); + scale_label (GTK_BIN (self->centered_radio), pango_attrs); + scale_label (GTK_BIN (self->push_radio), pango_attrs); + scale_label (GTK_BIN (self->proportional_radio), pango_attrs); + scale_label (GTK_BIN (self->extend_beyond_checkbox), pango_attrs); + + lens_mode = g_settings_get_boolean (self->settings, "lens-mode"); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->follow_mouse_radio), lens_mode); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->screen_part_radio), !lens_mode); + + set_enable_screen_part_ui (self); + + g_settings_bind (self->settings, "lens-mode", + self->follow_mouse_radio, "active", + G_SETTINGS_BIND_DEFAULT); + + g_settings_bind (self->settings, "scroll-at-edges", + self->extend_beyond_checkbox, "active", + G_SETTINGS_BIND_DEFAULT); + + g_signal_connect_object (G_OBJECT (self->screen_part_radio), "toggled", + G_CALLBACK (set_enable_screen_part_ui), self, G_CONNECT_SWAPPED); + + mouse_tracking_notify_cb (self); + g_signal_connect_object (G_OBJECT (self->settings), "changed::mouse-tracking", + G_CALLBACK (mouse_tracking_notify_cb), self, G_CONNECT_SWAPPED); +} + +static void +set_enable_screen_part_ui (ZoomOptions *self) +{ + gboolean screen_part; + + /* If the "screen part" radio is not checked, then the "follow mouse" radio + * is checked (== lens mode). Set mouse tracking back to the default. + */ + screen_part = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->screen_part_radio)); + if (!screen_part) + { + g_settings_set_string (self->settings, + "mouse-tracking", "proportional"); + } + + gtk_widget_set_sensitive (self->centered_radio, screen_part); + gtk_widget_set_sensitive (self->push_radio, screen_part); + gtk_widget_set_sensitive (self->proportional_radio, screen_part); + gtk_widget_set_sensitive (self->extend_beyond_checkbox, screen_part); +} + +static void +scale_label (GtkBin *toggle, PangoAttrList *attrs) +{ + GtkWidget *label; + + label = gtk_bin_get_child (toggle); + gtk_label_set_attributes (GTK_LABEL (label), attrs); +} + +static void +screen_position_combo_changed_cb (ZoomOptions *self) +{ + g_autofree gchar *combo_value = NULL; + GtkTreeIter iter; + + gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self->screen_position_combobox), &iter); + + gtk_tree_model_get (gtk_combo_box_get_model (GTK_COMBO_BOX (self->screen_position_combobox)), &iter, + POSITION_MODEL_VALUE_COLUMN, &combo_value, + -1); + + if (g_strcmp0 (combo_value, "")) + { + g_settings_set_string (self->settings, "screen-position", combo_value); + } +} + +static void +screen_position_notify_cb (ZoomOptions *self, + const gchar *key) +{ + g_autofree gchar *position = NULL; + GtkTreeIter iter; + GtkTreeModel *model; + GtkComboBox *combobox; + gboolean valid; + + position = g_settings_get_string (self->settings, key); + combobox = GTK_COMBO_BOX (self->screen_position_combobox); + model = gtk_combo_box_get_model (combobox); + + /* Find the matching screen position value in the combobox model. If nothing + * matches, leave the combobox as is. + */ + valid = gtk_tree_model_get_iter_first (model, &iter); + while (valid) + { + g_autofree gchar *combo_value = NULL; + + gtk_tree_model_get (model, &iter, + POSITION_MODEL_VALUE_COLUMN, &combo_value, + -1); + if (!g_strcmp0 (combo_value, position)) + { + g_signal_handlers_block_by_func (combobox, screen_position_combo_changed_cb, self); + gtk_combo_box_set_active_iter (combobox, &iter); + g_signal_handlers_unblock_by_func (combobox, screen_position_combo_changed_cb, self); + break; + } + + valid = gtk_tree_model_iter_next (model, &iter); + } +} + +static void +init_xhairs_color_opacity (GtkColorButton *color_button, GSettings *settings) +{ + g_autofree gchar *color_setting = NULL; + GdkRGBA rgba; + + color_setting = g_settings_get_string (settings, "cross-hairs-color"); + gdk_rgba_parse (&rgba, color_setting); + + rgba.alpha = g_settings_get_double (settings, "cross-hairs-opacity"); + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (color_button), &rgba); +} + +static void +xhairs_color_notify_cb (ZoomOptions *self) +{ + init_xhairs_color_opacity (GTK_COLOR_BUTTON (self->crosshair_picker_color_button), self->settings); +} + +static void +xhairs_opacity_notify_cb (ZoomOptions *self, gchar *key) +{ + GdkRGBA rgba; + gdouble opacity; + + opacity = g_settings_get_double (self->settings, key); + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (self->crosshair_picker_color_button), &rgba); + rgba.alpha = opacity * 65535; + gtk_color_chooser_set_rgba (GTK_COLOR_CHOOSER (self->crosshair_picker_color_button), &rgba); +} + +#define TO_HEX(x) (int) ((gdouble) x * 255.0) +static void +xhairs_color_opacity_changed (ZoomOptions *self) +{ + GdkRGBA rgba; + g_autofree gchar *color_string = NULL; + + gtk_color_chooser_get_rgba (GTK_COLOR_CHOOSER (self->crosshair_picker_color_button), &rgba); + color_string = g_strdup_printf ("#%02x%02x%02x", + TO_HEX(rgba.red), + TO_HEX(rgba.green), + TO_HEX(rgba.blue)); + + g_settings_set_string (self->settings, "cross-hairs-color", color_string); + + g_settings_set_double (self->settings, "cross-hairs-opacity", rgba.alpha); +} + +static void xhairs_length_add_marks (ZoomOptions *self, GtkScale *scale) +{ + GtkAdjustment *scale_model; + GdkRectangle rect; + GdkMonitor *monitor; + GdkDisplay *display; + GtkWindow *transient_for; + gint length, quarter_length; + + /* Get maximum dimension of the monitor */ + transient_for = gtk_window_get_transient_for (GTK_WINDOW (self)); + display = gtk_widget_get_display (GTK_WIDGET (transient_for)); + monitor = gdk_display_get_monitor_at_window (display, gtk_widget_get_window (GTK_WIDGET (transient_for))); + gdk_monitor_get_workarea (monitor, &rect); + + length = MAX (rect.width, rect.height); + scale_model = gtk_range_get_adjustment (GTK_RANGE (scale)); + + if (length < gtk_adjustment_get_upper (scale_model)) + gtk_adjustment_set_upper (scale_model, length); + + /* The crosshair is made up of four lines in pairs (top, bottom) and + (left, right). Stipulating: "quarter of the screen" means that the + length of one hair is 25% of the screen. */ + quarter_length = length / 4; + + gtk_scale_add_mark (scale, 0, GTK_POS_BOTTOM, C_("Distance", "Short")); + gtk_scale_add_mark (scale, quarter_length, GTK_POS_BOTTOM, C_("Distance", "¼ Screen")); + gtk_scale_add_mark (scale, quarter_length * 2 , GTK_POS_BOTTOM, C_("Distance", "½ Screen")); + gtk_scale_add_mark (scale, quarter_length * 3, GTK_POS_BOTTOM, C_("Distance", "¾ Screen")); + gtk_scale_add_mark (scale, length, GTK_POS_BOTTOM, C_("Distance", "Long")); +} + +static void +init_effects_slider (GtkRange *slider, + ZoomOptions *self, + gchar **keys, + GCallback notify_cb) +{ + gchar **key; + + g_object_set_data (G_OBJECT (slider), "settings-keys", keys); + effects_slider_set_value (slider, self->settings); + + for (key = keys; *key; key++) + { + g_autofree gchar *signal = g_strdup_printf ("changed::%s", *key); + g_signal_connect_object (G_OBJECT (self->settings), signal, notify_cb, self, G_CONNECT_SWAPPED); + } + g_signal_connect_object (G_OBJECT (slider), "value-changed", + G_CALLBACK (effects_slider_changed), + self, G_CONNECT_SWAPPED); + gtk_scale_add_mark (GTK_SCALE (slider), 0, GTK_POS_BOTTOM, NULL); +} + +static void +effects_slider_set_value (GtkRange *slider, GSettings *settings) +{ + gchar **keys; + gdouble red, green, blue; + gdouble value; + + keys = g_object_get_data (G_OBJECT (slider), "settings-keys"); + + red = g_settings_get_double (settings, keys[0]); + green = g_settings_get_double (settings, keys[1]); + blue = g_settings_get_double (settings, keys[2]); + + if (red == green && green == blue) + value = red; + else + /* use NTSC conversion weights for reasonable average */ + value = 0.299 * red + 0.587 * green + 0.114 * blue; + + gtk_range_set_value (slider, value); +} + +static void +brightness_slider_notify_cb (ZoomOptions *self, + const gchar *key) +{ + GtkRange *slider = GTK_RANGE (self->brightness_slider); + + g_signal_handlers_block_by_func (slider, effects_slider_changed, self); + effects_slider_set_value (slider, self->settings); + g_signal_handlers_unblock_by_func (slider, effects_slider_changed, self); +} + +static void +contrast_slider_notify_cb (ZoomOptions *self, + const gchar *key) +{ + GtkRange *slider = GTK_RANGE (self->contrast_slider); + + g_signal_handlers_block_by_func (slider, effects_slider_changed, self); + effects_slider_set_value (slider, self->settings); + g_signal_handlers_unblock_by_func (slider, effects_slider_changed, self); +} + +static void +effects_slider_changed (ZoomOptions *self, GtkRange *slider) +{ + gchar **keys, **key; + gdouble value; + + keys = g_object_get_data (G_OBJECT (slider), "settings-keys"); + value = gtk_range_get_value (slider); + + for (key = keys; *key; key++) + { + g_settings_set_double (self->settings, *key, value); + } +} + +static void +zoom_options_constructed (GObject *object) +{ + PangoAttribute *attr; + PangoAttrList *pango_attrs; + ZoomOptions *self; + + self = ZOOM_OPTIONS (object); + + G_OBJECT_CLASS (zoom_options_parent_class)->constructed (object); + + pango_attrs = pango_attr_list_new (); + attr = pango_attr_scale_new (FONT_SCALE); + pango_attr_list_insert (pango_attrs, attr); + + /* Zoom switch */ + g_settings_bind (self->application_settings, "screen-magnifier-enabled", + self->seeing_zoom_switch, "active", + G_SETTINGS_BIND_DEFAULT); + + /* Magnification factor */ + g_settings_bind (self->settings, "mag-factor", + gtk_spin_button_get_adjustment (GTK_SPIN_BUTTON (self->magnifier_factor_spin)), + "value", G_SETTINGS_BIND_DEFAULT); + + /* Screen position combo */ + screen_position_notify_cb (self, "screen-position"); + g_signal_connect_object (self->settings, "changed::screen-position", + G_CALLBACK (screen_position_notify_cb), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->screen_position_combobox, "changed", + G_CALLBACK (screen_position_combo_changed_cb), self, G_CONNECT_SWAPPED); + + /* Screen part section */ + init_screen_part_section (self, pango_attrs); + + /* Cross hairs: show/hide ... */ + g_settings_bind (self->settings, "show-cross-hairs", + self->crosshair_enabled_switcher, "active", + G_SETTINGS_BIND_DEFAULT); + + /* ... Cross hairs: color and opacity */ + init_xhairs_color_opacity (GTK_COLOR_BUTTON (self->crosshair_picker_color_button), self->settings); + g_signal_connect_object (self->settings, "changed::cross-hairs-color", + G_CALLBACK (xhairs_color_notify_cb), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->settings, "changed::cross-hairs-opacity", + G_CALLBACK (xhairs_opacity_notify_cb), self, G_CONNECT_SWAPPED); + g_signal_connect_object (self->crosshair_picker_color_button, "color-set", + G_CALLBACK (xhairs_color_opacity_changed), + self, G_CONNECT_SWAPPED); + + /* ... Cross hairs: thickness ... */ + g_settings_bind (self->settings, "cross-hairs-thickness", + gtk_range_get_adjustment (GTK_RANGE (self->crosshair_thickness_scale)), "value", + G_SETTINGS_BIND_DEFAULT); + + /* ... Cross hairs: clip ... */ + scale_label (GTK_BIN (self->crosshair_clip_checkbox), pango_attrs); + g_settings_bind (self->settings, "cross-hairs-clip", + self->crosshair_clip_checkbox, "active", + G_SETTINGS_BIND_INVERT_BOOLEAN); + + /* ... Cross hairs: length ... */ + xhairs_length_add_marks (self, GTK_SCALE (self->crosshair_length_slider)); + g_settings_bind (self->settings, "cross-hairs-length", + gtk_range_get_adjustment (GTK_RANGE (self->crosshair_length_slider)), "value", + G_SETTINGS_BIND_DEFAULT); + + /* ... Color effects ... */ + g_settings_bind (self->settings, "invert-lightness", self->inverse_enabled_switch, "active", + G_SETTINGS_BIND_DEFAULT); + + init_effects_slider (GTK_RANGE (self->brightness_slider), + self, + brightness_keys, + G_CALLBACK (brightness_slider_notify_cb)); + + init_effects_slider (GTK_RANGE (self->contrast_slider), + self, + contrast_keys, + G_CALLBACK (contrast_slider_notify_cb)); + + g_settings_bind (self->settings, "color-saturation", + gtk_range_get_adjustment (GTK_RANGE (self->grayscale_slider)), "value", + G_SETTINGS_BIND_DEFAULT); + gtk_scale_add_mark (GTK_SCALE(self->grayscale_slider), 1.0, GTK_POS_BOTTOM, NULL); + + pango_attr_list_unref (pango_attrs); +} + +static void +zoom_options_finalize (GObject *object) +{ + ZoomOptions *self = ZOOM_OPTIONS (object); + + g_clear_object (&self->settings); + g_clear_object (&self->application_settings); + + G_OBJECT_CLASS (zoom_options_parent_class)->finalize (object); +} + +static void +zoom_options_class_init (ZoomOptionsClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + object_class->finalize = zoom_options_finalize; + object_class->constructed = zoom_options_constructed; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/universal-access/zoom-options.ui"); + + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, brightness_slider); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, centered_radio); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, contrast_slider); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, crosshair_clip_checkbox); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, crosshair_enabled_switcher); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, crosshair_length_slider); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, crosshair_picker_color_button); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, crosshair_thickness_scale); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, extend_beyond_checkbox); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, follow_mouse_radio); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, grayscale_slider); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, inverse_enabled_switch); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, magnifier_factor_spin); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, proportional_radio); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, push_radio); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, screen_part_radio); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, screen_position_combobox); + gtk_widget_class_bind_template_child (widget_class, ZoomOptions, seeing_zoom_switch); + + gtk_widget_class_bind_template_callback (widget_class, mouse_tracking_radio_toggled_cb); +} + +static void +zoom_options_init (ZoomOptions *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + + self->settings = g_settings_new ("org.gnome.desktop.a11y.magnifier"); + self->application_settings = g_settings_new ("org.gnome.desktop.a11y.applications"); +} + +ZoomOptions * +zoom_options_new (GtkWindow *parent) +{ + return g_object_new (ZOOM_TYPE_OPTIONS, + "transient-for", parent, + "use-header-bar", TRUE, + NULL); +} |