summaryrefslogtreecommitdiffstats
path: root/panels/color
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:45:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:45:20 +0000
commitae1c76ff830d146d41e88d6fba724c0a54bce868 (patch)
tree3c354bec95af07be35fc71a4b738268496f1a1c4 /panels/color
parentInitial commit. (diff)
downloadgnome-control-center-ae1c76ff830d146d41e88d6fba724c0a54bce868.tar.xz
gnome-control-center-ae1c76ff830d146d41e88d6fba724c0a54bce868.zip
Adding upstream version 1:43.6.upstream/1%43.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'panels/color')
-rw-r--r--panels/color/cc-color-calibrate.c993
-rw-r--r--panels/color/cc-color-calibrate.h54
-rw-r--r--panels/color/cc-color-calibrate.ui89
-rw-r--r--panels/color/cc-color-cell-renderer-text.c132
-rw-r--r--panels/color/cc-color-cell-renderer-text.h32
-rw-r--r--panels/color/cc-color-common.c116
-rw-r--r--panels/color/cc-color-common.h32
-rw-r--r--panels/color/cc-color-device.c309
-rw-r--r--panels/color/cc-color-device.h38
-rw-r--r--panels/color/cc-color-panel.c2208
-rw-r--r--panels/color/cc-color-panel.h30
-rw-r--r--panels/color/cc-color-panel.ui707
-rw-r--r--panels/color/cc-color-profile.c475
-rw-r--r--panels/color/cc-color-profile.h43
-rw-r--r--panels/color/color.gresource.xml7
-rw-r--r--panels/color/gnome-color-panel.desktop.in.in20
-rw-r--r--panels/color/icons/meson.build4
-rw-r--r--panels/color/icons/scalable/org.gnome.Settings-color-symbolic.svg9
-rw-r--r--panels/color/meson.build56
19 files changed, 5354 insertions, 0 deletions
diff --git a/panels/color/cc-color-calibrate.c b/panels/color/cc-color-calibrate.c
new file mode 100644
index 0000000..dc9d0f0
--- /dev/null
+++ b/panels/color/cc-color-calibrate.c
@@ -0,0 +1,993 @@
+/* -*- 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 <colord-gtk.h>
+#include <gio/gunixfdlist.h>
+#include <glib/gi18n.h>
+#include <glib-object.h>
+#include <math.h>
+#include <colord-session/cd-session.h>
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include <gnome-rr/gnome-rr.h>
+
+#include "cc-color-calibrate.h"
+
+#define CALIBRATE_WINDOW_OPACITY 0.9
+
+struct _CcColorCalibrate
+{
+ GObject parent_instance;
+
+ CdDevice *device;
+ CdSensorCap device_kind;
+ CdSensor *sensor;
+ CdProfile *profile;
+ gchar *title;
+ GDBusProxy *proxy_helper;
+ GDBusProxy *proxy_inhibit;
+ GMainLoop *loop;
+ GnomeRROutput *output;
+ GnomeRRScreen *x11_screen;
+ GtkBuilder *builder;
+ GtkWindow *window;
+ GtkWidget *sample_widget;
+ guint gamma_size;
+ CdProfileQuality quality;
+ guint target_whitepoint; /* in Kelvin */
+ gdouble target_gamma;
+ gint inhibit_fd;
+ gint inhibit_cookie;
+ CdSessionError session_error_code;
+};
+
+#define CD_SESSION_ERROR cc_color_calibrate_error_quark()
+
+#define COLORD_SETTINGS_SCHEMA "org.freedesktop.ColorHelper"
+
+G_DEFINE_TYPE (CcColorCalibrate, cc_color_calibrate, G_TYPE_OBJECT)
+
+static GQuark
+cc_color_calibrate_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("CcColorCalibrateError");
+ return quark;
+}
+
+void
+cc_color_calibrate_set_kind (CcColorCalibrate *calibrate,
+ CdSensorCap kind)
+{
+ g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
+ calibrate->device_kind = kind;
+}
+
+void
+cc_color_calibrate_set_temperature (CcColorCalibrate *calibrate,
+ guint temperature)
+{
+ g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
+ g_return_if_fail (temperature < 10000);
+ calibrate->target_whitepoint = temperature;
+}
+
+void
+cc_color_calibrate_set_quality (CcColorCalibrate *calibrate,
+ CdProfileQuality quality)
+{
+ g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
+ calibrate->quality = quality;
+}
+
+CdProfileQuality
+cc_color_calibrate_get_quality (CcColorCalibrate *calibrate)
+{
+ g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), 0);
+ return calibrate->quality;
+}
+
+void
+cc_color_calibrate_set_device (CcColorCalibrate *calibrate,
+ CdDevice *device)
+{
+ g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
+ g_return_if_fail (CD_IS_DEVICE (device));
+ if (calibrate->device != NULL)
+ g_object_unref (calibrate->device);
+ calibrate->device = g_object_ref (device);
+}
+
+void
+cc_color_calibrate_set_sensor (CcColorCalibrate *calibrate,
+ CdSensor *sensor)
+{
+ g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
+ g_return_if_fail (CD_IS_SENSOR (sensor));
+ if (calibrate->sensor != NULL)
+ g_object_unref (calibrate->sensor);
+ calibrate->sensor = g_object_ref (sensor);
+}
+
+void
+cc_color_calibrate_set_title (CcColorCalibrate *calibrate,
+ const gchar *title)
+{
+ g_return_if_fail (CC_IS_COLOR_CALIBRATE (calibrate));
+ g_return_if_fail (title != NULL);
+ g_free (calibrate->title);
+ calibrate->title = g_strdup (title);
+}
+
+CdProfile *
+cc_color_calibrate_get_profile (CcColorCalibrate *calibrate)
+{
+ g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), NULL);
+ return calibrate->profile;
+}
+
+static guint
+_gnome_rr_output_get_gamma_size (GnomeRROutput *output)
+{
+ GnomeRRCrtc *crtc;
+ gint len = 0;
+
+ crtc = gnome_rr_output_get_crtc (output);
+ if (crtc == NULL)
+ return 0;
+ gnome_rr_crtc_get_gamma (crtc,
+ &len,
+ NULL, NULL, NULL);
+ return (guint) len;
+}
+
+static gboolean
+cc_color_calibrate_calib_setup_screen (CcColorCalibrate *calibrate,
+ const gchar *name,
+ GError **error)
+{
+ gboolean ret = TRUE;
+
+ /* get screen */
+ calibrate->x11_screen = gnome_rr_screen_new (gdk_display_get_default (), error);
+ if (calibrate->x11_screen == NULL)
+ {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* get the output */
+ calibrate->output = gnome_rr_screen_get_output_by_name (calibrate->x11_screen,
+ name);
+ if (calibrate->output == NULL)
+ {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_SESSION_ERROR,
+ CD_SESSION_ERROR_INTERNAL,
+ "failed to get output");
+ goto out;
+ }
+
+ /* create a lookup table */
+ calibrate->gamma_size = _gnome_rr_output_get_gamma_size (calibrate->output);
+ if (calibrate->gamma_size == 0)
+ {
+ ret = FALSE;
+ g_set_error_literal (error,
+ CD_SESSION_ERROR,
+ CD_SESSION_ERROR_INTERNAL,
+ "gamma size is zero");
+ }
+out:
+ return ret;
+}
+
+/**
+ * cc_color_calibrate_calib_set_output_gamma:
+ *
+ * Handle this here rather than in gnome-settings-daemon for two reasons:
+ *
+ * - We don't want to create a profile each time the video card gamma
+ * table is created, as that would mean ~15 DBus requests each time
+ * we get UpdateGamma from the session helper.
+ *
+ * - We only have 100ms to process the request before the next update
+ * could be scheduled.
+ **/
+static gboolean
+cc_color_calibrate_calib_set_output_gamma (CcColorCalibrate *calibrate,
+ GPtrArray *array,
+ GError **error)
+{
+ CdColorRGB *p1;
+ CdColorRGB *p2;
+ CdColorRGB result;
+ gdouble mix;
+ GnomeRRCrtc *crtc;
+ g_autofree guint16 *blue = NULL;
+ g_autofree guint16 *green = NULL;
+ g_autofree guint16 *red = NULL;
+ guint i;
+
+ /* no length? */
+ if (array->len == 0)
+ {
+ g_set_error_literal (error,
+ CD_SESSION_ERROR,
+ CD_SESSION_ERROR_INTERNAL,
+ "no data in the CLUT array");
+ return FALSE;
+ }
+
+ /* convert to a type X understands of the right size */
+ red = g_new (guint16, calibrate->gamma_size);
+ green = g_new (guint16, calibrate->gamma_size);
+ blue = g_new (guint16, calibrate->gamma_size);
+ cd_color_rgb_set (&result, 1.0, 1.0, 1.0);
+ for (i = 0; i < calibrate->gamma_size; i++)
+ {
+ mix = (gdouble) (array->len - 1) /
+ (gdouble) (calibrate->gamma_size - 1) *
+ (gdouble) i;
+ p1 = g_ptr_array_index (array, (guint) floor (mix));
+ p2 = g_ptr_array_index (array, (guint) ceil (mix));
+ cd_color_rgb_interpolate (p1,
+ p2,
+ mix - (gint) mix,
+ &result);
+ red[i] = result.R * 0xffff;
+ green[i] = result.G * 0xffff;
+ blue[i] = result.B * 0xffff;
+ }
+
+ /* send to LUT */
+ crtc = gnome_rr_output_get_crtc (calibrate->output);
+ if (crtc == NULL)
+ {
+ g_set_error (error,
+ CD_SESSION_ERROR,
+ CD_SESSION_ERROR_INTERNAL,
+ "failed to get ctrc for %s",
+ gnome_rr_output_get_name (calibrate->output));
+ return FALSE;
+ }
+ gnome_rr_crtc_set_gamma (crtc, calibrate->gamma_size,
+ red, green, blue);
+ return TRUE;
+}
+
+static void
+cc_color_calibrate_property_changed_cb (CcColorCalibrate *calibrate,
+ GVariant *changed_properties,
+ GStrv invalidated_properties)
+{
+ gboolean ret;
+ GtkWidget *widget;
+ guint value;
+
+ ret = g_variant_lookup (changed_properties,
+ "Progress",
+ "u", &value);
+ if (ret)
+ {
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "progressbar_status"));
+ gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (widget),
+ value / 100.0f);
+ }
+}
+
+static void
+cc_color_calibrate_interaction_required (CcColorCalibrate *calibrate,
+ CdSessionInteraction code,
+ const gchar *message,
+ const gchar *image_path)
+{
+ const gchar *message_transl;
+ gboolean show_button_start = FALSE;
+ GtkImage *img;
+ GtkLabel *label;
+ GtkWidget *widget;
+
+ /* the client helper does not ship an icon for this */
+ if (code == CD_SESSION_INTERACTION_SHUT_LAPTOP_LID)
+ image_path = "preferences-color-symbolic";
+
+ /* set image */
+ img = GTK_IMAGE (gtk_builder_get_object (calibrate->builder,
+ "image_status"));
+ if (image_path != NULL && image_path[0] != '\0')
+ {
+ g_autoptr(GdkPixbuf) pixbuf = NULL;
+
+ g_debug ("showing image %s", image_path);
+ pixbuf = gdk_pixbuf_new_from_file_at_size (image_path,
+ 400, 400,
+ NULL);
+ if (pixbuf != NULL)
+ gtk_image_set_from_pixbuf (img, pixbuf);
+ gtk_widget_set_visible (GTK_WIDGET (img), TRUE);
+ gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), FALSE);
+ }
+ else
+ {
+ g_debug ("hiding image");
+ gtk_widget_set_visible (GTK_WIDGET (img), FALSE);
+ gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), TRUE);
+ }
+
+ /* set new status */
+ switch (code)
+ {
+ case CD_SESSION_INTERACTION_ATTACH_TO_SCREEN:
+ show_button_start = TRUE;
+ /* TRANSLATORS: The user has to attach the sensor to the screen */
+ message_transl = _("Place your calibration device over the square and press “Start”");
+ break;
+ case CD_SESSION_INTERACTION_MOVE_TO_CALIBRATION:
+ /* TRANSLATORS: Some calibration devices need the user to move a
+ * dial or switch manually. We also show a picture showing them
+ * what to do... */
+ message_transl = _("Move your calibration device to the calibrate position and press “Continue”");
+ break;
+ case CD_SESSION_INTERACTION_MOVE_TO_SURFACE:
+ /* TRANSLATORS: Some calibration devices need the user to move a
+ * dial or switch manually. We also show a picture showing them
+ * what to do... */
+ message_transl = _("Move your calibration device to the surface position and press “Continue”");
+ break;
+ case CD_SESSION_INTERACTION_SHUT_LAPTOP_LID:
+ /* TRANSLATORS: on some hardware e.g. Lenovo W700 the sensor
+ * is built into the palmrest and we need to fullscreen the
+ * sample widget and shut the lid. */
+ message_transl = _("Shut the laptop lid");
+ break;
+ default:
+ message_transl = message;
+ break;
+ }
+ label = GTK_LABEL (gtk_builder_get_object (calibrate->builder,
+ "label_status"));
+ gtk_label_set_label (label, message_transl);
+
+ /* show the correct button */
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_start"));
+ gtk_widget_set_visible (widget, show_button_start);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_resume"));
+ gtk_widget_set_visible (widget, !show_button_start);
+}
+
+static const gchar *
+cc_color_calibrate_get_error_translation (CdSessionError code)
+{
+ const gchar *str = NULL;
+ switch (code)
+ {
+ case CD_SESSION_ERROR_FAILED_TO_FIND_DEVICE:
+ case CD_SESSION_ERROR_FAILED_TO_FIND_SENSOR:
+ case CD_SESSION_ERROR_INTERNAL:
+ case CD_SESSION_ERROR_INVALID_VALUE:
+ /* TRANSLATORS: We suck, the calibration failed and we have no
+ * good idea why or any suggestions */
+ str = _("An internal error occurred that could not be recovered.");
+ break;
+ case CD_SESSION_ERROR_FAILED_TO_FIND_TOOL:
+ /* TRANSLATORS: Some required-at-runtime tools were not
+ * installed, which should only affect insane distros */
+ str = _("Tools required for calibration are not installed.");
+ break;
+ case CD_SESSION_ERROR_FAILED_TO_GENERATE_PROFILE:
+ case CD_SESSION_ERROR_FAILED_TO_OPEN_PROFILE:
+ case CD_SESSION_ERROR_FAILED_TO_SAVE_PROFILE:
+ /* TRANSLATORS: The profile failed for some reason */
+ str = _("The profile could not be generated.");
+ break;
+ case CD_SESSION_ERROR_FAILED_TO_GET_WHITEPOINT:
+ /* TRANSLATORS: The user specified a whitepoint that was
+ * unobtainable with the hardware they've got -- see
+ * https://en.wikipedia.org/wiki/White_point for details */
+ str = _("The target whitepoint was not obtainable.");
+ break;
+ default:
+ break;
+ }
+ return str;
+}
+
+static void
+cc_color_calibrate_finished (CcColorCalibrate *calibrate,
+ CdSessionError code,
+ const gchar *error_fallback)
+{
+ GtkWidget *widget;
+ g_autoptr(GString) str = NULL;
+ const gchar *tmp;
+
+ /* save failure so we can get this after we've quit the loop */
+ calibrate->session_error_code = code;
+
+ /* show correct buttons */
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_cancel"));
+ gtk_widget_set_visible (widget, FALSE);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_start"));
+ gtk_widget_set_visible (widget, FALSE);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_resume"));
+ gtk_widget_set_visible (widget, FALSE);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_done"));
+ gtk_widget_set_visible (widget, TRUE);
+
+ str = g_string_new ("");
+ if (code == CD_SESSION_ERROR_NONE)
+ {
+ g_debug ("calibration succeeded");
+ /* TRANSLATORS: the display calibration process is finished */
+ g_string_append (str, _("Complete!"));
+ }
+ else
+ {
+ g_warning ("calibration failed with code %i: %s",
+ code, error_fallback);
+ /* TRANSLATORS: the display calibration failed, and we also show
+ * the translated (or untranslated) error string after this */
+ g_string_append (str, _("Calibration failed!"));
+ g_string_append (str, "\n\n");
+ tmp = cc_color_calibrate_get_error_translation (code);
+ g_string_append (str, tmp != NULL ? tmp : error_fallback);
+ }
+ g_string_append (str, "\n");
+ /* TRANSLATORS: The user can now remove the sensor from the screen */
+ g_string_append (str, _("You can remove the calibration device."));
+
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "label_status"));
+ gtk_label_set_label (GTK_LABEL (widget), str->str);
+}
+
+static void
+cc_color_calibrate_signal_cb (CcColorCalibrate *calibrate,
+ const gchar *sender_name,
+ const gchar *signal_name,
+ GVariant *parameters)
+{
+ CdColorRGB color;
+ CdColorRGB *color_tmp;
+ const gchar *image = NULL;
+ const gchar *message;
+ const gchar *profile_path = NULL;
+ const gchar *str = NULL;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ GPtrArray *array = NULL;
+ GtkImage *img;
+ GtkLabel *label;
+ g_autoptr(GVariant) dict = NULL;
+
+ if (g_strcmp0 (signal_name, "Finished") == 0)
+ {
+ CdSessionError error_code;
+
+ g_variant_get (parameters, "(u@a{sv})",
+ &error_code,
+ &dict);
+ g_variant_lookup (dict, "ErrorDetails", "&s", &str);
+ ret = g_variant_lookup (dict, "ProfilePath", "&s", &profile_path);
+ if (ret)
+ calibrate->profile = cd_profile_new_with_object_path (profile_path);
+ cc_color_calibrate_finished (calibrate, error_code, str);
+ return;
+ }
+ if (g_strcmp0 (signal_name, "UpdateSample") == 0)
+ {
+ g_variant_get (parameters, "(ddd)",
+ &color.R,
+ &color.G,
+ &color.B);
+ img = GTK_IMAGE (gtk_builder_get_object (calibrate->builder,
+ "image_status"));
+ gtk_widget_set_visible (GTK_WIDGET (img), FALSE);
+ gtk_widget_set_visible (GTK_WIDGET (calibrate->sample_widget), TRUE);
+ cd_sample_widget_set_color (CD_SAMPLE_WIDGET (calibrate->sample_widget),
+ &color);
+
+ /* for Lenovo W700 and W520 laptops we almost fullscreen the
+ * sample widget as the device is actually embedded in the
+ * palmrest! */
+ if (cd_sensor_get_embedded (calibrate->sensor))
+ {
+ g_debug ("Making sample window larger for embedded sensor");
+ gtk_widget_set_size_request (calibrate->sample_widget, 1000, 600);
+ }
+
+ /* set the generic label too */
+ label = GTK_LABEL (gtk_builder_get_object (calibrate->builder,
+ "label_status"));
+ /* TRANSLATORS: The user has to be careful not to knock the
+ * display off the screen (although we do cope if this is
+ * detected early enough) */
+ gtk_label_set_label (label, _("Do not disturb the calibration device while in progress"));
+ return;
+ }
+ if (g_strcmp0 (signal_name, "InteractionRequired") == 0)
+ {
+ CdSessionInteraction code;
+
+ g_variant_get (parameters, "(u&s&s)",
+ &code,
+ &message,
+ &image);
+ g_debug ("Interaction required type %i: %s",
+ code, message);
+ cc_color_calibrate_interaction_required (calibrate,
+ code,
+ message,
+ image);
+ return;
+ }
+ if (g_strcmp0 (signal_name, "UpdateGamma") == 0)
+ {
+ g_autoptr(GVariantIter) iter = NULL;
+
+ g_variant_get (parameters,
+ "(a(ddd))",
+ &iter);
+ array = g_ptr_array_new_with_free_func (g_free);
+ while (g_variant_iter_loop (iter, "(ddd)",
+ &color.R,
+ &color.G,
+ &color.B))
+ {
+ color_tmp = cd_color_rgb_new ();
+ cd_color_rgb_copy (&color, color_tmp);
+ g_ptr_array_add (array, color_tmp);
+ }
+ ret = cc_color_calibrate_calib_set_output_gamma (calibrate,
+ array,
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to update gamma: %s",
+ error->message);
+ return;
+ }
+ return;
+ }
+ g_warning ("got unknown signal %s", signal_name);
+}
+
+static void
+cc_color_calibrate_cancel (CcColorCalibrate *calibrate)
+{
+ g_autoptr(GVariant) retval = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* cancel the calibration to ensure the helper quits */
+ retval = g_dbus_proxy_call_sync (calibrate->proxy_helper,
+ "Cancel",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (retval == NULL)
+ g_warning ("Failed to send Cancel: %s", error->message);
+
+ /* return */
+ g_main_loop_quit (calibrate->loop);
+}
+
+static void
+cc_color_calibrate_button_done_cb (CcColorCalibrate *calibrate)
+{
+ g_main_loop_quit (calibrate->loop);
+}
+
+static void
+cc_color_calibrate_button_start_cb (CcColorCalibrate *calibrate)
+{
+ GtkWidget *widget;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GVariant) retval = NULL;
+
+ /* set correct buttons */
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_start"));
+ gtk_widget_set_visible (widget, FALSE);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_resume"));
+ gtk_widget_set_visible (widget, FALSE);
+
+ /* continue */
+ retval = g_dbus_proxy_call_sync (calibrate->proxy_helper,
+ "Resume",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+ if (retval == NULL)
+ g_warning ("Failed to send Resume: %s", error->message);
+}
+
+static void
+cc_color_calibrate_button_cancel_cb (CcColorCalibrate *calibrate)
+{
+ cc_color_calibrate_cancel (calibrate);
+}
+
+#if 0
+static gboolean
+cc_color_calibrate_alpha_window_draw (CcColorCalibrate *calibrate, cairo_t *cr)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "dialog_calibrate"));
+
+ if (gdk_screen_get_rgba_visual (gtk_widget_get_screen (widget)) &&
+ gdk_screen_is_composited (gtk_widget_get_screen (widget)))
+ {
+ /* transparent */
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, CALIBRATE_WINDOW_OPACITY);
+ }
+ else
+ {
+ /* opaque black */
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
+ }
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ return FALSE;
+}
+
+static void
+cc_color_calibrate_alpha_screen_changed_cb (CcColorCalibrate *calibrate)
+{
+ GtkWidget *window;
+ GdkScreen *screen;
+ GdkVisual *visual;
+
+ window = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "dialog_calibrate"));
+ screen = gtk_widget_get_screen (GTK_WIDGET (window));
+ visual = gdk_screen_get_rgba_visual (screen);
+ if (visual == NULL)
+ visual = gdk_screen_get_system_visual (screen);
+ gtk_widget_set_visual (GTK_WIDGET (window), visual);
+}
+#endif
+
+static void
+cc_color_calibrate_uninhibit (CcColorCalibrate *calibrate)
+{
+ GtkApplication *application;
+
+ if (calibrate->inhibit_fd != -1)
+ {
+ close (calibrate->inhibit_fd);
+ calibrate->inhibit_fd = -1;
+ }
+
+ if (calibrate->inhibit_cookie != 0)
+ {
+ application = GTK_APPLICATION (g_application_get_default ());
+ gtk_application_uninhibit (application, calibrate->inhibit_cookie);
+ calibrate->inhibit_cookie = 0;
+ }
+}
+
+static void
+cc_color_calibrate_inhibit (CcColorCalibrate *calibrate, GtkWindow *window)
+{
+ g_autoptr(GError) error = NULL;
+ gint idx;
+ g_autoptr(GUnixFDList) fd_list = NULL;
+ g_autoptr(GVariant) retval = NULL;
+ GtkApplication *application;
+
+ /* inhibit basically everything we can */
+ application = GTK_APPLICATION (g_application_get_default ());
+ calibrate->inhibit_cookie = gtk_application_inhibit (application,
+ window,
+ GTK_APPLICATION_INHIBIT_LOGOUT |
+ GTK_APPLICATION_INHIBIT_SWITCH |
+ GTK_APPLICATION_INHIBIT_SUSPEND |
+ GTK_APPLICATION_INHIBIT_IDLE,
+ "Display calibration in progress");
+
+ /* tell logind to disallow the lid switch */
+ retval = g_dbus_proxy_call_with_unix_fd_list_sync (calibrate->proxy_inhibit,
+ "Inhibit",
+ g_variant_new ("(ssss)",
+ "shutdown:"
+ "sleep:"
+ "idle:"
+ "handle-lid-switch",
+ "Display Calibrator",
+ "Display calibration in progress",
+ "block"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &fd_list,
+ NULL,
+ &error);
+ if (retval == NULL)
+ {
+ g_warning ("Failed to send Inhibit: %s", error->message);
+ return;
+ }
+ g_variant_get (retval, "(h)", &idx);
+ calibrate->inhibit_fd = g_unix_fd_list_get (fd_list, idx, &error);
+ if (calibrate->inhibit_fd == -1)
+ {
+ g_warning ("Failed to receive system inhibitor fd: %s", error->message);
+ return;
+ }
+ g_debug ("System inhibitor fd is %d", calibrate->inhibit_fd);
+}
+
+gboolean
+cc_color_calibrate_setup (CcColorCalibrate *calibrate,
+ GError **error)
+{
+ gboolean ret = TRUE;
+
+ g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), FALSE);
+ g_return_val_if_fail (calibrate->device_kind != CD_SENSOR_CAP_UNKNOWN, FALSE);
+
+ /* use logind to disable system state idle */
+ calibrate->proxy_inhibit = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.login1",
+ "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager",
+ NULL,
+ error);
+ if (calibrate->proxy_inhibit == NULL)
+ {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* start the calibration session daemon */
+ calibrate->proxy_helper = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ CD_SESSION_DBUS_SERVICE,
+ CD_SESSION_DBUS_PATH,
+ CD_SESSION_DBUS_INTERFACE_DISPLAY,
+ NULL,
+ error);
+ if (calibrate->proxy_helper == NULL)
+ {
+ ret = FALSE;
+ goto out;
+ }
+ g_signal_connect_object (calibrate->proxy_helper,
+ "g-properties-changed",
+ G_CALLBACK (cc_color_calibrate_property_changed_cb),
+ calibrate, G_CONNECT_SWAPPED);
+ g_signal_connect_object (calibrate->proxy_helper,
+ "g-signal",
+ G_CALLBACK (cc_color_calibrate_signal_cb),
+ calibrate, G_CONNECT_SWAPPED);
+out:
+ return ret;
+}
+
+gboolean
+cc_color_calibrate_start (CcColorCalibrate *calibrate,
+ GtkWindow *parent,
+ GError **error)
+{
+ const gchar *name;
+ GtkWidget *widget;
+ GtkWindow *window;
+ GVariantBuilder builder;
+ g_autoptr(GVariant) retval = NULL;
+
+ g_return_val_if_fail (CC_IS_COLOR_CALIBRATE (calibrate), FALSE);
+
+ /* get screen */
+ name = cd_device_get_metadata_item (calibrate->device,
+ CD_DEVICE_METADATA_XRANDR_NAME);
+ if (!cc_color_calibrate_calib_setup_screen (calibrate, name, error))
+ return FALSE;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
+ g_variant_builder_add (&builder,
+ "{sv}",
+ "Quality",
+ g_variant_new_uint32 (calibrate->quality));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ "Whitepoint",
+ g_variant_new_uint32 (calibrate->target_whitepoint));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ "Gamma",
+ g_variant_new_double (calibrate->target_gamma));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ "Title",
+ g_variant_new_string (calibrate->title));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ "DeviceKind",
+ g_variant_new_uint32 (calibrate->device_kind));
+ retval = g_dbus_proxy_call_sync (calibrate->proxy_helper,
+ "Start",
+ g_variant_new ("(ssa{sv})",
+ cd_device_get_id (calibrate->device),
+ cd_sensor_get_id (calibrate->sensor),
+ &builder),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ error);
+ if (retval == NULL)
+ return FALSE;
+
+ /* set this above our parent */
+ window = GTK_WINDOW (gtk_builder_get_object (calibrate->builder,
+ "dialog_calibrate"));
+ gtk_window_set_modal (window, TRUE);
+ gtk_widget_show (GTK_WIDGET (window));
+
+ /* show correct buttons */
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_cancel"));
+ gtk_widget_set_visible (widget, TRUE);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_start"));
+ gtk_widget_set_visible (widget, TRUE);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_resume"));
+ gtk_widget_set_visible (widget, FALSE);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_done"));
+ gtk_widget_set_visible (widget, FALSE);
+
+ /* stop the computer from auto-suspending or turning off the screen */
+ cc_color_calibrate_inhibit (calibrate, parent);
+
+ g_main_loop_run (calibrate->loop);
+ gtk_widget_hide (GTK_WIDGET (window));
+
+ /* we can go idle now */
+ cc_color_calibrate_uninhibit (calibrate);
+
+ /* see if we failed */
+ if (calibrate->session_error_code != CD_SESSION_ERROR_NONE)
+ {
+ g_set_error_literal (error,
+ CD_SESSION_ERROR,
+ CD_SESSION_ERROR_INTERNAL,
+ "failed to calibrate");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+cc_color_calibrate_finalize (GObject *object)
+{
+ CcColorCalibrate *calibrate = CC_COLOR_CALIBRATE (object);
+
+ g_clear_pointer (&calibrate->window, gtk_window_destroy);
+ g_clear_object (&calibrate->builder);
+ g_clear_object (&calibrate->device);
+ g_clear_object (&calibrate->proxy_helper);
+ g_clear_object (&calibrate->proxy_inhibit);
+ g_clear_object (&calibrate->sensor);
+ g_clear_object (&calibrate->x11_screen);
+ g_free (calibrate->title);
+ g_main_loop_unref (calibrate->loop);
+
+ G_OBJECT_CLASS (cc_color_calibrate_parent_class)->finalize (object);
+}
+
+static void
+cc_color_calibrate_class_init (CcColorCalibrateClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = cc_color_calibrate_finalize;
+}
+
+static void
+cc_color_calibrate_init (CcColorCalibrate *calibrate)
+{
+ g_autoptr(GError) error = NULL;
+ gint retval;
+ g_autoptr(GSettings) settings = NULL;
+ GtkBox *box;
+ GtkWidget *widget;
+ GtkWindow *window;
+
+ calibrate->loop = g_main_loop_new (NULL, FALSE);
+ calibrate->inhibit_fd = -1;
+
+ /* load UI */
+ calibrate->builder = gtk_builder_new ();
+ retval = gtk_builder_add_from_resource (calibrate->builder,
+ "/org/gnome/control-center/color/cc-color-calibrate.ui",
+ &error);
+ if (retval == 0)
+ g_warning ("Could not load interface: %s", error->message);
+
+ /* add sample widget */
+ box = GTK_BOX (gtk_builder_get_object (calibrate->builder,
+ "vbox_status"));
+ calibrate->sample_widget = cd_sample_widget_new ();
+ gtk_widget_set_size_request (calibrate->sample_widget, 400, 400);
+ gtk_box_prepend (box, calibrate->sample_widget);
+ gtk_widget_set_vexpand (calibrate->sample_widget, FALSE);
+ gtk_widget_set_hexpand (calibrate->sample_widget, FALSE);
+
+ /* get defaults */
+ settings = g_settings_new (COLORD_SETTINGS_SCHEMA);
+ calibrate->target_whitepoint = g_settings_get_int (settings, "display-whitepoint");
+ calibrate->target_gamma = g_settings_get_double (settings, "display-gamma");
+
+ /* connect to buttons */
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_start"));
+ g_signal_connect_object (widget, "clicked",
+ G_CALLBACK (cc_color_calibrate_button_start_cb), calibrate, G_CONNECT_SWAPPED);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_resume"));
+ g_signal_connect_object (widget, "clicked",
+ G_CALLBACK (cc_color_calibrate_button_start_cb), calibrate, G_CONNECT_SWAPPED);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_done"));
+ g_signal_connect_object (widget, "clicked",
+ G_CALLBACK (cc_color_calibrate_button_done_cb), calibrate, G_CONNECT_SWAPPED);
+ widget = GTK_WIDGET (gtk_builder_get_object (calibrate->builder,
+ "button_cancel"));
+ g_signal_connect_object (widget, "clicked",
+ G_CALLBACK (cc_color_calibrate_button_cancel_cb), calibrate, G_CONNECT_SWAPPED);
+ gtk_widget_show (widget);
+
+ /* setup the specialist calibration window */
+ window = GTK_WINDOW (gtk_builder_get_object (calibrate->builder,
+ "dialog_calibrate"));
+ calibrate->window = window;
+}
+
+CcColorCalibrate *
+cc_color_calibrate_new (void)
+{
+ CcColorCalibrate *calibrate;
+ calibrate = g_object_new (CC_TYPE_COLOR_CALIBRATE, NULL);
+ return CC_COLOR_CALIBRATE (calibrate);
+}
diff --git a/panels/color/cc-color-calibrate.h b/panels/color/cc-color-calibrate.h
new file mode 100644
index 0000000..15ea06d
--- /dev/null
+++ b/panels/color/cc-color-calibrate.h
@@ -0,0 +1,54 @@
+/* -*- 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.
+ */
+
+#pragma once
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include <colord.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_COLOR_CALIBRATE (cc_color_calibrate_get_type ())
+G_DECLARE_FINAL_TYPE (CcColorCalibrate, cc_color_calibrate, CC, COLOR_CALIBRATE, GObject)
+
+CcColorCalibrate *cc_color_calibrate_new (void);
+void cc_color_calibrate_set_kind (CcColorCalibrate *calibrate,
+ CdSensorCap kind);
+void cc_color_calibrate_set_temperature (CcColorCalibrate *calibrate,
+ guint temperature);
+void cc_color_calibrate_set_quality (CcColorCalibrate *calibrate,
+ CdProfileQuality quality);
+CdProfileQuality cc_color_calibrate_get_quality (CcColorCalibrate *calibrate);
+void cc_color_calibrate_set_device (CcColorCalibrate *calibrate,
+ CdDevice *device);
+void cc_color_calibrate_set_sensor (CcColorCalibrate *calibrate,
+ CdSensor *sensor);
+void cc_color_calibrate_set_title (CcColorCalibrate *calibrate,
+ const gchar *title);
+gboolean cc_color_calibrate_start (CcColorCalibrate *calibrate,
+ GtkWindow *parent,
+ GError **error);
+gboolean cc_color_calibrate_setup (CcColorCalibrate *calibrate,
+ GError **error);
+CdProfile *cc_color_calibrate_get_profile (CcColorCalibrate *calibrate);
+
+G_END_DECLS
diff --git a/panels/color/cc-color-calibrate.ui b/panels/color/cc-color-calibrate.ui
new file mode 100644
index 0000000..44f67e7
--- /dev/null
+++ b/panels/color/cc-color-calibrate.ui
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="dialog_calibrate">
+ <property name="margin_top">32</property>
+ <property name="margin_bottom">32</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <property name="title" translatable="yes">Display Calibration</property>
+ <property name="deletable">False</property>
+ <style>
+ <class name="osd"/>
+ </style>
+ <child>
+ <object class="GtkBox" id="dialog-vbox4">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkBox" id="dialog-action_area1">
+ <child>
+ <object class="GtkButton" id="button_cancel">
+ <property name="label" translatable="yes">_Cancel</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_start">
+ <property name="label" translatable="yes" comments="This starts the calibration process">_Start</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_resume">
+ <property name="label" translatable="yes" comments="This resumes the calibration process">_Resume</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_done">
+ <property name="label" translatable="yes" comments="This button returns the user back to the color control panel">_Done</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="vbox_status">
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">15</property>
+ <child>
+ <object class="GtkImage" id="image_status">
+ <property name="valign">end</property>
+ <property name="pixel_size">192</property>
+ <property name="icon_name">address-book-new</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_status">
+ <property name="valign">start</property>
+ <property name="label">Do not disturb the calibration device while in progress</property>
+ <property name="justify">center</property>
+ <property name="wrap">True</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="foreground" value="#ffffffffffff"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkProgressBar" id="progressbar_status">
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">button_cancel</action-widget>
+ <action-widget response="0">button_start</action-widget>
+ <action-widget response="0">button_resume</action-widget>
+ <action-widget response="0">button_done</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup_sample"/>
+</interface>
diff --git a/panels/color/cc-color-cell-renderer-text.c b/panels/color/cc-color-cell-renderer-text.c
new file mode 100644
index 0000000..5803ac5
--- /dev/null
+++ b/panels/color/cc-color-cell-renderer-text.c
@@ -0,0 +1,132 @@
+/* -*- 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 <gtk/gtk.h>
+
+#include "cc-color-cell-renderer-text.h"
+
+enum {
+ PROP_0,
+ PROP_IS_DIM_LABEL,
+ PROP_LAST
+};
+
+struct _CcColorCellRendererText
+{
+ GtkCellRendererText parent_instance;
+
+ gboolean is_dim_label;
+};
+
+G_DEFINE_TYPE (CcColorCellRendererText, cc_color_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT)
+
+static gpointer parent_class = NULL;
+
+static void
+cc_color_cell_renderer_text_get_property (GObject *object, guint param_id,
+ GValue *value, GParamSpec *pspec)
+{
+ CcColorCellRendererText *renderer = CC_COLOR_CELL_RENDERER_TEXT (object);
+
+ switch (param_id)
+ {
+ case PROP_IS_DIM_LABEL:
+ g_value_set_boolean (value, renderer->is_dim_label);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_color_cell_renderer_text_set_property (GObject *object, guint param_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ CcColorCellRendererText *renderer = CC_COLOR_CELL_RENDERER_TEXT (object);
+
+ switch (param_id)
+ {
+ case PROP_IS_DIM_LABEL:
+ renderer->is_dim_label = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+cc_color_cell_renderer_snapshot (GtkCellRenderer *cell,
+ GtkSnapshot *snapshot,
+ GtkWidget *widget,
+ const GdkRectangle *background_area,
+ const GdkRectangle *cell_area,
+ GtkCellRendererState flags)
+{
+ CcColorCellRendererText *renderer;
+ GtkStyleContext *context;
+
+ renderer = CC_COLOR_CELL_RENDERER_TEXT (cell);
+ context = gtk_widget_get_style_context (widget);
+ gtk_style_context_save (context);
+ if (renderer->is_dim_label)
+ gtk_style_context_add_class (context, "dim-label");
+ else
+ gtk_style_context_remove_class (context, "dim-label");
+ GTK_CELL_RENDERER_CLASS (parent_class)->snapshot (cell, snapshot, widget,
+ background_area,
+ cell_area, flags);
+ gtk_style_context_restore (context);
+}
+
+static void
+cc_color_cell_renderer_text_class_init (CcColorCellRendererTextClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkCellRendererClass *object_class_gcr = GTK_CELL_RENDERER_CLASS (class);
+ object_class_gcr->snapshot = cc_color_cell_renderer_snapshot;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ object_class->get_property = cc_color_cell_renderer_text_get_property;
+ object_class->set_property = cc_color_cell_renderer_text_set_property;
+
+ g_object_class_install_property (object_class, PROP_IS_DIM_LABEL,
+ g_param_spec_boolean ("is-dim-label",
+ NULL, NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+}
+
+static void
+cc_color_cell_renderer_text_init (CcColorCellRendererText *renderer)
+{
+ renderer->is_dim_label = FALSE;
+}
+
+GtkCellRenderer *
+cc_color_cell_renderer_text_new (void)
+{
+ return g_object_new (CC_COLOR_TYPE_CELL_RENDERER_TEXT, NULL);
+}
diff --git a/panels/color/cc-color-cell-renderer-text.h b/panels/color/cc-color-cell-renderer-text.h
new file mode 100644
index 0000000..241663a
--- /dev/null
+++ b/panels/color/cc-color-cell-renderer-text.h
@@ -0,0 +1,32 @@
+/* -*- 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.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define CC_COLOR_TYPE_CELL_RENDERER_TEXT (cc_color_cell_renderer_text_get_type ())
+G_DECLARE_FINAL_TYPE (CcColorCellRendererText, cc_color_cell_renderer_text, CC_COLOR, CELL_RENDERER_TEXT, GtkCellRendererText)
+
+GtkCellRenderer *cc_color_cell_renderer_text_new (void);
+
+G_END_DECLS
+
diff --git a/panels/color/cc-color-common.c b/panels/color/cc-color-common.c
new file mode 100644
index 0000000..cf269c2
--- /dev/null
+++ b/panels/color/cc-color-common.c
@@ -0,0 +1,116 @@
+/* -*- 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)
+{
+ g_autofree gchar *title = cc_color_device_get_title (device);
+ return g_strdup_printf ("%s-%s-%s",
+ cc_color_device_kind_to_sort (device),
+ cd_device_get_id (device),
+ title);
+}
diff --git a/panels/color/cc-color-common.h b/panels/color/cc-color-common.h
new file mode 100644
index 0000000..c90193e
--- /dev/null
+++ b/panels/color/cc-color-common.h
@@ -0,0 +1,32 @@
+/* -*- 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.
+ */
+
+#pragma once
+
+#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
+
diff --git a/panels/color/cc-color-device.c b/panels/color/cc-color-device.c
new file mode 100644
index 0000000..fbb74d8
--- /dev/null
+++ b/panels/color/cc-color-device.c
@@ -0,0 +1,309 @@
+/* -*- 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 _CcColorDevice
+{
+ GtkListBoxRow parent_instance;
+
+ CdDevice *device;
+ gboolean expanded;
+ gchar *sortable;
+ GtkWidget *widget_description;
+ GtkWidget *widget_button;
+ GtkWidget *widget_switch;
+ GtkWidget *widget_arrow;
+ GtkWidget *widget_nocalib;
+};
+
+G_DEFINE_TYPE (CcColorDevice, cc_color_device, GTK_TYPE_LIST_BOX_ROW)
+
+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)
+{
+ g_autofree gchar *title = NULL;
+ g_autoptr(GPtrArray) profiles = NULL;
+ g_autofree gchar *name1 = NULL;
+ g_autofree gchar *name2 = NULL;
+
+ /* add switch and expander if there are profiles, otherwise use a label */
+ profiles = cd_device_get_profiles (color_device->device);
+ if (profiles == NULL)
+ return;
+
+ title = cc_color_device_get_title (color_device->device);
+ gtk_label_set_label (GTK_LABEL (color_device->widget_description), title);
+ gtk_widget_set_visible (color_device->widget_description, TRUE);
+
+ gtk_widget_set_visible (color_device->widget_switch, profiles->len > 0);
+ gtk_widget_set_visible (color_device->widget_button, profiles->len > 0);
+ gtk_image_set_from_icon_name (GTK_IMAGE (color_device->widget_arrow),
+ color_device->expanded ? "pan-down-symbolic" : "pan-end-symbolic");
+ gtk_widget_set_visible (color_device->widget_nocalib, profiles->len == 0);
+ gtk_widget_set_sensitive (color_device->widget_button, cd_device_get_enabled (color_device->device));
+ gtk_switch_set_active (GTK_SWITCH (color_device->widget_switch),
+ cd_device_get_enabled (color_device->device));
+
+ name1 = g_strdup_printf (_("Enable color management for %s"), title);
+ gtk_accessible_update_property (GTK_ACCESSIBLE (color_device->widget_switch),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, name1,
+ -1);
+
+ name2 = g_strdup_printf (_("Show color profiles for %s"), title);
+ gtk_accessible_update_property (GTK_ACCESSIBLE (color_device->widget_button),
+ GTK_ACCESSIBLE_PROPERTY_LABEL, name2,
+ -1);
+}
+
+CdDevice *
+cc_color_device_get_device (CcColorDevice *color_device)
+{
+ g_return_val_if_fail (CC_IS_COLOR_DEVICE (color_device), NULL);
+ return color_device->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->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->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->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);
+
+ g_free (color_device->sortable);
+ g_object_unref (color_device->device);
+
+ G_OBJECT_CLASS (cc_color_device_parent_class)->finalize (object);
+}
+
+void
+cc_color_device_set_expanded (CcColorDevice *color_device,
+ gboolean expanded)
+{
+ /* same as before */
+ if (color_device->expanded == expanded)
+ return;
+
+ /* refresh */
+ color_device->expanded = expanded;
+ g_signal_emit (color_device,
+ signals[SIGNAL_EXPANDED_CHANGED], 0,
+ color_device->expanded);
+ cc_color_device_refresh (color_device);
+}
+
+static void
+cc_color_device_notify_enable_device_cb (CcColorDevice *color_device)
+{
+ gboolean enable;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+
+ enable = gtk_switch_get_active (GTK_SWITCH (color_device->widget_switch));
+ g_debug ("Set %s to %i", cd_device_get_id (color_device->device), enable);
+ ret = cd_device_set_enabled_sync (color_device->device,
+ enable, NULL, &error);
+ if (!ret)
+ g_warning ("failed to %s to the device: %s",
+ enable ? "enable" : "disable", error->message);
+
+ /* if expanded, close */
+ cc_color_device_set_expanded (color_device, FALSE);
+}
+
+static void
+cc_color_device_changed_cb (CcColorDevice *color_device)
+{
+ cc_color_device_refresh (color_device);
+}
+
+static void
+cc_color_device_constructed (GObject *object)
+{
+ CcColorDevice *color_device = CC_COLOR_DEVICE (object);
+ g_autofree gchar *sortable_tmp = NULL;
+
+ /* watch the device for changes */
+ g_signal_connect_object (color_device->device, "changed",
+ G_CALLBACK (cc_color_device_changed_cb), color_device, G_CONNECT_SWAPPED);
+
+ /* 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 (color_device->device);
+ color_device->sortable = g_strdup_printf ("%sXX", sortable_tmp);
+
+ cc_color_device_refresh (color_device);
+
+ /* watch to see if the user flicked the switch */
+ g_signal_connect_object (color_device->widget_switch, "notify::active",
+ G_CALLBACK (cc_color_device_notify_enable_device_cb),
+ color_device, G_CONNECT_SWAPPED);
+}
+
+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,
+ 0,
+ NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+}
+
+static void
+cc_color_device_clicked_expander_cb (CcColorDevice *color_device)
+{
+ color_device->expanded = !color_device->expanded;
+ cc_color_device_refresh (color_device);
+ g_signal_emit (color_device, signals[SIGNAL_EXPANDED_CHANGED], 0,
+ color_device->expanded);
+}
+
+static void
+cc_color_device_init (CcColorDevice *color_device)
+{
+ GtkStyleContext *context;
+ GtkWidget *box;
+
+ /* description */
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 9);
+ color_device->widget_description = gtk_label_new ("");
+ gtk_widget_set_margin_start (color_device->widget_description, 20);
+ gtk_widget_set_margin_top (color_device->widget_description, 12);
+ gtk_widget_set_margin_bottom (color_device->widget_description, 12);
+ gtk_widget_set_halign (color_device->widget_description, GTK_ALIGN_START);
+ gtk_widget_set_hexpand (color_device->widget_description, TRUE);
+ gtk_label_set_ellipsize (GTK_LABEL (color_device->widget_description), PANGO_ELLIPSIZE_END);
+ gtk_label_set_xalign (GTK_LABEL (color_device->widget_description), 0);
+ gtk_box_append (GTK_BOX (box), color_device->widget_description);
+
+ /* switch */
+ color_device->widget_switch = gtk_switch_new ();
+ gtk_widget_set_valign (color_device->widget_switch, GTK_ALIGN_CENTER);
+ gtk_box_append (GTK_BOX (box), color_device->widget_switch);
+
+ /* arrow button */
+ color_device->widget_arrow = gtk_image_new_from_icon_name ("pan-end-symbolic");
+ color_device->widget_button = gtk_button_new ();
+ g_signal_connect_object (color_device->widget_button, "clicked",
+ G_CALLBACK (cc_color_device_clicked_expander_cb),
+ color_device, G_CONNECT_SWAPPED);
+ gtk_widget_set_valign (color_device->widget_button, GTK_ALIGN_CENTER);
+ gtk_widget_add_css_class (color_device->widget_button, "flat");
+ gtk_button_set_child (GTK_BUTTON (color_device->widget_button), color_device->widget_arrow);
+ gtk_widget_set_visible (color_device->widget_arrow, TRUE);
+ gtk_widget_set_margin_top (color_device->widget_button, 9);
+ gtk_widget_set_margin_bottom (color_device->widget_button, 9);
+ gtk_widget_set_margin_end (color_device->widget_button, 12);
+ gtk_box_append (GTK_BOX (box), color_device->widget_button);
+
+ /* not calibrated */
+ color_device->widget_nocalib = gtk_label_new (_("Not calibrated"));
+ context = gtk_widget_get_style_context (color_device->widget_nocalib);
+ gtk_style_context_add_class (context, "dim-label");
+ gtk_widget_set_margin_end (color_device->widget_nocalib, 18);
+ gtk_box_append (GTK_BOX (box), color_device->widget_nocalib);
+
+ /* refresh */
+ gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (color_device), box);
+}
+
+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..41e0d63
--- /dev/null
+++ b/panels/color/cc-color-device.h
@@ -0,0 +1,38 @@
+/* -*- 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.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <colord.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_COLOR_DEVICE (cc_color_device_get_type ())
+G_DECLARE_FINAL_TYPE (CcColorDevice, cc_color_device, CC, COLOR_DEVICE, GtkListBoxRow)
+
+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
diff --git a/panels/color/cc-color-panel.c b/panels/color/cc-color-panel.c
new file mode 100644
index 0000000..42820c7
--- /dev/null
+++ b/panels/color/cc-color-panel.c
@@ -0,0 +1,2208 @@
+/* -*- 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include <glib/gi18n.h>
+#include <colord.h>
+#include <gtk/gtk.h>
+#include <gdk/x11/gdkx.h>
+
+#include "cc-color-calibrate.h"
+#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"
+
+struct _CcColorPanel
+{
+ CcPanel parent_instance;
+
+ CdClient *client;
+ CdDevice *current_device;
+ GPtrArray *devices;
+ GPtrArray *sensors;
+ GDBusProxy *proxy;
+ GSettings *settings;
+ GSettings *settings_colord;
+ GtkWidget *assistant_calib;
+ GtkWidget *box_calib_brightness;
+ GtkWidget *box_calib_kind;
+ GtkWidget *box_calib_quality;
+ GtkWidget *box_calib_sensor;
+ GtkWidget *box_calib_summary;
+ GtkWidget *box_calib_temp;
+ GtkWidget *box_calib_title;
+ GtkWidget *box_devices;
+ GtkWidget *button_assign_import;
+ GtkWidget *button_assign_ok;
+ GtkWidget *button_calib_export;
+ GtkWidget *dialog_assign;
+ GtkWidget *entry_calib_title;
+ GtkWidget *label_assign_warning;
+ GtkWidget *label_calib_summary_message;
+ GtkWidget *label_no_devices;
+ GtkTreeModel *liststore_assign;
+ GtkTreeModel *liststore_calib_kind;
+ GtkTreeModel *liststore_calib_sensor;
+ GtkWidget *toolbar_devices;
+ GtkWidget *toolbutton_device_calibrate;
+ GtkWidget *toolbutton_device_default;
+ GtkWidget *toolbutton_device_enable;
+ GtkWidget *toolbutton_profile_add;
+ GtkWidget *toolbutton_profile_remove;
+ GtkWidget *toolbutton_profile_view;
+ GtkWidget *treeview_assign;
+ GtkWidget *treeview_calib_kind;
+ GtkWidget *treeview_calib_quality;
+ GtkWidget *treeview_calib_sensor;
+ GtkWidget *treeview_calib_temp;
+ CcColorCalibrate *calibrate;
+ GtkListBox *list_box;
+ gchar *list_box_filter;
+ GtkSizeGroup *list_box_size;
+ gboolean is_live_cd;
+ gboolean model_is_changing;
+};
+
+CC_PANEL_REGISTER (CcColorPanel, cc_color_panel)
+
+enum {
+ GCM_PREFS_COMBO_COLUMN_TEXT,
+ GCM_PREFS_COMBO_COLUMN_PROFILE,
+ GCM_PREFS_COMBO_COLUMN_TYPE,
+ GCM_PREFS_COMBO_COLUMN_WARNING_FILENAME,
+ GCM_PREFS_COMBO_COLUMN_NUM_COLUMNS
+};
+
+/* for the GtkListStores */
+enum {
+ COLUMN_CALIB_KIND_DESCRIPTION,
+ COLUMN_CALIB_KIND_CAP_VALUE,
+ COLUMN_CALIB_KIND_VISIBLE,
+ COLUMN_CALIB_KIND_LAST
+};
+enum {
+ COLUMN_CALIB_QUALITY_DESCRIPTION,
+ COLUMN_CALIB_QUALITY_APPROX_TIME,
+ COLUMN_CALIB_QUALITY_VALUE,
+ COLUMN_CALIB_QUALITY_LAST
+};
+enum {
+ COLUMN_CALIB_SENSOR_OBJECT,
+ COLUMN_CALIB_SENSOR_DESCRIPTION,
+ COLUMN_CALIB_SENSOR_LAST
+};
+enum {
+ COLUMN_CALIB_TEMP_DESCRIPTION,
+ COLUMN_CALIB_TEMP_VALUE_K,
+ COLUMN_CALIB_TEMP_LAST
+};
+
+#define COLORD_SETTINGS_SCHEMA "org.freedesktop.ColorHelper"
+#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"
+
+/* max number of devices and profiles to cause auto-expand at startup */
+#define GCM_PREFS_MAX_DEVICES_PROFILES_EXPANDED 5
+
+static void gcm_prefs_refresh_toolbar_buttons (CcColorPanel *panel);
+
+static void
+gcm_prefs_combobox_add_profile (CcColorPanel *prefs,
+ CdProfile *profile,
+ GtkTreeIter *iter)
+{
+ const gchar *id;
+ GtkTreeIter iter_tmp;
+ g_autoptr(GString) string = NULL;
+ gchar *escaped = NULL;
+ guint kind = 0;
+ const gchar *warning = NULL;
+#if CD_CHECK_VERSION(0,1,25)
+ gchar **warnings;
+#endif
+
+ /* iter is optional */
+ if (iter == NULL)
+ iter = &iter_tmp;
+
+ /* use description */
+ string = g_string_new (cd_profile_get_title (profile));
+
+ /* any source prefix? */
+ id = cd_profile_get_metadata_item (profile,
+ CD_PROFILE_METADATA_DATA_SOURCE);
+ if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_EDID) == 0)
+ {
+ /* TRANSLATORS: this is a profile prefix to signify the
+ * profile has been auto-generated for this hardware */
+ g_string_prepend (string, _("Default: "));
+ kind = 1;
+ }
+#if CD_CHECK_VERSION(0,1,14)
+ if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_STANDARD) == 0)
+ {
+ /* TRANSLATORS: this is a profile prefix to signify the
+ * profile his a standard space like AdobeRGB */
+ g_string_prepend (string, _("Colorspace: "));
+ kind = 2;
+ }
+ if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_TEST) == 0)
+ {
+ /* TRANSLATORS: this is a profile prefix to signify the
+ * profile is a test profile */
+ g_string_prepend (string, _("Test profile: "));
+ kind = 3;
+ }
+#endif
+
+ /* is the profile faulty */
+#if CD_CHECK_VERSION(0,1,25)
+ warnings = cd_profile_get_warnings (profile);
+ if (warnings != NULL && warnings[0] != NULL)
+ warning = "dialog-warning-symbolic";
+#endif
+
+ escaped = g_markup_escape_text (string->str, -1);
+ gtk_list_store_append (GTK_LIST_STORE (prefs->liststore_assign), iter);
+ gtk_list_store_set (GTK_LIST_STORE (prefs->liststore_assign), iter,
+ GCM_PREFS_COMBO_COLUMN_TEXT, escaped,
+ GCM_PREFS_COMBO_COLUMN_PROFILE, profile,
+ GCM_PREFS_COMBO_COLUMN_TYPE, kind,
+ GCM_PREFS_COMBO_COLUMN_WARNING_FILENAME, warning,
+ -1);
+}
+
+static void
+gcm_prefs_default_cb (CcColorPanel *prefs)
+{
+ g_autoptr(CdProfile) profile = NULL;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+
+ /* TODO: check if the profile is already systemwide */
+ profile = cd_device_get_default_profile (prefs->current_device);
+ if (profile == NULL)
+ return;
+
+ /* install somewhere out of $HOME */
+ ret = cd_profile_install_system_wide_sync (profile,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (!ret)
+ g_warning ("failed to set profile system-wide: %s",
+ error->message);
+}
+
+typedef struct
+{
+ GtkResponseType response;
+ GMainLoop *mainloop;
+} DialogRunData;
+
+static void
+dialog_response_cb (GtkDialog *dialog,
+ GtkResponseType response,
+ DialogRunData *run_data)
+{
+ run_data->response = response;
+ g_main_loop_quit (run_data->mainloop);
+}
+
+static gboolean
+dialog_close_cb (GtkDialog *dialog,
+ GtkResponseType response,
+ DialogRunData *run_data)
+{
+ g_main_loop_quit (run_data->mainloop);
+ return GDK_EVENT_PROPAGATE;
+}
+
+static GtkResponseType
+run_dialog (GtkDialog *dialog)
+{
+ g_autoptr(GMainLoop) mainloop = NULL;
+ DialogRunData run_data;
+ guint response_id;
+ guint close_id;
+
+ mainloop = g_main_loop_new (NULL, FALSE);
+
+ run_data = (DialogRunData) {
+ .response = GTK_RESPONSE_DELETE_EVENT,
+ .mainloop = mainloop,
+ };
+
+ response_id = g_signal_connect (dialog, "response", G_CALLBACK (dialog_response_cb), &run_data);
+ close_id = g_signal_connect (dialog, "close-request", G_CALLBACK (dialog_close_cb), &run_data);
+
+ gtk_window_present (GTK_WINDOW (dialog));
+
+ g_main_loop_run (mainloop);
+
+ g_signal_handler_disconnect (dialog, response_id);
+ g_signal_handler_disconnect (dialog, close_id);
+
+ return run_data.response;
+}
+
+static GFile *
+gcm_prefs_file_chooser_get_icc_profile (CcColorPanel *prefs)
+{
+ g_autoptr(GFile) current_folder = NULL;
+ GtkWindow *window;
+ GtkWidget *dialog;
+ GFile *file = NULL;
+ GtkFileFilter *filter;
+
+ /* create new dialog */
+ window = GTK_WINDOW (prefs->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,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Import"), GTK_RESPONSE_ACCEPT,
+ NULL);
+ current_folder = g_file_new_for_path (g_get_home_dir ());
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), current_folder, NULL);
+ gtk_file_chooser_set_create_folders (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 (run_dialog (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+ file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER(dialog));
+
+ /* we're done */
+ gtk_window_destroy (GTK_WINDOW (dialog));
+
+ /* or NULL for missing */
+ return file;
+}
+
+static void
+gcm_prefs_calib_cancel_cb (CcColorPanel *prefs)
+{
+ gtk_widget_hide (prefs->assistant_calib);
+}
+
+static gboolean
+gcm_prefs_calib_delayed_complete_cb (gpointer user_data)
+{
+ CcColorPanel *panel = CC_COLOR_PANEL (user_data);
+ GtkAssistant *assistant;
+
+ assistant = GTK_ASSISTANT (panel->assistant_calib);
+ gtk_assistant_set_page_complete (assistant, panel->box_calib_brightness, TRUE);
+ return FALSE;
+}
+
+static void
+gcm_prefs_calib_prepare_cb (CcColorPanel *panel,
+ GtkWidget *page)
+{
+ /* give the user the indication they should actually manually set the
+ * desired brightness rather than clicking blindly by delaying the
+ * "Next" button deliberately for a second or so */
+ if (page == panel->box_calib_brightness)
+ {
+ g_timeout_add_seconds (1, gcm_prefs_calib_delayed_complete_cb, panel);
+ return;
+ }
+
+ /* disable the brightness page as we don't want to show a 'Finished'
+ * button if the user goes back at any point */
+ gtk_assistant_set_page_complete (GTK_ASSISTANT (panel->assistant_calib), panel->box_calib_brightness, FALSE);
+}
+
+static void
+gcm_prefs_calib_apply_cb (CcColorPanel *prefs)
+{
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ GtkWindow *window = NULL;
+
+ /* setup the calibration object with items that can fail */
+ ret = cc_color_calibrate_setup (prefs->calibrate,
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to setup calibrate: %s", error->message);
+ return;
+ }
+
+ /* actually start the calibration */
+ window = GTK_WINDOW (prefs->assistant_calib);
+ ret = cc_color_calibrate_start (prefs->calibrate,
+ window,
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to start calibrate: %s", error->message);
+ gtk_widget_hide (GTK_WIDGET (window));
+ return;
+ }
+
+ /* if we are a LiveCD then don't close the window as there is another
+ * summary pane with the export button */
+ if (!prefs->is_live_cd)
+ gtk_widget_hide (GTK_WIDGET (window));
+}
+
+static void
+gcm_prefs_calib_temp_treeview_clicked_cb (CcColorPanel *prefs,
+ GtkTreeSelection *selection)
+{
+ gboolean ret;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ guint target_whitepoint;
+ GtkAssistant *assistant;
+
+ /* check to see if anything is selected */
+ ret = gtk_tree_selection_get_selected (selection, &model, &iter);
+ assistant = GTK_ASSISTANT (prefs->assistant_calib);
+ gtk_assistant_set_page_complete (assistant, prefs->box_calib_temp, ret);
+ if (!ret)
+ return;
+
+ gtk_tree_model_get (model, &iter,
+ COLUMN_CALIB_TEMP_VALUE_K, &target_whitepoint,
+ -1);
+ cc_color_calibrate_set_temperature (prefs->calibrate, target_whitepoint);
+}
+
+static void
+gcm_prefs_calib_kind_treeview_clicked_cb (CcColorPanel *prefs,
+ GtkTreeSelection *selection)
+{
+ CdSensorCap device_kind;
+ gboolean ret;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ GtkAssistant *assistant;
+
+ /* check to see if anything is selected */
+ ret = gtk_tree_selection_get_selected (selection, &model, &iter);
+ assistant = GTK_ASSISTANT (prefs->assistant_calib);
+ gtk_assistant_set_page_complete (assistant, prefs->box_calib_kind, ret);
+ if (!ret)
+ return;
+
+ /* save the values if we have a selection */
+ gtk_tree_model_get (model, &iter,
+ COLUMN_CALIB_KIND_CAP_VALUE, &device_kind,
+ -1);
+ cc_color_calibrate_set_kind (prefs->calibrate, device_kind);
+}
+
+static void
+gcm_prefs_calib_quality_treeview_clicked_cb (CcColorPanel *prefs,
+ GtkTreeSelection *selection)
+{
+ CdProfileQuality quality;
+ gboolean ret;
+ GtkAssistant *assistant;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ /* check to see if anything is selected */
+ ret = gtk_tree_selection_get_selected (selection, &model, &iter);
+ assistant = GTK_ASSISTANT (prefs->assistant_calib);
+ gtk_assistant_set_page_complete (assistant, prefs->box_calib_quality, ret);
+ if (!ret)
+ return;
+
+ /* save the values if we have a selection */
+ gtk_tree_model_get (model, &iter,
+ COLUMN_CALIB_QUALITY_VALUE, &quality,
+ -1);
+ cc_color_calibrate_set_quality (prefs->calibrate, quality);
+}
+
+static gboolean
+gcm_prefs_calib_set_sensor_cap_supported_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ CdSensorCap cap;
+ CdSensor *sensor = CD_SENSOR (data);
+ gboolean supported;
+
+ gtk_tree_model_get (model, iter,
+ COLUMN_CALIB_KIND_CAP_VALUE, &cap,
+ -1);
+ supported = cd_sensor_has_cap (sensor, cap);
+ g_debug ("%s(%s) is %s",
+ cd_sensor_get_model (sensor),
+ cd_sensor_cap_to_string (cap),
+ supported ? "supported" : "not-supported");
+ gtk_list_store_set (GTK_LIST_STORE (model), iter,
+ COLUMN_CALIB_KIND_VISIBLE, supported,
+ -1);
+ return FALSE;
+}
+
+static guint8
+_cd_bitfield_popcount (guint64 bitfield)
+{
+ guint8 i;
+ guint8 tmp = 0;
+ for (i = 0; i < 64; i++)
+ tmp += cd_bitfield_contain (bitfield, i);
+ return tmp;
+}
+
+static void
+gcm_prefs_calib_set_sensor (CcColorPanel *prefs,
+ CdSensor *sensor)
+{
+ guint64 caps;
+ guint8 i;
+
+ /* use this sensor for calibration */
+ cc_color_calibrate_set_sensor (prefs->calibrate, sensor);
+
+ /* hide display types the sensor does not support */
+ gtk_tree_model_foreach (prefs->liststore_calib_kind,
+ gcm_prefs_calib_set_sensor_cap_supported_cb,
+ sensor);
+
+ /* if the sensor only supports one kind then do not show the panel at all */
+ caps = cd_sensor_get_caps (sensor);
+ if (_cd_bitfield_popcount (caps) == 1)
+ {
+ gtk_widget_set_visible (prefs->box_calib_kind, FALSE);
+ for (i = 0; i < CD_SENSOR_CAP_LAST; i++)
+ {
+ if (cd_bitfield_contain (caps, i))
+ cc_color_calibrate_set_kind (prefs->calibrate, i);
+ }
+ }
+ else
+ {
+ cc_color_calibrate_set_kind (prefs->calibrate, CD_SENSOR_CAP_UNKNOWN);
+ gtk_widget_set_visible (prefs->box_calib_kind, TRUE);
+ }
+}
+
+static void
+gcm_prefs_calib_sensor_treeview_clicked_cb (CcColorPanel *prefs,
+ GtkTreeSelection *selection)
+{
+ gboolean ret;
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ g_autoptr(CdSensor) sensor = NULL;
+ GtkAssistant *assistant;
+
+ /* check to see if anything is selected */
+ ret = gtk_tree_selection_get_selected (selection, &model, &iter);
+ assistant = GTK_ASSISTANT (prefs->assistant_calib);
+ gtk_assistant_set_page_complete (assistant, prefs->box_calib_sensor, ret);
+ if (!ret)
+ return;
+
+ /* save the values if we have a selection */
+ gtk_tree_model_get (model, &iter,
+ COLUMN_CALIB_SENSOR_OBJECT, &sensor,
+ -1);
+ gcm_prefs_calib_set_sensor (prefs, sensor);
+}
+
+static void
+gcm_prefs_calibrate_display (CcColorPanel *prefs)
+{
+ CdSensor *sensor_tmp;
+ const gchar *tmp;
+ GtkTreeIter iter;
+ guint i;
+
+ /* set target device */
+ cc_color_calibrate_set_device (prefs->calibrate, prefs->current_device);
+
+ /* add sensors to list */
+ gtk_list_store_clear (GTK_LIST_STORE (prefs->liststore_calib_sensor));
+ if (prefs->sensors->len > 1)
+ {
+ for (i = 0; i < prefs->sensors->len; i++)
+ {
+ sensor_tmp = g_ptr_array_index (prefs->sensors, i);
+ gtk_list_store_append (GTK_LIST_STORE (prefs->liststore_calib_sensor), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (prefs->liststore_calib_sensor), &iter,
+ COLUMN_CALIB_SENSOR_OBJECT, sensor_tmp,
+ COLUMN_CALIB_SENSOR_DESCRIPTION, cd_sensor_get_model (sensor_tmp),
+ -1);
+ }
+ gtk_widget_set_visible (prefs->box_calib_sensor, TRUE);
+ }
+ else
+ {
+ sensor_tmp = g_ptr_array_index (prefs->sensors, 0);
+ gcm_prefs_calib_set_sensor (prefs, sensor_tmp);
+ gtk_widget_set_visible (prefs->box_calib_sensor, FALSE);
+ }
+
+ /* set default profile title */
+ tmp = cd_device_get_model (prefs->current_device);
+ if (tmp == NULL)
+ tmp = cd_device_get_vendor (prefs->current_device);
+ if (tmp == NULL)
+ tmp = _("Screen");
+ gtk_editable_set_text (GTK_EDITABLE (prefs->entry_calib_title), tmp);
+ cc_color_calibrate_set_title (prefs->calibrate, tmp);
+
+ /* set the display whitepoint to D65 by default */
+ //FIXME?
+
+ /* show ui */
+ gtk_window_set_transient_for (GTK_WINDOW (prefs->assistant_calib),
+ GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (prefs))));
+ gtk_widget_show (prefs->assistant_calib);
+}
+
+static void
+gcm_prefs_title_entry_changed_cb (CcColorPanel *prefs)
+{
+ GtkAssistant *assistant;
+ const gchar *value;
+
+ assistant = GTK_ASSISTANT (prefs->assistant_calib);
+ value = gtk_editable_get_text (GTK_EDITABLE (prefs->entry_calib_title));
+ cc_color_calibrate_set_title (prefs->calibrate, value);
+ gtk_assistant_set_page_complete (assistant, prefs->box_calib_title, value[0] != '\0');
+}
+
+static void
+gcm_prefs_calibrate_cb (CcColorPanel *prefs)
+{
+ GtkNative *native;
+ GdkSurface *surface;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ guint xid = 0;
+ g_autoptr(GPtrArray) argv = NULL;
+
+ /* use the new-style calibration helper */
+ if (cd_device_get_kind (prefs->current_device) == CD_DEVICE_KIND_DISPLAY)
+ {
+ gcm_prefs_calibrate_display (prefs);
+ return;
+ }
+
+ /* get xid */
+ native = gtk_widget_get_native (GTK_WIDGET (prefs));
+ surface = gtk_native_get_surface (native);
+
+ if (GDK_IS_X11_SURFACE (surface))
+ xid = gdk_x11_surface_get_xid (GDK_X11_SURFACE (surface));
+
+ /* run with modal set */
+ argv = g_ptr_array_new_with_free_func (g_free);
+ g_ptr_array_add (argv, g_strdup ("gcm-calibrate"));
+ g_ptr_array_add (argv, g_strdup ("--device"));
+ g_ptr_array_add (argv, g_strdup (cd_device_get_id (prefs->current_device)));
+ g_ptr_array_add (argv, g_strdup ("--parent-window"));
+ g_ptr_array_add (argv, g_strdup_printf ("%i", xid));
+ g_ptr_array_add (argv, NULL);
+ ret = g_spawn_async (NULL, (gchar**) argv->pdata, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, &error);
+ if (!ret)
+ g_warning ("failed to run calibrate: %s", error->message);
+}
+
+static gboolean
+gcm_prefs_is_profile_suitable_for_device (CdProfile *profile,
+ CdDevice *device)
+{
+ const gchar *data_source;
+ CdProfileKind profile_kind_tmp;
+ CdProfileKind profile_kind;
+ CdColorspace profile_colorspace;
+ CdColorspace device_colorspace = 0;
+ gboolean ret = FALSE;
+ CdDeviceKind device_kind;
+ CdStandardSpace standard_space;
+
+ /* 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;
+
+ /* if this is a display matching with one of the standard spaces that displays
+ * could emulate, also mark it as suitable */
+ if (cd_device_get_kind (device) == CD_DEVICE_KIND_DISPLAY &&
+ cd_profile_get_kind (profile) == CD_PROFILE_KIND_DISPLAY_DEVICE)
+ {
+ data_source = cd_profile_get_metadata_item (profile,
+ CD_PROFILE_METADATA_STANDARD_SPACE);
+ standard_space = cd_standard_space_from_string (data_source);
+ if (standard_space == CD_STANDARD_SPACE_SRGB ||
+ standard_space == CD_STANDARD_SPACE_ADOBE_RGB)
+ {
+ ret = TRUE;
+ 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;
+
+ /* ignore the colorspace profiles */
+ data_source = cd_profile_get_metadata_item (profile,
+ CD_PROFILE_METADATA_DATA_SOURCE);
+ if (g_strcmp0 (data_source, CD_PROFILE_METADATA_DATA_SOURCE_STANDARD) == 0)
+ 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;
+ g_autofree gchar *text_a = NULL;
+ g_autofree gchar *text_b = NULL;
+
+ /* 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)
+ return -1;
+ else if (type_a > type_b)
+ return 1;
+ else
+ return g_strcmp0 (text_a, text_b);
+}
+
+static gboolean
+gcm_prefs_profile_exists_in_array (GPtrArray *array, CdProfile *profile)
+{
+ CdProfile *profile_tmp;
+ guint i;
+
+ for (i = 0; i < array->len; i++)
+ {
+ profile_tmp = g_ptr_array_index (array, i);
+ if (cd_profile_equal (profile, profile_tmp))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+gcm_prefs_add_profiles_suitable_for_devices (CcColorPanel *prefs,
+ GPtrArray *profiles)
+{
+ CdProfile *profile_tmp;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GPtrArray) profile_array = NULL;
+ GtkTreeIter iter;
+ guint i;
+
+ gtk_list_store_clear (GTK_LIST_STORE (prefs->liststore_assign));
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (prefs->liststore_assign),
+ GCM_PREFS_COMBO_COLUMN_TEXT,
+ GTK_SORT_ASCENDING);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (prefs->liststore_assign),
+ GCM_PREFS_COMBO_COLUMN_TEXT,
+ gcm_prefs_combo_sort_func_cb,
+ prefs->liststore_assign, NULL);
+
+ gtk_widget_hide (prefs->label_assign_warning);
+
+ /* get profiles */
+ profile_array = cd_client_get_profiles_sync (prefs->client,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (profile_array == NULL)
+ {
+ g_warning ("failed to get profiles: %s",
+ error->message);
+ return;
+ }
+
+ /* add profiles of the right kind */
+ for (i = 0; i < profile_array->len; i++)
+ {
+ profile_tmp = g_ptr_array_index (profile_array, i);
+
+ /* get properties */
+ ret = cd_profile_connect_sync (profile_tmp,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to get profile: %s", error->message);
+ return;
+ }
+
+ /* don't add any of the already added profiles */
+ if (profiles != NULL)
+ {
+ if (gcm_prefs_profile_exists_in_array (profiles, profile_tmp))
+ continue;
+ }
+
+ /* only add correct types */
+ ret = gcm_prefs_is_profile_suitable_for_device (profile_tmp,
+ prefs->current_device);
+ if (!ret)
+ continue;
+
+#if CD_CHECK_VERSION(0,1,13)
+ /* ignore profiles from other user accounts */
+ if (!cd_profile_has_access (profile_tmp))
+ continue;
+#endif
+
+ /* add */
+ gcm_prefs_combobox_add_profile (prefs,
+ profile_tmp,
+ &iter);
+ }
+}
+
+static void
+gcm_prefs_calib_export_cb (CcColorPanel *prefs)
+{
+ CdProfile *profile;
+ gboolean ret;
+ g_autofree gchar *default_name = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GFile) destination = NULL;
+ g_autoptr(GFile) source = NULL;
+ GtkWidget *dialog;
+
+ profile = cc_color_calibrate_get_profile (prefs->calibrate);
+ ret = cd_profile_connect_sync (profile, NULL, &error);
+ if (!ret)
+ {
+ g_warning ("Failed to get imported profile: %s", error->message);
+ return;
+ }
+
+ /* TRANSLATORS: this is the dialog to save the ICC profile */
+ dialog = gtk_file_chooser_dialog_new (_("Save Profile"),
+ GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (prefs))),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Save"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ default_name = g_strdup_printf ("%s.icc", cd_profile_get_title (profile));
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), default_name);
+
+ if (run_dialog (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
+ {
+ source = g_file_new_for_path (cd_profile_get_filename (profile));
+ destination = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+ ret = g_file_copy (source,
+ destination,
+ G_FILE_COPY_OVERWRITE,
+ NULL,
+ NULL,
+ NULL,
+ &error);
+ if (!ret)
+ g_warning ("Failed to copy profile: %s", error->message);
+ }
+
+ gtk_window_destroy (GTK_WINDOW (dialog));
+}
+
+static void
+gcm_prefs_calib_export_link_cb (CcColorPanel *prefs,
+ const gchar *url)
+{
+ gtk_show_uri (GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (prefs))),
+ "help:gnome-help/color-howtoimport",
+ GDK_CURRENT_TIME);
+}
+
+static void
+gcm_prefs_profile_add_cb (CcColorPanel *prefs)
+{
+ g_autoptr(GPtrArray) profiles = NULL;
+
+ /* add profiles of the right kind */
+ profiles = cd_device_get_profiles (prefs->current_device);
+ gcm_prefs_add_profiles_suitable_for_devices (prefs, profiles);
+
+ /* make insensitive until we have a selection */
+ gtk_widget_set_sensitive (prefs->button_assign_ok, FALSE);
+
+ /* show the dialog */
+ gtk_window_set_transient_for (GTK_WINDOW (prefs->dialog_assign),
+ GTK_WINDOW (gtk_widget_get_native (GTK_WIDGET (prefs))));
+
+ gtk_widget_show (prefs->dialog_assign);
+}
+
+static void
+gcm_prefs_profile_remove_cb (CcColorPanel *prefs)
+{
+ CdProfile *profile;
+ gboolean ret = FALSE;
+ g_autoptr(GError) error = NULL;
+ GtkListBoxRow *row;
+
+ /* get the selected profile */
+ row = gtk_list_box_get_selected_row (prefs->list_box);
+ if (row == NULL)
+ return;
+ profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (row));
+ 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 (prefs->current_device,
+ profile,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (!ret)
+ g_warning ("failed to remove profile: %s", error->message);
+}
+
+static void
+gcm_prefs_make_profile_default_cb (GObject *object,
+ GAsyncResult *res,
+ CcColorPanel *prefs)
+{
+ CdDevice *device = CD_DEVICE (object);
+ gboolean ret = FALSE;
+ g_autoptr(GError) error = NULL;
+
+ ret = cd_device_make_profile_default_finish (device,
+ res,
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to set default profile on %s: %s",
+ cd_device_get_id (device),
+ error->message);
+ }
+ else
+ {
+ gcm_prefs_refresh_toolbar_buttons (prefs);
+ }
+}
+
+static void
+gcm_prefs_device_profile_enable_cb (CcColorPanel *prefs)
+{
+ CdProfile *profile;
+ GtkListBoxRow *row;
+
+ /* get the selected profile */
+ row = gtk_list_box_get_selected_row (prefs->list_box);
+ if (row == NULL)
+ return;
+ profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (row));
+ 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 (prefs->current_device));
+ cd_device_make_profile_default (prefs->current_device,
+ profile,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ (GAsyncReadyCallback) gcm_prefs_make_profile_default_cb,
+ prefs);
+}
+
+static void
+gcm_prefs_profile_view (CcColorPanel *prefs, CdProfile *profile)
+{
+ GtkNative *native;
+ GdkSurface *surface;
+ g_autoptr(GPtrArray) argv = NULL;
+ guint xid = 0;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+
+ /* get xid */
+ native = gtk_widget_get_native (GTK_WIDGET (prefs));
+ surface = gtk_native_get_surface (native);
+
+ if (GDK_IS_X11_SURFACE (surface))
+ xid = gdk_x11_surface_get_xid (GDK_X11_SURFACE (surface));
+
+ /* open up gcm-viewer as a info pane */
+ argv = g_ptr_array_new_with_free_func (g_free);
+ g_ptr_array_add (argv, g_strdup ("gcm-viewer"));
+ 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));
+ g_ptr_array_add (argv, NULL);
+ ret = g_spawn_async (NULL, (gchar**) argv->pdata, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, &error);
+ if (!ret)
+ g_warning ("failed to run calibrate: %s", error->message);
+}
+
+static void
+gcm_prefs_profile_assign_link_activate_cb (CcColorPanel *prefs,
+ const gchar *uri)
+{
+ CdProfile *profile;
+ GtkListBoxRow *row;
+
+ /* get the selected profile */
+ row = gtk_list_box_get_selected_row (prefs->list_box);
+ if (row == NULL)
+ return;
+ profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (row));
+ if (profile == NULL)
+ {
+ g_warning ("failed to get the active profile");
+ return;
+ }
+
+ /* show it in the viewer */
+ gcm_prefs_profile_view (prefs, profile);
+}
+
+static void
+gcm_prefs_profile_view_cb (CcColorPanel *prefs)
+{
+ CdProfile *profile;
+ GtkListBoxRow *row;
+
+ /* get the selected profile */
+ row = gtk_list_box_get_selected_row (prefs->list_box);
+ if (row == NULL)
+ return;
+ profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (row));
+ 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);
+}
+
+static void
+gcm_prefs_button_assign_ok_cb (CcColorPanel *prefs)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+ g_autoptr(CdProfile) profile = NULL;
+ gboolean ret = FALSE;
+ g_autoptr(GError) error = NULL;
+ GtkTreeSelection *selection;
+
+ /* hide window */
+ gtk_widget_hide (GTK_WIDGET (prefs->dialog_assign));
+
+ /* get the selected profile */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->treeview_assign));
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return;
+ gtk_tree_model_get (model, &iter,
+ GCM_PREFS_COMBO_COLUMN_PROFILE, &profile,
+ -1);
+ if (profile == NULL)
+ {
+ g_warning ("failed to get the active profile");
+ return;
+ }
+
+ /* if the device is disabled, enable the device so that we can
+ * add color profiles to it */
+ if (!cd_device_get_enabled (prefs->current_device))
+ {
+ ret = cd_device_set_enabled_sync (prefs->current_device,
+ TRUE,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to enabled device: %s", error->message);
+ return;
+ }
+ }
+
+ /* just add it, the list store will get ::changed */
+ ret = cd_device_add_profile_sync (prefs->current_device,
+ CD_DEVICE_RELATION_HARD,
+ profile,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to add: %s", error->message);
+ return;
+ }
+
+ /* make it default */
+ cd_device_make_profile_default (prefs->current_device,
+ profile,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ (GAsyncReadyCallback) gcm_prefs_make_profile_default_cb,
+ prefs);
+}
+
+static void
+gcm_prefs_add_profiles_columns (CcColorPanel *prefs,
+ GtkTreeView *treeview)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ /* text */
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (column, renderer,
+ "markup", GCM_PREFS_COMBO_COLUMN_TEXT);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (treeview, column);
+
+ /* image */
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_add_attribute (column, renderer,
+ "icon-name", GCM_PREFS_COMBO_COLUMN_WARNING_FILENAME);
+ gtk_tree_view_append_column (treeview, column);
+}
+
+static void
+gcm_prefs_set_calibrate_button_sensitivity (CcColorPanel *prefs)
+{
+ gboolean ret = FALSE;
+ const gchar *tooltip;
+ CdDeviceKind kind;
+ CdSensor *sensor_tmp;
+
+ /* TRANSLATORS: this is when the button is sensitive */
+ tooltip = _("Create a color profile for the selected device");
+
+ /* no device selected */
+ if (prefs->current_device == NULL)
+ goto out;
+
+ /* are we a display */
+ kind = cd_device_get_kind (prefs->current_device);
+ if (kind == CD_DEVICE_KIND_DISPLAY)
+ {
+
+ /* find whether we have hardware installed */
+ if (prefs->sensors == NULL || prefs->sensors->len == 0)
+ {
+ /* 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 (prefs->sensors == NULL || prefs->sensors->len == 0)
+ {
+ /* 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 */
+ sensor_tmp = g_ptr_array_index (prefs->sensors, 0);
+ ret = cd_sensor_has_cap (sensor_tmp, 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 */
+ gtk_widget_set_tooltip_text (prefs->toolbutton_device_calibrate, tooltip);
+ gtk_widget_set_sensitive (prefs->toolbutton_device_calibrate, ret);
+}
+
+static void
+gcm_prefs_device_clicked (CcColorPanel *prefs, CdDevice *device)
+{
+ /* we have a new device */
+ g_debug ("selected device is: %s",
+ cd_device_get_id (device));
+
+ /* can this device calibrate */
+ gcm_prefs_set_calibrate_button_sensitivity (prefs);
+}
+
+static void
+gcm_prefs_profile_clicked (CcColorPanel *prefs, CdProfile *profile, CdDevice *device)
+{
+ g_autofree gchar *s = NULL;
+
+ /* get profile */
+ g_debug ("selected profile = %s",
+ cd_profile_get_filename (profile));
+
+ /* allow getting profile info */
+ if (cd_profile_get_filename (profile) != NULL &&
+ (s = g_find_program_in_path ("gcm-viewer")) != NULL)
+ gtk_widget_set_sensitive (prefs->toolbutton_profile_view, TRUE);
+ else
+ gtk_widget_set_sensitive (prefs->toolbutton_profile_view, FALSE);
+}
+
+static void
+gcm_prefs_profiles_treeview_clicked_cb (CcColorPanel *prefs,
+ GtkTreeSelection *selection)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ g_autoptr(CdProfile) profile = NULL;
+#if CD_CHECK_VERSION(0,1,25)
+ gchar **warnings;
+#endif
+
+ /* get selection */
+ if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+ return;
+ gtk_tree_model_get (model, &iter,
+ GCM_PREFS_COMBO_COLUMN_PROFILE, &profile,
+ -1);
+
+ /* as soon as anything is selected, make the Add button sensitive */
+ gtk_widget_set_sensitive (prefs->button_assign_ok, TRUE);
+
+ /* is the profile faulty */
+#if CD_CHECK_VERSION(0,1,25)
+ warnings = cd_profile_get_warnings (profile);
+ gtk_widget_set_visible (prefs->label_assign_warning, warnings != NULL && warnings[0] != NULL);
+#else
+ gtk_widget_set_visible (prefs->label_assign_warning, FALSE);
+#endif
+}
+
+static void
+gcm_prefs_profiles_row_activated_cb (CcColorPanel *prefs,
+ GtkTreePath *path)
+{
+ GtkTreeIter iter;
+ gboolean ret;
+
+ ret = gtk_tree_model_get_iter (gtk_tree_view_get_model (GTK_TREE_VIEW (prefs->treeview_assign)), &iter, path);
+ if (!ret)
+ return;
+ gcm_prefs_button_assign_ok_cb (prefs);
+}
+
+
+static void
+gcm_prefs_button_assign_import_cb (CcColorPanel *prefs)
+{
+ g_autoptr(GFile) file = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(CdProfile) profile = NULL;
+
+ file = gcm_prefs_file_chooser_get_icc_profile (prefs);
+ if (file == NULL)
+ {
+ g_warning ("failed to get ICC file");
+ gtk_widget_hide (GTK_WIDGET (prefs->dialog_assign));
+ return;
+ }
+
+#if CD_CHECK_VERSION(0,1,12)
+ profile = cd_client_import_profile_sync (prefs->client,
+ file,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (profile == NULL)
+ {
+ g_warning ("failed to get imported profile: %s", error->message);
+ return;
+ }
+#endif
+
+ /* add to list view */
+ gcm_prefs_profile_add_cb (prefs);
+}
+
+static void
+gcm_prefs_sensor_coldplug (CcColorPanel *prefs)
+{
+ CdSensor *sensor_tmp;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GPtrArray) sensors = NULL;
+ guint i;
+
+ /* unref old */
+ g_clear_pointer (&prefs->sensors, g_ptr_array_unref);
+
+ /* no present */
+ sensors = cd_client_get_sensors_sync (prefs->client, NULL, &error);
+ if (sensors == NULL)
+ {
+ g_warning ("%s", error->message);
+ return;
+ }
+ if (sensors->len == 0)
+ return;
+
+ /* save a copy of the sensor list */
+ prefs->sensors = g_ptr_array_ref (sensors);
+
+ /* connect to each sensor */
+ for (i = 0; i < sensors->len; i++)
+ {
+ sensor_tmp = g_ptr_array_index (sensors, i);
+ ret = cd_sensor_connect_sync (sensor_tmp, NULL, &error);
+ if (!ret)
+ {
+ g_warning ("%s", error->message);
+ return;
+ }
+ }
+}
+
+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 void
+gcm_prefs_add_device_profile (CcColorPanel *prefs,
+ CdDevice *device,
+ CdProfile *profile,
+ gboolean is_default)
+{
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ GtkWidget *widget;
+
+ /* get properties */
+ ret = cd_profile_connect_sync (profile,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ &error);
+ if (!ret)
+ {
+ g_warning ("failed to get profile: %s", error->message);
+ return;
+ }
+
+ /* 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));
+ }
+ return;
+ }
+
+ /* add to listbox */
+ widget = cc_color_profile_new (device, profile, is_default);
+ gtk_list_box_append (prefs->list_box, widget);
+ gtk_size_group_add_widget (prefs->list_box_size, widget);
+}
+
+static void
+gcm_prefs_add_device_profiles (CcColorPanel *prefs, CdDevice *device)
+{
+ CdProfile *profile_tmp;
+ g_autoptr(GPtrArray) profiles = NULL;
+ guint i;
+
+ /* add profiles */
+ profiles = cd_device_get_profiles (device);
+ if (profiles == NULL)
+ return;
+ for (i = 0; i < profiles->len; i++)
+ {
+ profile_tmp = g_ptr_array_index (profiles, i);
+ gcm_prefs_add_device_profile (prefs, device, profile_tmp, i == 0);
+ }
+}
+
+/* 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)
+{
+ CdProfile *profile_tmp;
+ guint i;
+
+ for (i = 0; i < profiles->len; i++)
+ {
+ 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;
+}
+
+/* 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)
+{
+ GList *l;
+ CdDevice *device_tmp;
+ CdProfile *profile_tmp;
+
+ for (l = list; l != NULL; l = l->next)
+ {
+ 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)
+ {
+ continue;
+ }
+
+ /* 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_changed_cb (CcColorPanel *prefs, CdDevice *device)
+{
+ GtkWidget *child;
+ CdDevice *device_tmp;
+ CdProfile *profile_tmp;
+ gboolean ret;
+ g_autoptr(GList) list = NULL;
+ GPtrArray *profiles;
+ guint i;
+
+ /* remove anything in the list view that's not in Device.Profiles */
+ profiles = cd_device_get_profiles (device);
+ child = gtk_widget_get_first_child (GTK_WIDGET (prefs->list_box));
+ while (child)
+ {
+ GtkWidget *next = gtk_widget_get_next_sibling (child);
+
+ if (!CC_IS_COLOR_PROFILE (child))
+ {
+ list = g_list_prepend (list, child);
+ goto next;
+ }
+
+ /* correct device ? */
+ device_tmp = cc_color_profile_get_device (CC_COLOR_PROFILE (child));
+ if (g_strcmp0 (cd_device_get_id (device),
+ cd_device_get_id (device_tmp)) != 0)
+ {
+ list = g_list_prepend (list, child);
+ goto next;
+ }
+
+ /* if profile is not in Device.Profiles then remove */
+ profile_tmp = cc_color_profile_get_profile (CC_COLOR_PROFILE (child));
+ ret = gcm_prefs_find_profile_by_object_path (profiles,
+ cd_profile_get_object_path (profile_tmp));
+ if (!ret)
+ gtk_list_box_remove (prefs->list_box, child);
+ else
+ list = g_list_prepend (list, child);
+
+next:
+ child = next;
+ }
+
+ /* 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);
+ 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);
+ }
+
+ /* resort */
+ gtk_list_box_invalidate_sort (prefs->list_box);
+}
+
+static void
+gcm_prefs_device_expanded_changed_cb (CcColorPanel *prefs,
+ gboolean is_expanded,
+ CcColorDevice *widget)
+{
+ /* ignore internal changes */
+ if (prefs->model_is_changing)
+ return;
+
+ g_free (prefs->list_box_filter);
+ if (is_expanded)
+ {
+ GtkWidget *child;
+
+ prefs->list_box_filter = g_strdup (cd_device_get_id (cc_color_device_get_device (widget)));
+
+ /* unexpand other device widgets */
+ prefs->model_is_changing = TRUE;
+ for (child = gtk_widget_get_first_child (GTK_WIDGET (prefs->list_box));
+ child != NULL;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ if (!CC_IS_COLOR_DEVICE (child))
+ continue;
+ if (CC_COLOR_DEVICE (child) != widget)
+ cc_color_device_set_expanded (CC_COLOR_DEVICE (child), FALSE);
+ }
+ prefs->model_is_changing = FALSE;
+ }
+ else
+ {
+ prefs->list_box_filter = NULL;
+ }
+ gtk_list_box_invalidate_filter (prefs->list_box);
+}
+
+static void
+gcm_prefs_add_device (CcColorPanel *prefs, CdDevice *device)
+{
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+ GtkWidget *widget;
+
+ /* get device properties */
+ ret = cd_device_connect_sync (device, cc_panel_get_cancellable (CC_PANEL (prefs)), &error);
+ if (!ret)
+ {
+ g_warning ("failed to connect to the device: %s", error->message);
+ return;
+ }
+
+ /* add device */
+ widget = cc_color_device_new (device);
+ g_signal_connect_object (widget, "expanded-changed",
+ G_CALLBACK (gcm_prefs_device_expanded_changed_cb), prefs, G_CONNECT_SWAPPED);
+ gtk_list_box_append (prefs->list_box, widget);
+ gtk_size_group_add_widget (prefs->list_box_size, widget);
+
+ /* add profiles */
+ gcm_prefs_add_device_profiles (prefs, device);
+
+ /* watch for changes */
+ g_ptr_array_add (prefs->devices, g_object_ref (device));
+ g_signal_connect_object (device, "changed",
+ G_CALLBACK (gcm_prefs_device_changed_cb), prefs, G_CONNECT_SWAPPED);
+ gtk_list_box_invalidate_sort (prefs->list_box);
+}
+
+static void
+gcm_prefs_remove_device (CcColorPanel *prefs, CdDevice *device)
+{
+ GtkWidget *child;
+ CdDevice *device_tmp;
+
+ child = gtk_widget_get_first_child (GTK_WIDGET (prefs->list_box));
+ while (child)
+ {
+ GtkWidget *next = gtk_widget_get_next_sibling (child);
+
+ if (CC_IS_COLOR_DEVICE (child))
+ device_tmp = cc_color_device_get_device (CC_COLOR_DEVICE (child));
+ else
+ device_tmp = cc_color_profile_get_device (CC_COLOR_PROFILE (child));
+ if (g_strcmp0 (cd_device_get_object_path (device),
+ cd_device_get_object_path (device_tmp)) == 0)
+ {
+ gtk_list_box_remove (prefs->list_box, child);
+ }
+
+ child = next;
+ }
+ g_signal_handlers_disconnect_by_func (device,
+ G_CALLBACK (gcm_prefs_device_changed_cb),
+ prefs);
+ g_ptr_array_remove (prefs->devices, device);
+}
+
+static void
+gcm_prefs_update_device_list_extra_entry (CcColorPanel *prefs)
+{
+ GtkListBoxRow *first_row;
+
+ /* any devices to show? */
+ first_row = gtk_list_box_get_row_at_index (prefs->list_box, 0);
+ gtk_widget_set_visible (prefs->label_no_devices, first_row == NULL);
+ gtk_widget_set_visible (prefs->box_devices, first_row != NULL);
+
+ /* if we have only one device expand it by default */
+ if (first_row != NULL &&
+ gtk_list_box_get_row_at_index (prefs->list_box, 1) == NULL)
+ cc_color_device_set_expanded (CC_COLOR_DEVICE (first_row), TRUE);
+}
+
+static void
+gcm_prefs_device_added_cb (CdClient *client,
+ CdDevice *device,
+ CcColorPanel *prefs)
+{
+ /* add the device */
+ gcm_prefs_add_device (prefs, device);
+
+ /* ensure we're not showing the 'No devices detected' entry */
+ gcm_prefs_update_device_list_extra_entry (prefs);
+}
+
+static void
+gcm_prefs_device_removed_cb (CdClient *client,
+ CdDevice *device,
+ CcColorPanel *prefs)
+{
+ /* 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);
+}
+
+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;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(GPtrArray) devices = NULL;
+ guint i;
+
+ /* 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);
+ return;
+ }
+ for (i = 0; i < devices->len; i++)
+ {
+ device = g_ptr_array_index (devices, i);
+ gcm_prefs_add_device (prefs, device);
+ }
+
+ /* ensure we show the 'No devices detected' entry if empty */
+ gcm_prefs_update_device_list_extra_entry (prefs);
+}
+
+static void
+gcm_prefs_list_box_row_selected_cb (CcColorPanel *panel,
+ GtkListBoxRow *row)
+{
+ if (gtk_widget_in_destruction (panel->toolbar_devices))
+ return;
+
+ gcm_prefs_refresh_toolbar_buttons (panel);
+}
+
+static void
+gcm_prefs_refresh_toolbar_buttons (CcColorPanel *panel)
+{
+ CdProfile *profile = NULL;
+ GtkListBoxRow *row;
+ gboolean is_device;
+
+ /* get the selected profile */
+ row = gtk_list_box_get_selected_row (panel->list_box);
+
+ is_device = CC_IS_COLOR_DEVICE (row);
+
+ /* nothing selected */
+ gtk_widget_set_visible (panel->toolbar_devices, row != NULL);
+ if (row == NULL)
+ return;
+
+ /* save current device */
+ g_clear_object (&panel->current_device);
+ g_object_get (row, "device", &panel->current_device, NULL);
+
+ /* device actions */
+ g_debug ("%s selected", is_device ? "device" : "profile");
+ if (CC_IS_COLOR_DEVICE (row))
+ {
+ gcm_prefs_device_clicked (panel, panel->current_device);
+ cc_color_device_set_expanded (CC_COLOR_DEVICE (row), TRUE);
+ }
+ else if (CC_IS_COLOR_PROFILE (row))
+ {
+ profile = cc_color_profile_get_profile (CC_COLOR_PROFILE (row));
+ gcm_prefs_profile_clicked (panel, profile, panel->current_device);
+ }
+ else
+ g_assert_not_reached ();
+
+ gtk_widget_set_visible (panel->toolbutton_device_default, !is_device && cc_color_profile_get_is_default (CC_COLOR_PROFILE (row)));
+ if (profile)
+ gtk_widget_set_sensitive (panel->toolbutton_device_default, !cd_profile_get_is_system_wide (profile));
+ gtk_widget_set_visible (panel->toolbutton_device_enable, !is_device && !cc_color_profile_get_is_default (CC_COLOR_PROFILE (row)));
+ gtk_widget_set_visible (panel->toolbutton_device_calibrate, is_device);
+ gtk_widget_set_visible (panel->toolbutton_profile_add, is_device);
+ gtk_widget_set_visible (panel->toolbutton_profile_view, !is_device);
+ gtk_widget_set_visible (panel->toolbutton_profile_remove, !is_device);
+}
+
+static void
+gcm_prefs_list_box_row_activated_cb (CcColorPanel *prefs,
+ GtkListBoxRow *row)
+{
+ if (CC_IS_COLOR_PROFILE (row))
+ {
+ gcm_prefs_device_profile_enable_cb (prefs);
+ }
+}
+
+static void
+gcm_prefs_connect_cb (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcColorPanel *prefs;
+ gboolean ret;
+ g_autoptr(GError) error = NULL;
+
+ ret = cd_client_connect_finish (CD_CLIENT (object),
+ res,
+ &error);
+ if (!ret)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("failed to connect to colord: %s", error->message);
+ return;
+ }
+
+ /* Only cast the parameters after making sure it didn't fail. At this point,
+ * the user can potentially already have changed to another panel, effectively
+ * making user_data invalid. */
+ prefs = CC_COLOR_PANEL (user_data);
+
+ /* set calibrate button sensitivity */
+ gcm_prefs_sensor_coldplug (prefs);
+
+ /* get devices */
+ cd_client_get_devices (prefs->client,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ gcm_prefs_get_devices_cb,
+ prefs);
+}
+
+static gboolean
+gcm_prefs_is_livecd (void)
+{
+#ifdef __linux__
+ gboolean ret = TRUE;
+ g_autofree gchar *data = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* allow testing */
+ if (g_getenv ("CC_COLOR_PANEL_IS_LIVECD") != NULL)
+ return TRUE;
+
+ /* get the kernel commandline */
+ ret = g_file_get_contents ("/proc/cmdline", &data, NULL, &error);
+ if (!ret)
+ {
+ g_warning ("failed to get kernel command line: %s",
+ error->message);
+ return TRUE;
+ }
+ return (g_strstr_len (data, -1, "liveimg") != NULL ||
+ g_strstr_len (data, -1, "casper") != NULL);
+#else
+ return FALSE;
+#endif
+}
+
+static const char *
+cc_color_panel_get_help_uri (CcPanel *panel)
+{
+ return "help:gnome-help/color";
+}
+
+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)
+{
+ CcColorPanel *prefs = CC_COLOR_PANEL (object);
+
+ g_clear_object (&prefs->settings);
+ g_clear_object (&prefs->settings_colord);
+ g_clear_object (&prefs->client);
+ g_clear_object (&prefs->current_device);
+ g_clear_pointer (&prefs->devices, g_ptr_array_unref);
+ g_clear_object (&prefs->calibrate);
+ g_clear_object (&prefs->list_box_size);
+ g_clear_pointer (&prefs->sensors, g_ptr_array_unref);
+ g_clear_pointer (&prefs->list_box_filter, g_free);
+ g_clear_pointer ((GtkWindow **)&prefs->dialog_assign, gtk_window_destroy);
+
+ 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);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
+
+ panel_class->get_help_uri = cc_color_panel_get_help_uri;
+
+ 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;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/color/cc-color-panel.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, assistant_calib);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_calib_brightness);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_calib_kind);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_calib_quality);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_calib_sensor);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_calib_summary);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_calib_temp);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_calib_title);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, box_devices);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, button_assign_import);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, button_assign_ok);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, button_calib_export);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, dialog_assign);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, entry_calib_title);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, label_assign_warning);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, label_calib_summary_message);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, label_no_devices);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, list_box);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, liststore_assign);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, liststore_calib_kind);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, liststore_calib_sensor);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, toolbar_devices);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, toolbutton_device_calibrate);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, toolbutton_device_default);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, toolbutton_device_enable);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, toolbutton_profile_add);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, toolbutton_profile_remove);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, toolbutton_profile_view);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, treeview_assign);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, treeview_calib_kind);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, treeview_calib_quality);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, treeview_calib_sensor);
+ gtk_widget_class_bind_template_child (widget_class, CcColorPanel, treeview_calib_temp);
+}
+
+static gint
+cc_color_panel_sort_func (GtkListBoxRow *a,
+ GtkListBoxRow *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 (GtkListBoxRow *row, void *user_data)
+{
+ CcColorPanel *prefs = CC_COLOR_PANEL (user_data);
+ g_autoptr(CdDevice) device = NULL;
+
+ /* always show all devices */
+ if (CC_IS_COLOR_DEVICE (row))
+ return TRUE;
+
+ g_object_get (row, "device", &device, NULL);
+ return g_strcmp0 (cd_device_get_id (device), prefs->list_box_filter) == 0;
+}
+
+static gboolean
+cc_color_panel_treeview_quality_default_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ CdProfileQuality quality;
+ GtkTreeSelection *selection = GTK_TREE_SELECTION (data);
+
+ gtk_tree_model_get (model, iter,
+ COLUMN_CALIB_QUALITY_VALUE, &quality,
+ -1);
+ if (quality == CD_PROFILE_QUALITY_MEDIUM)
+ gtk_tree_selection_select_iter (selection, iter);
+ return FALSE;
+}
+
+static void
+cc_color_panel_init (CcColorPanel *prefs)
+{
+ GtkCellRenderer *renderer;
+ GtkTreeModel *model;
+ GtkTreeModel *model_filter;
+ GtkTreeSelection *selection;
+ GtkTreeViewColumn *column;
+
+ g_resources_register (cc_color_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (prefs));
+
+ prefs->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+
+ /* can do native display calibration using colord-session */
+ prefs->calibrate = cc_color_calibrate_new ();
+ cc_color_calibrate_set_quality (prefs->calibrate, CD_PROFILE_QUALITY_MEDIUM);
+
+ /* setup defaults */
+ prefs->settings = g_settings_new (GCM_SETTINGS_SCHEMA);
+ prefs->settings_colord = g_settings_new (COLORD_SETTINGS_SCHEMA);
+
+ /* assign buttons */
+ g_signal_connect_object (prefs->toolbutton_profile_add, "clicked",
+ G_CALLBACK (gcm_prefs_profile_add_cb), prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->toolbutton_profile_remove, "clicked",
+ G_CALLBACK (gcm_prefs_profile_remove_cb), prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->toolbutton_profile_view, "clicked",
+ G_CALLBACK (gcm_prefs_profile_view_cb), prefs, G_CONNECT_SWAPPED);
+
+ /* href */
+ g_signal_connect_object (prefs->label_assign_warning, "activate-link",
+ G_CALLBACK (gcm_prefs_profile_assign_link_activate_cb), prefs, G_CONNECT_SWAPPED);
+
+ /* add columns to profile tree view */
+ gcm_prefs_add_profiles_columns (prefs, GTK_TREE_VIEW (prefs->treeview_assign));
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->treeview_assign));
+ g_signal_connect_object (selection, "changed",
+ G_CALLBACK (gcm_prefs_profiles_treeview_clicked_cb),
+ prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->treeview_assign, "row-activated",
+ G_CALLBACK (gcm_prefs_profiles_row_activated_cb),
+ prefs, G_CONNECT_SWAPPED);
+
+ g_signal_connect_object (prefs->toolbutton_device_default, "clicked",
+ G_CALLBACK (gcm_prefs_default_cb), prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->toolbutton_device_enable, "clicked",
+ G_CALLBACK (gcm_prefs_device_profile_enable_cb), prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->toolbutton_device_calibrate, "clicked",
+ G_CALLBACK (gcm_prefs_calibrate_cb), prefs, G_CONNECT_SWAPPED);
+
+ /* set up assign dialog */
+ g_signal_connect_object (prefs->button_assign_ok, "clicked",
+ G_CALLBACK (gcm_prefs_button_assign_ok_cb), prefs, G_CONNECT_SWAPPED);
+
+ /* setup icc profiles list */
+ g_signal_connect_object (prefs->button_assign_import, "clicked",
+ G_CALLBACK (gcm_prefs_button_assign_import_cb), prefs, G_CONNECT_SWAPPED);
+
+ /* setup the calibration helper */
+ g_signal_connect_object (prefs->assistant_calib, "apply",
+ G_CALLBACK (gcm_prefs_calib_apply_cb),
+ prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->assistant_calib, "cancel",
+ G_CALLBACK (gcm_prefs_calib_cancel_cb),
+ prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->assistant_calib, "close",
+ G_CALLBACK (gcm_prefs_calib_cancel_cb),
+ prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->assistant_calib, "prepare",
+ G_CALLBACK (gcm_prefs_calib_prepare_cb),
+ prefs, G_CONNECT_SWAPPED);
+
+ /* setup the calibration helper ::TreeView */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->treeview_calib_quality));
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (prefs->treeview_calib_quality));
+ gtk_tree_model_foreach (model,
+ cc_color_panel_treeview_quality_default_cb,
+ selection);
+ g_signal_connect_object (selection, "changed",
+ G_CALLBACK (gcm_prefs_calib_quality_treeview_clicked_cb),
+ prefs, G_CONNECT_SWAPPED);
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer,
+ "xpad", 9,
+ "ypad", 9,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (column, renderer,
+ "markup", COLUMN_CALIB_QUALITY_DESCRIPTION);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview_calib_quality),
+ GTK_TREE_VIEW_COLUMN (column));
+ column = gtk_tree_view_column_new ();
+ renderer = cc_color_cell_renderer_text_new ();
+ g_object_set (renderer,
+ "xpad", 9,
+ "ypad", 9,
+ "is-dim-label", TRUE,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (column, renderer,
+ "markup", COLUMN_CALIB_QUALITY_APPROX_TIME);
+ gtk_tree_view_column_set_expand (column, FALSE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview_calib_quality),
+ GTK_TREE_VIEW_COLUMN (column));
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->treeview_calib_sensor));
+ g_signal_connect_object (selection, "changed",
+ G_CALLBACK (gcm_prefs_calib_sensor_treeview_clicked_cb),
+ prefs, G_CONNECT_SWAPPED);
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer,
+ "xpad", 9,
+ "ypad", 9,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (column, renderer,
+ "markup", COLUMN_CALIB_SENSOR_DESCRIPTION);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview_calib_sensor),
+ GTK_TREE_VIEW_COLUMN (column));
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->treeview_calib_kind));
+ g_signal_connect_object (selection, "changed",
+ G_CALLBACK (gcm_prefs_calib_kind_treeview_clicked_cb),
+ prefs, G_CONNECT_SWAPPED);
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer,
+ "xpad", 9,
+ "ypad", 9,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (column, renderer,
+ "markup", COLUMN_CALIB_KIND_DESCRIPTION);
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (prefs->treeview_calib_kind));
+ model_filter = gtk_tree_model_filter_new (model, NULL);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (prefs->treeview_calib_kind), model_filter);
+ gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (model_filter),
+ COLUMN_CALIB_KIND_VISIBLE);
+
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview_calib_kind),
+ GTK_TREE_VIEW_COLUMN (column));
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (prefs->treeview_calib_temp));
+ g_signal_connect_object (selection, "changed",
+ G_CALLBACK (gcm_prefs_calib_temp_treeview_clicked_cb),
+ prefs, G_CONNECT_SWAPPED);
+ column = gtk_tree_view_column_new ();
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set (renderer,
+ "xpad", 9,
+ "ypad", 9,
+ NULL);
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_add_attribute (column, renderer,
+ "markup", COLUMN_CALIB_TEMP_DESCRIPTION);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (prefs->treeview_calib_temp),
+ GTK_TREE_VIEW_COLUMN (column));
+ g_signal_connect_object (prefs->entry_calib_title, "notify::text",
+ G_CALLBACK (gcm_prefs_title_entry_changed_cb), prefs, G_CONNECT_SWAPPED);
+
+ /* use a device client array */
+ prefs->client = cd_client_new ();
+ g_signal_connect_object (prefs->client, "device-added",
+ G_CALLBACK (gcm_prefs_device_added_cb), prefs, 0);
+ g_signal_connect_object (prefs->client, "device-removed",
+ G_CALLBACK (gcm_prefs_device_removed_cb), prefs, 0);
+
+ /* use a listbox for the main UI */
+ gtk_list_box_set_filter_func (prefs->list_box,
+ cc_color_panel_filter_func,
+ prefs,
+ NULL);
+ gtk_list_box_set_sort_func (prefs->list_box,
+ cc_color_panel_sort_func,
+ prefs,
+ NULL);
+ g_signal_connect_object (prefs->list_box, "row-selected",
+ G_CALLBACK (gcm_prefs_list_box_row_selected_cb),
+ prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->list_box, "row-activated",
+ G_CALLBACK (gcm_prefs_list_box_row_activated_cb),
+ prefs, G_CONNECT_SWAPPED);
+ prefs->list_box_size = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
+
+ /* connect to colord */
+ cd_client_connect (prefs->client,
+ cc_panel_get_cancellable (CC_PANEL (prefs)),
+ gcm_prefs_connect_cb,
+ prefs);
+
+ /* use the color sensor */
+ g_signal_connect_object (prefs->client, "sensor-added",
+ G_CALLBACK (gcm_prefs_client_sensor_changed_cb),
+ prefs, 0);
+ g_signal_connect_object (prefs->client, "sensor-removed",
+ G_CALLBACK (gcm_prefs_client_sensor_changed_cb),
+ prefs, 0);
+
+ /* set calibrate button sensitivity */
+ gcm_prefs_set_calibrate_button_sensitivity (prefs);
+
+ /* show the confirmation export page if we are running from a LiveCD */
+ prefs->is_live_cd = gcm_prefs_is_livecd ();
+ gtk_widget_set_visible (prefs->box_calib_summary, prefs->is_live_cd);
+ g_signal_connect_object (prefs->button_calib_export, "clicked",
+ G_CALLBACK (gcm_prefs_calib_export_cb), prefs, G_CONNECT_SWAPPED);
+ g_signal_connect_object (prefs->label_calib_summary_message, "activate-link",
+ G_CALLBACK (gcm_prefs_calib_export_link_cb), prefs, G_CONNECT_SWAPPED);
+}
diff --git a/panels/color/cc-color-panel.h b/panels/color/cc-color-panel.h
new file mode 100644
index 0000000..27861d1
--- /dev/null
+++ b/panels/color/cc-color-panel.h
@@ -0,0 +1,30 @@
+/* -*- 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <shell/cc-panel.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_COLOR_PANEL (cc_color_panel_get_type ())
+G_DECLARE_FINAL_TYPE (CcColorPanel, cc_color_panel, CC, COLOR_PANEL, CcPanel)
+
+G_END_DECLS
diff --git a/panels/color/cc-color-panel.ui b/panels/color/cc-color-panel.ui
new file mode 100644
index 0000000..5f7c3fb
--- /dev/null
+++ b/panels/color/cc-color-panel.ui
@@ -0,0 +1,707 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <object class="GtkAssistant" id="assistant_calib">
+ <property name="title" translatable="yes">Screen Calibration</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="hide-on-close">True</property>
+
+ <child>
+ <object class="GtkAssistantPage">
+ <property name="title" translatable="yes">Calibration Quality</property>
+ <property name="complete">True</property>
+ <property name="child">
+ <object class="GtkBox" id="box_calib_quality">
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_quality_message">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Calibration will produce a profile that you can use to color manage your screen. The longer you spend on calibration, the better the quality of the color profile.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_calib_quality_message2">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">You will not be able to use your computer while calibration takes place.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_label_calib_quality_header">
+ <property name="margin_top">12</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_quality_header">
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes" comments="This is the approximate time it takes to calibrate the display.">Quality</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_calib_quality_approx_time">
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes" comments="This is the approximate time it takes to calibrate the display.">Approximate Time</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_calib_quality">
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_calib_quality">
+ <property name="model">liststore_calib_quality</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection3"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkAssistantPage">
+ <property name="title" translatable="yes">Calibration Device</property>
+ <property name="child">
+ <object class="GtkBox" id="box_calib_sensor">
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_sensor_message">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Select the sensor device you want to use for calibration.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_calib_sensor">
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_calib_sensor">
+ <property name="model">liststore_calib_sensor</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">1</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection4"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkAssistantPage">
+ <property name="title" translatable="yes">Display Type</property>
+ <property name="child">
+ <object class="GtkBox" id="box_calib_kind">
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_kind_message">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Select the type of display that is connected.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_calib_kind">
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_calib_kind">
+ <property name="model">liststore_calib_kind</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection5"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkAssistantPage">
+ <property name="title" translatable="yes">Profile Whitepoint</property>
+ <property name="child">
+ <object class="GtkBox" id="box_calib_temp">
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_temp_message">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Select a display target white point. Most displays should be calibrated to a D65 illuminant.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_calib_temp">
+ <property name="hscrollbar_policy">never</property>
+ <property name="vscrollbar_policy">never</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_calib_temp">
+ <property name="model">liststore_calib_temp</property>
+ <property name="headers_visible">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection6"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkAssistantPage">
+ <property name="title" translatable="yes">Display Brightness</property>
+ <property name="child">
+ <object class="GtkBox" id="box_calib_brightness">
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_brightness_message1">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Please set the display to a brightness that is typical for you. Color management will be most accurate at this brightness level.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_calib_brightness_message2">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Alternatively, you can use the brightness level used with one of the other profiles for this device.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkAssistantPage">
+ <property name="page_type">confirm</property>
+ <property name="title" translatable="yes">Profile Name</property>
+ <property name="child">
+ <object class="GtkBox" id="box_calib_title">
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_title_message">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">You can use a color profile on different computers, or even create profiles for different lighting conditions.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_calib_title_header">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Profile Name:</property>
+ <property name="wrap">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry_calib_title">
+ <property name="invisible_char">●</property>
+ <property name="truncate_multiline">True</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child>
+ <object class="GtkAssistantPage">
+ <property name="page_type">summary</property>
+ <property name="title" translatable="yes">Summary</property>
+ <property name="child">
+ <object class="GtkBox" id="box_calib_summary">
+ <property name="orientation">vertical</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_calib_summary_title">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Profile successfully created!</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="halign">center</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkButton" id="button_calib_export">
+ <property name="halign">start</property>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkImage" id="image2">
+ <property name="pixel_size">48</property>
+ <property name="icon_name">folder-symbolic</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box6">
+ <property name="orientation">vertical</property>
+ <property name="spacing">3</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Copy profile</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label2">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Requires writable media</property>
+ <attributes>
+ <attribute name="style" value="italic"/>
+ </attributes>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_calib_summary_message">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">You may find these instructions on how to use the profile on &lt;a href="linux"&gt;GNU/Linux&lt;/a&gt;, &lt;a href="osx"&gt;Apple OS X&lt;/a&gt; and &lt;a href="windows"&gt;Microsoft Windows&lt;/a&gt; systems useful.</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ </child>
+ </object>
+ </property>
+ </object>
+ </child>
+
+ <child internal-child="action_area">
+ <object class="GtkBox" id="assistant-action_area1">
+ <property name="halign">end</property>
+ <property name="spacing">6</property>
+ </object>
+ </child>
+ </object>
+
+ <object class="GtkDialog" id="dialog_assign">
+ <property name="title" translatable="yes">Add Profile</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="icon_name">gnome-color-manager</property>
+ <property name="use-header-bar">1</property>
+ <property name="hide-on-close">True</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_assign">
+ <property name="hscrollbar_policy">never</property>
+ <property name="hscrollbar-policy">GTK_POLICY_NEVER</property>
+ <property name="min-content-height">300</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_assign">
+ <property name="model">liststore_assign</property>
+ <property name="headers_visible">False</property>
+ <property name="enable_search">False</property>
+ <property name="search_column">0</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection2"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_assign_warning">
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Problems detected. The profile may not work correctly. &lt;a href=""&gt;Show details.&lt;/a&gt;</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="titlebar">
+ <object class="GtkHeaderBar">
+ <child type="start">
+ <object class="GtkButton" id="button_assign_import">
+ <property name="label" translatable="yes">_Import File…</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child type="end">
+ <object class="GtkButton" id="button_assign_ok">
+ <property name="label" translatable="yes">_Add</property>
+ <property name="use_underline">True</property>
+ <style>
+ <class name="suggested-action" />
+ </style>
+ </object>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">button_assign_import</action-widget>
+ <action-widget response="ok">button_assign_ok</action-widget>
+ </action-widgets>
+ </object>
+ <template class="CcColorPanel" parent="CcPanel">
+ <child type="content">
+ <object class="GtkScrolledWindow" id="dialog_scroll">
+ <child>
+ <object class="AdwClamp">
+ <property name="margin_top">32</property>
+ <property name="margin_bottom">32</property>
+ <property name="margin_start">12</property>
+ <property name="margin_end">12</property>
+ <child>
+ <object class="GtkBox">
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <property name="hexpand">True</property>
+ <child>
+ <object class="GtkBox" id="hbox3">
+ <property name="spacing">9</property>
+ <child>
+ <object class="GtkLabel" id="label_intro">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Each device needs an up to date color profile to be color managed.</property>
+ <property name="wrap">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_padding">
+ </object>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="linkbutton_help">
+ <property name="label" translatable="yes">Learn more</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">Learn more about color management</property>
+ <property name="uri">help:gnome-help/color-whyimportant</property>
+ <accessibility>
+ <relation name="labelled-by">label_intro</relation>
+ </accessibility>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_devices">
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkListBox" id="list_box">
+ <property name="activate-on-single-click">False</property>
+ <property name="selection-mode">single</property>
+ <style>
+ <class name="boxed-list" />
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="toolbar_devices">
+ <property name="visible">False</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="margin_start">6</property>
+ <property name="margin_end">6</property>
+ <child>
+ <object class="GtkBox" id="left_button_group_box">
+ <property name="halign">start</property>
+ <property name="hexpand">True</property>
+ <style>
+ <class name="linked" />
+ </style>
+ <child>
+ <object class="GtkButton" id="toolbutton_device_default">
+ <property name="label" translatable="yes">_Set for all users</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">Set this profile for all users on this computer</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="toolbutton_device_enable">
+ <property name="label" translatable="yes">_Enable</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>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkBox" id="right_button_group_box">
+ <style>
+ <class name="linked" />
+ </style>
+ <child>
+ <object class="GtkButton" id="toolbutton_profile_add">
+ <property name="label" translatable="yes">_Add profile</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="toolbutton_device_calibrate">
+ <property name="label" translatable="yes">_Calibrate…</property>
+ <property name="has_tooltip">True</property>
+ <property name="tooltip_text" translatable="yes">Calibrate the device</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="toolbutton_profile_remove">
+ <property name="label" translatable="yes">_Remove profile</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="toolbutton_profile_view">
+ <property name="label" translatable="yes">_View details</property>
+ <property name="use_underline">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_no_devices">
+ <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>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </template>
+ <object class="GtkListStore" id="liststore_assign">
+ <columns>
+ <!-- column-name title -->
+ <column type="gchararray"/>
+ <!-- column-name profile -->
+ <column type="GObject"/>
+ <!-- column-name kind -->
+ <column type="guint"/>
+ <!-- column-name warningfn -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="liststore_calib_kind">
+ <columns>
+ <!-- column-name display_kind -->
+ <column type="gchararray"/>
+ <!-- column-name kind -->
+ <column type="guint"/>
+ <!-- column-name visible -->
+ <column type="gboolean"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">LCD</col>
+ <col id="1">1</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">LED</col>
+ <col id="1">8</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">CRT</col>
+ <col id="1">2</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Projector</col>
+ <col id="1">5</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Plasma</col>
+ <col id="1">9</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">LCD (CCFL backlight)</col>
+ <col id="1">10</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">LCD (RGB LED backlight)</col>
+ <col id="1">11</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">LCD (white LED backlight)</col>
+ <col id="1">12</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Wide gamut LCD (CCFL backlight)</col>
+ <col id="1">13</col>
+ <col id="2">False</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Wide gamut LCD (RGB LED backlight)</col>
+ <col id="1">14</col>
+ <col id="2">False</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_calib_quality">
+ <columns>
+ <!-- column-name quality -->
+ <column type="gchararray"/>
+ <!-- column-name approx_time -->
+ <column type="gchararray"/>
+ <!-- column-name value -->
+ <column type="guint"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes" context="Calibration quality">High</col>
+ <col id="1" translatable="yes">40 minutes</col>
+ <col id="2" translatable="no">2</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes" context="Calibration quality">Medium</col>
+ <col id="1" translatable="yes">30 minutes</col>
+ <col id="2" translatable="no">1</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes" context="Calibration quality">Low</col>
+ <col id="1" translatable="yes">15 minutes</col>
+ <col id="2" translatable="no">0</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkListStore" id="liststore_calib_sensor">
+ <columns>
+ <!-- column-name sensor -->
+ <column type="GObject"/>
+ <!-- column-name sensor_desc -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="liststore_calib_temp">
+ <columns>
+ <!-- column-name temp_desc -->
+ <column type="gchararray"/>
+ <!-- column-name temp_value -->
+ <column type="guint"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">Native to display</col>
+ <col id="1">0</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">D50 (Printing and publishing)</col>
+ <col id="1">5000</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">D55</col>
+ <col id="1">5500</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">D65 (Photography and graphics)</col>
+ <col id="1">6500</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">D75</col>
+ <col id="1">7500</col>
+ </row>
+ </data>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup_assign">
+ <widgets>
+ <widget name="label_assign_warning"/>
+ <widget name="scrolledwindow_assign"/>
+ </widgets>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup_buttons">
+ <widgets>
+ <widget name="toolbutton_profile_add"/>
+ <widget name="toolbutton_device_calibrate"/>
+ <widget name="toolbutton_profile_remove"/>
+ <widget name="toolbutton_profile_view"/>
+ </widgets>
+ </object>
+ <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"/>
+</interface>
diff --git a/panels/color/cc-color-profile.c b/panels/color/cc-color-profile.c
new file mode 100644
index 0000000..65cb99f
--- /dev/null
+++ b/panels/color/cc-color-profile.c
@@ -0,0 +1,475 @@
+/* -*- 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 _CcColorProfile
+{
+ GtkListBoxRow parent_instance;
+
+ GtkWidget *box;
+ CdDevice *device;
+ CdProfile *profile;
+ gboolean is_default;
+ gchar *sortable;
+ GtkWidget *widget_description;
+ GtkWidget *widget_image;
+ GtkWidget *widget_info;
+ GSettings *settings;
+};
+
+#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_LIST_BOX_ROW)
+
+enum
+{
+ PROP_0,
+ PROP_DEVICE,
+ PROP_PROFILE,
+ PROP_IS_DEFAULT,
+ PROP_LAST
+};
+
+static gchar *
+cc_color_profile_get_profile_date (CdProfile *profile)
+{
+ gint64 created;
+ g_autoptr(GDateTime) dt = NULL;
+
+ /* get profile age */
+ created = cd_profile_get_created (profile);
+ if (created == 0)
+ return NULL;
+ dt = g_date_time_new_from_unix_utc (created);
+ return g_date_time_format (dt, "%x");
+}
+
+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)
+{
+ 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 (color_profile->device) == CD_DEVICE_KIND_PRINTER &&
+ cd_profile_get_filename (color_profile->profile) == NULL)
+ {
+ tooltip = _("Vendor supplied factory calibration data");
+ goto out;
+ }
+
+ /* autogenerated profiles are crap */
+ if (cd_device_get_kind (color_profile->device) == CD_DEVICE_KIND_DISPLAY &&
+ cd_profile_get_kind (color_profile->profile) == CD_PROFILE_KIND_DISPLAY_DEVICE &&
+ !cd_profile_get_has_vcgt (color_profile->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 (color_profile->profile) / seconds_in_one_day;
+ if (cd_device_get_kind (color_profile->device) == CD_DEVICE_KIND_DISPLAY)
+ {
+ g_settings_get (color_profile->settings,
+ GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD,
+ "u",
+ &threshold_days);
+ }
+ else if (cd_device_get_kind (color_profile->device) == CD_DEVICE_KIND_DISPLAY)
+ {
+ g_settings_get (color_profile->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)
+{
+ const gchar *warnings;
+ g_autofree gchar *title = NULL;
+
+ /* show the image if the profile is default */
+ gtk_widget_set_visible (color_profile->widget_image, color_profile->is_default);
+ gtk_widget_set_margin_start (color_profile->widget_description,
+ color_profile->is_default ? 0 : IMAGE_WIDGET_PADDING * 4);
+
+ /* set the title */
+ title = gcm_prefs_get_profile_title (color_profile->profile);
+ gtk_label_set_markup (GTK_LABEL (color_profile->widget_description), title);
+
+ /* show any information */
+ warnings = cc_color_profile_get_warnings (color_profile);
+ gtk_widget_set_visible (color_profile->widget_info, warnings != NULL);
+ gtk_widget_set_tooltip_text (color_profile->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->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->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->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->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->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->device);
+ break;
+ case PROP_PROFILE:
+ g_value_set_object (value, color_profile->profile);
+ break;
+ case PROP_IS_DEFAULT:
+ g_value_set_boolean (value, color_profile->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->device = g_value_dup_object (value);
+ break;
+ case PROP_PROFILE:
+ color_profile->profile = g_value_dup_object (value);
+ break;
+ case PROP_IS_DEFAULT:
+ color_profile->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);
+
+ g_free (color_profile->sortable);
+ g_object_unref (color_profile->device);
+ g_object_unref (color_profile->profile);
+ g_object_unref (color_profile->settings);
+
+ G_OBJECT_CLASS (cc_color_profile_parent_class)->finalize (object);
+}
+
+static void
+cc_color_profile_changed_cb (CcColorProfile *color_profile)
+{
+ g_autoptr(CdProfile) profile = NULL;
+
+ /* check to see if the default has changed */
+ profile = cd_device_get_default_profile (color_profile->device);
+ if (profile != NULL)
+ color_profile->is_default = g_strcmp0 (cd_profile_get_object_path (profile),
+ cd_profile_get_object_path (color_profile->profile)) == 0;
+ 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);
+ const gchar *sortable_data_source;
+ g_autofree gchar *sortable_device = NULL;
+ g_autofree gchar *title = NULL;
+
+ /* watch to see if the default changes */
+ g_signal_connect_object (color_profile->device, "changed",
+ G_CALLBACK (cc_color_profile_changed_cb), color_profile, G_CONNECT_SWAPPED);
+ g_signal_connect_object (color_profile->profile, "changed",
+ G_CALLBACK (cc_color_profile_changed_cb), color_profile, G_CONNECT_SWAPPED);
+
+ /* sort the profiles in the list by:
+ * 1. their 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 (color_profile->profile);
+ sortable_device = cc_color_device_get_sortable_base (color_profile->device);
+ sortable_data_source = cc_color_profile_get_profile_sort_data_source (color_profile->profile);
+ color_profile->sortable = g_strdup_printf ("%s-%s-%012" G_GINT64_FORMAT "-%s",
+ sortable_device,
+ sortable_data_source,
+ cd_profile_get_created (color_profile->profile),
+ title);
+
+ 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));
+}
+
+static void
+cc_color_profile_init (CcColorProfile *color_profile)
+{
+ GtkWidget *box;
+
+ color_profile->settings = g_settings_new (GCM_SETTINGS_SCHEMA);
+
+ box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 9);
+
+ /* default tick */
+ color_profile->widget_image = gtk_image_new_from_icon_name ("object-select-symbolic");
+ gtk_widget_set_margin_start (color_profile->widget_image, IMAGE_WIDGET_PADDING);
+ gtk_widget_set_margin_end (color_profile->widget_image, IMAGE_WIDGET_PADDING);
+ gtk_box_append (GTK_BOX (box), color_profile->widget_image);
+
+ /* description */
+ color_profile->widget_description = gtk_label_new ("");
+ gtk_widget_set_margin_top (color_profile->widget_description, 9);
+ gtk_widget_set_margin_bottom (color_profile->widget_description, 9);
+ gtk_widget_set_halign (color_profile->widget_description, GTK_ALIGN_START);
+ gtk_label_set_ellipsize (GTK_LABEL (color_profile->widget_description), PANGO_ELLIPSIZE_END);
+ gtk_label_set_xalign (GTK_LABEL (color_profile->widget_description), 0);
+ gtk_widget_set_hexpand (color_profile->widget_description, TRUE);
+ gtk_widget_set_vexpand (color_profile->widget_description, TRUE);
+ gtk_box_append (GTK_BOX (box), color_profile->widget_description);
+
+ /* profile warnings/info */
+ color_profile->widget_info = gtk_image_new_from_icon_name ("dialog-information-symbolic");
+ gtk_widget_set_margin_start (color_profile->widget_info, IMAGE_WIDGET_PADDING);
+ gtk_widget_set_margin_end (color_profile->widget_info, IMAGE_WIDGET_PADDING);
+ gtk_box_append (GTK_BOX (box), color_profile->widget_info);
+
+ /* refresh */
+ gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (color_profile), box);
+}
+
+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..c749add
--- /dev/null
+++ b/panels/color/cc-color-profile.h
@@ -0,0 +1,43 @@
+/* -*- 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.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <colord.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_COLOR_PROFILE (cc_color_profile_get_type ())
+G_DECLARE_FINAL_TYPE (CcColorProfile, cc_color_profile, CC, COLOR_PROFILE, GtkListBoxRow)
+
+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
+
diff --git a/panels/color/color.gresource.xml b/panels/color/color.gresource.xml
new file mode 100644
index 0000000..04cf9b3
--- /dev/null
+++ b/panels/color/color.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+ <gresource prefix="/org/gnome/control-center/color">
+ <file preprocess="xml-stripblanks">cc-color-calibrate.ui</file>
+ <file preprocess="xml-stripblanks">cc-color-panel.ui</file>
+ </gresource>
+</gresources>
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..75e3f2c
--- /dev/null
+++ b/panels/color/gnome-color-panel.desktop.in.in
@@ -0,0 +1,20 @@
+[Desktop Entry]
+Name=Color
+Comment=Calibrate the color of your devices, such as displays, cameras or printers
+Exec=gnome-control-center color
+# Translators: Do NOT translate or transliterate this text (this is an icon file name)!
+Icon=org.gnome.Settings-color-symbolic
+Terminal=false
+Type=Application
+NoDisplay=true
+StartupNotify=true
+Categories=GNOME;GTK;Settings;X-GNOME-Settings-Panel;HardwareSettings;X-GNOME-DevicesSettings;
+OnlyShowIn=GNOME;Unity;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-control-center
+X-GNOME-Bugzilla-Component=color
+X-GNOME-Bugzilla-Version=@VERSION@
+# Translators: Search terms to find the Color panel. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!
+Keywords=Color;ICC;Profile;Calibrate;Printer;Display;
+# Notifications are emitted by gnome-settings-daemon
+X-GNOME-UsesNotifications=true
diff --git a/panels/color/icons/meson.build b/panels/color/icons/meson.build
new file mode 100644
index 0000000..5dfae21
--- /dev/null
+++ b/panels/color/icons/meson.build
@@ -0,0 +1,4 @@
+install_data(
+ 'scalable/org.gnome.Settings-color-symbolic.svg',
+ install_dir: join_paths(control_center_icondir, 'hicolor', 'scalable', 'apps')
+)
diff --git a/panels/color/icons/scalable/org.gnome.Settings-color-symbolic.svg b/panels/color/icons/scalable/org.gnome.Settings-color-symbolic.svg
new file mode 100644
index 0000000..9c1ec0c
--- /dev/null
+++ b/panels/color/icons/scalable/org.gnome.Settings-color-symbolic.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#2e3434">
+ <path d="m 8.085938 0.015625 c -2.203126 0 -4 1.796875 -4 4 s 1.796874 4 4 4 c 2.203124 0 4 -1.796875 4 -4 s -1.796876 -4 -4 -4 z m 0 1.972656 c 1.121093 -0.003906 2.03125 0.90625 2.027343 2.027344 c 0.003907 1.121094 -0.90625 2.03125 -2.027343 2.027344 c -1.121094 0.003906 -2.027344 -0.90625 -2.027344 -2.027344 s 0.90625 -2.03125 2.027344 -2.027344 z m 0 0"/>
+ <path d="m 4.070312 7.015625 c -2.21875 0 -4.0312495 1.8125 -4.0312495 4.03125 s 1.8124995 4.03125 4.0312495 4.03125 c 2.222657 0 4.03125 -1.8125 4.03125 -4.03125 s -1.808593 -4.03125 -4.03125 -4.03125 z m 0 1.988281 c 1.132813 -0.003906 2.046876 0.914063 2.042969 2.042969 c 0.003907 1.128906 -0.910156 2.046875 -2.042969 2.042969 c -1.128906 0.003906 -2.042968 -0.914063 -2.042968 -2.042969 s 0.914062 -2.046875 2.042968 -2.042969 z m 0 0"/>
+ <path d="m 15.992188 11 c 0 2.207031 -1.789063 4 -4 4 c -2.207032 0 -4 -1.789062 -4 -4 s 1.792968 -4 4 -4 c 2.210937 0 4 1.792969 4 4 z m 0 0"/>
+ <path d="m 6.898438 11 c 0 1.554688 -1.257813 2.8125 -2.8125 2.8125 c -1.550782 0 -2.8125 -1.257812 -2.8125 -2.8125 s 1.261718 -2.8125 2.8125 -2.8125 c 1.554687 0 2.8125 1.257812 2.8125 2.8125 z m 0 0" fill-opacity="0.5"/>
+ </g>
+</svg>
diff --git a/panels/color/meson.build b/panels/color/meson.build
new file mode 100644
index 0000000..9368ae9
--- /dev/null
+++ b/panels/color/meson.build
@@ -0,0 +1,56 @@
+panels_list += cappletname
+desktop = 'gnome-@0@-panel.desktop'.format(cappletname)
+
+desktop_in = configure_file(
+ input: desktop + '.in.in',
+ output: desktop + '.in',
+ configuration: desktop_conf
+)
+
+i18n.merge_file(
+ type: 'desktop',
+ input: desktop_in,
+ output: desktop,
+ po_dir: po_dir,
+ install: true,
+ install_dir: control_center_desktopdir
+)
+
+sources = files(
+ 'cc-color-panel.c',
+ 'cc-color-calibrate.c',
+ 'cc-color-cell-renderer-text.c',
+ 'cc-color-common.c',
+ 'cc-color-device.c',
+ 'cc-color-profile.c'
+)
+
+resource_data = files(
+ 'cc-color-calibrate.ui',
+ 'cc-color-panel.ui',
+)
+
+sources += gnome.compile_resources(
+ 'cc-' + cappletname + '-resources',
+ cappletname + '.gresource.xml',
+ c_name: 'cc_' + cappletname,
+ dependencies: resource_data,
+ export: true
+)
+
+deps = common_deps + [
+ colord_dep,
+ gnome_rr_dep,
+ m_dep,
+ dependency('colord-gtk4', version: '>= 0.1.24'),
+]
+
+panels_libs += static_library(
+ cappletname,
+ sources: sources,
+ include_directories: [ top_inc, common_inc ],
+ dependencies: deps,
+ c_args: cflags
+)
+
+subdir('icons')