/*
 * glade-gtk-icon-factory.c - GladeWidgetAdaptor for GtkIconFactory
 *
 * Copyright (C) 2013 Tristan Van Berkom
 *
 * Authors:
 *      Tristan Van Berkom <tristan.van.berkom@gmail.com>
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public 
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <glib/gi18n-lib.h>
#include <gladeui/glade.h>

#include "glade-icon-sources.h"
#include "glade-icon-factory-editor.h"

#define GLADE_TAG_SOURCES   "sources"
#define GLADE_TAG_SOURCE    "source"

#define GLADE_TAG_STOCK_ID  "stock-id"
#define GLADE_TAG_FILENAME  "filename"
#define GLADE_TAG_DIRECTION "direction"
#define GLADE_TAG_STATE     "state"
#define GLADE_TAG_SIZE      "size"

void
glade_gtk_icon_factory_post_create (GladeWidgetAdaptor *adaptor,
                                    GObject            *object,
                                    GladeCreateReason   reason)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gtk_icon_factory_add_default (GTK_ICON_FACTORY (object));
G_GNUC_END_IGNORE_DEPRECATIONS
}

void
glade_gtk_icon_factory_destroy_object (GladeWidgetAdaptor *adaptor,
                                       GObject            *object)
{
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  gtk_icon_factory_remove_default (GTK_ICON_FACTORY (object));
G_GNUC_END_IGNORE_DEPRECATIONS

  GLADE_WIDGET_ADAPTOR_GET_ADAPTOR_CLASS (G_TYPE_OBJECT)->destroy_object (adaptor, object);
}

static void
glade_gtk_icon_factory_read_sources (GladeWidget *widget, GladeXmlNode *node)
{
  GladeIconSources *sources;
  GtkIconSource *source;
  GladeXmlNode *sources_node, *source_node;
  GValue *value;
  GList *list;
  gchar *current_icon_name = NULL;
  GdkPixbuf *pixbuf;

  if ((sources_node = glade_xml_search_child (node, GLADE_TAG_SOURCES)) == NULL)
    return;

  sources = glade_icon_sources_new ();

  /* Here we expect all icon sets to remain together in the list. */
  for (source_node = glade_xml_node_get_children (sources_node); source_node;
       source_node = glade_xml_node_next (source_node))
    {
      gchar *icon_name;
      gchar *str;

      if (!glade_xml_node_verify (source_node, GLADE_TAG_SOURCE))
        continue;

      if (!(icon_name =
            glade_xml_get_property_string_required (source_node,
                                                    GLADE_TAG_STOCK_ID, NULL)))
        continue;

      if (!
          (str =
           glade_xml_get_property_string_required (source_node,
                                                   GLADE_TAG_FILENAME, NULL)))
        {
          g_free (icon_name);
          continue;
        }

      if (!current_icon_name || strcmp (current_icon_name, icon_name) != 0)
        current_icon_name = (g_free (current_icon_name), g_strdup (icon_name));

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      source = gtk_icon_source_new ();
G_GNUC_END_IGNORE_DEPRECATIONS

      /* Deal with the filename... */
      value = glade_utils_value_from_string (GDK_TYPE_PIXBUF, str, glade_widget_get_project (widget));
      pixbuf = g_value_dup_object (value);
      g_value_unset (value);
      g_free (value);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      gtk_icon_source_set_pixbuf (source, pixbuf);
G_GNUC_END_IGNORE_DEPRECATIONS
      g_object_unref (G_OBJECT (pixbuf));
      g_free (str);

      /* Now the attributes... */
      if ((str =
           glade_xml_get_property_string (source_node,
                                          GLADE_TAG_DIRECTION)) != NULL)
        {
          GtkTextDirection direction =
              glade_utils_enum_value_from_string (GTK_TYPE_TEXT_DIRECTION, str);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
          gtk_icon_source_set_direction_wildcarded (source, FALSE);
          gtk_icon_source_set_direction (source, direction);
G_GNUC_END_IGNORE_DEPRECATIONS
          g_free (str);
        }

      if ((str =
           glade_xml_get_property_string (source_node, GLADE_TAG_SIZE)) != NULL)
        {
          GtkIconSize size =
              glade_utils_enum_value_from_string (GTK_TYPE_ICON_SIZE, str);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
          gtk_icon_source_set_size_wildcarded (source, FALSE);
          gtk_icon_source_set_size (source, size);
G_GNUC_END_IGNORE_DEPRECATIONS
          g_free (str);
        }

      if ((str =
           glade_xml_get_property_string (source_node,
                                          GLADE_TAG_STATE)) != NULL)
        {
          GtkStateType state =
              glade_utils_enum_value_from_string (GTK_TYPE_STATE_TYPE, str);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
          gtk_icon_source_set_state_wildcarded (source, FALSE);
          gtk_icon_source_set_state (source, state);
G_GNUC_END_IGNORE_DEPRECATIONS
          g_free (str);
        }

      if ((list =
           g_hash_table_lookup (sources->sources,
                                g_strdup (current_icon_name))) != NULL)
        {
          GList *new_list = g_list_append (list, source);

          /* Warning: if we use g_list_prepend() the returned pointer will be different
           * so we would have to replace the list pointer in the hash table.
           * But before doing that we have to steal the old list pointer otherwise
           * we would have to make a copy then add the new icon to finally replace the hash table
           * value.
           * Anyways if we choose to prepend we would have to reverse the list outside this loop
           * so its better to append.
           */
          if (new_list != list)
            {
              /* current g_list_append() returns the same pointer so this is not needed */
              g_hash_table_steal (sources->sources, current_icon_name);
              g_hash_table_insert (sources->sources,
                                   g_strdup (current_icon_name), new_list);
            }
        }
      else
        {
          list = g_list_append (NULL, source);
          g_hash_table_insert (sources->sources, g_strdup (current_icon_name),
                               list);
        }
    }

  if (g_hash_table_size (sources->sources) > 0)
    glade_widget_property_set (widget, "sources", sources);

  glade_icon_sources_free (sources);
}

