[gnome-control-center/wip/benzea/display-settings-widget: 6/14] display: Add settings widget for output configuration
- From: Benjamin Berg <bberg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/benzea/display-settings-widget: 6/14] display: Add settings widget for output configuration
- Date: Sat, 19 Jan 2019 23:11:23 +0000 (UTC)
commit e0238c253a654868485d5ded678166e41163eddf
Author: Benjamin Berg <bberg redhat com>
Date: Thu Jan 17 16:10:51 2019 +0100
display: Add settings widget for output configuration
This widget now uses libhandy for the rows.
panels/display/cc-display-settings.c | 765 ++++++++++++++++++++++++++++++++++
panels/display/cc-display-settings.h | 39 ++
panels/display/cc-display-settings.ui | 64 +++
panels/display/cc-value-object.c | 155 +++++++
panels/display/cc-value-object.h | 21 +
panels/display/display.gresource.xml | 1 +
panels/display/meson.build | 5 +-
7 files changed, 1049 insertions(+), 1 deletion(-)
---
diff --git a/panels/display/cc-display-settings.c b/panels/display/cc-display-settings.c
new file mode 100644
index 000000000..582481376
--- /dev/null
+++ b/panels/display/cc-display-settings.c
@@ -0,0 +1,765 @@
+/* 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/>.
+ */
+
+#define HANDY_USE_UNSTABLE_API 1
+#include <handy.h>
+#include <glib/gi18n.h>
+#include <math.h>
+#include "list-box-helper.h"
+#include "cc-display-settings.h"
+#include "cc-display-config.h"
+#include "cc-value-object.h"
+
+/* The minimum supported size a monitor may have */
+#define MINIMUM_WIDTH 740
+#define MINIMUM_HEIGHT 530
+
+struct _CcDisplaySettings
+{
+ GtkDrawingArea object;
+
+ gboolean updating;
+ guint idle_udpate_id;
+
+ gboolean has_accelerometer;
+ CcDisplayConfig *config;
+ CcDisplayMonitor *selected_output;
+
+ GListStore *orientation_list;
+ GListStore *refresh_rate_list;
+ GListStore *resolution_list;
+ GListStore *scale_list;
+
+ GtkWidget *orientation_row;
+ GtkWidget *refresh_rate_row;
+ GtkWidget *resolution_row;
+ GtkWidget *scale_bbox;
+ GtkWidget *scale_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_LIST_BOX)
+
+static GParamSpec *props[PROP_LAST];
+
+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 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 char *
+make_resolution_string (CcDisplayMode *mode)
+{
+ const char *interlaced = cc_display_mode_is_interlaced (mode) ? "i" : "";
+ const char *aspect;
+ int width, height;
+
+ cc_display_mode_get_resolution (mode, &width, &height);
+ aspect = make_aspect_string (width, height);
+
+ 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 gchar *
+get_frequency_string (CcDisplayMode *mode)
+{
+ return g_strdup_printf (_("%.2lf Hz"), cc_display_mode_get_freq_f (mode));
+}
+
+static gboolean
+display_mode_supported_at_scale (CcDisplayMode *mode, double scale)
+{
+ int width, height;
+
+ cc_display_mode_get_resolution (mode, &width, &height);
+
+ return round (width / scale) >= MINIMUM_WIDTH && round (height / scale) >= MINIMUM_HEIGHT;
+}
+
+static double
+round_scale_for_ui (double scale)
+{
+ /* Keep in sync with mutter */
+ return round (scale*4)/4;
+}
+
+static gchar *
+get_scale_string (CcValueObject *obj)
+{
+ gdouble scale = g_value_get_double (cc_value_object_get_value (obj));
+
+ 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);
+
+ /* Prefer wide screen if the size is equal */
+ res = wb*hb - wa*ha;
+ if (res == 0)
+ return wb - wa;
+ return res;
+}
+
+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)
+{
+ GList *modes;
+ GList *item;
+ gint width, height;
+
+ self->idle_udpate_id = 0;
+
+ if (!self->selected_output)
+ {
+ 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_row, FALSE);
+ gtk_widget_set_visible (self->underscanning_row, FALSE);
+
+ return G_SOURCE_REMOVE;
+ }
+
+ 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_row);
+ g_object_freeze_notify ((GObject*) self->underscanning_switch);
+
+ cc_display_monitor_get_geometry (self->selected_output, NULL, NULL, &width, &height);
+
+ 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);
+
+ g_list_store_remove_all (self->orientation_list);
+ for (i = 0; i < G_N_ELEMENTS (rotations); i++)
+ {
+ CcValueObject *obj;
+
+ if (!cc_display_monitor_supports_rotation (self->selected_output, rotations[i]))
+ continue;
+
+ obj = cc_value_object_new_collect (G_TYPE_STRING, string_for_rotation (rotations[i]));
+ g_list_store_append (self->orientation_list, obj);
+ 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])
+ hdy_combo_row_set_selected_index (HDY_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))
+ {
+ CcDisplayMode *current_mode;
+ GList *item;
+ gdouble freq;
+
+ current_mode = cc_display_monitor_get_mode (self->selected_output);
+ 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))
+ hdy_combo_row_set_selected_index (HDY_COMBO_ROW (self->refresh_rate_row), new);
+ }
+
+ /* Show if we have more than one frequency to choose from. */
+ gtk_widget_set_visible (self->refresh_rate_row,
+ g_list_model_get_n_items (G_LIST_MODEL (self->refresh_rate_list)) > 1);
+ }
+ 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))
+ modes = cc_display_config_get_cloning_modes (self->config);
+ 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,
+ cc_display_monitor_get_mode (self->selected_output));
+ hdy_combo_row_set_selected_index (HDY_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);
+
+ /* Exclude unusable low resolutions */
+ if (!display_mode_supported_at_scale (mode, 1.0))
+ continue;
+
+ 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++)
+ {
+ CcDisplayMode *m;
+ 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);
+ }
+
+
+ /* Update scale row. */
+ g_list_store_remove_all (self->scale_list);
+ if (!cc_display_config_is_cloning (self->config))
+ {
+ CcDisplayMode *mode = cc_display_monitor_get_mode (self->selected_output);
+ const gdouble *scales, *scale;
+
+ scales = cc_display_mode_get_supported_scales (mode);
+ for (scale = scales; *scale != 0.0; scale++)
+ {
+ CcValueObject *obj;
+ if (!display_mode_supported_at_scale (mode, *scale) &&
+ cc_display_monitor_get_scale (self->selected_output) != *scale)
+ continue;
+
+ obj = cc_value_object_new_collect (G_TYPE_DOUBLE, *scale);
+ g_list_store_append (self->scale_list, obj);
+
+ if (cc_display_monitor_get_scale (self->selected_output) == *scale)
+ hdy_combo_row_set_selected_index (HDY_COMBO_ROW (self->scale_row),
+ g_list_model_get_n_items (G_LIST_MODEL (self->scale_list)) -
1);
+ }
+
+ gtk_widget_set_visible (self->scale_row, g_list_model_get_n_items (G_LIST_MODEL (self->scale_list)) >
1);
+ }
+ else
+ {
+ gtk_widget_set_visible (self->scale_row, FALSE);
+ }
+
+ 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->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_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_orientation_selection_changed_cb (GtkWidget *widget,
+ GParamSpec *pspec,
+ CcDisplaySettings *self)
+{
+ gint idx;
+ CcValueObject *obj;
+
+ if (self->updating)
+ return;
+
+ idx = hdy_combo_row_get_selected_index (HDY_COMBO_ROW (self->orientation_row));
+ obj = g_list_model_get_item (G_LIST_MODEL (self->orientation_list), idx);
+
+ 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;
+ CcDisplayMode *mode;
+
+ if (self->updating)
+ return;
+
+ idx = hdy_combo_row_get_selected_index (HDY_COMBO_ROW (self->refresh_rate_row));
+ mode = g_list_model_get_item (G_LIST_MODEL (self->refresh_rate_list), idx);
+
+ 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;
+ CcDisplayMode *mode;
+
+ if (self->updating)
+ return;
+
+ idx = hdy_combo_row_get_selected_index (HDY_COMBO_ROW (self->resolution_row));
+ mode = g_list_model_get_item (G_LIST_MODEL (self->resolution_list), idx);
+
+ /* 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_selection_changed_cb (GtkWidget *widget,
+ GParamSpec *pspec,
+ CcDisplaySettings *self)
+{
+ gint idx;
+ CcValueObject *obj;
+
+ if (self->updating)
+ return;
+
+ idx = hdy_combo_row_get_selected_index (HDY_COMBO_ROW (self->scale_row));
+ obj = g_list_model_get_item (G_LIST_MODEL (self->scale_list), idx);
+
+ cc_display_monitor_set_scale (self->selected_output,
+ g_value_get_double (cc_value_object_get_value (obj)));
+
+ 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_set_config (CcDisplaySettings *self,
+ CcDisplayConfig *config)
+{
+ const gchar *signals[] = { "rotation", "mode", "scale", "is-usable" };
+ GList *outputs, *l;
+ guint i;
+
+ g_assert (self->config == NULL);
+
+ 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]);
+}
+
+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, self->has_accelerometer);
+ 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:
+ self->has_accelerometer = 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;
+}
+
+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_CONSTRUCT_ONLY | 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_CONSTRUCT_ONLY | 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, 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_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_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)
+{
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ gtk_list_box_set_header_func (GTK_LIST_BOX (self),
+ cc_list_box_update_header_func,
+ NULL, NULL);
+
+ self->orientation_list = g_list_store_new (CC_TYPE_VALUE_OBJECT);
+ 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_store_new (CC_TYPE_VALUE_OBJECT);
+
+ self->updating = TRUE;
+
+ hdy_combo_row_bind_name_model (HDY_COMBO_ROW (self->orientation_row),
+ G_LIST_MODEL (self->orientation_list),
+ (HdyComboRowGetNameFunc) cc_value_object_dup_string,
+ NULL, NULL);
+ hdy_combo_row_bind_name_model (HDY_COMBO_ROW (self->refresh_rate_row),
+ G_LIST_MODEL (self->refresh_rate_list),
+ (HdyComboRowGetNameFunc) get_frequency_string,
+ NULL, NULL);
+ hdy_combo_row_bind_name_model (HDY_COMBO_ROW (self->resolution_row),
+ G_LIST_MODEL (self->resolution_list),
+ (HdyComboRowGetNameFunc) make_resolution_string,
+ NULL, NULL);
+ hdy_combo_row_bind_name_model (HDY_COMBO_ROW (self->scale_row),
+ G_LIST_MODEL (self->scale_list),
+ (HdyComboRowGetNameFunc) get_scale_string,
+ NULL, NULL);
+
+ self->updating = FALSE;
+}
+
+CcDisplaySettings*
+cc_display_settings_new (CcDisplayConfig *config,
+ gboolean has_accelerometer)
+{
+ return g_object_new (CC_TYPE_DISPLAY_SETTINGS,
+ "config", config,
+ "has-accelerometer", has_accelerometer,
+ NULL);
+}
+
+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]);
+}
+
diff --git a/panels/display/cc-display-settings.h b/panels/display/cc-display-settings.h
new file mode 100644
index 000000000..0910ccce3
--- /dev/null
+++ b/panels/display/cc-display-settings.h
@@ -0,0 +1,39 @@
+/* -*- mode: c; style: linux -*-
+ *
+ * Copyright (C) 2019 Red Hat, 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/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include "cc-display-config.h"
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_DISPLAY_SETTINGS cc_display_settings_get_type ()
+G_DECLARE_FINAL_TYPE (CcDisplaySettings, cc_display_settings, CC, DISPLAY_SETTINGS, GtkListBox);
+
+CcDisplaySettings* cc_display_settings_new (CcDisplayConfig *config,
+ gboolean has_accelerometer);
+
+CcDisplayMonitor* cc_display_settings_get_selected_output (CcDisplaySettings *settings);
+void cc_display_settings_set_selected_output (CcDisplaySettings *settings,
+ CcDisplayMonitor *output);
+
+G_END_DECLS
+
diff --git a/panels/display/cc-display-settings.ui b/panels/display/cc-display-settings.ui
new file mode 100644
index 000000000..14c5bdba4
--- /dev/null
+++ b/panels/display/cc-display-settings.ui
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.0 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <requires lib="libhandy" version="0.0"/>
+ <template class="CcDisplaySettings" parent="GtkListBox">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="selection_mode">none</property>
+ <child>
+ <object class="HdyComboRow" id="orientation_row">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="title">Orientation</property>
+ <signal name="notify::selected-index" handler="on_orientation_selection_changed_cb" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="HdyComboRow" id="resolution_row">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="title">Resolution</property>
+ <signal name="notify::selected-index" handler="on_resolution_selection_changed_cb" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="HdyComboRow" id="refresh_rate_row">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="title">Refresh Rate</property>
+ <signal name="notify::selected-index" handler="on_refresh_rate_selection_changed_cb" swapped="no"/>
+ </object>
+ </child>
+ <child>
+ <object class="HdyActionRow" id="underscanning_row">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="title">Adjust for TV</property>
+ <child type="action">
+ <object class="GtkSwitch" id="underscanning_switch">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="valign">center</property>
+ <signal name="notify::active" handler="on_underscanning_switch_active_changed_cb" swapped="no"/>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="HdyComboRow" id="scale_row">
+ <property name="width_request">100</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="title">Scale</property>
+ <signal name="notify::selected-index" handler="on_scale_selection_changed_cb" swapped="no"/>
+ </object>
+ </child>
+ </template>
+</interface>
diff --git a/panels/display/cc-value-object.c b/panels/display/cc-value-object.c
new file mode 100644
index 000000000..a0a8a3e4d
--- /dev/null
+++ b/panels/display/cc-value-object.c
@@ -0,0 +1,155 @@
+#include "cc-value-object.h"
+#include <gobject/gvaluecollector.h>
+
+struct _CcValueObject
+{
+ GObject parent_instance;
+
+ GValue value;
+};
+
+G_DEFINE_TYPE (CcValueObject, cc_value_object, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_VALUE,
+ N_PROPS
+};
+
+static GParamSpec *props [N_PROPS];
+
+CcValueObject *
+cc_value_object_new (const GValue *value)
+{
+ return g_object_new (CC_TYPE_VALUE_OBJECT,
+ "value", value,
+ NULL);
+}
+
+/**
+ * cc_value_object_new_collect(): (skip)
+ *
+ */
+CcValueObject*
+cc_value_object_new_collect (GType type, ...)
+{
+ GValue value = G_VALUE_INIT;
+ g_autofree gchar *error = NULL;
+ va_list var_args;
+
+ va_start (var_args, type);
+
+ G_VALUE_COLLECT_INIT (&value, type, var_args, 0, &error);
+
+ va_end (var_args);
+
+ if (error)
+ g_critical ("%s: %s", G_STRFUNC, error);
+
+ return g_object_new (CC_TYPE_VALUE_OBJECT,
+ "value", &value,
+ NULL);
+}
+
+static void
+cc_value_object_finalize (GObject *object)
+{
+ CcValueObject *self = (CcValueObject *)object;
+
+ g_value_unset (&self->value);
+
+ G_OBJECT_CLASS (cc_value_object_parent_class)->finalize (object);
+}
+
+static void
+cc_value_object_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ CcValueObject *self = CC_VALUE_OBJECT (object);
+
+ switch (prop_id)
+ {
+ case PROP_VALUE:
+ g_value_set_boxed (value, &self->value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+cc_value_object_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcValueObject *self = CC_VALUE_OBJECT (object);
+ GValue *real_value;
+
+ switch (prop_id)
+ {
+ case PROP_VALUE:
+ /* construct only */
+ real_value = g_value_get_boxed (value);
+ g_value_init (&self->value, real_value->g_type);
+ g_value_copy (real_value, &self->value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+cc_value_object_class_init (CcValueObjectClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = cc_value_object_finalize;
+ object_class->get_property = cc_value_object_get_property;
+ object_class->set_property = cc_value_object_set_property;
+
+ props[PROP_VALUE] =
+ g_param_spec_boxed ("value", "Value",
+ "The contained value",
+ G_TYPE_VALUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class,
+ N_PROPS,
+ props);
+}
+
+static void
+cc_value_object_init (CcValueObject *self)
+{
+}
+
+const GValue*
+cc_value_object_get_value (CcValueObject *self)
+{
+ return &self->value;
+}
+
+void
+cc_value_object_copy_value (CcValueObject *self,
+ GValue *dest_value)
+{
+ g_value_copy (&self->value, dest_value);
+}
+
+const gchar*
+cc_value_object_get_string (CcValueObject *self)
+{
+ return g_value_get_string (&self->value);
+}
+
+gchar*
+cc_value_object_dup_string (CcValueObject *self)
+{
+ return g_value_dup_string (&self->value);
+}
+
diff --git a/panels/display/cc-value-object.h b/panels/display/cc-value-object.h
new file mode 100644
index 000000000..392d93283
--- /dev/null
+++ b/panels/display/cc-value-object.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_VALUE_OBJECT (cc_value_object_get_type())
+
+G_DECLARE_FINAL_TYPE (CcValueObject, cc_value_object, CC, VALUE_OBJECT, GObject)
+
+CcValueObject *cc_value_object_new (const GValue *value);
+CcValueObject *cc_value_object_new_collect (GType type, ...);
+
+const GValue *cc_value_object_get_value (CcValueObject *self);
+void cc_value_object_copy_value (CcValueObject *self,
+ GValue *dest_value);
+
+const gchar *cc_value_object_get_string (CcValueObject *self);
+gchar *cc_value_object_dup_string (CcValueObject *self);
+
+G_END_DECLS
diff --git a/panels/display/display.gresource.xml b/panels/display/display.gresource.xml
index 5ecf8c596..70eedbaa9 100644
--- a/panels/display/display.gresource.xml
+++ b/panels/display/display.gresource.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/control-center/display">
+ <file preprocess="xml-stripblanks">cc-display-settings.ui</file>
<file preprocess="xml-stripblanks">cc-night-light-dialog.ui</file>
<file>display-arrangement.css</file>
<file>night-light.css</file>
diff --git a/panels/display/meson.build b/panels/display/meson.build
index bd4352b9b..8805f1f90 100644
--- a/panels/display/meson.build
+++ b/panels/display/meson.build
@@ -24,11 +24,14 @@ sources = files(
'cc-display-config-dbus.c',
'cc-display-config-manager-dbus.c',
'cc-display-config-manager.c',
+ 'cc-display-settings.c',
'cc-night-light-dialog.c',
+ 'cc-value-object.c',
)
resource_data = files(
- 'cc-night-light-dialog.ui'
+ 'cc-display-settings.ui',
+ 'cc-night-light-dialog.ui',
)
sources += gnome.compile_resources(
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]