/* LIBPIKA - The PIKA Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * pikacolornotebook.c
 * Copyright (C) 2002 Michael Natterer <mitch@gimp.org>
 *
 * based on color_notebook module
 * Copyright (C) 1998 Austin Donnelly <austin@greenend.org.uk>
 *
 * 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 "libpikacolor/pikacolor.h"

#include "pikawidgetstypes.h"

#include "pikacolornotebook.h"
#include "pikacolorscales.h"
#include "pikahelpui.h"

#include "libpika/libpika-intl.h"


/**
 * SECTION: pikacolornotebook
 * @title: PikaColorNotebook
 * @short_description: A #PikaColorSelector implementation.
 *
 * The #PikaColorNotebook widget is an implementation of a
 * #PikaColorSelector. It serves as a container for
 * #PikaColorSelectors.
 **/


#define DEFAULT_TAB_ICON_SIZE  GTK_ICON_SIZE_BUTTON


struct _PikaColorNotebookPrivate
{
  GtkWidget         *notebook;

  GList             *selectors;
  PikaColorSelector *cur_page;
};

#define GET_PRIVATE(obj) (((PikaColorNotebook *) obj)->priv)


static void   pika_color_notebook_style_updated   (GtkWidget         *widget);

static void   pika_color_notebook_togg_visible    (PikaColorSelector *selector,
                                                   gboolean           visible);
static void   pika_color_notebook_togg_sensitive  (PikaColorSelector *selector,
                                                   gboolean           sensitive);
static void   pika_color_notebook_set_show_alpha  (PikaColorSelector *selector,
                                                   gboolean           show_alpha);
static void   pika_color_notebook_set_color       (PikaColorSelector *selector,
                                                   const PikaRGB     *rgb,
                                                   const PikaHSV     *hsv);
static void   pika_color_notebook_set_channel     (PikaColorSelector *selector,
                                                   PikaColorSelectorChannel channel);
static void   pika_color_notebook_set_model_visible
                                                  (PikaColorSelector *selector,
                                                   PikaColorSelectorModel model,
                                                   gboolean           gboolean);
static void   pika_color_notebook_set_config      (PikaColorSelector *selector,
                                                   PikaColorConfig   *config);


static void   pika_color_notebook_switch_page     (GtkNotebook       *gtk_notebook,
                                                   gpointer           page,
                                                   guint              page_num,
                                                   PikaColorNotebook *notebook);

static void   pika_color_notebook_color_changed   (PikaColorSelector *page,
                                                   const PikaRGB     *rgb,
                                                   const PikaHSV     *hsv,
                                                   PikaColorNotebook *notebook);
static void   pika_color_notebook_channel_changed (PikaColorSelector *page,
                                                   PikaColorSelectorChannel channel,
                                                   PikaColorNotebook *notebook);
static void   pika_color_notebook_model_visible_changed
                                                  (PikaColorSelector *page,
                                                   PikaColorSelectorModel model,
                                                   gboolean           visible,
                                                   PikaColorNotebook *notebook);

static GtkWidget * pika_color_notebook_add_page   (PikaColorNotebook *notebook,
                                                   GType              page_type);
static void   pika_color_notebook_remove_selector (GtkContainer      *container,
                                                   GtkWidget         *widget,
                                                   PikaColorNotebook *notebook);


G_DEFINE_TYPE_WITH_PRIVATE (PikaColorNotebook, pika_color_notebook,
                            PIKA_TYPE_COLOR_SELECTOR)

#define parent_class pika_color_notebook_parent_class


