/* LIBPIKA - The PIKA Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * pikalabeled.c
 * Copyright (C) 2020 Jehan
 *
 * 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 3 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 Lesser General Public
 * License along with this library.  If not, see
 * <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <gegl.h>
#include <gtk/gtk.h>

#include "libpikamath/pikamath.h"
#include "libpikabase/pikabase.h"

#include "pikawidgets.h"


/**
 * SECTION: pikalabeled
 * @title: PikaLabeled
 * @short_description: Widget containing a label as mnemonic for another
 *                     widget.
 *
 * This widget is a #GtkGrid showing a #GtkLabel used as mnemonic on
 * another widget.
 **/

enum
{
  MNEMONIC_WIDGET_CHANGED,
  LAST_SIGNAL
};

enum
{
  PROP_0,
  PROP_LABEL,
};

typedef struct _PikaLabeledPrivate
{
  GtkWidget *label;
} PikaLabeledPrivate;


static void       pika_labeled_constructed            (GObject       *object);
static void       pika_labeled_set_property           (GObject       *object,
                                                       guint          property_id,
                                                       const GValue  *value,
                                                       GParamSpec    *pspec);
static void       pika_labeled_get_property           (GObject       *object,
                                                       guint          property_id,
                                                       GValue        *value,
                                                       GParamSpec    *pspec);

static void pika_labeled_real_mnemonic_widget_changed (PikaLabeled   *labeled,
                                                       GtkWidget     *widget);


G_DEFINE_TYPE_WITH_PRIVATE (PikaLabeled, pika_labeled, GTK_TYPE_GRID)

#define parent_class pika_labeled_parent_class

static guint signals[LAST_SIGNAL] = { 0 };

static void
pika_labeled_class_init (PikaLabeledClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->constructed  = pika_labeled_constructed;
  object_class->set_property = pika_labeled_set_property;
  object_class->get_property = pika_labeled_get_property;

  signals[MNEMONIC_WIDGET_CHANGED] =
    g_signal_new ("mnemonic-widget-changed",
                  G_TYPE_FROM_CLASS (klass),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (PikaLabeledClass, mnemonic_widget_changed),
                  NULL, NULL, NULL,
                  G_TYPE_NONE, 1,
                  GTK_TYPE_WIDGET);

  klass->mnemonic_widget_changed = pika_labeled_real_mnemonic_widget_changed;

  /**
   * PikaLabeled:label:
   *
   * Label text with pango markup and mnemonic.
   *
   * Since: 3.0
   **/
  g_object_class_install_property (object_class, PROP_LABEL,
                                   g_param_spec_string ("label",
                                                        "Label text",
                                                        "The text of the label part of this widget",
                                                        NULL,
                                                        PIKA_PARAM_READWRITE));
}

static void
pika_labeled_init (PikaLabeled *labeled)
{
}

static void
pika_labeled_constructed (GObject *object)
{
  PikaLabeledClass   *klass;
  PikaLabeled        *labeled = PIKA_LABELED (object);
  PikaLabeledPrivate *priv    = pika_labeled_get_instance_private (labeled);
  GtkWidget          *mnemonic_widget;
  gint                x       = 0;
  gint                y       = 0;
  gint                width   = 1;
  gint                height  = 1;

  G_OBJECT_CLASS (parent_class)->constructed (object);

  priv->label = gtk_label_new_with_mnemonic (NULL);
  gtk_label_set_xalign (GTK_LABEL (priv->label), 0.0);

  klass = PIKA_LABELED_GET_CLASS (labeled);
  g_return_if_fail (klass->populate);
  mnemonic_widget = klass->populate (labeled, &x, &y, &width, &height);

  g_signal_emit (object, signals[MNEMONIC_WIDGET_CHANGED], 0,
                 mnemonic_widget);

  gtk_grid_attach (GTK_GRID (labeled), priv->label, x, y, width, height);
  gtk_widget_show (priv->label);
}

static void
pika_labeled_set_property (GObject      *object,
                           guint         property_id,
                           const GValue *value,
                           GParamSpec   *pspec)
{
  PikaLabeled        *entry = PIKA_LABELED (object);
  PikaLabeledPrivate *priv  = pika_labeled_get_instance_private (entry);

  switch (property_id)
    {
    case PROP_LABEL:
        {
          /* This should not happen since the property is **not** set with
           * G_PARAM_CONSTRUCT, hence the label should exist when the
           * property is first set.
           */
          g_return_if_fail (priv->label);

          gtk_label_set_markup_with_mnemonic (GTK_LABEL (priv->label),
                                              g_value_get_string (value));
        }
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
pika_labeled_get_property (GObject    *object,
                           guint       property_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
  PikaLabeled        *entry = PIKA_LABELED (object);
  PikaLabeledPrivate *priv  = pika_labeled_get_instance_private (entry);

  switch (property_id)
    {
    case PROP_LABEL:
      g_value_set_string (value, gtk_label_get_label (GTK_LABEL (priv->label)));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
pika_labeled_real_mnemonic_widget_changed (PikaLabeled *labeled,
                                           GtkWidget   *widget)
{
  PikaLabeledPrivate *priv  = pika_labeled_get_instance_private (labeled);

  gtk_label_set_mnemonic_widget (GTK_LABEL (priv->label), widget);
}

/* Public functions */

/**
 * pika_labeled_get_label:
 * @labeled: The #PikaLabeled.
 *
 * This function returns the #GtkLabel packed in @labeled. This can be
 * useful if you need to customize some aspects of the widget.
 *
 * Returns: (transfer none) (type GtkLabel): The #GtkLabel contained in @labeled.
 **/
GtkWidget *
pika_labeled_get_label (PikaLabeled *labeled)
{
  PikaLabeledPrivate *priv = pika_labeled_get_instance_private (labeled);

  g_return_val_if_fail (PIKA_IS_LABELED (labeled), NULL);

  return priv->label;
}

/**
 * pika_labeled_get_text:
 * @labeled: the #PikaLabeled.
 *
 * This function will return exactly what you entered with
 * pika_labeled_set_text() or through the "label" property because this
 * class expects labels to have mnemonics (and allows Pango formatting).
 * To obtain instead the text as displayed with mnemonics and markup
 * removed, call:
 * |[<!-- language="C" -->
 * gtk_label_get_text (GTK_LABEL (pika_labeled_get_label (@labeled)));
 * ]|
 *
 * Returns: the label text as entered, which includes pango markup and
 *          mnemonics similarly to gtk_label_get_label().
 */
const gchar *
pika_labeled_get_text (PikaLabeled *labeled)
{
  PikaLabeledPrivate *priv = pika_labeled_get_instance_private (labeled);

  g_return_val_if_fail (PIKA_IS_LABELED (labeled), NULL);

  return gtk_label_get_label (GTK_LABEL (priv->label));
}

/**
 * pika_labeled_set_text:
 * @labeled: the #PikaLabeled.
 * @text: label text with Pango markup and mnemonic.
 *
 * This is the equivalent of running
 * gtk_label_set_markup_with_mnemonic() on the #GtkLabel as a
 * #PikaLabeled expects a mnemonic. Pango markup are also allowed.
 */
void
pika_labeled_set_text (PikaLabeled *labeled,
                       const gchar *text)
{
  PikaLabeledPrivate *priv = pika_labeled_get_instance_private (labeled);

  g_return_if_fail (PIKA_IS_LABELED (labeled));

  gtk_label_set_markup_with_mnemonic (GTK_LABEL (priv->label), text);
}
