[gnome-control-center] color: Use a EggListBox for the main device display to match the new mockups



commit 80ec65b2eff88e7869799eecb55f2aa4bcec37ab
Author: Richard Hughes <richard hughsie com>
Date:   Sun Jan 6 10:52:40 2013 +0000

    color: Use a EggListBox for the main device display to match the new mockups
    
    This matches the new mockups as specified by Allan in
    https://raw.github.com/gnome-design-team/gnome-mockups/master/system-settings/color/panel.png
    
    The CcColorDevice and CcColorProfile widgets are lines in the EggListBox and are
    smart by watching for changes in each colord device.
    
    To use this new functionality you need colord from git master, and for the
    'Laptop' devices to be recognised as internal, you also need to be _running_
    gnome-settings-daemon from git, although this is not strictly required.

 egg-list-box                    |    2 +-
 panels/color/Makefile.am        |    6 +
 panels/color/cc-color-common.c  |  120 ++++
 panels/color/cc-color-common.h  |   35 +
 panels/color/cc-color-device.c  |  321 +++++++++
 panels/color/cc-color-device.h  |   66 ++
 panels/color/cc-color-panel.c   | 1389 +++++++++++++--------------------------
 panels/color/cc-color-profile.c |  497 ++++++++++++++
 panels/color/cc-color-profile.h |   68 ++
 panels/color/color.ui           |   56 ++-
 po/POTFILES.in                  |    3 +
 11 files changed, 1603 insertions(+), 960 deletions(-)
---
diff --git a/egg-list-box b/egg-list-box
index ebcc7ed..33c2f96 160000
--- a/egg-list-box
+++ b/egg-list-box
@@ -1 +1 @@
-Subproject commit ebcc7ed3bd2782e29e56b5452a5d4080d4d87cc7
+Subproject commit 33c2f9634ac3cbec95d868b3e16fadd678f8e525
diff --git a/panels/color/Makefile.am b/panels/color/Makefile.am
index 8cfa6e3..938dd72 100644
--- a/panels/color/Makefile.am
+++ b/panels/color/Makefile.am
@@ -21,6 +21,12 @@ libcolor_la_SOURCES =		\
 	cc-color-calibrate.h		\
 	cc-color-cell-renderer-text.c	\
 	cc-color-cell-renderer-text.h	\
+	cc-color-profile.c	\
+	cc-color-profile.h	\
+	cc-color-device.c	\
+	cc-color-device.h	\
+	cc-color-common.c	\
+	cc-color-common.h	\
 	cc-color-panel.c	\
 	cc-color-panel.h
 
