[gnome-control-center] display: Re-design the panel



commit c0f686bb0f357752f8ea112b866dadfe5ce0db03
Author: Rui Matos <tiagomatos gmail com>
Date:   Mon Aug 7 16:04:13 2017 +0200

    display: Re-design the panel
    
    This implements most of the new Display panel re-design by Allan
    Day.
    
    Left out for now is brightness setting which still is in the
    Power panel because the plumbing layers don't yet associate backlights
    with outputs.
    
    Also left out is the presentation mode and trimming of options due to
    hardware constraints since we don't have a mutter implementation for
    those yet.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=785949

 panels/display/cc-display-panel.c | 3131 +++++++++++++++++++++---------------
 1 files changed, 1826 insertions(+), 1305 deletions(-)
---
diff --git a/panels/display/cc-display-panel.c b/panels/display/cc-display-panel.c
index 4647258..a30388a 100644
--- a/panels/display/cc-display-panel.c
+++ b/panels/display/cc-display-panel.c
@@ -43,18 +43,11 @@ CC_PANEL_REGISTER (CcDisplayPanel, cc_display_panel)
 #define DISPLAY_PANEL_PRIVATE(o) \
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_DISPLAY_PANEL, CcDisplayPanelPrivate))
 
-#define WID(s) GTK_WIDGET (gtk_builder_get_object (self->priv->builder, s))
-
 #define TOP_BAR_HEIGHT 5
 
-/* The minimum supported size for the panel, see:
- * http://live.gnome.org/Design/SystemSettings */
-#define MINIMUM_WIDTH 740
-#define MINIMUM_HEIGHT 530
-
-
-#define DISPLAY_PREVIEW_SETUP_HEIGHT 140
-#define DISPLAY_PREVIEW_LIST_HEIGHT  55
+#define PANEL_PADDING   32
+#define SECTION_PADDING 32
+#define HEADING_PADDING 12
 
 enum
 {
@@ -77,19 +70,9 @@ struct _CcDisplayPanelPrivate
 
   guint           focus_id;
 
+  GtkSizeGroup *rows_size_group;
   GtkWidget *stack;
-  GtkWidget *displays_listbox;
-  GtkWidget *arrange_button;
-  GtkWidget *res_combo;
-  GtkWidget *freq_combo;
-  GtkWidget *scale_slider;
-  GHashTable *res_freqs;
-  GtkWidget *scaling_switch;
-  GtkWidget *rotate_left_button;
-  GtkWidget *upside_down_button;
-  GtkWidget *rotate_right_button;
   GtkWidget *dialog;
-  GtkWidget *config_grid;
 
   CcNightLightDialog *night_light_dialog;
   GSettings *settings_color;
@@ -103,6 +86,9 @@ struct _CcDisplayPanelPrivate
   guint       sensor_watch_id;
   GDBusProxy *iio_sensor_proxy;
   gboolean    has_accelerometer;
+
+  GtkWidget *main_titlebar;
+  GtkWidget *apply_titlebar;
 };
 
 typedef struct
@@ -113,15 +99,104 @@ typedef struct
   int output_y;
 } GrabInfo;
 
-static GHashTable *output_ids;
+enum
+{
+  CURRENT_OUTPUT,
+  LAST_PANEL_SIGNAL
+};
+static guint panel_signals[LAST_PANEL_SIGNAL] = { 0 };
 
-static gint
-cc_display_panel_get_output_id (CcDisplayMonitor *output)
+static const gchar *
+get_resolution_string (CcDisplayMode *mode);
+static const gchar *
+get_frequency_string (CcDisplayMode *mode);
+static GtkWidget *
+make_night_light_widget (CcDisplayPanel *panel);
+static gboolean
+should_show_rotation (CcDisplayPanel *panel,
+                      CcDisplayMonitor  *output);
+static void
+update_apply_button (CcDisplayPanel *panel);
+static void
+apply_current_configuration (CcDisplayPanel *self);
+static void
+on_area_paint (FooScrollArea  *area,
+               cairo_t        *cr,
+               gpointer        data);
+static char *
+make_display_size_string (int width_mm,
+                          int height_mm);
+static void
+realign_outputs_after_resolution_change (CcDisplayPanel    *self,
+                                         CcDisplayMonitor  *output_that_changed,
+                                         int                old_width,
+                                         int                old_height,
+                                         CcDisplayRotation  old_rotation);
+static void
+lay_out_outputs_horizontally (CcDisplayPanel *self);
+
+static char *
+make_output_ui_name (CcDisplayMonitor *output)
 {
-  if (output_ids)
-    return GPOINTER_TO_INT (g_hash_table_lookup (output_ids, output));
+  int width_mm, height_mm;
+  char *size, *name;
+
+  cc_display_monitor_get_physical_size (output, &width_mm, &height_mm);
+  size = make_display_size_string (width_mm, height_mm);
+  if (size)
+    name = g_strdup_printf ("%s (%s)", cc_display_monitor_get_display_name (output), size);
   else
-    return 0;
+    name = g_strdup_printf ("%s", cc_display_monitor_get_display_name (output));
+
+  g_free (size);
+  return name;
+}
+
+static void
+ensure_output_numbers (CcDisplayConfig *config)
+{
+  GList *outputs, *l;
+  GList *sorted = NULL;
+  gint n = 0;
+
+  outputs = cc_display_config_get_monitors (config);
+
+  for (l = outputs; l != NULL; l = l->next)
+    {
+      CcDisplayMonitor *output = l->data;
+      if (cc_display_monitor_is_builtin (output))
+        sorted = g_list_prepend (sorted, output);
+      else
+        sorted = g_list_append (sorted, output);
+    }
+
+  for (l = sorted; l != NULL; l = l->next)
+    {
+      CcDisplayMonitor *output = l->data;
+      gchar *ui_name = make_output_ui_name (output);
+
+      g_object_set_data (G_OBJECT (output), "ui-number", GINT_TO_POINTER (++n));
+      g_object_set_data_full (G_OBJECT (output), "ui-number-name",
+                              g_strdup_printf ("%d\u2003%s", n, ui_name),
+                              g_free);
+      g_object_set_data_full (G_OBJECT (output), "ui-name", ui_name, g_free);
+    }
+
+  g_object_set_data_full (G_OBJECT (config), "ui-sorted-outputs", sorted, (GDestroyNotify) g_list_free);
+}
+
+static void
+monitor_labeler_hide (CcDisplayPanel *self)
+{
+  CcDisplayPanelPrivate *priv = self->priv;
+
+  if (!priv->shell_proxy)
+    return;
+
+  g_dbus_proxy_call (priv->shell_proxy,
+                     "HideMonitorLabels",
+                     NULL, G_DBUS_CALL_FLAGS_NONE,
+                     -1, NULL, NULL, NULL);
 }
 
 static void
@@ -130,30 +205,26 @@ monitor_labeler_show (CcDisplayPanel *self)
   CcDisplayPanelPrivate *priv = self->priv;
   GList *outputs, *l;
   GVariantBuilder builder;
-  gint number;
-  gboolean has_outputs;
+  gint number = 0;
 
   if (!priv->shell_proxy || !priv->current_config)
     return;
 
-  has_outputs = FALSE;
+  if (cc_display_config_is_cloning (priv->current_config))
+    return monitor_labeler_hide (self);
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
+  g_variant_builder_open (&builder, G_VARIANT_TYPE_ARRAY);
 
-  outputs = cc_display_config_get_monitors (priv->current_config);
+  outputs = g_object_get_data (G_OBJECT (priv->current_config), "ui-sorted-outputs");
   for (l = outputs; l != NULL; l = l->next)
     {
       CcDisplayMonitor *output = l->data;
 
-      number = cc_display_panel_get_output_id (output);
+      number = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (output), "ui-number"));
       if (number == 0)
         continue;
 
-      if (!has_outputs)
-        {
-          g_variant_builder_init (&builder, G_VARIANT_TYPE_TUPLE);
-          g_variant_builder_open (&builder, G_VARIANT_TYPE_ARRAY);
-          has_outputs = TRUE;
-        }
-
       if (priv->have_new_dbus_api)
         g_variant_builder_add (&builder, "{sv}",
                                cc_display_monitor_get_connector_name (output),
@@ -164,11 +235,11 @@ monitor_labeler_show (CcDisplayPanel *self)
                                g_variant_new_int32 (number));
     }
 
-  if (!has_outputs)
-    return;
-
   g_variant_builder_close (&builder);
 
+  if (number < 2)
+    return monitor_labeler_hide (self);
+
   g_dbus_proxy_call (priv->shell_proxy,
                      priv->have_new_dbus_api ? "ShowMonitorLabels2" : "ShowMonitorLabels",
                      g_variant_builder_end (&builder),
@@ -177,20 +248,6 @@ monitor_labeler_show (CcDisplayPanel *self)
 }
 
 static void
-monitor_labeler_hide (CcDisplayPanel *self)
-{
-  CcDisplayPanelPrivate *priv = self->priv;
-
-  if (!priv->shell_proxy)
-    return;
-
-  g_dbus_proxy_call (priv->shell_proxy,
-                     "HideMonitorLabels",
-                     NULL, G_DBUS_CALL_FLAGS_NONE,
-                     -1, NULL, NULL, NULL);
-}
-
-static void
 ensure_monitor_labels (CcDisplayPanel *self)
 {
   GList *windows, *w;
@@ -213,12 +270,49 @@ ensure_monitor_labels (CcDisplayPanel *self)
 }
 
 static void