static void
pika_color_notebook_class_init (PikaColorNotebookClass *klass)
{
  GtkWidgetClass         *widget_class   = GTK_WIDGET_CLASS (klass);
  PikaColorSelectorClass *selector_class = PIKA_COLOR_SELECTOR_CLASS (klass);

  widget_class->style_updated           = pika_color_notebook_style_updated;

  selector_class->name                  = "Notebook";
  selector_class->help_id               = "pika-colorselector-notebook";
  selector_class->set_toggles_visible   = pika_color_notebook_togg_visible;
  selector_class->set_toggles_sensitive = pika_color_notebook_togg_sensitive;
  selector_class->set_show_alpha        = pika_color_notebook_set_show_alpha;
  selector_class->set_color             = pika_color_notebook_set_color;
  selector_class->set_channel           = pika_color_notebook_set_channel;
  selector_class->set_model_visible     = pika_color_notebook_set_model_visible;
  selector_class->set_config            = pika_color_notebook_set_config;

  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_enum ("tab-icon-size",
                                                              NULL,
                                                              "Size for icons displayed in the tab",
                                                              GTK_TYPE_ICON_SIZE,
                                                              DEFAULT_TAB_ICON_SIZE,
                                                              G_PARAM_READABLE));

  gtk_widget_class_set_css_name (widget_class, "PikaColorNotebook");
}

static void
pika_color_notebook_init (PikaColorNotebook *notebook)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (notebook);
  GType                    *selector_types;
  guint                     n_selector_types;
  guint                     i;

  notebook->priv = pika_color_notebook_get_instance_private (notebook);

  private = notebook->priv;

  private->notebook = gtk_notebook_new ();
  gtk_notebook_popup_enable (GTK_NOTEBOOK (private->notebook));
  gtk_box_pack_start (GTK_BOX (notebook), private->notebook, TRUE, TRUE, 0);
  gtk_widget_show (private->notebook);

  g_signal_connect (private->notebook, "switch-page",
                    G_CALLBACK (pika_color_notebook_switch_page),
                    notebook);
  g_signal_connect (private->notebook, "remove",
                    G_CALLBACK (pika_color_notebook_remove_selector),
                    notebook);

  selector_types = g_type_children (PIKA_TYPE_COLOR_SELECTOR,
                                    &n_selector_types);

  if (n_selector_types == 2)
    {
      gtk_notebook_set_show_tabs (GTK_NOTEBOOK (private->notebook), FALSE);
      gtk_notebook_set_show_border (GTK_NOTEBOOK (private->notebook), FALSE);
    }

  for (i = 0; i < n_selector_types; i++)
    {
      /*  skip ourselves  */
      if (g_type_is_a (selector_types[i], PIKA_TYPE_COLOR_NOTEBOOK))
        continue;

      /*  skip the "Scales" color selector  */
      if (g_type_is_a (selector_types[i], PIKA_TYPE_COLOR_SCALES))
        continue;

      pika_color_notebook_add_page (notebook, selector_types[i]);
    }

  g_free (selector_types);
}

static void
pika_color_notebook_style_updated (GtkWidget *widget)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (widget);
  GList                    *list;
  GtkIconSize               icon_size;

  GTK_WIDGET_CLASS (parent_class)->style_updated (widget);

  gtk_widget_style_get (widget,
                        "tab-icon-size", &icon_size,
                        NULL);

  for (list = private->selectors; list; list = g_list_next (list))
    {
      PikaColorSelectorClass *selector_class;
      GtkWidget              *image;

      selector_class = PIKA_COLOR_SELECTOR_GET_CLASS (list->data);

      image = gtk_image_new_from_icon_name (selector_class->icon_name,
                                            icon_size);
      pika_help_set_help_data (image, gettext (selector_class->name), NULL);

      gtk_notebook_set_tab_label (GTK_NOTEBOOK (private->notebook),
                                  GTK_WIDGET (list->data),
                                  image);
    }
}

static void
pika_color_notebook_togg_visible (PikaColorSelector *selector,
                                  gboolean           visible)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (selector);
  GList                    *list;

  for (list = private->selectors; list; list = g_list_next (list))
    {
      PikaColorSelector *child = list->data;

      pika_color_selector_set_toggles_visible (child, visible);
    }
}

static void
pika_color_notebook_togg_sensitive (PikaColorSelector *selector,
                                    gboolean           sensitive)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (selector);
  GList                    *list;

  for (list = private->selectors; list; list = g_list_next (list))
    {
      PikaColorSelector *child = list->data;

      pika_color_selector_set_toggles_sensitive (child, sensitive);
    }
}