diff --git a/panels/color/cc-color-common.c b/panels/color/cc-color-common.c
new file mode 100644
index 0000000..7363bb4
--- /dev/null
+++ b/panels/color/cc-color-common.c
@@ -0,0 +1,120 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "cc-color-common.h"
+
+gchar *
+cc_color_device_get_title (CdDevice *device)
+{
+  const gchar *tmp;
+  GString *string;
+
+  string = g_string_new ("");
+
+  /* is laptop panel */
+  if (cd_device_get_kind (device) == CD_DEVICE_KIND_DISPLAY &&
+      cd_device_get_embedded (device))
+    {
+      /* TRANSLATORS: This refers to the TFT display on a laptop */
+      g_string_append (string, _("Laptop Screen"));
+      goto out;
+    }
+
+  /* is internal webcam */
+  if (cd_device_get_kind (device) == CD_DEVICE_KIND_WEBCAM &&
+      cd_device_get_embedded (device))
+    {
+      /* TRANSLATORS: This refers to the embedded webcam on a laptop */
+      g_string_append (string, _("Built-in Webcam"));
+      goto out;
+    }
+
+  /* get the display model, falling back to something sane */
+  tmp = cd_device_get_model (device);
+  if (tmp == NULL)
+    tmp = cd_device_get_vendor (device);
+  if (tmp == NULL)
+    tmp = cd_device_get_id (device);
+
+  switch (cd_device_get_kind (device)) {
+    case CD_DEVICE_KIND_DISPLAY:
+      /* TRANSLATORS: an externally connected display, where %s is either the
+       * model, vendor or ID, e.g. 'LP2480zx Monitor' */
+      g_string_append_printf (string, _("%s Monitor"), tmp);
+      break;
+    case CD_DEVICE_KIND_SCANNER:
+      /* TRANSLATORS: a flatbed scanner device, e.g. 'Epson Scanner' */
+      g_string_append_printf (string, _("%s Scanner"), tmp);
+      break;
+    case CD_DEVICE_KIND_CAMERA:
+      /* TRANSLATORS: a camera device, e.g. 'Nikon D60 Camera' */
+      g_string_append_printf (string, _("%s Camera"), tmp);
+      break;
+    case CD_DEVICE_KIND_PRINTER:
+      /* TRANSLATORS: a printer device, e.g. 'Epson Photosmart Printer' */
+      g_string_append_printf (string, _("%s Printer"), tmp);
+      break;
+    case CD_DEVICE_KIND_WEBCAM:
+      /* TRANSLATORS: a webcam device, e.g. 'Philips HiDef Camera' */
+      g_string_append_printf (string, _("%s Webcam"), tmp);
+      break;
+    default:
+      g_string_append (string, tmp);
+      break;
+  }
+out:
+  return g_string_free (string, FALSE);
+}
+
+static const gchar *
+cc_color_device_kind_to_sort (CdDevice *device)
+{
+  CdDeviceKind kind = cd_device_get_kind (device);
+  if (kind == CD_DEVICE_KIND_DISPLAY)
+    return "4";
+  if (kind == CD_DEVICE_KIND_SCANNER)
+    return "3";
+  if (kind == CD_DEVICE_KIND_CAMERA)
+    return "2";
+  if (kind == CD_DEVICE_KIND_WEBCAM)
+    return "1";
+  if (kind == CD_DEVICE_KIND_PRINTER)
+    return "0";
+  return "9";
+}
+
+gchar *
+cc_color_device_get_sortable_base (CdDevice *device)
+{
+  gchar *sortable;
+  gchar *title;
+  title = cc_color_device_get_title (device);
+  sortable = g_strdup_printf ("%s-%s-%s",
+                              cc_color_device_kind_to_sort (device),
+                              cd_device_get_id (device),
+                              title);
+  g_free (title);
+  return sortable;
+}
diff --git a/panels/color/cc-color-common.h b/panels/color/cc-color-common.h
new file mode 100644
index 0000000..fc611ac
--- /dev/null
+++ b/panels/color/cc-color-common.h
@@ -0,0 +1,35 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CC_COLOR_COMMON_H
+#define CC_COLOR_COMMON_H
+
+#include <colord.h>
+
+#define GCM_SETTINGS_SCHEMA             "org.gnome.settings-daemon.plugins.color"
+
+gchar   *cc_color_device_get_sortable_base (CdDevice *device);
+gchar   *cc_color_device_get_title         (CdDevice *device);
+
+G_END_DECLS
+
+#endif /* CC_COLOR_COMMON_H */
+
diff --git a/panels/color/cc-color-device.c b/panels/color/cc-color-device.c
new file mode 100644
index 0000000..f34a43c
--- /dev/null
+++ b/panels/color/cc-color-device.c
@@ -0,0 +1,321 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "cc-color-common.h"
+#include "cc-color-device.h"
+
+struct _CcColorDevicePrivate
+{
+  CdDevice    *device;
+  gboolean     expanded;
+  gchar       *sortable;
+  GtkWidget   *widget_description;
+  GtkWidget   *widget_button;
+  GtkWidget   *widget_switch;
+  GtkWidget   *widget_arrow;
+  GtkWidget   *widget_nocalib;
+  guint        device_changed_id;
+};
+
+G_DEFINE_TYPE (CcColorDevice, cc_color_device, GTK_TYPE_BOX)
+
+enum
+{
+  SIGNAL_EXPANDED_CHANGED,
+  SIGNAL_LAST
+};
+
+enum
+{
+  PROP_0,
+  PROP_DEVICE,
+  PROP_LAST
+};
+
+static guint signals [SIGNAL_LAST] = { 0 };
+
+static void
+cc_color_device_refresh (CcColorDevice *color_device)
+{
+  CcColorDevicePrivate *priv = color_device->priv;
+  gchar *title = NULL;
+  GPtrArray *profiles = NULL;
+
+  /* add switch and expander if there are profiles, otherwise use a label */
+  profiles = cd_device_get_profiles (priv->device);
+  if (profiles == NULL)
+    goto out;
+
+  title = cc_color_device_get_title (priv->device);
+  gtk_label_set_label (GTK_LABEL (priv->widget_description), title);
+  gtk_widget_set_visible (priv->widget_description, TRUE);
+
+  gtk_widget_set_visible (priv->widget_switch, profiles->len > 0);
+  gtk_widget_set_visible (priv->widget_button, profiles->len > 0);
+  gtk_arrow_set (GTK_ARROW (priv->widget_arrow),
+                 priv->expanded ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
+                 GTK_SHADOW_OUT);
+  gtk_widget_set_visible (priv->widget_nocalib, profiles->len == 0);
+  gtk_widget_set_sensitive (priv->widget_button, cd_device_get_enabled (priv->device));
+  gtk_switch_set_active (GTK_SWITCH (priv->widget_switch),
+                         cd_device_get_enabled (priv->device));
+out:
+  if (profiles != NULL)
+    g_ptr_array_unref (profiles);
+  g_free (title);
+}
+
+CdDevice *
+cc_color_device_get_device (CcColorDevice *color_device)
+{
+  g_return_val_if_fail (CC_IS_COLOR_DEVICE (color_device), NULL);
+  return color_device->priv->device;
+}
+
+const gchar *
+cc_color_device_get_sortable (CcColorDevice *color_device)
+{
+  g_return_val_if_fail (CC_IS_COLOR_DEVICE (color_device), NULL);
+  return color_device->priv->sortable;
+}
+
+static void
+cc_color_device_get_property (GObject *object, guint param_id,
+                              GValue *value, GParamSpec *pspec)
+{
+  CcColorDevice *color_device = CC_COLOR_DEVICE (object);
+  switch (param_id)
+    {
+      case PROP_DEVICE:
+        g_value_set_object (value, color_device->priv->device);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        break;
+    }
+}
+
+static void
+cc_color_device_set_property (GObject *object, guint param_id,
+                              const GValue *value, GParamSpec *pspec)
+{
+  CcColorDevice *color_device = CC_COLOR_DEVICE (object);
+
+  switch (param_id)
+    {
+      case PROP_DEVICE:
+        color_device->priv->device = g_value_dup_object (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        break;
+    }
+}
+
+static void
+cc_color_device_finalize (GObject *object)
+{
+  CcColorDevice *color_device = CC_COLOR_DEVICE (object);
+  CcColorDevicePrivate *priv = color_device->priv;
+
+  if (priv->device_changed_id > 0)
+    g_signal_handler_disconnect (priv->device, priv->device_changed_id);
+
+  g_free (priv->sortable);
+  g_object_unref (priv->device);
+
+  G_OBJECT_CLASS (cc_color_device_parent_class)->finalize (object);
+}
+
+void
+cc_color_device_set_expanded (CcColorDevice *color_device,
+                              gboolean expanded)
+{
+  CcColorDevicePrivate *priv = color_device->priv;
+  priv->expanded = FALSE;
+  cc_color_device_refresh (color_device);
+}
+
+static void
+cc_color_device_notify_enable_device_cb (GtkSwitch *sw,
+                                         GParamSpec *pspec,
+                                         gpointer user_data)
+{
+  CcColorDevice *color_device = CC_COLOR_DEVICE (user_data);
+  CcColorDevicePrivate *priv = color_device->priv;
+  gboolean enable;
+  gboolean ret;
+  GError *error = NULL;
+
+  enable = gtk_switch_get_active (sw);
+  g_debug ("Set %s to %i", cd_device_get_id (priv->device), enable);
+  ret = cd_device_set_enabled_sync (priv->device,
+                                    enable, NULL, &error);
+  if (!ret)
+    {
+      g_warning ("failed to %s to the device: %s",
+                 enable ? "enable" : "disable", error->message);
+      g_error_free (error);
+    }
+
+  /* if expanded, close */
+  if (priv->expanded)
+    {
+      cc_color_device_set_expanded (color_device, FALSE);
+      g_signal_emit (color_device, signals[SIGNAL_EXPANDED_CHANGED], 0,
+                     priv->expanded);
+    }
+}
+
+static void
+cc_color_device_changed_cb (CdDevice *device,
+                                   CcColorDevice *color_device)
+{
+  cc_color_device_refresh (color_device);
+}
+
+static void
+cc_color_device_constructed (GObject *object)
+{
+  CcColorDevice *color_device = CC_COLOR_DEVICE (object);
+  CcColorDevicePrivate *priv = color_device->priv;
+  gchar *sortable_tmp;
+
+  /* watch the device for changes */
+  priv->device_changed_id =
+    g_signal_connect (priv->device, "changed",
+                      G_CALLBACK (cc_color_device_changed_cb), color_device);
+
+  /* calculate sortable -- FIXME: we have to hack this as EggListBox
+   * does not let us specify a GtkSortType:
+   * https://bugzilla.gnome.org/show_bug.cgi?id=691341 */
+  sortable_tmp = cc_color_device_get_sortable_base (priv->device);
+  priv->sortable = g_strdup_printf ("%sXX", sortable_tmp);
+  g_free (sortable_tmp);
+
+  cc_color_device_refresh (color_device);
+
+  /* watch to see if the user flicked the switch */
+  g_signal_connect (priv->widget_switch, "notify::active",
+                    G_CALLBACK (cc_color_device_notify_enable_device_cb),
+                    color_device);
+}
+
+static void
+cc_color_device_class_init (CcColorDeviceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = cc_color_device_get_property;
+  object_class->set_property = cc_color_device_set_property;
+  object_class->constructed = cc_color_device_constructed;
+  object_class->finalize = cc_color_device_finalize;
+
+  g_object_class_install_property (object_class, PROP_DEVICE,
+                                   g_param_spec_object ("device", NULL,
+                                                        NULL,
+                                                        CD_TYPE_DEVICE,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  signals [SIGNAL_EXPANDED_CHANGED] =
+    g_signal_new ("expanded-changed",
+            G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+            G_STRUCT_OFFSET (CcColorDeviceClass, expanded_changed),
+            NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
+            G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+  g_type_class_add_private (klass, sizeof (CcColorDevicePrivate));
+}
+
+static void
+cc_color_device_clicked_expander_cb (GtkButton *button,
+                                     gpointer user_data)
+{
+  CcColorDevice *color_device = CC_COLOR_DEVICE (user_data);
+  color_device->priv->expanded = !color_device->priv->expanded;
+  cc_color_device_refresh (color_device);
+  g_signal_emit (color_device, signals[SIGNAL_EXPANDED_CHANGED], 0,
+                 color_device->priv->expanded);
+}
+
+static void
+cc_color_device_init (CcColorDevice *color_device)
+{
+  CcColorDevicePrivate *priv;
+  GtkStyleContext *context;
+  GtkWidget *box;
+
+  color_device->priv = G_TYPE_INSTANCE_GET_PRIVATE (color_device,
+                                                    CC_TYPE_COLOR_DEVICE,
+                                                    CcColorDevicePrivate);
+  priv = color_device->priv;
+
+  /* description */
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 9);
+  priv->widget_description = gtk_label_new ("");
+  gtk_widget_set_margin_left (priv->widget_description, 12);
+  gtk_widget_set_margin_top (priv->widget_description, 6);
+  gtk_widget_set_margin_bottom (priv->widget_description, 6);
+  gtk_misc_set_alignment (GTK_MISC (priv->widget_description), 0.0f, 0.5f);
+  gtk_box_pack_start (GTK_BOX (box), priv->widget_description, TRUE, TRUE, 0);
+
+  /* switch */
+  priv->widget_switch = gtk_switch_new ();
+  gtk_box_pack_start (GTK_BOX (box), priv->widget_switch, FALSE, FALSE, 0);
+
+  /* arrow button */
+  priv->widget_arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
+  priv->widget_button = gtk_button_new ();
+  g_signal_connect (priv->widget_button, "clicked",
+                    G_CALLBACK (cc_color_device_clicked_expander_cb),
+                    color_device);
+  gtk_button_set_relief (GTK_BUTTON (priv->widget_button), GTK_RELIEF_NONE);
+  gtk_container_add (GTK_CONTAINER (priv->widget_button), priv->widget_arrow);
+  gtk_widget_set_visible (priv->widget_arrow, TRUE);
+  gtk_widget_set_margin_top (priv->widget_button, 3);
+  gtk_widget_set_margin_bottom (priv->widget_button, 3);
+  gtk_widget_set_margin_right (priv->widget_button, 9);
+  gtk_box_pack_start (GTK_BOX (box), priv->widget_button, FALSE, FALSE, 0);
+
+  /* not calibrated */
+  priv->widget_nocalib = gtk_label_new (_("Not calibrated"));
+  context = gtk_widget_get_style_context (priv->widget_nocalib);
+  gtk_style_context_add_class (context, "dim-label");
+  gtk_widget_set_margin_right (priv->widget_nocalib, 18);
+  gtk_box_pack_start (GTK_BOX (box), priv->widget_nocalib, FALSE, FALSE, 0);
+
+  /* refresh */
+  gtk_box_pack_start (GTK_BOX (color_device), box, TRUE, TRUE, 0);
+  gtk_widget_set_visible (box, TRUE);
+}
+
+GtkWidget *
+cc_color_device_new (CdDevice *device)
+{
+  return g_object_new (CC_TYPE_COLOR_DEVICE,
+                       "device", device,
+                       NULL);
+}
+
diff --git a/panels/color/cc-color-device.h b/panels/color/cc-color-device.h
new file mode 100644
index 0000000..3c7c314
--- /dev/null
+++ b/panels/color/cc-color-device.h
@@ -0,0 +1,66 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CC_COLOR_DEVICE_H
+#define CC_COLOR_DEVICE_H
+
+#include <gtk/gtk.h>
+#include <colord.h>
+
+#define CC_TYPE_COLOR_DEVICE            (cc_color_device_get_type())
+#define CC_COLOR_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), CC_TYPE_COLOR_DEVICE, CcColorDevice))
+#define CC_COLOR_DEVICE_CLASS(cls)      (G_TYPE_CHECK_CLASS_CAST((cls), CC_TYPE_COLOR_DEVICE, CcColorDeviceClass))
+#define CC_IS_COLOR_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), CC_TYPE_COLOR_DEVICE))
+#define CC_IS_COLOR_DEVICE_CLASS(cls)   (G_TYPE_CHECK_CLASS_TYPE((cls), CC_TYPE_COLOR_DEVICE))
+#define CC_COLOR_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), CC_TYPE_COLOR_DEVICE, CcColorDeviceClass))
+
+G_BEGIN_DECLS
+
+typedef struct _CcColorDevice             CcColorDevice;
+typedef struct _CcColorDeviceClass        CcColorDeviceClass;
+typedef struct _CcColorDevicePrivate      CcColorDevicePrivate;
+
+struct _CcColorDevice
+{
+        GtkBox                         parent;
+
+        /*< private >*/
+        CcColorDevicePrivate    *priv;
+};
+
+struct _CcColorDeviceClass
+{
+        GtkBoxClass     parent_class;
+        void            (*expanded_changed) (CcColorDevice  *color_device,
+                                             gboolean        expanded);
+};
+
+GType        cc_color_device_get_type      (void);
+GtkWidget   *cc_color_device_new           (CdDevice       *device);
+CdDevice    *cc_color_device_get_device    (CcColorDevice  *color_device);
+const gchar *cc_color_device_get_sortable  (CcColorDevice  *color_device);
+void         cc_color_device_set_expanded  (CcColorDevice  *color_device,
+                                            gboolean        expanded);
+
+G_END_DECLS
+
+#endif /* CC_COLOR_DEVICE_H */
+
diff --git a/panels/color/cc-color-panel.c b/panels/color/cc-color-panel.c
index 010103c..a61f679 100644
--- a/panels/color/cc-color-panel.c
+++ b/panels/color/cc-color-panel.c
@@ -21,6 +21,7 @@
 
 #include <config.h>
 
+#include <egg-list-box/egg-list-box.h>
 #include <glib/gi18n.h>
 #include <colord.h>
 #include <gtk/gtk.h>
@@ -30,6 +31,9 @@
 #include "cc-color-cell-renderer-text.h"
 #include "cc-color-panel.h"
 #include "cc-color-resources.h"
+#include "cc-color-common.h"
+#include "cc-color-device.h"
+#include "cc-color-profile.h"
 
 #define WID(b, w) (GtkWidget *) gtk_builder_get_object (b, w)
 
@@ -47,24 +51,12 @@ struct _CcColorPanelPrivate
   GDBusProxy    *proxy;
   GSettings     *settings;
   GtkBuilder    *builder;
-  GtkTreeStore  *list_store_devices;
   GtkWidget     *main_window;
   CcColorCalibrate *calibrate;
-};
-
-enum {
-  GCM_PREFS_COLUMN_DEVICE_PATH,
-  GCM_PREFS_COLUMN_SORT,
-  GCM_PREFS_COLUMN_ICON,
-  GCM_PREFS_COLUMN_TITLE,
-  GCM_PREFS_COLUMN_DEVICE,
-  GCM_PREFS_COLUMN_PROFILE,
-  GCM_PREFS_COLUMN_STATUS,
-  GCM_PREFS_COLUMN_STATUS_IMAGE,
-  GCM_PREFS_COLUMN_TOOLTIP,
-  GCM_PREFS_COLUMN_RADIO_ACTIVE,
-  GCM_PREFS_COLUMN_RADIO_VISIBLE,
-  GCM_PREFS_COLUMN_NUM_COLUMNS
+  EggListBox    *list_box;
+  gchar         *list_box_filter;
+  guint          list_box_selected_id;
+  GtkSizeGroup  *list_box_size;
 };
 
 enum {
@@ -205,36 +197,6 @@ out:
     g_object_unref (profile);
 }
 
-static void
-gcm_prefs_treeview_popup_menu (CcColorPanel *prefs, GtkWidget *treeview)
-{
-  GtkWidget *menu, *menuitem;
-
-  menu = gtk_menu_new ();
-
-  /* TRANSLATORS: this is when the profile should be set for all users */
-  menuitem = gtk_menu_item_new_with_label (_("Set for all users"));
-  g_signal_connect (menuitem, "activate",
-                    G_CALLBACK (gcm_prefs_default_cb),
-                    prefs);
-  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
-
-  gtk_widget_show_all (menu);
-
-  /* Note: gdk_event_get_time() accepts a NULL argument */
-  gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0,
-                  gdk_event_get_time (NULL));
-}
-
-static gboolean
-gcm_prefs_treeview_popup_menu_cb (GtkWidget *treeview, CcColorPanel *prefs)
-{
-  if (prefs->priv->current_device == NULL)
-    return FALSE;
-  gcm_prefs_treeview_popup_menu (prefs, treeview);
-  return TRUE; /* we handled this */
-}
-
 static GFile *
 gcm_prefs_file_chooser_get_icc_profile (CcColorPanel *prefs)
 {
@@ -820,25 +782,21 @@ gcm_prefs_profile_add_cb (GtkWidget *widget, CcColorPanel *prefs)
 static void
 gcm_prefs_profile_remove_cb (GtkWidget *widget, CcColorPanel *prefs)
 {
-  GtkTreeIter iter;
-  GtkTreeSelection *selection;
-  GtkTreeModel *model;
+  CcColorPanelPrivate *priv = prefs->priv;
+  CdProfile *profile;
   gboolean ret = FALSE;
-  CdProfile *profile = NULL;
   GError *error = NULL;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  /* get the selected row */
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "treeview_devices"));
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
-  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
-    goto out;
 
-  /* if the profile is default, then we'll have to make the first profile default */
-  gtk_tree_model_get (model, &iter,
-                      GCM_PREFS_COLUMN_PROFILE, &profile,
-                      -1);
+  /* get the selected profile */
+  widget = egg_list_box_get_selected_child (priv->list_box);
+  if (widget == NULL)
+    return;
+  profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (widget));
+  if (profile == NULL)
+    {
+        g_warning ("failed to get the active profile");
+        return;
+    }
 
   /* just remove it, the list store will get ::changed */
   ret = cd_device_remove_profile_sync (priv->current_device,
@@ -849,12 +807,7 @@ gcm_prefs_profile_remove_cb (GtkWidget *widget, CcColorPanel *prefs)
     {
       g_warning ("failed to remove profile: %s", error->message);
       g_error_free (error);
-      goto out;
     }
-out:
-  if (profile != NULL)
-    g_object_unref (profile);
-  return;
 }
 
 static void
@@ -879,36 +832,31 @@ gcm_prefs_make_profile_default_cb (GObject *object,
 }
 
 static void
-gcm_prefs_profile_make_default_internal (CcColorPanel *prefs,
-                                         GtkTreeModel *model,
-                                         GtkTreeIter *iter_selected)
+gcm_prefs_device_profile_enable_cb (GtkWidget *widget, CcColorPanel *prefs)
 {
-  CdDevice *device;
-  CdProfile *profile;
   CcColorPanelPrivate *priv = prefs->priv;
+  CdProfile *profile;
 
-  /* get currentlt selected item */
-  gtk_tree_model_get (model, iter_selected,
-                      GCM_PREFS_COLUMN_DEVICE, &device,
-                      GCM_PREFS_COLUMN_PROFILE, &profile,
-                      -1);
-  if (profile == NULL || device == NULL)
-    goto out;
+  /* get the selected profile */
+  widget = egg_list_box_get_selected_child (priv->list_box);
+  if (widget == NULL)
+    return;
+  profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (widget));
+  if (profile == NULL)
+    {
+        g_warning ("failed to get the active profile");
+        return;
+    }
 
   /* just set it default */
   g_debug ("setting %s default on %s",
            cd_profile_get_id (profile),
-           cd_device_get_id (device));
-  cd_device_make_profile_default (device,
+           cd_device_get_id (priv->current_device));
+  cd_device_make_profile_default (priv->current_device,
                                   profile,
                                   priv->cancellable,
                                   gcm_prefs_make_profile_default_cb,
                                   prefs);