void
glade_gtk_icon_factory_read_widget (GladeWidgetAdaptor *adaptor,
                                    GladeWidget        *widget,
                                    GladeXmlNode       *node)
{
  if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) ||
        glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE)))
    return;

  /* First chain up and read in any normal properties.. */
  GLADE_WIDGET_ADAPTOR_GET_ADAPTOR_CLASS (G_TYPE_OBJECT)->read_widget (adaptor, widget, node);

  glade_gtk_icon_factory_read_sources (widget, node);
}

typedef struct
{
  GladeXmlContext *context;
  GladeXmlNode *node;
} SourceWriteTab;

static void
write_icon_sources (gchar *icon_name, GList *sources, SourceWriteTab *tab)
{
  GladeXmlNode *source_node;
  GtkIconSource *source;
  GList *l;
  gchar *string;

  GdkPixbuf *pixbuf;

  for (l = sources; l; l = l->next)
    {
      source = l->data;

      source_node = glade_xml_node_new (tab->context, GLADE_TAG_SOURCE);
      glade_xml_node_append_child (tab->node, source_node);

      glade_xml_node_set_property_string (source_node, GLADE_TAG_STOCK_ID,
                                          icon_name);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      if (!gtk_icon_source_get_direction_wildcarded (source))
        {
          GtkTextDirection direction = gtk_icon_source_get_direction (source);
G_GNUC_END_IGNORE_DEPRECATIONS
          string =
              glade_utils_enum_string_from_value (GTK_TYPE_TEXT_DIRECTION,
                                                  direction);
          glade_xml_node_set_property_string (source_node, GLADE_TAG_DIRECTION,
                                              string);
          g_free (string);
        }

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      if (!gtk_icon_source_get_size_wildcarded (source))
        {
          GtkIconSize size = gtk_icon_source_get_size (source);
G_GNUC_END_IGNORE_DEPRECATIONS
          string =
              glade_utils_enum_string_from_value (GTK_TYPE_ICON_SIZE, size);
          glade_xml_node_set_property_string (source_node, GLADE_TAG_SIZE,
                                              string);
          g_free (string);
        }

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      if (!gtk_icon_source_get_state_wildcarded (source))
        {
          GtkStateType state = gtk_icon_source_get_state (source);
G_GNUC_END_IGNORE_DEPRECATIONS
          string =
              glade_utils_enum_string_from_value (GTK_TYPE_STATE_TYPE, state);
          glade_xml_node_set_property_string (source_node, GLADE_TAG_STATE,
                                              string);
          g_free (string);
        }

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      pixbuf = gtk_icon_source_get_pixbuf (source);
G_GNUC_END_IGNORE_DEPRECATIONS
      string = g_object_get_data (G_OBJECT (pixbuf), "GladeFileName");

      glade_xml_node_set_property_string (source_node,
                                          GLADE_TAG_FILENAME, string);
    }
}


