summaryrefslogtreecommitdiffstats
path: root/panels/display/cc-display-settings.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:45:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:45:20 +0000
commitae1c76ff830d146d41e88d6fba724c0a54bce868 (patch)
tree3c354bec95af07be35fc71a4b738268496f1a1c4 /panels/display/cc-display-settings.c
parentInitial commit. (diff)
downloadgnome-control-center-ae1c76ff830d146d41e88d6fba724c0a54bce868.tar.xz
gnome-control-center-ae1c76ff830d146d41e88d6fba724c0a54bce868.zip
Adding upstream version 1:43.6.upstream/1%43.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--panels/display/cc-display-settings.c919
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);
+}