-out:
-  if (profile != NULL)
-    g_object_unref (profile);
-  if (device != NULL)
-    g_object_unref (device);
 }
 
 static void
@@ -947,60 +895,43 @@ gcm_prefs_profile_assign_link_activate_cb (GtkLabel *label,
                                            const gchar *uri,
                                            CcColorPanel *prefs)
 {
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-  CdProfile *profile = NULL;
-  GtkTreeSelection *selection;
   CcColorPanelPrivate *priv = prefs->priv;
+  CdProfile *profile;
   GtkWidget *widget;
 
   /* get the selected profile */
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "treeview_assign"));
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
-  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
-    goto out;
-  gtk_tree_model_get (model, &iter,
-                      GCM_PREFS_COMBO_COLUMN_PROFILE, &profile,
-                      -1);
+  widget = egg_list_box_get_selected_child (priv->list_box);
+  if (widget == NULL)
+    return;
+  profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (widget));
   if (profile == NULL)
     {
         g_warning ("failed to get the active profile");
-        goto out;
+        return;
     }
 
   /* show it in the viewer */
   gcm_prefs_profile_view (prefs, profile);
-out:
-  if (profile != NULL)
-    g_object_unref (profile);
 }
 
 static void
 gcm_prefs_profile_view_cb (GtkWidget *widget, CcColorPanel *prefs)
 {
-  CdProfile *profile = NULL;
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-  GtkTreeSelection *selection;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  /* get the selected row */
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "treeview_devices"));
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
-  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
-    g_assert_not_reached ();
+  CdProfile *profile;
 
-  /* get currentlt selected item */
-  gtk_tree_model_get (model, &iter,
-                      GCM_PREFS_COLUMN_PROFILE, &profile,
-                      -1);
+  /* get the selected profile */
+  widget = egg_list_box_get_selected_child (prefs->priv->list_box);
+  if (widget == NULL)
+    return;
+  profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (widget));
+  if (profile == NULL)
+    {
+        g_warning ("failed to get the active profile");
+        return;
+    }
 
   /* open up gcm-viewer as a info pane */
   gcm_prefs_profile_view (prefs, profile);
-
-  g_object_unref (profile);
 }
 
 static void
@@ -1043,6 +974,22 @@ gcm_prefs_button_assign_ok_cb (GtkWidget *widget, CcColorPanel *prefs)
         goto out;
     }
 
+  /* if the device is disabled, enable the device so that we can
+   * add color profiles to it */
+  if (!cd_device_get_enabled (priv->current_device))
+    {
+      ret = cd_device_set_enabled_sync (priv->current_device,
+                                        TRUE,
+                                        priv->cancellable,
+                                        &error);
+      if (!ret)
+        {
+          g_warning ("failed to enabled device: %s", error->message);
+          g_error_free (error);
+          goto out;
+        }
+    }
+
   /* just add it, the list store will get ::changed */
   ret = cd_device_add_profile_sync (priv->current_device,
                                     CD_DEVICE_RELATION_HARD,
@@ -1077,93 +1024,6 @@ gcm_prefs_profile_delete_event_cb (GtkWidget *widget,
 }
 
 static void
-gcm_prefs_treeview_renderer_toggled (GtkCellRendererToggle *cell,
-                                     const gchar *path, CcColorPanel *prefs)
-{
-  gboolean ret;
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  model = GTK_TREE_MODEL (priv->list_store_devices);
-  ret = gtk_tree_model_get_iter_from_string (model, &iter, path);
-  if (!ret)
-    return;
-  gcm_prefs_profile_make_default_internal (prefs, model, &iter);
-}
-
-static void
-gcm_prefs_add_devices_columns (CcColorPanel *prefs,
-                               GtkTreeView *treeview)
-{
-  GtkCellRenderer *renderer;
-  GtkTreeViewColumn *column;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  gtk_tree_view_set_headers_visible (treeview, TRUE);
-
-  /* --- column for device image and device title --- */
-  column = gtk_tree_view_column_new ();
-  gtk_tree_view_column_set_expand (column, TRUE);
-  /* TRANSLATORS: column for device list */
-  gtk_tree_view_column_set_title (column, _("Device"));
-
-  /* image */
-  renderer = gtk_cell_renderer_pixbuf_new ();
-  g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
-  gtk_tree_view_column_pack_start (column, renderer, FALSE);
-  gtk_tree_view_column_add_attribute (column, renderer,
-                                      "icon-name", GCM_PREFS_COLUMN_ICON);
-
-  /* option */
-  renderer = gtk_cell_renderer_toggle_new ();
-  g_signal_connect (renderer, "toggled",
-                    G_CALLBACK (gcm_prefs_treeview_renderer_toggled), prefs);
-  g_object_set (renderer, "radio", TRUE, NULL);
-  gtk_tree_view_column_pack_start (column, renderer, FALSE);
-  gtk_tree_view_column_add_attribute (column, renderer,
-                                      "active", GCM_PREFS_COLUMN_RADIO_ACTIVE);
-  gtk_tree_view_column_add_attribute (column, renderer,
-                                      "visible", GCM_PREFS_COLUMN_RADIO_VISIBLE);
-
-  /* text */
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_tree_view_column_pack_start (column, renderer, TRUE);
-  gtk_tree_view_column_add_attribute (column, renderer,
-                                      "markup", GCM_PREFS_COLUMN_TITLE);
-  gtk_tree_view_column_set_expand (column, TRUE);
-  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->list_store_devices),
-                                        GCM_PREFS_COLUMN_SORT,
-                                        GTK_SORT_DESCENDING);
-  gtk_tree_view_append_column (treeview, GTK_TREE_VIEW_COLUMN(column));
-
-  /* --- column for device status --- */
-  column = gtk_tree_view_column_new ();
-  gtk_tree_view_column_set_expand (column, TRUE);
-  /* TRANSLATORS: column for device list */
-  gtk_tree_view_column_set_title (column, _("Calibration"));
-
-  /* image */
-  renderer = gtk_cell_renderer_pixbuf_new ();
-  g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL);
-  gtk_tree_view_column_pack_start (column, renderer, FALSE);
-  gtk_tree_view_column_add_attribute (column, renderer,
-                                      "icon-name", GCM_PREFS_COLUMN_STATUS_IMAGE);
-
-  /* text */
-  renderer = gtk_cell_renderer_text_new ();
-  gtk_tree_view_column_pack_start (column, renderer, TRUE);
-  gtk_tree_view_column_add_attribute (column, renderer,
-                                      "markup", GCM_PREFS_COLUMN_STATUS);
-  gtk_tree_view_column_set_expand (column, FALSE);
-  gtk_tree_view_append_column (treeview, GTK_TREE_VIEW_COLUMN(column));
-
-  /* tooltip */
-  gtk_tree_view_set_tooltip_column (treeview,
-                                    GCM_PREFS_COLUMN_TOOLTIP);
-}
-
-static void
 gcm_prefs_add_profiles_columns (CcColorPanel *prefs,
                                 GtkTreeView *treeview)
 {
@@ -1273,16 +1133,6 @@ out:
 static void
 gcm_prefs_device_clicked (CcColorPanel *prefs, CdDevice *device)
 {
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  if (device == NULL)
-    g_assert_not_reached ();
-
-  /* get current device */
-  if (priv->current_device != NULL)
-    g_object_unref (priv->current_device);
-  priv->current_device = g_object_ref (device);
-
   /* we have a new device */
   g_debug ("selected device is: %s",
            cd_device_get_id (device));
@@ -1374,82 +1224,6 @@ gcm_prefs_profiles_treeview_clicked_cb (GtkTreeSelection *selection,
 }
 
 static void
-gcm_prefs_devices_treeview_clicked_cb (GtkTreeSelection *selection,
-                                       CcColorPanel *prefs)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  CdDevice *device = NULL;
-  CdProfile *profile = NULL;
-  GtkWidget *widget;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  /* get selection */
-  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
-    return;
-
-  gtk_tree_model_get (model, &iter,
-                      GCM_PREFS_COLUMN_DEVICE, &device,
-                      GCM_PREFS_COLUMN_PROFILE, &profile,
-                      -1);
-
-  /* device actions */
-  if (device != NULL)
-    gcm_prefs_device_clicked (prefs, device);
-  if (profile != NULL)
-    gcm_prefs_profile_clicked (prefs, profile, device);
-
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "toolbutton_device_default"));
-  gtk_widget_set_visible (widget, profile != NULL);
-  if (profile)
-    gtk_widget_set_sensitive (widget, !cd_profile_get_is_system_wide (profile));
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "toolbutton_device_calibrate"));
-  gtk_widget_set_visible (widget, profile == NULL);
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "toolbutton_profile_add"));
-  gtk_widget_set_visible (widget, profile == NULL);
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "toolbutton_profile_view"));
-  gtk_widget_set_visible (widget, profile != NULL);
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "toolbutton_profile_remove"));
-  gtk_widget_set_visible (widget, profile != NULL);
-
-  /* if no buttons then hide toolbar */
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "toolbar_devices"));
-  gtk_widget_set_visible (widget, profile != NULL || device != NULL);
-
-  if (device != NULL)
-    g_object_unref (device);
-  if (profile != NULL)
-    g_object_unref (profile);
-}
-
-static void
-gcm_prefs_treeview_row_activated_cb (GtkTreeView *tree_view,
-                                     GtkTreePath *path,
-                                     GtkTreeViewColumn *column,
-                                     CcColorPanel *prefs)
-{
-  GtkTreeModel *model;
-  GtkTreeIter iter;
-  gboolean ret;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  /* get the iter */
-  model = GTK_TREE_MODEL (priv->list_store_devices);
-  ret = gtk_tree_model_get_iter (model, &iter, path);
-  if (!ret)
-    return;
-
-  /* make this profile the default */
-  gcm_prefs_profile_make_default_internal (prefs, model, &iter);
-}
-
-static void
 gcm_prefs_profiles_row_activated_cb (GtkTreeView *tree_view,
                                      GtkTreePath *path,
                                      GtkTreeViewColumn *column,
@@ -1464,58 +1238,6 @@ gcm_prefs_profiles_row_activated_cb (GtkTreeView *tree_view,
   gcm_prefs_button_assign_ok_cb (NULL, prefs);
 }
 
-static const gchar *
-gcm_prefs_device_kind_to_sort (CdDeviceKind kind)
-{
-  if (kind == CD_DEVICE_KIND_DISPLAY)
-    return "4";
-  if (kind == CD_DEVICE_KIND_SCANNER)
-    return "3";
-  if (kind == CD_DEVICE_KIND_CAMERA)
-    return "2";
-  if (kind == CD_DEVICE_KIND_PRINTER)
-    return "1";
-  return "0";
-}
-
-static gchar *
-gcm_device_get_title (CdDevice *device)
-{
-  const gchar *model;
-  const gchar *vendor;
-  GString *string;
-
-  /* try to get a nice string suitable for display */
-  vendor = cd_device_get_vendor (device);
-  model = cd_device_get_model (device);
-  string = g_string_new ("");
-
-  if (vendor != NULL && model != NULL)
-    {
-      g_string_append_printf (string, "%s - %s",
-                              vendor, model);
-      goto out;
-    }
-
-  /* just model */
-  if (model != NULL)
-    {
-      g_string_append (string, model);
-      goto out;
-    }
-
-  /* just vendor */
-  if (vendor != NULL)
-    {
-      g_string_append (string, vendor);
-      goto out;
-    }
-
-  /* fallback to id */
-  g_string_append (string, cd_device_get_id (device));
-out:
-  return g_string_free (string, FALSE);
-}
 
 static void
 gcm_prefs_button_assign_import_cb (GtkWidget *widget,
@@ -1611,487 +1333,215 @@ gcm_prefs_client_sensor_changed_cb (CdClient *client,
   gcm_prefs_set_calibrate_button_sensitivity (prefs);
 }
 
-static const gchar *
-gcm_prefs_device_kind_to_icon_name (CdDeviceKind kind)
+static void
+gcm_prefs_add_device_profile (CcColorPanel *prefs,
+                              CdDevice *device,
+                              CdProfile *profile,
+                              gboolean is_default)
 {
-  switch (kind)
+  CcColorPanelPrivate *priv = prefs->priv;
+  gboolean ret;
+  GError *error = NULL;
+  GtkWidget *widget;
+
+  /* get properties */
+  ret = cd_profile_connect_sync (profile,
+                                 priv->cancellable,
+                                 &error);
+  if (!ret)
     {
-    case CD_DEVICE_KIND_DISPLAY:
-      return "video-display";
-    case CD_DEVICE_KIND_SCANNER:
-      return "scanner";
-    case CD_DEVICE_KIND_PRINTER:
-      return "printer";
-    case CD_DEVICE_KIND_CAMERA:
-      return "camera-photo";
-    case CD_DEVICE_KIND_WEBCAM:
-      return "camera-web";
-    default:
-      return "image-missing";
+      g_warning ("failed to get profile: %s", error->message);
+      g_error_free (error);
+      goto out;
     }
-}
 
-static GString *
-gcm_prefs_get_profile_age_as_string (CdProfile *profile)
-{
-  const gchar *id;
-  gint64 age;
-  GString *string = NULL;
-
-  if (profile == NULL)
-    {
-      /* TRANSLATORS: this is when there is no profile for the device */
-      string = g_string_new (_("No profile"));
-      goto out;
-    }
-
-  /* don't show details for EDID, colorspace or test profiles */
-  id = cd_profile_get_metadata_item (profile,
-                                     CD_PROFILE_METADATA_DATA_SOURCE);
-  if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_EDID) == 0)
-    goto out;
-#if CD_CHECK_VERSION(0,1,14)
-  if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_STANDARD) == 0)
-    goto out;
-  if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_TEST) == 0)
-    goto out;
-#endif
-
-  /* days */
-  age = cd_profile_get_age (profile);
-  if (age == 0)
-    {
-      string = g_string_new (NULL);
-      goto out;
-    }
-  age /= 60 * 60 * 24;
-  string = g_string_new ("");
-
-  /* approximate years */
-  if (age > 365)
+  /* ignore profiles from other user accounts */
+  if (!cd_profile_has_access (profile))
     {
-      age /= 365;
-      g_string_append_printf (string, ngettext (
-                              "%i year",
-                              "%i years",
-                              age), (guint) age);
-      goto out;
-    }
-
-  /* approximate months */
-  if (age > 30)
-    {
-      age /= 30;
-      g_string_append_printf (string, ngettext (
-                              "%i month",
-                              "%i months",
-                              age), (guint) age);
-      goto out;
-    }
-
-  /* approximate weeks */
-  if (age > 7)
-    {
-      age /= 7;
-      g_string_append_printf (string, ngettext (
-                              "%i week",
-                              "%i weeks",
-                              age), (guint) age);
+      /* only print the filename if it exists */
+      if (cd_profile_get_filename (profile) != NULL)
+        {
+          g_warning ("%s is not usable by this user",
+                     cd_profile_get_filename (profile));
+        }
+      else
+        {
+          g_warning ("%s is not usable by this user",
+                     cd_profile_get_id (profile));
+        }
       goto out;
     }
 