static void
glade_gtk_icon_factory_write_sources (GladeWidget     *widget,
                                      GladeXmlContext *context,
                                      GladeXmlNode    *node)
{
  GladeXmlNode *sources_node;
  GladeIconSources *sources = NULL;
  SourceWriteTab tab;

  glade_widget_property_get (widget, "sources", &sources);
  if (!sources)
    return;

  sources_node = glade_xml_node_new (context, GLADE_TAG_SOURCES);

  tab.context = context;
  tab.node = sources_node;
  g_hash_table_foreach (sources->sources, (GHFunc) write_icon_sources, &tab);

  if (!glade_xml_node_get_children (sources_node))
    glade_xml_node_delete (sources_node);
  else
    glade_xml_node_append_child (node, sources_node);

}


void
glade_gtk_icon_factory_write_widget (GladeWidgetAdaptor *adaptor,
                                     GladeWidget        *widget,
                                     GladeXmlContext    *context,
                                     GladeXmlNode       *node)
{
  if (!(glade_xml_node_verify_silent (node, GLADE_XML_TAG_WIDGET) ||
        glade_xml_node_verify_silent (node, GLADE_XML_TAG_TEMPLATE)))
    return;

  /* First chain up and write all the normal properties.. */
  GLADE_WIDGET_ADAPTOR_GET_ADAPTOR_CLASS (G_TYPE_OBJECT)->write_widget (adaptor, widget, context, node);

  glade_gtk_icon_factory_write_sources (widget, context, node);
}

static void
apply_icon_sources (gchar *icon_name, GList *sources, GtkIconFactory *factory)
{
  GtkIconSource *source;
  GtkIconSet *set;
  GList *l;

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  set = gtk_icon_set_new ();

  for (l = sources; l; l = l->next)
    {
      source = gtk_icon_source_copy ((GtkIconSource *) l->data);
      gtk_icon_set_add_source (set, source);
    }

  gtk_icon_factory_add (factory, icon_name, set);
G_GNUC_END_IGNORE_DEPRECATIONS
}

static void
glade_gtk_icon_factory_set_sources (GObject *object, const GValue *value)
{
  GladeIconSources *sources = g_value_get_boxed (value);
  if (sources)
    g_hash_table_foreach (sources->sources, (GHFunc) apply_icon_sources,
                          object);
}


void
glade_gtk_icon_factory_set_property (GladeWidgetAdaptor *adaptor,
                                     GObject            *object,
                                     const gchar        *property_name,
                                     const GValue       *value)
{
  if (strcmp (property_name, "sources") == 0)
    {
      glade_gtk_icon_factory_set_sources (object, value);
    }
  else
    /* Chain Up */
    GLADE_WIDGET_ADAPTOR_GET_ADAPTOR_CLASS (G_TYPE_OBJECT)->set_property (adaptor,
                                                 object, property_name, value);
}

