/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */

/*
   caja-directory-background.c: Helper for the background of a widget
                                    that is viewing a particular location.

   Copyright (C) 2000 Eazel, Inc.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of the
   License, or (at your option) any later version.

   This program 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
   General Public License for more details.

   You should have received a copy of the GNU General Public
   License along with this program; if not, write to the
   Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
   Boston, MA 02110-1301, USA.

   Author: Darin Adler <darin@bentspoon.com>
*/

#include <config.h>
#include "caja-directory-background.h"

#include <eel/eel-gdk-extensions.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-background.h>
#include "caja-dnd.h"
#include "caja-global-preferences.h"
#include "caja-metadata.h"
#include "caja-file-attributes.h"
#include <gtk/gtk.h>
#include <string.h>

static void background_changed_callback     (EelBackground *background,
        GdkDragAction  action,
        CajaFile  *file);
static void background_reset_callback       (EelBackground *background,
        CajaFile       *file);

static void saved_settings_changed_callback (CajaFile       *file,
        EelBackground *background);

static void caja_file_background_receive_mateconf_changes (EelBackground *background);

static void caja_file_background_theme_changed (GSettings   *settings,
                                                const gchar *key,
                                                gpointer     user_data);

void
caja_connect_desktop_background_to_file_metadata (CajaIconContainer *icon_container,
        CajaFile *file)
{
    EelBackground *background;

    background = eel_get_widget_background (GTK_WIDGET (icon_container));

    eel_background_set_desktop (background, GTK_WIDGET (icon_container), TRUE);

    /* Strictly speaking, we don't need to know about metadata changes, since
     * the desktop setting aren't stored there. But, hooking up to metadata
     * changes is actually a small part of what this fn does, and we do need
     * the other stuff (hooked up to background & theme changes, initialize
     * the background). Being notified of metadata changes on the file is a
     * waste, but won't hurt, so I don't think it's worth refactoring the fn
     * at this point.
     */
    caja_connect_background_to_file_metadata (GTK_WIDGET (icon_container), file, CAJA_DND_ACTION_SET_AS_FOLDER_BACKGROUND);

    caja_file_background_receive_mateconf_changes (background);
}

static void
caja_file_background_get_default_settings (char **color,
        char **image,
        EelBackgroundImagePlacement *placement)
{
    gboolean background_set;

    background_set = g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_BACKGROUND_SET);

    if (background_set && color)
    {
        *color = g_settings_get_string (caja_preferences, CAJA_PREFERENCES_BACKGROUND_COLOR);
    }

    if (background_set && image)
    {
        *image =  g_settings_get_string (caja_preferences, CAJA_PREFERENCES_BACKGROUND_URI);
    }

    if (placement)
    {
        *placement = EEL_BACKGROUND_TILED;
    }
}


#define BG_PREFERENCES_DRAW_BACKGROUND    "/desktop/mate/background/draw_background"
#define BG_PREFERENCES_PRIMARY_COLOR      "/desktop/mate/background/primary_color"
#define BG_PREFERENCES_SECONDARY_COLOR    "/desktop/mate/background/secondary_color"
#define BG_PREFERENCES_COLOR_SHADING_TYPE "/desktop/mate/background/color_shading_type"
#define BG_PREFERENCES_PICTURE_OPTIONS    "/desktop/mate/background/picture_options"
#define BG_PREFERENCES_PICTURE_OPACITY    "/desktop/mate/background/picture_opacity"
#define BG_PREFERENCES_PICTURE_FILENAME   "/desktop/mate/background/picture_filename"

static void
read_color (MateConfClient *client, const char *key, GdkColor *color)
{
    gchar *tmp;

    tmp = mateconf_client_get_string (client, key, NULL);

    if (tmp != NULL)
    {
        if (!gdk_color_parse (tmp, color))
            gdk_color_parse ("black", color);
        g_free (tmp);
    }
    else
    {
        gdk_color_parse ("black", color);
    }

    gdk_rgb_find_color (gdk_rgb_get_colormap (), color);
}

