diff options
Diffstat (limited to 'panels/display/cc-display-settings.c')
-rw-r--r-- | panels/display/cc-display-settings.c | 919 |
1 files changed, 919 insertions, 0 deletions
diff --git a/panels/display/cc-display-settings.c b/panels/display/cc-display-settings.c new file mode 100644 index 0000000..8fec65b --- /dev/null +++ b/panels/display/cc-display-settings.c @@ -0,0 +1,919 @@ +/* cc-display-settings.c + * + * Copyright (C) 2007, 2008, 2018, 2019 Red Hat, Inc. + * Copyright (C) 2013 Intel, Inc. + * + * Written by: Benjamin Berg <bberg@redhat.com> + * + * 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, 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 <float.h> +#include <glib/gi18n.h> +#include <float.h> +#include <math.h> +#include "cc-display-settings.h" +#include "cc-display-config.h" + +#define MAX_SCALE_BUTTONS 5 + +struct _CcDisplaySettings +{ + GtkBox object; + + gboolean updating; + gboolean num_scales; + gboolean folded; + guint idle_udpate_id; + + gboolean has_accelerometer; + CcDisplayConfig *config; + CcDisplayMonitor *selected_output; + + GListModel *orientation_list; + GListStore *refresh_rate_list; + GListStore *resolution_list; + GListModel *scale_list; + + GtkWidget *enabled_listbox; + AdwActionRow *enabled_row; + GtkSwitch *enabled_switch; + GtkWidget *orientation_row; + GtkWidget *refresh_rate_row; + GtkWidget *resolution_row; + GtkWidget *scale_bbox; + GtkWidget *scale_buttons_row; + GtkWidget *scale_combo_row; + GtkWidget *underscanning_row; + GtkWidget *underscanning_switch; +}; + +typedef struct _CcDisplaySettings CcDisplaySettings; + +enum { + PROP_0, + PROP_HAS_ACCELEROMETER, + PROP_CONFIG, + PROP_SELECTED_OUTPUT, + PROP_LAST +}; + +G_DEFINE_TYPE (CcDisplaySettings, cc_display_settings, GTK_TYPE_BOX) + +static GParamSpec *props[PROP_LAST]; + +static void on_scale_btn_active_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self); + + +static gboolean +should_show_rotation (CcDisplaySettings *self) +{ + gboolean supports_rotation; + + supports_rotation = cc_display_monitor_supports_rotation (self->selected_output, + CC_DISPLAY_ROTATION_90 | + CC_DISPLAY_ROTATION_180 | + CC_DISPLAY_ROTATION_270); + + /* Doesn't support rotation at all */ + if (!supports_rotation) + return FALSE; + + /* We can always rotate displays that aren't builtin */ + if (!cc_display_monitor_is_builtin (self->selected_output)) + return TRUE; + + /* Only offer rotation if there's no accelerometer */ + return !self->has_accelerometer; +} + +static const gchar * +string_for_rotation (CcDisplayRotation rotation) +{ + switch (rotation) + { + case CC_DISPLAY_ROTATION_NONE: + case CC_DISPLAY_ROTATION_180_FLIPPED: + return C_("Display rotation", "Landscape"); + case CC_DISPLAY_ROTATION_90: + case CC_DISPLAY_ROTATION_270_FLIPPED: + return C_("Display rotation", "Portrait Right"); + case CC_DISPLAY_ROTATION_270: + case CC_DISPLAY_ROTATION_90_FLIPPED: + return C_("Display rotation", "Portrait Left"); + case CC_DISPLAY_ROTATION_180: + case CC_DISPLAY_ROTATION_FLIPPED: + return C_("Display rotation", "Landscape (flipped)"); + } + return ""; +} + +static const gchar * +make_aspect_string (gint width, + gint height) +{ + int ratio; + const gchar *aspect = NULL; + + /* We use a number of Unicode characters below: + * ∶ is U+2236 RATIO + * is U+2009 THIN SPACE, + * × is U+00D7 MULTIPLICATION SIGN + */ + if (width && height) { + if (width > height) + ratio = width * 10 / height; + else + ratio = height * 10 / width; + + switch (ratio) { + case 13: + aspect = "4∶3"; + break; + case 16: + aspect = "16∶10"; + break; + case 17: + aspect = "16∶9"; + break; + case 23: + aspect = "21∶9"; + break; + case 35: + aspect = "32∶9"; + break; + case 12: + aspect = "5∶4"; + break; + /* This catches 1.5625 as well (1600x1024) when maybe it shouldn't. */ + case 15: + aspect = "3∶2"; + break; + case 18: + aspect = "9∶5"; + break; + case 10: + aspect = "1∶1"; + break; + } + } + + return aspect; +} + +static gchar * +make_refresh_rate_string (CcDisplayMode *mode) +{ + return g_strdup_printf (_("%.2lf Hz"), cc_display_mode_get_freq_f (mode)); +} + +static gchar * +make_resolution_string (CcDisplayMode *mode) +{ + const char *interlaced; + const char *aspect; + int width, height; + + cc_display_mode_get_resolution (mode, &width, &height); + aspect = make_aspect_string (width, height); + interlaced = cc_display_mode_is_interlaced (mode) ? "i" : ""; + + if (aspect != NULL) + return g_strdup_printf ("%d × %d%s (%s)", width, height, interlaced, aspect); + else + return g_strdup_printf ("%d × %d%s", width, height, interlaced); +} + +static double +round_scale_for_ui (double scale) +{ + /* Keep in sync with mutter */ + return round (scale*4)/4; +} + +static gchar * +make_scale_string (gdouble scale) +{ + return g_strdup_printf ("%d %%", (int) (round_scale_for_ui (scale)*100)); +} + +static gint +sort_modes_by_area_desc (CcDisplayMode *a, CcDisplayMode *b) +{ + gint wa, ha, wb, hb; + gint res; + + cc_display_mode_get_resolution (a, &wa, &ha); + cc_display_mode_get_resolution (b, &wb, &hb); + + /* Sort first by width, then height. + * We used to sort by area, but that can be confusing. */ + res = wb - wa; + if (res) + return res; + return hb - ha; +} + +static gint +sort_modes_by_freq_desc (CcDisplayMode *a, CcDisplayMode *b) +{ + double delta = (cc_display_mode_get_freq_f (b) - cc_display_mode_get_freq_f (a))*1000.; + return delta; +} + +static gboolean +cc_display_settings_rebuild_ui (CcDisplaySettings *self) +{ + GtkWidget *child; + g_autolist(CcDisplayMode) clone_modes = NULL; + GList *modes; + GList *item; + gint width, height; + CcDisplayMode *current_mode; + GtkToggleButton *group = NULL; + g_autoptr(GArray) scales = NULL; + gint i; + + self->idle_udpate_id = 0; + + if (!self->config || !self->selected_output) + { + gtk_widget_set_visible (self->enabled_listbox, FALSE); + gtk_widget_set_visible (self->orientation_row, FALSE); + gtk_widget_set_visible (self->refresh_rate_row, FALSE); + gtk_widget_set_visible (self->resolution_row, FALSE); + gtk_widget_set_visible (self->scale_combo_row, FALSE); + gtk_widget_set_visible (self->scale_buttons_row, FALSE); + gtk_widget_set_visible (self->underscanning_row, FALSE); + + return G_SOURCE_REMOVE; + } + + g_object_freeze_notify ((GObject*) self->enabled_switch); + g_object_freeze_notify ((GObject*) self->orientation_row); + g_object_freeze_notify ((GObject*) self->refresh_rate_row); + g_object_freeze_notify ((GObject*) self->resolution_row); + g_object_freeze_notify ((GObject*) self->scale_combo_row); + g_object_freeze_notify ((GObject*) self->underscanning_switch); + + cc_display_monitor_get_geometry (self->selected_output, NULL, NULL, &width, &height); + + /* Selecte the first mode we can find if the monitor is disabled. */ + current_mode = cc_display_monitor_get_mode (self->selected_output); + if (current_mode == NULL) + current_mode = cc_display_monitor_get_preferred_mode (self->selected_output); + if (current_mode == NULL) { + modes = cc_display_monitor_get_modes (self->selected_output); + /* Lets assume that a monitor always has at least one mode. */ + g_assert (modes); + current_mode = CC_DISPLAY_MODE (modes->data); + } + + /* Enabled Switch */ + adw_preferences_row_set_title (ADW_PREFERENCES_ROW (self->enabled_row), + cc_display_monitor_get_ui_name (self->selected_output)); + gtk_switch_set_active (GTK_SWITCH (self->enabled_switch), + cc_display_monitor_is_active (self->selected_output)); + + if (should_show_rotation (self)) + { + guint i; + CcDisplayRotation rotations[] = { CC_DISPLAY_ROTATION_NONE, + CC_DISPLAY_ROTATION_90, + CC_DISPLAY_ROTATION_270, + CC_DISPLAY_ROTATION_180 }; + + gtk_widget_set_visible (self->orientation_row, TRUE); + + gtk_string_list_splice (GTK_STRING_LIST (self->orientation_list), + 0, + g_list_model_get_n_items (self->orientation_list), + NULL); + for (i = 0; i < G_N_ELEMENTS (rotations); i++) + { + g_autoptr(GObject) obj = NULL; + + if (!cc_display_monitor_supports_rotation (self->selected_output, rotations[i])) + continue; + + gtk_string_list_append (GTK_STRING_LIST (self->orientation_list), + string_for_rotation (rotations[i])); + obj = g_list_model_get_item (self->orientation_list, i); + g_object_set_data (G_OBJECT (obj), "rotation-value", GINT_TO_POINTER (rotations[i])); + + if (cc_display_monitor_get_rotation (self->selected_output) == rotations[i]) + adw_combo_row_set_selected (ADW_COMBO_ROW (self->orientation_row), + g_list_model_get_n_items (G_LIST_MODEL (self->orientation_list)) - 1); + } + } + else + { + gtk_widget_set_visible (self->orientation_row, FALSE); + } + + /* Only show refresh rate if we are not in cloning mode. */ + if (!cc_display_config_is_cloning (self->config)) + { + GList *item; + gdouble freq; + + freq = cc_display_mode_get_freq_f (current_mode); + + modes = cc_display_monitor_get_modes (self->selected_output); + + g_list_store_remove_all (self->refresh_rate_list); + + for (item = modes; item != NULL; item = item->next) + { + gint w, h; + guint new; + CcDisplayMode *mode = CC_DISPLAY_MODE (item->data); + + cc_display_mode_get_resolution (mode, &w, &h); + if (w != width || h != height) + continue; + + /* At some point we used to filter very close resolutions, + * but we don't anymore these days. + */ + new = g_list_store_insert_sorted (self->refresh_rate_list, + mode, + (GCompareDataFunc) sort_modes_by_freq_desc, + NULL); + if (freq == cc_display_mode_get_freq_f (mode)) + adw_combo_row_set_selected (ADW_COMBO_ROW (self->refresh_rate_row), new); + } + + gtk_widget_set_visible (self->refresh_rate_row, TRUE); + } + else + { + gtk_widget_set_visible (self->refresh_rate_row, FALSE); + } + + + /* Resolutions are always shown. */ + gtk_widget_set_visible (self->resolution_row, TRUE); + if (cc_display_config_is_cloning (self->config)) + { + clone_modes = cc_display_config_generate_cloning_modes (self->config); + modes = clone_modes; + } + else + { + modes = cc_display_monitor_get_modes (self->selected_output); + } + + g_list_store_remove_all (self->resolution_list); + g_list_store_append (self->resolution_list, current_mode); + adw_combo_row_set_selected (ADW_COMBO_ROW (self->resolution_row), 0); + for (item = modes; item != NULL; item = item->next) + { + gint ins; + gint w, h; + CcDisplayMode *mode = CC_DISPLAY_MODE (item->data); + + cc_display_mode_get_resolution (mode, &w, &h); + + /* Find the appropriate insertion point. */ + for (ins = 0; ins < g_list_model_get_n_items (G_LIST_MODEL (self->resolution_list)); ins++) + { + g_autoptr(CcDisplayMode) m = NULL; + gint cmp; + + m = g_list_model_get_item (G_LIST_MODEL (self->resolution_list), ins); + + cmp = sort_modes_by_area_desc (mode, m); + /* Next item is smaller, insert at this point. */ + if (cmp < 0) + break; + + /* Don't insert if it is already in the list */ + if (cmp == 0) + { + ins = -1; + break; + } + } + + if (ins >= 0) + g_list_store_insert (self->resolution_list, ins, mode); + } + + + /* Scale row is usually shown. */ + while ((child = gtk_widget_get_first_child (self->scale_bbox)) != NULL) + gtk_box_remove (GTK_BOX (self->scale_bbox), child); + + gtk_string_list_splice (GTK_STRING_LIST (self->scale_list), + 0, + g_list_model_get_n_items (self->scale_list), + NULL); + scales = cc_display_mode_get_supported_scales (current_mode); + self->num_scales = scales->len; + for (i = 0; i < scales->len; i++) + { + g_autofree gchar *scale_str = NULL; + g_autoptr(GObject) value_object = NULL; + double scale = g_array_index (scales, double, i); + GtkWidget *scale_btn; + gboolean is_selected; + + /* ComboRow */ + scale_str = make_scale_string (scale); + is_selected = G_APPROX_VALUE (cc_display_monitor_get_scale (self->selected_output), + scale, DBL_EPSILON); + + gtk_string_list_append (GTK_STRING_LIST (self->scale_list), scale_str); + value_object = g_list_model_get_item (self->scale_list, i); + g_object_set_data_full (G_OBJECT (value_object), "scale", + g_memdup2 (&scale, sizeof (double)), g_free); + if (is_selected) + adw_combo_row_set_selected (ADW_COMBO_ROW (self->scale_combo_row), + g_list_model_get_n_items (G_LIST_MODEL (self->scale_list)) - 1); + + /* ButtonBox */ + scale_btn = gtk_toggle_button_new_with_label (scale_str); + gtk_toggle_button_set_group (GTK_TOGGLE_BUTTON (scale_btn), group); + g_object_set_data_full (G_OBJECT (scale_btn), "scale", + g_memdup2 (&scale, sizeof (double)), g_free); + + if (!group) + group = GTK_TOGGLE_BUTTON (scale_btn); + gtk_box_append (GTK_BOX (self->scale_bbox), scale_btn); + /* Set active before connecting the signal */ + if (is_selected) + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (scale_btn), TRUE); + + g_signal_connect_object (scale_btn, + "notify::active", + G_CALLBACK (on_scale_btn_active_changed_cb), + self, 0); + } + cc_display_settings_refresh_layout (self, self->folded); + + gtk_widget_set_visible (self->underscanning_row, + cc_display_monitor_supports_underscanning (self->selected_output) && + !cc_display_config_is_cloning (self->config)); + gtk_switch_set_active (GTK_SWITCH (self->underscanning_switch), + cc_display_monitor_get_underscanning (self->selected_output)); + + self->updating = TRUE; + g_object_thaw_notify ((GObject*) self->enabled_switch); + g_object_thaw_notify ((GObject*) self->orientation_row); + g_object_thaw_notify ((GObject*) self->refresh_rate_row); + g_object_thaw_notify ((GObject*) self->resolution_row); + g_object_thaw_notify ((GObject*) self->scale_combo_row); + g_object_thaw_notify ((GObject*) self->underscanning_switch); + self->updating = FALSE; + + return G_SOURCE_REMOVE; +} + +static void +on_output_changed_cb (CcDisplaySettings *self, + GParamSpec *pspec, + CcDisplayMonitor *output) +{ + /* Do this frmo an idle handler, because otherwise we may create an + * infinite loop triggering the notify::selected-index from the + * combo rows. */ + if (self->idle_udpate_id) + return; + + self->idle_udpate_id = g_idle_add ((GSourceFunc) cc_display_settings_rebuild_ui, self); +} + +static void +on_enabled_switch_active_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + if (self->updating) + return; + + cc_display_monitor_set_active (self->selected_output, + gtk_switch_get_active (self->enabled_switch)); + + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static void +on_orientation_selection_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + gint idx; + g_autoptr(GObject) obj = NULL; + + if (self->updating) + return; + + idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->orientation_row)); + obj = g_list_model_get_item (G_LIST_MODEL (self->orientation_list), idx); + + if (!obj) + return; + + cc_display_monitor_set_rotation (self->selected_output, + GPOINTER_TO_INT (g_object_get_data (G_OBJECT (obj), "rotation-value"))); + + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static void +on_refresh_rate_selection_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + gint idx; + g_autoptr(CcDisplayMode) mode = NULL; + + if (self->updating) + return; + + idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->refresh_rate_row)); + mode = g_list_model_get_item (G_LIST_MODEL (self->refresh_rate_list), idx); + + if (!mode) + return; + + cc_display_monitor_set_mode (self->selected_output, mode); + + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static void +on_resolution_selection_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + gint idx; + g_autoptr(CcDisplayMode) mode = NULL; + + if (self->updating) + return; + + idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->resolution_row)); + mode = g_list_model_get_item (G_LIST_MODEL (self->resolution_list), idx); + + if (!mode) + return; + + /* This is the only row that can be changed when in cloning mode. */ + if (!cc_display_config_is_cloning (self->config)) + cc_display_monitor_set_mode (self->selected_output, mode); + else + cc_display_config_set_mode_on_all_outputs (self->config, mode); + + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static void +on_scale_btn_active_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + gdouble scale; + if (self->updating) + return; + + if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + return; + + scale = *(gdouble*) g_object_get_data (G_OBJECT (widget), "scale"); + cc_display_monitor_set_scale (self->selected_output, + scale); + + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static void +on_scale_selection_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + int idx; + double scale; + g_autoptr(GObject) obj = NULL; + + if (self->updating) + return; + + idx = adw_combo_row_get_selected (ADW_COMBO_ROW (self->scale_combo_row)); + obj = g_list_model_get_item (G_LIST_MODEL (self->scale_list), idx); + if (!obj) + return; + scale = *(gdouble*) g_object_get_data (G_OBJECT (obj), "scale"); + + cc_display_monitor_set_scale (self->selected_output, scale); + + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static void +on_underscanning_switch_active_changed_cb (GtkWidget *widget, + GParamSpec *pspec, + CcDisplaySettings *self) +{ + if (self->updating) + return; + + cc_display_monitor_set_underscanning (self->selected_output, + gtk_switch_get_active (GTK_SWITCH (self->underscanning_switch))); + + g_signal_emit_by_name (G_OBJECT (self), "updated", self->selected_output); +} + +static void +cc_display_settings_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + CcDisplaySettings *self = CC_DISPLAY_SETTINGS (object); + + switch (prop_id) + { + case PROP_HAS_ACCELEROMETER: + g_value_set_boolean (value, cc_display_settings_get_has_accelerometer (self)); + break; + + case PROP_CONFIG: + g_value_set_object (value, self->config); + break; + + case PROP_SELECTED_OUTPUT: + g_value_set_object (value, self->selected_output); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_display_settings_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + CcDisplaySettings *self = CC_DISPLAY_SETTINGS (object); + + switch (prop_id) + { + case PROP_HAS_ACCELEROMETER: + cc_display_settings_set_has_accelerometer (self, g_value_get_boolean (value)); + break; + + case PROP_CONFIG: + cc_display_settings_set_config (self, g_value_get_object (value)); + break; + + case PROP_SELECTED_OUTPUT: + cc_display_settings_set_selected_output (self, g_value_get_object (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +cc_display_settings_finalize (GObject *object) +{ + CcDisplaySettings *self = CC_DISPLAY_SETTINGS (object); + + g_clear_object (&self->config); + + g_clear_object (&self->orientation_list); + g_clear_object (&self->refresh_rate_list); + g_clear_object (&self->resolution_list); + g_clear_object (&self->scale_list); + + if (self->idle_udpate_id) + g_source_remove (self->idle_udpate_id); + self->idle_udpate_id = 0; + + G_OBJECT_CLASS (cc_display_settings_parent_class)->finalize (object); +} + +static void +cc_display_settings_class_init (CcDisplaySettingsClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + gobject_class->finalize = cc_display_settings_finalize; + gobject_class->get_property = cc_display_settings_get_property; + gobject_class->set_property = cc_display_settings_set_property; + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/display/cc-display-settings.ui"); + + props[PROP_HAS_ACCELEROMETER] = + g_param_spec_boolean ("has-accelerometer", "Has Accelerometer", + "If an accelerometre is available for the builtin display", + FALSE, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + props[PROP_CONFIG] = + g_param_spec_object ("config", "Display Config", + "The display configuration to work with", + CC_TYPE_DISPLAY_CONFIG, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + props[PROP_SELECTED_OUTPUT] = + g_param_spec_object ("selected-output", "Selected Output", + "The output that is currently selected on the configuration", + CC_TYPE_DISPLAY_MONITOR, + G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (gobject_class, + PROP_LAST, + props); + + g_signal_new ("updated", + CC_TYPE_DISPLAY_SETTINGS, + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 1, CC_TYPE_DISPLAY_MONITOR); + + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, enabled_listbox); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, enabled_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, enabled_switch); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, orientation_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, refresh_rate_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, resolution_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_bbox); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_buttons_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, scale_combo_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, underscanning_row); + gtk_widget_class_bind_template_child (widget_class, CcDisplaySettings, underscanning_switch); + + gtk_widget_class_bind_template_callback (widget_class, on_enabled_switch_active_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_orientation_selection_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_refresh_rate_selection_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_resolution_selection_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_scale_selection_changed_cb); + gtk_widget_class_bind_template_callback (widget_class, on_underscanning_switch_active_changed_cb); +} + +static void +cc_display_settings_init (CcDisplaySettings *self) +{ + GtkExpression *expression; + + gtk_widget_init_template (GTK_WIDGET (self)); + + self->orientation_list = G_LIST_MODEL (gtk_string_list_new (NULL)); + self->refresh_rate_list = g_list_store_new (CC_TYPE_DISPLAY_MODE); + self->resolution_list = g_list_store_new (CC_TYPE_DISPLAY_MODE); + self->scale_list = G_LIST_MODEL (gtk_string_list_new (NULL)); + + self->updating = TRUE; + + adw_combo_row_set_model (ADW_COMBO_ROW (self->orientation_row), + G_LIST_MODEL (self->orientation_list)); + adw_combo_row_set_model (ADW_COMBO_ROW (self->scale_combo_row), + G_LIST_MODEL (self->scale_list)); + + expression = gtk_cclosure_expression_new (G_TYPE_STRING, + NULL, 0, NULL, + G_CALLBACK (make_refresh_rate_string), + self, NULL); + adw_combo_row_set_expression (ADW_COMBO_ROW (self->refresh_rate_row), expression); + adw_combo_row_set_model (ADW_COMBO_ROW (self->refresh_rate_row), + G_LIST_MODEL (self->refresh_rate_list)); + + expression = gtk_cclosure_expression_new (G_TYPE_STRING, + NULL, 0, NULL, + G_CALLBACK (make_resolution_string), + self, NULL); + adw_combo_row_set_expression (ADW_COMBO_ROW (self->resolution_row), expression); + adw_combo_row_set_model (ADW_COMBO_ROW (self->resolution_row), + G_LIST_MODEL (self->resolution_list)); + + self->updating = FALSE; +} + +CcDisplaySettings* +cc_display_settings_new (void) +{ + return g_object_new (CC_TYPE_DISPLAY_SETTINGS, + NULL); +} + +gboolean +cc_display_settings_get_has_accelerometer (CcDisplaySettings *settings) +{ + return settings->has_accelerometer; +} + +void +cc_display_settings_set_has_accelerometer (CcDisplaySettings *self, + gboolean has_accelerometer) +{ + self->has_accelerometer = has_accelerometer; + + cc_display_settings_rebuild_ui (self); + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONFIG]); +} + +CcDisplayConfig* +cc_display_settings_get_config (CcDisplaySettings *self) +{ + return self->config; +} + +void +cc_display_settings_set_config (CcDisplaySettings *self, + CcDisplayConfig *config) +{ + const gchar *signals[] = { "rotation", "mode", "scale", "is-usable", "active" }; + GList *outputs, *l; + guint i; + + if (self->config) + { + outputs = cc_display_config_get_monitors (self->config); + for (l = outputs; l; l = l->next) + { + CcDisplayMonitor *output = l->data; + + g_signal_handlers_disconnect_by_data (output, self); + } + } + g_clear_object (&self->config); + + self->config = g_object_ref (config); + + /* Listen to all the signals */ + if (self->config) + { + outputs = cc_display_config_get_monitors (self->config); + for (l = outputs; l; l = l->next) + { + CcDisplayMonitor *output = l->data; + + for (i = 0; i < G_N_ELEMENTS (signals); ++i) + g_signal_connect_object (output, signals[i], G_CALLBACK (on_output_changed_cb), self, G_CONNECT_SWAPPED); + } + } + + cc_display_settings_set_selected_output (self, NULL); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_CONFIG]); +} + +CcDisplayMonitor* +cc_display_settings_get_selected_output (CcDisplaySettings *self) +{ + return self->selected_output; +} + +void +cc_display_settings_set_selected_output (CcDisplaySettings *self, + CcDisplayMonitor *output) +{ + self->selected_output = output; + + cc_display_settings_rebuild_ui (self); + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTED_OUTPUT]); +} + +void +cc_display_settings_refresh_layout (CcDisplaySettings *self, + gboolean folded) +{ + gboolean use_combo; + + self->folded = folded; + use_combo = self->num_scales > MAX_SCALE_BUTTONS || (self->num_scales > 2 && folded); + + gtk_widget_set_visible (self->scale_combo_row, use_combo); + gtk_widget_set_visible (self->scale_buttons_row, self->num_scales > 1 && !use_combo); +} + +void +cc_display_settings_set_multimonitor (CcDisplaySettings *self, + gboolean multimonitor) +{ + gtk_widget_set_visible (self->enabled_listbox, multimonitor); + + if (!multimonitor) + gtk_switch_set_active (GTK_SWITCH (self->enabled_switch), TRUE); +} |