-  /* fallback */
-  g_string_append_printf (string, _("Less than 1 week"));
+  /* add to listbox */
+  widget = cc_color_profile_new (device, profile, is_default);
+  gtk_widget_show (widget);
+  gtk_container_add (GTK_CONTAINER (priv->list_box), widget);
+  gtk_size_group_add_widget (priv->list_box_size, widget);
 out:
-  return string;
+  return;
 }
 
-static gchar *
-gcm_prefs_get_profile_created_for_sort (CdProfile *profile)
+static void
+gcm_prefs_add_device_profiles (CcColorPanel *prefs, CdDevice *device)
 {
-  gint64 created;
-  gchar *string = NULL;
-  GDateTime *dt = NULL;
+  CdProfile *profile_tmp;
+  GPtrArray *profiles = NULL;
+  guint i;
 
-  /* get profile age */
-  created = cd_profile_get_created (profile);
-  if (created == 0)
+  /* add profiles */
+  profiles = cd_device_get_profiles (device);
+  if (profiles == NULL)
     goto out;
-  dt = g_date_time_new_from_unix_utc (created);
-  /* note: this is not shown in the UI, just used for sorting */
-  string = g_date_time_format (dt, "%Y%m%d");
-out:
-  if (dt != NULL)
-    g_date_time_unref (dt);
-  return string;
-}
-
-static gchar *
-gcm_prefs_get_profile_title (CcColorPanel *prefs, CdProfile *profile)
-{
-  CdColorspace colorspace;
-  const gchar *title;
-  gchar *string;
-  gboolean ret;
-  GError *error = NULL;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  g_return_val_if_fail (profile != NULL, NULL);
-
-  string = NULL;
-
-  /* get properties */
-  ret = cd_profile_connect_sync (profile,
-                                 priv->cancellable,
-                                 &error);
-  if (!ret)
-    {
-      g_warning ("failed to get profile: %s", error->message);
-      g_error_free (error);
-      goto out;
-    }
-
-  /* add profile description */
-  title = cd_profile_get_title (profile);
-  if (title != NULL)
-    {
-      string = g_markup_escape_text (title, -1);
-      goto out;
-    }
-
-  /* some meta profiles do not have ICC profiles */
-  colorspace = cd_profile_get_colorspace (profile);
-  if (colorspace == CD_COLORSPACE_RGB)
-    {
-      string = g_strdup (C_("Colorspace fallback", "Default RGB"));
-      goto out;
-    }
-  if (colorspace == CD_COLORSPACE_CMYK)
-    {
-      string = g_strdup (C_("Colorspace fallback", "Default CMYK"));
-      goto out;
-    }
-  if (colorspace == CD_COLORSPACE_GRAY)
+  for (i = 0; i < profiles->len; i++)
     {
-      string = g_strdup (C_("Colorspace fallback", "Default Gray"));
-      goto out;
+      profile_tmp = g_ptr_array_index (profiles, i);
+      gcm_prefs_add_device_profile (prefs, device, profile_tmp, i == 0);
     }
-
-  /* fall back to ID, ick */
-  string = g_strdup (cd_profile_get_id (profile));
 out:
-  return string;
-}
-
-static void
-gcm_prefs_device_remove_profiles_phase1 (CcColorPanel *prefs, GtkTreeIter *parent)
-{
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-  gboolean ret;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  /* get first element */
-  model = GTK_TREE_MODEL (priv->list_store_devices);
-  ret = gtk_tree_model_iter_children (model, &iter, parent);
-  if (!ret)
-    return;
-
-  /* mark to be removed */
-  do {
-    gtk_tree_store_set (priv->list_store_devices, &iter,
-                        GCM_PREFS_COLUMN_DEVICE_PATH, NULL,
-                        -1);
-  } while (gtk_tree_model_iter_next (model, &iter));
+  if (profiles != NULL)
+    g_ptr_array_unref (profiles);
 }
 
-static void
-gcm_prefs_device_remove_profiles_phase2 (CcColorPanel *prefs, GtkTreeIter *parent)
+/* find the profile in the array -- for flicker-free changes */
+static gboolean
+gcm_prefs_find_profile_by_object_path (GPtrArray *profiles,
+                                       const gchar *object_path)
 {
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-  gchar *id_tmp;
-  gboolean ret;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  /* get first element */
-  model = GTK_TREE_MODEL (priv->list_store_devices);
-  ret = gtk_tree_model_iter_children (model, &iter, parent);
-  if (!ret)
-    return;
+  CdProfile *profile_tmp;
+  guint i;
 
-  /* remove the other elements */
-  do
+  for (i = 0; i < profiles->len; i++)
     {
-      gtk_tree_model_get (model, &iter,
-              GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp,
-              -1);
-      if (id_tmp == NULL)
-        ret = gtk_tree_store_remove (priv->list_store_devices, &iter);
-      else
-        ret = gtk_tree_model_iter_next (model, &iter);
-      g_free (id_tmp);
-    } while (ret);
+      profile_tmp = g_ptr_array_index (profiles, i);
+      if (g_strcmp0 (cd_profile_get_object_path (profile_tmp), object_path) == 0)
+        return TRUE;
+    }
+  return FALSE;
 }
 
-static GtkTreeIter *
-get_iter_for_profile (GtkTreeModel *model, CdProfile *profile, GtkTreeIter *parent)
+/* find the profile in the list view -- for flicker-free changes */
+static gboolean
+gcm_prefs_find_widget_by_object_path (GList *list,
+                                      const gchar *object_path_device,
+                                      const gchar *object_path_profile)
 {
-  const gchar *id;
-  gboolean ret;
-  GtkTreeIter iter;
+  GList *l;
+  CdDevice *device_tmp;
   CdProfile *profile_tmp;
 
-  /* get first element */
-  ret = gtk_tree_model_iter_children (model, &iter, parent);
-  if (!ret)
-    return NULL;
-
-  /* remove the other elements */
-  id = cd_profile_get_id (profile);
-  while (ret)
+  for (l = list; l != NULL; l = l->next)
     {
-      gtk_tree_model_get (model, &iter,
-              GCM_PREFS_COLUMN_PROFILE, &profile_tmp,
-              -1);
-      if (g_strcmp0 (id, cd_profile_get_id (profile_tmp)) == 0)
+      if (!CC_IS_COLOR_PROFILE (l->data))
+        continue;
+
+      /* correct device ? */
+      device_tmp = cc_color_profile_get_device (CC_COLOR_PROFILE (l->data));
+      if (g_strcmp0 (object_path_device,
+                     cd_device_get_object_path (device_tmp)) != 0)
         {
-          g_object_unref (profile_tmp);
-          return gtk_tree_iter_copy (&iter);
+          continue;
         }
-      g_object_unref (profile_tmp);
-      ret = gtk_tree_model_iter_next (model, &iter);
-    }
 
-  return NULL;
+      /* this profile */
+      profile_tmp = cc_color_profile_get_profile (CC_COLOR_PROFILE (l->data));
+      if (g_strcmp0 (object_path_profile,
+                     cd_profile_get_object_path (profile_tmp)) == 0)
+        {
+          return TRUE;
+        }
+    }
+  return FALSE;
 }
 
 static void