static void
caja_file_background_read_desktop_settings (char **color,
        char **image,
        EelBackgroundImagePlacement *placement)
{
    MateConfClient *client;
    gboolean enabled;
    GdkColor primary, secondary;
    gchar *tmp, *filename;
    char	*end_color;
    char	*start_color;
    gboolean use_gradient;
    gboolean is_horizontal;

    filename = NULL;

    client = mateconf_client_get_default ();

    /* Get the image filename */
    enabled = mateconf_client_get_bool (client, BG_PREFERENCES_DRAW_BACKGROUND, NULL);
    if (enabled)
    {
        tmp = mateconf_client_get_string (client, BG_PREFERENCES_PICTURE_FILENAME, NULL);
        if (tmp != NULL)
        {
            if (g_utf8_validate (tmp, -1, NULL) && g_file_test (tmp, G_FILE_TEST_EXISTS))
            {
                filename = g_strdup (tmp);
            }
            else
            {
                filename = g_filename_from_utf8 (tmp, -1, NULL, NULL, NULL);
            }
        }
        g_free (tmp);

        if (filename != NULL && filename[0] != '\0')
        {
            *image = g_filename_to_uri (filename, NULL, NULL);
        }
        else
        {
            *image = NULL;
        }
        g_free (filename);
    }
    else
    {
        *image = NULL;
    }

    /* Get the placement */
    tmp = mateconf_client_get_string (client, BG_PREFERENCES_PICTURE_OPTIONS, NULL);
    if (tmp != NULL)
    {
        if (strcmp (tmp, "wallpaper") == 0)
        {
            *placement = EEL_BACKGROUND_TILED;
        }
        else if (strcmp (tmp, "centered") == 0)
        {
            *placement = EEL_BACKGROUND_CENTERED;
        }
        else if (strcmp (tmp, "scaled") == 0)
        {
            *placement = EEL_BACKGROUND_SCALED_ASPECT;
        }
        else if (strcmp (tmp, "stretched") == 0)
        {
            *placement = EEL_BACKGROUND_SCALED;
        }
        else if (strcmp (tmp, "zoom") == 0)
        {
            *placement = EEL_BACKGROUND_ZOOM;
        }
        else if (strcmp (tmp, "spanned") == 0)
        {
            *placement = EEL_BACKGROUND_SPANNED;
        }
        else if (strcmp (tmp, "none") == 0)
        {
            g_free (*image);

            *placement = EEL_BACKGROUND_CENTERED;
            *image = NULL;
        }
        else
        {
            *placement = EEL_BACKGROUND_CENTERED;
        }
    }
    else
    {
        *placement = EEL_BACKGROUND_CENTERED;
    }
    g_free (tmp);

    /* Get the color */
    tmp = mateconf_client_get_string (client, BG_PREFERENCES_COLOR_SHADING_TYPE, NULL);
    if (tmp != NULL)
    {
        if (strcmp (tmp, "solid") == 0)
        {
            use_gradient = FALSE;
            is_horizontal = FALSE;
        }
        else if (strcmp (tmp, "vertical-gradient") == 0)
        {
            use_gradient = TRUE;
            is_horizontal = FALSE;
        }
        else if (strcmp (tmp, "horizontal-gradient") == 0)
        {
            use_gradient = TRUE;
            is_horizontal = TRUE;
        }
        else
        {
            use_gradient = FALSE;
            is_horizontal = FALSE;
        }
    }
    else
    {
        use_gradient = FALSE;
        is_horizontal = FALSE;
    }
    g_free (tmp);

    read_color (client, BG_PREFERENCES_PRIMARY_COLOR, &primary);
    read_color (client, BG_PREFERENCES_SECONDARY_COLOR, &secondary);

    start_color   = eel_gdk_rgb_to_color_spec (eel_gdk_color_to_rgb (&primary));
    end_color     = eel_gdk_rgb_to_color_spec (eel_gdk_color_to_rgb (&secondary));

    if (use_gradient)
    {
        *color = eel_gradient_new (start_color, end_color, is_horizontal);
    }
    else
    {
        *color = g_strdup (start_color);
    }

    g_free (start_color);
    g_free (end_color);
}