static void
pika_color_notebook_set_show_alpha (PikaColorSelector *selector,
                                    gboolean           show_alpha)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (selector);
  GList                    *list;

  for (list = private->selectors; list; list = g_list_next (list))
    {
      PikaColorSelector *child = list->data;

      pika_color_selector_set_show_alpha (child, show_alpha);
    }
}

static void
pika_color_notebook_set_color (PikaColorSelector *selector,
                               const PikaRGB     *rgb,
                               const PikaHSV     *hsv)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (selector);

  g_signal_handlers_block_by_func (private->cur_page,
                                   pika_color_notebook_color_changed,
                                   selector);

  pika_color_selector_set_color (private->cur_page, rgb, hsv);

  g_signal_handlers_unblock_by_func (private->cur_page,
                                     pika_color_notebook_color_changed,
                                     selector);
}

static void
pika_color_notebook_set_channel (PikaColorSelector        *selector,
                                 PikaColorSelectorChannel  channel)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (selector);

  g_signal_handlers_block_by_func (private->cur_page,
                                   pika_color_notebook_channel_changed,
                                   selector);

  pika_color_selector_set_channel (private->cur_page, channel);

  g_signal_handlers_unblock_by_func (private->cur_page,
                                     pika_color_notebook_channel_changed,
                                     selector);
}

static void
pika_color_notebook_set_model_visible (PikaColorSelector      *selector,
                                       PikaColorSelectorModel  model,
                                       gboolean                visible)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (selector);

  g_signal_handlers_block_by_func (private->cur_page,
                                   pika_color_notebook_model_visible_changed,
                                   selector);

  pika_color_selector_set_model_visible (private->cur_page, model, visible);

  g_signal_handlers_unblock_by_func (private->cur_page,
                                     pika_color_notebook_model_visible_changed,
                                     selector);
}

static void
pika_color_notebook_set_config (PikaColorSelector *selector,
                                PikaColorConfig   *config)
{
  PikaColorNotebookPrivate *private = GET_PRIVATE (selector);
  GList                    *list;

  for (list = private->selectors; list; list = g_list_next (list))
    {
      PikaColorSelector *child = list->data;

      pika_color_selector_set_config (child, config);
    }
}

static void
pika_color_notebook_switch_page (GtkNotebook       *gtk_notebook,
                                 gpointer           page,
                                 guint              page_num,
                                 PikaColorNotebook *notebook)
{
  PikaColorNotebookPrivate *private  = GET_PRIVATE (notebook);
  PikaColorSelector        *selector = PIKA_COLOR_SELECTOR (notebook);
  GtkWidget                *page_widget;
  PikaColorSelectorModel    model;

  page_widget = gtk_notebook_get_nth_page (gtk_notebook, page_num);

  private->cur_page = PIKA_COLOR_SELECTOR (page_widget);

  g_signal_handlers_block_by_func (private->cur_page,
                                   pika_color_notebook_color_changed,
                                   notebook);
  g_signal_handlers_block_by_func (private->cur_page,
                                   pika_color_notebook_channel_changed,
                                   notebook);
  g_signal_handlers_block_by_func (private->cur_page,
                                   pika_color_notebook_model_visible_changed,
                                   notebook);

  pika_color_selector_set_color (private->cur_page,
                                 &selector->rgb,
                                 &selector->hsv);
  pika_color_selector_set_channel (private->cur_page,
                                   pika_color_selector_get_channel (selector));

  for (model = PIKA_COLOR_SELECTOR_MODEL_RGB;
       model <= PIKA_COLOR_SELECTOR_MODEL_HSV;
       model++)
    {
      gboolean visible = pika_color_selector_get_model_visible (selector, model);

      pika_color_selector_set_model_visible (private->cur_page, model,
                                             visible);
    }

  g_signal_handlers_unblock_by_func (private->cur_page,
                                     pika_color_notebook_color_changed,
                                     notebook);
  g_signal_handlers_unblock_by_func (private->cur_page,
                                     pika_color_notebook_channel_changed,
                                     notebook);
  g_signal_handlers_unblock_by_func (private->cur_page,
                                     pika_color_notebook_model_visible_changed,
                                     notebook);
}

