/* GIMP - The GNU Image Manipulation Program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * 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 3 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, see <https://www.gnu.org/licenses/>.
 */

#include "config.h"

#include <string.h>

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

#include "libgimpbase/gimpbase.h"
#include "libgimpwidgets/gimpwidgets.h"

#include "widgets-types.h"

#include "config/gimpguiconfig.h"

#include "core/gimp.h"
#include "core/gimpcontext.h"
#include "core/gimpdisplay.h"

#include "file/file-open.h"

#include "gimpcairo-wilber.h"
#include "gimpdevices.h"
#include "gimpdialogfactory.h"
#include "gimpdockwindow.h"
#include "gimphelp-ids.h"
#include "gimppanedbox.h"
#include "gimpviewrenderer.h"
#include "gimptoolbox.h"
#include "gimptoolbox-color-area.h"
#include "gimptoolbox-dnd.h"
#include "gimptoolbox-image-area.h"
#include "gimptoolbox-indicator-area.h"
#include "gimptoolpalette.h"
#include "gimpuimanager.h"
#include "gimpview.h"
#include "gimpwidgets-utils.h"

#include "about.h"

#include "gimp-intl.h"


enum
{
  PROP_0,
  PROP_CONTEXT
};


struct _GimpToolboxPrivate
{
  GimpContext       *context;

  GtkWidget         *vbox;

  GtkWidget         *header;
  GtkWidget         *tool_palette;
  GtkWidget         *area_box;
  GtkWidget         *color_area;
  GtkWidget         *foo_area;
  GtkWidget         *image_area;

  gint               area_rows;
  gint               area_columns;

  GimpPanedBox      *drag_handler;

  gboolean           in_destruction;
};


static void        gimp_toolbox_constructed             (GObject          *object);
static void        gimp_toolbox_dispose                 (GObject          *object);
static void        gimp_toolbox_set_property            (GObject          *object,
                                                         guint             property_id,
                                                         const GValue     *value,
                                                         GParamSpec       *pspec);
static void        gimp_toolbox_get_property            (GObject          *object,
                                                         guint             property_id,
                                                         GValue           *value,
                                                         GParamSpec       *pspec);

static void        gimp_toolbox_style_updated           (GtkWidget        *widget);
static gboolean    gimp_toolbox_button_press_event      (GtkWidget        *widget,
                                                         GdkEventButton   *event);
static void        gimp_toolbox_drag_leave              (GtkWidget        *widget,
                                                         GdkDragContext   *context,
                                                         guint             time,
                                                         GimpToolbox      *toolbox);
static gboolean    gimp_toolbox_drag_motion             (GtkWidget        *widget,
                                                         GdkDragContext   *context,
                                                         gint              x,
                                                         gint              y,
                                                         guint             time,
                                                         GimpToolbox      *toolbox);
static gboolean    gimp_toolbox_drag_drop               (GtkWidget        *widget,
                                                         GdkDragContext   *context,
                                                         gint              x,
                                                         gint              y,
                                                         guint             time,
                                                         GimpToolbox      *toolbox);
static gchar     * gimp_toolbox_get_description         (GimpDock         *dock,
                                                         gboolean          complete);
static void        gimp_toolbox_set_host_geometry_hints (GimpDock         *dock,
                                                         GtkWindow        *window);
static void        gimp_toolbox_book_added              (GimpDock         *dock,
                                                         GimpDockbook     *dockbook);
static void        gimp_toolbox_book_removed            (GimpDock         *dock,
                                                         GimpDockbook     *dockbook);

static void        gimp_toolbox_notify_theme            (GimpGuiConfig    *config,
                                                         GParamSpec       *pspec,
                                                         GimpToolbox      *toolbox);

static void        gimp_toolbox_palette_style_updated   (GtkWidget        *palette,
                                                         GimpToolbox      *toolbox);

static void        gimp_toolbox_wilber_style_updated    (GtkWidget        *widget,
                                                         GimpToolbox      *toolbox);
static gboolean    gimp_toolbox_draw_wilber             (GtkWidget        *widget,
                                                         cairo_t          *cr);
static GtkWidget * toolbox_create_color_area            (GimpToolbox      *toolbox,
                                                         GimpContext      *context);
static GtkWidget * toolbox_create_foo_area              (GimpToolbox      *toolbox,
                                                         GimpContext      *context);