-gcm_prefs_device_set_model_by_iter (CcColorPanel *prefs, CdDevice *device, GtkTreeIter *iter)
+gcm_prefs_device_changed_cb (CdDevice *device, CcColorPanel *prefs)
 {
-  GString *status = NULL;
-  const gchar *status_image = NULL;
-  const gchar *tooltip = NULL;
-  CdProfile *profile = NULL;
-  gint age;
-  GPtrArray *profiles = NULL;
+  CcColorPanelPrivate *priv = prefs->priv;
+  CdDevice *device_tmp;
   CdProfile *profile_tmp;
-  guint i;
-  gchar *title_tmp;
-  GString *date_tmp;
-  gchar *sort_tmp;
-  GtkTreeIter iter_tmp;
-  GtkTreeIter *iter_tmp_p;
-  guint threshold = 0;
   gboolean ret;
-  GError *error = NULL;
-  CcColorPanelPrivate *priv = prefs->priv;
-
-  /* set status */
-  profile = cd_device_get_default_profile (device);
-  if (profile == NULL)
-    {
-      status = g_string_new (_("Uncalibrated"));
-      g_string_prepend (status, "<span foreground='gray'><i>");
-      g_string_append (status, "</i></span>");
-      tooltip = _("This device is not color managed.");
-      goto skip;
-    }
-
-  /* get properties */
-  ret = cd_profile_connect_sync (profile,
-                                 priv->cancellable,
-                                 &error);
-  if (!ret)
-    {
-      g_warning ("failed to get profile: %s", error->message);
-      g_error_free (error);
-      goto out;
-    }
-
-#if CD_CHECK_VERSION(0,1,13)
-      /* ignore profiles from other user accounts */
-      if (!cd_profile_has_access (profile))
-        {
-          /* only print the filename if it exists */
-          if (cd_profile_get_filename (profile) != NULL)
-            {
-              g_warning ("%s is not usable by this user",
-                         cd_profile_get_filename (profile));
-            }
-          else
-            {
-              g_warning ("%s is not usable by this user",
-                         cd_profile_get_id (profile));
-            }
-          goto out;
-        }
-#endif
-
-  /* autogenerated printer defaults */
-  if (cd_device_get_kind (device) == CD_DEVICE_KIND_PRINTER &&
-      cd_profile_get_filename (profile) == NULL)
-    {
-      status = g_string_new (_("Uncalibrated"));
-      g_string_prepend (status, "<span foreground='gray'><i>");
-      g_string_append (status, "</i></span>");
-      tooltip = _("This device is using manufacturing calibrated data.");
-      goto skip;
-    }
+  GList *l;
+  GList *list;
+  GPtrArray *profiles;
+  guint i;
 
-  /* autogenerated profiles are crap */
-  if (cd_profile_get_kind (profile) == CD_PROFILE_KIND_DISPLAY_DEVICE &&
-      !cd_profile_get_has_vcgt (profile))
+  /* remove anything in the list view that's not in Device.Profiles */
+  profiles = cd_device_get_profiles (device);
+  list = gtk_container_get_children (GTK_CONTAINER (priv->list_box));
+  for (l = list; l != NULL; l = l->next)
     {
-      status = g_string_new (_("Uncalibrated"));
-      g_string_prepend (status, "<span foreground='gray'><i>");
-      g_string_append (status, "</i></span>");
-      tooltip = _("This device does not have a profile suitable for whole-screen color correction.");
-      goto skip;
-    }
+      if (!CC_IS_COLOR_PROFILE (l->data))
+        continue;
 
-  /* yay! */
-  status = gcm_prefs_get_profile_age_as_string (profile);
-  if (status == NULL)
-    {
-      status = g_string_new (_("Uncalibrated"));
-      g_string_prepend (status, "<span foreground='gray'><i>");
-      g_string_append (status, "</i></span>");
-    }
+      /* correct device ? */
+      device_tmp = cc_color_profile_get_device (CC_COLOR_PROFILE (l->data));
+      if (g_strcmp0 (cd_device_get_id (device),
+                     cd_device_get_id (device_tmp)) != 0)
+        continue;
 
-  /* greater than the calibration threshold for the device type */
-  age = cd_profile_get_age (profile);
-  age /= 60 * 60 * 24;
-  if (cd_device_get_kind (device) == CD_DEVICE_KIND_DISPLAY)
-    {
-      g_settings_get (priv->settings,
-                      GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD,
-                      "u",
-                      &threshold);
-    }
-  else if (cd_device_get_kind (device) == CD_DEVICE_KIND_DISPLAY)
-    {
-      g_settings_get (priv->settings,
-                      GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD,
-                      "u",
-                      &threshold);
-    }
-  if (threshold > 0 && age > threshold)
-    {
-      status_image = "dialog-warning-symbolic";
-      tooltip = _("This device has an old profile that may no longer be accurate.");
+      /* if profile is not in Device.Profiles then remove */
+      profile_tmp = cc_color_profile_get_profile (CC_COLOR_PROFILE (l->data));
+      ret = gcm_prefs_find_profile_by_object_path (profiles,
+                                                   cd_profile_get_object_path (profile_tmp));
+      if (!ret)
+        gtk_widget_destroy (GTK_WIDGET (l->data));
     }
-skip:
-  /* save to store */
-  gtk_tree_store_set (priv->list_store_devices, iter,
-                      GCM_PREFS_COLUMN_STATUS, status->str,
-                      GCM_PREFS_COLUMN_STATUS_IMAGE, status_image,
-                      GCM_PREFS_COLUMN_TOOLTIP, tooltip,
-                      -1);
-
-  /* remove old profiles */
-  gcm_prefs_device_remove_profiles_phase1 (prefs, iter);
 
-  /* add profiles */
-  profiles = cd_device_get_profiles (device);
-  if (profiles == NULL)
-    goto out;
+  /* add anything in Device.Profiles that's not in the list view */
   for (i = 0; i < profiles->len; i++)
     {
       profile_tmp = g_ptr_array_index (profiles, i);
-      title_tmp = gcm_prefs_get_profile_title (prefs, profile_tmp);
-
-      /* get profile age */
-      date_tmp = gcm_prefs_get_profile_age_as_string (profile_tmp);
-      if (date_tmp == NULL)
-        {
-          /* TRANSLATORS: this is when the calibration profile age is not
-           * specified as it has been autogenerated from the hardware */
-          date_tmp = g_string_new (_("Not specified"));
-          g_string_prepend (date_tmp, "<span foreground='gray'><i>");
-          g_string_append (date_tmp, "</i></span>");
-        }
-      sort_tmp = gcm_prefs_get_profile_created_for_sort (profile_tmp);
-
-      /* get an existing profile, or create a new one */
-      iter_tmp_p = get_iter_for_profile (GTK_TREE_MODEL (priv->list_store_devices),
-                                         profile_tmp, iter);
-      if (iter_tmp_p == NULL)
-        gtk_tree_store_append (priv->list_store_devices, &iter_tmp, iter);
-
-      gtk_tree_store_set (priv->list_store_devices, iter_tmp_p ? iter_tmp_p : &iter_tmp,
-                          GCM_PREFS_COLUMN_DEVICE, device,
-                          GCM_PREFS_COLUMN_PROFILE, profile_tmp,
-                          GCM_PREFS_COLUMN_DEVICE_PATH, cd_device_get_object_path (device),
-                          GCM_PREFS_COLUMN_SORT, sort_tmp,
-                          GCM_PREFS_COLUMN_STATUS, date_tmp->str,
-                          GCM_PREFS_COLUMN_TITLE, title_tmp,
-                          GCM_PREFS_COLUMN_RADIO_VISIBLE, TRUE,
-                          GCM_PREFS_COLUMN_RADIO_ACTIVE, i==0,
-                          -1);
-      if (iter_tmp_p != NULL)
-        gtk_tree_iter_free (iter_tmp_p);
-      g_free (title_tmp);
-      g_free (sort_tmp);
-      g_string_free (date_tmp, TRUE);
+      ret = gcm_prefs_find_widget_by_object_path (list,
+                                                  cd_device_get_object_path (device),
+                                                  cd_profile_get_object_path (profile_tmp));
+      if (!ret)
+        gcm_prefs_add_device_profile (prefs, device, profile_tmp, i == 0);
     }
+  g_list_free (list);
 
-  /* remove old profiles that no longer exist */
-  gcm_prefs_device_remove_profiles_phase2 (prefs, iter);
-out:
-  if (status != NULL)
-    g_string_free (status, TRUE);
-  if (profiles != NULL)
-    g_ptr_array_unref (profiles);
-  if (profile != NULL)
-    g_object_unref (profile);
+  /* resort */
+  egg_list_box_resort (priv->list_box);
 }
 
 static void
-gcm_prefs_device_changed_cb (CdDevice *device, CcColorPanel *prefs)
+gcm_prefs_device_expanded_changed_cb (CcColorDevice *widget,
+                                      gboolean is_expanded,
+                                      CcColorPanel *prefs)
 {
-  const gchar *id;
-  gboolean ret;
-  gchar *id_tmp;
-  GtkTreeIter iter;
-  GtkTreeModel *model;
   CcColorPanelPrivate *priv = prefs->priv;
+  GList *l;
+  GList *list;
 
-  /* get first element */
-  model = GTK_TREE_MODEL (priv->list_store_devices);
-  ret = gtk_tree_model_get_iter_first (model, &iter);
-  if (!ret)
-    return;
-
-  /* get the other elements */
-  id = cd_device_get_object_path (device);
-  do
+  g_free (prefs->priv->list_box_filter);
+  if (is_expanded)
     {
-      gtk_tree_model_get (model, &iter,
-                          GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp,
-                          -1);
-      if (g_strcmp0 (id_tmp, id) == 0)
+      priv->list_box_filter = g_strdup (cd_device_get_id (cc_color_device_get_device (widget)));
+
+      /* unexpand other device widgets */
+      list = gtk_container_get_children (GTK_CONTAINER (priv->list_box));
+      for (l = list; l != NULL; l = l->next)
         {
-          /* populate device */
-          gcm_prefs_device_set_model_by_iter (prefs, device, &iter);
+          if (!CC_IS_COLOR_DEVICE (l->data))
+            continue;
+          if (l->data != widget)
+            cc_color_device_set_expanded (CC_COLOR_DEVICE (l->data), FALSE);
         }
-      g_free (id_tmp);
-    } while (gtk_tree_model_iter_next (model, &iter));
+      g_list_free (list);
+    }
+  else
+    {
+      priv->list_box_filter = NULL;
+    }
+  egg_list_box_refilter (priv->list_box);
 }
 
 static void
 gcm_prefs_add_device (CcColorPanel *prefs, CdDevice *device)
 {
+  CcColorPanelPrivate *priv = prefs->priv;
   gboolean ret;
   GError *error = NULL;
-  CdDeviceKind kind;
-  const gchar *icon_name;
-  const gchar *id;
-  gchar *sort = NULL;
-  gchar *title = NULL;
-  GtkTreeIter parent;
-  CcColorPanelPrivate *priv = prefs->priv;
-
+  GtkWidget *widget;
 
   /* get device properties */
   ret = cd_device_connect_sync (device, priv->cancellable, &error);
@@ -2102,119 +1552,74 @@ gcm_prefs_add_device (CcColorPanel *prefs, CdDevice *device)
       goto out;
     }
 
-  /* get icon */
-  kind = cd_device_get_kind (device);
-  icon_name = gcm_prefs_device_kind_to_icon_name (kind);
-
-  /* italic for non-connected devices */
-  title = gcm_device_get_title (device);
+  /* add device */
+  widget = cc_color_device_new (device);
+  g_signal_connect (widget, "expanded-changed",
+                    G_CALLBACK (gcm_prefs_device_expanded_changed_cb), prefs);
+  gtk_widget_show (widget);
+  gtk_container_add (GTK_CONTAINER (priv->list_box), widget);
+  gtk_size_group_add_widget (priv->list_box_size, widget);
 
-  /* create sort order */
-  sort = g_strdup_printf ("%s%s",
-                          gcm_prefs_device_kind_to_sort (kind),
-                          title);
+  /* add profiles */
+  gcm_prefs_add_device_profiles (prefs, device);
 
-  /* watch for changes to update the status icons */
+  /* watch for changes */
   g_signal_connect (device, "changed",
                     G_CALLBACK (gcm_prefs_device_changed_cb), prefs);
-
-  /* add to list */
-  id = cd_device_get_object_path (device);
-  g_debug ("add %s to device list", id);
-  gtk_tree_store_append (priv->list_store_devices, &parent, NULL);
-  gtk_tree_store_set (priv->list_store_devices, &parent,
-                      GCM_PREFS_COLUMN_DEVICE, device,
-                      GCM_PREFS_COLUMN_DEVICE_PATH, id,
-                      GCM_PREFS_COLUMN_SORT, sort,
-                      GCM_PREFS_COLUMN_TITLE, title,
-                      GCM_PREFS_COLUMN_ICON, icon_name,
-                      -1);
-  gcm_prefs_device_set_model_by_iter (prefs, device, &parent);
+  egg_list_box_resort (priv->list_box);
 out:
-  g_free (sort);
-  g_free (title);
+  return;
 }
 
 static void
-gcm_prefs_remove_device (CcColorPanel *prefs, CdDevice *cd_device)
+gcm_prefs_remove_device (CcColorPanel *prefs, CdDevice *device)
 {
-  GtkTreeIter iter;
-  GtkTreeModel *model;
-  const gchar *id;
-  gchar *id_tmp;
-  gboolean ret;
-  CdDevice *device_tmp;
   CcColorPanelPrivate *priv = prefs->priv;
+  CdDevice *device_tmp;
+  GList *l;
+  GList *list;
 
-  /* remove */
-  id = cd_device_get_object_path (cd_device);
-
-  /* get first element */
-  model = GTK_TREE_MODEL (priv->list_store_devices);
-  ret = gtk_tree_model_get_iter_first (model, &iter);
-  if (!ret)
-    return;
-
-  /* get the other elements */
-  do
+  list = gtk_container_get_children (GTK_CONTAINER (priv->list_box));
+  for (l = list; l != NULL; l = l->next)
     {
-      gtk_tree_model_get (model, &iter,
-                          GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp,
-                          -1);
-      if (g_strcmp0 (id_tmp, id) == 0)
+      if (CC_IS_COLOR_DEVICE (l->data))
+        device_tmp = cc_color_device_get_device (CC_COLOR_DEVICE (l->data));
+      else
+        device_tmp = cc_color_profile_get_device (CC_COLOR_PROFILE (l->data));
+      if (g_strcmp0 (cd_device_get_object_path (device),
+                     cd_device_get_object_path (device_tmp)) == 0)
         {
-          gtk_tree_model_get (model, &iter,
-                              GCM_PREFS_COLUMN_DEVICE, &device_tmp,
-                              -1);
-          g_signal_handlers_disconnect_by_func (device_tmp,
-                                                G_CALLBACK (gcm_prefs_device_changed_cb),
-                                                prefs);
-          gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
-          g_free (id_tmp);
-          g_object_unref (device_tmp);
-          break;
+          gtk_widget_destroy (GTK_WIDGET (l->data));
         }
-      g_free (id_tmp);
-    } while (gtk_tree_model_iter_next (model, &iter));
+    }
+  g_list_free (list);
+  g_signal_handlers_disconnect_by_func (device,
+                                        G_CALLBACK (gcm_prefs_device_changed_cb),
+                                        prefs);
 }
 
 static void
 gcm_prefs_update_device_list_extra_entry (CcColorPanel *prefs)
 {
   CcColorPanelPrivate *priv = prefs->priv;
-  gboolean ret;
-  gchar *id_tmp;
-  gchar *title = NULL;
-  GtkTreeIter iter;
+  GList *device_widgets;
+  GtkWidget *widget;
+  guint number_of_devices;
 
-  /* select the first device */
-  ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store_devices), &iter);
-  if (!ret)
-    {
-      /* add the 'No devices detected' entry */
-      title = g_strdup_printf ("<i>%s</i>", _("No devices supporting color management detected"));
-      gtk_tree_store_append (priv->list_store_devices, &iter, NULL);
-      gtk_tree_store_set (priv->list_store_devices, &iter,
-                          GCM_PREFS_COLUMN_RADIO_VISIBLE, FALSE,
-                          GCM_PREFS_COLUMN_TITLE, title,
-                          -1);
-      g_free (title);
-      return;
-    }
+  /* any devices to show? */
+  device_widgets = gtk_container_get_children (GTK_CONTAINER (priv->list_box));
+  number_of_devices = g_list_length (device_widgets);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "label_no_devices"));
+  gtk_widget_set_visible (widget, number_of_devices == 0);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "vbox3"));
+  gtk_widget_set_visible (widget, number_of_devices > 0);
 