static void
pika_color_notebook_color_changed (PikaColorSelector *page,
                                   const PikaRGB     *rgb,
                                   const PikaHSV     *hsv,
                                   PikaColorNotebook *notebook)
{
  PikaColorSelector *selector = PIKA_COLOR_SELECTOR (notebook);

  selector->rgb = *rgb;
  selector->hsv = *hsv;

  pika_color_selector_emit_color_changed (selector);
}

static void
pika_color_notebook_channel_changed (PikaColorSelector        *page,
                                     PikaColorSelectorChannel  channel,
                                     PikaColorNotebook        *notebook)
{
  PikaColorSelector *selector = PIKA_COLOR_SELECTOR (notebook);

  pika_color_selector_set_channel (selector, channel);
}

static void
pika_color_notebook_model_visible_changed (PikaColorSelector      *page,
                                           PikaColorSelectorModel  model,
                                           gboolean                visible,
                                           PikaColorNotebook      *notebook)
{
  PikaColorSelector *selector = PIKA_COLOR_SELECTOR (notebook);

  pika_color_selector_set_model_visible (selector, model, visible);
}

static GtkWidget *
pika_color_notebook_add_page (PikaColorNotebook *notebook,
                              GType              page_type)
{
  PikaColorNotebookPrivate *private  = GET_PRIVATE (notebook);
  PikaColorSelector        *selector = PIKA_COLOR_SELECTOR (notebook);
  PikaColorSelectorClass   *selector_class;
  GtkWidget                *page;
  GtkWidget                *menu_widget;
  GtkWidget                *image;
  GtkWidget                *label;
  gboolean                  show_alpha;

  page = pika_color_selector_new (page_type,
                                  &selector->rgb,
                                  &selector->hsv,
                                  pika_color_selector_get_channel (selector));

  if (! page)
    return NULL;

  selector_class = PIKA_COLOR_SELECTOR_GET_CLASS (page);

  show_alpha = pika_color_selector_get_show_alpha (PIKA_COLOR_SELECTOR (notebook));
  pika_color_selector_set_show_alpha (PIKA_COLOR_SELECTOR (page), show_alpha);

  menu_widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);

  image = gtk_image_new_from_icon_name (selector_class->icon_name,
                                        GTK_ICON_SIZE_MENU);
  gtk_box_pack_start (GTK_BOX (menu_widget), image, FALSE, FALSE, 0);
  gtk_widget_show (image);

  label = gtk_label_new (gettext (selector_class->name));
  gtk_box_pack_start (GTK_BOX (menu_widget), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

  image = gtk_image_new_from_icon_name (selector_class->icon_name,
                                        DEFAULT_TAB_ICON_SIZE);
  pika_help_set_help_data (image, gettext (selector_class->name), NULL);

  gtk_notebook_append_page_menu (GTK_NOTEBOOK (private->notebook),
                                 page, image, menu_widget);

  if (! private->cur_page)
    private->cur_page = PIKA_COLOR_SELECTOR (page);

  private->selectors = g_list_append (private->selectors, page);

  gtk_widget_show (page);

  g_signal_connect (page, "color-changed",
                    G_CALLBACK (pika_color_notebook_color_changed),
                    notebook);
  g_signal_connect (page, "channel-changed",
                    G_CALLBACK (pika_color_notebook_channel_changed),
                    notebook);
  g_signal_connect (page, "model-visible-changed",
                    G_CALLBACK (pika_color_notebook_model_visible_changed),
                    notebook);

  return page;
}

static void
pika_color_notebook_remove_selector (GtkContainer      *container,
                                     GtkWidget         *widget,
                                     PikaColorNotebook *notebook)
{
  notebook->priv->selectors = g_list_remove (notebook->priv->selectors,
                                             widget);

  if (! notebook->priv->selectors)
    notebook->priv->cur_page = NULL;
}