static GtkWidget * toolbox_create_image_area            (GimpToolbox      *toolbox,
                                                         GimpContext      *context);
static void        toolbox_paste_received               (GtkClipboard     *clipboard,
                                                         const gchar      *text,
                                                         gpointer          data);
static void        toolbox_area_config_notify           (GimpGuiConfig    *config,
                                                         const GParamSpec *pspec,
                                                         GtkWidget        *area_box);



G_DEFINE_TYPE_WITH_PRIVATE (GimpToolbox, gimp_toolbox, GIMP_TYPE_DOCK)

#define parent_class gimp_toolbox_parent_class


static void
gimp_toolbox_class_init (GimpToolboxClass *klass)
{
  GObjectClass   *object_class = G_OBJECT_CLASS (klass);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
  GimpDockClass  *dock_class   = GIMP_DOCK_CLASS (klass);

  object_class->constructed           = gimp_toolbox_constructed;
  object_class->dispose               = gimp_toolbox_dispose;
  object_class->set_property          = gimp_toolbox_set_property;
  object_class->get_property          = gimp_toolbox_get_property;

  widget_class->button_press_event    = gimp_toolbox_button_press_event;
  widget_class->style_updated         = gimp_toolbox_style_updated;

  dock_class->get_description         = gimp_toolbox_get_description;
  dock_class->set_host_geometry_hints = gimp_toolbox_set_host_geometry_hints;
  dock_class->book_added              = gimp_toolbox_book_added;
  dock_class->book_removed            = gimp_toolbox_book_removed;

  g_object_class_install_property (object_class, PROP_CONTEXT,
                                   g_param_spec_object ("context",
                                                        NULL, NULL,
                                                        GIMP_TYPE_CONTEXT,
                                                        GIMP_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
}

static void
gimp_toolbox_init (GimpToolbox *toolbox)
{
  toolbox->p = gimp_toolbox_get_instance_private (toolbox);

  gimp_help_connect (GTK_WIDGET (toolbox), NULL, gimp_standard_help_func,
                     GIMP_HELP_TOOLBOX, NULL, NULL);
}

static void
gimp_toolbox_constructed (GObject *object)
{
  GimpToolbox   *toolbox = GIMP_TOOLBOX (object);
  GimpGuiConfig *config;
  GtkWidget     *main_vbox;
  GtkWidget     *event_box;

  gimp_assert (GIMP_IS_CONTEXT (toolbox->p->context));

  config = GIMP_GUI_CONFIG (toolbox->p->context->gimp->config);

  main_vbox = gimp_dock_get_main_vbox (GIMP_DOCK (toolbox));

  toolbox->p->vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
  gtk_box_pack_start (GTK_BOX (main_vbox), toolbox->p->vbox, FALSE, FALSE, 0);
  gtk_box_reorder_child (GTK_BOX (main_vbox), toolbox->p->vbox, 0);
  gtk_widget_set_visible (toolbox->p->vbox, TRUE);

  /* Use g_signal_connect() also for the toolbox itself so we can pass
   * data and reuse the same function for the vbox
   */
  g_signal_connect (toolbox, "drag-leave",
                    G_CALLBACK (gimp_toolbox_drag_leave),
                    toolbox);
  g_signal_connect (toolbox, "drag-motion",
                    G_CALLBACK (gimp_toolbox_drag_motion),
                    toolbox);
  g_signal_connect (toolbox, "drag-drop",
                    G_CALLBACK (gimp_toolbox_drag_drop),
                    toolbox);
  g_signal_connect (toolbox->p->vbox, "drag-leave",
                    G_CALLBACK (gimp_toolbox_drag_leave),
                    toolbox);
  g_signal_connect (toolbox->p->vbox, "drag-motion",
                    G_CALLBACK (gimp_toolbox_drag_motion),
                    toolbox);
  g_signal_connect (toolbox->p->vbox, "drag-drop",
                    G_CALLBACK (gimp_toolbox_drag_drop),
                    toolbox);

  g_signal_connect_object (config,
                           "notify::theme",
                           G_CALLBACK (gimp_toolbox_style_updated),
                           toolbox, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
  g_signal_connect_object (config,
                           "notify::override-theme-icon-size",
                           G_CALLBACK (gimp_toolbox_style_updated),
                           toolbox, G_CONNECT_AFTER | G_CONNECT_SWAPPED);
  g_signal_connect_object (config,
                           "notify::custom-icon-size",
                           G_CALLBACK (gimp_toolbox_style_updated),
                           toolbox, G_CONNECT_AFTER | G_CONNECT_SWAPPED);

  event_box = gtk_event_box_new ();
  gtk_box_pack_start (GTK_BOX (toolbox->p->vbox), event_box, FALSE, FALSE, 0);
  g_signal_connect_swapped (event_box, "button-press-event",
                            G_CALLBACK (gimp_toolbox_button_press_event),
                            toolbox);
  gtk_widget_set_visible (event_box, TRUE);

  toolbox->p->header = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (toolbox->p->header), GTK_SHADOW_NONE);
  gtk_container_add (GTK_CONTAINER (event_box), toolbox->p->header);

  g_object_bind_property (config,             "toolbox-wilber",
                          toolbox->p->header, "visible",
                          G_BINDING_SYNC_CREATE);

  g_signal_connect (toolbox->p->header, "style-updated",
                    G_CALLBACK (gimp_toolbox_wilber_style_updated),
                    toolbox);
  g_signal_connect (toolbox->p->header, "draw",
                    G_CALLBACK (gimp_toolbox_draw_wilber),
                    toolbox);

  gimp_help_set_help_data (toolbox->p->header,
                           _("Drop image files here to open them"), NULL);

  toolbox->p->tool_palette = gimp_tool_palette_new ();
  gimp_tool_palette_set_toolbox (GIMP_TOOL_PALETTE (toolbox->p->tool_palette),
                                 toolbox);
  gtk_box_pack_start (GTK_BOX (toolbox->p->vbox), toolbox->p->tool_palette,
                      FALSE, FALSE, 0);
  gtk_widget_set_visible (toolbox->p->tool_palette, TRUE);

  toolbox->p->area_box = gtk_flow_box_new ();
  gtk_flow_box_set_selection_mode (GTK_FLOW_BOX (toolbox->p->area_box),
                                   GTK_SELECTION_NONE);
  gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX (toolbox->p->area_box),
                                          3);
  gtk_box_pack_start (GTK_BOX (toolbox->p->vbox), toolbox->p->area_box,
                      FALSE, FALSE, 0);
  gtk_widget_set_visible (toolbox->p->area_box, TRUE);

  gtk_widget_add_events (GTK_WIDGET (toolbox), GDK_POINTER_MOTION_MASK);
  gimp_devices_add_widget (toolbox->p->context->gimp, GTK_WIDGET (toolbox));

  toolbox->p->color_area = toolbox_create_color_area (toolbox,
                                                      toolbox->p->context);
  gtk_flow_box_insert (GTK_FLOW_BOX (toolbox->p->area_box),
                       toolbox->p->color_area, -1);

  g_object_bind_property (config,                 "toolbox-color-area",
                          toolbox->p->color_area, "visible",
                          G_BINDING_SYNC_CREATE);
  g_signal_connect_object (config, "notify::toolbox-color-area",
                           G_CALLBACK (toolbox_area_config_notify),
                           toolbox->p->area_box, 0);

  toolbox->p->foo_area = toolbox_create_foo_area (toolbox, toolbox->p->context);
  gtk_flow_box_insert (GTK_FLOW_BOX (toolbox->p->area_box),
                       toolbox->p->foo_area, -1);

  g_object_bind_property (config,               "toolbox-foo-area",
                          toolbox->p->foo_area, "visible",
                          G_BINDING_SYNC_CREATE);
  g_signal_connect_object (config, "notify::toolbox-foo-area",
                           G_CALLBACK (toolbox_area_config_notify),
                           toolbox->p->area_box, 0);

  toolbox->p->image_area = toolbox_create_image_area (toolbox,
                                                      toolbox->p->context);
  gtk_flow_box_insert (GTK_FLOW_BOX (toolbox->p->area_box),
                       toolbox->p->image_area, -1);

  g_object_bind_property (config,                 "toolbox-image-area",
                          toolbox->p->image_area, "visible",
                          G_BINDING_SYNC_CREATE);
  g_signal_connect_object (config, "notify::toolbox-image-area",
                           G_CALLBACK (toolbox_area_config_notify),
                           toolbox->p->area_box, 0);

  toolbox_area_config_notify (config, NULL, toolbox->p->area_box);

  gimp_toolbox_dnd_init (GIMP_TOOLBOX (toolbox), toolbox->p->vbox);
}

static void
gimp_toolbox_dispose (GObject *object)
{
  GimpToolbox *toolbox = GIMP_TOOLBOX (object);

  toolbox->p->in_destruction = TRUE;

  g_clear_object (&toolbox->p->context);

  G_OBJECT_CLASS (parent_class)->dispose (object);

  toolbox->p->in_destruction = FALSE;
}

static void
gimp_toolbox_set_property (GObject      *object,
                           guint         property_id,
                           const GValue *value,
                           GParamSpec   *pspec)
{
  GimpToolbox *toolbox = GIMP_TOOLBOX (object);

  switch (property_id)
    {
    case PROP_CONTEXT:
      toolbox->p->context = g_value_dup_object (value);
      break;

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

static void
gimp_toolbox_get_property (GObject    *object,
                           guint       property_id,
                           GValue     *value,
                           GParamSpec *pspec)
{
  GimpToolbox *toolbox = GIMP_TOOLBOX (object);

  switch (property_id)
    {
    case PROP_CONTEXT:
      g_value_set_object (value, toolbox->p->context);
      break;

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

static void
gimp_toolbox_style_updated (GtkWidget *widget)
{
  GimpToolbox  *toolbox = GIMP_TOOLBOX (widget);
  GtkWidget    *area;
  GtkIconSize   tool_icon_size;
  gint          pixel_size;
  gint          width;
  gint          height;

  gtk_widget_style_get (widget,
                        "tool-icon-size", &tool_icon_size,
                        NULL);
  gtk_icon_size_lookup (tool_icon_size, &pixel_size, NULL);

  width  = pixel_size * (13/6.0f);
  height = pixel_size * 1.75f;

  /* Image Area */
  area = gtk_grid_get_child_at (GTK_GRID (toolbox->p->image_area), 0, 0);
  gimp_view_renderer_set_size_full (GIMP_VIEW (area)->renderer,
                                    width, height, 0);

  /* Brush Area */
  area = gtk_grid_get_child_at (GTK_GRID (toolbox->p->foo_area), 0, 0);
  gimp_view_renderer_set_size_full (GIMP_VIEW (area)->renderer,
                                    pixel_size, pixel_size, 0);

  /* Pattern Area */
  area = gtk_grid_get_child_at (GTK_GRID (toolbox->p->foo_area), 1, 0);
  gimp_view_renderer_set_size_full (GIMP_VIEW (area)->renderer,
                                    pixel_size, pixel_size, 0);

  /* Gradient Area */
  area = gtk_grid_get_child_at (GTK_GRID (toolbox->p->foo_area), 0, 1);
  gimp_view_renderer_set_size_full (GIMP_VIEW (area)->renderer,
                                    width, (pixel_size / 2), 0);
}

static gboolean
gimp_toolbox_button_press_event (GtkWidget      *widget,
                                 GdkEventButton *event)
{
  GimpToolbox *toolbox = GIMP_TOOLBOX (widget);
  GimpDisplay *display;
  gboolean     stop_event = GDK_EVENT_PROPAGATE;

  if (event->type == GDK_BUTTON_PRESS && event->button == 2)
    {
      GtkClipboard *clipboard;

      clipboard = gtk_widget_get_clipboard (widget, GDK_SELECTION_PRIMARY);
      gtk_clipboard_request_text (clipboard,
                                  toolbox_paste_received,
                                  g_object_ref (toolbox));

      stop_event = GDK_EVENT_STOP;
    }
  else if ((display = gimp_context_get_display (toolbox->p->context)))
    {
      /* Any button event in empty spaces or the Wilber area gives focus
       * to the top image.
       */
      gimp_display_grab_focus (display);
    }

  return stop_event;
}

static void
gimp_toolbox_drag_leave (GtkWidget      *widget,
                         GdkDragContext *context,
                         guint           time,
                         GimpToolbox    *toolbox)
{
  gimp_highlight_widget (widget, FALSE, NULL);
}

static gboolean
gimp_toolbox_drag_motion (GtkWidget      *widget,
                          GdkDragContext *context,
                          gint            x,
                          gint            y,
                          guint           time,
                          GimpToolbox    *toolbox)
{
  gboolean handle;

  if (gimp_paned_box_will_handle_drag (toolbox->p->drag_handler,
                                       widget,
                                       context,
                                       x, y,
                                       time))
    {
      gdk_drag_status (context, 0, time);
      gimp_highlight_widget (widget, FALSE, NULL);

      return FALSE;
    }

  handle = (gtk_drag_dest_find_target (widget, context, NULL) != GDK_NONE);

  gdk_drag_status (context, handle ? GDK_ACTION_MOVE : 0, time);
  gimp_highlight_widget (widget, handle, NULL);

  /* Return TRUE so drag_leave() is called */
  return TRUE;
}

static gboolean
gimp_toolbox_drag_drop (GtkWidget      *widget,
                        GdkDragContext *context,
                        gint            x,
                        gint            y,
                        guint           time,
                        GimpToolbox    *toolbox)
{
  GdkAtom  target;
  gboolean dropped = FALSE;

  if (gimp_paned_box_will_handle_drag (toolbox->p->drag_handler,
                                       widget,
                                       context,
                                       x, y,
                                       time))
    {
      return FALSE;
    }

  target = gtk_drag_dest_find_target (widget, context, NULL);

  if (target != GDK_NONE)
    {
      /* The URI handlers etc will handle this */
      gtk_drag_get_data (widget, context, target, time);
      dropped = TRUE;
    }
  else
    {
      gtk_drag_finish (context, FALSE,
                       gdk_drag_context_get_selected_action (context) ==
                       GDK_ACTION_MOVE,
                       time);
    }

  return dropped;
}

static gchar *
gimp_toolbox_get_description (GimpDock *dock,
                              gboolean  complete)
{
  GString *desc      = g_string_new (_("Toolbox"));
  gchar   *dock_desc = GIMP_DOCK_CLASS (parent_class)->get_description (dock,
                                                                        complete);

  if (dock_desc && strlen (dock_desc) > 0)
    {
      g_string_append (desc, GIMP_DOCK_BOOK_SEPARATOR);
      g_string_append (desc, dock_desc);
    }

  g_free (dock_desc);

  return g_string_free (desc, FALSE /*free_segment*/);
}

static void
gimp_toolbox_book_added (GimpDock     *dock,
                         GimpDockbook *dockbook)
{
  if (GIMP_DOCK_CLASS (parent_class)->book_added)
    GIMP_DOCK_CLASS (parent_class)->book_added (dock, dockbook);

  if (g_list_length (gimp_dock_get_dockbooks (dock)) == 1)
    {
      gimp_dock_invalidate_geometry (dock);
    }
}

static void
gimp_toolbox_book_removed (GimpDock     *dock,
                           GimpDockbook *dockbook)
{
  GimpToolbox *toolbox = GIMP_TOOLBOX (dock);

  if (GIMP_DOCK_CLASS (parent_class)->book_removed)
    GIMP_DOCK_CLASS (parent_class)->book_removed (dock, dockbook);

  if (! gimp_dock_get_dockbooks (dock) &&
      ! toolbox->p->in_destruction)
    {
      gimp_dock_invalidate_geometry (dock);
    }
}

static void
gimp_toolbox_set_host_geometry_hints (GimpDock  *dock,
                                      GtkWindow *window)
{
  GimpToolbox *toolbox = GIMP_TOOLBOX (dock);
  gint         button_width;
  gint         button_height;

  if (gimp_tool_palette_get_button_size (GIMP_TOOL_PALETTE (toolbox->p->tool_palette),
                                         &button_width, &button_height))
    {
      GdkGeometry geometry;

      geometry.min_width   = 2 * button_width;
      geometry.min_height  = -1;
      geometry.base_width  = button_width;
      geometry.base_height = 0;
      geometry.width_inc   = button_width;
      geometry.height_inc  = 1;

      gtk_window_set_geometry_hints (window,
                                     NULL,
                                     &geometry,
                                     GDK_HINT_MIN_SIZE   |
                                     GDK_HINT_BASE_SIZE  |
                                     GDK_HINT_RESIZE_INC |
                                     GDK_HINT_USER_POS);

      gimp_dialog_factory_set_has_min_size (window, TRUE);
    }
}

GtkWidget *
gimp_toolbox_new (GimpDialogFactory *factory,
                  GimpContext       *context,
                  GimpUIManager     *ui_manager)
{
  g_return_val_if_fail (GIMP_IS_DIALOG_FACTORY (factory), NULL);
  g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
  g_return_val_if_fail (GIMP_IS_UI_MANAGER (ui_manager), NULL);

  return g_object_new (GIMP_TYPE_TOOLBOX,
                       "context", context,
                       NULL);
}

GimpContext *
gimp_toolbox_get_context (GimpToolbox *toolbox)
{
  g_return_val_if_fail (GIMP_IS_TOOLBOX (toolbox), NULL);

  return toolbox->p->context;
}

void
gimp_toolbox_set_drag_handler (GimpToolbox  *toolbox,
                               GimpPanedBox *drag_handler)
{
  g_return_if_fail (GIMP_IS_TOOLBOX (toolbox));

  toolbox->p->drag_handler = drag_handler;
}


/*  private functions  */

static void
gimp_toolbox_notify_theme (GimpGuiConfig *config,
                           GParamSpec    *pspec,
                           GimpToolbox   *toolbox)
{
  gimp_toolbox_palette_style_updated (GTK_WIDGET (toolbox->p->tool_palette), toolbox);
}

static void
gimp_toolbox_palette_style_updated (GtkWidget   *widget,
                                    GimpToolbox *toolbox)
{
  GtkIconSize  tool_icon_size = GTK_ICON_SIZE_LARGE_TOOLBAR;
  gint         icon_width     = 40;
  gint         icon_height    = 38;

  gtk_widget_style_get (widget,
                        "tool-icon-size", &tool_icon_size,
                        NULL);
  gtk_icon_size_lookup (tool_icon_size, &icon_width, &icon_height);

  gtk_widget_set_size_request (toolbox->p->color_area,
                               (gint) (icon_width * 1.75),
                               (gint) (icon_height * 1.75));
  gtk_widget_queue_resize (toolbox->p->color_area);
}

static void
gimp_toolbox_wilber_style_updated (GtkWidget   *widget,
                                   GimpToolbox *toolbox)
{
  gint button_width;
  gint button_height;

  if (gimp_tool_palette_get_button_size (GIMP_TOOL_PALETTE (toolbox->p->tool_palette),
                                         &button_width, &button_height))
    {
      gtk_widget_set_size_request (widget,
                                   button_width  * PANGO_SCALE_SMALL,
                                   button_height * PANGO_SCALE_SMALL);
    }
}

static gboolean
gimp_toolbox_draw_wilber (GtkWidget *widget,
                          cairo_t   *cr)
{
  gimp_cairo_draw_toolbox_wilber (widget, cr);

  return FALSE;
}

static GtkWidget *
toolbox_create_color_area (GimpToolbox *toolbox,
                           GimpContext *context)
{
  GtkWidget   *col_area;
  GtkIconSize  tool_icon_size = GTK_ICON_SIZE_LARGE_TOOLBAR;
  gint         icon_width     = 40;
  gint         icon_height    = 38;

  gtk_widget_style_get (GTK_WIDGET (toolbox->p->tool_palette),
                        "tool-icon-size", &tool_icon_size,
                        NULL);
  gtk_icon_size_lookup (tool_icon_size, &icon_width, &icon_height);
  col_area = gimp_toolbox_color_area_create (toolbox,
                                             (gint) (icon_width * 1.75),
                                             (gint) (icon_height * 1.75));
  g_object_set (col_area,
                "halign",        GTK_ALIGN_CENTER,
                "valign",        GTK_ALIGN_CENTER,
                "margin-start",  2,
                "margin-end",    2,
                "margin-top",    2,
                "margin-bottom", 2,
                NULL);

  g_signal_connect (toolbox->p->tool_palette, "style-updated",
                    G_CALLBACK (gimp_toolbox_palette_style_updated),
                    toolbox);
  g_signal_connect_after (GIMP_GUI_CONFIG (toolbox->p->context->gimp->config),
                          "notify::theme",
                          G_CALLBACK (gimp_toolbox_notify_theme),
                          toolbox);
  g_signal_connect_after (GIMP_GUI_CONFIG (toolbox->p->context->gimp->config),
                          "notify::override-theme-icon-size",
                          G_CALLBACK (gimp_toolbox_notify_theme),
                          toolbox);
  g_signal_connect_after (GIMP_GUI_CONFIG (toolbox->p->context->gimp->config),
                          "notify::custom-icon-size",
                          G_CALLBACK (gimp_toolbox_notify_theme),
                          toolbox);
  return col_area;
}

static GtkWidget *
toolbox_create_foo_area (GimpToolbox *toolbox,
                         GimpContext *context)
{
  GtkWidget *foo_area;

  foo_area = gimp_toolbox_indicator_area_create (toolbox);
  g_object_set (foo_area,
                "halign",        GTK_ALIGN_CENTER,
                "valign",        GTK_ALIGN_CENTER,
                "margin-start",  2,
                "margin-end",    2,
                "margin-top",    2,
                "margin-bottom", 2,
                NULL);

  return foo_area;
}

static GtkWidget *
toolbox_create_image_area (GimpToolbox *toolbox,
                           GimpContext *context)
{
  GtkWidget   *image_area;
  GtkWidget   *grid;
  GtkIconSize  tool_icon_size;
  gint         width = 52;
  gint         height = 42;

  grid = gtk_grid_new ();
  gtk_grid_set_row_spacing (GTK_GRID (grid), 2);
  gtk_grid_set_column_spacing (GTK_GRID (grid), 2);

  gtk_widget_style_get (GTK_WIDGET (toolbox),
                        "tool-icon-size", &tool_icon_size,
                        NULL);
  gtk_icon_size_lookup (tool_icon_size, &width, &height);
  width  *= (13/6.0f);
  height *= 1.75f;

  image_area = gimp_toolbox_image_area_create (toolbox, width, height);
  gtk_grid_attach (GTK_GRID (grid), image_area, 0, 0, 1, 1);
  gtk_widget_set_visible (grid, TRUE);

  g_object_set (grid,
                "halign",        GTK_ALIGN_CENTER,
                "valign",        GTK_ALIGN_CENTER,
                "margin-start",  2,
                "margin-end",    2,
                "margin-top",    2,
                "margin-bottom", 2,
                NULL);

  return grid;
}

static void
toolbox_paste_received (GtkClipboard *clipboard,
                        const gchar  *text,
                        gpointer      data)
{
  GimpToolbox *toolbox = GIMP_TOOLBOX (data);
  GimpContext *context = toolbox->p->context;

  if (text)
    {
      const gchar *newline = strchr (text, '\n');
      gchar       *copy;
      GFile       *file = NULL;

      if (newline)
        copy = g_strndup (text, newline - text);
      else
        copy = g_strdup (text);

      g_strstrip (copy);

      if (strlen (copy))
        file = g_file_new_for_commandline_arg (copy);

      g_free (copy);

      if (file)
        {
          GtkWidget         *widget = GTK_WIDGET (toolbox);
          GimpImage         *image;
          GimpDisplay       *display;
          GimpPDBStatusType  status;
          GError            *error = NULL;

          image = file_open_with_display (context->gimp, context, NULL,
                                          file, FALSE,
                                          G_OBJECT (gimp_widget_get_monitor (widget)),
                                          &status, &error);

          if (! image && status != GIMP_PDB_CANCEL)
            {
              gimp_message (context->gimp, NULL, GIMP_MESSAGE_ERROR,
                            _("Opening '%s' failed:\n\n%s"),
                            gimp_file_get_utf8_name (file), error->message);
              g_clear_error (&error);
            }
          else if ((display = gimp_context_get_display (context)))
            {
              /* Giving focus to newly opened image. */
              gimp_display_grab_focus (display);
            }

          g_object_unref (file);
        }
    }

  g_object_unref (toolbox);
}

static void
toolbox_area_config_notify (GimpGuiConfig    *config,
                            const GParamSpec *pspec,
                            GtkWidget        *area_box)
{
  gint visible_areas = 0;

  visible_areas  = (gint) config->toolbox_color_area;
  visible_areas += (gint) config->toolbox_foo_area;
  visible_areas += (gint) config->toolbox_image_area;

  gtk_flow_box_set_max_children_per_line (GTK_FLOW_BOX (area_box),
                                          visible_areas);
}