static void
serialize_icon_sources (gchar *icon_name, GList *sources, GString *string)
{
  GList *l;

  for (l = sources; l; l = g_list_next (l))
    {
      GtkIconSource *source = l->data;
      GdkPixbuf *pixbuf;
      gchar *str;

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      pixbuf = gtk_icon_source_get_pixbuf (source);
G_GNUC_END_IGNORE_DEPRECATIONS
      str = g_object_get_data (G_OBJECT (pixbuf), "GladeFileName");

      g_string_append_printf (string, "%s[%s] ", icon_name, str);

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      if (!gtk_icon_source_get_direction_wildcarded (source))
        {
          GtkTextDirection direction = gtk_icon_source_get_direction (source);
G_GNUC_END_IGNORE_DEPRECATIONS
          str =
              glade_utils_enum_string_from_value (GTK_TYPE_TEXT_DIRECTION,
                                                  direction);
          g_string_append_printf (string, "dir-%s ", str);
          g_free (str);
        }

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      if (!gtk_icon_source_get_size_wildcarded (source))
        {
          GtkIconSize size = gtk_icon_source_get_size (source);
G_GNUC_END_IGNORE_DEPRECATIONS
          str = glade_utils_enum_string_from_value (GTK_TYPE_ICON_SIZE, size);
          g_string_append_printf (string, "size-%s ", str);
          g_free (str);
        }

G_GNUC_BEGIN_IGNORE_DEPRECATIONS
      if (!gtk_icon_source_get_state_wildcarded (source))
        {
          GtkStateType state = gtk_icon_source_get_state (source);
G_GNUC_END_IGNORE_DEPRECATIONS
          str = glade_utils_enum_string_from_value (GTK_TYPE_STATE_TYPE, state);
          g_string_append_printf (string, "state-%s ", str);
          g_free (str);
        }

      g_string_append_printf (string, "| ");
    }
}

gchar *
glade_gtk_icon_factory_string_from_value (GladeWidgetAdaptor *adaptor,
                                          GladePropertyDef   *def,
                                          const GValue       *value)
{
  GString *string;
  GParamSpec *pspec;

  pspec = glade_property_def_get_pspec (def);

  if (pspec->value_type == GLADE_TYPE_ICON_SOURCES)
    {
      GladeIconSources *sources = g_value_get_boxed (value);
      if (!sources)
        return g_strdup ("");

      string = g_string_new ("");
      g_hash_table_foreach (sources->sources, (GHFunc) serialize_icon_sources,
                            string);

      return g_string_free (string, FALSE);
    }
  else
    return GLADE_WIDGET_ADAPTOR_GET_ADAPTOR_CLASS
        (G_TYPE_OBJECT)->string_from_value (adaptor, def, value);
}


GladeEditorProperty *
glade_gtk_icon_factory_create_eprop (GladeWidgetAdaptor *adaptor,
                                     GladePropertyDef   *def,
                                     gboolean            use_command)
{
  GladeEditorProperty *eprop;
  GParamSpec          *pspec;

  pspec = glade_property_def_get_pspec (def);

  if (pspec->value_type == GLADE_TYPE_ICON_SOURCES)
    eprop = g_object_new (GLADE_TYPE_EPROP_ICON_SOURCES,
                          "property-def", def,
                          "use-command", use_command, NULL);
  else
    eprop = GLADE_WIDGET_ADAPTOR_GET_ADAPTOR_CLASS
        (G_TYPE_OBJECT)->create_eprop (adaptor, def, use_command);
  return eprop;
}

GladeEditable *
glade_gtk_icon_factory_create_editable (GladeWidgetAdaptor *adaptor,
                                        GladeEditorPageType type)
{
  GladeEditable *editable;

  /* Get base editable */
  editable = GLADE_WIDGET_ADAPTOR_GET_ADAPTOR_CLASS (G_TYPE_OBJECT)->create_editable (adaptor, type);

  if (type == GLADE_PAGE_GENERAL)
    return (GladeEditable *) glade_icon_factory_editor_new (adaptor, editable);

  return editable;
}