+dialog_toplevel_focus_changed (CcDisplayPanel *self)
+{
+  ensure_monitor_labels (self);
+}
+
+static void
+reset_titlebar (CcDisplayPanel *self)
+{
+  CcDisplayPanelPrivate *priv = self->priv;
+  GtkWidget *toplevel = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
+
+  if (priv->main_titlebar)
+    {
+      gtk_window_set_titlebar (GTK_WINDOW (toplevel), priv->main_titlebar);
+      g_clear_object (&priv->main_titlebar);
+    }
+
+  g_clear_object (&priv->apply_titlebar);
+}
+
+static void
+active_panel_changed (CcShell    *shell,
+                      GParamSpec *pspec,
+                      CcPanel    *self)
+{
+  CcPanel *panel = NULL;
+
+  g_object_get (shell, "active-panel", &panel, NULL);
+  if (panel != self)
+    reset_titlebar (CC_DISPLAY_PANEL (self));
+
+  g_object_unref (panel);
+}
+
+static void
 cc_display_panel_dispose (GObject *object)
 {
   CcDisplayPanelPrivate *priv = CC_DISPLAY_PANEL (object)->priv;
   CcShell *shell;
   GtkWidget *toplevel;
 
+  reset_titlebar (CC_DISPLAY_PANEL (object));
+
   if (priv->sensor_watch_id > 0)
     {
       g_bus_unwatch_name (priv->sensor_watch_id);
@@ -227,12 +321,6 @@ cc_display_panel_dispose (GObject *object)
 
   g_clear_object (&priv->iio_sensor_proxy);
 
-  if (output_ids)
-    {
-      g_hash_table_destroy (output_ids);
-      output_ids = NULL;
-    }
-
   if (priv->focus_id)
     {
       shell = cc_panel_get_shell (CC_PANEL (object));
@@ -265,6 +353,15 @@ cc_display_panel_dispose (GObject *object)
   G_OBJECT_CLASS (cc_display_panel_parent_class)->dispose (object);
 }
 
+static void
+cc_display_panel_constructed (GObject *object)
+{
+  g_signal_connect_object (cc_panel_get_shell (CC_PANEL (object)), "notify::active-panel",
+                           G_CALLBACK (active_panel_changed), object, 0);
+
+  G_OBJECT_CLASS (cc_display_panel_parent_class)->constructed (object);
+}
+
 static const char *
 cc_display_panel_get_help_uri (CcPanel *panel)
 {
@@ -281,21 +378,23 @@ cc_display_panel_class_init (CcDisplayPanelClass *klass)
 
   panel_class->get_help_uri = cc_display_panel_get_help_uri;
 
+  object_class->constructed = cc_display_panel_constructed;
   object_class->dispose = cc_display_panel_dispose;
+
+  panel_signals[CURRENT_OUTPUT] =
+    g_signal_new ("current-output",
+                  CC_TYPE_DISPLAY_PANEL,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
 }
 
-static gboolean
-should_show_resolution (gint output_width,
-                        gint output_height,
-                        gint width,
-                        gint height)
+static void
+set_current_output (CcDisplayPanel   *panel,
+                    CcDisplayMonitor *output)
 {
-  if (width >= MIN (output_width, MINIMUM_WIDTH) &&
-      height >= MIN (output_height, MINIMUM_HEIGHT))
-    {
-      return TRUE;
-    }
-  return FALSE;
+  panel->priv->current_output = output;
+  g_signal_emit (panel, panel_signals[CURRENT_OUTPUT], 0);
 }
 
 static void
@@ -446,170 +545,1627 @@ paint_output (CcDisplayPanel    *panel,
     }
 }
 
+static GtkWidget *
+make_bin (void)
+{
+  return g_object_new (GTK_TYPE_FRAME, "shadow-type", GTK_SHADOW_NONE, NULL);
+}
+
+static GtkWidget *
+wrap_in_boxes (GtkWidget *widget)
+{
+  GtkWidget *box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, PANEL_PADDING);
+  gtk_box_pack_start (GTK_BOX (box), make_bin(), TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), widget, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (box), make_bin(), TRUE, TRUE, 0);
+  return box;
+}
+
+static GtkWidget *
+make_scrollable (GtkWidget *widget)
+{
+  GtkWidget *sw;
+  sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+                     "hscrollbar-policy", GTK_POLICY_NEVER,
+                     "min-content-height", 450,
+                     "propagate-natural-height", TRUE,
+                     NULL);
+  gtk_container_add (GTK_CONTAINER (sw), wrap_in_boxes (widget));
+  return sw;
+}
+
+static GtkWidget *
+make_bold_label (const gchar *text)
+{
+  GtkCssProvider *provider;
+  GtkWidget *label = gtk_label_new (text);
+
+  provider = gtk_css_provider_new ();
+  gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider),
+                                   "label { font-weight: bold; }", -1, NULL);
+  gtk_style_context_add_provider (gtk_widget_get_style_context (label),
+                                  GTK_STYLE_PROVIDER (provider),
+                                  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+  g_object_unref (provider);
+
+  return label;
+}
+
+static GtkWidget *
+make_main_vbox (void)
+{
+  GtkWidget *vbox;
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+  gtk_widget_set_margin_top (vbox, PANEL_PADDING);
+  gtk_widget_set_margin_bottom (vbox, PANEL_PADDING);
+
+  return vbox;
+}
+
+static GtkWidget *
+make_row (GtkSizeGroup *size_group,
+          GtkWidget    *start_widget,
+          GtkWidget    *end_widget)
+{
+  GtkWidget *row, *box;
+
+  row = g_object_new (CC_TYPE_LIST_BOX_ROW, NULL);
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);
+  gtk_widget_set_margin_start (box, 20);
+  gtk_widget_set_margin_end (box, 20);
+  gtk_widget_set_margin_top (box, 20);
+  gtk_widget_set_margin_bottom (box, 20);
+
+  if (start_widget)
+    {
+      gtk_widget_set_halign (start_widget, GTK_ALIGN_START);
+      gtk_box_pack_start (GTK_BOX (box), start_widget, FALSE, FALSE, 0);
+    }
+  if (end_widget)
+    {
+      gtk_widget_set_halign (end_widget, GTK_ALIGN_END);
+      gtk_box_pack_end (GTK_BOX (box), end_widget, FALSE, FALSE, 0);
+    }
+
+  gtk_container_add (GTK_CONTAINER (row), box);
+
+  if (size_group)
+    gtk_size_group_add_widget (size_group, row);
+
+  return row;
+}
+
+static GtkWidget *
+make_frame (const gchar *title, const gchar *subtitle)
+{
+  GtkWidget *frame;
+
+  frame = gtk_frame_new (NULL);
+  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
+
+  if (title)
+    {
+      GtkWidget *vbox, *label;
+
+      vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, HEADING_PADDING/2);
+      gtk_widget_set_margin_bottom (vbox, HEADING_PADDING);
+
+      label = make_bold_label (title);
+      gtk_widget_set_halign (label, GTK_ALIGN_START);
+      gtk_container_add (GTK_CONTAINER (vbox), label);
+
+      if (subtitle)
+        {
+          label = gtk_label_new (subtitle);
+          gtk_widget_set_halign (label, GTK_ALIGN_START);
+          gtk_container_add (GTK_CONTAINER (vbox), label);
+          gtk_style_context_add_class (gtk_widget_get_style_context (label),
+                                       GTK_STYLE_CLASS_DIM_LABEL);
+        }
+
+      gtk_frame_set_label_widget (GTK_FRAME (frame), vbox);
+      gtk_frame_set_label_align (GTK_FRAME (frame), 0.0, 1.0);
+    }
+
+  return frame;
+}
+
+static GtkWidget *
+make_list_box (void)
+{
+  GtkWidget *listbox;
+
+  listbox = g_object_new (CC_TYPE_LIST_BOX, NULL);
+  gtk_list_box_set_selection_mode (GTK_LIST_BOX (listbox), GTK_SELECTION_NONE);
+  gtk_list_box_set_header_func (GTK_LIST_BOX (listbox),
+                                cc_list_box_update_header_func,
+                                NULL, NULL);
+  return listbox;
+}
+
+static GtkWidget *
+make_dialog (CcDisplayPanel *panel,
+             const gchar    *title)
+{
+  GtkWidget *dialog;
+
+  dialog = g_object_new (GTK_TYPE_DIALOG,
+                         "title", title,
+                         "transient-for", cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel))),
+                         "modal", TRUE,
+                         "use-header-bar", TRUE,
+                         "destroy-with-parent", TRUE,
+                         "resizable", FALSE,
+                         NULL);
+  g_signal_connect_object (dialog, "notify::has-toplevel-focus",
+                           G_CALLBACK (dialog_toplevel_focus_changed),
+                           panel, G_CONNECT_SWAPPED);
+  return dialog;
+}
+
+static gboolean
+dialog_closed (GtkWidget      *dialog,
+               GdkEvent       *event,
+               CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+
+  if (priv->dialog == dialog)
+    {
+      gtk_widget_destroy (dialog);
+      priv->dialog = NULL;
+    }
+  else
+    g_warn_if_reached ();
+
+  return TRUE;
+}
+
+static void
+show_dialog (CcDisplayPanel *panel,
+             GtkWidget      *dialog)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+
+  if (!priv->dialog)
+    {
+      priv->dialog = dialog;
+      gtk_widget_show_all (dialog);
+      g_signal_connect_object (dialog, "delete-event", G_CALLBACK (dialog_closed),
+                               panel, 0);
+    }
+  else
+    {
+      gtk_widget_destroy (dialog);
+    }
+}
+
+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 void
+orientation_row_activated (CcDisplayPanel *panel,
+                           GtkListBoxRow  *row)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayRotation rotation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row), "rotation"));
+  CcDisplayRotation old_rotation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (priv->current_output), 
"old-rotation"));
+  int width, height;
+
+  cc_display_monitor_get_geometry (priv->current_output, NULL, NULL, &width, &height);
+
+  cc_display_monitor_set_rotation (priv->current_output, rotation);
+  realign_outputs_after_resolution_change (panel, priv->current_output, width, height, old_rotation);
+  update_apply_button (panel);
+}
+
+static void
+orientation_row_rotation_changed (GtkListBoxRow    *row,
+                                  CcDisplayMonitor *output)
+{
+  GtkWidget *check = g_object_get_data (G_OBJECT (row), "check");
+  CcDisplayRotation rotation = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row), "rotation"));
+
+  if (cc_display_monitor_get_rotation (output) == rotation)
+    gtk_widget_set_opacity (check, 1.0);
+  else
+    gtk_widget_set_opacity (check, 0.0);
+}
+
+static void
+show_orientation_dialog (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *dialog, *listbox;
+  GtkSizeGroup *size_group;
+  CcDisplayRotation rotations[] = { CC_DISPLAY_ROTATION_NONE,
+                                    CC_DISPLAY_ROTATION_90,
+                                    CC_DISPLAY_ROTATION_270,
+                                    CC_DISPLAY_ROTATION_180 };
+  guint i = 0;
+
+  dialog = make_dialog (panel, _("Orientation"));
+  listbox = make_list_box ();
+  gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
+                     listbox);
+
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+  for (i = 0; i < G_N_ELEMENTS (rotations); ++i)
+    {
+      CcDisplayRotation rotation = rotations[i];
+      if (cc_display_monitor_supports_rotation (priv->current_output, rotation))
+        {
+          GtkWidget *row, *check;
+
+          check = gtk_image_new ();
+          gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU);
+          if (cc_display_monitor_get_rotation (priv->current_output) != rotation)
+            gtk_widget_set_opacity (check, 0.0);
+
+          row = make_row (size_group, gtk_label_new (string_for_rotation (rotation)), check);
+          g_object_set_data (G_OBJECT (row), "check", check);
+          g_object_set_data (G_OBJECT (row), "rotation", GUINT_TO_POINTER (rotation));
+
+          g_signal_connect_object (row, "activated", G_CALLBACK (orientation_row_activated),
+                                   panel, G_CONNECT_SWAPPED);
+          g_signal_connect_object (priv->current_output, "rotation",
+                                   G_CALLBACK (orientation_row_rotation_changed),
+                                   row, G_CONNECT_SWAPPED);
+
+          gtk_container_add (GTK_CONTAINER (listbox), row);
+        }
+    }
+  g_object_unref (size_group);
+
+  show_dialog (panel, dialog);
+}
+
+static void
+orientation_label_rotation_changed (GtkLabel         *label,
+                                    CcDisplayMonitor *output)
+{
+  gtk_label_set_text (label, string_for_rotation (cc_display_monitor_get_rotation (output)));
+}
+
+static GtkWidget *
+make_orientation_row (CcDisplayPanel *panel, CcDisplayMonitor *output)
+{
+  GtkWidget *row, *label;
+
+  label = gtk_label_new (string_for_rotation (cc_display_monitor_get_rotation (output)));
+
+  row = make_row (panel->priv->rows_size_group, gtk_label_new (_("Orientation")), label);
+  g_signal_connect_object (row, "activated", G_CALLBACK (show_orientation_dialog),
+                           panel, G_CONNECT_SWAPPED);
+  g_signal_connect_object (output, "rotation", G_CALLBACK (orientation_label_rotation_changed),
+                           label, G_CONNECT_SWAPPED);
+  return row;
+}
+
+static void
+resolution_row_activated (CcDisplayPanel *panel,
+                          GtkListBoxRow  *row)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayMode *mode = g_object_get_data (G_OBJECT (row), "mode");
+  int old_width = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (priv->current_output), "old-width"));
+  int old_height = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (priv->current_output), "old-height"));
+
+  cc_display_monitor_set_mode (priv->current_output, mode);
+  realign_outputs_after_resolution_change (panel, priv->current_output, old_width, old_height,
+                                           cc_display_monitor_get_rotation (priv->current_output));
+  update_apply_button (panel);
+}
+
+static void
+resolution_row_mode_changed (GtkListBoxRow    *row,
+                             CcDisplayMonitor *output)
+{
+  GtkWidget *check = g_object_get_data (G_OBJECT (row), "check");
+  CcDisplayMode *mode = g_object_get_data (G_OBJECT (row), "mode");
+
+  if (g_str_equal (get_resolution_string (cc_display_monitor_get_mode (output)),
+                   get_resolution_string (mode)))
+    gtk_widget_set_opacity (check, 1.0);
+  else
+    gtk_widget_set_opacity (check, 0.0);
+}
+
+static void
+show_resolution_dialog (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *dialog, *listbox, *sw;
+  GtkSizeGroup *size_group;
+  GList *resolutions, *l;
+
+  resolutions = g_object_get_data (G_OBJECT (priv->current_output), "res-list");
+
+  dialog = make_dialog (panel, _("Resolution"));
+  listbox = make_list_box ();
+  sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+                     "hscrollbar-policy", GTK_POLICY_NEVER,
+                     "max-content-height", 450,
+                     "propagate-natural-height", TRUE,
+                     NULL);
+  gtk_container_add (GTK_CONTAINER (sw), listbox);
+  gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), sw);
+
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+  for (l = resolutions; l; l = l->next)
+    {
+      CcDisplayMode *mode = l->data;
+      GtkWidget *row, *check;
+
+      check = gtk_image_new ();
+      gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU);
+      if (!g_str_equal (get_resolution_string (cc_display_monitor_get_mode (priv->current_output)),
+                        get_resolution_string (mode)))
+        gtk_widget_set_opacity (check, 0.0);
+
+      row = make_row (size_group, gtk_label_new (get_resolution_string (mode)), check);
+      g_object_set_data (G_OBJECT (row), "check", check);
+      g_object_set_data (G_OBJECT (row), "mode", mode);
+
+      g_signal_connect_object (row, "activated", G_CALLBACK (resolution_row_activated),
+                               panel, G_CONNECT_SWAPPED);
+      g_signal_connect_object (priv->current_output, "mode",
+                               G_CALLBACK (resolution_row_mode_changed),
+                               row, G_CONNECT_SWAPPED);
+      gtk_container_add (GTK_CONTAINER (listbox), row);
+    }
+  g_object_unref (size_group);
+
+  show_dialog (panel, dialog);
+}
+
+static void
+resolution_label_mode_changed (GtkLabel         *label,
+                               CcDisplayMonitor *output)
+{
+  gtk_label_set_text (label, get_resolution_string (cc_display_monitor_get_mode (output)));
+}
+
+static GtkWidget *
+make_resolution_row (CcDisplayPanel *panel, CcDisplayMonitor *output)
+{
+  GtkWidget *row, *label;
+
+  label = gtk_label_new (get_resolution_string (cc_display_monitor_get_mode (output)));
+
+  row = make_row (panel->priv->rows_size_group, gtk_label_new (_("Resolution")), label);
+  g_signal_connect_object (row, "activated", G_CALLBACK (show_resolution_dialog),
+                           panel, G_CONNECT_SWAPPED);
+  g_signal_connect_object (output, "mode", G_CALLBACK (resolution_label_mode_changed),
+                           label, G_CONNECT_SWAPPED);
+  return row;
+}
+
+static void
+refresh_rate_row_activated (CcDisplayPanel *panel,
+                            GtkListBoxRow  *row)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayMode *mode = g_object_get_data (G_OBJECT (row), "mode");
+
+  cc_display_monitor_set_mode (priv->current_output, mode);
+  update_apply_button (panel);
+}
+
+static void
+refresh_rate_row_mode_changed (GtkListBoxRow    *row,
+                               CcDisplayMonitor *output)
+{
+  GtkWidget *check = g_object_get_data (G_OBJECT (row), "check");
+  CcDisplayMode *mode = g_object_get_data (G_OBJECT (row), "mode");
+
+  if (cc_display_monitor_get_mode (output) == mode)
+    gtk_widget_set_opacity (check, 1.0);
+  else
+    gtk_widget_set_opacity (check, 0.0);
+}
+
+static void
+show_refresh_rate_dialog (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *dialog, *listbox, *sw;
+  GtkSizeGroup *size_group;
+  GHashTable *res_freqs;
+  GList *freqs, *l;
+
+  res_freqs = g_object_get_data (G_OBJECT (priv->current_output), "res-freqs");
+  freqs = g_hash_table_lookup (res_freqs,
+                               get_resolution_string (cc_display_monitor_get_mode (priv->current_output)));
+
+  dialog = make_dialog (panel, _("Refresh Rate"));
+  listbox = make_list_box ();
+  sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+                     "hscrollbar-policy", GTK_POLICY_NEVER,
+                     "max-content-height", 450,
+                     "propagate-natural-height", TRUE,
+                     NULL);
+  gtk_container_add (GTK_CONTAINER (sw), listbox);
+  gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), sw);
+
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+  for (l = freqs; l; l = l->next)
+    {
+      CcDisplayMode *mode = l->data;
+      GtkWidget *row, *check;
+
+      check = gtk_image_new ();
+      gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU);
+      if (cc_display_monitor_get_mode (priv->current_output) != mode)
+        gtk_widget_set_opacity (check, 0.0);
+
+      row = make_row (size_group, gtk_label_new (get_frequency_string (mode)), check);
+      g_object_set_data (G_OBJECT (row), "check", check);
+      g_object_set_data (G_OBJECT (row), "mode", mode);
+
+      g_signal_connect_object (row, "activated", G_CALLBACK (refresh_rate_row_activated),
+                               panel, G_CONNECT_SWAPPED);
+      g_signal_connect_object (priv->current_output, "mode",
+                               G_CALLBACK (refresh_rate_row_mode_changed),
+                               row, G_CONNECT_SWAPPED);
+      gtk_container_add (GTK_CONTAINER (listbox), row);
+    }
+  g_object_unref (size_group);
+
+  show_dialog (panel, dialog);
+}
+
+static void
+refresh_rate_label_mode_changed (GtkLabel         *label,
+                                 CcDisplayMonitor *output)
+{
+  gtk_label_set_text (label, get_frequency_string (cc_display_monitor_get_mode (output)));
+}
+
+static gboolean
+should_show_refresh_rate (CcDisplayMonitor *output)
+{
+  GHashTable *res_freqs = g_object_get_data (G_OBJECT (output), "res-freqs");
+  const gchar *resolution = get_resolution_string (cc_display_monitor_get_mode (output));
+  GList *freqs = g_hash_table_lookup (res_freqs, resolution);
+
+  return g_list_length (freqs) > 1;
+}
+
+static void
+refresh_rate_row_sync_visibility (GtkWidget        *row,
+                                  CcDisplayMonitor *output)
+{
+  if (!should_show_refresh_rate (output))
+    gtk_widget_hide (row);
+  else
+    gtk_widget_show (row);
+}
+
+static GtkWidget *
+make_refresh_rate_row (CcDisplayPanel *panel, CcDisplayMonitor *output)
+{
+  GtkWidget *row, *label;
+
+  label = gtk_label_new (get_frequency_string (cc_display_monitor_get_mode (output)));
+
+  row = make_row (panel->priv->rows_size_group, gtk_label_new (_("Refresh Rate")), label);
+  g_signal_connect_object (row, "activated", G_CALLBACK (show_refresh_rate_dialog),
+                           panel, G_CONNECT_SWAPPED);
+  g_signal_connect_object (output, "mode", G_CALLBACK (refresh_rate_label_mode_changed),
+                           label, G_CONNECT_SWAPPED);
+
+  gtk_widget_show_all (row);
+  gtk_widget_set_no_show_all (row, TRUE);
+
+  g_signal_connect_object (output, "mode", G_CALLBACK (refresh_rate_row_sync_visibility),
+                           row, G_CONNECT_SWAPPED);
+  refresh_rate_row_sync_visibility (row, output);
+
+  return row;
+}
+
+static guint
+n_supported_scales (CcDisplayMode *mode)
+{
+  const double *scales = cc_display_mode_get_supported_scales (mode);
+  guint n = 0;
+
+  while (scales[n] != 0.0)
+    n++;
+
+  return n;
+}
+
+static void
+setup_scale_slider (GtkWidget        *slider,
+                    CcDisplayMonitor *output)
+{
+  CcDisplayMode *mode;
+  const double *scales;
+  double current_scale;
+  int current_index = -1;
+  guint i, n;
+
+  mode = cc_display_monitor_get_mode (output);
+  scales = cc_display_mode_get_supported_scales (mode);
+  n = n_supported_scales (mode);
+
+  gtk_range_set_range (GTK_RANGE (slider), 0, n-1);
+
+  current_scale = cc_display_monitor_get_scale (output);
+  gtk_scale_clear_marks (GTK_SCALE (slider));
+
+  for (i = 0; i < n; ++i)
+    {
+      double integral;
+      double fractional = modf (scales[i], &integral);
+      gchar *s = NULL;
+
+      if (scales[i] == current_scale || (current_index < 0 && scales[i] == 1.0))
+        current_index = i;
+
+      if (fractional != 0)
+        s = NULL;
+      else
+        s = g_strdup_printf ("%d", (int) integral);
+
+      gtk_scale_add_mark (GTK_SCALE (slider), i, GTK_POS_TOP, s);
+      g_free (s);
+    }
+
+  gtk_range_set_value (GTK_RANGE (slider), current_index);
+}
+
 static gboolean