static void
caja_file_background_write_desktop_default_settings (void)
{
    /* We just unset all the mateconf keys so they go back to
     * defaults
     */
    MateConfClient *client;
    MateConfChangeSet *set;

    client = mateconf_client_get_default ();
    set = mateconf_change_set_new ();

    /* the list of keys here has to be kept in sync with libmate
     * schemas, which isn't the most maintainable thing ever.
     */
    mateconf_change_set_unset (set, "/desktop/mate/background/picture_options");
    mateconf_change_set_unset (set, "/desktop/mate/background/picture_filename");
    mateconf_change_set_unset (set, "/desktop/mate/background/picture_opacity");
    mateconf_change_set_unset (set, "/desktop/mate/background/primary_color");
    mateconf_change_set_unset (set, "/desktop/mate/background/secondary_color");
    mateconf_change_set_unset (set, "/desktop/mate/background/color_shading_type");

    /* this isn't atomic yet so it'll be a bit inefficient, but
     * someday it might be atomic.
     */
    mateconf_client_commit_change_set (client, set, FALSE, NULL);

    mateconf_change_set_unref (set);

    g_object_unref (G_OBJECT (client));
}

static int
call_settings_changed (EelBackground *background)
{
    CajaFile *file;
    file = g_object_get_data (G_OBJECT (background), "eel_background_file");
    if (file)
    {
        saved_settings_changed_callback (file, background);
    }
    g_object_set_data (G_OBJECT (background), "desktop_mateconf_notification_timeout", GUINT_TO_POINTER (0));
    return FALSE;
}

static void
desktop_background_destroyed_callback (EelBackground *background, void *georgeWBush)
{
    guint notification_id;
    guint notification_timeout_id;

    notification_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (background), "desktop_mateconf_notification"));
    eel_mateconf_notification_remove (notification_id);

    notification_timeout_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (background), "desktop_mateconf_notification_timeout"));
    if (notification_timeout_id != 0)
    {
        g_source_remove (notification_timeout_id);
    }
}

static void
desktop_background_mateconf_notify_cb (MateConfClient *client, guint notification_id, MateConfEntry *entry, gpointer data)
{
    EelBackground *background;
    guint notification_timeout_id;

    background = EEL_BACKGROUND (data);

    notification_timeout_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (background), "desktop_mateconf_notification_timeout"));

    if (strcmp (entry->key, "/desktop/mate/background/stamp") == 0)
    {
        if (notification_timeout_id != 0)
            g_source_remove (notification_timeout_id);

        call_settings_changed (background);
    }
    else if (notification_timeout_id == 0)
    {
        notification_timeout_id = g_timeout_add (300, (GSourceFunc) call_settings_changed, background);

        g_object_set_data (G_OBJECT (background), "desktop_mateconf_notification_timeout", GUINT_TO_POINTER (notification_timeout_id));
    }
}

static void
caja_file_background_receive_mateconf_changes (EelBackground *background)
{
    guint notification_id;

    eel_mateconf_monitor_add ("/desktop/mate/background");
    notification_id = eel_mateconf_notification_add ("/desktop/mate/background", desktop_background_mateconf_notify_cb, background);

    g_object_set_data (G_OBJECT (background), "desktop_mateconf_notification", GUINT_TO_POINTER (notification_id));

    g_signal_connect (background, "destroy",
                      G_CALLBACK (desktop_background_destroyed_callback), NULL);
}

/* return true if the background is not in the default state */
gboolean
caja_file_background_is_set (EelBackground *background)
{
    char *color;
    char *image;

    gboolean is_set;

    color = eel_background_get_color (background);
    image = eel_background_get_image_uri (background);

    is_set = (color || image);

    g_free (color);
    g_free (image);

    return is_set;
}

