aboutsummaryrefslogblamecommitdiffstats
path: root/e-util/e-util-labels.c
blob: 5c859be97512977c6e0c3554fc58e6a11960629c (plain) (tree)







































































































































































































































































































































                                                                                                                                          
                                                              

















                                                                                                
                                                                                   














                                                                                       
                                                                                                                                 




























































































































































































                                                                                               
/* -*- 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_entry_set_activates_default (GTK_ENTRY (e), TRUE);
    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_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
    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 = _("A label having the same tag already exists on the server. Please rename your label.");
            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;
}