-  /* remove the 'No devices detected' entry */
-  do
-    {
-      gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store_devices), &iter,
-                          GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp,
-                          -1);
-      if (id_tmp == NULL)
-        {
-          gtk_tree_store_remove (priv->list_store_devices, &iter);
-          break;
-        }
-      g_free (id_tmp);
-    } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store_devices), &iter));
+  /* if we have only one device expand it by default */
+  if (number_of_devices == 1)
+    cc_color_device_set_expanded (CC_COLOR_DEVICE (device_widgets->data), TRUE);
+  g_list_free (device_widgets);
 }
 
 static void
@@ -2230,53 +1635,15 @@ gcm_prefs_device_added_cb (CdClient *client,
 }
 
 static void
-gcm_prefs_changed_cb (CdClient *client,
-                      CdDevice *device,
-                      CcColorPanel *prefs)
-{
-  g_debug ("changed: %s (doing nothing)", cd_device_get_id (device));
-}
-
-static void
 gcm_prefs_device_removed_cb (CdClient *client,
                              CdDevice *device,
                              CcColorPanel *prefs)
 {
-  GtkTreeIter iter;
-  GtkTreeSelection *selection;
-  GtkWidget *widget;
-  gboolean ret;
-  CcColorPanelPrivate *priv = prefs->priv;
-
   /* remove from the UI */
   gcm_prefs_remove_device (prefs, device);
 
   /* ensure we showing the 'No devices detected' entry if required */
   gcm_prefs_update_device_list_extra_entry (prefs);
-
-  /* select the first device */
-  ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store_devices), &iter);
-  if (!ret)
-    return;
-
-  /* click it */
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "treeview_devices"));
-  gtk_tree_view_set_model (GTK_TREE_VIEW (widget),
-                           GTK_TREE_MODEL (priv->list_store_devices));
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
-  gtk_tree_selection_select_iter (selection, &iter);
-}
-
-static gboolean
-gcm_prefs_tree_model_count_cb (GtkTreeModel *model,
-                               GtkTreePath *path,
-                               GtkTreeIter *iter,
-                               gpointer user_data)
-{
-  guint *i = (guint *) user_data;
-  (*i)++;
-  return FALSE;
 }
 
 static void
@@ -2289,11 +1656,7 @@ gcm_prefs_get_devices_cb (GObject *object,
   CdDevice *device;
   GError *error = NULL;
   GPtrArray *devices;
-  GtkTreePath *path;
-  GtkWidget *widget;
   guint i;
-  guint devices_and_profiles = 0;
-  CcColorPanelPrivate *priv = prefs->priv;
 
   /* get devices and add them */
   devices = cd_client_get_devices_finish (client, res, &error);
@@ -2312,28 +1675,97 @@ gcm_prefs_get_devices_cb (GObject *object,
 
   /* ensure we show the 'No devices detected' entry if empty */
   gcm_prefs_update_device_list_extra_entry (prefs);
-
-  /* set the cursor on the first device */
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "treeview_devices"));
-  path = gtk_tree_path_new_from_string ("0");
-  gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE);
-  gtk_tree_path_free (path);
-
-  /* if we have only a few devices and profiles expand the treeview
-   * devices so they can all be seen */
-  gtk_tree_model_foreach (GTK_TREE_MODEL (priv->list_store_devices),
-                          gcm_prefs_tree_model_count_cb,
-                          &devices_and_profiles);
-  if (devices_and_profiles <= GCM_PREFS_MAX_DEVICES_PROFILES_EXPANDED)
-    gtk_tree_view_expand_all (GTK_TREE_VIEW (widget));
-
 out:
   if (devices != NULL)
     g_ptr_array_unref (devices);
 }
 
 static void
+gcm_prefs_list_box_handle_selection_flags (EggListBox *list_box,
+                                           GtkWidget *child)
+{
+  GList *l;
+  GList *list;
+
+  list = gtk_container_get_children (GTK_CONTAINER (list_box));
+  for (l = list; l != NULL; l = l->next)
+    {
+      if (child != l->data)
+        {
+          gtk_widget_unset_state_flags (GTK_WIDGET (l->data),
+                                        GTK_STATE_FLAG_SELECTED);
+          continue;
+        }
+        gtk_widget_set_state_flags (child, GTK_STATE_FLAG_SELECTED, FALSE);
+    }
+  g_list_free (list);
+}
+
+static void
+gcm_prefs_list_box_child_selected_cb (EggListBox *list_box,
+                                      GtkWidget *child,
+                                      CcColorPanel *panel)
+{
+  CdProfile *profile = NULL;
+  GtkWidget *widget;
+  CcColorPanelPrivate *priv = panel->priv;
+  gboolean is_device = CC_IS_COLOR_DEVICE (child);
+
+  /* nothing selected */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbar_devices"));
+  gtk_widget_set_visible (widget, child != NULL);
+  if (child == NULL)
+    return;
+
+  /* until Alex works out what to do with forwarding the state onto
+   * children in an EggListBox, we have to do this ourselves */
+  gcm_prefs_list_box_handle_selection_flags (list_box, child);
+
+  /* save current device */
+  if (priv->current_device != NULL)
+    g_object_unref (priv->current_device);
+  g_object_get (child, "device", &priv->current_device, NULL);
+
+  /* device actions */
+  g_debug ("%s selected", is_device ? "device" : "profile");
+  if (CC_IS_COLOR_DEVICE (child))
+    {
+      gcm_prefs_device_clicked (panel, priv->current_device);
+    }
+  else if (CC_IS_COLOR_PROFILE (child))
+    {
+      profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (child));
+      gcm_prefs_profile_clicked (panel, profile, priv->current_device);
+    }
+  else
+    g_assert_not_reached ();
+
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_default"));
+  gtk_widget_set_visible (widget, !is_device && cc_color_profile_get_is_default (CC_COLOR_PROFILE (child)));
+
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_enable"));
+  gtk_widget_set_visible (widget, !is_device && !cc_color_profile_get_is_default (CC_COLOR_PROFILE (child)));
+
+  if (profile)
+    gtk_widget_set_sensitive (widget, !cd_profile_get_is_system_wide (profile));
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_calibrate"));
+  gtk_widget_set_visible (widget, is_device);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_profile_add"));
+  gtk_widget_set_visible (widget, is_device);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_profile_view"));
+  gtk_widget_set_visible (widget, !is_device);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_profile_remove"));
+  gtk_widget_set_visible (widget, !is_device);
+}
+
+static void
 gcm_prefs_connect_cb (GObject *object,
                       GAsyncResult *res,
                       gpointer user_data)
@@ -2407,6 +1839,14 @@ cc_color_panel_dispose (GObject *object)
 {
   CcColorPanelPrivate *priv = CC_COLOR_PANEL (object)->priv;
 
+  /* stop the EggListView from firing when it gets disposed */
+  if (priv->list_box_selected_id != 0)
+    {
+      g_signal_handler_disconnect (priv->list_box,
+                                   priv->list_box_selected_id);
+      priv->list_box_selected_id = 0;
+    }
+
   if (priv->cancellable != NULL)
     g_cancellable_cancel (priv->cancellable);
   g_clear_object (&priv->settings);
@@ -2415,7 +1855,9 @@ cc_color_panel_dispose (GObject *object)
   g_clear_object (&priv->client);
   g_clear_object (&priv->current_device);
   g_clear_object (&priv->calibrate);
+  g_clear_object (&priv->list_box_size);
   g_clear_pointer (&priv->sensors, g_ptr_array_unref);
+  g_clear_pointer (&priv->list_box_filter, g_free);
 
   G_OBJECT_CLASS (cc_color_panel_parent_class)->dispose (object);
 }
@@ -2442,6 +1884,62 @@ cc_color_panel_class_init (CcColorPanelClass *klass)
   object_class->finalize = cc_color_panel_finalize;
 }
 
+static gint
+cc_color_panel_sort_func (gconstpointer a,
+                          gconstpointer b,
+                          gpointer user_data)
+{
+  const gchar *sort_a = NULL;
+  const gchar *sort_b = NULL;
+  if (CC_IS_COLOR_DEVICE (a))
+    sort_a = cc_color_device_get_sortable (CC_COLOR_DEVICE (a));
+  else if (CC_IS_COLOR_PROFILE (a))
+    sort_a = cc_color_profile_get_sortable (CC_COLOR_PROFILE (a));
+  else
+    g_assert_not_reached ();
+  if (CC_IS_COLOR_DEVICE (b))
+    sort_b = cc_color_device_get_sortable (CC_COLOR_DEVICE (b));
+  else if (CC_IS_COLOR_PROFILE (b))
+    sort_b = cc_color_profile_get_sortable (CC_COLOR_PROFILE (b));
+  else
+    g_assert_not_reached ();
+  return g_strcmp0 (sort_b, sort_a);
+}
+
+static gboolean
+cc_color_panel_filter_func (GtkWidget *child, void *user_data)
+{
+  CcColorPanel *prefs = CC_COLOR_PANEL (user_data);
+  CdDevice *device;
+  gboolean ret;
+
+  /* always show all devices */
+  if (CC_IS_COLOR_DEVICE (child))
+    return TRUE;
+
+  g_object_get (child, "device", &device, NULL);
+  ret = g_strcmp0 (cd_device_get_id (device), prefs->priv->list_box_filter) == 0;
+  g_object_unref (device);
+  return ret;
+}
+
+static void
+cc_color_panel_separator_func (GtkWidget **separator,
+                               GtkWidget  *child,
+                               GtkWidget  *before,
+                               gpointer    user_data)
+{
+  if (before == NULL)
+    return;
+
+  if (*separator == NULL)
+    {
+      *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
+      g_object_ref_sink (*separator);
+      gtk_widget_show (*separator);
+    }
+}
+
 static void
 cc_color_panel_init (CcColorPanel *prefs)
 {
@@ -2476,20 +1974,6 @@ cc_color_panel_init (CcColorPanel *prefs)
   /* setup defaults */
   priv->settings = g_settings_new (GCM_SETTINGS_SCHEMA);
 
-  /* create list stores */
-  priv->list_store_devices = gtk_tree_store_new (GCM_PREFS_COLUMN_NUM_COLUMNS,
-                                                 G_TYPE_STRING,
-                                                 G_TYPE_STRING,
-                                                 G_TYPE_STRING,
-                                                 G_TYPE_STRING,
-                                                 CD_TYPE_DEVICE,
-                                                 CD_TYPE_PROFILE,
-                                                 G_TYPE_STRING,
-                                                 G_TYPE_STRING,
-                                                 G_TYPE_STRING,
-                                                 G_TYPE_BOOLEAN,
-                                                 G_TYPE_BOOLEAN);
-
   /* assign buttons */
   widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                "toolbutton_profile_add"));
@@ -2510,27 +1994,6 @@ cc_color_panel_init (CcColorPanel *prefs)
   g_signal_connect (widget, "activate-link",
                     G_CALLBACK (gcm_prefs_profile_assign_link_activate_cb), prefs);
 
-  /* create device tree view */
-  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
-                                               "treeview_devices"));
-  gtk_tree_view_set_model (GTK_TREE_VIEW (widget),
-                           GTK_TREE_MODEL (priv->list_store_devices));
-  gtk_tree_view_set_enable_tree_lines (GTK_TREE_VIEW (widget), TRUE);
-  gtk_tree_view_set_level_indentation (GTK_TREE_VIEW (widget), 0);
-  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
-  g_signal_connect (selection, "changed",
-                    G_CALLBACK (gcm_prefs_devices_treeview_clicked_cb),
-                    prefs);
-  g_signal_connect (GTK_TREE_VIEW (widget), "row-activated",
-                    G_CALLBACK (gcm_prefs_treeview_row_activated_cb),
-                    prefs);
-  g_signal_connect (GTK_TREE_VIEW (widget), "popup-menu",
-                    G_CALLBACK (gcm_prefs_treeview_popup_menu_cb),
-                    prefs);
-
-  /* add columns to the tree view */
-  gcm_prefs_add_devices_columns (prefs, GTK_TREE_VIEW (widget));
-
   /* add columns to profile tree view */
   widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                "treeview_assign"));
@@ -2552,13 +2015,17 @@ cc_color_panel_init (CcColorPanel *prefs)
   widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                "scrolledwindow_devices"));
   gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (widget),
-                                              200);
+                                              300);
 
   widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                "toolbutton_device_default"));
   g_signal_connect (widget, "clicked",
                     G_CALLBACK (gcm_prefs_default_cb), prefs);
   widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_enable"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_device_profile_enable_cb), prefs);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
                                                "toolbutton_device_calibrate"));
   g_signal_connect (widget, "clicked",
                     G_CALLBACK (gcm_prefs_calibrate_cb), prefs);
