/* LIBPIKA - The PIKA Library * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball * * pikacolorprofilecombobox.c * Copyright (C) 2007 Sven Neumann * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include "config.h" #include #include #include "libpikabase/pikabase.h" #include "libpikacolor/pikacolor.h" #include "pikawidgetstypes.h" #include "pikacolorprofilechooserdialog.h" #include "pikacolorprofilecombobox.h" #include "pikacolorprofilestore.h" #include "pikacolorprofilestore-private.h" /** * SECTION: pikacolorprofilecombobox * @title: PikaColorProfileComboBox * @short_description: A combo box for selecting color profiles. * * A combo box for selecting color profiles. **/ enum { PROP_0, PROP_DIALOG, PROP_MODEL }; struct _PikaColorProfileComboBoxPrivate { GtkWidget *dialog; GtkTreePath *last_path; }; #define GET_PRIVATE(obj) (((PikaColorProfileComboBox *) (obj))->priv) static void pika_color_profile_combo_box_finalize (GObject *object); static void pika_color_profile_combo_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void pika_color_profile_combo_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void pika_color_profile_combo_box_changed (GtkComboBox *combo); static gboolean pika_color_profile_row_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data); static void pika_color_profile_combo_dialog_response (PikaColorProfileChooserDialog *dialog, gint response, PikaColorProfileComboBox *combo); G_DEFINE_TYPE_WITH_PRIVATE (PikaColorProfileComboBox, pika_color_profile_combo_box, GTK_TYPE_COMBO_BOX) #define parent_class pika_color_profile_combo_box_parent_class static void pika_color_profile_combo_box_class_init (PikaColorProfileComboBoxClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkComboBoxClass *combo_class = GTK_COMBO_BOX_CLASS (klass); object_class->set_property = pika_color_profile_combo_box_set_property; object_class->get_property = pika_color_profile_combo_box_get_property; object_class->finalize = pika_color_profile_combo_box_finalize; combo_class->changed = pika_color_profile_combo_box_changed; /** * PikaColorProfileComboBox:dialog: * * #GtkDialog to present when the user selects the * "Select color profile from disk..." item. * * Since: 2.4 */ g_object_class_install_property (object_class, PROP_DIALOG, g_param_spec_object ("dialog", "Dialog", "The dialog to present when selecting profiles from disk", GTK_TYPE_DIALOG, G_PARAM_CONSTRUCT_ONLY | PIKA_PARAM_READWRITE)); /** * PikaColorProfileComboBox:model: * * Overrides the "model" property of the #GtkComboBox class. * #PikaColorProfileComboBox requires the model to be a * #PikaColorProfileStore. * * Since: 2.4 */ g_object_class_install_property (object_class, PROP_MODEL, g_param_spec_object ("model", "Model", "The profile store used for this combo box", PIKA_TYPE_COLOR_PROFILE_STORE, PIKA_PARAM_READWRITE)); } static void pika_color_profile_combo_box_init (PikaColorProfileComboBox *combo_box) { GtkCellRenderer *cell; combo_box->priv = pika_color_profile_combo_box_get_instance_private (combo_box); cell = gtk_cell_renderer_text_new (); g_object_set (cell, "width-chars", 42, "ellipsize", PANGO_ELLIPSIZE_END, NULL); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell, "text", PIKA_COLOR_PROFILE_STORE_LABEL, NULL); gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box), pika_color_profile_row_separator_func, NULL, NULL); } static void pika_color_profile_combo_box_finalize (GObject *object) { PikaColorProfileComboBoxPrivate *private = GET_PRIVATE (object); if (private->dialog) { if (PIKA_IS_COLOR_PROFILE_CHOOSER_DIALOG (private->dialog)) gtk_widget_destroy (private->dialog); g_object_unref (private->dialog); private->dialog = NULL; } g_clear_pointer (&private->last_path, gtk_tree_path_free); G_OBJECT_CLASS (parent_class)->finalize (object); } static void pika_color_profile_combo_box_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { PikaColorProfileComboBoxPrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_DIALOG: g_return_if_fail (private->dialog == NULL); private->dialog = g_value_dup_object (value); if (PIKA_IS_COLOR_PROFILE_CHOOSER_DIALOG (private->dialog)) g_signal_connect (private->dialog, "response", G_CALLBACK (pika_color_profile_combo_dialog_response), object); break; case PROP_MODEL: gtk_combo_box_set_model (GTK_COMBO_BOX (object), g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_color_profile_combo_box_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { PikaColorProfileComboBoxPrivate *private = GET_PRIVATE (object); switch (property_id) { case PROP_DIALOG: g_value_set_object (value, private->dialog); break; case PROP_MODEL: g_value_set_object (value, gtk_combo_box_get_model (GTK_COMBO_BOX (object))); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void pika_color_profile_combo_box_changed (GtkComboBox *combo) { PikaColorProfileComboBoxPrivate *priv = GET_PRIVATE (combo); GtkTreeModel *model = gtk_combo_box_get_model (combo); GtkTreeIter iter; gint type; if (! gtk_combo_box_get_active_iter (combo, &iter)) return; gtk_tree_model_get (model, &iter, PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type, -1); switch (type) { case PIKA_COLOR_PROFILE_STORE_ITEM_DIALOG: { GtkWidget *parent = gtk_widget_get_toplevel (GTK_WIDGET (combo)); if (GTK_IS_WINDOW (parent)) gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), GTK_WINDOW (parent)); gtk_window_present (GTK_WINDOW (priv->dialog)); if (priv->last_path && gtk_tree_model_get_iter (model, &iter, priv->last_path)) { gtk_combo_box_set_active_iter (combo, &iter); } } break; case PIKA_COLOR_PROFILE_STORE_ITEM_FILE: if (priv->last_path) gtk_tree_path_free (priv->last_path); priv->last_path = gtk_tree_model_get_path (model, &iter); _pika_color_profile_store_history_reorder (PIKA_COLOR_PROFILE_STORE (model), &iter); break; default: break; } } /** * pika_color_profile_combo_box_new: * @dialog: a #GtkDialog to present when the user selects the * "Select color profile from disk..." item * @history: #GFile of the profilerc (or %NULL for no history) * * Create a combo-box widget for selecting color profiles. The combo-box * is populated from the file specified as @history. This filename is * typically created using the following code snippet: * * gchar *history = pika_personal_rc_file ("profilerc"); * * * The recommended @dialog type to use is a #PikaColorProfileChooserDialog. * If a #PikaColorProfileChooserDialog is passed, #PikaColorProfileComboBox * will take complete control over the dialog, which means connecting * a GtkDialog::response() callback by itself, and take care of destroying * the dialog when the combo box is destroyed. * * If another type of @dialog is passed, this has to be implemented * separately. * * See also pika_color_profile_combo_box_new_with_model(). * * Returns: a new #PikaColorProfileComboBox. * * Since: 2.4 **/ GtkWidget * pika_color_profile_combo_box_new (GtkWidget *dialog, GFile *history) { GtkWidget *combo; GtkListStore *store; g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); g_return_val_if_fail (history == NULL || G_IS_FILE (history), NULL); store = pika_color_profile_store_new (history); combo = pika_color_profile_combo_box_new_with_model (dialog, GTK_TREE_MODEL (store)); g_object_unref (store); return combo; } /** * pika_color_profile_combo_box_new_with_model: * @dialog: a #GtkDialog to present when the user selects the * "Select color profile from disk..." item * @model: a #PikaColorProfileStore object * * This constructor is useful when you want to create several * combo-boxes for profile selection that all share the same * #PikaColorProfileStore. This is for example done in the * PIKA Preferences dialog. * * See also pika_color_profile_combo_box_new(). * * Returns: a new #PikaColorProfileComboBox. * * Since: 2.4 **/ GtkWidget * pika_color_profile_combo_box_new_with_model (GtkWidget *dialog, GtkTreeModel *model) { g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL); g_return_val_if_fail (PIKA_IS_COLOR_PROFILE_STORE (model), NULL); return g_object_new (PIKA_TYPE_COLOR_PROFILE_COMBO_BOX, "dialog", dialog, "model", model, NULL); } /** * pika_color_profile_combo_box_add_file: * @combo: a #PikaColorProfileComboBox * @file: file of the profile to add (or %NULL) * @label: label to use for the profile * (may only be %NULL if @file is %NULL) * * This function delegates to the underlying * #PikaColorProfileStore. Please refer to the documentation of * pika_color_profile_store_add_file() for details. * * Since: 2.10 **/ void pika_color_profile_combo_box_add_file (PikaColorProfileComboBox *combo, GFile *file, const gchar *label) { GtkTreeModel *model; g_return_if_fail (PIKA_IS_COLOR_PROFILE_COMBO_BOX (combo)); g_return_if_fail (label != NULL || file == NULL); g_return_if_fail (file == NULL || G_IS_FILE (file)); model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); pika_color_profile_store_add_file (PIKA_COLOR_PROFILE_STORE (model), file, label); } /** * pika_color_profile_combo_box_set_active_file: * @combo: a #PikaColorProfileComboBox * @file: file of the profile to select * @label: label to use when adding a new entry (can be %NULL) * * Selects a color profile from the @combo and makes it the active * item. If the profile is not listed in the @combo, then it is added * with the given @label (or @file in case that @label is %NULL). * * Since: 2.10 **/ void pika_color_profile_combo_box_set_active_file (PikaColorProfileComboBox *combo, GFile *file, const gchar *label) { PikaColorProfile *profile = NULL; GtkTreeModel *model; GtkTreeIter iter; g_return_if_fail (PIKA_IS_COLOR_PROFILE_COMBO_BOX (combo)); g_return_if_fail (file == NULL || G_IS_FILE (file)); model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); if (file && ! (label && *label)) { GError *error = NULL; profile = pika_color_profile_new_from_file (file, &error); if (! profile) { g_message ("%s", error->message); g_clear_error (&error); } else { label = pika_color_profile_get_label (profile); } } if (_pika_color_profile_store_history_add (PIKA_COLOR_PROFILE_STORE (model), file, label, &iter)) { gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter); } if (profile) g_object_unref (profile); } /** * pika_color_profile_combo_box_set_active_profile: * @combo: a #PikaColorProfileComboBox * @profile: a #PikaColorProfile to set * * Selects a color profile from the @combo and makes it the active * item. * * Since: 3.0 **/ void pika_color_profile_combo_box_set_active_profile (PikaColorProfileComboBox *combo, PikaColorProfile *profile) { GtkTreeModel *model; GtkTreeIter iter; g_return_if_fail (PIKA_IS_COLOR_PROFILE_COMBO_BOX (combo)); g_return_if_fail (profile == NULL || PIKA_IS_COLOR_PROFILE (profile)); model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); if (_pika_color_profile_store_history_find_profile (PIKA_COLOR_PROFILE_STORE (model), profile, &iter)) { gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter); } } /** * pika_color_profile_combo_box_get_active_file: * @combo: a #PikaColorProfileComboBox * * Returns: (transfer none): The file of the currently selected * color profile, release using g_object_unref() when it * is not any longer needed. * * Since: 2.10 **/ GFile * pika_color_profile_combo_box_get_active_file (PikaColorProfileComboBox *combo) { GtkTreeModel *model; GtkTreeIter iter; g_return_val_if_fail (PIKA_IS_COLOR_PROFILE_COMBO_BOX (combo), NULL); model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo)); if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) { GFile *file; gint type; gtk_tree_model_get (model, &iter, PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type, PIKA_COLOR_PROFILE_STORE_FILE, &file, -1); if (type == PIKA_COLOR_PROFILE_STORE_ITEM_FILE) return file; if (file) g_object_unref (file); } return NULL; } static gboolean pika_color_profile_row_separator_func (GtkTreeModel *model, GtkTreeIter *iter, gpointer data) { gint type; gtk_tree_model_get (model, iter, PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type, -1); switch (type) { case PIKA_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP: case PIKA_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM: return TRUE; default: return FALSE; } } static void pika_color_profile_combo_dialog_response (PikaColorProfileChooserDialog *dialog, gint response, PikaColorProfileComboBox *combo) { if (response == GTK_RESPONSE_ACCEPT) { GFile *file; file = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)); if (file) { pika_color_profile_combo_box_set_active_file (combo, file, NULL); g_object_unref (file); } } gtk_widget_hide (GTK_WIDGET (dialog)); }