-display_preview_draw (GtkWidget      *widget,
-                      cairo_t        *cr,
+should_show_scale_row (CcDisplayMonitor *output)
+{
+  CcDisplayMode *mode = cc_display_monitor_get_mode (output);
+  return n_supported_scales (mode) > 1;
+}
+
+static void
+scale_row_sync_visibility (GtkWidget        *row,
+                           CcDisplayMonitor *output)
+{
+  if (!should_show_scale_row (output))
+    gtk_widget_hide (row);
+  else
+    gtk_widget_show (row);
+}
+
+static double
+scale_slider_get_selected_scale (GtkRange       *slider,
+                                 CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayMode *mode = cc_display_monitor_get_mode (priv->current_output);
+  const double *scales = cc_display_mode_get_supported_scales (mode);
+  int selected = gtk_range_get_value (slider);
+
+  g_return_val_if_fail (selected < n_supported_scales (mode), 1.0);
+
+  return scales[selected];
+}
+
+static void
+scale_slider_changed (GtkRange       *slider,
                       CcDisplayPanel *panel)
 {
-  CcDisplayMonitor *output;
-  CcDisplayConfig *config;
-  gint num, width, height;
+  cc_display_monitor_set_scale (panel->priv->current_output,
+                                scale_slider_get_selected_scale (slider, panel));
+  update_apply_button (panel);
+}
+
+static GtkWidget *
+make_scale_row (CcDisplayPanel *panel, CcDisplayMonitor *output)
+{
+  GtkWidget *row, *slider;
 
-  output = g_object_get_data (G_OBJECT (widget), "output");
-  config = g_object_get_data (G_OBJECT (widget), "config");
-  num = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "number"));
+  slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, NULL);
+  gtk_widget_set_size_request (slider, 200, -1);
 
-  width = gtk_widget_get_allocated_width (widget);
-  height = gtk_widget_get_allocated_height (widget);
+  g_signal_connect_object (slider, "value-changed",
+                           G_CALLBACK (scale_slider_changed), panel, 0);
 
-  paint_output (panel, cr, config, output, num, width, height);
+  gtk_scale_set_draw_value (GTK_SCALE (slider), FALSE);
+  gtk_scale_set_has_origin (GTK_SCALE (slider), FALSE);
+  gtk_scale_set_digits (GTK_SCALE (slider), 0);
+  gtk_range_set_increments (GTK_RANGE (slider), 1, 1);
 
-  return TRUE;
+  setup_scale_slider (slider, output);
+
+  row = make_row (panel->priv->rows_size_group, gtk_label_new (_("Scale")), slider);
+  gtk_widget_set_margin_top (gtk_bin_get_child (GTK_BIN (row)), 0);
+  gtk_widget_set_margin_bottom (gtk_bin_get_child (GTK_BIN (row)), 0);
+  g_signal_connect_object (output, "mode", G_CALLBACK (setup_scale_slider),
+                           slider, G_CONNECT_SWAPPED);
+
+  gtk_widget_show_all (row);
+  gtk_widget_set_no_show_all (row, TRUE);
+
+  g_signal_connect_object (output, "mode", G_CALLBACK (scale_row_sync_visibility),
+                           row, G_CONNECT_SWAPPED);
+  scale_row_sync_visibility (row, output);
+
+  return row;
 }
 
-static GtkWidget*
-display_preview_new (CcDisplayPanel    *panel,
-                     CcDisplayMonitor  *output,
-                     CcDisplayConfig   *config,
-                     gint               num,
-                     gint               base_height)
+static void
+underscanning_switch_active (CcDisplayPanel *panel,
+                             GParamSpec     *pspec,
+                             GtkWidget      *button)
 {
-  GtkWidget *area;
-  gint width, height;
+  cc_display_monitor_set_active (panel->priv->current_output,
+                                 gtk_switch_get_active (GTK_SWITCH (button)));
+  update_apply_button (panel);
+}
 
-  get_geometry (output, NULL, NULL, &width, &height);
+static GtkWidget *
+make_underscanning_row (CcDisplayPanel   *panel,
+                        CcDisplayMonitor *output)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *row, *button;
 
-  area = gtk_drawing_area_new ();
-  g_signal_connect (area, "draw", G_CALLBACK (display_preview_draw), panel);
+  button = gtk_switch_new ();
+  gtk_switch_set_active (GTK_SWITCH (button),
+                         cc_display_monitor_get_underscanning (output));
+  g_signal_connect_object (button, "notify::active", G_CALLBACK (underscanning_switch_active),
+                           panel, G_CONNECT_SWAPPED);
 
-  gtk_widget_set_size_request (area, base_height * (width / (gdouble) height), base_height);
+  row = make_row (priv->rows_size_group, gtk_label_new (_("Adjust for TV")), button);
+  return row;
+}
 
-  gtk_widget_set_valign (area, GTK_ALIGN_CENTER);
-  gtk_widget_set_halign (area, GTK_ALIGN_CENTER);
+static gint
+sort_modes_by_area_desc (CcDisplayMode *a, CcDisplayMode *b)
+{
+  int wa, ha, wb, hb;
 
-  g_object_set_data (G_OBJECT (area), "output", output);
-  g_object_set_data (G_OBJECT (area), "config", config);
-  g_object_set_data (G_OBJECT (area), "number", GINT_TO_POINTER (num));
+  cc_display_mode_get_resolution (a, &wa, &ha);
+  cc_display_mode_get_resolution (b, &wb, &hb);
 
-  return area;
+  return wb*hb - wa*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 void
-on_screen_changed (CcDisplayPanel *panel)
+ensure_res_freqs (CcDisplayMonitor *output)
+{
+  GHashTable *res_freqs;
+  GHashTableIter iter;
+  GList *resolutions, *modes, *m;
+  int old_width, old_height;
+
+  if (g_object_get_data (G_OBJECT (output), "res-freqs"))
+    return;
+
+  res_freqs = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                     NULL, (GDestroyNotify) g_list_free);
+  resolutions = NULL;
+
+  modes = cc_display_monitor_get_modes (output);
+  for (m = modes; m; m = m->next)
+    {
+      CcDisplayMode *mode = m->data;
+      const gchar *resolution = get_resolution_string (mode);
+      GList *l, *exist;
+
+      exist = l = g_hash_table_lookup (res_freqs, resolution);
+      l = g_list_append (l, mode);
+      if (!exist)
+        g_hash_table_insert (res_freqs, (gpointer) resolution, l);
+    }
+
+  g_hash_table_iter_init (&iter, res_freqs);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &modes))
+    {
+      modes = g_list_copy (modes);
+      modes = g_list_sort (modes, (GCompareFunc) sort_modes_by_freq_desc);
+      g_hash_table_iter_replace (&iter, modes);
+
+      resolutions = g_list_prepend (resolutions, g_list_nth_data (modes, 0));
+    }
+
+  resolutions = g_list_sort (resolutions, (GCompareFunc) sort_modes_by_area_desc);
+
+  g_object_set_data_full (G_OBJECT (output), "res-freqs",
+                          res_freqs, (GDestroyNotify) g_hash_table_destroy);
+  g_object_set_data_full (G_OBJECT (output), "res-list",
+                          resolutions, (GDestroyNotify) g_list_free);
+
+  cc_display_monitor_get_geometry (output, NULL, NULL, &old_width, &old_height);
+  g_object_set_data (G_OBJECT (output), "old-width", GINT_TO_POINTER (old_width));
+  g_object_set_data (G_OBJECT (output), "old-height", GINT_TO_POINTER (old_height));
+  g_object_set_data (G_OBJECT (output), "old-rotation",
+                     GUINT_TO_POINTER (cc_display_monitor_get_rotation (output)));
+}
+
+static GtkWidget *
+make_output_ui (CcDisplayPanel *panel)
 {
   CcDisplayPanelPrivate *priv = panel->priv;
-  CcDisplayConfig *current;
-  GList *outputs;
-  gint num_connected_outputs = 0, number = 0;
-  gboolean clone = FALSE, combined = FALSE;
-  GtkSizeGroup *sizegroup;
-  GList *sorted_outputs = NULL, *l;
+  GtkWidget *listbox;
 
-  if (priv->dialog)
-    gtk_dialog_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_NONE);
+  ensure_res_freqs (priv->current_output);
 
-  g_clear_object (&priv->current_config);
+  listbox = make_list_box ();
 
-  current = cc_display_config_manager_get_current (priv->manager);
-  if (!current)
+  if (should_show_rotation (panel, priv->current_output))
+      gtk_container_add (GTK_CONTAINER (listbox),
+                         make_orientation_row (panel, priv->current_output));
+
+  gtk_container_add (GTK_CONTAINER (listbox),
+                     make_resolution_row (panel, priv->current_output));
+
+  gtk_container_add (GTK_CONTAINER (listbox),
+                     make_refresh_rate_row (panel, priv->current_output));
+
+  gtk_container_add (GTK_CONTAINER (listbox),
+                     make_scale_row (panel, priv->current_output));
+
+  if (cc_display_monitor_supports_underscanning (priv->current_output))
+    gtk_container_add (GTK_CONTAINER (listbox),
+                       make_underscanning_row (panel, priv->current_output));
+
+  return listbox;
+}
+
+static GtkWidget *
+make_single_output_ui (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *vbox, *frame;
+
+  priv->rows_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+
+  vbox = make_main_vbox ();
+
+  frame = make_frame (g_object_get_data (G_OBJECT (priv->current_output), "ui-name"), NULL);
+  gtk_container_add (GTK_CONTAINER (vbox), frame);
+
+  gtk_container_add (GTK_CONTAINER (frame), make_output_ui (panel));
+
+  gtk_container_add (GTK_CONTAINER (vbox), make_night_light_widget (panel));
+
+  g_clear_object (&priv->rows_size_group);
+  return make_scrollable (vbox);
+}
+
+static GtkWidget *
+make_arrangement_row (CcDisplayPanel *panel)
+{
+  GtkWidget *row, *area;
+
+  area = (GtkWidget *) foo_scroll_area_new ();
+  g_object_set_data (G_OBJECT (area), "panel", panel);
+  foo_scroll_area_set_min_size (FOO_SCROLL_AREA (area), 400, 150);
+  g_signal_connect (area, "paint",
+                    G_CALLBACK (on_area_paint), panel);
+  g_signal_connect (area, "viewport_changed",
+                    G_CALLBACK (on_viewport_changed), panel);
+
+  row = g_object_new (CC_TYPE_LIST_BOX_ROW, NULL);
+  gtk_container_add (GTK_CONTAINER (row), area);
+
+  return row;
+}
+
+static void
+primary_label_sync (GtkWidget       *label,
+                    CcDisplayConfig *config)
+{
+  GList *outputs, *l;
+
+  outputs = cc_display_config_get_monitors (config);
+  for (l = outputs; l; l = l->next)
     {
-      gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "error");
-      return;
+      CcDisplayMonitor *output = l->data;
+      if (cc_display_monitor_is_primary (output))
+        {
+          gchar *text = g_object_get_data (G_OBJECT (output), "ui-number-name");
+          gtk_label_set_text (GTK_LABEL (label), text);
+          return;
+        }
     }
-  priv->current_config = current;
-  gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "main");
+}
+
+static void
+primary_chooser_row_activated (CcDisplayPanel *panel,
+                               GtkListBoxRow  *row)
+{
+  CcDisplayMonitor *output = g_object_get_data (G_OBJECT (row), "output");
+
+  cc_display_monitor_set_primary (output, TRUE);
+  update_apply_button (panel);
+}
+
+static void
+primary_chooser_row_primary_changed (GtkListBoxRow    *row,
+                                     CcDisplayMonitor *output)
+{
+  GtkWidget *check = g_object_get_data (G_OBJECT (row), "check");
+
+  if (cc_display_monitor_is_primary (output))
+    gtk_widget_set_opacity (check, 1.0);
+  else
+    gtk_widget_set_opacity (check, 0.0);
+}
+
+static void
+show_primary_chooser_dialog (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *dialog, *listbox, *sw;
+  GtkSizeGroup *size_group;
+  GList *outputs, *l;
+
+  outputs = g_object_get_data (G_OBJECT (priv->current_config), "ui-sorted-outputs");
+
+  dialog = make_dialog (panel, _("Primary Display"));
+  listbox = make_list_box ();
+  sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+                     "hscrollbar-policy", GTK_POLICY_NEVER,
+                     "max-content-height", 450,
+                     "propagate-natural-height", TRUE,
+                     NULL);
+  gtk_container_add (GTK_CONTAINER (sw), listbox);
+  gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), sw);
+
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+  for (l = outputs; l; l = l->next)
+    {
+      CcDisplayMonitor *output = l->data;
+      GtkWidget *row, *check;
+      gchar *text;
+
+      check = gtk_image_new ();
+      gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU);
+      if (!cc_display_monitor_is_primary (output))
+        gtk_widget_set_opacity (check, 0.0);
+
+      text = g_object_get_data (G_OBJECT (output), "ui-number-name");
+      row = make_row (size_group, gtk_label_new (text), check);
+      g_object_set_data (G_OBJECT (row), "check", check);
+      g_object_set_data (G_OBJECT (row), "output", output);
+
+      g_signal_connect_object (row, "activated", G_CALLBACK (primary_chooser_row_activated),
+                               panel, G_CONNECT_SWAPPED);
+      g_signal_connect_object (output, "primary",
+                               G_CALLBACK (primary_chooser_row_primary_changed),
+                               row, G_CONNECT_SWAPPED);
+      gtk_container_add (GTK_CONTAINER (listbox), row);
+    }
+  g_object_unref (size_group);
 
-  gtk_container_foreach (GTK_CONTAINER (priv->displays_listbox),
-                         (GtkCallback) gtk_widget_destroy, NULL);
+  show_dialog (panel, dialog);
+}
 
-  outputs = cc_display_config_get_monitors (current);
-  /* count the number of active and connected outputs */
+static GtkWidget *
+make_primary_chooser_row (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *row, *label;
+
+  label = gtk_label_new (NULL);
+  row = make_row (priv->rows_size_group, gtk_label_new (_("Primary Display")), label);
+  g_signal_connect_object (row, "activated", G_CALLBACK (show_primary_chooser_dialog),
+                           panel, G_CONNECT_SWAPPED);
+  g_signal_connect_object (priv->current_config, "primary", G_CALLBACK (primary_label_sync),
+                           label, G_CONNECT_SWAPPED);
+  primary_label_sync (label, priv->current_config);
+
+  return row;
+}
+
+static void
+replace_current_output_ui (GtkWidget      *frame,
+                           CcDisplayPanel *panel)
+{
+  gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (frame)));
+  gtk_container_add (GTK_CONTAINER (frame), make_output_ui (panel));
+  gtk_widget_show_all (frame);
+}
+
+static guint
+count_active_outputs (GList *outputs)
+{
+  GList *l;
+  guint active = 0;
   for (l = outputs; l != NULL; l = l->next)
     {
       CcDisplayMonitor *output = l->data;
+      if (cc_display_monitor_is_active (output))
+        active++;
+    }
+  return active;
+}
 
