/* cc-display-settings.c * * Copyright (C) 2007, 2008, 2018, 2019 Red Hat, Inc. * Copyright (C) 2013 Intel, Inc. * * Written by: Benjamin Berg * * 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 . */ #include #include #include #include #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); }