/* -*- 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include "cc-color-panel.h" #include "cc-color-resources.h" #define WID(b, w) (GtkWidget *) gtk_builder_get_object (b, w) CC_PANEL_REGISTER (CcColorPanel, cc_color_panel) #define COLOR_PANEL_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_COLOR_PANEL, CcColorPanelPrivate)) struct _CcColorPanelPrivate { CdClient *client; CdDevice *current_device; GPtrArray *sensors; GCancellable *cancellable; GDBusProxy *proxy; GSettings *settings; GtkBuilder *builder; GtkTreeStore *list_store_devices; GtkWidget *main_window; }; enum { GCM_PREFS_COLUMN_DEVICE_PATH, GCM_PREFS_COLUMN_SORT, GCM_PREFS_COLUMN_ICON, GCM_PREFS_COLUMN_TITLE, GCM_PREFS_COLUMN_DEVICE, GCM_PREFS_COLUMN_PROFILE, GCM_PREFS_COLUMN_STATUS, GCM_PREFS_COLUMN_STATUS_IMAGE, GCM_PREFS_COLUMN_TOOLTIP, GCM_PREFS_COLUMN_RADIO_ACTIVE, GCM_PREFS_COLUMN_RADIO_VISIBLE, GCM_PREFS_COLUMN_NUM_COLUMNS }; 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 }; #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_combobox_add_profile (CcColorPanel *prefs, CdProfile *profile, GtkTreeIter *iter) { const gchar *id; GtkTreeIter iter_tmp; GString *string; GtkListStore *list_store; 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); list_store = GTK_LIST_STORE(gtk_builder_get_object (prefs->priv->builder, "liststore_assign")); gtk_list_store_append (list_store, iter); gtk_list_store_set (list_store, 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); g_string_free (string, TRUE); } static void gcm_prefs_default_cb (GtkWidget *widget, CcColorPanel *prefs) { CdProfile *profile; gboolean ret; GError *error = NULL; CcColorPanelPrivate *priv = prefs->priv; /* TODO: check if the profile is already systemwide */ profile = cd_device_get_default_profile (priv->current_device); if (profile == NULL) goto out; /* install somewhere out of $HOME */ ret = cd_profile_install_system_wide_sync (profile, priv->cancellable, &error); if (!ret) { g_warning ("failed to set profile system-wide: %s", error->message); g_error_free (error); goto out; } out: if (profile != NULL) g_object_unref (profile); } static void gcm_prefs_treeview_popup_menu (CcColorPanel *prefs, GtkWidget *treeview) { GtkWidget *menu, *menuitem; menu = gtk_menu_new (); /* TRANSLATORS: this is when the profile should be set for all users */ menuitem = gtk_menu_item_new_with_label (_("Set for all users")); g_signal_connect (menuitem, "activate", G_CALLBACK (gcm_prefs_default_cb), prefs); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem); gtk_widget_show_all (menu); /* Note: gdk_event_get_time() accepts a NULL argument */ gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, gdk_event_get_time (NULL)); } static gboolean gcm_prefs_treeview_popup_menu_cb (GtkWidget *treeview, CcColorPanel *prefs) { if (prefs->priv->current_device == NULL) return FALSE; gcm_prefs_treeview_popup_menu (prefs, treeview); return TRUE; /* we handled this */ } static GFile * gcm_prefs_file_chooser_get_icc_profile (CcColorPanel *prefs) { GtkWindow *window; GtkWidget *dialog; GFile *file = NULL; GtkFileFilter *filter; CcColorPanelPrivate *priv = prefs->priv; /* create new dialog */ window = GTK_WINDOW(gtk_builder_get_object (priv->builder, "dialog_assign")); /* TRANSLATORS: an ICC profile is a file containing colorspace data */ dialog = gtk_file_chooser_dialog_new (_("Select ICC Profile File"), window, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, _("_Import"), GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(dialog), g_get_home_dir ()); gtk_file_chooser_set_create_folders (GTK_FILE_CHOOSER(dialog), FALSE); gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER(dialog), FALSE); /* setup the filter */ filter = gtk_file_filter_new (); gtk_file_filter_add_mime_type (filter, "application/vnd.iccprofile"); /* TRANSLATORS: filter name on the file->open dialog */ gtk_file_filter_set_name (filter, _("Supported ICC profiles")); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter); /* setup the all files filter */ filter = gtk_file_filter_new (); gtk_file_filter_add_pattern (filter, "*"); /* TRANSLATORS: filter name on the file->open dialog */ gtk_file_filter_set_name (filter, _("All files")); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter); /* did user choose file */ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER(dialog)); /* we're done */ gtk_widget_destroy (dialog); /* or NULL for missing */ return file; } static void gcm_prefs_calibrate_cb (GtkWidget *widget, CcColorPanel *prefs) { gboolean ret; GError *error = NULL; guint xid; GPtrArray *argv; CcColorPanelPrivate *priv = prefs->priv; /* get xid */ xid = gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (priv->main_window))); /* run with modal set */ argv = g_ptr_array_new_with_free_func (g_free); g_ptr_array_add (argv, g_build_filename (BINDIR, "gcm-calibrate", NULL)); g_ptr_array_add (argv, g_strdup ("--device")); g_ptr_array_add (argv, g_strdup (cd_device_get_id (priv->current_device))); g_ptr_array_add (argv, g_strdup ("--parent-window")); g_ptr_array_add (argv, g_strdup_printf ("%i", xid)); g_ptr_array_add (argv, NULL); ret = g_spawn_async (NULL, (gchar**) argv->pdata, NULL, 0, NULL, NULL, NULL, &error); if (!ret) { g_warning ("failed to run calibrate: %s", error->message); g_error_free (error); } g_ptr_array_unref (argv); } static gboolean gcm_prefs_is_profile_suitable_for_device (CdProfile *profile, CdDevice *device) { #if CD_CHECK_VERSION(0,1,14) const gchar *data_source; #endif CdProfileKind profile_kind_tmp; CdProfileKind profile_kind; CdColorspace profile_colorspace; CdColorspace device_colorspace = 0; gboolean ret = FALSE; CdDeviceKind device_kind; /* not the right colorspace */ device_colorspace = cd_device_get_colorspace (device); profile_colorspace = cd_profile_get_colorspace (profile); if (device_colorspace != profile_colorspace) goto out; /* not the correct kind */ device_kind = cd_device_get_kind (device); profile_kind_tmp = cd_profile_get_kind (profile); profile_kind = cd_device_kind_to_profile_kind (device_kind); if (profile_kind_tmp != profile_kind) goto out; #if CD_CHECK_VERSION(0,1,14) /* 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; #endif /* success */ ret = TRUE; out: return ret; } static gint gcm_prefs_combo_sort_func_cb (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data) { gint type_a, type_b; gchar *text_a; gchar *text_b; gint retval; /* get data from model */ gtk_tree_model_get (model, a, GCM_PREFS_COMBO_COLUMN_TYPE, &type_a, GCM_PREFS_COMBO_COLUMN_TEXT, &text_a, -1); gtk_tree_model_get (model, b, GCM_PREFS_COMBO_COLUMN_TYPE, &type_b, GCM_PREFS_COMBO_COLUMN_TEXT, &text_b, -1); /* prefer normal type profiles over the 'Other Profile...' entry */ if (type_a < type_b) retval = -1; else if (type_a > type_b) retval = 1; else retval = g_strcmp0 (text_a, text_b); g_free (text_a); g_free (text_b); return retval; } static 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; GError *error = NULL; GPtrArray *profile_array = NULL; GtkTreeIter iter; GtkListStore *list_store; guint i; CcColorPanelPrivate *priv = prefs->priv; list_store = GTK_LIST_STORE(gtk_builder_get_object (prefs->priv->builder, "liststore_assign")); gtk_list_store_clear (list_store); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (list_store), GCM_PREFS_COMBO_COLUMN_TEXT, GTK_SORT_ASCENDING); gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (list_store), GCM_PREFS_COMBO_COLUMN_TEXT, gcm_prefs_combo_sort_func_cb, list_store, NULL); /* get profiles */ profile_array = cd_client_get_profiles_sync (priv->client, priv->cancellable, &error); if (profile_array == NULL) { g_warning ("failed to get profiles: %s", error->message); g_error_free (error); goto out; } /* add profiles of the right kind */ for (i = 0; i < profile_array->len; i++) { profile_tmp = g_ptr_array_index (profile_array, i); /* get properties */ ret = cd_profile_connect_sync (profile_tmp, priv->cancellable, &error); if (!ret) { g_warning ("failed to get profile: %s", error->message); g_error_free (error); goto out; } /* 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, priv->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); } out: if (profile_array != NULL) g_ptr_array_unref (profile_array); } static void gcm_prefs_profile_add_cb (GtkWidget *widget, CcColorPanel *prefs) { GPtrArray *profiles; CcColorPanelPrivate *priv = prefs->priv; /* add profiles of the right kind */ profiles = cd_device_get_profiles (priv->current_device); gcm_prefs_add_profiles_suitable_for_devices (prefs, profiles); /* make insensitve until we have a selection */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_assign_ok")); gtk_widget_set_sensitive (widget, FALSE); /* show the dialog */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_assign")); gtk_widget_show (widget); gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (priv->main_window)); if (profiles != NULL) g_ptr_array_unref (profiles); } static void gcm_prefs_profile_remove_cb (GtkWidget *widget, CcColorPanel *prefs) { GtkTreeIter iter; GtkTreeSelection *selection; GtkTreeModel *model; gboolean ret = FALSE; CdProfile *profile = NULL; GError *error = NULL; CcColorPanelPrivate *priv = prefs->priv; /* get the selected row */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_devices")); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) goto out; /* if the profile is default, then we'll have to make the first profile default */ gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_PROFILE, &profile, -1); /* just remove it, the list store will get ::changed */ ret = cd_device_remove_profile_sync (priv->current_device, profile, priv->cancellable, &error); if (!ret) { g_warning ("failed to remove profile: %s", error->message); g_error_free (error); goto out; } out: if (profile != NULL) g_object_unref (profile); return; } static void gcm_prefs_make_profile_default_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CdDevice *device = CD_DEVICE (object); gboolean ret = FALSE; 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); g_error_free (error); } } static void gcm_prefs_profile_make_default_internal (CcColorPanel *prefs, GtkTreeModel *model, GtkTreeIter *iter_selected) { CdDevice *device; CdProfile *profile; CcColorPanelPrivate *priv = prefs->priv; /* get currentlt selected item */ gtk_tree_model_get (model, iter_selected, GCM_PREFS_COLUMN_DEVICE, &device, GCM_PREFS_COLUMN_PROFILE, &profile, -1); if (profile == NULL || device == NULL) goto out; /* just set it default */ g_debug ("setting %s default on %s", cd_profile_get_id (profile), cd_device_get_id (device)); cd_device_make_profile_default (device, profile, priv->cancellable, gcm_prefs_make_profile_default_cb, prefs); out: if (profile != NULL) g_object_unref (profile); if (device != NULL) g_object_unref (device); } static void gcm_prefs_profile_view (CcColorPanel *prefs, CdProfile *profile) { GPtrArray *argv = NULL; guint xid; gboolean ret; GError *error = NULL; CcColorPanelPrivate *priv = prefs->priv; /* get xid */ xid = gdk_x11_window_get_xid (gtk_widget_get_window (GTK_WIDGET (priv->main_window))); /* open up gcm-viewer as a info pane */ argv = g_ptr_array_new_with_free_func (g_free); g_ptr_array_add (argv, g_build_filename (BINDIR, "gcm-viewer", NULL)); g_ptr_array_add (argv, g_strdup ("--profile")); g_ptr_array_add (argv, g_strdup (cd_profile_get_id (profile))); g_ptr_array_add (argv, g_strdup ("--parent-window")); g_ptr_array_add (argv, g_strdup_printf ("%i", xid)); g_ptr_array_add (argv, NULL); ret = g_spawn_async (NULL, (gchar**) argv->pdata, NULL, 0, NULL, NULL, NULL, &error); if (!ret) { g_warning ("failed to run calibrate: %s", error->message); g_error_free (error); } g_ptr_array_unref (argv); } static void gcm_prefs_profile_assign_link_activate_cb (GtkLabel *label, const gchar *uri, CcColorPanel *prefs) { GtkTreeIter iter; GtkTreeModel *model; CdProfile *profile = NULL; GtkTreeSelection *selection; CcColorPanelPrivate *priv = prefs->priv; GtkWidget *widget; /* get the selected profile */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_assign")); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) goto out; gtk_tree_model_get (model, &iter, GCM_PREFS_COMBO_COLUMN_PROFILE, &profile, -1); if (profile == NULL) { g_warning ("failed to get the active profile"); goto out; } /* show it in the viewer */ gcm_prefs_profile_view (prefs, profile); out: if (profile != NULL) g_object_unref (profile); } static void gcm_prefs_profile_view_cb (GtkWidget *widget, CcColorPanel *prefs) { CdProfile *profile = NULL; GtkTreeIter iter; GtkTreeModel *model; GtkTreeSelection *selection; CcColorPanelPrivate *priv = prefs->priv; /* get the selected row */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_devices")); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) g_assert_not_reached (); /* get currentlt selected item */ gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_PROFILE, &profile, -1); /* open up gcm-viewer as a info pane */ gcm_prefs_profile_view (prefs, profile); g_object_unref (profile); } static void gcm_prefs_button_assign_cancel_cb (GtkWidget *widget, CcColorPanel *prefs) { CcColorPanelPrivate *priv = prefs->priv; widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_assign")); gtk_widget_hide (widget); } static void gcm_prefs_button_assign_ok_cb (GtkWidget *widget, CcColorPanel *prefs) { GtkTreeIter iter; GtkTreeModel *model; CdProfile *profile = NULL; gboolean ret = FALSE; GError *error = NULL; GtkTreeSelection *selection; CcColorPanelPrivate *priv = prefs->priv; /* hide window */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_assign")); gtk_widget_hide (widget); /* get the selected profile */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_assign")); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); if (!gtk_tree_selection_get_selected (selection, &model, &iter)) goto out; gtk_tree_model_get (model, &iter, GCM_PREFS_COMBO_COLUMN_PROFILE, &profile, -1); if (profile == NULL) { g_warning ("failed to get the active profile"); goto out; } /* just add it, the list store will get ::changed */ ret = cd_device_add_profile_sync (priv->current_device, CD_DEVICE_RELATION_HARD, profile, priv->cancellable, &error); if (!ret) { g_warning ("failed to add: %s", error->message); g_error_free (error); goto out; } /* make it default */ cd_device_make_profile_default (priv->current_device, profile, priv->cancellable, gcm_prefs_make_profile_default_cb, prefs); out: if (profile != NULL) g_object_unref (profile); } static gboolean gcm_prefs_profile_delete_event_cb (GtkWidget *widget, GdkEvent *event, CcColorPanel *prefs) { gcm_prefs_button_assign_cancel_cb (widget, prefs); return TRUE; } static void gcm_prefs_treeview_renderer_toggled (GtkCellRendererToggle *cell, const gchar *path, CcColorPanel *prefs) { gboolean ret; GtkTreeModel *model; GtkTreeIter iter; CcColorPanelPrivate *priv = prefs->priv; model = GTK_TREE_MODEL (priv->list_store_devices); ret = gtk_tree_model_get_iter_from_string (model, &iter, path); if (!ret) return; gcm_prefs_profile_make_default_internal (prefs, model, &iter); } static void gcm_prefs_add_devices_columns (CcColorPanel *prefs, GtkTreeView *treeview) { GtkCellRenderer *renderer; GtkTreeViewColumn *column; CcColorPanelPrivate *priv = prefs->priv; gtk_tree_view_set_headers_visible (treeview, TRUE); /* --- column for device image and device title --- */ column = gtk_tree_view_column_new (); gtk_tree_view_column_set_expand (column, TRUE); /* TRANSLATORS: column for device list */ gtk_tree_view_column_set_title (column, _("Device")); /* image */ renderer = gtk_cell_renderer_pixbuf_new (); g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_add_attribute (column, renderer, "icon-name", GCM_PREFS_COLUMN_ICON); /* option */ renderer = gtk_cell_renderer_toggle_new (); g_signal_connect (renderer, "toggled", G_CALLBACK (gcm_prefs_treeview_renderer_toggled), prefs); g_object_set (renderer, "radio", TRUE, NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_add_attribute (column, renderer, "active", GCM_PREFS_COLUMN_RADIO_ACTIVE); gtk_tree_view_column_add_attribute (column, renderer, "visible", GCM_PREFS_COLUMN_RADIO_VISIBLE); /* text */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_add_attribute (column, renderer, "markup", GCM_PREFS_COLUMN_TITLE); gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->list_store_devices), GCM_PREFS_COLUMN_SORT, GTK_SORT_DESCENDING); gtk_tree_view_append_column (treeview, GTK_TREE_VIEW_COLUMN(column)); /* --- column for device status --- */ column = gtk_tree_view_column_new (); gtk_tree_view_column_set_expand (column, TRUE); /* TRANSLATORS: column for device list */ gtk_tree_view_column_set_title (column, _("Calibration")); /* image */ renderer = gtk_cell_renderer_pixbuf_new (); g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_add_attribute (column, renderer, "icon-name", GCM_PREFS_COLUMN_STATUS_IMAGE); /* text */ renderer = gtk_cell_renderer_text_new (); gtk_tree_view_column_pack_start (column, renderer, TRUE); gtk_tree_view_column_add_attribute (column, renderer, "markup", GCM_PREFS_COLUMN_STATUS); gtk_tree_view_column_set_expand (column, FALSE); gtk_tree_view_append_column (treeview, GTK_TREE_VIEW_COLUMN(column)); /* tooltip */ gtk_tree_view_set_tooltip_column (treeview, GCM_PREFS_COLUMN_TOOLTIP); } static void gcm_prefs_add_profiles_columns (CcColorPanel *prefs, GtkTreeView *treeview) { 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 (); g_object_set (renderer, "stock-size", GTK_ICON_SIZE_MENU, NULL); gtk_tree_view_column_pack_start (column, renderer, FALSE); gtk_tree_view_column_add_attribute (column, renderer, "icon-name", GCM_PREFS_COMBO_COLUMN_WARNING_FILENAME); gtk_tree_view_append_column (treeview, column); } static void gcm_prefs_set_calibrate_button_sensitivity (CcColorPanel *prefs) { gboolean ret = FALSE; GtkWidget *widget; const gchar *tooltip; CdDeviceKind kind; CdSensor *sensor_tmp; CcColorPanelPrivate *priv = prefs->priv; /* TRANSLATORS: this is when the button is sensitive */ tooltip = _("Create a color profile for the selected device"); /* no device selected */ if (priv->current_device == NULL) goto out; /* are we a display */ kind = cd_device_get_kind (priv->current_device); if (kind == CD_DEVICE_KIND_DISPLAY) { /* find whether we have hardware installed */ if (priv->sensors == NULL || priv->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 (priv->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 (priv->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 */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_device_calibrate")); gtk_widget_set_tooltip_text (widget, tooltip); gtk_widget_set_sensitive (widget, ret); } static void gcm_prefs_device_clicked (CcColorPanel *prefs, CdDevice *device) { CcColorPanelPrivate *priv = prefs->priv; if (device == NULL) g_assert_not_reached (); /* get current device */ if (priv->current_device != NULL) g_object_unref (priv->current_device); priv->current_device = g_object_ref (device); /* we have a new device */ g_debug ("selected device is: %s", cd_device_get_id (device)); /* can this device calibrate */ gcm_prefs_set_calibrate_button_sensitivity (prefs); } static void gcm_prefs_profile_clicked (CcColorPanel *prefs, CdProfile *profile, CdDevice *device) { GtkWidget *widget; CdDeviceRelation relation; gchar *s; CcColorPanelPrivate *priv = prefs->priv; /* get profile */ g_debug ("selected profile = %s", cd_profile_get_filename (profile)); /* find the profile relationship */ relation = cd_device_get_profile_relation_sync (device, profile, NULL, NULL); /* we can only remove hard relationships */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_remove")); if (relation == CD_DEVICE_RELATION_HARD) { gtk_widget_set_tooltip_text (widget, ""); gtk_widget_set_sensitive (widget, TRUE); } else { /* TRANSLATORS: this is when an auto-added profile cannot be removed */ gtk_widget_set_tooltip_text (widget, _("Cannot remove automatically added profile")); gtk_widget_set_sensitive (widget, FALSE); } /* allow getting profile info */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_view")); if ((s = g_find_program_in_path ("gcm-viewer"))) { gtk_widget_set_sensitive (widget, TRUE); g_free (s); } else gtk_widget_set_sensitive (widget, FALSE); } static void gcm_prefs_profiles_treeview_clicked_cb (GtkTreeSelection *selection, CcColorPanel *prefs) { GtkWidget *widget; GtkTreeModel *model; GtkTreeIter iter; 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 */ widget = GTK_WIDGET (gtk_builder_get_object (prefs->priv->builder, "button_assign_ok")); gtk_widget_set_sensitive (widget, TRUE); /* is the profile faulty */ widget = GTK_WIDGET (gtk_builder_get_object (prefs->priv->builder, "label_assign_warning")); #if CD_CHECK_VERSION(0,1,25) warnings = cd_profile_get_warnings (profile); gtk_widget_set_visible (widget, warnings != NULL && warnings[0] != NULL); #else gtk_widget_set_visible (widget, FALSE); #endif g_object_unref (profile); } static void gcm_prefs_devices_treeview_clicked_cb (GtkTreeSelection *selection, CcColorPanel *prefs) { GtkTreeModel *model; GtkTreeIter iter; CdDevice *device = NULL; CdProfile *profile = NULL; GtkWidget *widget; CcColorPanelPrivate *priv = prefs->priv; /* get selection */ if (!gtk_tree_selection_get_selected (selection, &model, &iter)) return; gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_DEVICE, &device, GCM_PREFS_COLUMN_PROFILE, &profile, -1); /* device actions */ if (device != NULL) gcm_prefs_device_clicked (prefs, device); if (profile != NULL) gcm_prefs_profile_clicked (prefs, profile, device); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_device_default")); gtk_widget_set_visible (widget, profile != NULL); if (profile) gtk_widget_set_sensitive (widget, !cd_profile_get_is_system_wide (profile)); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_device_calibrate")); gtk_widget_set_visible (widget, profile == NULL); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_add")); gtk_widget_set_visible (widget, profile == NULL); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_view")); gtk_widget_set_visible (widget, profile != NULL); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_remove")); gtk_widget_set_visible (widget, profile != NULL); /* if no buttons then hide toolbar */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbar_devices")); gtk_widget_set_visible (widget, profile != NULL || device != NULL); if (device != NULL) g_object_unref (device); if (profile != NULL) g_object_unref (profile); } static void gcm_prefs_treeview_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, CcColorPanel *prefs) { GtkTreeModel *model; GtkTreeIter iter; gboolean ret; CcColorPanelPrivate *priv = prefs->priv; /* get the iter */ model = GTK_TREE_MODEL (priv->list_store_devices); ret = gtk_tree_model_get_iter (model, &iter, path); if (!ret) return; /* make this profile the default */ gcm_prefs_profile_make_default_internal (prefs, model, &iter); } static void gcm_prefs_profiles_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, CcColorPanel *prefs) { GtkTreeIter iter; gboolean ret; ret = gtk_tree_model_get_iter (gtk_tree_view_get_model (tree_view), &iter, path); if (!ret) return; gcm_prefs_button_assign_ok_cb (NULL, prefs); } static const gchar * gcm_prefs_device_kind_to_sort (CdDeviceKind kind) { if (kind == CD_DEVICE_KIND_DISPLAY) return "4"; if (kind == CD_DEVICE_KIND_SCANNER) return "3"; if (kind == CD_DEVICE_KIND_CAMERA) return "2"; if (kind == CD_DEVICE_KIND_PRINTER) return "1"; return "0"; } static gchar * gcm_device_get_title (CdDevice *device) { const gchar *model; const gchar *vendor; GString *string; /* try to get a nice string suitable for display */ vendor = cd_device_get_vendor (device); model = cd_device_get_model (device); string = g_string_new (""); if (vendor != NULL && model != NULL) { g_string_append_printf (string, "%s - %s", vendor, model); goto out; } /* just model */ if (model != NULL) { g_string_append (string, model); goto out; } /* just vendor */ if (vendor != NULL) { g_string_append (string, vendor); goto out; } /* fallback to id */ g_string_append (string, cd_device_get_id (device)); out: return g_string_free (string, FALSE); } static void gcm_prefs_button_assign_import_cb (GtkWidget *widget, CcColorPanel *prefs) { GFile *file = NULL; GError *error = NULL; CdProfile *profile = NULL; CcColorPanelPrivate *priv = prefs->priv; file = gcm_prefs_file_chooser_get_icc_profile (prefs); if (file == NULL) { g_warning ("failed to get ICC file"); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_assign")); gtk_widget_hide (widget); goto out; } #if CD_CHECK_VERSION(0,1,12) profile = cd_client_import_profile_sync (priv->client, file, priv->cancellable, &error); if (profile == NULL) { g_warning ("failed to get imported profile: %s", error->message); g_error_free (error); goto out; } #endif /* add to list view */ gcm_prefs_profile_add_cb (NULL, prefs); out: if (file != NULL) g_object_unref (file); if (profile != NULL) g_object_unref (profile); } static void gcm_prefs_sensor_coldplug (CcColorPanel *prefs) { CcColorPanelPrivate *priv = prefs->priv; CdSensor *sensor_tmp; gboolean ret; GError *error = NULL; GPtrArray *sensors; guint i; /* unref old */ g_clear_pointer (&priv->sensors, g_ptr_array_unref); /* no present */ sensors = cd_client_get_sensors_sync (priv->client, NULL, &error); if (sensors == NULL) { g_warning ("%s", error->message); g_error_free (error); goto out; } if (sensors->len == 0) goto out; /* save a copy of the sensor list */ priv->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); g_error_free (error); goto out; } } out: if (sensors != NULL) g_ptr_array_unref (sensors); } static void gcm_prefs_client_sensor_changed_cb (CdClient *client, CdSensor *sensor, CcColorPanel *prefs) { gcm_prefs_sensor_coldplug (prefs); gcm_prefs_set_calibrate_button_sensitivity (prefs); } static const gchar * gcm_prefs_device_kind_to_icon_name (CdDeviceKind kind) { switch (kind) { case CD_DEVICE_KIND_DISPLAY: return "video-display"; case CD_DEVICE_KIND_SCANNER: return "scanner"; case CD_DEVICE_KIND_PRINTER: return "printer"; case CD_DEVICE_KIND_CAMERA: return "camera-photo"; case CD_DEVICE_KIND_WEBCAM: return "camera-web"; default: return "image-missing"; } } static GString * gcm_prefs_get_profile_age_as_string (CdProfile *profile) { const gchar *id; gint64 age; GString *string = NULL; if (profile == NULL) { /* TRANSLATORS: this is when there is no profile for the device */ string = g_string_new (_("No profile")); goto out; } /* don't show details for EDID, colorspace or test profiles */ id = cd_profile_get_metadata_item (profile, CD_PROFILE_METADATA_DATA_SOURCE); if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_EDID) == 0) goto out; #if CD_CHECK_VERSION(0,1,14) if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_STANDARD) == 0) goto out; if (g_strcmp0 (id, CD_PROFILE_METADATA_DATA_SOURCE_TEST) == 0) goto out; #endif /* days */ age = cd_profile_get_age (profile); if (age == 0) { string = g_string_new (NULL); goto out; } age /= 60 * 60 * 24; string = g_string_new (""); /* approximate years */ if (age > 365) { age /= 365; g_string_append_printf (string, ngettext ( "%i year", "%i years", age), (guint) age); goto out; } /* approximate months */ if (age > 30) { age /= 30; g_string_append_printf (string, ngettext ( "%i month", "%i months", age), (guint) age); goto out; } /* approximate weeks */ if (age > 7) { age /= 7; g_string_append_printf (string, ngettext ( "%i week", "%i weeks", age), (guint) age); goto out; } /* fallback */ g_string_append_printf (string, _("Less than 1 week")); out: return string; } static gchar * gcm_prefs_get_profile_created_for_sort (CdProfile *profile) { gint64 created; gchar *string = NULL; GDateTime *dt = NULL; /* get profile age */ created = cd_profile_get_created (profile); if (created == 0) goto out; dt = g_date_time_new_from_unix_utc (created); /* note: this is not shown in the UI, just used for sorting */ string = g_date_time_format (dt, "%Y%m%d"); out: if (dt != NULL) g_date_time_unref (dt); return string; } static gchar * gcm_prefs_get_profile_title (CcColorPanel *prefs, CdProfile *profile) { CdColorspace colorspace; const gchar *title; gchar *string; gboolean ret; GError *error = NULL; CcColorPanelPrivate *priv = prefs->priv; g_return_val_if_fail (profile != NULL, NULL); string = NULL; /* get properties */ ret = cd_profile_connect_sync (profile, priv->cancellable, &error); if (!ret) { g_warning ("failed to get profile: %s", error->message); g_error_free (error); goto out; } /* add profile description */ title = cd_profile_get_title (profile); if (title != NULL) { string = g_markup_escape_text (title, -1); goto out; } /* some meta profiles do not have ICC profiles */ colorspace = cd_profile_get_colorspace (profile); if (colorspace == CD_COLORSPACE_RGB) { string = g_strdup (C_("Colorspace fallback", "Default RGB")); goto out; } if (colorspace == CD_COLORSPACE_CMYK) { string = g_strdup (C_("Colorspace fallback", "Default CMYK")); goto out; } if (colorspace == CD_COLORSPACE_GRAY) { string = g_strdup (C_("Colorspace fallback", "Default Gray")); goto out; } /* fall back to ID, ick */ string = g_strdup (cd_profile_get_id (profile)); out: return string; } static void gcm_prefs_device_remove_profiles_phase1 (CcColorPanel *prefs, GtkTreeIter *parent) { GtkTreeIter iter; GtkTreeModel *model; gboolean ret; CcColorPanelPrivate *priv = prefs->priv; /* get first element */ model = GTK_TREE_MODEL (priv->list_store_devices); ret = gtk_tree_model_iter_children (model, &iter, parent); if (!ret) return; /* mark to be removed */ do { gtk_tree_store_set (priv->list_store_devices, &iter, GCM_PREFS_COLUMN_DEVICE_PATH, NULL, -1); } while (gtk_tree_model_iter_next (model, &iter)); } static void gcm_prefs_device_remove_profiles_phase2 (CcColorPanel *prefs, GtkTreeIter *parent) { GtkTreeIter iter; GtkTreeModel *model; gchar *id_tmp; gboolean ret; CcColorPanelPrivate *priv = prefs->priv; /* get first element */ model = GTK_TREE_MODEL (priv->list_store_devices); ret = gtk_tree_model_iter_children (model, &iter, parent); if (!ret) return; /* remove the other elements */ do { gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp, -1); if (id_tmp == NULL) ret = gtk_tree_store_remove (priv->list_store_devices, &iter); else ret = gtk_tree_model_iter_next (model, &iter); g_free (id_tmp); } while (ret); } static GtkTreeIter * get_iter_for_profile (GtkTreeModel *model, CdProfile *profile, GtkTreeIter *parent) { const gchar *id; gboolean ret; GtkTreeIter iter; CdProfile *profile_tmp; /* get first element */ ret = gtk_tree_model_iter_children (model, &iter, parent); if (!ret) return NULL; /* remove the other elements */ id = cd_profile_get_id (profile); while (ret) { gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_PROFILE, &profile_tmp, -1); if (g_strcmp0 (id, cd_profile_get_id (profile_tmp)) == 0) { g_object_unref (profile_tmp); return gtk_tree_iter_copy (&iter); } g_object_unref (profile_tmp); ret = gtk_tree_model_iter_next (model, &iter); } return NULL; } static void gcm_prefs_device_set_model_by_iter (CcColorPanel *prefs, CdDevice *device, GtkTreeIter *iter) { GString *status = NULL; const gchar *status_image = NULL; const gchar *tooltip = NULL; CdProfile *profile = NULL; gint age; GPtrArray *profiles = NULL; CdProfile *profile_tmp; guint i; gchar *title_tmp; GString *date_tmp; gchar *sort_tmp; GtkTreeIter iter_tmp; GtkTreeIter *iter_tmp_p; guint threshold = 0; gboolean ret; GError *error = NULL; CcColorPanelPrivate *priv = prefs->priv; /* set status */ profile = cd_device_get_default_profile (device); if (profile == NULL) { status = g_string_new (_("Uncalibrated")); g_string_prepend (status, ""); g_string_append (status, ""); tooltip = _("This device is not color managed."); goto skip; } /* get properties */ ret = cd_profile_connect_sync (profile, priv->cancellable, &error); if (!ret) { g_warning ("failed to get profile: %s", error->message); g_error_free (error); goto out; } #if CD_CHECK_VERSION(0,1,13) /* ignore profiles from other user accounts */ if (!cd_profile_has_access (profile)) { /* only print the filename if it exists */ if (cd_profile_get_filename (profile) != NULL) { g_warning ("%s is not usable by this user", cd_profile_get_filename (profile)); } else { g_warning ("%s is not usable by this user", cd_profile_get_id (profile)); } goto out; } #endif /* autogenerated printer defaults */ if (cd_device_get_kind (device) == CD_DEVICE_KIND_PRINTER && cd_profile_get_filename (profile) == NULL) { status = g_string_new (_("Uncalibrated")); g_string_prepend (status, ""); g_string_append (status, ""); tooltip = _("This device is using manufacturing calibrated data."); goto skip; } /* autogenerated profiles are crap */ if (cd_profile_get_kind (profile) == CD_PROFILE_KIND_DISPLAY_DEVICE && !cd_profile_get_has_vcgt (profile)) { status = g_string_new (_("Uncalibrated")); g_string_prepend (status, ""); g_string_append (status, ""); tooltip = _("This device does not have a profile suitable for whole-screen color correction."); goto skip; } /* yay! */ status = gcm_prefs_get_profile_age_as_string (profile); if (status == NULL) { status = g_string_new (_("Uncalibrated")); g_string_prepend (status, ""); g_string_append (status, ""); } /* greater than the calibration threshold for the device type */ age = cd_profile_get_age (profile); age /= 60 * 60 * 24; if (cd_device_get_kind (device) == CD_DEVICE_KIND_DISPLAY) { g_settings_get (priv->settings, GCM_SETTINGS_RECALIBRATE_DISPLAY_THRESHOLD, "u", &threshold); } else if (cd_device_get_kind (device) == CD_DEVICE_KIND_DISPLAY) { g_settings_get (priv->settings, GCM_SETTINGS_RECALIBRATE_PRINTER_THRESHOLD, "u", &threshold); } if (threshold > 0 && age > threshold) { status_image = "dialog-warning-symbolic"; tooltip = _("This device has an old profile that may no longer be accurate."); } skip: /* save to store */ gtk_tree_store_set (priv->list_store_devices, iter, GCM_PREFS_COLUMN_STATUS, status->str, GCM_PREFS_COLUMN_STATUS_IMAGE, status_image, GCM_PREFS_COLUMN_TOOLTIP, tooltip, -1); /* remove old profiles */ gcm_prefs_device_remove_profiles_phase1 (prefs, iter); /* add profiles */ profiles = cd_device_get_profiles (device); if (profiles == NULL) goto out; for (i = 0; i < profiles->len; i++) { profile_tmp = g_ptr_array_index (profiles, i); title_tmp = gcm_prefs_get_profile_title (prefs, profile_tmp); /* get profile age */ date_tmp = gcm_prefs_get_profile_age_as_string (profile_tmp); if (date_tmp == NULL) { /* TRANSLATORS: this is when the calibration profile age is not * specified as it has been autogenerated from the hardware */ date_tmp = g_string_new (_("Not specified")); g_string_prepend (date_tmp, ""); g_string_append (date_tmp, ""); } sort_tmp = gcm_prefs_get_profile_created_for_sort (profile_tmp); /* get an existing profile, or create a new one */ iter_tmp_p = get_iter_for_profile (GTK_TREE_MODEL (priv->list_store_devices), profile_tmp, iter); if (iter_tmp_p == NULL) gtk_tree_store_append (priv->list_store_devices, &iter_tmp, iter); gtk_tree_store_set (priv->list_store_devices, iter_tmp_p ? iter_tmp_p : &iter_tmp, GCM_PREFS_COLUMN_DEVICE, device, GCM_PREFS_COLUMN_PROFILE, profile_tmp, GCM_PREFS_COLUMN_DEVICE_PATH, cd_device_get_object_path (device), GCM_PREFS_COLUMN_SORT, sort_tmp, GCM_PREFS_COLUMN_STATUS, date_tmp->str, GCM_PREFS_COLUMN_TITLE, title_tmp, GCM_PREFS_COLUMN_RADIO_VISIBLE, TRUE, GCM_PREFS_COLUMN_RADIO_ACTIVE, i==0, -1); if (iter_tmp_p != NULL) gtk_tree_iter_free (iter_tmp_p); g_free (title_tmp); g_free (sort_tmp); g_string_free (date_tmp, TRUE); } /* remove old profiles that no longer exist */ gcm_prefs_device_remove_profiles_phase2 (prefs, iter); out: if (status != NULL) g_string_free (status, TRUE); if (profiles != NULL) g_ptr_array_unref (profiles); if (profile != NULL) g_object_unref (profile); } static void gcm_prefs_device_changed_cb (CdDevice *device, CcColorPanel *prefs) { const gchar *id; gboolean ret; gchar *id_tmp; GtkTreeIter iter; GtkTreeModel *model; CcColorPanelPrivate *priv = prefs->priv; /* get first element */ model = GTK_TREE_MODEL (priv->list_store_devices); ret = gtk_tree_model_get_iter_first (model, &iter); if (!ret) return; /* get the other elements */ id = cd_device_get_object_path (device); do { gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp, -1); if (g_strcmp0 (id_tmp, id) == 0) { /* populate device */ gcm_prefs_device_set_model_by_iter (prefs, device, &iter); } g_free (id_tmp); } while (gtk_tree_model_iter_next (model, &iter)); } static void gcm_prefs_add_device (CcColorPanel *prefs, CdDevice *device) { gboolean ret; GError *error = NULL; CdDeviceKind kind; const gchar *icon_name; const gchar *id; gchar *sort = NULL; gchar *title = NULL; GtkTreeIter parent; CcColorPanelPrivate *priv = prefs->priv; /* get device properties */ ret = cd_device_connect_sync (device, priv->cancellable, &error); if (!ret) { g_warning ("failed to connect to the device: %s", error->message); g_error_free (error); goto out; } /* get icon */ kind = cd_device_get_kind (device); icon_name = gcm_prefs_device_kind_to_icon_name (kind); /* italic for non-connected devices */ title = gcm_device_get_title (device); /* create sort order */ sort = g_strdup_printf ("%s%s", gcm_prefs_device_kind_to_sort (kind), title); /* watch for changes to update the status icons */ g_signal_connect (device, "changed", G_CALLBACK (gcm_prefs_device_changed_cb), prefs); /* add to list */ id = cd_device_get_object_path (device); g_debug ("add %s to device list", id); gtk_tree_store_append (priv->list_store_devices, &parent, NULL); gtk_tree_store_set (priv->list_store_devices, &parent, GCM_PREFS_COLUMN_DEVICE, device, GCM_PREFS_COLUMN_DEVICE_PATH, id, GCM_PREFS_COLUMN_SORT, sort, GCM_PREFS_COLUMN_TITLE, title, GCM_PREFS_COLUMN_ICON, icon_name, -1); gcm_prefs_device_set_model_by_iter (prefs, device, &parent); out: g_free (sort); g_free (title); } static void gcm_prefs_remove_device (CcColorPanel *prefs, CdDevice *cd_device) { GtkTreeIter iter; GtkTreeModel *model; const gchar *id; gchar *id_tmp; gboolean ret; CdDevice *device_tmp; CcColorPanelPrivate *priv = prefs->priv; /* remove */ id = cd_device_get_object_path (cd_device); /* get first element */ model = GTK_TREE_MODEL (priv->list_store_devices); ret = gtk_tree_model_get_iter_first (model, &iter); if (!ret) return; /* get the other elements */ do { gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp, -1); if (g_strcmp0 (id_tmp, id) == 0) { gtk_tree_model_get (model, &iter, GCM_PREFS_COLUMN_DEVICE, &device_tmp, -1); g_signal_handlers_disconnect_by_func (device_tmp, G_CALLBACK (gcm_prefs_device_changed_cb), prefs); gtk_tree_store_remove (GTK_TREE_STORE (model), &iter); g_free (id_tmp); g_object_unref (device_tmp); break; } g_free (id_tmp); } while (gtk_tree_model_iter_next (model, &iter)); } static void gcm_prefs_update_device_list_extra_entry (CcColorPanel *prefs) { CcColorPanelPrivate *priv = prefs->priv; gboolean ret; gchar *id_tmp; gchar *title = NULL; GtkTreeIter iter; /* select the first device */ ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store_devices), &iter); if (!ret) { /* add the 'No devices detected' entry */ title = g_strdup_printf ("%s", _("No devices supporting color management detected")); gtk_tree_store_append (priv->list_store_devices, &iter, NULL); gtk_tree_store_set (priv->list_store_devices, &iter, GCM_PREFS_COLUMN_RADIO_VISIBLE, FALSE, GCM_PREFS_COLUMN_TITLE, title, -1); g_free (title); return; } /* remove the 'No devices detected' entry */ do { gtk_tree_model_get (GTK_TREE_MODEL (priv->list_store_devices), &iter, GCM_PREFS_COLUMN_DEVICE_PATH, &id_tmp, -1); if (id_tmp == NULL) { gtk_tree_store_remove (priv->list_store_devices, &iter); break; } g_free (id_tmp); } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->list_store_devices), &iter)); } 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_changed_cb (CdClient *client, CdDevice *device, CcColorPanel *prefs) { g_debug ("changed: %s (doing nothing)", cd_device_get_id (device)); } static void gcm_prefs_device_removed_cb (CdClient *client, CdDevice *device, CcColorPanel *prefs) { GtkTreeIter iter; GtkTreeSelection *selection; GtkWidget *widget; gboolean ret; CcColorPanelPrivate *priv = prefs->priv; /* remove from the UI */ gcm_prefs_remove_device (prefs, device); /* ensure we showing the 'No devices detected' entry if required */ gcm_prefs_update_device_list_extra_entry (prefs); /* select the first device */ ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->list_store_devices), &iter); if (!ret) return; /* click it */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_devices")); gtk_tree_view_set_model (GTK_TREE_VIEW (widget), GTK_TREE_MODEL (priv->list_store_devices)); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); gtk_tree_selection_select_iter (selection, &iter); } static gboolean gcm_prefs_tree_model_count_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data) { guint *i = (guint *) user_data; (*i)++; return FALSE; } static void gcm_prefs_get_devices_cb (GObject *object, GAsyncResult *res, gpointer user_data) { CcColorPanel *prefs = (CcColorPanel *) user_data; CdClient *client = CD_CLIENT (object); CdDevice *device; GError *error = NULL; GPtrArray *devices; GtkTreePath *path; GtkWidget *widget; guint i; guint devices_and_profiles = 0; CcColorPanelPrivate *priv = prefs->priv; /* get devices and add them */ devices = cd_client_get_devices_finish (client, res, &error); if (devices == NULL) { g_warning ("failed to add connected devices: %s", error->message); g_error_free (error); goto out; } for (i = 0; i < devices->len; i++) { device = g_ptr_array_index (devices, i); gcm_prefs_add_device (prefs, device); } /* ensure we show the 'No devices detected' entry if empty */ gcm_prefs_update_device_list_extra_entry (prefs); /* set the cursor on the first device */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_devices")); path = gtk_tree_path_new_from_string ("0"); gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), path, NULL, FALSE); gtk_tree_path_free (path); /* if we have only a few devices and profiles expand the treeview * devices so they can all be seen */ gtk_tree_model_foreach (GTK_TREE_MODEL (priv->list_store_devices), gcm_prefs_tree_model_count_cb, &devices_and_profiles); if (devices_and_profiles <= GCM_PREFS_MAX_DEVICES_PROFILES_EXPANDED) gtk_tree_view_expand_all (GTK_TREE_VIEW (widget)); out: if (devices != NULL) g_ptr_array_unref (devices); } static void gcm_prefs_connect_cb (GObject *object, GAsyncResult *res, gpointer user_data) { gboolean ret; GError *error = NULL; CcColorPanel *prefs = CC_COLOR_PANEL (user_data); CcColorPanelPrivate *priv = prefs->priv; ret = cd_client_connect_finish (priv->client, res, &error); if (!ret) { g_warning ("failed to connect to colord: %s", error->message); g_error_free (error); return; } /* set calibrate button sensitivity */ gcm_prefs_sensor_coldplug (prefs); /* get devices */ cd_client_get_devices (priv->client, priv->cancellable, gcm_prefs_get_devices_cb, prefs); } static void gcm_prefs_window_realize_cb (GtkWidget *widget, CcColorPanel *prefs) { prefs->priv->main_window = gtk_widget_get_toplevel (widget); } 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) { CcColorPanelPrivate *priv = CC_COLOR_PANEL (object)->priv; if (priv->cancellable != NULL) g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->settings); g_clear_object (&priv->cancellable); g_clear_object (&priv->builder); g_clear_object (&priv->client); g_clear_object (&priv->current_device); g_clear_pointer (&priv->sensors, g_ptr_array_unref); 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); CcPanelClass *panel_class = CC_PANEL_CLASS (klass); g_type_class_add_private (klass, sizeof (CcColorPanelPrivate)); 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; } static void cc_color_panel_init (CcColorPanel *prefs) { CcColorPanelPrivate *priv; GError *error = NULL; GtkStyleContext *context; GtkTreeSelection *selection; GtkWidget *widget; priv = prefs->priv = COLOR_PANEL_PRIVATE (prefs); g_resources_register (cc_color_get_resource ()); priv->builder = gtk_builder_new (); gtk_builder_add_from_resource (priv->builder, "/org/gnome/control-center/color/color.ui", &error); if (error != NULL) { g_warning ("Could not load interface file: %s", error->message); g_error_free (error); return; } priv->cancellable = g_cancellable_new (); /* setup defaults */ priv->settings = g_settings_new (GCM_SETTINGS_SCHEMA); /* create list stores */ priv->list_store_devices = gtk_tree_store_new (GCM_PREFS_COLUMN_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, CD_TYPE_DEVICE, CD_TYPE_PROFILE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); /* assign buttons */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_add")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_profile_add_cb), prefs); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_remove")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_profile_remove_cb), prefs); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_profile_view")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_profile_view_cb), prefs); /* href */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "label_assign_warning")); g_signal_connect (widget, "activate-link", G_CALLBACK (gcm_prefs_profile_assign_link_activate_cb), prefs); /* create device tree view */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_devices")); gtk_tree_view_set_model (GTK_TREE_VIEW (widget), GTK_TREE_MODEL (priv->list_store_devices)); gtk_tree_view_set_enable_tree_lines (GTK_TREE_VIEW (widget), TRUE); gtk_tree_view_set_level_indentation (GTK_TREE_VIEW (widget), 0); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); g_signal_connect (selection, "changed", G_CALLBACK (gcm_prefs_devices_treeview_clicked_cb), prefs); g_signal_connect (GTK_TREE_VIEW (widget), "row-activated", G_CALLBACK (gcm_prefs_treeview_row_activated_cb), prefs); g_signal_connect (GTK_TREE_VIEW (widget), "popup-menu", G_CALLBACK (gcm_prefs_treeview_popup_menu_cb), prefs); /* add columns to the tree view */ gcm_prefs_add_devices_columns (prefs, GTK_TREE_VIEW (widget)); /* add columns to profile tree view */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "treeview_assign")); gcm_prefs_add_profiles_columns (prefs, GTK_TREE_VIEW (widget)); selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget)); g_signal_connect (selection, "changed", G_CALLBACK (gcm_prefs_profiles_treeview_clicked_cb), prefs); g_signal_connect (GTK_TREE_VIEW (widget), "row-activated", G_CALLBACK (gcm_prefs_profiles_row_activated_cb), prefs); /* make larger by default */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_assign")); gtk_widget_set_size_request (widget, -1, 250); /* force to be at least ~6 rows high */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_devices")); gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (widget), 200); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_device_default")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_default_cb), prefs); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbutton_device_calibrate")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_calibrate_cb), prefs); /* make devices toolbar sexy */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "scrolledwindow_devices")); context = gtk_widget_get_style_context (widget); gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "toolbar_devices")); context = gtk_widget_get_style_context (widget); gtk_style_context_add_class (context, GTK_STYLE_CLASS_INLINE_TOOLBAR); gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP); /* set up assign dialog */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "dialog_assign")); g_signal_connect (widget, "delete-event", G_CALLBACK (gcm_prefs_profile_delete_event_cb), prefs); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_assign_cancel")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_button_assign_cancel_cb), prefs); widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_assign_ok")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_button_assign_ok_cb), prefs); /* setup icc profiles list */ widget = GTK_WIDGET (gtk_builder_get_object (priv->builder, "button_assign_import")); g_signal_connect (widget, "clicked", G_CALLBACK (gcm_prefs_button_assign_import_cb), prefs); /* use a device client array */ priv->client = cd_client_new (); g_signal_connect (priv->client, "device-added", G_CALLBACK (gcm_prefs_device_added_cb), prefs); g_signal_connect (priv->client, "device-removed", G_CALLBACK (gcm_prefs_device_removed_cb), prefs); g_signal_connect (priv->client, "changed", G_CALLBACK (gcm_prefs_changed_cb), prefs); /* connect to colord */ cd_client_connect (priv->client, priv->cancellable, gcm_prefs_connect_cb, prefs); /* use the color sensor */ g_signal_connect (priv->client, "sensor-added", G_CALLBACK (gcm_prefs_client_sensor_changed_cb), prefs); g_signal_connect (priv->client, "sensor-removed", G_CALLBACK (gcm_prefs_client_sensor_changed_cb), prefs); /* set calibrate button sensitivity */ gcm_prefs_set_calibrate_button_sensitivity (prefs); widget = WID (priv->builder, "dialog-vbox1"); gtk_widget_reparent (widget, (GtkWidget *) prefs); g_signal_connect (widget, "realize", G_CALLBACK (gcm_prefs_window_realize_cb), prefs); }