-      /* ensure the built in display is first in the list */
-      if (cc_display_monitor_is_builtin (output))
-        sorted_outputs = g_list_prepend (sorted_outputs, output);
-      else
-        sorted_outputs = g_list_append (sorted_outputs, output);
+static GtkWidget *
+make_arrangement_ui (CcDisplayPanel *panel)
+{
+  GtkWidget *frame, *listbox;
 
-      num_connected_outputs++;
+  frame = make_frame (_("Display Arrangement"),
+                      _("Drag displays to match your setup. The top bar is placed on the primary display."));
+  listbox = make_list_box ();
+  gtk_container_add (GTK_CONTAINER (frame), listbox);
+
+  gtk_container_add (GTK_CONTAINER (listbox), make_arrangement_row (panel));
+
+  gtk_container_add (GTK_CONTAINER (listbox), make_primary_chooser_row (panel));
+
+  return frame;
+}
+
+static void
+two_output_chooser_active (CcDisplayPanel *panel,
+                           GParamSpec     *pspec,
+                           GtkWidget      *button)
+{
+  CcDisplayMonitor *output = g_object_get_data (G_OBJECT (button), "output");
+
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+    set_current_output (panel, output);
+}
+
+static void
+two_output_chooser_sync (GtkWidget      *box,
+                         CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GList *children, *l;
+
+  children = gtk_container_get_children (GTK_CONTAINER (box));
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *button = l->data;
+      CcDisplayMonitor *output = g_object_get_data (G_OBJECT (button), "output");
+      if (priv->current_output == output)
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
     }
+  g_list_free (children);
+}
 
-  sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+static GtkWidget *
+make_two_output_chooser (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *box;
+  GtkRadioButton *group;
+  GList *outputs, *l;
+
+  outputs = g_object_get_data (G_OBJECT (priv->current_config), "ui-sorted-outputs");
 
-  g_hash_table_remove_all (output_ids);
+  box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
+  gtk_button_box_set_layout (GTK_BUTTON_BOX (box), GTK_BUTTONBOX_EXPAND);
+  gtk_style_context_add_class (gtk_widget_get_style_context (box), GTK_STYLE_CLASS_LINKED);
 
-  for (l = sorted_outputs; l; l = g_list_next (l))
+  group = NULL;
+  for (l = outputs; l; l = l->next)
     {
-      GtkWidget *row, *item, *preview, *label;
-      gboolean primary, active;
-      const gchar *status;
-      gboolean display_closed = FALSE;
       CcDisplayMonitor *output = l->data;
+      GtkWidget *button = gtk_radio_button_new_from_widget (group);
+
+      gtk_button_set_label (GTK_BUTTON (button), g_object_get_data (G_OBJECT (output), "ui-name"));
+      gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
+
+      g_object_set_data (G_OBJECT (button), "output", output);
+      g_signal_connect_object (button, "notify::active", G_CALLBACK (two_output_chooser_active),
+                               panel, G_CONNECT_SWAPPED);
+      gtk_container_add (GTK_CONTAINER (box), button);
+      group = GTK_RADIO_BUTTON (button);
+    }
 
-      if (priv->lid_is_closed)
-        display_closed = cc_display_monitor_is_builtin (output);
+  g_signal_connect_object (panel, "current-output", G_CALLBACK (two_output_chooser_sync),
+                           box, G_CONNECT_SWAPPED);
+  two_output_chooser_sync (box, panel);
 
-      row = gtk_list_box_row_new ();
-      item = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
-      gtk_container_set_border_width (GTK_CONTAINER (item), 12);
+  return box;
+}
 
-      preview = display_preview_new (panel, output, current, ++number,
-                                     DISPLAY_PREVIEW_LIST_HEIGHT);
-      gtk_size_group_add_widget (sizegroup, preview);
+static GtkWidget *
+make_two_join_ui (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *vbox, *frame, *box;
 
-      if (display_closed)
-        gtk_widget_set_sensitive (row, FALSE);
+  priv->rows_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
 
-      g_hash_table_insert (output_ids, output, GINT_TO_POINTER (number));
+  vbox = make_main_vbox ();
 
-      gtk_container_add (GTK_CONTAINER (item), preview);
+  gtk_container_add (GTK_CONTAINER (vbox), make_arrangement_ui (panel));
 
-      label = gtk_label_new (cc_display_monitor_get_display_name (output));
-      gtk_container_add (GTK_CONTAINER (item), label);
+  box = make_two_output_chooser (panel);
+  gtk_widget_set_margin_top (box, SECTION_PADDING);
+  gtk_container_add (GTK_CONTAINER (vbox), box);
 
-      primary = cc_display_monitor_is_primary (output);
-      active = cc_display_monitor_is_active (output);
+  frame = make_frame (NULL, NULL);
+  gtk_widget_set_margin_top (frame, HEADING_PADDING);
+  gtk_container_add (GTK_CONTAINER (vbox), frame);
+
+  gtk_container_add (GTK_CONTAINER (frame), make_output_ui (panel));
+  g_signal_connect_object (panel, "current-output", G_CALLBACK (replace_current_output_ui),
+                           frame, G_CONNECT_SWAPPED);
+
+  gtk_container_add (GTK_CONTAINER (vbox), make_night_light_widget (panel));
+
+  g_clear_object (&priv->rows_size_group);
+  return make_scrollable (vbox);
+}
+
+static void
+two_output_chooser_activate_output (CcDisplayPanel *panel,
+                                    GParamSpec     *pspec,
+                                    GtkWidget      *button)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayMonitor *output = g_object_get_data (G_OBJECT (button), "output");
 
-      if (num_connected_outputs > 1)
+  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
+    {
+      GList *outputs, *l;
+
+      cc_display_monitor_set_active (output, TRUE);
+
+      outputs = cc_display_config_get_monitors (priv->current_config);
+      for (l = outputs; l; l = l->next)
         {
-          if (display_closed)
-            status = _("Lid Closed");
-          else if (clone)
-            /* translators: "Mirrored" describes when both displays show the same view */
-            status = _("Mirrored");
-          else if (primary)
-            status = _("Primary");
-          else if (!active)
-            status = _("Off");
-          else
-            {
-              status = _("Secondary");
-              combined = TRUE;
-            }
+          CcDisplayMonitor *other = l->data;
+          if (other != output)
+            cc_display_monitor_set_active (other, FALSE);
+        }
+
+      update_apply_button (panel);
+    }
+}
+
+static void
+connect_activate_output (GtkWidget *button,
+                         gpointer   panel)
+{
+  g_signal_connect_object (button, "notify::active", G_CALLBACK (two_output_chooser_activate_output),
+                           panel, G_CONNECT_SWAPPED);
+}
+
+static GtkWidget *
+make_two_single_ui (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *vbox, *frame, *box;
+
+  priv->rows_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
 
-          label = gtk_label_new (status);
-          gtk_widget_set_hexpand (label, TRUE);
-          gtk_widget_set_halign (label, GTK_ALIGN_END);
-          gtk_container_add (GTK_CONTAINER (item), label);
+  vbox = make_main_vbox ();
+
+  box = make_two_output_chooser (panel);
+  gtk_container_foreach (GTK_CONTAINER (box), connect_activate_output, panel);
+  gtk_container_add (GTK_CONTAINER (vbox), box);
+
+  frame = make_frame (NULL, NULL);
+  gtk_widget_set_margin_top (frame, HEADING_PADDING);
+  gtk_container_add (GTK_CONTAINER (vbox), frame);
+
+  gtk_container_add (GTK_CONTAINER (frame), make_output_ui (panel));
+  g_signal_connect_object (panel, "current-output", G_CALLBACK (replace_current_output_ui),
+                           frame, G_CONNECT_SWAPPED);
+
+  gtk_container_add (GTK_CONTAINER (vbox), make_night_light_widget (panel));
+
+  g_clear_object (&priv->rows_size_group);
+  return make_scrollable (vbox);
+}
+
+static void
+set_mode_on_all_outputs (CcDisplayConfig *config,
+                         CcDisplayMode   *mode)
+{
+  GList *outputs, *l;
+  outputs = cc_display_config_get_monitors (config);
+  for (l = outputs; l; l = l->next)
+    {
+      CcDisplayMonitor *output = l->data;
+      cc_display_monitor_set_mode (output, mode);
+      cc_display_monitor_set_position (output, 0, 0);
+    }
+}
+
+static void
+mirror_resolution_row_activated (CcDisplayPanel *panel,
+                                 GtkListBoxRow  *row)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayMode *mode = g_object_get_data (G_OBJECT (row), "mode");
+
+  set_mode_on_all_outputs (priv->current_config, mode);
+  update_apply_button (panel);
+}
+
+static void
+show_mirror_resolution_dialog (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *dialog, *listbox, *sw;
+  GtkSizeGroup *size_group;
+  GList *resolutions, *l;
+
+  resolutions = g_object_get_data (G_OBJECT (priv->current_config), "mirror-res-list");
+
+  dialog = make_dialog (panel, _("Resolution"));
+  listbox = make_list_box ();
+  sw = g_object_new (GTK_TYPE_SCROLLED_WINDOW,
+                     "hscrollbar-policy", GTK_POLICY_NEVER,
+                     "max-content-height", 450,
+                     "propagate-natural-height", TRUE,
+                     NULL);
+  gtk_container_add (GTK_CONTAINER (sw), listbox);
+  gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), sw);
+
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+  for (l = resolutions; l; l = l->next)
+    {
+      CcDisplayMode *mode = l->data;
+      GtkWidget *row, *check;
+
+      check = gtk_image_new ();
+      gtk_image_set_from_icon_name (GTK_IMAGE (check), "object-select-symbolic", GTK_ICON_SIZE_MENU);
+      if (!g_str_equal (get_resolution_string (cc_display_monitor_get_mode (priv->current_output)),
+                        get_resolution_string (mode)))
+        gtk_widget_set_opacity (check, 0.0);
+
+      row = make_row (size_group, gtk_label_new (get_resolution_string (mode)), check);
+      g_object_set_data (G_OBJECT (row), "check", check);
+      g_object_set_data (G_OBJECT (row), "mode", mode);
+
+      g_signal_connect_object (row, "activated", G_CALLBACK (mirror_resolution_row_activated),
+                               panel, G_CONNECT_SWAPPED);
+      g_signal_connect_object (priv->current_output, "mode",
+                               G_CALLBACK (resolution_row_mode_changed),
+                               row, G_CONNECT_SWAPPED);
+      gtk_container_add (GTK_CONTAINER (listbox), row);
+    }
+  g_object_unref (size_group);
+
+  show_dialog (panel, dialog);
+}
+
+static GtkWidget *
+make_mirror_resolution_row (CcDisplayPanel   *panel,
+                            CcDisplayMonitor *output)
+{
+  GtkWidget *row, *label;
+
+  label = gtk_label_new (get_resolution_string (cc_display_monitor_get_mode (output)));
+
+  row = make_row (panel->priv->rows_size_group, gtk_label_new (_("Resolution")), label);
+  g_signal_connect_object (row, "activated", G_CALLBACK (show_mirror_resolution_dialog),
+                           panel, G_CONNECT_SWAPPED);
+  g_signal_connect_object (output, "mode", G_CALLBACK (resolution_label_mode_changed),
+                           label, G_CONNECT_SWAPPED);
+  return row;
+}
+
+static void
+ensure_mirror_res_list (CcDisplayConfig *config)
+{
+  GHashTable *res_set;
+  GList *resolutions, *l;
+
+  if (g_object_get_data (G_OBJECT (config), "mirror-res-list"))
+    return;
+
+  res_set = g_hash_table_new (g_str_hash, g_str_equal);
+
+  resolutions = cc_display_config_get_cloning_modes (config);
+  for (l = resolutions; l; l = l->next)
+    {
+      CcDisplayMode *mode = l->data;
+      const gchar *resolution = get_resolution_string (mode);
+      if (!g_hash_table_contains (res_set, resolution))
+        g_hash_table_insert (res_set, (gpointer) resolution, mode);
+    }
+
+  resolutions = g_hash_table_get_values (res_set);
+  g_hash_table_destroy (res_set);
+
+  resolutions = g_list_sort (resolutions, (GCompareFunc) sort_modes_by_area_desc);
+
+  g_object_set_data_full (G_OBJECT (config), "mirror-res-list",
+                          resolutions, (GDestroyNotify) g_list_free);
+}
+
+static GtkWidget *
+make_two_mirror_ui (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *vbox, *listbox, *frame;
+
+  ensure_mirror_res_list (priv->current_config);
+  if (!cc_display_config_is_cloning (priv->current_config))
+    {
+      GList *modes;
+      cc_display_config_set_cloning (priv->current_config, TRUE);
+      modes = g_object_get_data (G_OBJECT (priv->current_config), "mirror-res-list");
+      set_mode_on_all_outputs (priv->current_config,
+                               CC_DISPLAY_MODE (g_list_nth_data (modes, 0)));
+    }
+
+  priv->rows_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+
+  vbox = make_main_vbox ();
+  frame = make_frame (NULL, NULL);
+  gtk_container_add (GTK_CONTAINER (vbox), frame);
+  listbox = make_list_box ();
+  gtk_container_add (GTK_CONTAINER (frame), listbox);
+
+  if (should_show_rotation (panel, priv->current_output))
+    gtk_container_add (GTK_CONTAINER (listbox),
+                       make_orientation_row (panel, priv->current_output));
+
+  gtk_container_add (GTK_CONTAINER (listbox),
+                     make_mirror_resolution_row (panel, priv->current_output));
+
+  gtk_container_add (GTK_CONTAINER (vbox), make_night_light_widget (panel));
+
+  g_clear_object (&priv->rows_size_group);
+  return make_scrollable (vbox);
+}
+
+static void
+two_output_visible_child_changed (CcDisplayPanel *panel,
+                                  GParamSpec     *pspec,
+                                  GtkWidget      *stack)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *bin;
+  GList *children, *l;
+
+  children = gtk_container_get_children (GTK_CONTAINER (stack));
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *ui = gtk_bin_get_child (GTK_BIN (l->data));
+      if (ui)
+        gtk_widget_destroy (ui);
+    }
+  g_list_free (children);
+
+  bin = gtk_stack_get_visible_child (GTK_STACK (stack));
+
+  if (g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (stack)), "mirror"))
+    {
+      gtk_container_add (GTK_CONTAINER (bin), make_two_mirror_ui (panel));
+    }
+  else
+    {
+      gboolean single;
+      GList *outputs, *l;
+
+      if (cc_display_config_is_cloning (priv->current_config))
+        {
+          cc_display_config_set_cloning (priv->current_config, FALSE);
+          lay_out_outputs_horizontally (panel);
         }
+      single = g_str_equal (gtk_stack_get_visible_child_name (GTK_STACK (stack)), "single");
+      outputs = cc_display_config_get_monitors (priv->current_config);
+      for (l = outputs; l; l = l->next)
+        {
+          CcDisplayMonitor *output = l->data;
+          cc_display_monitor_set_active (output, (!single || output == priv->current_output));
+        }
+
+      if (single)
+        gtk_container_add (GTK_CONTAINER (bin), make_two_single_ui (panel));
+      else
+        gtk_container_add (GTK_CONTAINER (bin), make_two_join_ui (panel));
+    }
+
+  gtk_widget_show_all (stack);
+
+  ensure_monitor_labels (panel);
+  update_apply_button (panel);
+}
+
+static void
+setup_stack_switcher (GtkWidget *switcher)
+{
+  GList *children, *l;
 
-      g_object_set_data (G_OBJECT (row), "cc-display-monitor", output);
-      gtk_container_add (GTK_CONTAINER (row), item);
-      gtk_container_add (GTK_CONTAINER (priv->displays_listbox), row);
-      gtk_widget_show_all (row);
+  children = gtk_container_get_children (GTK_CONTAINER (switcher));
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *button = l->data;
+      gtk_container_child_set (GTK_CONTAINER (switcher), button,
+                               "expand", TRUE,
+                               NULL);
     }
