PIKApp/libpikawidgets/pikacolorprofilestore.c

846 lines
26 KiB
C

/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikaprofilestore.c
* Copyright (C) 2004-2008 Sven Neumann <sven@gimp.org>
*
* 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
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <string.h>
#include <gtk/gtk.h>
#include <gegl.h>
#include "libpikabase/pikabase.h"
#include "libpikacolor/pikacolor.h"
#include "libpikaconfig/pikaconfig.h"
#include "pikawidgetstypes.h"
#include "pikacolorprofilestore.h"
#include "pikacolorprofilestore-private.h"
#include "libpika/libpika-intl.h"
/**
* SECTION: pikacolorprofilestore
* @title: PikaColorProfileStore
* @short_description: A #GtkListStore subclass that keep color profiles.
*
* A #GtkListStore subclass that keep color profiles.
**/
#define HISTORY_SIZE 8
enum
{
PROP_0,
PROP_HISTORY
};
struct _PikaColorProfileStorePrivate
{
GFile *history;
};
#define GET_PRIVATE(obj) (((PikaColorProfileStore *) (obj))->priv)
static void pika_color_profile_store_constructed (GObject *object);
static void pika_color_profile_store_dispose (GObject *object);
static void pika_color_profile_store_finalize (GObject *object);
static void pika_color_profile_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_color_profile_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gboolean pika_color_profile_store_history_insert (PikaColorProfileStore *store,
GtkTreeIter *iter,
GFile *file,
const gchar *label,
gint index);
static void pika_color_profile_store_get_separator (PikaColorProfileStore *store,
GtkTreeIter *iter,
gboolean top);
static gboolean pika_color_profile_store_save (PikaColorProfileStore *store,
GFile *file,
GError **error);
static gboolean pika_color_profile_store_load (PikaColorProfileStore *store,
GFile *file,
GError **error);
G_DEFINE_TYPE_WITH_PRIVATE (PikaColorProfileStore, pika_color_profile_store,
GTK_TYPE_LIST_STORE)
#define parent_class pika_color_profile_store_parent_class
static void
pika_color_profile_store_class_init (PikaColorProfileStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_color_profile_store_constructed;
object_class->dispose = pika_color_profile_store_dispose;
object_class->finalize = pika_color_profile_store_finalize;
object_class->set_property = pika_color_profile_store_set_property;
object_class->get_property = pika_color_profile_store_get_property;
/**
* PikaColorProfileStore:history:
*
* #GFile of the color history used to populate the profile store.
*
* Since: 2.4
*/
g_object_class_install_property (object_class,
PROP_HISTORY,
g_param_spec_object ("history",
"History",
"Filen of the color history used to populate the profile store",
G_TYPE_FILE,
G_PARAM_CONSTRUCT_ONLY |
PIKA_PARAM_READWRITE));
}
static void
pika_color_profile_store_init (PikaColorProfileStore *store)
{
GType types[] =
{
G_TYPE_INT, /* PIKA_COLOR_PROFILE_STORE_ITEM_TYPE */
G_TYPE_STRING, /* PIKA_COLOR_PROFILE_STORE_LABEL */
G_TYPE_FILE, /* PIKA_COLOR_PROFILE_STORE_FILE */
G_TYPE_INT /* PIKA_COLOR_PROFILE_STORE_INDEX */
};
store->priv = pika_color_profile_store_get_instance_private (store);
gtk_list_store_set_column_types (GTK_LIST_STORE (store),
G_N_ELEMENTS (types), types);
}
static void
pika_color_profile_store_constructed (GObject *object)
{
PikaColorProfileStore *store = PIKA_COLOR_PROFILE_STORE (object);
PikaColorProfileStorePrivate *private = GET_PRIVATE (store);
GtkTreeIter iter;
G_OBJECT_CLASS (parent_class)->constructed (object);
gtk_list_store_append (GTK_LIST_STORE (store), &iter);
gtk_list_store_set (GTK_LIST_STORE (store), &iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE,
PIKA_COLOR_PROFILE_STORE_ITEM_DIALOG,
PIKA_COLOR_PROFILE_STORE_LABEL,
_("Select color profile from disk..."),
-1);
if (private->history)
pika_color_profile_store_load (store, private->history, NULL);
}
static void
pika_color_profile_store_dispose (GObject *object)
{
PikaColorProfileStore *store = PIKA_COLOR_PROFILE_STORE (object);
PikaColorProfileStorePrivate *private = GET_PRIVATE (store);
if (private->history)
pika_color_profile_store_save (store, private->history, NULL);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
pika_color_profile_store_finalize (GObject *object)
{
PikaColorProfileStorePrivate *private = GET_PRIVATE (object);
g_clear_object (&private->history);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
pika_color_profile_store_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
PikaColorProfileStorePrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_HISTORY:
g_return_if_fail (private->history == NULL);
private->history = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_color_profile_store_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaColorProfileStorePrivate *private = GET_PRIVATE (object);
switch (property_id)
{
case PROP_HISTORY:
g_value_set_object (value, private->history);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* pika_color_profile_store_new:
* @history: #GFile of the profilerc (or %NULL for no history)
*
* Creates a new #PikaColorProfileStore object and populates it with
* last used profiles read from the file @history. The updated history
* is written back to disk when the store is disposed.
*
* The #GFile passed as @history is typically created using the
* following code snippet:
* <informalexample><programlisting>
* gchar *history = pika_personal_rc_file ("profilerc");
* </programlisting></informalexample>
*
* Returns: a new #PikaColorProfileStore
*
* Since: 2.4
**/
GtkListStore *
pika_color_profile_store_new (GFile *history)
{
g_return_val_if_fail (history == NULL || G_IS_FILE (history), NULL);
return g_object_new (PIKA_TYPE_COLOR_PROFILE_STORE,
"history", history,
NULL);
}
/**
* pika_color_profile_store_add_file:
* @store: a #PikaColorProfileStore
* @file: #GFile of the profile to add (or %NULL)
* @label: label to use for the profile
* (may only be %NULL if @file is %NULL)
*
* Adds a color profile item to the #PikaColorProfileStore. Items
* added with this function will be kept at the top, separated from
* the history of last used color profiles.
*
* This function is often used to add a selectable item for the %NULL
* file. If you pass %NULL for both @file and @label, the @label will
* be set to the string "None" for you (and translated for the user).
*
* Since: 2.10
**/
void
pika_color_profile_store_add_file (PikaColorProfileStore *store,
GFile *file,
const gchar *label)
{
GtkTreeIter separator;
GtkTreeIter iter;
g_return_if_fail (PIKA_IS_COLOR_PROFILE_STORE (store));
g_return_if_fail (label != NULL || file == NULL);
g_return_if_fail (file == NULL || G_IS_FILE (file));
if (! file && ! label)
label = C_("profile", "None");
pika_color_profile_store_get_separator (store, &separator, TRUE);
gtk_list_store_insert_before (GTK_LIST_STORE (store), &iter, &separator);
gtk_list_store_set (GTK_LIST_STORE (store), &iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE,
PIKA_COLOR_PROFILE_STORE_ITEM_FILE,
PIKA_COLOR_PROFILE_STORE_FILE, file,
PIKA_COLOR_PROFILE_STORE_LABEL, label,
PIKA_COLOR_PROFILE_STORE_INDEX, -1,
-1);
}
/**
* _pika_color_profile_store_history_add:
* @store: a #PikaColorProfileStore
* @file: file of the profile to add (or %NULL)
* @label: label to use for the profile (or %NULL)
* @iter: a #GtkTreeIter
*
* Returns: %TRUE if the iter is valid and pointing to the item
*
* Since: 2.4
**/
gboolean
_pika_color_profile_store_history_add (PikaColorProfileStore *store,
GFile *file,
const gchar *label,
GtkTreeIter *iter)
{
GtkTreeModel *model;
gboolean iter_valid;
gint max = -1;
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE_STORE (store), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
model = GTK_TREE_MODEL (store);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gint index;
GFile *this;
gtk_tree_model_get (model, iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
PIKA_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (type != PIKA_COLOR_PROFILE_STORE_ITEM_FILE)
continue;
if (index > max)
max = index;
/* check if we found a filename match */
gtk_tree_model_get (model, iter,
PIKA_COLOR_PROFILE_STORE_FILE, &this,
-1);
if ((this && file && g_file_equal (this, file)) ||
(! this && ! file))
{
/* update the label */
if (label && *label)
gtk_list_store_set (GTK_LIST_STORE (store), iter,
PIKA_COLOR_PROFILE_STORE_LABEL, label,
-1);
if (this)
g_object_unref (this);
return TRUE;
}
if (this)
g_object_unref (this);
}
if (! file)
return FALSE;
if (label && *label)
{
iter_valid = pika_color_profile_store_history_insert (store, iter,
file, label,
++max);
}
else
{
const gchar *utf8 = pika_file_get_utf8_name (file);
gchar *basename = g_path_get_basename (utf8);
iter_valid = pika_color_profile_store_history_insert (store, iter,
file, basename,
++max);
g_free (basename);
}
return iter_valid;
}
/**
* _pika_color_profile_store_history_find_profile:
* @store: a #PikaColorProfileStore
* @profile: a #PikaColorProfile to find (or %NULL)
* @iter: a #GtkTreeIter
*
* Returns: %TRUE if the iter is valid and pointing to the item
*
* Since: 3.0
**/
gboolean
_pika_color_profile_store_history_find_profile (PikaColorProfileStore *store,
PikaColorProfile *profile,
GtkTreeIter *iter)
{
GtkTreeModel *model;
gboolean iter_valid;
gint max = -1;
g_return_val_if_fail (PIKA_IS_COLOR_PROFILE_STORE (store), FALSE);
g_return_val_if_fail (iter != NULL, FALSE);
model = GTK_TREE_MODEL (store);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gint index;
GFile *file;
PikaColorProfile *combo_profile = NULL;
gtk_tree_model_get (model, iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
PIKA_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (type != PIKA_COLOR_PROFILE_STORE_ITEM_FILE)
continue;
if (index > max)
max = index;
/* check if we found a filename match */
gtk_tree_model_get (model, iter,
PIKA_COLOR_PROFILE_STORE_FILE, &file,
-1);
/* Convert file to PikaColorProfile */
if (file)
combo_profile = pika_color_profile_new_from_file (file, NULL);
if ((combo_profile && profile &&
pika_color_profile_is_equal (profile, combo_profile)) ||
(! file && ! profile))
{
if (file)
g_object_unref (file);
if (combo_profile)
g_object_unref (combo_profile);
return TRUE;
}
if (file)
g_object_unref (file);
if (combo_profile)
g_object_unref (combo_profile);
}
if (! profile)
return FALSE;
return iter_valid;
}
/**
* _pika_color_profile_store_history_reorder
* @store: a #PikaColorProfileStore
* @iter: a #GtkTreeIter
*
* Moves the entry pointed to by @iter to the front of the MRU list.
*
* Since: 2.4
**/
void
_pika_color_profile_store_history_reorder (PikaColorProfileStore *store,
GtkTreeIter *iter)
{
GtkTreeModel *model;
gint index;
gboolean iter_valid;
g_return_if_fail (PIKA_IS_COLOR_PROFILE_STORE (store));
g_return_if_fail (iter != NULL);
model = GTK_TREE_MODEL (store);
gtk_tree_model_get (model, iter,
PIKA_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (index == 0)
return; /* already at the top */
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gint this_index;
gtk_tree_model_get (model, iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
PIKA_COLOR_PROFILE_STORE_INDEX, &this_index,
-1);
if (type == PIKA_COLOR_PROFILE_STORE_ITEM_FILE && this_index > -1)
{
if (this_index < index)
{
this_index++;
}
else if (this_index == index)
{
this_index = 0;
}
gtk_list_store_set (GTK_LIST_STORE (store), iter,
PIKA_COLOR_PROFILE_STORE_INDEX, this_index,
-1);
}
}
}
static gboolean
pika_color_profile_store_history_insert (PikaColorProfileStore *store,
GtkTreeIter *iter,
GFile *file,
const gchar *label,
gint index)
{
GtkTreeModel *model = GTK_TREE_MODEL (store);
GtkTreeIter sibling;
gboolean iter_valid;
g_return_val_if_fail (G_IS_FILE (file), FALSE);
g_return_val_if_fail (label != NULL, FALSE);
g_return_val_if_fail (index > -1, FALSE);
pika_color_profile_store_get_separator (store, iter, FALSE);
for (iter_valid = gtk_tree_model_get_iter_first (model, &sibling);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &sibling))
{
gint type;
gint this_index;
gtk_tree_model_get (model, &sibling,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
PIKA_COLOR_PROFILE_STORE_INDEX, &this_index,
-1);
if (type == PIKA_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM)
{
gtk_list_store_insert_before (GTK_LIST_STORE (store),
iter, &sibling);
break;
}
if (type == PIKA_COLOR_PROFILE_STORE_ITEM_FILE && this_index > -1)
{
gchar *this_label;
gtk_tree_model_get (model, &sibling,
PIKA_COLOR_PROFILE_STORE_LABEL, &this_label,
-1);
if (this_label && g_utf8_collate (label, this_label) < 0)
{
gtk_list_store_insert_before (GTK_LIST_STORE (store),
iter, &sibling);
g_free (this_label);
break;
}
g_free (this_label);
}
}
if (iter_valid)
gtk_list_store_set (GTK_LIST_STORE (store), iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE,
PIKA_COLOR_PROFILE_STORE_ITEM_FILE,
PIKA_COLOR_PROFILE_STORE_FILE, file,
PIKA_COLOR_PROFILE_STORE_LABEL, label,
PIKA_COLOR_PROFILE_STORE_INDEX, index,
-1);
return iter_valid;
}
static void
pika_color_profile_store_create_separator (PikaColorProfileStore *store,
GtkTreeIter *iter,
gboolean top)
{
if (top)
{
gtk_list_store_prepend (GTK_LIST_STORE (store), iter);
}
else
{
GtkTreeModel *model = GTK_TREE_MODEL (store);
GtkTreeIter sibling;
gboolean iter_valid;
for (iter_valid = gtk_tree_model_get_iter_first (model, &sibling);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &sibling))
{
gint type;
gtk_tree_model_get (model, &sibling,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
-1);
if (type == PIKA_COLOR_PROFILE_STORE_ITEM_DIALOG)
break;
}
if (iter_valid)
gtk_list_store_insert_before (GTK_LIST_STORE (store), iter, &sibling);
}
gtk_list_store_set (GTK_LIST_STORE (store), iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE,
top ?
PIKA_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP :
PIKA_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM,
PIKA_COLOR_PROFILE_STORE_INDEX, -1,
-1);
}
static void
pika_color_profile_store_get_separator (PikaColorProfileStore *store,
GtkTreeIter *iter,
gboolean top)
{
GtkTreeModel *model = GTK_TREE_MODEL (store);
gboolean iter_valid;
gint needle;
needle = (top ?
PIKA_COLOR_PROFILE_STORE_ITEM_SEPARATOR_TOP :
PIKA_COLOR_PROFILE_STORE_ITEM_SEPARATOR_BOTTOM);
for (iter_valid = gtk_tree_model_get_iter_first (model, iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, iter))
{
gint type;
gtk_tree_model_get (model, iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
-1);
if (type == needle)
return;
}
pika_color_profile_store_create_separator (store, iter, top);
}
static GTokenType
pika_color_profile_store_load_profile (PikaColorProfileStore *store,
GScanner *scanner,
gint index)
{
GtkTreeIter iter;
gchar *label = NULL;
gchar *path = NULL;
if (pika_scanner_parse_string (scanner, &label) &&
pika_scanner_parse_string (scanner, &path))
{
GFile *file = NULL;
if (g_str_has_prefix (path, "file://"))
{
file = g_file_new_for_uri (path);
}
else
{
file = pika_file_new_for_config_path (path, NULL);
}
if (file)
{
if (g_file_query_file_type (file, 0, NULL) == G_FILE_TYPE_REGULAR)
{
pika_color_profile_store_history_insert (store, &iter,
file, label, index);
}
g_object_unref (file);
}
g_free (label);
g_free (path);
return G_TOKEN_RIGHT_PAREN;
}
g_free (label);
g_free (path);
return G_TOKEN_STRING;
}
static gboolean
pika_color_profile_store_load (PikaColorProfileStore *store,
GFile *file,
GError **error)
{
GScanner *scanner;
GTokenType token;
gint i = 0;
scanner = pika_scanner_new_file (file, error);
if (! scanner)
return FALSE;
g_scanner_scope_add_symbol (scanner, 0, "color-profile", NULL);
token = G_TOKEN_LEFT_PAREN;
while (g_scanner_peek_next_token (scanner) == token)
{
token = g_scanner_get_next_token (scanner);
switch (token)
{
case G_TOKEN_LEFT_PAREN:
token = G_TOKEN_SYMBOL;
break;
case G_TOKEN_SYMBOL:
token = pika_color_profile_store_load_profile (store, scanner, i++);
break;
case G_TOKEN_RIGHT_PAREN:
token = G_TOKEN_LEFT_PAREN;
break;
default: /* do nothing */
break;
}
}
if (token != G_TOKEN_LEFT_PAREN)
{
g_scanner_get_next_token (scanner);
g_scanner_unexp_token (scanner, token, NULL, NULL, NULL,
_("fatal parse error"), TRUE);
}
pika_scanner_unref (scanner);
return TRUE;
}
static gboolean
pika_color_profile_store_save (PikaColorProfileStore *store,
GFile *file,
GError **error)
{
PikaConfigWriter *writer;
GtkTreeModel *model;
GtkTreeIter iter;
gchar *labels[HISTORY_SIZE] = { NULL, };
GFile *files[HISTORY_SIZE] = { NULL, };
gboolean iter_valid;
gint i;
writer = pika_config_writer_new_from_file (file,
TRUE,
"PIKA color profile history",
error);
if (! writer)
return FALSE;
model = GTK_TREE_MODEL (store);
for (iter_valid = gtk_tree_model_get_iter_first (model, &iter);
iter_valid;
iter_valid = gtk_tree_model_iter_next (model, &iter))
{
gint type;
gint index;
gtk_tree_model_get (model, &iter,
PIKA_COLOR_PROFILE_STORE_ITEM_TYPE, &type,
PIKA_COLOR_PROFILE_STORE_INDEX, &index,
-1);
if (type == PIKA_COLOR_PROFILE_STORE_ITEM_FILE &&
index >= 0 &&
index < HISTORY_SIZE)
{
if (labels[index] || files[index])
g_warning ("%s: double index %d", G_STRFUNC, index);
gtk_tree_model_get (model, &iter,
PIKA_COLOR_PROFILE_STORE_LABEL,
&labels[index],
PIKA_COLOR_PROFILE_STORE_FILE,
&files[index],
-1);
}
}
for (i = 0; i < HISTORY_SIZE; i++)
{
if (files[i] && labels[i])
{
gchar *path = pika_file_get_config_path (files[i], NULL);
if (path)
{
pika_config_writer_open (writer, "color-profile");
pika_config_writer_string (writer, labels[i]);
pika_config_writer_string (writer, path);
pika_config_writer_close (writer);
g_free (path);
}
}
if (files[i])
g_object_unref (files[i]);
g_free (labels[i]);
}
return pika_config_writer_finish (writer,
"end of color profile history", error);
}