aboutsummaryrefslogtreecommitdiffstats
path: root/e-util/e-util-labels.c
diff options
context:
space:
mode:
Diffstat (limited to 'e-util/e-util-labels.c')
-rw-r--r--e-util/e-util-labels.c551
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;
+}