+  g_list_free (children);
+}
+
+static GtkWidget *
+make_two_output_ui (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *vbox, *hbox, *stack, *switcher;
+  gboolean show_mirror;
+
+  show_mirror = g_list_length (cc_display_config_get_cloning_modes (priv->current_config)) > 0;
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, HEADING_PADDING);
+  gtk_widget_set_margin_top (hbox, HEADING_PADDING);
+  gtk_widget_set_margin_bottom (hbox, HEADING_PADDING);
+  gtk_container_add (GTK_CONTAINER (vbox), wrap_in_boxes (hbox));
+
+  gtk_container_add (GTK_CONTAINER (hbox), make_bold_label (_("Display Mode")));
+
+  switcher = gtk_stack_switcher_new ();
+  gtk_box_pack_end (GTK_BOX (hbox), switcher, TRUE, TRUE, 0);
+
+  gtk_container_add (GTK_CONTAINER (vbox), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
+
+  stack = gtk_stack_new ();
+  gtk_container_add (GTK_CONTAINER (vbox), stack);
+  gtk_stack_switcher_set_stack (GTK_STACK_SWITCHER (switcher), GTK_STACK (stack));
 
-  g_list_free (sorted_outputs);
+  gtk_stack_add_titled (GTK_STACK (stack), make_bin (),
+                        "join", _("Join Displays"));
+  if (show_mirror)
+    gtk_stack_add_titled (GTK_STACK (stack), make_bin (),
+                          "mirror", _("Mirror"));
+  gtk_stack_add_titled (GTK_STACK (stack), make_bin (),
+                        "single", _("Single Display"));
 
-  if (combined)
-    gtk_widget_show (priv->arrange_button);
+  g_signal_connect_object (stack, "notify::visible-child-name",
+                           G_CALLBACK (two_output_visible_child_changed),
+                           panel, G_CONNECT_SWAPPED);
+
+  setup_stack_switcher (switcher);
+
+  if (cc_display_config_is_cloning (priv->current_config) && show_mirror)
+    gtk_stack_set_visible_child_name (GTK_STACK (stack), "mirror");
+  else if (count_active_outputs (cc_display_config_get_monitors (priv->current_config)) > 1)
+    gtk_stack_set_visible_child_name (GTK_STACK (stack), "join");
   else
-    gtk_widget_hide (priv->arrange_button);
+    gtk_stack_set_visible_child_name (GTK_STACK (stack), "single");
+
+  return vbox;
+}
+
+static void
+output_switch_active (CcDisplayPanel *panel,
+                      GParamSpec     *pspec,
+                      GtkWidget      *button)
+{
+  cc_display_monitor_set_active (panel->priv->current_output,
+                                 gtk_switch_get_active (GTK_SWITCH (button)));
+  update_apply_button (panel);
+}
+
+static void
+output_switch_sync (GtkWidget        *button,
+                    CcDisplayMonitor *output)
+{
+  gtk_switch_set_active (GTK_SWITCH (button), cc_display_monitor_is_active (output));
+}
+
+static GtkWidget *
+make_output_switch (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *button = gtk_switch_new ();
+
+  g_signal_connect_object (button, "notify::active", G_CALLBACK (output_switch_active),
+                           panel, G_CONNECT_SWAPPED);
+  g_signal_connect_object (priv->current_output, "active", G_CALLBACK (output_switch_sync),
+                           button, G_CONNECT_SWAPPED);
+  output_switch_sync (button, priv->current_output);
+
+  if (count_active_outputs (cc_display_config_get_monitors (priv->current_config)) < 2 &&
+      cc_display_monitor_is_active (priv->current_output))
+    gtk_widget_set_sensitive (button, FALSE);
+
+  return button;
+}
+
+static void
+replace_output_switch (GtkWidget      *frame,
+                       CcDisplayPanel *panel)
+{
+  gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (frame)));
+  gtk_container_add (GTK_CONTAINER (frame), make_output_switch (panel));
+  gtk_widget_show_all (frame);
+}
+
+static void
+output_chooser_row_activated (CcDisplayPanel *panel,
+                              GtkWidget      *row)
+{
+  CcDisplayMonitor *output = g_object_get_data (G_OBJECT (row), "output");
+  set_current_output (panel, output);
+}
+
+static void
+output_chooser_sync (GtkWidget      *button,
+                     CcDisplayPanel *panel)
+{
+  gchar *text = g_object_get_data (G_OBJECT (panel->priv->current_output), "ui-number-name");
+  GtkWidget *label = gtk_bin_get_child (GTK_BIN (button));
+
+  gtk_label_set_text (GTK_LABEL (label), text);
+  gtk_widget_hide (GTK_WIDGET (gtk_menu_button_get_popover (GTK_MENU_BUTTON (button))));
+}
+
+static GtkWidget *
+make_output_chooser_button (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *listbox, *button, *popover;
+  GList *outputs, *l;
+
+  outputs = g_object_get_data (G_OBJECT (priv->current_config), "ui-sorted-outputs");
+
+  popover = gtk_popover_new (NULL);
+  listbox = make_list_box ();
+  gtk_container_add (GTK_CONTAINER (popover), listbox);
+
+  for (l = outputs; l; l = l->next)
+    {
+      CcDisplayMonitor *output = l->data;
+      GtkWidget *row = g_object_new (CC_TYPE_LIST_BOX_ROW, NULL);
+      gchar *text = g_object_get_data (G_OBJECT (output), "ui-number-name");
+      GtkWidget *label = g_object_new (GTK_TYPE_LABEL,
+                                       "label", text,
+                                       "margin", 4,
+                                       "halign", GTK_ALIGN_START,
+                                       NULL);
+      gtk_container_add (GTK_CONTAINER (row), label);
+      g_object_set_data (G_OBJECT (row), "output", output);
+
+      g_signal_connect_object (row, "activated", G_CALLBACK (output_chooser_row_activated),
+                               panel, G_CONNECT_SWAPPED);
+      gtk_container_add (GTK_CONTAINER (listbox), row);
+    }
+
+  gtk_widget_show_all (listbox);
+
+  button = gtk_menu_button_new ();
+  gtk_container_add (GTK_CONTAINER (button), make_bold_label (NULL));
+  gtk_menu_button_set_popover (GTK_MENU_BUTTON (button), popover);
+  g_signal_connect_object (panel, "current-output", G_CALLBACK (output_chooser_sync),
+                           button, G_CONNECT_SWAPPED);
+  output_chooser_sync (button, panel);
+
+  return button;
+}
+
+static GtkWidget *
+make_multi_output_ui (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *vbox, *frame, *hbox;
+
+  priv->rows_size_group = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
+
+  vbox = make_main_vbox ();
+
+  gtk_container_add (GTK_CONTAINER (vbox), make_arrangement_ui (panel));
 
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
+  gtk_widget_set_margin_top (hbox, SECTION_PADDING);
+  gtk_container_add (GTK_CONTAINER (vbox), hbox);
+
+  gtk_box_pack_start (GTK_BOX (hbox), make_output_chooser_button (panel), FALSE, FALSE, 0);
+
+  frame = make_bin ();
+  gtk_box_pack_end (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
+  gtk_container_add (GTK_CONTAINER (frame), make_output_switch (panel));
+  g_signal_connect_object (panel, "current-output", G_CALLBACK (replace_output_switch),
+                           frame, G_CONNECT_SWAPPED);
+
+  frame = make_frame (NULL, NULL);
+  gtk_widget_set_margin_top (frame, HEADING_PADDING);
+  gtk_container_add (GTK_CONTAINER (vbox), frame);
+
+  gtk_container_add (GTK_CONTAINER (frame), make_output_ui (panel));
+  g_signal_connect_object (panel, "current-output", G_CALLBACK (replace_current_output_ui),
+                           frame, G_CONNECT_SWAPPED);
+
+  gtk_container_add (GTK_CONTAINER (vbox), make_night_light_widget (panel));
+
+  g_clear_object (&priv->rows_size_group);
+  return make_scrollable (vbox);
+}
+
+static void
+on_screen_changed (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  CcDisplayConfig *current;
+  GList *outputs, *l;
+  GtkWidget *main_widget;
+
+  reset_titlebar (panel);
+
+  main_widget = gtk_stack_get_child_by_name (GTK_STACK (priv->stack), "main");
+  if (main_widget)
+    gtk_widget_destroy (main_widget);
+
+  if (priv->dialog)
+    gtk_dialog_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_NONE);
+
+  g_clear_object (&priv->current_config);
+
+  current = cc_display_config_manager_get_current (priv->manager);
+  if (!current)
+    goto show_error;
+
+  priv->current_config = current;
+
+  ensure_output_numbers (current);
   ensure_monitor_labels (panel);
+
+  priv->current_output = NULL;
+  l = outputs = g_object_get_data (G_OBJECT (current), "ui-sorted-outputs");
+  while (!priv->current_output && l != NULL)
+    {
+      CcDisplayMonitor *output = l->data;
+      if (cc_display_monitor_is_active (output))
+        priv->current_output = output;
+      else
+        l = l->next;
+    }
+
+  if (!priv->current_output)
+    goto show_error;
+
+  switch (g_list_length (outputs))
+    {
+    case 1:
+      main_widget = make_single_output_ui (panel);
+      break;
+    case 2:
+      main_widget = make_two_output_ui (panel);
+      break;
+    default:
+      main_widget = make_multi_output_ui (panel);
+      break;
+    }
+
+  gtk_widget_show_all (main_widget);
+  gtk_stack_add_named (GTK_STACK (priv->stack), main_widget, "main");
+  gtk_stack_set_visible_child (GTK_STACK (priv->stack), main_widget);
+  return;
+
+ show_error:
+  gtk_stack_set_visible_child_name (GTK_STACK (priv->stack), "error");
 }
 
 
@@ -628,6 +2184,9 @@ realign_outputs_after_resolution_change (CcDisplayPanel *self, CcDisplayMonitor
   GList *outputs, *l;
   CcDisplayRotation rotation;
 
+  if (self->priv->have_new_dbus_api)
+    return;
+
   g_assert (self->priv->current_config != NULL);
 
   cc_display_monitor_get_geometry (output_that_changed, &x, &y, &width, &height);
@@ -692,6 +2251,9 @@ lay_out_outputs_horizontally (CcDisplayPanel *self)
   int x;
   GList *outputs, *l;
 
+  if (self->priv->have_new_dbus_api)
+    return;
+
   /* Lay out all the monitors horizontally when "mirror screens" is turned
    * off, to avoid having all of them overlapped initially.  We put the
    * outputs turned off on the right-hand side.
@@ -733,7 +2295,6 @@ lay_out_outputs_horizontally (CcDisplayPanel *self)
 }
 
 
-
 #define SPACE 15
 #define MARGIN  15
 
@@ -1159,6 +2720,47 @@ grab_weak_ref_notify (gpointer  area,
 }
 
 static void
+show_apply_titlebar (CcDisplayPanel *panel)
+{
+  CcDisplayPanelPrivate *priv = panel->priv;
+  GtkWidget *header, *button, *toplevel;
+  GtkSizeGroup *size_group;
+
+  if (priv->apply_titlebar)
+    return;
+
+  priv->apply_titlebar = header = gtk_header_bar_new ();
+  gtk_header_bar_set_title (GTK_HEADER_BAR (header), _("Apply Changes?"));
+
+  size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+  button = gtk_button_new_with_label (_("Cancel"));
+  gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
+  gtk_size_group_add_widget (size_group, button);
+  g_signal_connect_object (button, "clicked", G_CALLBACK (on_screen_changed),
+                           panel, G_CONNECT_SWAPPED);
+
+  button = gtk_button_new_with_label (_("Apply"));
+  gtk_header_bar_pack_end (GTK_HEADER_BAR (header), button);
+  gtk_size_group_add_widget (size_group, button);
+  g_signal_connect_object (button, "clicked", G_CALLBACK (apply_current_configuration),
+                           panel, G_CONNECT_SWAPPED);
+  gtk_style_context_add_class (gtk_widget_get_style_context (button),
+                               GTK_STYLE_CLASS_SUGGESTED_ACTION);
+
+  gtk_widget_show_all (header);
+  g_object_unref (size_group);
+
+  toplevel = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (panel)));
+  header = gtk_window_get_titlebar (GTK_WINDOW (toplevel));
+  if (header)
+    priv->main_titlebar = g_object_ref (header);
+
+  gtk_window_set_titlebar (GTK_WINDOW (toplevel), priv->apply_titlebar);
+  g_object_ref (priv->apply_titlebar);
+}
+
+static void
 update_apply_button (CcDisplayPanel *panel)
 {
   CcDisplayPanelPrivate *priv = panel->priv;
@@ -1167,7 +2769,7 @@ update_apply_button (CcDisplayPanel *panel)
 
   if (!cc_display_config_is_applicable (priv->current_config))
     {
-      gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog), GTK_RESPONSE_ACCEPT, FALSE);
+      reset_titlebar (panel);
       return;
     }
 
@@ -1177,7 +2779,10 @@ update_apply_button (CcDisplayPanel *panel)
                                           applied_config);
   g_object_unref (applied_config);
 
-  gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog), GTK_RESPONSE_ACCEPT, !config_equal);
+  if (config_equal)
+    reset_titlebar (panel);
+  else
+    show_apply_titlebar (panel);
 }
 
 static void
@@ -1213,8 +2818,7 @@ on_output_event (FooScrollArea *area,
     {
       GrabInfo *info;
 
-      self->priv->current_output = output;
-
+      set_current_output (self, output);
 
       if (!cc_display_config_is_cloning (self->priv->current_config) &&
           n_monitors > 1)
@@ -1327,7 +2931,7 @@ paint_background (FooScrollArea *area,
 
   foo_scroll_area_get_viewport (area, &viewport);
 
-  cairo_set_source_rgba (cr, 0, 0, 0, 0.4);
+  cairo_set_source_rgba (cr, 0, 0, 0, 0.0);
   cairo_rectangle (cr,
                    viewport.x, viewport.y,
                    viewport.width, viewport.height);
@@ -1387,7 +2991,7 @@ on_area_paint (FooScrollArea  *area,
 
       cairo_translate (cr, x, y);
       paint_output (self, cr, self->priv->current_config, output,
-                    cc_display_panel_get_output_id (output),
+                    GPOINTER_TO_INT (g_object_get_data (G_OBJECT (output), "ui-number")),
                     w * scale, h * scale);
 
       cairo_restore (cr);
@@ -1414,73 +3018,6 @@ apply_current_configuration (CcDisplayPanel *self)
     }
 }
 
-static void
-dialog_toplevel_focus_changed (GtkWindow      *window,
-                               GParamSpec     *pspec,
-                               CcDisplayPanel *self)
-{
-  ensure_monitor_labels (self);
-}
-
-static void
-show_arrange_displays_dialog (GtkButton      *button,
-                              CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  GtkWidget *content_area, *area, *vbox, *label;
-  gint response;
-
-  /* Title of displays dialog when multiple monitors are present. */
-  priv->dialog = gtk_dialog_new_with_buttons (_("Arrange Combined Displays"),
-                                              GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell 
(CC_PANEL (panel)))),
-                                              GTK_DIALOG_MODAL | GTK_DIALOG_USE_HEADER_BAR,
-                                              _("_Cancel"), GTK_RESPONSE_CANCEL,
-                                              _("_Apply"), GTK_RESPONSE_ACCEPT,
-                                              NULL);
-  g_signal_connect (priv->dialog, "notify::has-toplevel-focus",
-                    G_CALLBACK (dialog_toplevel_focus_changed), panel);
-  gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_ACCEPT);
-  gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog), GTK_RESPONSE_ACCEPT, FALSE);
-
-  content_area = gtk_dialog_get_content_area (GTK_DIALOG (priv->dialog));
-
-  area = (GtkWidget *) foo_scroll_area_new ();
-  g_object_set_data (G_OBJECT (area), "panel", panel);
-
-  foo_scroll_area_set_min_size (FOO_SCROLL_AREA (area), 520, 290);
-  gtk_widget_set_margin_end (area, 12);
-  gtk_widget_set_margin_start (area, 12);
-  gtk_widget_set_size_request (area, 520, 290);
-  g_signal_connect (area, "paint",
-                    G_CALLBACK (on_area_paint), panel);
-  g_signal_connect (area, "viewport_changed",
-                    G_CALLBACK (on_viewport_changed), panel);
-
-  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-  gtk_container_add (GTK_CONTAINER (vbox), area);
-
-  label = gtk_label_new (_("Drag displays to rearrange them"));
-  gtk_widget_set_margin_top (label, 12);
-  gtk_widget_set_margin_bottom (label, 12);
-  gtk_container_add (GTK_CONTAINER (vbox), label);
-  gtk_style_context_add_class (gtk_widget_get_style_context (label), GTK_STYLE_CLASS_DIM_LABEL);
-
-  gtk_widget_show_all (vbox);
-  gtk_container_add (GTK_CONTAINER (content_area), vbox);
-
-  response = gtk_dialog_run (GTK_DIALOG (priv->dialog));
-  if (response == GTK_RESPONSE_ACCEPT)
-    apply_current_configuration (panel);
-  else if (response != GTK_RESPONSE_NONE)
-    {
-      /* re-read the previous configuration */
-      on_screen_changed (panel);
-    }
-
-  gtk_widget_destroy (priv->dialog);
-  priv->dialog = NULL;
-}
-
 static const gchar *
 make_aspect_string (gint width,
                     gint height)
