[gnome-control-center] Add a color panel based on the functionality available in gnome-color-manager



commit 2e7e54c3c2d89cb8376bccb34cfeb95e19c3582f
Author: Richard Hughes <richard hughsie com>
Date:   Thu May 19 17:36:48 2011 +0100

    Add a color panel based on the functionality available in gnome-color-manager
    
    All the major functionality is present, but the virtual device stuff needs
    further work with the designers before being included.

 configure.ac                                 |    3 +
 panels/Makefile.am                           |    1 +
 panels/color/Makefile.am                     |   34 +
 panels/color/cc-color-panel.c                | 1939 ++++++++++++++++++++++++++
 panels/color/cc-color-panel.h                |   74 +
 panels/color/color-module.c                  |   42 +
 panels/color/color.ui                        |  365 +++++
 panels/color/gnome-color-panel.desktop.in.in |   17 +
 po/POTFILES.in                               |    3 +
 po/POTFILES.skip                             |    1 +
 10 files changed, 2479 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index da06a40..f2729be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -102,6 +102,7 @@ PKG_CHECK_MODULES(MOUSE_PANEL, $COMMON_MODULES xi >= 1.2
                   gnome-settings-daemon >= $GSD_REQUIRED_VERSION x11)
 PKG_CHECK_MODULES(NETWORK_PANEL, $COMMON_MODULES)
 PKG_CHECK_MODULES(POWER_PANEL, $COMMON_MODULES upower-glib >= 0.9.1)