/* handle the background changed signal */
static void
background_changed_callback (EelBackground *background,
                             GdkDragAction  action,
                             CajaFile   *file)
{
    char *color;
    char *image;

    g_assert (EEL_IS_BACKGROUND (background));
    g_assert (CAJA_IS_FILE (file));
    g_assert (g_object_get_data (G_OBJECT (background), "eel_background_file") == file);


    color = eel_background_get_color (background);
    image = eel_background_get_image_uri (background);

    if (eel_background_is_desktop (background))
    {
        eel_background_save_to_mateconf (background);
    }
    else
    {
        /* Block the other handler while we are writing metadata so it doesn't
         * try to change the background.
         */
        g_signal_handlers_block_by_func (
            file, G_CALLBACK (saved_settings_changed_callback), background);

        if (action != (GdkDragAction) CAJA_DND_ACTION_SET_AS_FOLDER_BACKGROUND &&
                action != (GdkDragAction) CAJA_DND_ACTION_SET_AS_GLOBAL_BACKGROUND)
        {
            GdkDragAction default_drag_action;

            default_drag_action = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (background), "default_drag_action"));


            action = default_drag_action;
        }

        if (action == (GdkDragAction) CAJA_DND_ACTION_SET_AS_GLOBAL_BACKGROUND)
        {
            caja_file_set_metadata (file,
                                    CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR,
                                    NULL,
                                    NULL);

            caja_file_set_metadata (file,
                                    CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE,
                                    NULL,
                                    NULL);

            g_signal_handlers_block_by_func (caja_preferences, G_CALLBACK (caja_file_background_theme_changed), background);

            g_settings_set_string (caja_preferences,
                                   CAJA_PREFERENCES_BACKGROUND_COLOR, color ? color : "");
            g_settings_set_string (caja_preferences,
                                   CAJA_PREFERENCES_BACKGROUND_URI,
                                   image ? image : "");
            g_settings_set_boolean (caja_preferences,
                                    CAJA_PREFERENCES_BACKGROUND_SET, TRUE);

            g_signal_handlers_unblock_by_func (caja_preferences, G_CALLBACK (caja_file_background_theme_changed), background);
        }
        else
        {
            caja_file_set_metadata (file,
                                    CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR,
                                    NULL,
                                    color);

            caja_file_set_metadata (file,
                                    CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE,
                                    NULL,
                                    image);
        }

        /* Unblock the handler. */
        g_signal_handlers_unblock_by_func (
            file, G_CALLBACK (saved_settings_changed_callback), background);
    }

    g_free (color);
    g_free (image);
}

static void
initialize_background_from_settings (CajaFile *file,
                                     EelBackground *background)
{
    char *color;
    char *image;
    EelBackgroundImagePlacement placement;

    g_assert (CAJA_IS_FILE (file));
    g_assert (EEL_IS_BACKGROUND (background));
    g_assert (g_object_get_data (G_OBJECT (background), "eel_background_file")
              == file);

    if (eel_background_is_desktop (background))
    {
        caja_file_background_read_desktop_settings (&color, &image, &placement);
    }
    else
    {
        color = caja_file_get_metadata (file,
                                        CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR,
                                        NULL);
        image = caja_file_get_metadata (file,
                                        CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE,
                                        NULL);
        placement = EEL_BACKGROUND_TILED; /* non-tiled only avail for desktop, at least for now */

        /* if there's none, read the default from the theme */
        if (color == NULL && image == NULL)
        {
            caja_file_background_get_default_settings
            (&color, &image, &placement);
        }
    }

    /* Block the other handler while we are responding to changes
     * in the metadata so it doesn't try to change the metadata.
     */
    g_signal_handlers_block_by_func
    (background,
     G_CALLBACK (background_changed_callback),
     file);

    eel_background_set_color (background, color);
    eel_background_set_image_uri (background, image);
    eel_background_set_image_placement (background, placement);

    /* Unblock the handler. */
    g_signal_handlers_unblock_by_func
    (background,
     G_CALLBACK (background_changed_callback),
     file);

    g_free (color);
    g_free (image);
}

/* handle the file changed signal */
static void
saved_settings_changed_callback (CajaFile *file,
                                 EelBackground *background)
{
    initialize_background_from_settings (file, background);
}

/* handle the theme changing */
static void
caja_file_background_theme_changed (GSettings   *settings,
                                    const gchar *key,
                                    gpointer     user_data)
{
    CajaFile *file;
    EelBackground *background;

    background = EEL_BACKGROUND (user_data);
    file = g_object_get_data (G_OBJECT (background), "eel_background_file");
    if (file)
    {
        saved_settings_changed_callback (file, background);
    }
}