/**
 * pika_color_notebook_set_has_page:
 * @notebook:  A #PikaColorNotebook widget.
 * @page_type: The #GType of the notebook page to add or remove.
 * @has_page:  Whether the page should be added or removed.
 *
 * This function adds and removed pages to / from a #PikaColorNotebook.
 * The @page_type passed must be a #PikaColorSelector subtype.
 *
 * Returns: (transfer none): The new page widget, if @has_page was
 *               %TRUE, or %NULL if @has_page was %FALSE.
 **/
GtkWidget *
pika_color_notebook_set_has_page (PikaColorNotebook *notebook,
                                  GType              page_type,
                                  gboolean           has_page)
{
  GList *list;

  g_return_val_if_fail (PIKA_IS_COLOR_NOTEBOOK (notebook), NULL);
  g_return_val_if_fail (g_type_is_a (page_type, PIKA_TYPE_COLOR_SELECTOR),
                        NULL);
  g_return_val_if_fail (! g_type_is_a (page_type, PIKA_TYPE_COLOR_NOTEBOOK),
                        NULL);

  for (list = notebook->priv->selectors; list; list = g_list_next (list))
    {
      PikaColorSelector *page = list->data;

      if (G_TYPE_FROM_INSTANCE (page) == page_type)
        {
          if (has_page)
            return GTK_WIDGET (page);

          gtk_container_remove (GTK_CONTAINER (notebook->priv->notebook),
                                GTK_WIDGET (page));

          return NULL;
        }
    }

  if (! has_page)
    return NULL;

  return pika_color_notebook_add_page (notebook, page_type);
}

/**
 * pika_color_notebook_get_notebook:
 * @notebook:  A #PikaColorNotebook widget.
 *
 * Returns: (transfer none) (type GtkNotebook): The #GtkNotebook inside.
 *
 * Since: 3.0
 **/
GtkWidget *
pika_color_notebook_get_notebook (PikaColorNotebook *notebook)
{
  g_return_val_if_fail (PIKA_IS_COLOR_NOTEBOOK (notebook), NULL);

  return notebook->priv->notebook;
}

/**
 * pika_color_notebook_get_selectors:
 * @notebook:  A #PikaColorNotebook widget.
 *
 * Returns: (element-type PikaColorSelector) (transfer none): The
 *               notebook's list of #PikaColorSelector's.
 *
 * Since: 3.0
 **/
GList *
pika_color_notebook_get_selectors (PikaColorNotebook *notebook)
{
  g_return_val_if_fail (PIKA_IS_COLOR_NOTEBOOK (notebook), NULL);

  return notebook->priv->selectors;
}

/**
 * pika_color_notebook_get_current_selector:
 * @notebook:  A #PikaColorNotebook widget.
 *
 * Returns: (transfer none): The active page's #PikaColorSelector.
 *
 * Since: 3.0
 **/
PikaColorSelector *
pika_color_notebook_get_current_selector (PikaColorNotebook *notebook)
{
  g_return_val_if_fail (PIKA_IS_COLOR_NOTEBOOK (notebook), NULL);

  return notebook->priv->cur_page;
}

/**
 * pika_color_notebook_set_simulation:
 * @notebook:  A #PikaColorNotebook widget.
 * @profile:   A #PikaColorProfile object.
 * @intent:    A #PikaColorRenderingIntent enum.
 * @bpc:       A gboolean.
 *
 * Updates all selectors with the current simulation settings.
 *
 * Since: 3.0
 **/
void
pika_color_notebook_set_simulation (PikaColorNotebook *notebook,
                                    PikaColorProfile  *profile,
                                    PikaColorRenderingIntent intent,
                                    gboolean           bpc)
{
  GList *list;

  g_return_if_fail (PIKA_IS_COLOR_NOTEBOOK (notebook));
  g_return_if_fail (profile == NULL || PIKA_IS_COLOR_PROFILE (profile));

  for (list = notebook->priv->selectors; list; list = g_list_next (list))
    {
      PikaColorSelector *selector = list->data;

      if (selector)
        pika_color_selector_set_simulation (selector, profile, intent, bpc);
    }
}
