diff options
Diffstat (limited to 'e-util/e-util-labels.c')
-rw-r--r-- | e-util/e-util-labels.c | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/e-util/e-util-labels.c b/e-util/e-util-labels.c new file mode 100644 index 0000000000..85225a93a5 --- /dev/null +++ b/e-util/e-util-labels.c @@ -0,0 +1,551 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* e-util-labels.c + * + * Copyright (C) 2007 Novell, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <glib.h> +#include <glib/gi18n.h> + +#include <stdio.h> +#include <string.h> + +#include <gconf/gconf-client.h> + +#include <gtk/gtkbox.h> +#include <gtk/gtkcolorbutton.h> +#include <gtk/gtkdialog.h> +#include <gtk/gtkentry.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkstock.h> +#include <gtk/gtktable.h> + +#include <camel/camel-utf8.h> + +#include "e-util-labels.h" +#include "e-dialog-utils.h" + +/* Note, the first element of each EUtilLabel must NOT be translated */ +EUtilLabel label_defaults[LABEL_DEFAULTS_NUM] = { + { "$Labelimportant", N_("I_mportant"), "#EF2929" }, /* red */ + { "$Labelwork", N_("_Work"), "#F57900" }, /* orange */ + { "$Labelpersonal", N_("_Personal"), "#4E9A06" }, /* green */ + { "$Labeltodo", N_("_To Do"), "#3465A4" }, /* blue */ + { "$Labellater", N_("_Later"), "#75507B" } /* purple */ +}; + +/** + * e_util_labels_parse + * Reads the setup from client and parses it to list of EUtilLabel objects. + * + * @param client The config client to be used for reading setup. + * Can be NULL, in that case it will use the default client. + * @return Newly allocated list of labels, should be freed with @ref e_util_labels_free. + **/ +GSList * +e_util_labels_parse (GConfClient *client) +{ + GSList *labels, *list, *head; + EUtilLabel *label; + char *buf; + int num = 0; + gboolean unref_client = client == NULL; + + labels = NULL; + + if (!client) + client = gconf_client_get_default (); + + head = gconf_client_get_list (client, E_UTIL_LABELS_GCONF_KEY, GCONF_VALUE_STRING, NULL); + + for (list = head; list; list = list->next) { + char *color, *name, *tag; + name = buf = list->data; + color = strrchr (buf, ':'); + + *color++ = '\0'; + tag = strchr (color, '|'); + if (tag) + *tag++ = '\0'; + + label = g_new (EUtilLabel, 1); + + /* Needed for Backward Compatibility */ + if (num < LABEL_DEFAULTS_NUM) { + label->name = g_strdup (_(buf)); + label->tag = g_strdup (label_defaults[num].tag); + num++; + } else if (!tag) { + g_free (buf); + g_free (label); + continue; + } else { + label->name = g_strdup (name); + label->tag = g_strdup (tag); + } + + label->colour = g_strdup (color); + labels = g_slist_prepend (labels, label); + + g_free (buf); + } + + if (head) + g_slist_free (head); + + while (num < LABEL_DEFAULTS_NUM) { + /* complete the list with defaults */ + label = g_new (EUtilLabel, 1); + label->tag = g_strdup (label_defaults[num].tag); + label->name = g_strdup (_(label_defaults[num].name)); + label->colour = g_strdup (label_defaults[num].colour); + + labels = g_slist_prepend (labels, label); + + num++; + } + + if (unref_client) + g_object_unref (client); + + return g_slist_reverse (labels); +} + +static void +free_label_struct (EUtilLabel *label) +{ + if (!label) + return; + + g_free (label->tag); + g_free (label->name); + g_free (label->colour); + g_free (label); +} + +/** + * e_util_labels_free + * Frees memory previously allocated by @ref e_util_labels_parse + * + * @param labels Labels list, previously allocated by @ref e_util_labels_parse + * It is safe to call with NULL. + **/ +void +e_util_labels_free (GSList *labels) +{ + if (!labels) + return; + + g_slist_foreach (labels, (GFunc)free_label_struct, NULL); + g_slist_free (labels); +} + +/* stores the actual cache to gconf */ +static gboolean +flush_labels_cache (GSList *labels, gboolean free_labels) +{ + GSList *l, *text_labels; + GConfClient *client; + + if (!labels) + return FALSE; + + text_labels = NULL; + + for (l = labels; l; l = l->next) { + EUtilLabel *label = l->data; + + if (label && label->tag && label->name && label->colour) + text_labels = g_slist_prepend (text_labels, g_strdup_printf ("%s:%s|%s", label->name, label->colour, label->tag)); + } + + if (!text_labels) { + if (free_labels) + e_util_labels_free (labels); + + return FALSE; + } + + text_labels = g_slist_reverse (text_labels); + + client = gconf_client_get_default (); + gconf_client_set_list (client, E_UTIL_LABELS_GCONF_KEY, GCONF_VALUE_STRING, text_labels, NULL); + g_object_unref (client); + + g_slist_foreach (text_labels, (GFunc)g_free, NULL); + g_slist_free (text_labels); + + if (free_labels) + e_util_labels_free (labels); + + /* not true if gconf failed to write; who cares */ + return TRUE; +} + +/** + * find_label + * + * Looks for label in labels cache by tag and returns actual pointer to cache. + * @param labels The cache of labels; comes from @ref e_util_labels_parse + * @param tag Tag of label you are looking for. + * @return Pointer to cache data if label with such tag exists or NULL. Do not free it! + **/ +static EUtilLabel * +find_label (GSList *labels, const char *tag) +{ + GSList *l; + + g_return_val_if_fail (tag != NULL, NULL); + + for (l = labels; l; l = l->next) { + EUtilLabel *label = l->data; + + if (label && label->tag && !strcmp (tag, label->tag)) + return label; + } + + return NULL; +} + + +static char * +tag_from_name (const char *name) +{ + /* this does thunderbird, just do not ask */ + char *s1, *s2, *p; + const char *bads = " ()/{%*<>\\\""; + + if (!name || !*name) + return NULL; + + s1 = g_strdup (name); + for (p = s1; p && *p; p++) { + if (strchr (bads, *p)) + *p = '_'; + } + + s2 = camel_utf8_utf7 (s1); + g_free (s1); + + s1 = g_ascii_strdown (s2, -1); + g_free (s2); + + return s1; +} + +/** + * e_util_labels_add + * Creates new label at the end of actual list of labels. + * + * @param name User readable name of this label. Should not be NULL. + * @param color Color assigned to this label. Should not be NULL. + * @return If succeeded then new label tag, NULL otherwise. + * Returned pointer should be freed with g_free. + * It will return NULL when the tag will be same as already existed. + * Tag name is generated in similar way as in Thunderbird. + **/ +char * +e_util_labels_add (const char *name, const GdkColor *color) +{ + EUtilLabel *label; + GSList *labels; + char *tag; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (color != NULL, NULL); + + labels = e_util_labels_parse (NULL); + tag = tag_from_name (name); + + if (!tag || find_label (labels, tag) != NULL) { + g_free (tag); + e_util_labels_free (labels); + return NULL; + } + + label = g_new0 (EUtilLabel, 1); + label->tag = g_strdup (tag); + label->name = g_strdup (name); + label->colour = gdk_color_to_string (color); + + labels = g_slist_append (labels, label); + + flush_labels_cache (labels, TRUE); + + return tag; +} + +/** + * e_util_labels_add_with_dlg + * This will open a dialog to add or edit label. + * + * @param parent Parent widget for the dialog. + * @param tag A tag for existing label to edit or NULL to add new label. + * @return Tag for newly added label or NULL, if something failed. + * Returned value should be freed with g_free. + **/ +char * +e_util_labels_add_with_dlg (GtkWindow *parent, const char *tag) +{ + GtkWidget *table, *dialog, *l, *e, *c; + const char *name; + GdkColor color; + gboolean is_edit = FALSE; + char *new_tag = NULL; + GSList *labels; + + table = gtk_table_new (2, 2, FALSE); + + labels = e_util_labels_parse (NULL); + name = tag ? e_util_labels_get_name (labels, tag) : NULL; + + l = gtk_label_new_with_mnemonic (_("Label _Name:")); + e = gtk_entry_new (); + c = gtk_color_button_new (); + + if (!tag || !e_util_labels_get_color (labels, tag, &color)) + memset (&color, 0xCD, sizeof (GdkColor)); + else + is_edit = TRUE; + + if (name) + gtk_entry_set_text (GTK_ENTRY (e), name); + + gtk_label_set_mnemonic_widget (GTK_LABEL (l), e); + gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.0); + gtk_color_button_set_color (GTK_COLOR_BUTTON (c), &color); + + gtk_table_attach (GTK_TABLE (table), l, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), e, 0, 1, 1, 2, 0, 0, 0, 0); + gtk_table_attach (GTK_TABLE (table), c, 1, 2, 1, 2, 0, 0, 0, 0); + gtk_container_set_border_width (GTK_CONTAINER (table), 10); + gtk_widget_show_all (table); + + dialog = gtk_dialog_new_with_buttons (is_edit ? _("Edit Label") : _("Add Label"), + parent, + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, + NULL); + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), table, TRUE, TRUE, 0); + + while (!new_tag) { + const char *error = NULL; + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { + name = gtk_entry_get_text (GTK_ENTRY (e)); + gtk_color_button_get_color (GTK_COLOR_BUTTON (c), &color); + + if (!name || !*name) + error = _("Label name cannot be empty."); + else if (is_edit) { + e_util_labels_set_data (tag, name, &color); + break; + } else if (!(new_tag = e_util_labels_add (name, &color))) + error = _("Label with same tag already exists. Rename your label please."); + else + break; + } else + break; + + if (error) + e_notice (parent, GTK_MESSAGE_ERROR, error); + } + + gtk_widget_destroy (dialog); + e_util_labels_free (labels); + + return new_tag; +} + +/** + * e_util_labels_remove + * + * @param tag Tag of the label to remove. + * @return Whether was removed. + **/ +gboolean +e_util_labels_remove (const char *tag) +{ + EUtilLabel *label; + GSList *labels; + + g_return_val_if_fail (tag != NULL, FALSE); + + labels = e_util_labels_parse (NULL); + label = find_label (labels, tag); + + if (!label) { + e_util_labels_free (labels); + return FALSE; + } + + labels = g_slist_remove (labels, label); + + free_label_struct (label); + + return flush_labels_cache (labels, TRUE); +} + +/** + * e_util_labels_set_data + * + * @param tag Tag of the label of our interest. + * @param name New name for the label. + * @param color New color for the label. + * @return Whether successfully saved. + **/ +gboolean +e_util_labels_set_data (const char *tag, const char *name, const GdkColor *color) +{ + EUtilLabel *label; + GSList *labels; + + g_return_val_if_fail (tag != NULL, FALSE); + g_return_val_if_fail (name != NULL, FALSE); + g_return_val_if_fail (color != NULL, FALSE); + + labels = e_util_labels_parse (NULL); + label = find_label (labels, tag); + + if (!label) { + e_util_labels_free (labels); + return FALSE; + } + + g_free (label->name); + label->name = g_strdup (name); + + g_free (label->colour); + label->colour = gdk_color_to_string (color); + + return flush_labels_cache (labels, TRUE); +} + +/** + * e_util_labels_is_system + * + * @return Whether the tag is one of default/system labels or not. + **/ +gboolean +e_util_labels_is_system (const char *tag) +{ + int i; + + if (!tag) + return FALSE; + + for (i = 0; i < LABEL_DEFAULTS_NUM; i++) { + if (strcmp (tag, label_defaults[i].tag) == 0) + return TRUE; + } + + return FALSE; +} + +/** + * e_util_labels_get_new_tag + * + * @param old_tag Tag of the label from old version of Evolution. + * @return New tag name equivalent with the old tag, or NULL if no such name existed before. + **/ +const char * +e_util_labels_get_new_tag (const char *old_tag) +{ + int i; + + if (!old_tag) + return NULL; + + for (i = 0; i < LABEL_DEFAULTS_NUM; i++) { + /* default labels have same name as those old, only with prefix "$Label" */ + if (!strcmp (old_tag, label_defaults[i].tag + 6)) + return label_defaults[i].tag; + } + + return NULL; +} + +/** + * e_util_labels_get_name + * + * @param labels Cache of labels from call of @ref e_util_labels_parse. + * The returned pointer will be taken from this list, so it's alive as long as the list. + * @param tag Tag of the label of our interest. + * @return Name of the label with that tag or NULL, if no such label exists. + **/ +const char * +e_util_labels_get_name (GSList *labels, const char *tag) +{ + EUtilLabel *label; + + g_return_val_if_fail (tag != NULL, NULL); + + label = find_label (labels, tag); + if (!label) + return NULL; + + return label->name; +} + +/** + * e_util_labels_get_color + * + * @param labels Cache of labels from call of @ref e_util_labels_parse. + * @param tag Tag of the label of our interest. + * @param color [out] Actual color of the label with that tag, or unchanged if failed. + * @return Whether found such label and color has been set. + **/ +gboolean +e_util_labels_get_color (GSList *labels, const char *tag, GdkColor *color) +{ + EUtilLabel *label; + + g_return_val_if_fail (tag != NULL, FALSE); + g_return_val_if_fail (color != NULL, FALSE); + + label = find_label (labels, tag); + if (!label) + return FALSE; + + return gdk_color_parse (label->colour, color); +} + +/** + * e_util_labels_get_color_str + * + * @param labels Cache of labels from call of @ref e_util_labels_parse. + * The returned pointer will be taken from this list, so it's alive as long as the list. + * @param tag Tag of the label of our interest. + * @return String representation of that label, or NULL, is no such label exists. + **/ +const char * +e_util_labels_get_color_str (GSList *labels, const char *tag) +{ + EUtilLabel *label; + + g_return_val_if_fail (tag != NULL, FALSE); + + label = find_label (labels, tag); + if (!label) + return FALSE; + + return label->colour; +} |