diff options
Diffstat (limited to 'src/eom-uri-converter.c')
-rw-r--r-- | src/eom-uri-converter.c | 988 |
1 files changed, 988 insertions, 0 deletions
diff --git a/src/eom-uri-converter.c b/src/eom-uri-converter.c new file mode 100644 index 0000000..738820c --- /dev/null +++ b/src/eom-uri-converter.c @@ -0,0 +1,988 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <math.h> +#include <string.h> +#include <glib.h> + +#include "eom-uri-converter.h" +#include "eom-pixbuf-util.h" + +enum { + PROP_0, + PROP_CONVERT_SPACES, + PROP_SPACE_CHARACTER, + PROP_COUNTER_START, + PROP_COUNTER_N_DIGITS, + PROP_N_IMAGES +}; + +typedef struct { + EomUCType type; + union { + char *string; /* if type == EOM_UC_STRING */ + gulong counter; /* if type == EOM_UC_COUNTER */ + } data; +} EomUCToken; + + +struct _EomURIConverterPrivate { + GFile *base_file; + GList *token_list; + char *suffix; + GdkPixbufFormat *img_format; + gboolean requires_exif; + + /* options */ + gboolean convert_spaces; + gchar space_character; + gulong counter_start; + guint counter_n_digits; +}; + +static void eom_uri_converter_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); + +static void eom_uri_converter_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + +#define EOM_URI_CONVERTER_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), EOM_TYPE_URI_CONVERTER, EomURIConverterPrivate)) + +G_DEFINE_TYPE (EomURIConverter, eom_uri_converter, G_TYPE_OBJECT) + +static void +free_token (gpointer data) +{ + EomUCToken *token = (EomUCToken*) data; + + if (token->type == EOM_UC_STRING) { + g_free (token->data.string); + } + + g_slice_free (EomUCToken, token); +} + +static void +eom_uri_converter_dispose (GObject *object) +{ + EomURIConverter *instance = EOM_URI_CONVERTER (object); + EomURIConverterPrivate *priv; + + priv = instance->priv; + + if (priv->base_file) { + g_object_unref (priv->base_file); + priv->base_file = NULL; + } + + if (priv->token_list) { + g_list_foreach (priv->token_list, (GFunc) free_token, NULL); + g_list_free (priv->token_list); + priv->token_list = NULL; + } + + if (priv->suffix) { + g_free (priv->suffix); + priv->suffix = NULL; + } + + + G_OBJECT_CLASS (eom_uri_converter_parent_class)->dispose (object); +} + +static void +eom_uri_converter_init (EomURIConverter *obj) +{ + EomURIConverterPrivate *priv; + + priv = obj->priv = EOM_URI_CONVERTER_GET_PRIVATE (obj); + + priv->convert_spaces = FALSE; + priv->space_character = '_'; + priv->counter_start = 0; + priv->counter_n_digits = 1; + priv->requires_exif = FALSE; +} + +static void +eom_uri_converter_class_init (EomURIConverterClass *klass) +{ + GObjectClass *object_class = (GObjectClass*) klass; + + object_class->dispose = eom_uri_converter_dispose; + + /* GObjectClass */ + object_class->set_property = eom_uri_converter_set_property; + object_class->get_property = eom_uri_converter_get_property; + + /* Properties */ + g_object_class_install_property ( + object_class, + PROP_CONVERT_SPACES, + g_param_spec_boolean ("convert-spaces", NULL, NULL, + FALSE, G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SPACE_CHARACTER, + g_param_spec_char ("space-character", NULL, NULL, + ' ', '~', '_', G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_COUNTER_START, + g_param_spec_ulong ("counter-start", NULL, NULL, + 0, + G_MAXULONG, + 1, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_COUNTER_N_DIGITS, + g_param_spec_uint ("counter-n-digits", NULL, NULL, + 1, + G_MAXUINT, + 1, + G_PARAM_READWRITE)); + + + g_object_class_install_property ( + object_class, + PROP_N_IMAGES, + g_param_spec_uint ("n-images", NULL, NULL, + 1, + G_MAXUINT, + 1, + G_PARAM_WRITABLE)); + + g_type_class_add_private (klass, sizeof (EomURIConverterPrivate)); +} + +GQuark +eom_uc_error_quark (void) +{ + static GQuark q = 0; + if (q == 0) + q = g_quark_from_static_string ("eom-uri-converter-error-quark"); + + return q; +} + + +static void +eom_uri_converter_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + EomURIConverter *conv; + EomURIConverterPrivate *priv; + + g_return_if_fail (EOM_IS_URI_CONVERTER (object)); + + conv = EOM_URI_CONVERTER (object); + priv = conv->priv; + + switch (property_id) + { + case PROP_CONVERT_SPACES: + priv->convert_spaces = g_value_get_boolean (value); + break; + + case PROP_SPACE_CHARACTER: + priv->space_character = g_value_get_char (value); + break; + + case PROP_COUNTER_START: + { + guint new_n_digits; + + priv->counter_start = g_value_get_ulong (value); + + new_n_digits = ceil (log10 (priv->counter_start + pow (10, priv->counter_n_digits) - 1)); + + if (new_n_digits != priv->counter_n_digits) { + priv->counter_n_digits = ceil (MIN (log10 (G_MAXULONG), new_n_digits)); + } + break; + } + + case PROP_COUNTER_N_DIGITS: + priv->counter_n_digits = ceil (MIN (log10 (G_MAXULONG), g_value_get_uint (value))); + break; + + case PROP_N_IMAGES: + priv->counter_n_digits = ceil (MIN (log10 (G_MAXULONG), + log10 (priv->counter_start + g_value_get_uint (value)))); + break; + + default: + g_assert_not_reached (); + } +} + +static void +eom_uri_converter_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + EomURIConverter *conv; + EomURIConverterPrivate *priv; + + g_return_if_fail (EOM_IS_URI_CONVERTER (object)); + + conv = EOM_URI_CONVERTER (object); + priv = conv->priv; + + switch (property_id) + { + case PROP_CONVERT_SPACES: + g_value_set_boolean (value, priv->convert_spaces); + break; + + case PROP_SPACE_CHARACTER: + g_value_set_char (value, priv->space_character); + break; + + case PROP_COUNTER_START: + g_value_set_ulong (value, priv->counter_start); + break; + + case PROP_COUNTER_N_DIGITS: + g_value_set_uint (value, priv->counter_n_digits); + break; + + default: + g_assert_not_reached (); + } +} + +/* parser states */ +enum { + PARSER_NONE, + PARSER_STRING, + PARSER_TOKEN +}; + +static EomUCToken* +create_token_string (const char *string, int substr_start, int substr_len) +{ + char *start_byte; + char *end_byte; + int n_bytes; + EomUCToken *token; + + if (string == NULL) return NULL; + if (substr_len <= 0) return NULL; + + start_byte = g_utf8_offset_to_pointer (string, substr_start); + end_byte = g_utf8_offset_to_pointer (string, substr_start + substr_len); + + /* FIXME: is this right? */ + n_bytes = end_byte - start_byte; + + token = g_slice_new0 (EomUCToken); + token->type = EOM_UC_STRING; + token->data.string = g_new0 (char, n_bytes); + token->data.string = g_utf8_strncpy (token->data.string, start_byte, substr_len); + + return token; +} + +static EomUCToken* +create_token_counter (int start_counter) +{ + EomUCToken *token; + + token = g_slice_new0 (EomUCToken); + token->type = EOM_UC_COUNTER; + token->data.counter = 0; + + return token; +} + +static EomUCToken* +create_token_other (EomUCType type) +{ + EomUCToken *token; + + token = g_slice_new0 (EomUCToken); + token->type = type; + + return token; +} + +static GList* +eom_uri_converter_parse_string (EomURIConverter *conv, const char *string) +{ + EomURIConverterPrivate *priv; + GList *list = NULL; + gulong len; + int i; + int state = PARSER_NONE; + int start = -1; + int substr_len = 0; + gunichar c; + const char *s; + EomUCToken *token; + + g_return_val_if_fail (EOM_IS_URI_CONVERTER (conv), NULL); + + priv = conv->priv; + + if (string == NULL) return NULL; + + if (!g_utf8_validate (string, -1, NULL)) + return NULL; + + len = g_utf8_strlen (string, -1); + s = string; + + for (i = 0; i < len; i++) { + c = g_utf8_get_char (s); + token = NULL; + + switch (state) { + case PARSER_NONE: + if (c == '%') { + start = -1; + state = PARSER_TOKEN; + } else { + start = i; + substr_len = 1; + state = PARSER_STRING; + } + break; + + case PARSER_STRING: + if (c == '%') { + if (start != -1) { + token = create_token_string (string, start, substr_len); + } + + state = PARSER_TOKEN; + start = -1; + } else { + substr_len++; + } + break; + + case PARSER_TOKEN: { + EomUCType type = EOM_UC_END; + + if (c == 'f') { + type = EOM_UC_FILENAME; + } + else if (c == 'n') { + type = EOM_UC_COUNTER; + token = create_token_counter (priv->counter_start); + } + else if (c == 'c') { + type = EOM_UC_COMMENT; + } + else if (c == 'd') { + type = EOM_UC_DATE; + } + else if (c == 't') { + type = EOM_UC_TIME; + } + else if (c == 'a') { + type = EOM_UC_DAY; + } + else if (c == 'm') { + type = EOM_UC_MONTH; + } + else if (c == 'y') { + type = EOM_UC_YEAR; + } + else if (c == 'h') { + type = EOM_UC_HOUR; + } + else if (c == 'i') { + type = EOM_UC_MINUTE; + } + else if (c == 's') { + type = EOM_UC_SECOND; + } + + if (type != EOM_UC_END && token == NULL) { + token = create_token_other (type); + priv->requires_exif = TRUE; + } + state = PARSER_NONE; + break; + } + default: + g_assert_not_reached (); + } + + + if (token != NULL) { + list = g_list_append (list, token); + } + + s = g_utf8_next_char (s); + } + + if (state != PARSER_TOKEN && start >= 0) { + /* add remaining chars as string token */ + list = g_list_append (list, create_token_string (string, start, substr_len)); + } + + return list; +} + +void +eom_uri_converter_print_list (EomURIConverter *conv) +{ + EomURIConverterPrivate *priv; + GList *it; + + g_return_if_fail (EOM_URI_CONVERTER (conv)); + + priv = conv->priv; + + for (it = priv->token_list; it != NULL; it = it->next) { + EomUCToken *token; + char *str; + + token = (EomUCToken*) it->data; + + switch (token->type) { + case EOM_UC_STRING: + str = g_strdup_printf ("string [%s]", token->data.string); + break; + case EOM_UC_FILENAME: + str = "filename"; + break; + case EOM_UC_COUNTER: + str = g_strdup_printf ("counter [%lu]", token->data.counter); + break; + case EOM_UC_COMMENT: + str = "comment"; + break; + case EOM_UC_DATE: + str = "date"; + break; + case EOM_UC_TIME: + str = "time"; + break; + case EOM_UC_DAY: + str = "day"; + break; + case EOM_UC_MONTH: + str = "month"; + break; + case EOM_UC_YEAR: + str = "year"; + break; + case EOM_UC_HOUR: + str = "hour"; + break; + case EOM_UC_MINUTE: + str = "minute"; + break; + case EOM_UC_SECOND: + str = "second"; + break; + default: + str = "unknown"; + break; + } + + g_print ("- %s\n", str); + + if (token->type == EOM_UC_STRING || token->type == EOM_UC_COUNTER) { + g_free (str); + } + } +} + + +EomURIConverter* +eom_uri_converter_new (GFile *base_file, GdkPixbufFormat *img_format, const char *format_str) +{ + EomURIConverter *conv; + + g_return_val_if_fail (format_str != NULL, NULL); + + conv = g_object_new (EOM_TYPE_URI_CONVERTER, NULL); + + if (base_file != NULL) { + conv->priv->base_file = g_object_ref (base_file); + } + else { + conv->priv->base_file = NULL; + } + conv->priv->img_format = img_format; + conv->priv->token_list = eom_uri_converter_parse_string (conv, format_str); + + return conv; +} + +static GFile* +get_file_directory (EomURIConverter *conv, EomImage *image) +{ + GFile *file = NULL; + EomURIConverterPrivate *priv; + + g_return_val_if_fail (EOM_IS_URI_CONVERTER (conv), NULL); + g_return_val_if_fail (EOM_IS_IMAGE (image), NULL); + + priv = conv->priv; + + if (priv->base_file != NULL) { + file = g_object_ref (priv->base_file); + } + else { + GFile *img_file; + + img_file = eom_image_get_file (image); + g_assert (img_file != NULL); + + file = g_file_get_parent (img_file); + + g_object_unref (img_file); + } + + return file; +} + +static void +split_filename (GFile *file, char **name, char **suffix) +{ + char *basename; + char *suffix_start; + guint len; + + *name = NULL; + *suffix = NULL; + + /* get unescaped string */ + basename = g_file_get_basename (file); + + /* FIXME: does this work for all locales? */ + suffix_start = g_utf8_strrchr (basename, -1, '.'); + + if (suffix_start == NULL) { /* no suffix found */ + *name = g_strdup (basename); + } + else { + len = (suffix_start - basename); + *name = g_strndup (basename, len); + + len = strlen (basename) - len - 1; + *suffix = g_strndup (suffix_start+1, len); + } + + g_free (basename); +} + +static GString* +append_filename (GString *str, EomImage *img) +{ + /* appends the name of the original file without + filetype suffix */ + GFile *img_file; + char *name; + char *suffix; + GString *result; + + img_file = eom_image_get_file (img); + split_filename (img_file, &name, &suffix); + + result = g_string_append (str, name); + + g_free (name); + g_free (suffix); + + g_object_unref (img_file); + + return result; +} + +static GString* +append_counter (GString *str, gulong counter, EomURIConverter *conv) +{ + EomURIConverterPrivate *priv; + + priv = conv->priv; + + g_string_append_printf (str, "%.*lu", priv->counter_n_digits, counter); + + return str; +} + + +static void +build_absolute_file (EomURIConverter *conv, EomImage *image, GString *str, /* input */ + GFile **file, GdkPixbufFormat **format) /* output */ +{ + GFile *dir_file; + EomURIConverterPrivate *priv; + + *file = NULL; + if (format != NULL) + *format = NULL; + + g_return_if_fail (EOM_IS_URI_CONVERTER (conv)); + g_return_if_fail (EOM_IS_IMAGE (image)); + g_return_if_fail (file != NULL); + g_return_if_fail (str != NULL); + + priv = conv->priv; + + dir_file = get_file_directory (conv, image); + g_assert (dir_file != NULL); + + if (priv->img_format == NULL) { + /* use same file type/suffix */ + char *name; + char *old_suffix; + GFile *img_file; + + img_file = eom_image_get_file (image); + split_filename (img_file, &name, &old_suffix); + + g_assert (old_suffix != NULL); + + g_string_append_unichar (str, '.'); + g_string_append (str, old_suffix); + + if (format != NULL) + *format = eom_pixbuf_get_format_by_suffix (old_suffix); + + g_object_unref (img_file); + } else { + if (priv->suffix == NULL) + priv->suffix = eom_pixbuf_get_common_suffix (priv->img_format); + + g_string_append_unichar (str, '.'); + g_string_append (str, priv->suffix); + + if (format != NULL) + *format = priv->img_format; + } + + *file = g_file_get_child (dir_file, str->str); + + g_object_unref (dir_file); +} + + +static GString* +replace_remove_chars (GString *str, gboolean convert_spaces, gunichar space_char) +{ + GString *result; + guint len; + char *s; + int i; + gunichar c; + + g_return_val_if_fail (str != NULL, NULL); + + if (!g_utf8_validate (str->str, -1, NULL)) + return NULL; + + result = g_string_new (NULL); + + len = g_utf8_strlen (str->str, -1); + s = str->str; + + for (i = 0; i < len; i++, s = g_utf8_next_char (s)) { + c = g_utf8_get_char (s); + + if (c == '/') { + continue; + } + else if (g_unichar_isspace (c) && convert_spaces) { + result = g_string_append_unichar (result, space_char); + } + else { + result = g_string_append_unichar (result, c); + } + } + + /* ensure maximum length of 250 characters */ + len = MIN (result->len, 250); + result = g_string_truncate (result, len); + + return result; +} + +/* + * This function converts the uri of the EomImage object, according to the + * EomUCToken list. The absolute uri (converted filename appended to base uri) + * is returned in uri and the image format will be in the format pointer. + */ +gboolean +eom_uri_converter_do (EomURIConverter *conv, EomImage *image, + GFile **file, GdkPixbufFormat **format, GError **error) +{ + EomURIConverterPrivate *priv; + GList *it; + GString *str; + GString *repl_str; + + g_return_val_if_fail (EOM_IS_URI_CONVERTER (conv), FALSE); + + priv = conv->priv; + + *file = NULL; + if (format != NULL) + *format = NULL; + + str = g_string_new (""); + + for (it = priv->token_list; it != NULL; it = it->next) { + EomUCToken *token = (EomUCToken*) it->data; + + switch (token->type) { + case EOM_UC_STRING: + str = g_string_append (str, token->data.string); + break; + + case EOM_UC_FILENAME: + str = append_filename (str, image); + break; + + case EOM_UC_COUNTER: { + if (token->data.counter < priv->counter_start) + token->data.counter = priv->counter_start; + + str = append_counter (str, token->data.counter++, conv); + break; + } +#if 0 + case EOM_UC_COMMENT: + str = g_string_append_printf (); + str = "comment"; + break; + case EOM_UC_DATE: + str = "date"; + break; + case EOM_UC_TIME: + str = "time"; + break; + case EOM_UC_DAY: + str = "day"; + break; + case EOM_UC_MONTH: + str = "month"; + break; + case EOM_UC_YEAR: + str = "year"; + break; + case EOM_UC_HOUR: + str = "hour"; + break; + case EOM_UC_MINUTE: + str = "minute"; + break; + case EOM_UC_SECOND: + str = "second"; + break; +#endif + default: + /* skip all others */ + + break; + } + } + + repl_str = replace_remove_chars (str, priv->convert_spaces, priv->space_character); + + if (repl_str->len > 0) { + build_absolute_file (conv, image, repl_str, file, format); + } + + g_string_free (repl_str, TRUE); + g_string_free (str, TRUE); + + + return (*file != NULL); +} + + +char* +eom_uri_converter_preview (const char *format_str, EomImage *img, GdkPixbufFormat *format, + gulong counter, guint n_images, + gboolean convert_spaces, gunichar space_char) +{ + GString *str; + GString *repl_str; + guint n_digits; + guint len; + int i; + const char *s; + gunichar c; + char *filename; + gboolean token_next; + + g_return_val_if_fail (format_str != NULL, NULL); + g_return_val_if_fail (EOM_IS_IMAGE (img), NULL); + + if (n_images == 0) return NULL; + + n_digits = ceil (MIN (log10 (G_MAXULONG), MAX (log10 (counter), log10 (n_images)))); + + str = g_string_new (""); + + if (!g_utf8_validate (format_str, -1, NULL)) + return NULL; + + len = g_utf8_strlen (format_str, -1); + s = format_str; + token_next = FALSE; + + for (i = 0; i < len; i++, s = g_utf8_next_char (s)) { + c = g_utf8_get_char (s); + + if (token_next) { + if (c == 'f') { + str = append_filename (str, img); + } + else if (c == 'n') { + g_string_append_printf (str, "%.*lu", + n_digits ,counter); + + } +#if 0 /* ignore the rest for now */ + else if (c == 'c') { + type = EOM_UC_COMMENT; + } + else if (c == 'd') { + type = EOM_UC_DATE; + } + else if (c == 't') { + type = EOM_UC_TIME; + } + else if (c == 'a') { + type = EOM_UC_DAY; + } + else if (c == 'm') { + type = EOM_UC_MONTH; + } + else if (c == 'y') { + type = EOM_UC_YEAR; + } + else if (c == 'h') { + type = EOM_UC_HOUR; + } + else if (c == 'i') { + type = EOM_UC_MINUTE; + } + else if (c == 's') { + type = EOM_UC_SECOND; + } +#endif + token_next = FALSE; + } + else if (c == '%') { + token_next = TRUE; + } + else { + str = g_string_append_unichar (str, c); + } + } + + + filename = NULL; + repl_str = replace_remove_chars (str, convert_spaces, space_char); + + if (repl_str->len > 0) { + if (format == NULL) { + /* use same file type/suffix */ + char *name; + char *old_suffix; + GFile *img_file; + + img_file = eom_image_get_file (img); + split_filename (img_file, &name, &old_suffix); + + g_assert (old_suffix != NULL); + + g_string_append_unichar (repl_str, '.'); + g_string_append (repl_str, old_suffix); + + g_free (old_suffix); + g_free (name); + g_object_unref (img_file); + } + else { + char *suffix = eom_pixbuf_get_common_suffix (format); + + g_string_append_unichar (repl_str, '.'); + g_string_append (repl_str, suffix); + + g_free (suffix); + } + + filename = repl_str->str; + } + + g_string_free (repl_str, FALSE); + g_string_free (str, TRUE); + + return filename; +} + +gboolean +eom_uri_converter_requires_exif (EomURIConverter *converter) +{ + g_return_val_if_fail (EOM_IS_URI_CONVERTER (converter), FALSE); + + return converter->priv->requires_exif; +} + +gboolean +eom_uri_converter_check (EomURIConverter *converter, GList *img_list, GError **error) +{ + GList *it; + GList *file_list = NULL; + gboolean all_different = TRUE; + + g_return_val_if_fail (EOM_IS_URI_CONVERTER (converter), FALSE); + + /* convert all image uris */ + for (it = img_list; it != NULL; it = it->next) { + gboolean result; + GFile *file; + GError *conv_error = NULL; + + result = eom_uri_converter_do (converter, EOM_IMAGE (it->data), + &file, NULL, &conv_error); + + if (result) { + file_list = g_list_prepend (file_list, file); + } + } + + /* check for all different uris */ + for (it = file_list; it != NULL && all_different; it = it->next) { + GList *p; + GFile *file; + + file = (GFile*) it->data; + + for (p = it->next; p != NULL && all_different; p = p->next) { + all_different = !g_file_equal (file, (GFile*) p->data); + } + } + + if (!all_different) { + g_set_error (error, EOM_UC_ERROR, + EOM_UC_ERROR_EQUAL_FILENAMES, + _("At least two file names are equal.")); + } + + return all_different; +} |