@@ -2719,8 +2186,32 @@ cc_color_panel_init (CcColorPanel *prefs)
                            G_CALLBACK (gcm_prefs_device_added_cb), prefs, 0);
   g_signal_connect_object (priv->client, "device-removed",
                            G_CALLBACK (gcm_prefs_device_removed_cb), prefs, 0);
-  g_signal_connect_object (priv->client, "changed",
-                           G_CALLBACK (gcm_prefs_changed_cb), prefs, 0);
+
+  /* use a listbox for the main UI */
+  priv->list_box = egg_list_box_new ();
+  egg_list_box_set_filter_func (priv->list_box,
+                                cc_color_panel_filter_func,
+                                prefs,
+                                NULL);
+  egg_list_box_set_sort_func (priv->list_box,
+                              cc_color_panel_sort_func,
+                              prefs,
+                              NULL);
+  egg_list_box_set_separator_funcs (priv->list_box,
+                                    cc_color_panel_separator_func,
+                                    prefs, NULL);
+  egg_list_box_set_selection_mode (priv->list_box,
+                                   GTK_SELECTION_SINGLE);
+  priv->list_box_selected_id =
+    g_signal_connect (priv->list_box, "child-selected",
+                      G_CALLBACK (gcm_prefs_list_box_child_selected_cb),
+                      prefs);
+  priv->list_box_size = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_devices"));
+  egg_list_box_add_to_scrolled (priv->list_box,
+                                GTK_SCROLLED_WINDOW (widget));
+  gtk_widget_show (GTK_WIDGET (priv->list_box));
 
   /* connect to colord */
   cd_client_connect (priv->client,
diff --git a/panels/color/cc-color-profile.c b/panels/color/cc-color-profile.c
new file mode 100644
index 0000000..4847c17
--- /dev/null
+++ b/panels/color/cc-color-profile.c
@@ -0,0 +1,497 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "cc-color-common.h"
+#include "cc-color-profile.h"
+
+struct _CcColorProfilePrivate
+{
+  CdDevice    *device;
+  CdProfile   *profile;
+  gboolean     is_default;
+  gchar       *sortable;
+  GtkWidget   *widget_description;
+  GtkWidget   *widget_image;
+  GtkWidget   *widget_info;
+  GSettings   *settings;
+  guint        device_changed_id;
+};
+
+#define GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD      "recalibrate-printer-threshold"
+#define GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD      "recalibrate-display-threshold"
+
+
+#define IMAGE_WIDGET_PADDING 12
+
+G_DEFINE_TYPE (CcColorProfile, cc_color_profile, GTK_TYPE_BOX)
+
+enum
+{
+  PROP_0,
+  PROP_DEVICE,
+  PROP_PROFILE,
+  PROP_IS_DEFAULT,
+  PROP_LAST
+};
+
+static gchar *
+cc_color_profile_get_profile_date (CdProfile *profile)
+{
+  gint64 created;
+  gchar *string = NULL;
+  GDateTime *dt = NULL;
+
+  /* get profile age */
+  created = cd_profile_get_created (profile);
+  if (created == 0)
+    goto out;
+  dt = g_date_time_new_from_unix_utc (created);
+  string = g_date_time_format (dt, "%x");
+out:
+  if (dt != NULL)
+    g_date_time_unref (dt);
+  return string;
+}
+
+static gchar *
+gcm_prefs_get_profile_title (CdProfile *profile)
+{
+  CdColorspace colorspace;
+  const gchar *tmp;
+  GString *str;
+
+  str = g_string_new ("");
+
+  /* add date only if it's a calibration profile or the profile has
+   * not been tagged with this data */
+  tmp = cd_profile_get_metadata_item (profile, CD_PROFILE_METADATA_DATA_SOURCE);
+  if (tmp == NULL || g_strcmp0 (tmp, CD_PROFILE_METADATA_DATA_SOURCE_CALIB) == 0)
+    {
+      tmp = cc_color_profile_get_profile_date (profile);
+      if (tmp != NULL)
+        g_string_append_printf (str, "%s - ", tmp);
+    }
+  else if (g_strcmp0 (tmp, CD_PROFILE_METADATA_DATA_SOURCE_STANDARD) == 0)
+    {
+      /* TRANSLATORS: standard spaces are well known colorspaces like
+       * sRGB, AdobeRGB and ProPhotoRGB */
+      g_string_append_printf (str, "%s - ", _("Standard Space"));
+    }
+  else if (g_strcmp0 (tmp, CD_PROFILE_METADATA_DATA_SOURCE_TEST) == 0)
+    {
+      /* TRANSLATORS: test profiles do things like changing the screen
+       * a different color, or swap the red and green channels */
+      g_string_append_printf (str, "%s - ", _("Test Profile"));
+    }
+  else if (g_strcmp0 (tmp, CD_PROFILE_METADATA_DATA_SOURCE_EDID) == 0)
+    {
+      /* TRANSLATORS: automatic profiles are generated automatically
+       * by the color management system based on manufacturing data,
+       * for instance the default monitor profile is created from the
+       * primaries specified in the monitor EDID */
+      g_string_append_printf (str, "%s - ", C_("Automatically generated profile", "Automatic"));
+    }
+
+  /* add quality if it exists */
+  tmp = cd_profile_get_metadata_item (profile, CD_PROFILE_METADATA_QUALITY);
+  if (g_strcmp0 (tmp, CD_PROFILE_METADATA_QUALITY_LOW) == 0)
+    {
+      /* TRANSLATORS: the profile quality - low quality profiles take
+       * much less time to generate but may be a poor reflection of the
+       * device capability */
+      g_string_append_printf (str, "%s - ", C_("Profile quality", "Low Quality"));
+    }
+  else if (g_strcmp0 (tmp, CD_PROFILE_METADATA_QUALITY_MEDIUM) == 0)
+    {
+      /* TRANSLATORS: the profile quality */
+      g_string_append_printf (str, "%s - ", C_("Profile quality", "Medium Quality"));
+    }
+  else if (g_strcmp0 (tmp, CD_PROFILE_METADATA_QUALITY_HIGH) == 0)
+    {
+      /* TRANSLATORS: the profile quality - high quality profiles take
+       * a *long* time, and have the best calibration and
+       * characterisation data. */
+      g_string_append_printf (str, "%s - ", C_("Profile quality", "High Quality"));
+    }
+
+  /* add profile description */
+  tmp = cd_profile_get_title (profile);
+  if (tmp != NULL)
+    {
+      g_string_append (str, tmp);
+      goto out;
+    }
+
+  /* some meta profiles do not have ICC profiles */
+  colorspace = cd_profile_get_colorspace (profile);
+  if (colorspace == CD_COLORSPACE_RGB)
+    {
+      /* TRANSLATORS: this default RGB space is used for printers that
+       * do not have additional printer profiles specified in the PPD */
+      g_string_append (str, C_("Colorspace fallback", "Default RGB"));
+      goto out;
+    }
+  if (colorspace == CD_COLORSPACE_CMYK)
+    {
+      /* TRANSLATORS: this default CMYK space is used for printers that
+       * do not have additional printer profiles specified in the PPD */
+      g_string_append (str, C_("Colorspace fallback", "Default CMYK"));
+      goto out;
+    }
+  if (colorspace == CD_COLORSPACE_GRAY)
+    {
+      /* TRANSLATORS: this default gray space is used for printers that
+       * do not have additional printer profiles specified in the PPD */
+      g_string_append (str, C_("Colorspace fallback", "Default Gray"));
+      goto out;
+    }
+
+  /* fall back to ID, ick */
+  tmp = g_strdup (cd_profile_get_id (profile));
+  g_string_append (str, tmp);
+out:
+  return g_string_free (str, FALSE);
+}
+
+static const gchar *
+cc_color_profile_get_warnings (CcColorProfile *color_profile)
+{
+  CcColorProfilePrivate *priv = color_profile->priv;
+  const gchar *tooltip = NULL;
+  const guint seconds_in_one_day = 60 * 60 * 24;
+  gint num_days;
+  guint threshold_days = 0;
+
+  /* autogenerated printer defaults */
+  if (cd_device_get_kind (priv->device) == CD_DEVICE_KIND_PRINTER &&
+      cd_profile_get_filename (priv->profile) == NULL)
+    {
+      tooltip = _("Vendor supplied factory calibration data");
+      goto out;
+    }
+
+  /* autogenerated profiles are crap */
+  if (cd_device_get_kind (priv->device) == CD_DEVICE_KIND_DISPLAY &&
+      cd_profile_get_kind (priv->profile) == CD_PROFILE_KIND_DISPLAY_DEVICE &&
+      !cd_profile_get_has_vcgt (priv->profile))
+    {
+      tooltip = _("Full-screen display correction not possible with this profile");
+      goto out;
+    }
+
+  /* greater than the calibration threshold for the device type */
+  num_days = cd_profile_get_age (priv->profile) / seconds_in_one_day;
+  if (cd_device_get_kind (priv->device) == CD_DEVICE_KIND_DISPLAY)
+    {
+      g_settings_get (priv->settings,
+                      GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD,
+                      "u",
+                      &threshold_days);
+    }
+  else if (cd_device_get_kind (priv->device) == CD_DEVICE_KIND_DISPLAY)
+    {
+      g_settings_get (priv->settings,
+                      GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD,
+                      "u",
+                      &threshold_days);
+    }
+  if (threshold_days > 0 && num_days > threshold_days)
+    {
+      tooltip = _("This profile may no longer be accurate");
+      goto out;
+    }
+out:
+  return tooltip;
+}
+
+static void
+cc_color_profile_refresh (CcColorProfile *color_profile)
+{
+  CcColorProfilePrivate *priv = color_profile->priv;
+  const gchar *warnings;
+  gchar *title = NULL;
+
+  /* show the image if the profile is default */
+  gtk_widget_set_visible (priv->widget_image, priv->is_default);
+  gtk_widget_set_margin_left (priv->widget_description,
+                              priv->is_default ? 0 : IMAGE_WIDGET_PADDING * 4);
+
+  /* set the title */
+  title = gcm_prefs_get_profile_title (priv->profile);
+  gtk_label_set_markup (GTK_LABEL (priv->widget_description), title);
+  g_free (title);
+
+  /* show any information */
+  warnings = cc_color_profile_get_warnings (color_profile);
+  gtk_widget_set_visible (priv->widget_info, warnings != NULL);
+  gtk_widget_set_tooltip_text (priv->widget_info, warnings);
+}
+
+CdDevice *
+cc_color_profile_get_device (CcColorProfile *color_profile)
+{
+  g_return_val_if_fail (CC_IS_COLOR_PROFILE (color_profile), NULL);
+  return color_profile->priv->device;
+}
+
+CdProfile *
+cc_color_profile_get_profile (CcColorProfile *color_profile)
+{
+  g_return_val_if_fail (CC_IS_COLOR_PROFILE (color_profile), NULL);
+  return color_profile->priv->profile;
+}
+
+const gchar *
+cc_color_profile_get_sortable (CcColorProfile *color_profile)
+{
+  g_return_val_if_fail (CC_IS_COLOR_PROFILE (color_profile), NULL);
+  return color_profile->priv->sortable;
+}
+
+gboolean
+cc_color_profile_get_is_default (CcColorProfile *color_profile)
+{
+  g_return_val_if_fail (CC_IS_COLOR_PROFILE (color_profile), 0);
+  return color_profile->priv->is_default;
+}
+
+void
+cc_color_profile_set_is_default (CcColorProfile *color_profile, gboolean is_default)
+{
+  g_return_if_fail (CC_IS_COLOR_PROFILE (color_profile));
+  color_profile->priv->is_default = is_default;
+  cc_color_profile_refresh (color_profile);
+}
+
+static void
+cc_color_profile_get_property (GObject *object, guint param_id,
+                               GValue *value, GParamSpec *pspec)
+{
+  CcColorProfile *color_profile = CC_COLOR_PROFILE (object);
+  switch (param_id)
+    {
+      case PROP_DEVICE:
+        g_value_set_object (value, color_profile->priv->device);
+        break;
+      case PROP_PROFILE:
+        g_value_set_object (value, color_profile->priv->profile);
+        break;
+      case PROP_IS_DEFAULT:
+        g_value_set_boolean (value, color_profile->priv->is_default);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        break;
+    }
+}
+
+static void
+cc_color_profile_set_property (GObject *object, guint param_id,
+                               const GValue *value, GParamSpec *pspec)
+{
+  CcColorProfile *color_profile = CC_COLOR_PROFILE (object);
+
+  switch (param_id)
+    {
+      case PROP_DEVICE:
+        color_profile->priv->device = g_value_dup_object (value);
+        break;
+      case PROP_PROFILE:
+        color_profile->priv->profile = g_value_dup_object (value);
+        break;
+      case PROP_IS_DEFAULT:
+        color_profile->priv->is_default = g_value_get_boolean (value);
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+        break;
+    }
+}
+
+static void
+cc_color_profile_finalize (GObject *object)
+{
+  CcColorProfile *color_profile = CC_COLOR_PROFILE (object);
+  CcColorProfilePrivate *priv = color_profile->priv;
+
+  if (priv->device_changed_id > 0)
+    g_signal_handler_disconnect (priv->device, priv->device_changed_id);
+
+  g_free (priv->sortable);
+  g_object_unref (priv->device);
+  g_object_unref (priv->profile);
+  g_object_unref (priv->settings);
+
+  G_OBJECT_CLASS (cc_color_profile_parent_class)->finalize (object);
+}
+
+static void
+cc_color_profile_changed_cb (CdDevice *device,
+                             CcColorProfile *color_profile)
+{
+  CdProfile *profile;
+  CcColorProfilePrivate *priv = color_profile->priv;
+
+  /* check to see if the default has changed */
+  profile = cd_device_get_default_profile (device);
+  if (profile != NULL)
+    {
+      priv->is_default = g_strcmp0 (cd_profile_get_object_path (profile),
+                                    cd_profile_get_object_path (priv->profile)) == 0;
+      g_object_unref (profile);
+    }
+  cc_color_profile_refresh (color_profile);
+}
+
+static const gchar *
+cc_color_profile_get_profile_sort_data_source (CdProfile *profile)
+{
+  const gchar *tmp;
+  tmp = cd_profile_get_metadata_item (profile, CD_PROFILE_METADATA_DATA_SOURCE);
+  if (tmp == NULL)
+    return "9";
+  if (g_strcmp0 (tmp, CD_PROFILE_METADATA_DATA_SOURCE_CALIB) == 0)
+    return "3";
+  if (g_strcmp0 (tmp, CD_PROFILE_METADATA_DATA_SOURCE_STANDARD) == 0)
+    return "2";
+  if (g_strcmp0 (tmp, CD_PROFILE_METADATA_DATA_SOURCE_TEST) == 0)
+    return "1";
+  return "0";
+}
+
+static void
+cc_color_profile_constructed (GObject *object)
+{
+  CcColorProfile *color_profile = CC_COLOR_PROFILE (object);
+  CcColorProfilePrivate *priv = color_profile->priv;
+  const gchar *sortable_data_source;
+  gchar *sortable_device;
+  gchar *title;
+
+  /* watch to see if the default changes */
+  priv->device_changed_id =
+    g_signal_connect (priv->device, "changed",
+                      G_CALLBACK (cc_color_profile_changed_cb), color_profile);
+
+  /* sort the profiles in the list by:
+   * 1. thier device (required)
+   * 2. the data source (so calibration profiles are listed before autogenerated ones)
+   * 3. the date the profiles were created (newest first)
+   * 4. the alpha sorting of the filename
+   */
+  title = gcm_prefs_get_profile_title (priv->profile);
+  sortable_device = cc_color_device_get_sortable_base (priv->device);
+  sortable_data_source = cc_color_profile_get_profile_sort_data_source (priv->profile);
+  priv->sortable = g_strdup_printf ("%s-%s-%012" G_GINT64_FORMAT "-%s",
+                                    sortable_device,
+                                    sortable_data_source,
+                                    cd_profile_get_created (priv->profile),
+                                    title);
+  g_free (title);
+  g_free (sortable_device);
+
+  cc_color_profile_refresh (color_profile);
+}
+
+static void
+cc_color_profile_class_init (CcColorProfileClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = cc_color_profile_get_property;
+  object_class->set_property = cc_color_profile_set_property;
+  object_class->constructed = cc_color_profile_constructed;
+  object_class->finalize = cc_color_profile_finalize;
+
+  g_object_class_install_property (object_class, PROP_DEVICE,
+                                   g_param_spec_object ("device", NULL,
+                                                        NULL,
+                                                        CD_TYPE_DEVICE,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_PROFILE,
+                                   g_param_spec_object ("profile", NULL,
+                                                        NULL,
+                                                        CD_TYPE_PROFILE,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+  g_object_class_install_property (object_class, PROP_IS_DEFAULT,
+                                   g_param_spec_boolean ("is-default", NULL,
+                                                         NULL,
+                                                         FALSE,
+                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_type_class_add_private (klass, sizeof (CcColorProfilePrivate));
+}
+
+static void
+cc_color_profile_init (CcColorProfile *color_profile)
+{
+  CcColorProfilePrivate *priv;
+  GtkWidget *box;
+
+  color_profile->priv = G_TYPE_INSTANCE_GET_PRIVATE (color_profile,
+                                                     CC_TYPE_COLOR_PROFILE,
+                                                     CcColorProfilePrivate);
+  priv = color_profile->priv;
+  priv->settings = g_settings_new (GCM_SETTINGS_SCHEMA);
+
+  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 9);
+
+  /* default tick */
+  priv->widget_image = gtk_image_new_from_icon_name ("object-select-symbolic", GTK_ICON_SIZE_MENU);
+  gtk_widget_set_margin_left (priv->widget_image, IMAGE_WIDGET_PADDING);
+  gtk_widget_set_margin_right (priv->widget_image, IMAGE_WIDGET_PADDING);
+  gtk_box_pack_start (GTK_BOX (box), priv->widget_image, FALSE, FALSE, 0);
+
+  /* description */
+  priv->widget_description = gtk_label_new ("");
+  gtk_widget_set_margin_top (priv->widget_description, 9);
+  gtk_widget_set_margin_bottom (priv->widget_description, 9);
+  gtk_misc_set_alignment (GTK_MISC (priv->widget_description), 0.0f, 0.5f);
+  gtk_box_pack_start (GTK_BOX (box), priv->widget_description, TRUE, TRUE, 0);
+  gtk_widget_show (priv->widget_description);
+
+  /* profile warnings/info */
+  priv->widget_info = gtk_image_new_from_icon_name ("dialog-information-symbolic", GTK_ICON_SIZE_MENU);
+  gtk_widget_set_margin_left (priv->widget_info, IMAGE_WIDGET_PADDING);
+  gtk_widget_set_margin_right (priv->widget_info, IMAGE_WIDGET_PADDING);
+  gtk_box_pack_start (GTK_BOX (box), priv->widget_info, FALSE, FALSE, 0);
+
+  /* refresh */
+  gtk_box_pack_start (GTK_BOX (color_profile), box, TRUE, TRUE, 0);
+  gtk_widget_set_visible (box, TRUE);
+}
+
+GtkWidget *
+cc_color_profile_new (CdDevice *device,
+                      CdProfile *profile,
+                      gboolean is_default)
+{
+  return g_object_new (CC_TYPE_COLOR_PROFILE,
+                       "device", device,
+                       "profile", profile,
+                       "is-default", is_default,
+                       NULL);
+}
+
diff --git a/panels/color/cc-color-profile.h b/panels/color/cc-color-profile.h
new file mode 100644
index 0000000..2842238
--- /dev/null
+++ b/panels/color/cc-color-profile.h
@@ -0,0 +1,68 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2012 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef CC_COLOR_PROFILE_H
+#define CC_COLOR_PROFILE_H
+
+#include <gtk/gtk.h>
+#include <colord.h>
+
+#define CC_TYPE_COLOR_PROFILE            (cc_color_profile_get_type())
+#define CC_COLOR_PROFILE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), CC_TYPE_COLOR_PROFILE, CcColorProfile))
+#define CC_COLOR_PROFILE_CLASS(cls)      (G_TYPE_CHECK_CLASS_CAST((cls), CC_TYPE_COLOR_PROFILE, CcColorProfileClass))
+#define CC_IS_COLOR_PROFILE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), CC_TYPE_COLOR_PROFILE))
+#define CC_IS_COLOR_PROFILE_CLASS(cls)   (G_TYPE_CHECK_CLASS_TYPE((cls), CC_TYPE_COLOR_PROFILE))
+#define CC_COLOR_PROFILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), CC_TYPE_COLOR_PROFILE, CcColorProfileClass))
+
+G_BEGIN_DECLS
+
+typedef struct _CcColorProfile                   CcColorProfile;
+typedef struct _CcColorProfileClass              CcColorProfileClass;
+typedef struct _CcColorProfilePrivate            CcColorProfilePrivate;
+
+struct _CcColorProfile
+{
+        GtkBox                   parent;
+
+        /*< private >*/
+        CcColorProfilePrivate    *priv;
+};
+
+struct _CcColorProfileClass
+{
+        GtkBoxClass              parent_class;
+};
+
+GType        cc_color_profile_get_type         (void);
+GtkWidget   *cc_color_profile_new              (CdDevice        *device,
+                                                CdProfile       *profile,
+                                                gboolean         is_default);
+gboolean     cc_color_profile_get_is_default   (CcColorProfile  *color_profile);
+void         cc_color_profile_set_is_default   (CcColorProfile  *color_profile,
+                                                gboolean         profile_is_default);
+CdDevice    *cc_color_profile_get_device       (CcColorProfile  *color_profile);
+CdProfile   *cc_color_profile_get_profile      (CcColorProfile  *color_profile);
+const gchar *cc_color_profile_get_sortable     (CcColorProfile  *color_profile);
+
+G_END_DECLS
+
+#endif /* CC_COLOR_PROFILE_H */
+
diff --git a/panels/color/color.ui b/panels/color/color.ui
index 0aaa847..86dad9b 100644
--- a/panels/color/color.ui
+++ b/panels/color/color.ui
@@ -784,6 +784,10 @@
         <property name="border_width">15</property>
         <property name="orientation">vertical</property>
         <property name="spacing">12</property>
+        <property name="margin-left">6</property>
+        <property name="margin-right">6</property>
+        <property name="margin-top">6</property>
+        <property name="margin-bottom">6</property>
         <child>
           <object class="GtkHBox" id="hbox3">
             <property name="visible">True</property>
@@ -852,13 +856,7 @@
                 <property name="can_focus">True</property>
                 <property name="shadow_type">in</property>
                 <child>
-                  <object class="GtkTreeView" id="treeview_devices">
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <child internal-child="selection">
-                      <object class="GtkTreeSelection" id="treeview-selection"/>
-                    </child>
-                  </object>
+                  <placeholder/>
                 </child>
               </object>
               <packing>
@@ -869,7 +867,6 @@
             </child>
             <child>
               <object class="GtkToolbar" id="toolbar_devices">
-                <property name="visible">True</property>
                 <property name="can_focus">False</property>
                 <property name="show_arrow">False</property>
                 <property name="icon_size">1</property>
@@ -896,6 +893,22 @@
                             <property name="position">1</property>
                           </packing>
                         </child>
+                        <child>
+                          <object class="GtkButton" id="toolbutton_device_enable">
+                            <property name="label" translatable="yes">Enable</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="has_tooltip">True</property>
+                            <property name="tooltip_markup" translatable="yes">Set this profile for all users on this computer</property>
+                            <property name="tooltip_text" translatable="yes">Set this profile for all users on this computer</property>
+                            <property name="use_underline">True</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="fill">True</property>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
                       </object>
                     </child>
                   </object>
@@ -914,7 +927,6 @@
                         <child>
                           <object class="GtkButton" id="toolbutton_profile_add">
                             <property name="label" translatable="yes">Add profile</property>
-                            <property name="visible">True</property>
                             <property name="can_focus">False</property>
                             <property name="receives_default">False</property>
                             <property name="use_underline">True</property>
@@ -982,11 +994,29 @@
             </child>
           </object>
           <packing>
-            <property name="expand">False</property>
+            <property name="expand">True</property>
             <property name="fill">True</property>
             <property name="position">1</property>
           </packing>
         </child>
+        <child>
+          <object class="GtkLabel" id="label_no_devices">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Unable to detect any devices that can be color managed</property>
+            <style>
+              <class name="dim-label"/>
+            </style>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
       </object>
     </child>
   </object>
@@ -1100,6 +1130,12 @@
     </widgets>
   </object>
   <object class="GtkSizeGroup" id="sizegroup_buttons"/>
+  <object class="GtkSizeGroup" id="sizegroup_buttons_enable">
+    <widgets>
+      <widget name="toolbutton_device_default"/>
+      <widget name="toolbutton_device_enable"/>
+    </widgets>
+  </object>
   <object class="GtkSizeGroup" id="sizegroup_combos"/>
   <object class="GtkSizeGroup" id="sizegroup_defaults"/>
   <object class="GtkSizeGroup" id="sizegroup_devices"/>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 42738ad..78da5b5 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -11,6 +11,9 @@ panels/bluetooth/gnome-bluetooth-panel.desktop.in.in
 [type: gettext/glade]panels/bluetooth/bluetooth.ui
 panels/bluetooth/cc-bluetooth-panel.c
 panels/color/cc-color-panel.c
+panels/color/cc-color-common.c
+panels/color/cc-color-device.c
+panels/color/cc-color-profile.c
 [type: gettext/glade]panels/color/color.ui
 panels/color/gnome-color-panel.desktop.in.in
 panels/common/cc-common-language.c



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