/*
 * Copyright (C) 2013 Tristan Van Berkom.
 *
 * 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.
 *
 * Authors:
 *   Tristan Van Berkom <tvb@gnome.org>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib/gi18n-lib.h>

#include "glade.h"
#include "glade-widget.h"
#include "glade-popup.h"
#include "glade-editable.h"
#include "glade-property-label.h"

/* GObjectClass */
static void      glade_property_label_finalize          (GObject         *object);
static void      glade_property_label_dispose           (GObject         *object);
static void      glade_property_label_set_real_property (GObject         *object,
							 guint            prop_id,
							 const GValue    *value,
							 GParamSpec      *pspec);
static void      glade_property_label_get_real_property (GObject         *object,
							 guint            prop_id,
							 GValue          *value,
							 GParamSpec      *pspec);

/* GtkWidgetClass */
static gint      glade_property_label_button_press      (GtkWidget       *widget,
							 GdkEventButton  *event);

/* GladeEditableIface */
static void      glade_property_label_editable_init     (GladeEditableIface *iface);

struct _GladePropertyLabelPrivate
{
  GladeProperty *property;

  GtkWidget     *warning;
  GtkWidget     *label;
  GtkWidget     *box;

  gulong         tooltip_id;   /* signal connection id for tooltip changes     */
  gulong         state_id;     /* signal connection id for state changes       */
  gulong         sensitive_id; /* signal connection id for sensitivity changes */
  gulong         enabled_id;   /* signal connection id for property enabled changes */

  gchar         *property_name; /* The property name to use when loading by GladeWidget */

  guint          packing : 1;
  guint          custom_text : 1;
  guint          custom_tooltip : 1;
  guint          append_colon : 1;
};

enum {
  PROP_0,
  PROP_PROPERTY,
  PROP_PROPERTY_NAME,
  PROP_APPEND_COLON,
  PROP_PACKING,
  PROP_CUSTOM_TEXT,
  PROP_CUSTOM_TOOLTIP,
};

static GladeEditableIface *parent_editable_iface;

G_DEFINE_TYPE_WITH_CODE (GladePropertyLabel, glade_property_label, GTK_TYPE_EVENT_BOX,
                         G_ADD_PRIVATE (GladePropertyLabel)
			 G_IMPLEMENT_INTERFACE (GLADE_TYPE_EDITABLE,
                                                glade_property_label_editable_init));

static void
glade_property_label_init (GladePropertyLabel *label)
{
  label->priv = glade_property_label_get_instance_private (label);

  label->priv->packing = FALSE;
  label->priv->custom_text = FALSE;
  label->priv->custom_tooltip = FALSE;
  label->priv->append_colon = TRUE;
  
  gtk_widget_init_template (GTK_WIDGET (label));
}

static void
glade_property_label_class_init (GladePropertyLabelClass *class)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
  
  gobject_class->finalize = glade_property_label_finalize;
  gobject_class->dispose = glade_property_label_dispose;
  gobject_class->set_property = glade_property_label_set_real_property;
  gobject_class->get_property = glade_property_label_get_real_property;

  widget_class->button_press_event = glade_property_label_button_press;

  g_object_class_install_property
    (gobject_class, PROP_PROPERTY,
     g_param_spec_object ("property", _("Property"),
			  _("The GladeProperty to display a label for"),
			  GLADE_TYPE_PROPERTY, G_PARAM_READWRITE));

  g_object_class_install_property
      (gobject_class, PROP_PROPERTY_NAME,
       g_param_spec_string ("property-name", _("Property Name"),
                            /* To Translators: the property name/id to use to get
                             * the GladeProperty object from the GladeWidget the
                             * property belongs to.
                             */
			    _("The property name to use when loading by widget"),
			    NULL, G_PARAM_READWRITE));

  g_object_class_install_property
      (gobject_class, PROP_APPEND_COLON,
       g_param_spec_boolean ("append-colon", _("Append Colon"),
			     _("Whether to append a colon ':' to the property name"),
			     TRUE, G_PARAM_READWRITE));

  g_object_class_install_property
      (gobject_class, PROP_PACKING,
       g_param_spec_boolean ("packing", _("Packing"),
                             /* To Translators: packing properties or child properties are
                              * properties introduced by GtkContainer and they are not specific
                              * to the container or child widget but to the relation.
                              * For more information see GtkContainer docs.
                              */
			     _("Whether the property to load is a packing property or not"),
			     FALSE, G_PARAM_READWRITE));

  g_object_class_install_property
      (gobject_class, PROP_CUSTOM_TEXT,
       g_param_spec_string ("custom-text", _("Custom Text"),
			    _("Custom text to override the property name"),
			    NULL, G_PARAM_READWRITE));

  g_object_class_install_property
      (gobject_class, PROP_CUSTOM_TOOLTIP,
       g_param_spec_string ("custom-tooltip", _("Custom Tooltip"),
			    _("Custom tooltip to override the property description"),
			    NULL, G_PARAM_READWRITE));

  /* Bind to template */
  gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/gladeui/glade-property-label.ui");
  gtk_widget_class_bind_template_child_private (widget_class, GladePropertyLabel, box);
  gtk_widget_class_bind_template_child_private (widget_class, GladePropertyLabel, label);
  gtk_widget_class_bind_template_child_private (widget_class, GladePropertyLabel, warning);
}