@@ -1547,421 +3084,33 @@ make_resolution_string (CcDisplayMode *mode)
     return g_strdup_printf ("%d × %d%s", width, height, interlaced);
 }
 
-static GtkWidget *
-list_box_item (const gchar *title,
-               const gchar *subtitle)
-{
-  GtkWidget *item, *label, *row;
-
-  row = gtk_list_box_row_new ();
-
-  item = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
-  gtk_container_set_border_width (GTK_CONTAINER (item), 12);
-
-  label = gtk_label_new (title);
-  gtk_container_add (GTK_CONTAINER (item), label);
-  gtk_widget_set_halign (label, GTK_ALIGN_START);
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
-
-  label = gtk_label_new (subtitle);
-  gtk_container_add (GTK_CONTAINER (item), label);
-  gtk_style_context_add_class (gtk_widget_get_style_context (label), GTK_STYLE_CLASS_DIM_LABEL);
-  gtk_widget_set_halign (label, GTK_ALIGN_START);
-  gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
-
-  gtk_container_add (GTK_CONTAINER (row), item);
-
-  return row;
-}
-
-static gboolean
-is_atsc_duplicate_freq (CcDisplayMode *mode,
-                        CcDisplayMode *next_mode)
-{
-  double freq, next_freq;
-  gboolean ret;
-
-  if (next_mode == NULL)
-    return FALSE;
-
-  freq = cc_display_mode_get_freq_f (mode);
-  next_freq = cc_display_mode_get_freq_f (next_mode);
-
-  ret = fabs (freq - (next_freq / 1000.0 * 1001.0)) < 0.01;
-
-  if (ret)
-    g_debug ("Next frequency %f is the NTSC variant of %f",
-             next_freq, freq);
-
-  return ret;
-}
-
-static int
-sort_frequencies (gconstpointer a, gconstpointer b)
-{
-  CcDisplayMode *mode_a = (CcDisplayMode *) a;
-  CcDisplayMode *mode_b = (CcDisplayMode *) b;
-
-  /* Highest to lowest */
-  if (cc_display_mode_get_freq_f (mode_a) > cc_display_mode_get_freq_f (mode_b))
-    return -1;
-  if (cc_display_mode_get_freq_f (mode_a) < cc_display_mode_get_freq_f (mode_b))
-    return 1;
-  return 0;
-}
-
-static void
-setup_frequency_combo_box (CcDisplayPanel *panel,
-                           CcDisplayMode  *resolution_mode)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  CcDisplayMode *current_mode;
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  gchar *res;
-  GSList *l, *frequencies;
-  guint i;
-  gboolean prev_dup;
-
-  current_mode = cc_display_monitor_get_mode (priv->current_output);
-
-  model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->freq_combo));
-  gtk_list_store_clear (GTK_LIST_STORE (model));
-
-  i = 0;
-  res = make_resolution_string (resolution_mode);
-  frequencies = g_slist_copy (g_hash_table_lookup (priv->res_freqs, res));
-  g_free (res);
-  frequencies = g_slist_sort (frequencies, sort_frequencies);
-  prev_dup = FALSE;
-
-  /* Look for 59.94Hz, and if it exists, remove the 60Hz option
-   * in favour of this NTSC/ATSC frequency.
-   * 60Hz is a "PC" frequency, 59.94Hz is a holdover
-   * from NTSC:
-   * https://en.wikipedia.org/wiki/NTSC#Lines_and_refresh_rate
-   *
-   * We also want to handle this for ~30Hz and ~120Hz */
-  for (l = frequencies; l != NULL; l = l->next)
-    {
-      CcDisplayMode *mode = l->data;
-      CcDisplayMode *next_mode;
-      gchar *freq;
-      gboolean dup;
-
-      if (l->next != NULL)
-        next_mode = l->next->data;
-      else
-        next_mode = NULL;
-
-      dup = is_atsc_duplicate_freq (mode, next_mode);
-      if (dup && mode != current_mode)
-        {
-          prev_dup = TRUE;
-          continue;
-        }
-
-      if (prev_dup)
-        {
-          /* translators: example string is "60 Hz (NTSC)"
-           * NTSC is https://en.wikipedia.org/wiki/NTSC */
-          freq = g_strdup_printf (_("%d Hz (NTSC)"),
-                                  (int) (roundf (cc_display_mode_get_freq_f (mode))));
-        }
-      else
-        {
-          /* translators: example string is "60 Hz" */
-          freq = g_strdup_printf (_("%d Hz"), cc_display_mode_get_freq (mode));
-        }
-
-      gtk_list_store_insert_with_values (GTK_LIST_STORE (model), &iter,
-                                         -1, 0, freq, 1, mode, -1);
-      g_free (freq);
-
-      if (mode == current_mode)
-        gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->freq_combo), &iter);
-
-      prev_dup = dup;
-      i++;
-    }
-
-  g_slist_free (frequencies);
-
-  if (i < 2)
-    {
-      gtk_widget_hide (priv->freq_combo);
-      return;
-    }
-
-  gtk_widget_show (priv->freq_combo);
-  if (gtk_combo_box_get_active (GTK_COMBO_BOX (priv->freq_combo)) == -1)
-    gtk_combo_box_set_active (GTK_COMBO_BOX (priv->freq_combo), 0);
-}
-
-static guint
-n_supported_scales (CcDisplayMode *mode)
-{
-  const double *scales = cc_display_mode_get_supported_scales (mode);
-  guint n = 0;
-
-  while (scales[n] != 0.0)
-    n++;
-
-  return n;
-}
-
-static void
-setup_scaling_slider (CcDisplayPanel *panel,
-                      CcDisplayMode  *resolution_mode)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  guint i;
-  guint n;
-  const double *scales;
-
-  scales = cc_display_mode_get_supported_scales (resolution_mode);
-  n = n_supported_scales (resolution_mode);
-
-  if (n > 0)
-    {
-      GtkAdjustment *adj;
-      double current_scale;
-      int current_index = -1;
-
-      current_scale = cc_display_monitor_get_scale (priv->current_output);
-      adj = gtk_range_get_adjustment (GTK_RANGE (priv->scale_slider));
-
-      gtk_scale_clear_marks (GTK_SCALE (priv->scale_slider));
-
-      gtk_adjustment_set_step_increment (adj, 1);
-      gtk_adjustment_set_lower (adj, 0);
-      gtk_adjustment_set_upper (adj, n-1);
-      gtk_scale_set_digits (GTK_SCALE (priv->scale_slider), 0);
-
-      for (i = 0; i < n; ++i)
-        {
-          double rounded_scale = round (scales[i] * 10) / 10;
-          double integral;
-          double fractional = modf (rounded_scale, &integral);
-
-          if (scales[i] == current_scale || (current_index < 0 && scales[i] == 1.0))
-            current_index = i;
-
-          if (fractional != 0 && (fractional != 0.5 || integral > 0))
-            continue;
-
-          gchar *s = g_strdup_printf ("%.2lg×", scales[i]);
-          gtk_scale_add_mark (GTK_SCALE (priv->scale_slider), i, GTK_POS_TOP, s);
-          g_free (s);
-        }
-
-      gtk_widget_show (priv->scale_slider);
-      gtk_range_set_value (GTK_RANGE (priv->scale_slider), current_index);
-    }
-  else
-    {
-      gtk_widget_hide (priv->scale_slider);
-    }
-}
-
-static void
-free_mode_list (gpointer key,
-                gpointer value,
-                gpointer data)
-{
-  g_slist_free (value);
-}
-
-static void
-clear_res_freqs (CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  if (priv->res_freqs)
-    {
-      g_hash_table_foreach (priv->res_freqs, free_mode_list, NULL);
-      g_hash_table_destroy (priv->res_freqs);
-      priv->res_freqs = NULL;
-    }
-}
-
-static void
-setup_resolution_combo_box (CcDisplayPanel  *panel,
-                            GList           *modes,
-                            CcDisplayMode   *current_mode)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  GtkTreeModel *res_model;
-  GList *m;
-
-  clear_res_freqs (panel);
-  priv->res_freqs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
-  res_model = gtk_combo_box_get_model (GTK_COMBO_BOX (priv->res_combo));
-  gtk_list_store_clear (GTK_LIST_STORE (res_model));
-
-  for (m = modes; m != NULL; m = m->next)
-    {
-      CcDisplayMode *mode = m->data;
-      GSList *l;
-      gchar *res;
-      gint output_width, output_height, mode_width, mode_height;
-
-      if (!current_mode)
-        current_mode = mode;
-
-      cc_display_mode_get_resolution (mode, &mode_width, &mode_height);
-
-      cc_display_mode_get_resolution (cc_display_monitor_get_preferred_mode (priv->current_output),
-                                      &output_width, &output_height);
-
-      if (!should_show_resolution (output_width, output_height, mode_width,
-                                   mode_height))
-        continue;
-
-      res = make_resolution_string (mode);
-
-      if ((l = g_hash_table_lookup (priv->res_freqs, res)) == NULL)
-        {
-          GtkTreeIter iter;
-          gint current_mode_width, current_mode_height;
-
-          gtk_list_store_insert_with_values (GTK_LIST_STORE (res_model), &iter,
-                                             -1, 0, res, 1, mode, -1);
-
-          cc_display_mode_get_resolution (current_mode,
-                                          &current_mode_width, &current_mode_height);
-          /* select the current mode in the combo box */
-          if (mode_width == current_mode_width && mode_height == current_mode_height
-              && cc_display_mode_is_interlaced (mode) == cc_display_mode_is_interlaced (current_mode))
-            {
-              gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->res_combo),
-                                             &iter);
-            }
-        }
-
-      l = g_slist_append (l, mode);
-      g_hash_table_replace (priv->res_freqs, res, l);
-    }
-
-  /* ensure a resolution is selected by default */
-  if (gtk_combo_box_get_active (GTK_COMBO_BOX (priv->res_combo)) == -1)
-    gtk_combo_box_set_active (GTK_COMBO_BOX (priv->res_combo), 0);
-
-  setup_frequency_combo_box (panel, current_mode);
-  setup_scaling_slider (panel, current_mode);
-}
-
-
-static void
-setup_listbox_row_activated (GtkListBox     *list_box,
-                             GtkListBoxRow  *row,
-                             CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  GList *modes;
-  gint index;
-
-  if (!row)
-    return;
-
-  index = gtk_list_box_row_get_index (row);
-
-  gtk_widget_set_sensitive (priv->config_grid, index != DISPLAY_MODE_OFF);
-  cc_display_monitor_set_active (priv->current_output,
-                                 (index != DISPLAY_MODE_OFF));
-
-  if (index == DISPLAY_MODE_MIRROR)
-    {
-      modes = cc_display_config_get_cloning_modes (priv->current_config);
-      cc_display_config_set_cloning (priv->current_config, TRUE);
-    }
-  else
-    {
-      cc_display_monitor_set_primary (priv->current_output,
-                                      (index == DISPLAY_MODE_PRIMARY));
-      cc_display_config_set_cloning (priv->current_config, FALSE);
-
-      modes = cc_display_monitor_get_modes (priv->current_output);
-    }
-
-  setup_resolution_combo_box (panel, modes,
-                              cc_display_monitor_get_mode (priv->current_output));
-  update_apply_button (panel);
-}
-
-static void
-rotate_left_clicked (GtkButton      *button,
-                     CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  CcDisplayRotation rotation;
-  gboolean active;
-
-  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
-
-  if (active)
-    {
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rotate_right_button), FALSE);
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->upside_down_button), FALSE);
-      rotation = CC_DISPLAY_ROTATION_90;
-    }
-  else
-    {
-      rotation = CC_DISPLAY_ROTATION_NONE;
-    }
-
-  cc_display_monitor_set_rotation (priv->current_output, rotation);
-  update_apply_button (panel);
-}
-
-static void
-upside_down_clicked (GtkButton      *button,
-                     CcDisplayPanel *panel)
+static const gchar *
+get_resolution_string (CcDisplayMode *mode)
 {
-  CcDisplayPanelPrivate *priv = panel->priv;
-  CcDisplayRotation rotation;
-  gboolean active;
+  char *resolution;
 
-  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+  resolution = g_object_get_data (G_OBJECT (mode), "resolution");
+  if (resolution)
+    return resolution;
 
-  if (active)
-    {
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rotate_left_button), FALSE);
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rotate_right_button), FALSE);
-      rotation = CC_DISPLAY_ROTATION_180;
-    }
-  else
-    {
-      rotation = CC_DISPLAY_ROTATION_NONE;
-    }
-
-  cc_display_monitor_set_rotation (priv->current_output, rotation);
-  update_apply_button (panel);
+  resolution = make_resolution_string (mode);
+  g_object_set_data_full (G_OBJECT (mode), "resolution", resolution, g_free);
+  return resolution;
 }
 
