From ae1c76ff830d146d41e88d6fba724c0a54bce868 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:45:20 +0200 Subject: Adding upstream version 1:43.6. Signed-off-by: Daniel Baumann --- panels/color/cc-color-calibrate.c | 993 +++++++++ panels/color/cc-color-calibrate.h | 54 + panels/color/cc-color-calibrate.ui | 89 + panels/color/cc-color-cell-renderer-text.c | 132 ++ panels/color/cc-color-cell-renderer-text.h | 32 + panels/color/cc-color-common.c | 116 + panels/color/cc-color-common.h | 32 + panels/color/cc-color-device.c | 309 +++ panels/color/cc-color-device.h | 38 + panels/color/cc-color-panel.c | 2208 ++++++++++++++++++++ panels/color/cc-color-panel.h | 30 + panels/color/cc-color-panel.ui | 707 +++++++ panels/color/cc-color-profile.c | 475 +++++ panels/color/cc-color-profile.h | 43 + panels/color/color.gresource.xml | 7 + panels/color/gnome-color-panel.desktop.in.in | 20 + panels/color/icons/meson.build | 4 + .../scalable/org.gnome.Settings-color-symbolic.svg | 9 + panels/color/meson.build | 56 + 19 files changed, 5354 insertions(+) create mode 100644 panels/color/cc-color-calibrate.c create mode 100644 panels/color/cc-color-calibrate.h create mode 100644 panels/color/cc-color-calibrate.ui create mode 100644 panels/color/cc-color-cell-renderer-text.c create mode 100644 panels/color/cc-color-cell-renderer-text.h create mode 100644 panels/color/cc-color-common.c create mode 100644 panels/color/cc-color-common.h create mode 100644 panels/color/cc-color-device.c create mode 100644 panels/color/cc-color-device.h create mode 100644 panels/color/cc-color-panel.c create mode 100644 panels/color/cc-color-panel.h create mode 100644 panels/color/cc-color-panel.ui create mode 100644 panels/color/cc-color-profile.c create mode 100644 panels/color/cc-color-profile.h create mode 100644 panels/color/color.gresource.xml create mode 100644 panels/color/gnome-color-panel.desktop.in.in create mode 100644 panels/color/icons/meson.build create mode 100644 panels/color/icons/scalable/org.gnome.Settings-color-symbolic.svg create mode 100644 panels/color/meson.build (limited to 'panels/color') 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 + * + * 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 +#include +#include +#include +#include +#include + +#define GNOME_DESKTOP_USE_UNSTABLE_API +#include + +#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 + * + * 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 +#include +#include + +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 @@ + + + + + 32 + 32 + 12 + 12 + Display Calibration + False + + + + vertical + 12 + + + + + _Cancel + True + + + + + _Start + True + + + + + _Resume + True + + + + + _Done + True + + + + + + + center + center + True + True + vertical + 15 + + + end + 192 + address-book-new + + + + + start + Do not disturb the calibration device while in progress + center + True + + + + + + + + + + + + + + + + button_cancel + button_start + button_resume + button_done + + + + 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 + * + * 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 + +#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 + * + * 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 + +#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 + * + * 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 + +#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 + * + * 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 + +#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 + * + * 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 +#include + +#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 + * + * 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 +#include + +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 + * + * 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 . + * + */ + +#include + +#include +#include +#include +#include + +#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 + * + * 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 . + * + */ + +#pragma once + +#include + +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 @@ + + + + Screen Calibration + False + True + True + True + + + + Calibration Quality + True + + + vertical + 9 + + + 0 + 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. + True + + + + + 0 + You will not be able to use your computer while calibration takes place. + True + + + + + 12 + + + True + True + 0 + Quality + + + + + + True + True + 1 + Approximate Time + + + + + + + + never + never + + + liststore_calib_quality + False + 0 + + + + + + + + + + + + + + + Calibration Device + + + vertical + 9 + + + 0 + Select the sensor device you want to use for calibration. + True + + + + + never + never + + + liststore_calib_sensor + False + 1 + + + + + + + + + + + + + + + Display Type + + + vertical + 9 + + + 0 + Select the type of display that is connected. + True + + + + + never + never + + + liststore_calib_kind + False + 0 + + + + + + + + + + + + + + + Profile Whitepoint + + + vertical + 9 + + + 0 + Select a display target white point. Most displays should be calibrated to a D65 illuminant. + True + + + + + never + never + + + liststore_calib_temp + False + 0 + + + + + + + + + + + + + + + Display Brightness + + + vertical + 9 + + + 0 + Please set the display to a brightness that is typical for you. Color management will be most accurate at this brightness level. + True + + + + + 0 + Alternatively, you can use the brightness level used with one of the other profiles for this device. + True + + + + + + + + + + confirm + Profile Name + + + vertical + 9 + + + 0 + You can use a color profile on different computers, or even create profiles for different lighting conditions. + True + + + + + 0 + Profile Name: + True + + + + + + + True + True + + + + + + + + + + summary + Summary + + + vertical + 9 + + + 0 + Profile successfully created! + True + + + + + center + 6 + + + start + + + 6 + 6 + 6 + 6 + 9 + + + 48 + folder-symbolic + + + + + vertical + 3 + + + 0 + Copy profile + + + + + 0 + Requires writable media + + + + + + + + + + + + + + + + 0 + You may find these instructions on how to use the profile on <a href="linux">GNU/Linux</a>, <a href="osx">Apple OS X</a> and <a href="windows">Microsoft Windows</a> systems useful. + True + True + + + + + + + + + + + end + 6 + + + + + + Add Profile + False + True + True + gnome-color-manager + 1 + True + + + vertical + 6 + + + never + GTK_POLICY_NEVER + 300 + + + liststore_assign + False + False + 0 + + + + + + + + + + 6 + 6 + 6 + 6 + True + True + 0 + Problems detected. The profile may not work correctly. <a href="">Show details.</a> + True + True + + + + + + + + + _Import File… + True + + + + + _Add + True + + + + + + + button_assign_import + button_assign_ok + + + + + + + + + + + + + + + + + + + + + + + + + + + LCD + 1 + False + + + LED + 8 + False + + + CRT + 2 + False + + + Projector + 5 + False + + + Plasma + 9 + False + + + LCD (CCFL backlight) + 10 + False + + + LCD (RGB LED backlight) + 11 + False + + + LCD (white LED backlight) + 12 + False + + + Wide gamut LCD (CCFL backlight) + 13 + False + + + Wide gamut LCD (RGB LED backlight) + 14 + False + + + + + + + + + + + + + + + High + 40 minutes + 2 + + + Medium + 30 minutes + 1 + + + Low + 15 minutes + 0 + + + + + + + + + + + + + + + + + + + + + Native to display + 0 + + + D50 (Printing and publishing) + 5000 + + + D55 + 5500 + + + D65 (Photography and graphics) + 6500 + + + D75 + 7500 + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + * + * 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 +#include + +#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 + * + * 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 +#include + +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 @@ + + + + cc-color-calibrate.ui + cc-color-panel.ui + + 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 @@ + + + + + + + + + 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') -- cgit v1.2.3