/* handle the background reset signal by setting values from the current theme */
static void
background_reset_callback (EelBackground *background,
                           CajaFile  *file)
{
    char *color;
    char *image;

    if (eel_background_is_desktop (background))
    {
        caja_file_background_write_desktop_default_settings ();
    }
    else
    {
        /* Block the other handler while we are writing metadata so it doesn't
         * try to change the background.
         */
        g_signal_handlers_block_by_func (
            file,
            G_CALLBACK (saved_settings_changed_callback),
            background);

        color = caja_file_get_metadata (file,
                                        CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR,
                                        NULL);
        image = caja_file_get_metadata (file,
                                        CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE,
                                        NULL);
        if (!color && !image)
        {
            g_signal_handlers_block_by_func (caja_preferences, G_CALLBACK (caja_file_background_theme_changed), background);
            g_settings_set_boolean (caja_preferences,
                                    CAJA_PREFERENCES_BACKGROUND_SET,
                                    FALSE);
            g_signal_handlers_unblock_by_func (caja_preferences, G_CALLBACK (caja_file_background_theme_changed), background);
        }
        else
        {
            /* reset the metadata */
            caja_file_set_metadata (file,
                                    CAJA_METADATA_KEY_LOCATION_BACKGROUND_COLOR,
                                    NULL,
                                    NULL);

            caja_file_set_metadata (file,
                                    CAJA_METADATA_KEY_LOCATION_BACKGROUND_IMAGE,
                                    NULL,
                                    NULL);
        }
        g_free (color);
        g_free (image);

        /* Unblock the handler. */
        g_signal_handlers_unblock_by_func (
            file,
            G_CALLBACK (saved_settings_changed_callback),
            background);
    }

    saved_settings_changed_callback (file, background);
}

/* handle the background destroyed signal */
static void
background_destroyed_callback (EelBackground *background,
                               CajaFile *file)
{
    g_signal_handlers_disconnect_by_func
    (file,
     G_CALLBACK (saved_settings_changed_callback), background);
    caja_file_monitor_remove (file, background);
    g_signal_handlers_disconnect_by_func (caja_preferences,
                                          caja_file_background_theme_changed,
                                          background);
}

/* key routine that hooks up a background and location */
void
caja_connect_background_to_file_metadata (GtkWidget    *widget,
        CajaFile *file,
        GdkDragAction default_drag_action)
{
    EelBackground *background;
    gpointer old_file;

    /* Get at the background object we'll be connecting. */
    background = eel_get_widget_background (widget);

    /* Check if it is already connected. */
    old_file = g_object_get_data (G_OBJECT (background), "eel_background_file");
    if (old_file == file)
    {
        return;
    }

    /* Disconnect old signal handlers. */
    if (old_file != NULL)
    {
        g_assert (CAJA_IS_FILE (old_file));
        g_signal_handlers_disconnect_by_func
        (background,
         G_CALLBACK (background_changed_callback), old_file);
        g_signal_handlers_disconnect_by_func
        (background,
         G_CALLBACK (background_destroyed_callback), old_file);
        g_signal_handlers_disconnect_by_func
        (background,
         G_CALLBACK (background_reset_callback), old_file);
        g_signal_handlers_disconnect_by_func
        (old_file,
         G_CALLBACK (saved_settings_changed_callback), background);
        caja_file_monitor_remove (old_file, background);
        g_signal_handlers_disconnect_by_func (caja_preferences,
                                              caja_file_background_theme_changed,
                                              background);

    }

    /* Attach the new directory. */
    caja_file_ref (file);
    g_object_set_data_full (G_OBJECT (background), "eel_background_file",
                            file, (GDestroyNotify) caja_file_unref);

    g_object_set_data (G_OBJECT (background), "default_drag_action", GINT_TO_POINTER (default_drag_action));

    /* Connect new signal handlers. */
    if (file != NULL)
    {
        g_signal_connect_object (background, "settings_changed",
                                 G_CALLBACK (background_changed_callback), file, 0);
        g_signal_connect_object (background, "destroy",
                                 G_CALLBACK (background_destroyed_callback), file, 0);
        g_signal_connect_object (background, "reset",
                                 G_CALLBACK (background_reset_callback), file, 0);
        g_signal_connect_object (file, "changed",
                                 G_CALLBACK (saved_settings_changed_callback), background, 0);

        /* arrange to receive file metadata */
        caja_file_monitor_add (file,
                               background,
                               CAJA_FILE_ATTRIBUTE_INFO);

        /* arrange for notification when the theme changes */
        g_signal_connect (caja_preferences,
                          "changed::" CAJA_PREFERENCES_BACKGROUND_SET,
                          G_CALLBACK(caja_file_background_theme_changed),
                          background);
        g_signal_connect (caja_preferences,
                          "changed::" CAJA_PREFERENCES_BACKGROUND_COLOR,
                          G_CALLBACK(caja_file_background_theme_changed),
                          background);
        g_signal_connect (caja_preferences,
                          "changed::" CAJA_PREFERENCES_BACKGROUND_URI,
                          G_CALLBACK(caja_file_background_theme_changed),
                          background);
    }

    /* Update the background based on the file metadata. */
    initialize_background_from_settings (file, background);
}