-static void
-rotate_right_clicked (GtkButton      *button,
-                      CcDisplayPanel *panel)
+static const gchar *
+get_frequency_string (CcDisplayMode *mode)
 {
-  CcDisplayPanelPrivate *priv = panel->priv;
-  CcDisplayRotation rotation;
-  gboolean active;
+  char *frequency;
 
-  active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+  frequency = g_object_get_data (G_OBJECT (mode), "frequency");
+  if (frequency)
+    return frequency;
 
-  if (active)
-    {
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rotate_left_button), FALSE);
-      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->upside_down_button), FALSE);
-      rotation = CC_DISPLAY_ROTATION_270;
-    }
-  else
-    {
-      rotation = CC_DISPLAY_ROTATION_NONE;
-    }
+  frequency = g_strdup_printf (_("%.1lf Hz"), cc_display_mode_get_freq_f (mode));
 
-  cc_display_monitor_set_rotation (priv->current_output, rotation);
-  update_apply_button (panel);
+  g_object_set_data_full (G_OBJECT (mode), "frequency", frequency, g_free);
+  return frequency;
 }
 
 static const double known_diagonals[] = {
@@ -2003,77 +3152,6 @@ make_display_size_string (int width_mm,
   return inches;
 }
 
-static void
-freq_combo_changed (GtkComboBox    *combo,
-                    CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  CcDisplayMode *mode;
-
-  model = gtk_combo_box_get_model (combo);
-
-  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
-    {
-      gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 1, &mode, -1);
-      if (mode)
-        {
-          cc_display_monitor_set_mode (priv->current_output, mode);
-          update_apply_button (panel);
-        }
-    }
-}
-
-static void
-res_combo_changed (GtkComboBox    *combo,
-                   CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  GtkTreeModel *res_model;
-  GtkTreeIter iter;
-  CcDisplayMode *mode;
-
-  res_model = gtk_combo_box_get_model (combo);
-
-  if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
-    {
-      gtk_tree_model_get (GTK_TREE_MODEL (res_model), &iter, 1, &mode, -1);
-
-      if (cc_display_config_is_cloning (priv->current_config))
-        {
-          GList *outputs, *l;
-          outputs = cc_display_config_get_monitors (priv->current_config);
-          for (l = outputs; l != NULL; l = l->next)
-            cc_display_monitor_set_mode (CC_DISPLAY_MONITOR (l->data), mode);
-        }
-      else
-        {
-          cc_display_monitor_set_mode (priv->current_output, mode);
-        }
-
-      update_apply_button (panel);
-
-      setup_frequency_combo_box (panel, mode);
-      setup_scaling_slider (panel, mode);
-    }
-}
-
-static void
-sanity_check_rotation (CcDisplayMonitor *output)
-{
-  CcDisplayRotation rotation;
-
-  rotation = cc_display_monitor_get_rotation (output);
-
-  /* other options such as reflection are not supported */
-  rotation &= (CC_DISPLAY_ROTATION_NONE | CC_DISPLAY_ROTATION_90
-               | CC_DISPLAY_ROTATION_180 | CC_DISPLAY_ROTATION_270);
-  if (rotation == 0)
-    rotation = CC_DISPLAY_ROTATION_NONE;
-  cc_display_monitor_set_rotation (output, rotation);
-}
-
 static gboolean
 should_show_rotation (CcDisplayPanel *panel,
                       CcDisplayMonitor  *output)
@@ -2099,495 +3177,7 @@ should_show_rotation (CcDisplayPanel *panel,
 }
 
 static void