/***********************************************************
 *                     GObjectClass                        *
 ***********************************************************/
static void
glade_property_label_finalize (GObject *object)
{
  GladePropertyLabel *label = GLADE_PROPERTY_LABEL (object);

  g_free (label->priv->property_name);

  G_OBJECT_CLASS (glade_property_label_parent_class)->finalize (object);
}

static void
glade_property_label_dispose (GObject *object)
{
  GladePropertyLabel *label = GLADE_PROPERTY_LABEL (object);

  glade_property_label_set_property (label, NULL);

  G_OBJECT_CLASS (glade_property_label_parent_class)->dispose (object);
}

static void
glade_property_label_set_real_property (GObject         *object,
					guint            prop_id,
					const GValue    *value,
					GParamSpec      *pspec)
{
  GladePropertyLabel *label = GLADE_PROPERTY_LABEL (object);

  switch (prop_id)
    {
    case PROP_PROPERTY:
      glade_property_label_set_property (label, g_value_get_object (value));
      break;
    case PROP_PROPERTY_NAME:
      glade_property_label_set_property_name (label, g_value_get_string (value));
      break;
    case PROP_APPEND_COLON:
      glade_property_label_set_append_colon (label, g_value_get_boolean (value));
      break;
    case PROP_PACKING:
      glade_property_label_set_packing (label, g_value_get_boolean (value));
      break;
    case PROP_CUSTOM_TEXT:
      glade_property_label_set_custom_text (label, g_value_get_string (value));
      break;
    case PROP_CUSTOM_TOOLTIP:
      glade_property_label_set_custom_tooltip (label, g_value_get_string (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
glade_property_label_get_real_property (GObject         *object,
					guint            prop_id,
					GValue          *value,
					GParamSpec      *pspec)
{
  GladePropertyLabel *label = GLADE_PROPERTY_LABEL (object);

  switch (prop_id)
    {
    case PROP_PROPERTY:
      g_value_set_object (value, glade_property_label_get_property (label));
      break;
    case PROP_PROPERTY_NAME:
      g_value_set_string (value, glade_property_label_get_property_name (label));
      break;
    case PROP_PACKING:
      g_value_set_boolean (value, glade_property_label_get_packing (label));
      break;
    case PROP_APPEND_COLON:
      g_value_set_boolean (value, glade_property_label_get_append_colon (label));
      break;
    case PROP_CUSTOM_TEXT:
      g_value_set_string (value, glade_property_label_get_custom_text (label));
      break;
    case PROP_CUSTOM_TOOLTIP:
      g_value_set_string (value, glade_property_label_get_custom_tooltip (label));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

/*******************************************************************************
 *                            GladeEditableIface                               *                               
 *******************************************************************************/
static void
glade_property_label_load (GladeEditable   *editable,
			   GladeWidget     *widget)
{
  GladePropertyLabel *label = GLADE_PROPERTY_LABEL (editable);
  GladePropertyLabelPrivate *priv;
  GladeProperty *property;

  /* Chain up to default implementation */
  parent_editable_iface->load (editable, widget);

  g_return_if_fail (label->priv->property_name != NULL);

  priv = label->priv;

  if (widget)
    {
      if (priv->packing)
	property = glade_widget_get_pack_property (widget, priv->property_name);
      else
	property = glade_widget_get_property (widget, priv->property_name);

      glade_property_label_set_property (label, property);
    }
  else
    glade_property_label_set_property (label, NULL);
}

static void
glade_property_label_set_show_name (GladeEditable * editable, gboolean show_name)
{
}

static void
glade_property_label_editable_init (GladeEditableIface * iface)
{
  parent_editable_iface = g_type_default_interface_peek (GLADE_TYPE_EDITABLE);

  iface->load = glade_property_label_load;
  iface->set_show_name = glade_property_label_set_show_name;
}

/***********************************************************
 *                     GtkWidgetClass                      *
 ***********************************************************/
static gint
glade_property_label_button_press (GtkWidget       *widget,
				   GdkEventButton  *event)
{
  GladePropertyLabel        *label = GLADE_PROPERTY_LABEL (widget);
  GladePropertyLabelPrivate *priv = label->priv;

  if (priv->property && glade_popup_is_popup_event (event))
    {
      glade_popup_property_pop (priv->property, event);
      return TRUE;
    }

  return FALSE;
}

/***********************************************************
 *                        Callbacks                        *
 ***********************************************************/
static void
glade_property_label_tooltip_cb (GladeProperty      *property,
				 const gchar        *tooltip,
				 const gchar        *insensitive,
				 const gchar        *support,
				 GladePropertyLabel *label)
{
  GladePropertyLabelPrivate *priv = label->priv;
  const gchar *choice_tooltip;

  if (glade_property_get_sensitive (property))
    choice_tooltip = tooltip;
  else
    choice_tooltip = insensitive;

  if (!priv->custom_tooltip)
    gtk_widget_set_tooltip_text (priv->label, choice_tooltip);

  gtk_widget_set_tooltip_text (priv->warning, support);
}

static void
glade_property_label_sensitivity_cb (GladeProperty      *property,
				     GParamSpec         *pspec,
				     GladePropertyLabel *label)
{
  GladePropertyLabelPrivate *priv = label->priv;
  gboolean sensitive;

  sensitive = glade_property_get_enabled (property);
  sensitive = sensitive && glade_property_get_sensitive (priv->property);
  sensitive = sensitive && (glade_property_get_state (priv->property) & GLADE_STATE_SUPPORT_DISABLED) == 0;

  gtk_widget_set_sensitive (priv->box, sensitive);
}

static PangoAttrList *
get_modified_attribute (void)
{
  static PangoAttrList *attrs = NULL;

  if (!attrs)
    {
      PangoAttribute *attr;

      attrs = pango_attr_list_new ();
      attr  = pango_attr_style_new (PANGO_STYLE_ITALIC);
      pango_attr_list_insert (attrs, attr);
    }

  return attrs;
}

static void
glade_property_label_state_cb (GladeProperty      *property,
			       GParamSpec         *pspec,
			       GladePropertyLabel *label)
{
  GladePropertyLabelPrivate *priv = label->priv;

  if (!priv->property)
    return;

  /* refresh label */
  if ((glade_property_get_state (priv->property) & GLADE_STATE_CHANGED) != 0)
    gtk_label_set_attributes (GTK_LABEL (priv->label), get_modified_attribute());
  else
    gtk_label_set_attributes (GTK_LABEL (priv->label), NULL);

  /* refresh icon */
  if ((glade_property_get_state (priv->property) & GLADE_STATE_UNSUPPORTED) != 0)
    gtk_widget_show (priv->warning);
  else
    gtk_widget_hide (priv->warning);
}

static void
glade_property_label_property_finalized (GladePropertyLabel *label,
					 GladeProperty *where_property_was)
{
  /* Silent disconnect */
  label->priv->property = NULL;
  label->priv->tooltip_id = 0;
  label->priv->state_id = 0;
  label->priv->sensitive_id = 0;
  label->priv->enabled_id = 0;
}

/***********************************************************
 *                            API                          *
 ***********************************************************/
GtkWidget *
glade_property_label_new (void)
{
  return g_object_new (GLADE_TYPE_PROPERTY_LABEL, NULL);
}

void
glade_property_label_set_property_name (GladePropertyLabel *label,
					const gchar        *property_name)
{
  GladePropertyLabelPrivate *priv;

  g_return_if_fail (GLADE_IS_PROPERTY_LABEL (label));

  priv = label->priv;

  if (g_strcmp0 (priv->property_name, property_name))
    {
      g_free (priv->property_name);
      priv->property_name = g_strdup (property_name);

      g_object_notify (G_OBJECT (label), "property-name");
    }
}

const gchar *
glade_property_label_get_property_name (GladePropertyLabel *label)
{
  g_return_val_if_fail (GLADE_IS_PROPERTY_LABEL (label), NULL);

  return label->priv->property_name;
}

void
glade_property_label_set_append_colon (GladePropertyLabel *label,
				       gboolean            append_colon)
{
  GladePropertyLabelPrivate *priv;

  g_return_if_fail (GLADE_IS_PROPERTY_LABEL (label));

  priv = label->priv;

  if (priv->append_colon != append_colon)
    {
      priv->append_colon = append_colon;

      g_object_notify (G_OBJECT (label), "append-colon");
    }
}

gboolean
glade_property_label_get_append_colon (GladePropertyLabel *label)
{
  g_return_val_if_fail (GLADE_IS_PROPERTY_LABEL (label), FALSE);

  return label->priv->append_colon;
}

void
glade_property_label_set_packing (GladePropertyLabel *label,
				  gboolean            packing)
{
  GladePropertyLabelPrivate *priv;

  g_return_if_fail (GLADE_IS_PROPERTY_LABEL (label));

  priv = label->priv;

  if (priv->packing != packing)
    {
      priv->packing = packing;

      g_object_notify (G_OBJECT (label), "packing");
    }
}

gboolean
glade_property_label_get_packing (GladePropertyLabel *label)
{
  g_return_val_if_fail (GLADE_IS_PROPERTY_LABEL (label), FALSE);

  return label->priv->packing;
}

void
glade_property_label_set_custom_text (GladePropertyLabel *label,
				      const gchar        *custom_text)
{
  GladePropertyLabelPrivate *priv;
  gboolean changed = FALSE;

  g_return_if_fail (GLADE_IS_PROPERTY_LABEL (label));

  priv = label->priv;

  if (custom_text)
    {
      if (!priv->custom_text)
	changed = TRUE;

      priv->custom_text = TRUE;

      gtk_label_set_markup (GTK_LABEL (priv->label), custom_text);
    }
  else
    {
      if (priv->custom_text)
	changed = TRUE;

      priv->custom_text = FALSE;

      if (priv->property)
	  glade_property_label_state_cb (priv->property, NULL, label);
    }

  if (changed)
    g_object_notify (G_OBJECT (label), "custom-text");
}

const gchar *
glade_property_label_get_custom_text (GladePropertyLabel *label)
{
  GladePropertyLabelPrivate *priv;

  g_return_val_if_fail (GLADE_IS_PROPERTY_LABEL (label), NULL);

  priv = label->priv;

  if (priv->custom_text)
    return gtk_label_get_text (GTK_LABEL (priv->label));

  return NULL;
}

void
glade_property_label_set_custom_tooltip (GladePropertyLabel *label,
					 const gchar        *custom_tooltip)
{
  GladePropertyLabelPrivate *priv;
  gboolean changed = FALSE;

  g_return_if_fail (GLADE_IS_PROPERTY_LABEL (label));

  priv = label->priv;

  if (custom_tooltip)
    {
      if (!priv->custom_tooltip)
	changed = TRUE;

      priv->custom_tooltip = TRUE;

      gtk_widget_set_tooltip_text (GTK_WIDGET (priv->label), custom_tooltip);
    }
  else
    {
      if (priv->custom_tooltip)
	changed = TRUE;

      priv->custom_tooltip = FALSE;

      if (priv->property)
	{
	  GladePropertyClass *pclass = glade_property_get_class (priv->property);

	  glade_property_label_tooltip_cb
	    (priv->property, glade_property_class_get_tooltip (pclass),
	     glade_propert_get_insensitive_tooltip (priv->property),
	     glade_property_get_support_warning (priv->property), label);
	}
    }

  if (changed)
    g_object_notify (G_OBJECT (label), "custom-tooltip");
}

const gchar *
glade_property_label_get_custom_tooltip (GladePropertyLabel *label)
{
  GladePropertyLabelPrivate *priv;

  g_return_val_if_fail (GLADE_IS_PROPERTY_LABEL (label), NULL);

  priv = label->priv;

  if (priv->custom_tooltip)
    return gtk_widget_get_tooltip_text (priv->label);

  return NULL;
}

void
glade_property_label_set_property (GladePropertyLabel    *label,
				   GladeProperty         *property)
{
  GladePropertyLabelPrivate *priv;

  g_return_if_fail (GLADE_IS_PROPERTY_LABEL (label));
  g_return_if_fail (property == NULL || GLADE_IS_PROPERTY (property));

  priv = label->priv;

  if (priv->property != property)
    {

      /* Disconnect last */
      if (priv->property)
	{
	  if (priv->tooltip_id > 0)
	    g_signal_handler_disconnect (priv->property, priv->tooltip_id);
	  if (priv->state_id > 0)
	    g_signal_handler_disconnect (priv->property, priv->state_id);
	  if (priv->sensitive_id > 0)
	    g_signal_handler_disconnect (priv->property, priv->sensitive_id);
	  if (priv->enabled_id > 0)
	    g_signal_handler_disconnect (priv->property, priv->enabled_id);

	  priv->tooltip_id = 0;
	  priv->state_id = 0;
	  priv->sensitive_id = 0;
	  priv->enabled_id = 0;

	  g_object_weak_unref (G_OBJECT (priv->property),
			       (GWeakNotify) glade_property_label_property_finalized, label);
	}

      priv->property = property;

      /* Connect new */
      if (priv->property)
	{
	  GladePropertyClass *pclass = glade_property_get_class (priv->property);

	  priv->tooltip_id =
	    g_signal_connect (G_OBJECT (priv->property),
			      "tooltip-changed",
			      G_CALLBACK (glade_property_label_tooltip_cb),
			      label);
	  priv->sensitive_id =
	    g_signal_connect (G_OBJECT (priv->property),
			      "notify::sensitive",
			      G_CALLBACK (glade_property_label_sensitivity_cb),
			      label);
	  priv->state_id =
	    g_signal_connect (G_OBJECT (priv->property),
			      "notify::state",
			      G_CALLBACK (glade_property_label_state_cb), label);
	  priv->enabled_id =
	    g_signal_connect (G_OBJECT (priv->property),
			      "notify::enabled",
			      G_CALLBACK (glade_property_label_sensitivity_cb),
			      label);

	  g_object_weak_ref (G_OBJECT (priv->property),
			     (GWeakNotify) glade_property_label_property_finalized, label);

	  /* Load initial tooltips
	   */
	  glade_property_label_tooltip_cb
	    (property, glade_property_class_get_tooltip (pclass),
	     glade_propert_get_insensitive_tooltip (property),
	     glade_property_get_support_warning (property), label);

	  /* Load initial sensitive state.
	   */
	  glade_property_label_sensitivity_cb (property, NULL, label);

	  /* Load intial label state
	   */
	  glade_property_label_state_cb (property, NULL, label);

	  if (!priv->custom_text)
	    {
	      if (priv->append_colon)
		{
		  gchar *text = g_strdup_printf ("%s:", glade_property_class_get_name (pclass));
		  gtk_label_set_text (GTK_LABEL (priv->label), text);
		  g_free (text);
		}
	      else
		{
		  gtk_label_set_text (GTK_LABEL (priv->label),
				      glade_property_class_get_name (pclass));
		}
	    }
	}

      g_object_notify (G_OBJECT (label), "property");
    }
}

GladeProperty *
glade_property_label_get_property (GladePropertyLabel *label)
{
  g_return_val_if_fail (GLADE_IS_PROPERTY_LABEL (label), NULL);

  return label->priv->property;
}