+PKG_CHECK_MODULES(COLOR_PANEL, $COMMON_MODULES colord >= 0.1.8)
 PKG_CHECK_MODULES(PRINTERS_PANEL, $COMMON_MODULES dbus-glib-1
                   polkit-gobject-1 >= $POLKIT_REQUIRED_VERSION)
 PKG_CHECK_MODULES(REGION_PANEL, $COMMON_MODULES libgnomekbd >= 2.91.91
@@ -354,6 +355,8 @@ panels/info/Makefile
 panels/info/gnome-info-panel.desktop.in
 panels/power/Makefile
 panels/power/gnome-power-panel.desktop.in
+panels/color/Makefile
+panels/color/gnome-color-panel.desktop.in
 panels/printers/Makefile
 panels/printers/gnome-printers-panel.desktop.in
 panels/network/Makefile
diff --git a/panels/Makefile.am b/panels/Makefile.am
index 06f9fdc..b4e335a 100644
--- a/panels/Makefile.am
+++ b/panels/Makefile.am
@@ -3,6 +3,7 @@ SUBDIRS= \
 	background \
 	screen \
 	power \
+	color \
 	display \
 	media \
 	mouse \
diff --git a/panels/color/Makefile.am b/panels/color/Makefile.am
new file mode 100644
index 0000000..e94eb91
--- /dev/null
+++ b/panels/color/Makefile.am
@@ -0,0 +1,34 @@
+cappletname = color
+
+INCLUDES = 						\
+	$(PANEL_CFLAGS)					\
+	$(COLOR_PANEL_CFLAGS)				\
+	-DGNOMECC_UI_DIR="\"$(uidir)\""			\
+	-DGNOMELOCALEDIR="\"$(datadir)/locale\""	\
+	-DBINDIR="\"$(bindir)\""			\
+	-DGNOMECC_DATA_DIR="\"$(pkgdatadir)\""		\
+	$(NULL)
+
+ccpanelsdir = $(PANELS_DIR)
+ccpanels_LTLIBRARIES = libcolor.la
+
+libcolor_la_SOURCES =		\
+	color-module.c		\
+	cc-color-panel.c	\
+	cc-color-panel.h
+
+libcolor_la_LIBADD = $(PANEL_LIBS) $(COLOR_PANEL_LIBS)
+libcolor_la_LDFLAGS = $(PANEL_LDFLAGS)
+
+uidir = $(pkgdatadir)/ui
+dist_ui_DATA = color.ui
+
+ INTLTOOL_DESKTOP_RULE@
+
+desktopdir = $(datadir)/applications
+desktop_in_files = gnome-color-panel.desktop.in
+desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
+
+CLEANFILES = $(desktop_in_files) $(desktop_DATA)
+
+-include $(top_srcdir)/git.mk
diff --git a/panels/color/cc-color-panel.c b/panels/color/cc-color-panel.c
new file mode 100644
index 0000000..6ab5a4f
--- /dev/null
+++ b/panels/color/cc-color-panel.c
@@ -0,0 +1,1939 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc
+ * Copyright (C) 2011 Richard Hughes <richard hughsie com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <colord.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "cc-color-panel.h"
+
+#define WID(b, w) (GtkWidget *) gtk_builder_get_object (b, w)
+
+G_DEFINE_DYNAMIC_TYPE (CcColorPanel, cc_color_panel, CC_TYPE_PANEL)
+
+#define COLOR_PANEL_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_COLOR_PANEL, CcColorPanelPrivate))
+
+struct _CcColorPanelPrivate
+{
+  CdClient      *client;
+  CdDevice      *current_device;
+  CdSensor      *sensor;
+  GCancellable  *cancellable;
+  GDBusProxy    *proxy;
+  GSettings     *settings;
+  GtkBuilder    *builder;
+  GtkTreeStore  *list_store_devices;
+  GtkWidget     *main_window;
+};
+
+enum {
+  GCM_PREFS_COLUMN_DEVICE_ID,
+  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
+};
+
+enum {
+  GCM_PREFS_COMBO_COLUMN_TEXT,
+  GCM_PREFS_COMBO_COLUMN_PROFILE,
+  GCM_PREFS_COMBO_COLUMN_TYPE,
+  GCM_PREFS_COMBO_COLUMN_NUM_COLUMNS
+};
+
+typedef enum {
+  GCM_PREFS_ENTRY_TYPE_PROFILE,
+  GCM_PREFS_ENTRY_TYPE_IMPORT
+} GcmPrefsEntryType;
+
+#define GCM_SETTINGS_SCHEMA                             "org.gnome.settings-daemon.plugins.color"
+#define GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD      "recalibrate-printer-threshold"
+#define GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD      "recalibrate-display-threshold"
+
+static void
+gcm_prefs_combobox_add_profile (GtkWidget *widget,
+                                CdProfile *profile,
+                                GcmPrefsEntryType entry_type,
+                                GtkTreeIter *iter)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter_tmp;
+  const gchar *description;
+
+  /* iter is optional */
+  if (iter == NULL)
+    iter = &iter_tmp;
+
+  /* use description */
+  if (entry_type == GCM_PREFS_ENTRY_TYPE_IMPORT)
+    {
+      /* TRANSLATORS: this is where the user can click and import a profile */
+      description = _("Other profileâ?¦");
+    }
+  else
+    {
+      description = cd_profile_get_title (profile);
+    }
+
+  /* also add profile */
+  model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
+  gtk_list_store_append (GTK_LIST_STORE(model), iter);
+  gtk_list_store_set (GTK_LIST_STORE(model), iter,
+                      GCM_PREFS_COMBO_COLUMN_TEXT, description,
+                      GCM_PREFS_COMBO_COLUMN_PROFILE, profile,
+                      GCM_PREFS_COMBO_COLUMN_TYPE, entry_type,
+                      -1);
+}
+
+static void
+gcm_prefs_default_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  CdProfile *profile;
+  gboolean ret;
+  GError *error = NULL;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* TODO: check if the profile is already systemwide */
+  profile = cd_device_get_default_profile (priv->current_device);
+  if (profile == NULL)
+    goto out;
+
+  /* install somewhere out of $HOME */
+  ret = cd_profile_install_system_wide_sync (profile,
+                                             priv->cancellable,
+                                             &error);
+  if (!ret)
+    {
+      g_warning ("failed to set profile system-wide: %s",
+           error->message);
+      g_error_free (error);
+      goto out;
+    }
+out:
+  if (profile != NULL)
+    g_object_unref (profile);
+}
+
+static GFile *
+gcm_prefs_file_chooser_get_icc_profile (CcColorPanel *prefs)
+{
+  GtkWindow *window;
+  GtkWidget *dialog;
+  GFile *file = NULL;
+  GtkFileFilter *filter;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* create new dialog */
+  window = GTK_WINDOW(gtk_builder_get_object (priv->builder,
+                "dialog_assign"));
+  /* TRANSLATORS: an ICC profile is a file containing colorspace data */
+  dialog = gtk_file_chooser_dialog_new (_("Select ICC Profile File"), window,
+                                        GTK_FILE_CHOOSER_ACTION_OPEN,
+                                        GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+                                        _("Import"), GTK_RESPONSE_ACCEPT,
+                                        NULL);
+  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), g_get_home_dir ());
+  gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER(dialog), FALSE);
+  gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER(dialog), FALSE);
+
+  /* setup the filter */
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_add_mime_type (filter, "application/vnd.iccprofile");
+
+  /* TRANSLATORS: filter name on the file->open dialog */
+  gtk_file_filter_set_name (filter, _("Supported ICC profiles"));
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
+
+  /* setup the all files filter */
+  filter = gtk_file_filter_new ();
+  gtk_file_filter_add_pattern (filter, "*");
+  /* TRANSLATORS: filter name on the file->open dialog */
+  gtk_file_filter_set_name (filter, _("All files"));
+  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
+
+  /* did user choose file */
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+    file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER(dialog));
+
+  /* we're done */
+  gtk_widget_destroy (dialog);
+
+  /* or NULL for missing */
+  return file;
+}
+
+static void
+gcm_prefs_calibrate_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  gboolean ret;
+  GError *error = NULL;
+  guint xid;
+  GPtrArray *argv;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* get xid */
+  xid = gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (priv->main_window)));
+
+  /* run with modal set */
+  argv = g_ptr_array_new_with_free_func (g_free);
+  g_ptr_array_add (argv, g_build_filename (BINDIR, "gcm-calibrate", NULL));
+  g_ptr_array_add (argv, g_strdup ("--device"));
+  g_ptr_array_add (argv, g_strdup (cd_device_get_id (priv->current_device)));
+  g_ptr_array_add (argv, g_strdup ("--parent-window"));
+  g_ptr_array_add (argv, g_strdup_printf ("%i", xid));
+  ret = g_spawn_async (NULL, (gchar**) argv->pdata, NULL, 0,
+                       NULL, NULL, NULL, &error);
+  if (!ret)
+    {
+      g_warning ("failed to run calibrate: %s", error->message);
+      g_error_free (error);
+    }
+  g_ptr_array_unref (argv);
+}
+
+static void
+gcm_prefs_device_add_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* show ui */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "dialog_virtual"));
+  gtk_widget_show (widget);
+  gtk_window_set_transient_for (GTK_WINDOW (widget),
+                                GTK_WINDOW (priv->main_window));
+
+  /* clear entries */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "combobox_virtual_type"));
+  gtk_combo_box_set_active (GTK_COMBO_BOX(widget), 0);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "entry_virtual_model"));
+  gtk_entry_set_text (GTK_ENTRY (widget), "");
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "entry_virtual_manufacturer"));
+  gtk_entry_set_text (GTK_ENTRY (widget), "");
+}
+
+static gboolean
+gcm_prefs_is_profile_suitable_for_device (CdProfile *profile,
+            CdDevice *device)
+{
+  CdProfileKind profile_kind_tmp;
+  CdProfileKind profile_kind;
+  CdColorspace profile_colorspace;
+  CdColorspace device_colorspace = 0;
+  gboolean ret = FALSE;
+  CdDeviceKind device_kind;
+
+  /* not the right colorspace */
+  device_colorspace = cd_device_get_colorspace (device);
+  profile_colorspace = cd_profile_get_colorspace (profile);
+  if (device_colorspace != profile_colorspace)
+    goto out;
+
+  /* not the correct kind */
+  device_kind = cd_device_get_kind (device);
+  profile_kind_tmp = cd_profile_get_kind (profile);
+  profile_kind = cd_device_kind_to_profile_kind (device_kind);
+  if (profile_kind_tmp != profile_kind)
+    goto out;
+
+  /* success */
+  ret = TRUE;
+out:
+  return ret;
+}
+
+static gint
+gcm_prefs_combo_sort_func_cb (GtkTreeModel *model,
+                              GtkTreeIter *a,
+                              GtkTreeIter *b,
+                              gpointer user_data)
+{
+  gint type_a, type_b;
+  gchar *text_a;
+  gchar *text_b;
+  gint retval;
+
+  /* get data from model */
+  gtk_tree_model_get (model, a,
+                      GCM_PREFS_COMBO_COLUMN_TYPE, &type_a,
+                      GCM_PREFS_COMBO_COLUMN_TEXT, &text_a,
+                      -1);
+  gtk_tree_model_get (model, b,
+                      GCM_PREFS_COMBO_COLUMN_TYPE, &type_b,
+                      GCM_PREFS_COMBO_COLUMN_TEXT, &text_b,
+                      -1);
+
+  /* prefer normal type profiles over the 'Other Profile...' entry */
+  if (type_a < type_b)
+    retval = -1;
+  else if (type_a > type_b)
+    retval = 1;
+  else
+    retval = g_strcmp0 (text_a, text_b);
+
+  g_free (text_a);
+  g_free (text_b);
+  return retval;
+}
+
+static void
+gcm_prefs_add_profiles_suitable_for_devices (CcColorPanel *prefs,
+                                             GtkWidget *widget,
+                                             CdProfile *profile)
+{
+  CdProfile *profile_tmp;
+  gboolean ret;
+  GError *error = NULL;
+  GPtrArray *profile_array = NULL;
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  guint i;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* clear existing entries */
+  model = gtk_combo_box_get_model (GTK_COMBO_BOX (widget));
+  gtk_list_store_clear (GTK_LIST_STORE (model));
+  gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (model),
+                                   GCM_PREFS_COMBO_COLUMN_TEXT,
+                                   gcm_prefs_combo_sort_func_cb,
+                                   model, NULL);
+
+  /* get profiles */
+  profile_array = cd_client_get_profiles_sync (priv->client,
+                                               priv->cancellable,
+                                               &error);
+  if (profile_array == NULL)
+    {
+      g_warning ("failed to get profiles: %s",
+           error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  /* add profiles of the right kind */
+  for (i = 0; i < profile_array->len; i++)
+    {
+      profile_tmp = g_ptr_array_index (profile_array, i);
+
+      /* don't add the current profile */
+      if (profile != NULL && cd_profile_equal (profile, profile_tmp))
+        continue;
+
+      /* only add correct types */
+      ret = gcm_prefs_is_profile_suitable_for_device (profile_tmp,
+                                                      priv->current_device);
+      if (!ret)
+        continue;
+
+      /* add */
+      gcm_prefs_combobox_add_profile (widget,
+                                      profile_tmp,
+                                      GCM_PREFS_ENTRY_TYPE_PROFILE,
+                                      &iter);
+    }
+
+  /* add a import entry */
+  gcm_prefs_combobox_add_profile (widget, NULL, GCM_PREFS_ENTRY_TYPE_IMPORT, NULL);
+  gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+out:
+  if (profile_array != NULL)
+    g_ptr_array_unref (profile_array);
+}
+
+static void
+gcm_prefs_profile_add_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  CdProfile *profile = NULL;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* add profiles of the right kind */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "combobox_profile"));
+  profile = cd_device_get_default_profile (priv->current_device);
+  gcm_prefs_add_profiles_suitable_for_devices (prefs, widget, profile);
+
+  /* show the dialog */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "dialog_assign"));
+  gtk_widget_show (widget);
+  gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (priv->main_window));
+  if (profile != NULL)
+    g_object_unref (profile);
+}
+
+static void
+gcm_prefs_profile_remove_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  GtkTreeIter iter;
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  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))
+    g_assert_not_reached ();
+
+  /* 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);
+
+  /* just remove it, the list store will get ::changed */
+  ret = cd_device_remove_profile_sync (priv->current_device,
+                                       profile,
+                                       priv->cancellable,
+                                       &error);
+  if (!ret)
+    {
+      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
+gcm_prefs_profile_make_default_internal (CcColorPanel *prefs,
+                                         GtkTreeModel *model,
+                                         GtkTreeIter *iter_selected)
+{
+  CdProfile *profile;
+  GError *error = NULL;
+  gboolean ret = FALSE;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* get currentlt selected item */
+  gtk_tree_model_get (model, iter_selected,
+                      GCM_PREFS_COLUMN_PROFILE, &profile,
+                      -1);
+  if (profile == NULL)
+    goto out;
+
+  /* just set it default */
+  ret = cd_device_make_profile_default_sync (priv->current_device,
+                                             profile,
+                                             priv->cancellable,
+                                             &error);
+  if (!ret)
+    {
+      g_warning ("failed to set default profile: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+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;
+  gchar *options = NULL;
+  GPtrArray *argv = NULL;
+  guint xid;
+  gboolean ret;
+  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))
+    g_assert_not_reached ();
+
+  /* get currentlt selected item */
+  gtk_tree_model_get (model, &iter,
+                      GCM_PREFS_COLUMN_PROFILE, &profile,
+                      -1);
+
+  /* get xid */
+  xid = gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (priv->main_window)));
+
+  /* open up gcm-viewer as a info pane */
+  argv = g_ptr_array_new_with_free_func (g_free);
+  g_ptr_array_add (argv, g_build_filename (BINDIR, "gcm-viewer", NULL));
+  g_ptr_array_add (argv, g_strdup ("--profile"));
+  g_ptr_array_add (argv, g_strdup (cd_profile_get_id (profile)));
+  g_ptr_array_add (argv, g_strdup ("--parent-window"));
+  g_ptr_array_add (argv, g_strdup_printf ("%i", xid));
+  ret = g_spawn_async (NULL, (gchar**) argv->pdata, NULL, 0,
+                       NULL, NULL, NULL, &error);
+  if (!ret)
+    {
+      g_warning ("failed to run calibrate: %s", error->message);
+      g_error_free (error);
+    }
+
+  if (argv != NULL)
+    g_ptr_array_unref (argv);
+  g_free (options);
+  if (profile != NULL)
+    g_object_unref (profile);
+}
+
+static void
+gcm_prefs_button_assign_cancel_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  CcColorPanelPrivate *priv = prefs->priv;
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "dialog_assign"));
+  gtk_widget_hide (widget);
+}
+
+static void
+gcm_prefs_button_assign_ok_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  CdProfile *profile = NULL;
+  gboolean ret = FALSE;
+  GError *error = NULL;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* hide window */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "dialog_assign"));
+  gtk_widget_hide (widget);
+
+  /* get entry */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "combobox_profile"));
+  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(widget), &iter);
+  if (!ret)
+    goto out;
+  model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
+  gtk_tree_model_get (model, &iter,
+                      GCM_PREFS_COMBO_COLUMN_PROFILE, &profile,
+                      -1);
+
+  /* just add it, the list store will get ::changed */
+  ret = cd_device_add_profile_sync (priv->current_device,
+                                    CD_DEVICE_RELATION_HARD,
+                                    profile,
+                                    priv->cancellable,
+                                    &error);
+  if (!ret)
+    {
+      g_warning ("failed to add: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  /* make it default */
+  ret = cd_device_make_profile_default_sync (priv->current_device,
+                                             profile,
+                                             priv->cancellable,
+                                             &error);
+  if (!ret)
+    {
+      g_warning ("failed to set default: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+out:
+  if (profile != NULL)
+    g_object_unref (profile);
+}
+
+static gboolean
+gcm_prefs_profile_delete_event_cb (GtkWidget *widget,
+                                   GdkEvent *event,
+                                   CcColorPanel *prefs)
+{
+  gcm_prefs_button_assign_cancel_cb (widget, prefs);
+  return TRUE;
+}
+
+static void
+gcm_prefs_delete_cb (GtkWidget *widget, CcColorPanel *prefs)
+{
+  gboolean ret = FALSE;
+  GError *error = NULL;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* try to delete device */
+  ret = cd_client_delete_device_sync (priv->client,
+                                      cd_device_get_id (priv->current_device),
+                                      priv->cancellable,
+                                      &error);
+  if (!ret)
+    {
+      g_warning ("failed to delete device: %s", error->message);
+      g_error_free (error);
+    }
+}
+
+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_set_calibrate_button_sensitivity (CcColorPanel *prefs)
+{
+  gboolean ret = FALSE;
+  GtkWidget *widget;
+  const gchar *tooltip;
+  CdDeviceKind kind;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* TRANSLATORS: this is when the button is sensitive */
+  tooltip = _("Create a color profile for the selected device");
+
+  /* no device selected */
+  if (priv->current_device == NULL)
+    goto out;
+
+  /* are we a display */
+  kind = cd_device_get_kind (priv->current_device);
+  if (kind == CD_DEVICE_KIND_DISPLAY)
+    {
+
+      /* find whether we have hardware installed */
+      if (priv->sensor == NULL) {
+        /* TRANSLATORS: this is when the button is insensitive */
+        tooltip = _("The measuring instrument is not detected. Please check it is turned on and correctly connected.");
+        goto out;
+      }
+
+      /* success */
+      ret = TRUE;
+
+    }
+  else if (kind == CD_DEVICE_KIND_SCANNER ||
+           kind == CD_DEVICE_KIND_CAMERA ||
+           kind == CD_DEVICE_KIND_WEBCAM)
+    {
+
+      /* TODO: find out if we can scan using gnome-scan */
+      ret = TRUE;
+
+    }
+  else if (kind == CD_DEVICE_KIND_PRINTER)
+    {
+
+    /* find whether we have hardware installed */
+    if (priv->sensor == NULL)
+      {
+        /* TRANSLATORS: this is when the button is insensitive */
+        tooltip = _("The measuring instrument is not detected. Please check it is turned on and correctly connected.");
+        goto out;
+      }
+
+    /* find whether we have hardware installed */
+    ret = cd_sensor_has_cap (priv->sensor, CD_SENSOR_CAP_PRINTER);
+    if (!ret)
+      {
+        /* TRANSLATORS: this is when the button is insensitive */
+        tooltip = _("The measuring instrument does not support printer profiling.");
+        goto out;
+      }
+
+    /* success */
+    ret = TRUE;
+
+    }
+  else
+    {
+      /* TRANSLATORS: this is when the button is insensitive */
+      tooltip = _("The device type is not currently supported.");
+    }
+out:
+  /* control the tooltip and sensitivity of the button */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_calibrate"));
+  gtk_widget_set_tooltip_text (widget, tooltip);
+  gtk_widget_set_sensitive (widget, ret);
+}
+
+static void
+gcm_prefs_device_clicked (CcColorPanel *prefs, CdDevice *device)
+{
+  GtkWidget *widget;
+  CdDeviceMode device_mode;
+  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));
+
+  /* make sure selectable */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "combobox_profile"));
+  gtk_widget_set_sensitive (widget, TRUE);
+
+  /* can we delete this device? */
+  device_mode = cd_device_get_mode (priv->current_device);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_remove"));
+  gtk_widget_set_visible (widget, device_mode == CD_DEVICE_MODE_VIRTUAL);
+
+  /* can this device calibrate */
+  gcm_prefs_set_calibrate_button_sensitivity (prefs);
+}
+
+static void
+gcm_prefs_profile_clicked (CcColorPanel *prefs, CdProfile *profile, CdDevice *device)
+{
+  GtkWidget *widget;
+  CdDeviceRelation relation;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* get profile */
+  g_debug ("selected profile = %s",
+     cd_profile_get_filename (profile));
+
+
+  /* find the profile relationship */
+  relation = cd_device_get_profile_relation (device,
+               profile,
+               NULL, NULL);
+
+  /* we can only remove hard relationships */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_profile_remove"));
+  if (relation == CD_DEVICE_RELATION_HARD)
+    {
+      gtk_widget_set_tooltip_text (widget, "");
+      gtk_widget_set_sensitive (widget, TRUE);
+    }
+  else
+    {
+      /* TRANSLATORS: this is when an auto-added profile cannot be removed */
+      gtk_widget_set_tooltip_text (widget, _("Cannot remove automatically added profile"));
+      gtk_widget_set_sensitive (widget, FALSE);
+    }
+
+  /* allow getting profile info */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+               "toolbutton_profile_view"));
+  gtk_widget_set_sensitive (widget, TRUE);
+}
+
+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))
+    g_assert_not_reached ();
+
+  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, FALSE);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_add"));
+  gtk_widget_set_visible (widget, FALSE);
+  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_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 (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 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_set_combo_simple_text (GtkWidget *combo_box)
+{
+  GtkCellRenderer *renderer;
+  GtkListStore *store;
+
+  store = gtk_list_store_new (GCM_PREFS_COMBO_COLUMN_NUM_COLUMNS,
+                              G_TYPE_STRING,
+                              CD_TYPE_PROFILE,
+                              G_TYPE_UINT);
+  gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box),
+                           GTK_TREE_MODEL (store));
+  g_object_unref (store);
+
+  renderer = gtk_cell_renderer_text_new ();
+  g_object_set (renderer,
+                "ellipsize", PANGO_ELLIPSIZE_END,
+                "wrap-mode", PANGO_WRAP_WORD_CHAR,
+                "width-chars", 60,
+                NULL);
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE);
+  gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer,
+                                  "text", GCM_PREFS_COMBO_COLUMN_TEXT,
+                                  NULL);
+}
+
+static void
+gcm_prefs_profile_combo_changed_cb (GtkWidget *widget,
+                                    CcColorPanel *prefs)
+{
+  GFile *file = NULL;
+  gboolean ret;
+  CdProfile *profile = NULL;
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  GcmPrefsEntryType entry_type;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* no devices */
+  if (priv->current_device == NULL)
+    return;
+
+  /* no selection */
+  ret = gtk_combo_box_get_active_iter (GTK_COMBO_BOX(widget), &iter);
+  if (!ret)
+    return;
+
+  /* get entry */
+  model = gtk_combo_box_get_model (GTK_COMBO_BOX(widget));
+  gtk_tree_model_get (model, &iter,
+                      GCM_PREFS_COMBO_COLUMN_TYPE, &entry_type,
+                      -1);
+
+  /* import */
+  if (entry_type == GCM_PREFS_ENTRY_TYPE_IMPORT)
+    {
+      file = gcm_prefs_file_chooser_get_icc_profile (prefs);
+      if (file == NULL)
+        {
+          g_warning ("failed to get ICC file");
+          gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0);
+          goto out;
+        }
+
+      /* add to combobox */
+      gtk_list_store_append (GTK_LIST_STORE(model), &iter);
+      gtk_list_store_set (GTK_LIST_STORE(model), &iter,
+                          GCM_PREFS_COMBO_COLUMN_PROFILE, profile,
+                          -1);
+      gtk_combo_box_set_active_iter (GTK_COMBO_BOX (widget), &iter);
+    }
+out:
+  if (file != NULL)
+    g_object_unref (file);
+  if (profile != NULL)
+    g_object_unref (profile);
+}
+
+
+
+static void
+gcm_prefs_sensor_coldplug (CcColorPanel *prefs)
+{
+  GPtrArray *sensors;
+  GError *error = NULL;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* unref old */
+  if (priv->sensor != NULL)
+    {
+      g_object_unref (priv->sensor);
+      priv->sensor = NULL;
+    }
+
+  /* no present */
+  sensors = cd_client_get_sensors_sync (priv->client, NULL, &error);
+  if (sensors == NULL)
+    {
+      g_warning ("%s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+  if (sensors->len == 0)
+    goto out;
+
+  /* save a copy of the sensor */
+  priv->sensor = g_object_ref (g_ptr_array_index (sensors, 0));
+out:
+  if (sensors != NULL)
+    g_ptr_array_unref (sensors);
+}
+
+static void
+gcm_prefs_client_sensor_changed_cb (CdClient *client,
+            CdSensor *sensor,
+            CcColorPanel *prefs)
+{
+  gcm_prefs_sensor_coldplug (prefs);
+  gcm_prefs_set_calibrate_button_sensitivity (prefs);
+}
+
+static const gchar *
+gcm_prefs_device_kind_to_icon_name (CdDeviceKind kind)
+{
+  if (kind == CD_DEVICE_KIND_DISPLAY)
+    return "video-display";
+  if (kind == CD_DEVICE_KIND_SCANNER)
+    return "scanner";
+  if (kind == CD_DEVICE_KIND_PRINTER)
+    return "printer";
+  if (kind == CD_DEVICE_KIND_CAMERA)
+    return "camera-photo";
+  if (kind == CD_DEVICE_KIND_CAMERA)
+    return "camera-photo";
+  if (kind == CD_DEVICE_KIND_WEBCAM)
+    return "camera-web";
+  return "image-missing";
+}
+
+static gboolean
+gcm_prefs_profile_is_based_from_edid (CdProfile *profile)
+{
+  if (cd_profile_get_kind (profile) != CD_PROFILE_KIND_DISPLAY_DEVICE)
+    return FALSE;
+  if (cd_profile_get_metadata_item (profile, "EDID_md5") == NULL)
+    return FALSE;
+  return TRUE;
+}
+
+static GString *
+gcm_prefs_get_profile_age_as_string (CdProfile *profile)
+{
+  gint64 age;
+  GString *string;
+
+  if (profile == NULL)
+    {
+      /* TRANSLATORS: this is when there is no profile for the device */
+      string = g_string_new (_("No profile"));
+      goto out;
+    }
+
+  /* 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)
+    {
+      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);
+      goto out;
+    }
+
+  /* fallback */
+  g_string_append_printf (string, _("Less than 1 week"));
+out:
+  return string;
+}
+
+static gchar *
+gcm_prefs_get_profile_created_for_sort (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);
+  /* 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 (CdProfile *profile)
+{
+  CdColorspace colorspace;
+  const gchar *title;
+  gchar *string;
+
+  g_return_val_if_fail (profile != NULL, NULL);
+
+  /* add profile description */
+  title = cd_profile_get_title (profile);
+  if (title != NULL)
+    {
+      string = g_strdup (title);
+      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)
+    {
+      string = g_strdup (C_("Colorspace fallback", "Default Gray"));
+      goto out;
+    }
+
+  /* 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_ID, NULL,
+                        -1);
+  } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+gcm_prefs_device_remove_profiles_phase2 (CcColorPanel *prefs, GtkTreeIter *parent)
+{
+  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;
+
+  /* remove the other elements */
+  do
+    {
+      gtk_tree_model_get (model, &iter,
+              GCM_PREFS_COLUMN_DEVICE_ID, &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);
+}
+
+static GtkTreeIter *
+get_iter_for_profile (GtkTreeModel *model, CdProfile *profile, GtkTreeIter *parent)
+{
+  const gchar *id;
+  GtkTreeIter iter_tmp;
+  gboolean ret;
+  GtkTreeIter *iter = NULL;
+  CdProfile *profile_tmp;
+
+  /* get first element */
+  ret = gtk_tree_model_iter_children (model, &iter_tmp, parent);
+  if (!ret)
+    goto out;
+
+  /* remove the other elements */
+  id = cd_profile_get_id (profile);
+  do
+    {
+      gtk_tree_model_get (model, &iter_tmp,
+              GCM_PREFS_COLUMN_PROFILE, &profile_tmp,
+              -1);
+      if (g_strcmp0 (id, cd_profile_get_id (profile_tmp)) == 0)
+        iter = &iter_tmp;
+      g_object_unref (profile_tmp);
+    } while (iter == NULL && gtk_tree_model_iter_next (model, &iter_tmp));
+out:
+  return iter;
+}
+
+static void
+gcm_prefs_device_set_model_by_iter (CcColorPanel *prefs, CdDevice *device, GtkTreeIter *iter)
+{
+  GString *status;
+  const gchar *status_image = NULL;
+  const gchar *tooltip = NULL;
+  CdProfile *profile = NULL;
+  gint age;
+  GPtrArray *profiles;
+  CdProfile *profile_tmp;
+  guint i;
+  gchar *title_tmp;
+  GString *date_tmp;
+  gchar *sort_tmp;
+  GtkTreeIter iter_tmp;
+  GtkTreeIter *iter_tmp_p;
+  guint threshold = 0;
+  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;
+    }
+
+  /* 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;
+    }
+
+  /* autogenerated profiles are crap */
+  if (cd_profile_get_kind (profile) == CD_PROFILE_KIND_DISPLAY_DEVICE &&
+      !cd_profile_get_has_vcgt (profile))
+    {
+      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;
+    }
+
+  /* yay! */
+  status = gcm_prefs_get_profile_age_as_string (profile);
+
+  /* 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.");
+    }
+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;
+  for (i = 0; i < profiles->len; i++)
+    {
+      profile_tmp = g_ptr_array_index (profiles, i);
+      title_tmp = gcm_prefs_get_profile_title (profile_tmp);
+
+      /* don't show details for EDID profiles */
+      if (gcm_prefs_profile_is_based_from_edid (profile_tmp))
+        {
+          date_tmp = g_string_new ("Not specified");
+          g_string_prepend (date_tmp, "<span foreground='gray'><i>");
+          g_string_append (date_tmp, "</i></span>");
+        }
+      else
+        {
+          date_tmp = gcm_prefs_get_profile_age_as_string (profile_tmp);
+        }
+      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);
+          iter_tmp_p = &iter_tmp;
+        }
+
+      gtk_tree_store_set (priv->list_store_devices, iter_tmp_p,
+                          GCM_PREFS_COLUMN_DEVICE, device,
+                          GCM_PREFS_COLUMN_PROFILE, profile_tmp,
+                          GCM_PREFS_COLUMN_DEVICE_ID, cd_device_get_id (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);
+      g_free (title_tmp);
+      g_free (sort_tmp);
+      g_string_free (date_tmp, TRUE);
+    }
+
+  /* remove old profiles that no longer exist */
+  gcm_prefs_device_remove_profiles_phase2 (prefs, iter);
+out:
+  g_string_free (status, TRUE);
+  if (profiles != NULL)
+    g_ptr_array_unref (profiles);
+  if (profile != NULL)
+    g_object_unref (profile);
+}
+
+static void
+gcm_prefs_device_changed_cb (CdDevice *device, CcColorPanel *prefs)
+{
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  const gchar *id;
+  gchar *id_tmp;
+  gboolean ret;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* 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_id (device);
+  do
+    {
+      gtk_tree_model_get (model, &iter,
+              GCM_PREFS_COLUMN_DEVICE_ID, &id_tmp,
+              -1);
+      if (g_strcmp0 (id_tmp, id) == 0)
+        gcm_prefs_device_set_model_by_iter (prefs, device, &iter);
+      g_free (id_tmp);
+    } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+gcm_prefs_add_device (CcColorPanel *prefs, CdDevice *device)
+{
+  CdDeviceKind kind;
+  const gchar *icon_name;
+  const gchar *id;
+  gchar *sort = NULL;
+  gchar *title = NULL;
+  GtkTreeIter parent;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* 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);
+
+  /* create sort order */
+  sort = g_strdup_printf ("%s%s",
+        gcm_prefs_device_kind_to_sort (kind),
+        title);
+
+  /* watch for changes to update the status icons */
+  g_signal_connect (device, "changed",
+                    G_CALLBACK (gcm_prefs_device_changed_cb), prefs);
+
+  /* add to list */
+  id = cd_device_get_id (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_ID, 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);
+  g_free (sort);
+  g_free (title);
+}
+
+static void
+gcm_prefs_remove_device (CcColorPanel *prefs, CdDevice *cd_device)
+{
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  const gchar *id;
+  gchar *id_tmp;
+  gboolean ret;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* remove */
+  id = cd_device_get_id (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
+    {
+      gtk_tree_model_get (model, &iter,
+              GCM_PREFS_COLUMN_DEVICE_ID, &id_tmp,
+              -1);
+      if (g_strcmp0 (id_tmp, id) == 0)
+        {
+          gtk_list_store_remove (GTK_LIST_STORE(model), &iter);
+          g_free (id_tmp);
+          break;
+        }
+      g_free (id_tmp);
+    } while (gtk_tree_model_iter_next (model, &iter));
+}
+
+static void
+gcm_prefs_device_added_cb (CdClient *client,
+                           CdDevice *device,
+                           CcColorPanel *prefs)
+{
+  /* remove the saved device if it's already there */
+  gcm_prefs_remove_device (prefs, device);
+
+  /* add the device */
+  gcm_prefs_add_device (prefs, device);
+}
+
+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);
+
+  /* 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 void
+gcm_prefs_get_devices_cb (GObject *object,
+                          GAsyncResult *res,
+                          gpointer user_data)
+{
+  CcColorPanel *prefs = (CcColorPanel *) user_data;
+  CdClient *client = CD_CLIENT (object);
+  CdDevice *device;
+  GError *error = NULL;
+  GPtrArray *devices;
+  GtkTreePath *path;
+  GtkWidget *widget;
+  guint i;
+  CcColorPanelPrivate *priv = prefs->priv;
+
+  /* get devices and add them */
+  devices = cd_client_get_devices_finish (client, res, &error);
+  if (devices == NULL)
+    {
+      g_warning ("failed to add connected devices: %s",
+                 error->message);
+      g_error_free (error);
+      goto out;
+    }
+  for (i = 0; i < devices->len; i++)
+    {
+      device = g_ptr_array_index (devices, i);
+      gcm_prefs_add_device (prefs, device);
+    }
+
+  /* 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);
+out:
+  if (devices != NULL)
+    g_ptr_array_unref (devices);
+}
+
+static void
+cc_color_panel_get_property (GObject    *object,
+                              guint       property_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  switch (property_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+cc_color_panel_set_property (GObject      *object,
+                              guint         property_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  switch (property_id)
+    {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+    }
+}
+
+static void
+cc_color_panel_dispose (GObject *object)
+{
+  CcColorPanelPrivate *priv = CC_COLOR_PANEL (object)->priv;
+
+  if (priv->settings)
+    {
+      g_object_unref (priv->settings);
+      priv->settings = NULL;
+    }
+  if (priv->cancellable != NULL)
+    {
+      g_cancellable_cancel (priv->cancellable);
+      g_object_unref (priv->cancellable);
+      priv->cancellable = NULL;
+    }
+  if (priv->builder != NULL)
+    {
+      g_object_unref (priv->builder);
+      priv->builder = NULL;
+    }
+  if (priv->client != NULL)
+    {
+      g_object_unref (priv->client);
+      priv->client = NULL;
+    }
+  if (priv->current_device != NULL)
+    {
+      g_object_unref (priv->current_device);
+      priv->current_device = NULL;
+    }
+  if (priv->sensor != NULL)
+    {
+      g_object_unref (priv->sensor);
+      priv->sensor = NULL;
+    }
+
+  G_OBJECT_CLASS (cc_color_panel_parent_class)->dispose (object);
+}
+
+static void
+cc_color_panel_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (cc_color_panel_parent_class)->finalize (object);
+}
+
+static void
+cc_color_panel_class_init (CcColorPanelClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (CcColorPanelPrivate));
+
+  object_class->get_property = cc_color_panel_get_property;
+  object_class->set_property = cc_color_panel_set_property;
+  object_class->dispose = cc_color_panel_dispose;
+  object_class->finalize = cc_color_panel_finalize;
+}
+
+static void
+cc_color_panel_class_finalize (CcColorPanelClass *klass)
+{
+}
+
+static void
+cc_color_panel_init (CcColorPanel *prefs)
+{
+  CcColorPanelPrivate *priv;
+  gboolean ret;
+  gchar *text = NULL;
+  GError *error = NULL;
+  GtkStyleContext *context;
+  GtkTreeSelection *selection;
+  GtkWidget *widget;
+
+  priv = prefs->priv = COLOR_PANEL_PRIVATE (prefs);
+
+  priv->builder = gtk_builder_new ();
+  gtk_builder_add_from_file (priv->builder,
+                             GNOMECC_UI_DIR "/color.ui",
+                             &error);
+
+  if (error != NULL)
+    {
+      g_warning ("Could not load interface file: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  priv->cancellable = g_cancellable_new ();
+
+  /* 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"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_profile_add_cb), prefs);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_profile_remove"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_profile_remove_cb), prefs);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_profile_view"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_profile_view_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);
+
+  /* add columns to the tree view */
+  gcm_prefs_add_devices_columns (prefs, GTK_TREE_VIEW (widget));
+
+  /* force to be at least 3 rows high */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "scrolledwindow_devices"));
+
+  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_remove"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_delete_cb), prefs);
+  gtk_widget_set_sensitive (widget, FALSE);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbutton_device_add"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_device_add_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);
+
+  /* make devices toolbar sexy */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "scrolledwindow_devices"));
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
+
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "toolbar_devices"));
+  context = gtk_widget_get_style_context (widget);
+  gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR);
+  gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
+
+  /* set up assign dialog */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "dialog_assign"));
+  g_signal_connect (widget, "delete-event",
+                    G_CALLBACK (gcm_prefs_profile_delete_event_cb), prefs);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "button_assign_cancel"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_button_assign_cancel_cb), prefs);
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "button_assign_ok"));
+  g_signal_connect (widget, "clicked",
+                    G_CALLBACK (gcm_prefs_button_assign_ok_cb), prefs);
+
+  /* setup icc profiles list */
+  widget = GTK_WIDGET (gtk_builder_get_object (priv->builder,
+                                               "combobox_profile"));
+  gcm_prefs_set_combo_simple_text (widget);
+  gtk_widget_set_sensitive (widget, FALSE);
+  g_signal_connect (G_OBJECT (widget), "changed",
+                    G_CALLBACK (gcm_prefs_profile_combo_changed_cb), prefs);
+
+  /* use a device client array */
+  priv->client = cd_client_new ();
+  g_signal_connect (priv->client, "device-added",
+                    G_CALLBACK (gcm_prefs_device_added_cb), prefs);
+  g_signal_connect (priv->client, "device-removed",
+                    G_CALLBACK (gcm_prefs_device_removed_cb), prefs);
+  g_signal_connect (priv->client, "changed",
+                    G_CALLBACK (gcm_prefs_changed_cb), prefs);
+
+  /* connect to colord */
+  ret = cd_client_connect_sync (priv->client,
+                                priv->cancellable,
+                                &error);
+  if (!ret)
+    {
+      g_warning ("failed to connect to colord: %s", error->message);
+      g_error_free (error);
+      goto out;
+    }
+
+  /* get devices async */
+  cd_client_get_devices (priv->client,
+                         priv->cancellable,
+                         gcm_prefs_get_devices_cb,
+                         prefs);
+
+  /* use the color sensor */
+  g_signal_connect (priv->client, "sensor-added",
+                    G_CALLBACK (gcm_prefs_client_sensor_changed_cb),
+                    prefs);
+  g_signal_connect (priv->client, "sensor-removed",
+                    G_CALLBACK (gcm_prefs_client_sensor_changed_cb),
+                    prefs);
+
+out:
+  g_free (text);
+
+  /* set calibrate button sensitivity */
+  gcm_prefs_sensor_coldplug (prefs);
+  gcm_prefs_set_calibrate_button_sensitivity (prefs);
+
+  widget = WID (priv->builder, "dialog-vbox1");
+  gtk_widget_reparent (widget, (GtkWidget *) prefs);
+  priv->main_window = gtk_widget_get_toplevel (widget);
+}
+
+void
+cc_color_panel_register (GIOModule *module)
+{
+  cc_color_panel_register_type (G_TYPE_MODULE (module));
+  g_io_extension_point_implement (CC_SHELL_PANEL_EXTENSION_POINT,
+                                  CC_TYPE_COLOR_PANEL,
+                                  "color", 0);
+}
+
diff --git a/panels/color/cc-color-panel.h b/panels/color/cc-color-panel.h
new file mode 100644
index 0000000..2b7070e
--- /dev/null
+++ b/panels/color/cc-color-panel.h
@@ -0,0 +1,74 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc
+ * Copyright (C) 2011 Richard Hughes <richard hughsie com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+
+#ifndef _CC_COLOR_PANEL_H
+#define _CC_COLOR_PANEL_H
+
+#include <libgnome-control-center/cc-panel.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_COLOR_PANEL cc_color_panel_get_type()
+
+#define CC_COLOR_PANEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+  CC_TYPE_COLOR_PANEL, CcColorPanel))
+
+#define CC_COLOR_PANEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_CAST ((klass), \
+  CC_TYPE_COLOR_PANEL, CcColorPanelClass))
+
+#define CC_IS_COLOR_PANEL(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+  CC_TYPE_COLOR_PANEL))
+
+#define CC_IS_COLOR_PANEL_CLASS(klass) \
+  (G_TYPE_CHECK_CLASS_TYPE ((klass), \
+  CC_TYPE_COLOR_PANEL))
+
+#define CC_COLOR_PANEL_GET_CLASS(obj) \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+  CC_TYPE_COLOR_PANEL, CcColorPanelClass))
+
+typedef struct _CcColorPanel CcColorPanel;
+typedef struct _CcColorPanelClass CcColorPanelClass;
+typedef struct _CcColorPanelPrivate CcColorPanelPrivate;
+
+struct _CcColorPanel
+{
+  CcPanel parent;
+
+  CcColorPanelPrivate *priv;
+};
+
+struct _CcColorPanelClass
+{
+  CcPanelClass parent_class;
+};
+
+GType cc_color_panel_get_type (void) G_GNUC_CONST;
+
+void  cc_color_panel_register (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* _CC_COLOR_PANEL_H */
diff --git a/panels/color/color-module.c b/panels/color/color-module.c
new file mode 100644
index 0000000..31106d8
--- /dev/null
+++ b/panels/color/color-module.c
@@ -0,0 +1,42 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2010 Red Hat, Inc
+ * Copyright (C) 2011 Richard Hughes <richard hughsie com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ */
+
+#include <config.h>
+
+#include "cc-color-panel.h"
+
+#include <glib/gi18n-lib.h>
+
+void
+g_io_module_load (GIOModule *module)
+{
+  bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+
+  /* register the panel */
+  cc_color_panel_register (module);
+}
+
+void
+g_io_module_unload (GIOModule *module)
+{
+}
diff --git a/panels/color/color.ui b/panels/color/color.ui
new file mode 100644
index 0000000..e802b51
--- /dev/null
+++ b/panels/color/color.ui
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkDialog" id="dialog_assign">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="title"> </property>
+    <property name="modal">True</property>
+    <property name="window_position">center-on-parent</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="icon_name">gnome-color-manager</property>
+    <property name="type_hint">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="skip_pager_hint">True</property>
+    <property name="transient_for">dialog_prefs</property>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox3">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area3">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="button_assign_cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+                <property name="secondary">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button_assign_ok">
+                <property name="label">gtk-add</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_action_appearance">False</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox5">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">9</property>
+            <child>
+              <object class="GtkHBox" id="hbox29">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkLabel" id="label12">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes" comments="Profiles that can be added to the device">Available Profiles</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="combobox_profile">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox57">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">9</property>
+                <child>
+                  <object class="GtkImage" id="image2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="stock">gtk-dialog-info</property>
+                    <property name="icon-size">6</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label13">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label" translatable="yes" comments="Some profiles are not compatible with some devices">Only profiles that are compatible with the device will be listed above.</property>
+                    <property name="wrap">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">button_assign_cancel</action-widget>
+      <action-widget response="0">button_assign_ok</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkWindow" id="dialog_prefs">
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="title" translatable="yes">Color</property>
+    <property name="window_position">center</property>
+    <property name="icon_name">preferences-system</property>
+    <child>
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="border_width">15</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkHBox" id="hbox3">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="spacing">9</property>
+            <child>
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Each device needs an up to date color profile to be color managed.</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkVBox" id="vbox3">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow_devices">
+                <property name="visible">True</property>
+                <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>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </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>
+                <child>
+                  <object class="GtkToolButton" id="toolbutton_device_add">
+                    <property name="can_focus">False</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Add a virtual device</property>
+                    <property name="tooltip_text">Add a virtual device</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Add device</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="toolbutton_device_remove">
+                    <property name="sensitive">False</property>
+                    <property name="can_focus">False</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Remove a device</property>
+                    <property name="tooltip_text">Remove a device</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Delete device</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkSeparatorToolItem" id="toolbutton2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="use_action_appearance">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="toolbutton_device_default">
+                    <property name="can_focus">False</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Set this device for all users on this computer</property>
+                    <property name="tooltip_text">Set this device for all users on this computer</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Set for all users</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="toolbutton_profile_add">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Add profile</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="toolbutton_device_calibrate">
+                    <property name="can_focus">False</property>
+                    <property name="has_tooltip">True</property>
+                    <property name="tooltip_markup" translatable="yes">Calibrate the device</property>
+                    <property name="tooltip_text" translatable="yes">Calibrate the device</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Calibrateâ?¦</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="toolbutton_profile_remove">
+                    <property name="can_focus">False</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">Remove profile</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="toolbutton_profile_view">
+                    <property name="can_focus">False</property>
+                    <property name="use_action_appearance">False</property>
+                    <property name="is_important">True</property>
+                    <property name="label" translatable="yes">View details</property>
+                    <property name="use_underline">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+  <object class="GtkSizeGroup" id="sizegroup_buttons"/>
+  <object class="GtkSizeGroup" id="sizegroup_combos"/>
+  <object class="GtkSizeGroup" id="sizegroup_defaults"/>
+  <object class="GtkSizeGroup" id="sizegroup_devices"/>
+</interface>
diff --git a/panels/color/gnome-color-panel.desktop.in.in b/panels/color/gnome-color-panel.desktop.in.in
new file mode 100644
index 0000000..7a8e8ec
--- /dev/null
+++ b/panels/color/gnome-color-panel.desktop.in.in
@@ -0,0 +1,17 @@
+[Desktop Entry]
+_Name=Color
+_Comment=Color management settings
+Exec=gnome-control-center color
+Icon=gnome-color-manager
+Terminal=false
+Type=Application
+StartupNotify=true
+Categories=GNOME;GTK;Settings;DesktopSettings;X-GNOME-Settings-Panel;HardwareSettings
+OnlyShowIn=GNOME;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-control-center
+X-GNOME-Bugzilla-Component=color
+X-GNOME-Bugzilla-Version= VERSION@
+X-GNOME-Settings-Panel=color
+# Translators: those are keywords for the color control-center panel
+_X-GNOME-Keywords=Color;ICC;Profile;Calibrate;Printer;Display;
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9d4c33e..da6fe10 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -53,6 +53,9 @@ panels/network/panel-common.c
 panels/power/gnome-power-panel.desktop.in.in
 panels/power/cc-power-panel.c
 [type: gettext/glade]panels/power/power.ui
+panels/color/gnome-color-panel.desktop.in.in
+panels/color/cc-color-panel.c
+[type: gettext/glade]panels/color/color.ui
 panels/screen/gnome-screen-panel.desktop.in.in
 [type: gettext/glade]panels/screen/screen.ui
 panels/sound/applet-main.c
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index 5923745..345d8ef 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -9,6 +9,7 @@ panels/info/gnome-info-panel.desktop.in
 panels/keyboard/gnome-keyboard-panel.desktop.in
 panels/media/gnome-media-panel.desktop.in
 panels/power/gnome-power-panel.desktop.in
+panels/color/gnome-color-panel.desktop.in
 panels/printers/gnome-printers-panel.desktop.in
 panels/region/gnome-region-panel.desktop.in
 panels/screen/gnome-screen-panel.desktop.in



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