-underscan_switch_toggled (CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  gboolean value;
-
-  value = gtk_switch_get_active (GTK_SWITCH (priv->scaling_switch));
-  cc_display_monitor_set_underscanning (priv->current_output, value);
-  update_apply_button (panel);
-}
-
-static double
-scale_slider_get_selected_scale (CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  CcDisplayMode *mode = cc_display_monitor_get_mode (priv->current_output);
-  const double *scales = cc_display_mode_get_supported_scales (mode);
-  int selected = gtk_range_get_value (GTK_RANGE (priv->scale_slider));
-
-  g_return_val_if_fail (selected < n_supported_scales (mode), 1.0);
-
-  return scales[selected];
-}
-
-static void
-scale_slider_changed (GtkRange *slider,
-                      CcDisplayPanel *panel)
-{
-  cc_display_monitor_set_scale (panel->priv->current_output,
-                                scale_slider_get_selected_scale (panel));
-  update_apply_button (panel);
-}
-
-static char *
-scale_slider_format_value (GtkScale *slider,
-                           double value,
-                           CcDisplayPanel *panel)
-{
-  return g_strdup_printf ("%.3g", scale_slider_get_selected_scale (panel));
-}
-
-static void
-show_setup_dialog (CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  GtkWidget *listbox = NULL, *content_area, *item, *box, *frame, *preview;
-  GtkWidget *label, *rotate_box;
-  gint width_mm, height_mm, old_width, old_height, grid_pos;
-  gchar *str;
-  gboolean clone, was_clone, primary, active;
-  GtkListStore *res_model, *freq_model;
-  GtkCellRenderer *renderer;
-  CcDisplayRotation rotation;
-  gboolean show_rotation;
-  gint response, num_active_outputs = 0;
-  GList *outputs, *l;
-
-  outputs = cc_display_config_get_monitors (priv->current_config);
-
-  /* count the number of active */
-  for (l = outputs; l != NULL; l = l->next)
-    {
-      CcDisplayMonitor *output = l->data;
-      if (cc_display_monitor_is_active (output))
-        num_active_outputs++;
-    }
-
-  priv->dialog = gtk_dialog_new_with_buttons (cc_display_monitor_get_display_name (priv->current_output),
-                                              GTK_WINDOW (cc_shell_get_toplevel (cc_panel_get_shell 
(CC_PANEL (panel)))),
-                                              GTK_DIALOG_MODAL | GTK_DIALOG_USE_HEADER_BAR,
-                                              _("_Cancel"), GTK_RESPONSE_CANCEL,
-                                              _("_Apply"), GTK_RESPONSE_ACCEPT,
-                                              NULL);
-  g_signal_connect (priv->dialog, "notify::has-toplevel-focus",
-                    G_CALLBACK (dialog_toplevel_focus_changed), panel);
-  gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog), GTK_RESPONSE_ACCEPT);
-  gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog), GTK_RESPONSE_ACCEPT, FALSE);
-  gtk_window_set_resizable (GTK_WINDOW (priv->dialog), FALSE);
-
-  content_area = gtk_dialog_get_content_area (GTK_DIALOG (priv->dialog));
-
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
-  gtk_widget_set_margin_start (box, 12);
-  gtk_widget_set_margin_end (box, 12);
-  gtk_widget_set_margin_top (box, 6);
-  gtk_widget_set_margin_bottom (box, 12);
-  gtk_container_add (GTK_CONTAINER (content_area), box);
-
-  /* configuration grid */
-  grid_pos = 0;
-  priv->config_grid = gtk_grid_new ();
-  gtk_widget_set_margin_start (priv->config_grid, 36);
-  gtk_widget_set_margin_end (priv->config_grid, 36);
-  gtk_widget_set_margin_bottom (priv->config_grid, 6);
-  gtk_grid_set_column_spacing (GTK_GRID (priv->config_grid), 12);
-  gtk_grid_set_row_spacing (GTK_GRID (priv->config_grid), 12);
-
-  /* preview */
-  preview = display_preview_new (panel, priv->current_output,
-                                 priv->current_config,
-                                 cc_display_panel_get_output_id (priv->current_output),
-                                 DISPLAY_PREVIEW_SETUP_HEIGHT);
-  gtk_grid_attach (GTK_GRID (priv->config_grid), preview, 0, grid_pos, 2, 1);
-  grid_pos++;
-
-  /* rotation */
-  show_rotation = should_show_rotation (panel, priv->current_output);
-  rotation = cc_display_monitor_get_rotation (priv->current_output);
-
-  if (show_rotation)
-    {
-      rotate_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
-      gtk_widget_set_margin_bottom (rotate_box, 12);
-      gtk_style_context_add_class (gtk_widget_get_style_context (rotate_box),
-                                   GTK_STYLE_CLASS_LINKED);
-      gtk_grid_attach (GTK_GRID (priv->config_grid), rotate_box, 0, grid_pos, 2, 1);
-      gtk_widget_set_halign (rotate_box, GTK_ALIGN_CENTER);
-      grid_pos++;
-
-      if (cc_display_monitor_supports_rotation (priv->current_output,
-                                                CC_DISPLAY_ROTATION_90))
-        {
-          priv->rotate_left_button = gtk_toggle_button_new ();
-          gtk_widget_set_tooltip_text (priv->rotate_left_button, _("Rotate counterclockwise by 90\xc2\xb0"));
-          if (rotation == CC_DISPLAY_ROTATION_90)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rotate_left_button), TRUE);
-          g_signal_connect (priv->rotate_left_button, "clicked",
-                            G_CALLBACK (rotate_left_clicked), panel);
-          g_signal_connect_swapped (priv->rotate_left_button, "clicked",
-                                    G_CALLBACK (gtk_widget_queue_draw), preview);
-          gtk_container_add (GTK_CONTAINER (priv->rotate_left_button),
-                             gtk_image_new_from_icon_name ("object-rotate-left-symbolic",
-                                                           GTK_ICON_SIZE_BUTTON));
-          gtk_widget_set_halign (priv->rotate_left_button, GTK_ALIGN_END);
-          gtk_container_add (GTK_CONTAINER (rotate_box), priv->rotate_left_button);
-        }
-
-      if (cc_display_monitor_supports_rotation (priv->current_output,
-                                                CC_DISPLAY_ROTATION_180))
-        {
-          priv->upside_down_button = gtk_toggle_button_new ();
-          gtk_widget_set_tooltip_text (priv->upside_down_button, _("Rotate by 180\xc2\xb0"));
-          if (rotation == CC_DISPLAY_ROTATION_180)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->upside_down_button), TRUE);
-          g_signal_connect (priv->upside_down_button, "clicked",
-                            G_CALLBACK (upside_down_clicked), panel);
-          g_signal_connect_swapped (priv->upside_down_button, "clicked",
-                                    G_CALLBACK (gtk_widget_queue_draw), preview);
-          gtk_container_add (GTK_CONTAINER (priv->upside_down_button),
-                             gtk_image_new_from_icon_name ("object-flip-vertical-symbolic",
-                                                           GTK_ICON_SIZE_BUTTON));
-          gtk_widget_set_halign (priv->upside_down_button, GTK_ALIGN_FILL);
-          gtk_container_add (GTK_CONTAINER (rotate_box), priv->upside_down_button);
-        }
-
-      if (cc_display_monitor_supports_rotation (priv->current_output,
-                                                CC_DISPLAY_ROTATION_270))
-        {
-          priv->rotate_right_button = gtk_toggle_button_new ();
-          gtk_widget_set_tooltip_text (priv->rotate_right_button, _("Rotate clockwise by 90\xc2\xb0"));
-          if (rotation == CC_DISPLAY_ROTATION_270)
-            gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->rotate_right_button), TRUE);
-          g_signal_connect (priv->rotate_right_button, "clicked",
-                            G_CALLBACK (rotate_right_clicked), panel);
-          g_signal_connect_swapped (priv->rotate_right_button, "clicked",
-                                    G_CALLBACK (gtk_widget_queue_draw), preview);
-          gtk_container_add (GTK_CONTAINER (priv->rotate_right_button),
-                             gtk_image_new_from_icon_name ("object-rotate-right-symbolic",
-                                                           GTK_ICON_SIZE_BUTTON));
-          gtk_widget_set_halign (priv->rotate_right_button, GTK_ALIGN_START);
-          gtk_container_add (GTK_CONTAINER (rotate_box), priv->rotate_right_button);
-        }
-    }
-
-  /* size */
-  cc_display_monitor_get_physical_size (priv->current_output, &width_mm, &height_mm);
-  str = make_display_size_string (width_mm, height_mm);
-
-  if (str != NULL)
-    {
-      label = gtk_label_new (_("Size"));
-      gtk_style_context_add_class (gtk_widget_get_style_context (label),
-                                   GTK_STYLE_CLASS_DIM_LABEL);
-      gtk_grid_attach (GTK_GRID (priv->config_grid), label, 0, grid_pos, 1, 1);
-      gtk_widget_set_halign (label, GTK_ALIGN_END);
-
-      label = gtk_label_new (str);
-      gtk_grid_attach (GTK_GRID (priv->config_grid), label, 1, grid_pos, 1, 1);
-      gtk_widget_set_halign (label, GTK_ALIGN_START);
-      g_free (str);
-
-      grid_pos++;
-    }
-
-  /* aspect ratio */
-  label = gtk_label_new (_("Aspect Ratio"));
-  gtk_style_context_add_class (gtk_widget_get_style_context (label),
-                               GTK_STYLE_CLASS_DIM_LABEL);
-  gtk_grid_attach (GTK_GRID (priv->config_grid), label, 0, grid_pos, 1, 1);
-  gtk_widget_set_halign (label, GTK_ALIGN_END);
-  {
-    int w, h;
-    cc_display_mode_get_resolution (cc_display_monitor_get_preferred_mode (priv->current_output),
-                                    &w, &h);
-    label = gtk_label_new (make_aspect_string (w, h));
-  }
-  gtk_grid_attach (GTK_GRID (priv->config_grid), label, 1, grid_pos, 1, 1);
-  gtk_widget_set_halign (label, GTK_ALIGN_START);
-  grid_pos++;
-
-  /* resolution combo box */
-  res_model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
-  priv->res_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (res_model));
-  g_object_unref (res_model);
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->res_combo), renderer, TRUE);
-  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->res_combo), renderer, "text", 0);
-  g_signal_connect (priv->res_combo, "changed", G_CALLBACK (res_combo_changed),
-                    panel);
-  g_signal_connect_swapped (priv->res_combo, "changed",
-                            G_CALLBACK (gtk_widget_queue_draw), preview);
-
-  label = gtk_label_new (_("Resolution"));
-  gtk_style_context_add_class (gtk_widget_get_style_context (label),
-                               GTK_STYLE_CLASS_DIM_LABEL);
-  gtk_grid_attach (GTK_GRID (priv->config_grid), label, 0, grid_pos, 1, 1);
-  gtk_grid_attach (GTK_GRID (priv->config_grid), priv->res_combo, 1, grid_pos, 1, 1);
-  grid_pos++;
-
-  gtk_widget_set_halign (label, GTK_ALIGN_END);
-  gtk_widget_set_halign (priv->res_combo, GTK_ALIGN_START);
-
-  /* overscan */
-  if (!cc_display_monitor_is_builtin (priv->current_output) &&
-      cc_display_monitor_supports_underscanning (priv->current_output))
-    {
-      priv->scaling_switch = gtk_switch_new ();
-      gtk_switch_set_active (GTK_SWITCH (priv->scaling_switch),
-                             cc_display_monitor_get_underscanning (priv->current_output));
-      g_signal_connect_swapped (G_OBJECT (priv->scaling_switch), "notify::active",
-                                G_CALLBACK (underscan_switch_toggled), panel);
-
-      label = gtk_label_new (_("Adjust for TV"));
-      gtk_style_context_add_class (gtk_widget_get_style_context (label),
-                                   GTK_STYLE_CLASS_DIM_LABEL);
-      gtk_grid_attach (GTK_GRID (priv->config_grid), label, 0, grid_pos, 1, 1);
-      gtk_grid_attach (GTK_GRID (priv->config_grid), priv->scaling_switch, 1, grid_pos, 1, 1);
-      grid_pos++;
-
-      gtk_widget_set_halign (label, GTK_ALIGN_END);
-      gtk_widget_set_halign (priv->scaling_switch, GTK_ALIGN_START);
-    }
-
-  /* frequency combo box */
-  freq_model = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_POINTER);
-  priv->freq_combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (freq_model));
-  g_object_unref (freq_model);
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->freq_combo), renderer, TRUE);
-  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->freq_combo), renderer, "text", 0);
-  g_signal_connect (priv->freq_combo, "changed", G_CALLBACK (freq_combo_changed),
-                    panel);
-  gtk_grid_attach (GTK_GRID (priv->config_grid), priv->freq_combo, 1, grid_pos, 1, 1);
-  gtk_widget_set_halign (priv->freq_combo, GTK_ALIGN_START);
-  gtk_widget_set_no_show_all (priv->freq_combo, TRUE);
-
-  label = gtk_label_new (_("Refresh Rate"));
-  gtk_style_context_add_class (gtk_widget_get_style_context (label),
-                               GTK_STYLE_CLASS_DIM_LABEL);
-  gtk_grid_attach (GTK_GRID (priv->config_grid), label, 0, grid_pos, 1, 1);
-  gtk_widget_set_halign (label, GTK_ALIGN_END);
-  gtk_widget_set_no_show_all (label, TRUE);
-  g_object_bind_property (priv->freq_combo, "visible",
-                          label, "visible", G_BINDING_BIDIRECTIONAL);
-  grid_pos++;
-
-  /* scale */
-  priv->scale_slider = gtk_scale_new (GTK_ORIENTATION_HORIZONTAL, NULL);
-  gtk_widget_set_no_show_all (priv->scale_slider, TRUE);
-
-  g_signal_connect (priv->scale_slider, "format-value",
-                    G_CALLBACK (scale_slider_format_value), panel);
-  g_signal_connect (priv->scale_slider, "value-changed",
-                    G_CALLBACK (scale_slider_changed), panel);
-
-  gtk_scale_set_draw_value (GTK_SCALE (priv->scale_slider), TRUE);
-  gtk_range_set_round_digits (GTK_RANGE (priv->scale_slider), 2);
-  gtk_scale_set_value_pos (GTK_SCALE (priv->scale_slider), GTK_POS_BOTTOM);
-  gtk_scale_set_has_origin (GTK_SCALE (priv->scale_slider), FALSE);
-  gtk_grid_attach (GTK_GRID (priv->config_grid),
-                   priv->scale_slider, 1, grid_pos, 1, 1);
-  gtk_widget_set_halign (priv->freq_combo, GTK_ALIGN_CENTER);
-  grid_pos++;
-
-  was_clone = clone = cc_display_config_is_cloning (priv->current_config);
-  primary = cc_display_monitor_is_primary (priv->current_output);
-  active = cc_display_monitor_is_active (priv->current_output);
-
-  if (num_active_outputs > 1 || !active)
-    {
-      frame = gtk_frame_new (NULL);
-      gtk_container_add (GTK_CONTAINER (box), frame);
-      gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
-
-      listbox = gtk_list_box_new ();
-      gtk_container_add (GTK_CONTAINER (frame), listbox);
-      gtk_list_box_set_header_func (GTK_LIST_BOX (listbox),
-                                    cc_list_box_update_header_func,
-                                    NULL, NULL);
-      g_signal_connect (listbox, "row-selected",
-                        G_CALLBACK (setup_listbox_row_activated), panel);
-      g_signal_connect_swapped (listbox, "row-selected",
-                                G_CALLBACK (gtk_widget_queue_draw), preview);
-      gtk_widget_show (listbox);
-
-      item = list_box_item (_("Primary"),
-                            _("Show the top bar and Activities Overview on this display"));
-      gtk_container_add (GTK_CONTAINER (listbox), item);
-      if (primary)
-        gtk_list_box_select_row (GTK_LIST_BOX (listbox),
-                                 GTK_LIST_BOX_ROW (item));
-
-      item = list_box_item (_("Secondary Display"),
-                            _("Join this display with another to create an extra workspace"));
-      gtk_container_add (GTK_CONTAINER (listbox), item);
-      if (!primary && !clone)
-        gtk_list_box_select_row (GTK_LIST_BOX (listbox),
-                                 GTK_LIST_BOX_ROW (item));
-
-#if 0
-      item = list_box_item (_("Presentation"),
-                            _("Show slideshows and media only"));
-      gtk_container_add (GTK_CONTAINER (listbox), item);
-#endif
-
-      /* translators: "Mirror" describes when both displays show the same view */
-      item = list_box_item (_("Mirror"),
-                            _("Show your existing view on both displays"));
-      gtk_container_add (GTK_CONTAINER (listbox), item);
-      if (clone && active)
-        gtk_list_box_select_row (GTK_LIST_BOX (listbox),
-                                 GTK_LIST_BOX_ROW (item));
-
-      item = list_box_item (_("Turn Off"),
-                            _("Don’t use this display"));
-      gtk_container_add (GTK_CONTAINER (listbox), item);
-
-      if (!active)
-        gtk_list_box_select_row (GTK_LIST_BOX (listbox),
-                                 GTK_LIST_BOX_ROW (item));
-    }
-  else
-    {
-      setup_resolution_combo_box (panel,
-                                  cc_display_monitor_get_modes (priv->current_output),
-                                  cc_display_monitor_get_mode (priv->current_output));
-    }
-
-  content_area = gtk_dialog_get_content_area (GTK_DIALOG (priv->dialog));
-  gtk_container_add (GTK_CONTAINER (box), priv->config_grid);
-  gtk_widget_show_all (box);
-
-  cc_display_monitor_get_geometry (priv->current_output, NULL, NULL,
-                                   &old_width, &old_height);
-
-  response = gtk_dialog_run (GTK_DIALOG (priv->dialog));
-  if (response == GTK_RESPONSE_ACCEPT)
-    {
-      GtkListBoxRow *row;
-      gboolean active = TRUE;
-
-      if (g_hash_table_size (output_ids) > 1)
-        {
-          gboolean primary_chosen = FALSE;
-
-          row = gtk_list_box_get_selected_row (GTK_LIST_BOX (listbox));
-
-          switch (gtk_list_box_row_get_index (row))
-            {
-            case DISPLAY_MODE_PRIMARY:
-              primary = TRUE;
-              clone = FALSE;
-              break;
-
-#if 0
-            case DISPLAY_MODE_PRESENTATION:
-              gnome_rr_config_set_clone (priv->current_configuration, FALSE);
-              primary = FALSE;
-              clone = FALSE;
-              break;
-#endif
-
-            case DISPLAY_MODE_MIRROR:
-              clone = TRUE;
-              break;
-
-            case DISPLAY_MODE_SECONDARY:
-              primary = FALSE;
-              clone = FALSE;
-              break;
-
-            case DISPLAY_MODE_OFF:
-              primary = FALSE;
-              clone = FALSE;
-              active = FALSE;
-              break;
-            }
-
-          cc_display_monitor_set_active (priv->current_output, active);
-          cc_display_monitor_set_primary (priv->current_output, primary);
-          cc_display_config_set_cloning (priv->current_config, clone);
-
-          for (l = outputs; l != NULL; l = l->next)
-            {
-              CcDisplayMonitor *output = l->data;
-
-              if (!cc_display_monitor_is_active (output))
-                continue;
-
-              if (clone)
-                {
-                  /* set all active outputs to the same size and position when
-                   * cloning */
-                  cc_display_monitor_set_mode (output,
-                                               cc_display_monitor_get_mode (priv->current_output));
-                  cc_display_monitor_set_position (output, 0, 0);
-                }
-              else if (output != priv->current_output)
-                {
-                  /* ensure no other outputs are primary if this output is now
-                   * primary, or find another output to set as primary if this
-                   * output is no longer primary */
-                  if (primary)
-                    {
-                      cc_display_monitor_set_primary (output, FALSE);
-                    }
-                  else if (!primary_chosen)
-                    {
-                      cc_display_monitor_set_primary (output, TRUE);
-                      primary_chosen = TRUE;
-                    }
-                }
-            }
-
-          sanity_check_rotation (priv->current_output);
-
-          /* The new API checks with mutter every time
-             update_apply_button() is called and mutter is very
-             strict about this so it already does these adjustments
-             internally. */
-          if (!priv->have_new_dbus_api)
-            {
-              /* if the display was previously in clone mode, ensure the outputs
-               * are arranged correctly */
-              if ((was_clone && !clone))
-                lay_out_outputs_horizontally (panel);
-
-              if (!clone)
-                realign_outputs_after_resolution_change (panel,
-                                                         priv->current_output,
-                                                         old_width, old_height, rotation);
-            }
-        }
-      else
-        {
-          /* check rotation */
-          sanity_check_rotation (priv->current_output);
-        }
-
-      apply_current_configuration (panel);
-    }
-  else if (response != GTK_RESPONSE_NONE)
-    {
-      /* changes cancelled, so re-read the current configuration */
-      on_screen_changed (panel);
-    }
-
-  priv->rotate_left_button = NULL;
-  priv->rotate_right_button = NULL;
-  priv->res_combo = NULL;
-  priv->freq_combo = NULL;
-  clear_res_freqs (panel);
-  gtk_widget_destroy (priv->dialog);
-  priv->dialog = NULL;
-}
-
-static void
-cc_display_panel_night_light_activated (GtkListBox     *listbox,
-                                          GtkWidget      *row,
-                                          CcDisplayPanel *panel)
+cc_display_panel_night_light_activated (CcDisplayPanel *panel)
 {
   CcDisplayPanelPrivate *priv = panel->priv;
   GtkWindow *toplevel;
@@ -2596,26 +3186,6 @@ cc_display_panel_night_light_activated (GtkListBox     *listbox,
 }
 
 static void
-cc_display_panel_box_row_activated (GtkListBox     *listbox,
-                                    GtkWidget      *row,
-                                    CcDisplayPanel *panel)
-{
-  CcDisplayPanelPrivate *priv = panel->priv;
-  CcDisplayMonitor *output;
-
-  gtk_list_box_select_row (listbox, NULL);
-
-  output = g_object_get_data (G_OBJECT (row), "cc-display-monitor");
-
-  if (!output)
-    return;
-
-  priv->current_output = output;
-
-  show_setup_dialog (panel);
-}
-
-static void
 mapped_cb (CcDisplayPanel *panel)
 {
   CcDisplayPanelPrivate *priv = panel->priv;
@@ -2624,9 +3194,9 @@ mapped_cb (CcDisplayPanel *panel)
 
   shell = cc_panel_get_shell (CC_PANEL (panel));
   toplevel = cc_shell_get_toplevel (shell);
-  if (toplevel)
-    priv->focus_id = g_signal_connect (toplevel, "notify::has-toplevel-focus",
-                                       G_CALLBACK (dialog_toplevel_focus_changed), panel);
+  if (toplevel && !priv->focus_id)
+    priv->focus_id = g_signal_connect_swapped (toplevel, "notify::has-toplevel-focus",
+                                               G_CALLBACK (dialog_toplevel_focus_changed), panel);
 }
 
 static void
@@ -2767,41 +3337,28 @@ static GtkWidget *
 make_night_light_widget (CcDisplayPanel *self)
 {
   CcDisplayPanelPrivate *priv = DISPLAY_PANEL_PRIVATE (self);
-  GtkWidget *frame, *row, *box, *label;
+  GtkWidget *frame, *row, *label, *state_label;
   GtkWidget *night_light_listbox;
 
-  frame = gtk_frame_new (NULL);
-  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
-  night_light_listbox = gtk_list_box_new ();
-  gtk_list_box_set_selection_mode (GTK_LIST_BOX (night_light_listbox),
-                                   GTK_SELECTION_NONE);
+  frame = make_frame (NULL, NULL);
+  night_light_listbox = make_list_box ();
   gtk_container_add (GTK_CONTAINER (frame), night_light_listbox);
-  g_signal_connect (night_light_listbox, "row-activated",
-                    G_CALLBACK (cc_display_panel_night_light_activated),
-                    self);
-  row = gtk_list_box_row_new ();
-  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 50);
-  gtk_container_add (GTK_CONTAINER (row), box);
-  gtk_container_add (GTK_CONTAINER (night_light_listbox), row);
 
   label = gtk_label_new (_("_Night Light"));
-  gtk_widget_set_halign (label, GTK_ALIGN_START);
   gtk_label_set_use_underline (GTK_LABEL (label), TRUE);
-  gtk_widget_set_margin_start (label, 20);
-  gtk_widget_set_margin_end (label, 20);
-  gtk_widget_set_margin_top (label, 12);
-  gtk_widget_set_margin_bottom (label, 12);
-  gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
-
-  label = gtk_label_new ("");
-  gtk_widget_set_halign (label, GTK_ALIGN_END);
-  gtk_widget_set_margin_start (label, 24);
-  gtk_widget_set_margin_end (label, 24);
-  gtk_box_pack_start (GTK_BOX (box), label, FALSE, TRUE, 0);
+
+  state_label = gtk_label_new ("");
   g_signal_connect_object (priv->settings_color, "changed",
-                           G_CALLBACK (settings_color_changed_cb), label, 0);
-  night_light_sync_label (label, priv->settings_color);
+                           G_CALLBACK (settings_color_changed_cb), state_label, 0);
+  night_light_sync_label (state_label, priv->settings_color);
 
+  row = make_row (priv->rows_size_group, label, state_label);
+  gtk_container_add (GTK_CONTAINER (night_light_listbox), row);
+  g_signal_connect_object (row, "activated",
+                           G_CALLBACK (cc_display_panel_night_light_activated),
+                           self, G_CONNECT_SWAPPED);
+
+  gtk_widget_set_margin_top (frame, SECTION_PADDING);
   return frame;
 }
 
@@ -2886,7 +3443,6 @@ static void
 cc_display_panel_init (CcDisplayPanel *self)
 {
   CcDisplayPanelPrivate *priv;
-  GtkWidget *frame, *vbox;
   GSettings *settings;
 
   g_resources_register (cc_display_get_resource ());
@@ -2910,41 +3466,6 @@ cc_display_panel_init (CcDisplayPanel *self)
   priv->night_light_dialog = cc_night_light_dialog_new ();
   priv->settings_color = g_settings_new ("org.gnome.settings-daemon.plugins.color");
 
-  output_ids = g_hash_table_new (g_direct_hash, g_direct_equal);
-
-  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 22);
-  gtk_stack_add_named (GTK_STACK (priv->stack), vbox, "main");
-  gtk_stack_set_visible_child (GTK_STACK (priv->stack), vbox);
-
-  frame = gtk_frame_new (NULL);
-  gtk_widget_set_margin_start (vbox, 134);
-  gtk_widget_set_margin_end (vbox, 134);
-  gtk_widget_set_margin_top (vbox, 22);
-  gtk_widget_set_margin_bottom (vbox, 22);
-  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
-  gtk_container_add (GTK_CONTAINER (vbox), frame);
-
-  priv->displays_listbox = gtk_list_box_new ();
-  gtk_list_box_set_header_func (GTK_LIST_BOX (priv->displays_listbox),
-                                cc_list_box_update_header_func, NULL,
-                                NULL);
-  g_signal_connect (priv->displays_listbox, "row-activated",
-                    G_CALLBACK (cc_display_panel_box_row_activated),
-                    self);
-  gtk_container_add (GTK_CONTAINER (frame), priv->displays_listbox);
-
-
-  priv->arrange_button = gtk_button_new_with_mnemonic (_("_Arrange Combined Displays"));
-  g_signal_connect (priv->arrange_button, "clicked",
-                    G_CALLBACK (show_arrange_displays_dialog), self);
-  gtk_widget_set_halign (priv->arrange_button, GTK_ALIGN_CENTER);
-
-  gtk_container_add (GTK_CONTAINER (vbox), priv->arrange_button);
-
-
-  gtk_container_add (GTK_CONTAINER (vbox), make_night_light_widget (self));
-  gtk_widget_show_all (vbox);
-
   self->priv->up_client = up_client_new ();
   if (up_client_get_lid_is_present (self->priv->up_client))
     {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]