Updated new files from upstream

This commit is contained in:
2023-10-30 15:56:43 -07:00
parent 3bbdd873ef
commit 852cbfc1fb
44 changed files with 7719 additions and 0 deletions

312
libpika/pikabrush.c Normal file
View File

@ -0,0 +1,312 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikabrush.c
* Copyright (C) 2023 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
* Lesser 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 "pika.h"
#include "pikabrush.h"
struct _PikaBrush
{
PikaResource parent_instance;
/* Native size buffers of the brush contents. */
GeglBuffer *buffer;
GeglBuffer *mask;
};
G_DEFINE_TYPE (PikaBrush, pika_brush, PIKA_TYPE_RESOURCE);
static void pika_brush_finalize (GObject *object);
static void pika_brush_get_data (PikaBrush *brush);
static GeglBuffer * pika_brush_scale (GeglBuffer *buffer,
gint max_width,
gint max_height);
static const Babl * pika_brush_data_get_format (gint bpp,
gboolean with_alpha);
static void pika_brush_class_init (PikaBrushClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pika_brush_finalize;
}
static void pika_brush_init (PikaBrush *brush)
{
brush->buffer = NULL;
brush->mask = NULL;
}
static void
pika_brush_finalize (GObject *object)
{
PikaBrush *brush = PIKA_BRUSH (object);
g_clear_object (&brush->buffer);
g_clear_object (&brush->mask);
G_OBJECT_CLASS (pika_brush_parent_class)->finalize (object);
}
/**
* pika_brush_get_buffer:
* @brush: a [class@Brush].
* @max_width: a maximum width for the returned buffer.
* @max_height: a maximum height for the returned buffer.
* @format: an optional Babl format.
*
* Gets pixel data of the brush within the bounding box specified by @max_width
* and @max_height. The data will be scaled down so that it fits within this
* size without changing its ratio. If the brush is smaller than this size to
* begin with, it will not be scaled up.
*
* If @max_width or @max_height are %NULL, the buffer is returned in the brush's
* native size.
*
* When the brush is parametric or a raster mask, only the mask (as returned by
* [method@Gimp.Brush.get_mask]) will be set. The returned buffer will be NULL.
*
* Make sure you called [func@Gegl.init] before calling any function using
* `GEGL`.
*
* Returns: (transfer full): a [class@Gegl.Buffer] of %NULL if the brush is parametric
* or mask only.
*/
GeglBuffer *
pika_brush_get_buffer (PikaBrush *brush,
gint max_width,
gint max_height,
const Babl *format)
{
pika_brush_get_data (brush);
if (brush->buffer == NULL)
return NULL;
if (max_width == 0 || max_height == 0 ||
(gegl_buffer_get_width (brush->buffer) <= max_width &&
gegl_buffer_get_height (brush->buffer) <= max_height))
return gegl_buffer_dup (brush->buffer);
return pika_brush_scale (brush->buffer, max_width, max_height);
}
/**
* pika_brush_get_mask:
* @brush: a [class@Brush].
* @max_width: a maximum width for the returned buffer.
* @max_height: a maximum height for the returned buffer.
* @format: an optional Babl format.
*
* Gets mask data of the brush within the bounding box specified by @max_width
* and @max_height. The data will be scaled down so that it fits within this
* size without changing its ratio. If the brush is smaller than this size to
* begin with, it will not be scaled up.
*
* If @max_width or @max_height are %NULL, the buffer is returned in the brush's
* native size.
*
* Make sure you called [func@Gegl.init] before calling any function using
* `GEGL`.
*
* Returns: (transfer full): a [class@Gegl.Buffer] representing the @brush mask.
*/
GeglBuffer *
pika_brush_get_mask (PikaBrush *brush,
gint max_width,
gint max_height,
const Babl *format)
{
pika_brush_get_data (brush);
g_return_val_if_fail (brush->mask != NULL, NULL);
if (max_width == 0 || max_height == 0 ||
(gegl_buffer_get_width (brush->mask) <= max_width &&
gegl_buffer_get_height (brush->mask) <= max_height))
return gegl_buffer_dup (brush->mask);
return pika_brush_scale (brush->mask, max_width, max_height);
}
static void
pika_brush_get_data (PikaBrush *brush)
{
gint width;
gint height;
gint mask_bpp;
GBytes *mask_bytes;
gint color_bpp;
GBytes *color_bytes;
const guchar *mask;
gsize mask_size;
const guchar *color;
gsize color_size;
/* We check the mask because the buffer might be NULL.
*
* This check assumes that the brush contents doesn't change, which is not a
* perfect assumption. We could maybe add a PDB call which would return
* the new brush data only if it changed since last call (which can be
* verified with some kind of internal runtime version to pass from one call
* to another). TODO
*/
if (brush->mask != NULL)
return;
g_clear_object (&brush->buffer);
g_clear_object (&brush->mask);
_pika_brush_get_pixels (brush, &width, &height,
&mask_bpp, &mask_bytes,
&color_bpp, &color_bytes);
mask = g_bytes_unref_to_data (mask_bytes, &mask_size);
color = g_bytes_unref_to_data (color_bytes, &color_size);
brush->mask = gegl_buffer_linear_new_from_data ((const gpointer) mask,
pika_brush_data_get_format (mask_bpp, FALSE),
GEGL_RECTANGLE (0, 0, width, height),
0, g_free, NULL);
if (color_bpp > 0)
{
GeglBufferIterator *gi;
GeglBuffer *buffer;
buffer = gegl_buffer_linear_new_from_data ((const gpointer) color,
pika_brush_data_get_format (color_bpp, FALSE),
GEGL_RECTANGLE (0, 0, width, height),
0, g_free, NULL);
brush->buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
pika_brush_data_get_format (color_bpp, TRUE));
gi = gegl_buffer_iterator_new (brush->buffer, NULL, 0, NULL,
GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 3);
gegl_buffer_iterator_add (gi, buffer, GEGL_RECTANGLE (0, 0, width, height),
0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
gegl_buffer_iterator_add (gi, brush->mask, GEGL_RECTANGLE (0, 0, width, height),
0, NULL, GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
while (gegl_buffer_iterator_next (gi))
{
guint8 *out = gi->items[0].data;
guint8 *color = gi->items[1].data;
guint8 *alpha = gi->items[2].data;
for (gint i = 0; i < gi->length; i++)
{
gint c;
for (c = 0; c < color_bpp; c++)
out[c] = color[c];
out[c] = alpha[0];
out += color_bpp + 1;
color += color_bpp;
alpha += 1;
}
}
g_object_unref (buffer);
}
}
static GeglBuffer *
pika_brush_scale (GeglBuffer *buffer,
gint max_width,
gint max_height)
{
GeglBuffer *scaled = NULL;
GeglNode *graph;
GeglNode *source;
GeglNode *op;
GeglNode *sink;
gdouble width;
gdouble height;
gdouble scale;
height = (gdouble) max_height;
width = (gdouble) gegl_buffer_get_width (buffer) / gegl_buffer_get_height (buffer) * height;
if (width > (gdouble) max_width)
{
width = (gdouble) max_width;
height = (gdouble) gegl_buffer_get_height (buffer) / gegl_buffer_get_width (buffer) * width;
}
scale = width / gegl_buffer_get_width (buffer);
graph = gegl_node_new ();
source = gegl_node_new_child (graph,
"operation", "gegl:buffer-source",
"buffer", buffer,
NULL);
op = gegl_node_new_child (graph,
"operation", "gegl:scale-ratio",
"origin-x", 0.0,
"origin-y", 0.0,
"sampler", PIKA_INTERPOLATION_LINEAR,
"abyss-policy", GEGL_ABYSS_CLAMP,
"x", scale,
"y", scale,
NULL);
sink = gegl_node_new_child (graph,
"operation", "gegl:buffer-sink",
"buffer", &scaled,
"format", gegl_buffer_get_format (buffer),
NULL);
gegl_node_link_many (source, op, sink, NULL);
gegl_node_process (sink);
g_object_unref (graph);
return scaled;
}
static const Babl *
pika_brush_data_get_format (gint bpp,
gboolean with_alpha)
{
/* It's an ugly way to determine the proper format but pika_brush_get_pixels()
* doesn't give more info.
*/
switch (bpp)
{
case 1:
if (! with_alpha)
return babl_format ("Y' u8");
/* fallthrough */
case 2:
return babl_format ("Y'A u8");
case 3:
if (! with_alpha)
return babl_format ("R'G'B' u8");
/* fallthrough */
case 4:
return babl_format ("R'G'B'A u8");
default:
g_return_val_if_reached (NULL);
}
g_return_val_if_reached (NULL);
}

52
libpika/pikabrush.h Normal file
View File

@ -0,0 +1,52 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikabrush.h
* Copyright (C) 2023 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
* Lesser 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/>.
*/
#if !defined (__PIKA_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pika.h> can be included directly."
#endif
#ifndef __PIKA_BRUSH_H__
#define __PIKA_BRUSH_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#include <libpika/pikaresource.h>
#define PIKA_TYPE_BRUSH (pika_brush_get_type ())
G_DECLARE_FINAL_TYPE (PikaBrush, pika_brush, PIKA, BRUSH, PikaResource)
GeglBuffer * pika_brush_get_buffer (PikaBrush *brush,
gint max_width,
gint max_height,
const Babl *format) G_GNUC_WARN_UNUSED_RESULT;
GeglBuffer * pika_brush_get_mask (PikaBrush *brush,
gint max_width,
gint max_height,
const Babl *format) G_GNUC_WARN_UNUSED_RESULT;
G_END_DECLS
#endif /* __PIKA_BRUSH_H__ */

456
libpika/pikabrushchooser.c Normal file
View File

@ -0,0 +1,456 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikabrushchooser.c
* Copyright (C) 1998 Andy Thomas
*
* 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 "libpikawidgets/pikawidgets.h"
#include "pika.h"
#include "pikauitypes.h"
#include "pikabrushchooser.h"
#include "pikauimarshal.h"
#include "libpika-intl.h"
/**
* SECTION: pikabrushchooser
* @title: PikaBrushChooser
* @short_description: A button which pops up a brush selection dialog.
*
* A button which pops up a brush selection dialog.
*
* Note that this widget draws itself using `GEGL` code. You **must** call
* [func@Gegl.init] first to be able to use this chooser.
**/
#define CELL_SIZE 40
struct _PikaBrushChooser
{
PikaResourceChooser parent_instance;
GtkWidget *preview;
GtkWidget *popup;
PikaBrush *brush;
gint width;
gint height;
GeglBuffer *buffer;
GeglBuffer *mask;
};
static void pika_brush_chooser_finalize (GObject *object);
static gboolean pika_brush_select_on_preview_events (GtkWidget *widget,
GdkEvent *event,
PikaBrushChooser *button);
static void pika_brush_select_preview_draw (PikaBrushChooser *chooser,
PikaPreviewArea *area);
static void pika_brush_chooser_draw (PikaBrushChooser *self);
static void pika_brush_chooser_get_brush_bitmap (PikaBrushChooser *self,
gint width,
gint height);
/* Popup methods. */
static void pika_brush_chooser_open_popup (PikaBrushChooser *button,
gint x,
gint y);
static void pika_brush_chooser_close_popup (PikaBrushChooser *button);
static const GtkTargetEntry drag_target = { "application/x-pika-brush-name", 0, 0 };
G_DEFINE_FINAL_TYPE (PikaBrushChooser, pika_brush_chooser, PIKA_TYPE_RESOURCE_CHOOSER)
static void
pika_brush_chooser_class_init (PikaBrushChooserClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PikaResourceChooserClass *res_class = PIKA_RESOURCE_CHOOSER_CLASS (klass);
res_class->draw_interior = (void (*)(PikaResourceChooser *)) pika_brush_chooser_draw;
res_class->resource_type = PIKA_TYPE_BRUSH;
object_class->finalize = pika_brush_chooser_finalize;
}
static void
pika_brush_chooser_init (PikaBrushChooser *chooser)
{
GtkWidget *widget;
gint scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (chooser));
chooser->brush = NULL;
chooser->buffer = NULL;
chooser->mask = NULL;
widget = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (chooser), widget, FALSE, FALSE, 0);
gtk_widget_show (widget);
chooser->preview = pika_preview_area_new ();
gtk_widget_add_events (chooser->preview,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
gtk_widget_set_size_request (chooser->preview, scale_factor * CELL_SIZE, scale_factor * CELL_SIZE);
gtk_container_add (GTK_CONTAINER (widget), chooser->preview);
gtk_widget_show (chooser->preview);
g_signal_connect_swapped (chooser->preview, "size-allocate",
G_CALLBACK (pika_brush_chooser_draw),
chooser);
g_signal_connect (chooser->preview, "event",
G_CALLBACK (pika_brush_select_on_preview_events),
chooser);
widget = gtk_button_new_with_mnemonic (_("_Browse..."));
gtk_box_pack_start (GTK_BOX (chooser), widget, FALSE, FALSE, 0);
pika_resource_chooser_set_drag_target (PIKA_RESOURCE_CHOOSER (chooser),
chooser->preview,
&drag_target);
pika_resource_chooser_set_clickable (PIKA_RESOURCE_CHOOSER (chooser),
widget);
gtk_widget_show (widget);
}
static void
pika_brush_chooser_finalize (GObject *object)
{
PikaBrushChooser *chooser = PIKA_BRUSH_CHOOSER (object);
g_clear_object (&chooser->buffer);
g_clear_object (&chooser->mask);
G_OBJECT_CLASS (pika_brush_chooser_parent_class)->finalize (object);
}
/**
* pika_brush_chooser_new:
* @title: (nullable): Title of the dialog to use or %NULL to use the default title.
* @label: (nullable): Button label or %NULL for no label.
* @resource: (nullable): Initial brush.
*
* Creates a new #GtkWidget that lets a user choose a brush.
* You can put this widget in a plug-in dialog.
*
* When brush is NULL, initial choice is from context.
*
* Returns: A #GtkWidget that you can use in your UI.
*
* Since: 2.4
*/
GtkWidget *
pika_brush_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource)
{
GtkWidget *self;
if (resource == NULL)
resource = PIKA_RESOURCE (pika_context_get_brush ());
if (title)
self = g_object_new (PIKA_TYPE_BRUSH_CHOOSER,
"title", title,
"label", label,
"resource", resource,
NULL);
else
self = g_object_new (PIKA_TYPE_BRUSH_CHOOSER,
"label", label,
"resource", resource,
NULL);
return self;
}
/* private functions */
static void
pika_brush_chooser_draw (PikaBrushChooser *chooser)
{
GtkAllocation allocation;
gtk_widget_get_allocation (chooser->preview, &allocation);
pika_brush_chooser_get_brush_bitmap (chooser, allocation.width, allocation.height);
pika_brush_select_preview_draw (chooser, PIKA_PREVIEW_AREA (chooser->preview));
}
static void
pika_brush_chooser_get_brush_bitmap (PikaBrushChooser *chooser,
gint width,
gint height)
{
PikaBrush *brush;
g_object_get (chooser, "resource", &brush, NULL);
if (chooser->brush == brush &&
chooser->width == width &&
chooser->height == height)
{
/* Let's assume brush contents is not changing in a single run. */
g_object_unref (brush);
return;
}
g_clear_object (&chooser->buffer);
g_clear_object (&chooser->mask);
chooser->width = chooser->height = 0;
chooser->brush = brush;
chooser->buffer = pika_brush_get_buffer (brush, width, height, NULL);
if (chooser->buffer)
{
GeglColor *white_color = gegl_color_new ("rgb(1.0,1.0,1.0)");
GeglNode *graph;
GeglNode *white;
GeglNode *source;
GeglNode *op;
chooser->width = gegl_buffer_get_width (chooser->buffer);
chooser->height = gegl_buffer_get_height (chooser->buffer);
graph = gegl_node_new ();
white = gegl_node_new_child (graph,
"operation", "gegl:rectangle",
"x", 0.0,
"y", 0.0,
"width", (gdouble) gegl_buffer_get_width (chooser->buffer),
"height", (gdouble) gegl_buffer_get_height (chooser->buffer),
"color", white_color,
NULL);
source = gegl_node_new_child (graph,
"operation", "gegl:buffer-source",
"buffer", chooser->buffer,
NULL);
op = gegl_node_new_child (graph,
"operation", "svg:src-over",
NULL);
gegl_node_link_many (white, op, NULL);
gegl_node_connect (source, "output", op, "aux");
gegl_node_blit_buffer (op, chooser->buffer, NULL, 0, GEGL_ABYSS_NONE);
g_object_unref (graph);
g_object_unref (white_color);
}
else
{
GeglBufferIterator *iter;
chooser->mask = pika_brush_get_mask (brush, width, height, NULL);
chooser->width = gegl_buffer_get_width (chooser->mask);
chooser->height = gegl_buffer_get_height (chooser->mask);
/* More common to display mask brushes as black on white. */
iter = gegl_buffer_iterator_new (chooser->mask, NULL, 0,
babl_format ("Y' u8"),
GEGL_ACCESS_READWRITE,
GEGL_ABYSS_NONE, 1);
while (gegl_buffer_iterator_next (iter))
{
guint8 *data = iter->items[0].data;
for (gint i = 0; i < iter->length; i++)
{
*data = 255 - *data;
data++;
}
}
}
g_object_unref (brush);
}
/* On mouse events in self's preview, popup a zoom view of entire brush */
static gboolean
pika_brush_select_on_preview_events (GtkWidget *widget,
GdkEvent *event,
PikaBrushChooser *self)
{
GdkEventButton *bevent;
switch (event->type)
{
case GDK_BUTTON_PRESS:
bevent = (GdkEventButton *) event;
if (bevent->button == 1)
{
gtk_grab_add (widget);
pika_brush_chooser_open_popup (self,
bevent->x, bevent->y);
}
break;
case GDK_BUTTON_RELEASE:
bevent = (GdkEventButton *) event;
if (bevent->button == 1)
{
gtk_grab_remove (widget);
pika_brush_chooser_close_popup (self);
}
break;
default:
break;
}
return FALSE;
}
/* Draw a PikaPreviewArea with a given bitmap. */
static void
pika_brush_select_preview_draw (PikaBrushChooser *chooser,
PikaPreviewArea *preview)
{
PikaPreviewArea *area = PIKA_PREVIEW_AREA (preview);
GeglBuffer *src_buffer;
const Babl *format;
guchar *src;
PikaImageType type;
gint rowstride;
GtkAllocation allocation;
gint x = 0;
gint y = 0;
gtk_widget_get_allocation (GTK_WIDGET (preview), &allocation);
/* Fill with white. */
if (chooser->width < allocation.width ||
chooser->height < allocation.height)
{
pika_preview_area_fill (area,
0, 0,
allocation.width,
allocation.height,
0xFF, 0xFF, 0xFF);
x = ((allocation.width - chooser->width) / 2);
y = ((allocation.height - chooser->height) / 2);
}
/* Draw the brush. */
if (chooser->buffer)
{
src_buffer = chooser->buffer;
format = gegl_buffer_get_format (src_buffer);
rowstride = chooser->width * babl_format_get_bytes_per_pixel (format);
if (babl_format_has_alpha (format))
type = PIKA_RGBA_IMAGE;
else
type = PIKA_RGB_IMAGE;
}
else
{
src_buffer = chooser->mask;
format = babl_format ("Y' u8");
rowstride = chooser->width;
type = PIKA_GRAY_IMAGE;
}
src = g_try_malloc (sizeof (guchar) * rowstride * chooser->height);
gegl_buffer_get (src_buffer, NULL, 1.0, format, src, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
pika_preview_area_draw (area, x, y, chooser->width, chooser->height, type, src, rowstride);
g_free (src);
}
/* popup methods. */
static void
pika_brush_chooser_open_popup (PikaBrushChooser *chooser,
gint x,
gint y)
{
GtkWidget *frame;
GtkWidget *preview;
GdkMonitor *monitor;
GdkRectangle workarea;
gint x_org;
gint y_org;
if (chooser->popup)
pika_brush_chooser_close_popup (chooser);
if (chooser->width <= CELL_SIZE && chooser->height <= CELL_SIZE)
return;
chooser->popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_type_hint (GTK_WINDOW (chooser->popup), GDK_WINDOW_TYPE_HINT_DND);
gtk_window_set_screen (GTK_WINDOW (chooser->popup),
gtk_widget_get_screen (GTK_WIDGET (chooser)));
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
gtk_container_add (GTK_CONTAINER (chooser->popup), frame);
gtk_widget_show (frame);
preview = pika_preview_area_new ();
gtk_widget_set_size_request (preview, chooser->width, chooser->height);
gtk_container_add (GTK_CONTAINER (frame), preview);
gtk_widget_show (preview);
/* decide where to put the popup: near the preview i.e. at mousedown coords */
gdk_window_get_origin (gtk_widget_get_window (chooser->preview),
&x_org, &y_org);
monitor = pika_widget_get_monitor (GTK_WIDGET (chooser));
gdk_monitor_get_workarea (monitor, &workarea);
x = x_org + x - (chooser->width / 2);
y = y_org + y - (chooser->height / 2);
x = CLAMP (x, workarea.x, workarea.x + workarea.width - chooser->width);
y = CLAMP (y, workarea.y, workarea.y + workarea.height - chooser->height);
gtk_window_move (GTK_WINDOW (chooser->popup), x, y);
gtk_widget_show (chooser->popup);
/* Draw popup now. Usual events do not cause a draw. */
pika_brush_select_preview_draw (chooser, PIKA_PREVIEW_AREA (preview));
gdk_window_set_transient_for (gtk_widget_get_window (chooser->popup),
gtk_widget_get_window (gtk_widget_get_toplevel (GTK_WIDGET (chooser))));
}
static void
pika_brush_chooser_close_popup (PikaBrushChooser *self)
{
g_clear_pointer (&self->popup, gtk_widget_destroy);
}

View File

@ -0,0 +1,43 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikabrushchooser.h
*
* 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_BRUSH_CHOOSER_H__
#define __PIKA_BRUSH_CHOOSER_H__
#include <libpika/pikaresourcechooser.h>
G_BEGIN_DECLS
#define PIKA_TYPE_BRUSH_CHOOSER (pika_brush_chooser_get_type ())
G_DECLARE_FINAL_TYPE (PikaBrushChooser, pika_brush_chooser, PIKA, BRUSH_CHOOSER, PikaResourceChooser)
GtkWidget * pika_brush_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource);
G_END_DECLS
#endif /* __PIKA_BRUSH_CHOOSER_H__ */

View File

@ -0,0 +1,615 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikadrawablechooser.h
* Copyright (C) 2023 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 "libpikawidgets/pikawidgets.h"
#include "pika.h"
#include "pikauitypes.h"
#include "pikadrawablechooser.h"
#include "pikauimarshal.h"
#include "libpika-intl.h"
/**
* SECTION: pikadrawablechooser
* @title: PikaDrawableChooser
* @short_description: A widget allowing to select a drawable.
*
* The chooser contains an optional label and a button which queries the core
* process to pop up a drawable selection dialog.
*
* Since: 3.0
**/
#define CELL_SIZE 40
enum
{
PROP_0,
PROP_TITLE,
PROP_LABEL,
PROP_DRAWABLE,
PROP_DRAWABLE_TYPE,
N_PROPS
};
struct _PikaDrawableChooser
{
GtkBox parent_instance;
GType drawable_type;
PikaDrawable *drawable;
gchar *title;
gchar *label;
gchar *callback;
GBytes *thumbnail;
PikaDrawable *thumbnail_drawable;
gint width;
gint height;
gint bpp;
GtkWidget *label_widget;
GtkWidget *preview_frame;
GtkWidget *preview;
GtkWidget *preview_title;
};
/* local function prototypes */
static void pika_drawable_chooser_constructed (GObject *object);
static void pika_drawable_chooser_dispose (GObject *object);
static void pika_drawable_chooser_finalize (GObject *object);
static void pika_drawable_chooser_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_drawable_chooser_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_drawable_chooser_clicked (PikaDrawableChooser *chooser);
static PikaValueArray * pika_temp_callback_run (PikaProcedure *procedure,
PikaProcedureConfig *config,
PikaDrawableChooser *chooser);
static gboolean pika_drawable_select_remove_after_run (const gchar *procedure_name);
static void pika_drawable_chooser_draw (PikaDrawableChooser *chooser);
static void pika_drawable_chooser_get_thumbnail (PikaDrawableChooser *chooser,
gint width,
gint height);
static GParamSpec *drawable_button_props[N_PROPS] = { NULL, };
G_DEFINE_FINAL_TYPE (PikaDrawableChooser, pika_drawable_chooser, GTK_TYPE_BOX)
static void
pika_drawable_chooser_class_init (PikaDrawableChooserClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_drawable_chooser_constructed;
object_class->dispose = pika_drawable_chooser_dispose;
object_class->finalize = pika_drawable_chooser_finalize;
object_class->set_property = pika_drawable_chooser_set_property;
object_class->get_property = pika_drawable_chooser_get_property;
/**
* PikaDrawableChooser:title:
*
* The title to be used for the drawable selection popup dialog.
*
* Since: 3.0
*/
drawable_button_props[PROP_TITLE] =
g_param_spec_string ("title",
"Title",
"The title to be used for the drawable selection popup dialog",
"Drawable Selection",
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
/**
* PikaDrawableChooser:label:
*
* Label text with mnemonic.
*
* Since: 3.0
*/
drawable_button_props[PROP_LABEL] =
g_param_spec_string ("label",
"Label",
"The label to be used next to the button",
NULL,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
/**
* PikaDrawableChooser:drawable:
*
* The currently selected drawable.
*
* Since: 3.0
*/
drawable_button_props[PROP_DRAWABLE] =
pika_param_spec_drawable ("drawable",
"Drawable",
"The currently selected drawable",
TRUE,
PIKA_PARAM_READWRITE |
G_PARAM_EXPLICIT_NOTIFY);
/**
* PikaDrawableChooser:drawable-type:
*
* Allowed drawable types, which must be either PIKA_TYPE_DRAWABLE or a
* subtype.
*
* Since: 3.0
*/
drawable_button_props[PROP_DRAWABLE_TYPE] =
g_param_spec_gtype ("drawable-type",
"Allowed drawable Type",
"The GType of the drawable property",
PIKA_TYPE_DRAWABLE,
G_PARAM_CONSTRUCT_ONLY |
PIKA_PARAM_READWRITE);
g_object_class_install_properties (object_class,
N_PROPS, drawable_button_props);
}
static void
pika_drawable_chooser_init (PikaDrawableChooser *chooser)
{
gtk_orientable_set_orientation (GTK_ORIENTABLE (chooser),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (chooser), 6);
chooser->thumbnail_drawable = NULL;
chooser->thumbnail = NULL;
}
static void
pika_drawable_chooser_constructed (GObject *object)
{
PikaDrawableChooser *chooser = PIKA_DRAWABLE_CHOOSER (object);
GtkWidget *button;
GtkWidget *box;
gint scale_factor;
scale_factor = gtk_widget_get_scale_factor (GTK_WIDGET (chooser));
chooser->label_widget = gtk_label_new (NULL);
gtk_box_pack_start (GTK_BOX (chooser), chooser->label_widget, FALSE, FALSE, 0);
gtk_label_set_text_with_mnemonic (GTK_LABEL (chooser->label_widget), chooser->label);
gtk_widget_show (GTK_WIDGET (chooser->label_widget));
button = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (chooser), button, FALSE, FALSE, 0);
gtk_label_set_mnemonic_widget (GTK_LABEL (chooser->label_widget), button);
gtk_widget_show (button);
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
gtk_container_add (GTK_CONTAINER (button), box);
gtk_widget_show (box);
chooser->preview_frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
gtk_frame_set_shadow_type (GTK_FRAME (chooser->preview_frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (box), chooser->preview_frame, FALSE, FALSE, 0);
gtk_widget_show (chooser->preview_frame);
chooser->preview = pika_preview_area_new ();
gtk_widget_set_size_request (chooser->preview, scale_factor * CELL_SIZE, scale_factor * CELL_SIZE);
gtk_container_add (GTK_CONTAINER (chooser->preview_frame), chooser->preview);
gtk_widget_show (chooser->preview);
chooser->preview_title = gtk_label_new (_("Browse..."));
gtk_box_pack_start (GTK_BOX (box), chooser->preview_title, FALSE, FALSE, 0);
gtk_widget_show (chooser->preview_title);
g_signal_connect_swapped (button, "clicked",
G_CALLBACK (pika_drawable_chooser_clicked),
chooser);
G_OBJECT_CLASS (pika_drawable_chooser_parent_class)->constructed (object);
}
static void
pika_drawable_chooser_dispose (GObject *object)
{
PikaDrawableChooser *chooser = PIKA_DRAWABLE_CHOOSER (object);
if (chooser->callback)
{
pika_drawables_close_popup (chooser->callback);
pika_plug_in_remove_temp_procedure (pika_get_plug_in (), chooser->callback);
g_clear_pointer (&chooser->callback, g_free);
}
G_OBJECT_CLASS (pika_drawable_chooser_parent_class)->dispose (object);
}
static void
pika_drawable_chooser_finalize (GObject *object)
{
PikaDrawableChooser *chooser = PIKA_DRAWABLE_CHOOSER (object);
g_clear_pointer (&chooser->title, g_free);
g_clear_pointer (&chooser->label, g_free);
g_clear_pointer (&chooser->thumbnail, g_bytes_unref);
G_OBJECT_CLASS (pika_drawable_chooser_parent_class)->finalize (object);
}
static void
pika_drawable_chooser_set_property (GObject *object,
guint property_id,
const GValue *gvalue,
GParamSpec *pspec)
{
PikaDrawableChooser *chooser = PIKA_DRAWABLE_CHOOSER (object);
switch (property_id)
{
case PROP_TITLE:
chooser->title = g_value_dup_string (gvalue);
break;
case PROP_LABEL:
chooser->label = g_value_dup_string (gvalue);
break;
case PROP_DRAWABLE:
g_return_if_fail (g_value_get_object (gvalue) == NULL ||
g_type_is_a (G_TYPE_FROM_INSTANCE (g_value_get_object (gvalue)),
chooser->drawable_type));
pika_drawable_chooser_set_drawable (chooser, g_value_get_object (gvalue));
break;
case PROP_DRAWABLE_TYPE:
chooser->drawable_type = g_value_get_gtype (gvalue);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_drawable_chooser_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaDrawableChooser *chooser = PIKA_DRAWABLE_CHOOSER (object);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, chooser->title);
break;
case PROP_LABEL:
g_value_set_string (value, chooser->label);
break;
case PROP_DRAWABLE:
g_value_set_object (value, chooser->drawable);
break;
case PROP_DRAWABLE_TYPE:
g_value_set_gtype (value, chooser->drawable_type);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/**
* pika_drawable_chooser_new:
* @title: (nullable): Title of the dialog to use or %NULL to use the default title.
* @label: (nullable): Button label or %NULL for no label.
* @drawable_type: the acceptable subtype of choosable drawables.
* @drawable: (nullable): Initial drawable.
*
* Creates a new #GtkWidget that lets a user choose a drawable which must be of
* type @drawable_type. @drawable_type of values %G_TYPE_NONE and
* %PIKA_TYPE_DRAWABLE are equivalent. Otherwise it must be a subtype of
* %PIKA_TYPE_DRAWABLE.
*
* When @drawable is %NULL, initial choice is from context.
*
* Returns: A [class@GimpUi.DrawableChooser.
*
* Since: 3.0
*/
GtkWidget *
pika_drawable_chooser_new (const gchar *title,
const gchar *label,
GType drawable_type,
PikaDrawable *drawable)
{
GtkWidget *chooser;
if (drawable_type == G_TYPE_NONE)
drawable_type = PIKA_TYPE_DRAWABLE;
g_return_val_if_fail (g_type_is_a (drawable_type, PIKA_TYPE_DRAWABLE), NULL);
g_return_val_if_fail (drawable == NULL ||
g_type_is_a (G_TYPE_FROM_INSTANCE (drawable), drawable_type),
NULL);
chooser = g_object_new (PIKA_TYPE_DRAWABLE_CHOOSER,
"title", title,
"label", label,
"drawable", drawable,
"drawable-type", drawable_type,
NULL);
return chooser;
}
/**
* pika_drawable_chooser_get_drawable:
* @chooser: A #PikaDrawableChooser
*
* Gets the currently selected drawable.
*
* Returns: (transfer none): an internal copy of the drawable which must not be freed.
*
* Since: 3.0
*/
PikaDrawable *
pika_drawable_chooser_get_drawable (PikaDrawableChooser *chooser)
{
g_return_val_if_fail (PIKA_IS_DRAWABLE_CHOOSER (chooser), NULL);
return chooser->drawable;
}
/**
* pika_drawable_chooser_set_drawable:
* @chooser: A #PikaDrawableChooser
* @drawable: Drawable to set.
*
* Sets the currently selected drawable.
* This will select the drawable in both the button and any chooser popup.
*
* Since: 3.0
*/
void
pika_drawable_chooser_set_drawable (PikaDrawableChooser *chooser,
PikaDrawable *drawable)
{
g_return_if_fail (PIKA_IS_DRAWABLE_CHOOSER (chooser));
g_return_if_fail (drawable == NULL || PIKA_IS_DRAWABLE (drawable));
chooser->drawable = drawable;
if (chooser->callback)
pika_drawables_set_popup (chooser->callback, chooser->drawable);
g_object_notify_by_pspec (G_OBJECT (chooser), drawable_button_props[PROP_DRAWABLE]);
pika_drawable_chooser_draw (chooser);
}
/**
* pika_drawable_chooser_get_label:
* @widget: A [class@DrawableChooser].
*
* Returns the label widget.
*
* Returns: (transfer none): the [class@Gtk.Widget] showing the label text.
* Since: 3.0
*/
GtkWidget *
pika_drawable_chooser_get_label (PikaDrawableChooser *chooser)
{
g_return_val_if_fail (PIKA_IS_DRAWABLE_CHOOSER (chooser), NULL);
return chooser->label_widget;
}
/* private functions */
static PikaValueArray *
pika_temp_callback_run (PikaProcedure *procedure,
PikaProcedureConfig *config,
PikaDrawableChooser *chooser)
{
PikaDrawable *drawable;
gboolean closing;
g_object_get (config,
"drawable", &drawable,
"closing", &closing,
NULL);
g_object_set (chooser, "drawable", drawable, NULL);
if (closing)
{
g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
(GSourceFunc) pika_drawable_select_remove_after_run,
g_strdup (pika_procedure_get_name (procedure)),
g_free);
g_clear_pointer (&chooser->callback, g_free);
}
g_clear_object (&drawable);
return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL);
}
static gboolean
pika_drawable_select_remove_after_run (const gchar *procedure_name)
{
pika_plug_in_remove_temp_procedure (pika_get_plug_in (), procedure_name);
return G_SOURCE_REMOVE;
}
static void
pika_drawable_chooser_clicked (PikaDrawableChooser *chooser)
{
if (chooser->callback)
{
/* Popup already created. Calling setter raises the popup. */
pika_drawables_set_popup (chooser->callback, chooser->drawable);
}
else
{
PikaPlugIn *plug_in = pika_get_plug_in ();
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (chooser));
GBytes *handle = NULL;
gchar *callback_name;
PikaProcedure *callback_procedure;
if (PIKA_IS_DIALOG (toplevel))
handle = pika_dialog_get_native_handle (PIKA_DIALOG (toplevel));
callback_name = pika_pdb_temp_procedure_name (pika_get_pdb ());
callback_procedure = pika_procedure_new (plug_in,
callback_name,
PIKA_PDB_PROC_TYPE_TEMPORARY,
(PikaRunFunc) pika_temp_callback_run,
g_object_ref (chooser),
(GDestroyNotify) g_object_unref);
PIKA_PROC_ARG_DRAWABLE (callback_procedure, "drawable",
"Drawable", "The selected drawable",
TRUE, G_PARAM_READWRITE);
PIKA_PROC_ARG_BOOLEAN (callback_procedure, "closing",
"Closing", "If the dialog was closing",
FALSE, G_PARAM_READWRITE);
pika_plug_in_add_temp_procedure (plug_in, callback_procedure);
g_object_unref (callback_procedure);
g_free (callback_name);
if (pika_drawables_popup (pika_procedure_get_name (callback_procedure), chooser->title,
g_type_name (chooser->drawable_type), chooser->drawable, handle))
{
/* Allow callbacks to be watched */
pika_plug_in_extension_enable (plug_in);
chooser->callback = g_strdup (pika_procedure_get_name (callback_procedure));
}
else
{
g_warning ("%s: failed to open remote drawable select dialog.", G_STRFUNC);
pika_plug_in_remove_temp_procedure (plug_in, pika_procedure_get_name (callback_procedure));
return;
}
pika_drawables_set_popup (chooser->callback, chooser->drawable);
}
}
static void
pika_drawable_chooser_draw (PikaDrawableChooser *chooser)
{
PikaPreviewArea *area = PIKA_PREVIEW_AREA (chooser->preview);
GtkAllocation allocation;
gint x = 0;
gint y = 0;
gtk_widget_get_allocation (chooser->preview, &allocation);
pika_drawable_chooser_get_thumbnail (chooser, allocation.width, allocation.height);
if (chooser->width < allocation.width ||
chooser->height < allocation.height)
{
x = ((allocation.width - chooser->width) / 2);
y = ((allocation.height - chooser->height) / 2);
}
pika_preview_area_reset (area);
if (chooser->thumbnail)
{
PikaImageType type;
gint rowstride;
rowstride = chooser->width * chooser->bpp;
switch (chooser->bpp)
{
case 1:
type = PIKA_GRAY_IMAGE;
break;
case 2:
type = PIKA_GRAYA_IMAGE;
break;
case 3:
type = PIKA_RGB_IMAGE;
break;
case 4:
type = PIKA_RGBA_IMAGE;
break;
default:
g_return_if_reached ();
}
pika_preview_area_draw (area, x, y, chooser->width, chooser->height, type,
g_bytes_get_data (chooser->thumbnail, NULL), rowstride);
}
}
static void
pika_drawable_chooser_get_thumbnail (PikaDrawableChooser *chooser,
gint width,
gint height)
{
if (chooser->drawable == chooser->thumbnail_drawable &&
chooser->width == width &&
chooser->height == height)
/* Let's assume drawable contents is not changing in a single run. */
return;
g_clear_pointer (&chooser->thumbnail, g_bytes_unref);
chooser->width = chooser->height = 0;
chooser->thumbnail_drawable = chooser->drawable;
if (chooser->drawable)
chooser->thumbnail = pika_drawable_get_thumbnail_data (chooser->drawable,
width, height,
&chooser->width,
&chooser->height,
&chooser->bpp);
}

View File

@ -0,0 +1,48 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikadrawablechooser.c
* Copyright (C) 2023 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_DRAWABLE_CHOOSER_H__
#define __PIKA_DRAWABLE_CHOOSER_H__
G_BEGIN_DECLS
#define PIKA_TYPE_DRAWABLE_CHOOSER (pika_drawable_chooser_get_type ())
G_DECLARE_FINAL_TYPE (PikaDrawableChooser, pika_drawable_chooser, PIKA, DRAWABLE_CHOOSER, GtkBox)
GtkWidget * pika_drawable_chooser_new (const gchar *title,
const gchar *label,
GType drawable_type,
PikaDrawable *drawable);
PikaDrawable * pika_drawable_chooser_get_drawable (PikaDrawableChooser *chooser);
void pika_drawable_chooser_set_drawable (PikaDrawableChooser *chooser,
PikaDrawable *drawable);
GtkWidget * pika_drawable_chooser_get_label (PikaDrawableChooser *widget);
G_END_DECLS
#endif /* __PIKA_DRAWABLE_CHOOSER_H__ */

View File

@ -0,0 +1,151 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
*
* pikadrawableselect_pdb.c
*
* 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
* Lesser 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/>.
*/
/* NOTE: This file is auto-generated by pdbgen.pl */
#include "config.h"
#include "stamp-pdbgen.h"
#include "pika.h"
/**
* SECTION: pikadrawableselect
* @title: pikadrawableselect
* @short_description: Methods of a drawable chooser dialog
*
**/
/**
* pika_drawables_popup:
* @callback: The callback PDB proc to call when user chooses an drawable.
* @popup_title: Title of the drawable selection dialog.
* @drawable_type: The name of the PIKA_TYPE_DRAWABLE subtype.
* @initial_drawable: The drawable to set as the initial choice.
* @parent_window: An optional parent window handle for the popup to be set transient to.
*
* Invokes the drawable selection dialog.
*
* Opens a dialog letting a user choose an drawable.
*
* Returns: TRUE on success.
**/
gboolean
pika_drawables_popup (const gchar *callback,
const gchar *popup_title,
const gchar *drawable_type,
PikaDrawable *initial_drawable,
GBytes *parent_window)
{
PikaValueArray *args;
PikaValueArray *return_vals;
gboolean success = TRUE;
args = pika_value_array_new_from_types (NULL,
G_TYPE_STRING, callback,
G_TYPE_STRING, popup_title,
G_TYPE_STRING, drawable_type,
PIKA_TYPE_DRAWABLE, initial_drawable,
G_TYPE_BYTES, parent_window,
G_TYPE_NONE);
return_vals = _pika_pdb_run_procedure_array (pika_get_pdb (),
"pika-drawables-popup",
args);
pika_value_array_unref (args);
success = PIKA_VALUES_GET_ENUM (return_vals, 0) == PIKA_PDB_SUCCESS;
pika_value_array_unref (return_vals);
return success;
}
/**
* pika_drawables_close_popup:
* @callback: The name of the callback registered for this pop-up.
*
* Close the drawable selection dialog.
*
* Closes an open drawable selection dialog.
*
* Returns: TRUE on success.
**/
gboolean
pika_drawables_close_popup (const gchar *callback)
{
PikaValueArray *args;
PikaValueArray *return_vals;
gboolean success = TRUE;
args = pika_value_array_new_from_types (NULL,
G_TYPE_STRING, callback,
G_TYPE_NONE);
return_vals = _pika_pdb_run_procedure_array (pika_get_pdb (),
"pika-drawables-close-popup",
args);
pika_value_array_unref (args);
success = PIKA_VALUES_GET_ENUM (return_vals, 0) == PIKA_PDB_SUCCESS;
pika_value_array_unref (return_vals);
return success;
}
/**
* pika_drawables_set_popup:
* @callback: The name of the callback registered for this pop-up.
* @drawable: The drawable to set as selected.
*
* Sets the selected drawable in a drawable selection dialog.
*
* Sets the selected drawable in a drawable selection dialog.
*
* Returns: TRUE on success.
**/
gboolean
pika_drawables_set_popup (const gchar *callback,
PikaDrawable *drawable)
{
PikaValueArray *args;
PikaValueArray *return_vals;
gboolean success = TRUE;
args = pika_value_array_new_from_types (NULL,
G_TYPE_STRING, callback,
PIKA_TYPE_DRAWABLE, drawable,
G_TYPE_NONE);
return_vals = _pika_pdb_run_procedure_array (pika_get_pdb (),
"pika-drawables-set-popup",
args);
pika_value_array_unref (args);
success = PIKA_VALUES_GET_ENUM (return_vals, 0) == PIKA_PDB_SUCCESS;
pika_value_array_unref (return_vals);
return success;
}

View File

@ -0,0 +1,47 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-2003 Peter Mattis and Spencer Kimball
*
* pikadrawableselect_pdb.h
*
* 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
* Lesser 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/>.
*/
/* NOTE: This file is auto-generated by pdbgen.pl */
#if !defined (__PIKA_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pika.h> can be included directly."
#endif
#ifndef __PIKA_DRAWABLE_SELECT_PDB_H__
#define __PIKA_DRAWABLE_SELECT_PDB_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
gboolean pika_drawables_popup (const gchar *callback,
const gchar *popup_title,
const gchar *drawable_type,
PikaDrawable *initial_drawable,
GBytes *parent_window);
gboolean pika_drawables_close_popup (const gchar *callback);
gboolean pika_drawables_set_popup (const gchar *callback,
PikaDrawable *drawable);
G_END_DECLS
#endif /* __PIKA_DRAWABLE_SELECT_PDB_H__ */

86
libpika/pikafont.c Normal file
View File

@ -0,0 +1,86 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikafont.c
* Copyright (C) 2023 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
* Lesser 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 <pango/pangofc-fontmap.h>
#include "pika.h"
#include "pikafont.h"
struct _PikaFont
{
PikaResource parent_instance;
};
G_DEFINE_TYPE (PikaFont, pika_font, PIKA_TYPE_RESOURCE);
static void pika_font_class_init (PikaFontClass *klass)
{
}
static void pika_font_init (PikaFont *font)
{
}
/**
* pika_font_get_pango_font_description:
* @font: (transfer none): the [class@Gimp.Font]
*
* Returns a [class@Pango.Font.Description] representing @font.
*
* Returns: (transfer full): a %PangoFontDescription representing @font.
*
* Since: 3.0
**/
PangoFontDescription *
pika_font_get_pango_font_description (PikaFont *font)
{
PangoFontDescription *desc = NULL;
gchar *name = NULL;
gchar *collection_id = NULL;
gboolean is_internal;
is_internal = _pika_resource_get_identifiers (PIKA_RESOURCE (font),
&name, &collection_id);
/* TODO: we can't create fonts from internal fonts right now, but it should
* actually be possible because these are in fact alias to non-internal fonts.
* See #9985.
*/
if (! is_internal)
{
gchar *expanded_path;
expanded_path = pika_config_path_expand (collection_id, FALSE, NULL);
if (expanded_path != NULL &&
FcConfigAppFontAddFile (FcConfigGetCurrent (), (const FcChar8 *) expanded_path))
desc = pango_font_description_from_string (name);
g_free (expanded_path);
}
g_free (name);
g_free (collection_id);
return desc;
}

46
libpika/pikafont.h Normal file
View File

@ -0,0 +1,46 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikafont.h
* Copyright (C) 2023 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
* Lesser 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/>.
*/
#if !defined (__PIKA_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pika.h> can be included directly."
#endif
#ifndef __PIKA_FONT_H__
#define __PIKA_FONT_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#include <libpika/pikaresource.h>
#define PIKA_TYPE_FONT (pika_font_get_type ())
G_DECLARE_FINAL_TYPE (PikaFont, pika_font, PIKA, FONT, PikaResource)
PangoFontDescription * pika_font_get_pango_font_description (PikaFont *font);
G_END_DECLS
#endif /* __PIKA_FONT_H__ */

158
libpika/pikafontchooser.c Normal file
View File

@ -0,0 +1,158 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikafontchooser.c
* Copyright (C) 2003 Sven Neumann <sven@gimp.org>
*
* 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 "libpikawidgets/pikawidgets.h"
#include "pika.h"
#include "pikauitypes.h"
#include "pikafontchooser.h"
#include "pikauimarshal.h"
#include "libpika-intl.h"
/**
* SECTION: pikafontchooser
* @title: PikaFontChooser
* @short_description: A button which pops up a font selection dialog.
*
* A button which pops up a font selection dialog.
**/
struct _PikaFontChooser
{
PikaResourceChooser parent_instance;
GtkWidget *label;
};
static void pika_font_chooser_draw_interior (PikaResourceChooser *self);
static const GtkTargetEntry drag_target = { "application/x-pika-font-name", 0, 0 };
G_DEFINE_FINAL_TYPE (PikaFontChooser, pika_font_chooser, PIKA_TYPE_RESOURCE_CHOOSER)
static void
pika_font_chooser_class_init (PikaFontChooserClass *klass)
{
PikaResourceChooserClass *superclass = PIKA_RESOURCE_CHOOSER_CLASS (klass);
superclass->draw_interior = pika_font_chooser_draw_interior;
superclass->resource_type = PIKA_TYPE_FONT;
}
static void
pika_font_chooser_init (PikaFontChooser *self)
{
GtkWidget *button;
GtkWidget *hbox;
GtkWidget *image;
button = gtk_button_new ();
gtk_container_add (GTK_CONTAINER (self), button);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_container_add (GTK_CONTAINER (button), hbox);
image = gtk_image_new_from_icon_name (PIKA_ICON_FONT,
GTK_ICON_SIZE_BUTTON);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
self->label = gtk_label_new ("unknown");
gtk_box_pack_start (GTK_BOX (hbox), self->label, TRUE, TRUE, 4);
gtk_widget_show_all (GTK_WIDGET (self));
pika_resource_chooser_set_drag_target (PIKA_RESOURCE_CHOOSER (self),
hbox, &drag_target);
pika_resource_chooser_set_clickable (PIKA_RESOURCE_CHOOSER (self), button);
}
static void
pika_font_chooser_draw_interior (PikaResourceChooser *self)
{
PikaFontChooser *font_select= PIKA_FONT_CHOOSER (self);
PikaResource *resource;
gchar *name = NULL;
resource = pika_resource_chooser_get_resource (self);
if (resource)
name = pika_resource_get_name (resource);
gtk_label_set_text (GTK_LABEL (font_select->label), name);
}
/**
* pika_font_chooser_new:
* @title: (nullable): Title of the dialog to use or %NULL to use the default title.
* @label: (nullable): Button label or %NULL for no label.
* @resource: (nullable): Initial font.
*
* Creates a new #GtkWidget that lets a user choose a font.
* You can put this widget in a plug-in dialog.
*
* When font is NULL, initial choice is from context.
*
* Returns: A #GtkWidget that you can use in your UI.
*
* Since: 2.4
*/
GtkWidget *
pika_font_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource)
{
GtkWidget *chooser;
g_return_val_if_fail (resource == NULL || PIKA_IS_FONT (resource), NULL);
if (resource == NULL)
resource = PIKA_RESOURCE (pika_context_get_font ());
if (title)
chooser = g_object_new (PIKA_TYPE_FONT_CHOOSER,
"title", title,
"label", label,
"resource", resource,
NULL);
else
chooser = g_object_new (PIKA_TYPE_FONT_CHOOSER,
"label", label,
"resource", resource,
NULL);
pika_font_chooser_draw_interior (PIKA_RESOURCE_CHOOSER (chooser));
return chooser;
}

43
libpika/pikafontchooser.h Normal file
View File

@ -0,0 +1,43 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikafontchooser.h
*
* 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_FONT_CHOOSER_H__
#define __PIKA_FONT_CHOOSER_H__
#include <libpika/pikaresourcechooser.h>
G_BEGIN_DECLS
#define PIKA_TYPE_FONT_CHOOSER (pika_font_chooser_get_type ())
G_DECLARE_FINAL_TYPE (PikaFontChooser, pika_font_chooser, PIKA, FONT_CHOOSER, PikaResourceChooser)
GtkWidget * pika_font_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource);
G_END_DECLS
#endif /* __PIKA_FONT_CHOOSER_H__ */

42
libpika/pikagradient.c Normal file
View File

@ -0,0 +1,42 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikagradient.c
* Copyright (C) 2023 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
* Lesser 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 "pika.h"
#include "pikagradient.h"
struct _PikaGradient
{
PikaResource parent_instance;
};
G_DEFINE_TYPE (PikaGradient, pika_gradient, PIKA_TYPE_RESOURCE);
static void pika_gradient_class_init (PikaGradientClass *klass)
{
}
static void pika_gradient_init (PikaGradient *gradient)
{
}

43
libpika/pikagradient.h Normal file
View File

@ -0,0 +1,43 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikagradient.h
* Copyright (C) 2023 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
* Lesser 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/>.
*/
#if !defined (__PIKA_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pika.h> can be included directly."
#endif
#ifndef __PIKA_GRADIENT_H__
#define __PIKA_GRADIENT_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#include <libpika/pikaresource.h>
#define PIKA_TYPE_GRADIENT (pika_gradient_get_type ())
G_DECLARE_FINAL_TYPE (PikaGradient, pika_gradient, PIKA, GRADIENT, PikaResource)
G_END_DECLS
#endif /* __PIKA_GRADIENT_H__ */

View File

@ -0,0 +1,303 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikagradientchooser.c
* Copyright (C) 1998 Andy Thomas
*
* 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 "libpikawidgets/pikawidgets.h"
#include "pika.h"
#include "pikauitypes.h"
#include "pikagradientchooser.h"
#include "pikauimarshal.h"
#include "libpika-intl.h"
/**
* SECTION: pikagradientchooser
* @title: PikaGradientChooser
* @short_description: A button which pops up a gradient select dialog.
*
* A button which pops up a gradient select dialog.
**/
struct _PikaGradientChooser
{
PikaResourceChooser parent_instance;
GtkWidget *preview;
};
/* local function prototypes */
static void pika_gradient_chooser_draw_interior (PikaResourceChooser *self);
static void pika_gradient_select_preview_size_allocate (GtkWidget *widget,
GtkAllocation *allocation,
PikaGradientChooser *self);
static gboolean pika_gradient_select_preview_draw_handler (GtkWidget *preview,
cairo_t *cr,
PikaGradientChooser *self);
static const GtkTargetEntry drag_target = { "application/x-pika-gradient-name", 0 };
G_DEFINE_FINAL_TYPE (PikaGradientChooser,
pika_gradient_chooser,
PIKA_TYPE_RESOURCE_CHOOSER)
/* Initial dimensions of widget. */
#define CELL_HEIGHT 18
#define CELL_WIDTH 84
static void
pika_gradient_chooser_class_init (PikaGradientChooserClass *klass)
{
PikaResourceChooserClass *superclass = PIKA_RESOURCE_CHOOSER_CLASS (klass);
superclass->draw_interior = pika_gradient_chooser_draw_interior;
superclass->resource_type = PIKA_TYPE_GRADIENT;
}
static void
pika_gradient_chooser_init (PikaGradientChooser *self)
{
GtkWidget *button;
button = gtk_button_new ();
gtk_container_add (GTK_CONTAINER (self), button);
self->preview = gtk_drawing_area_new ();
gtk_widget_set_size_request (self->preview, CELL_WIDTH, CELL_HEIGHT);
gtk_container_add (GTK_CONTAINER (button), self->preview);
g_signal_connect (self->preview, "size-allocate",
G_CALLBACK (pika_gradient_select_preview_size_allocate),
self);
g_signal_connect (self->preview, "draw",
G_CALLBACK (pika_gradient_select_preview_draw_handler),
self);
gtk_widget_show_all (GTK_WIDGET (self));
pika_resource_chooser_set_drag_target (PIKA_RESOURCE_CHOOSER (self),
self->preview, &drag_target);
pika_resource_chooser_set_clickable (PIKA_RESOURCE_CHOOSER (self), button);
}
static void
pika_gradient_chooser_draw_interior (PikaResourceChooser *self)
{
PikaGradientChooser *gradient_select = PIKA_GRADIENT_CHOOSER (self);
gtk_widget_queue_draw (gradient_select->preview);
}
/**
* pika_gradient_chooser_new:
* @title: (nullable): Title of the dialog to use or %NULL to use the default title.
* @label: (nullable): Button label or %NULL for no label.
* @gradient: (nullable): Initial gradient.
*
* Creates a new #GtkWidget that lets a user choose a gradient.
* You can use this widget in a table in a plug-in dialog.
*
* Returns: A #GtkWidget that you can use in your UI.
*
* Since: 2.4
*/
GtkWidget *
pika_gradient_chooser_new (const gchar *title,
const gchar *label,
PikaResource *gradient)
{
GtkWidget *self;
if (gradient == NULL)
gradient = PIKA_RESOURCE (pika_context_get_gradient ());
if (title)
self = g_object_new (PIKA_TYPE_GRADIENT_CHOOSER,
"title", title,
"label", label,
"resource", gradient,
NULL);
else
self = g_object_new (PIKA_TYPE_GRADIENT_CHOOSER,
"label", label,
"resource", gradient,
NULL);
pika_gradient_chooser_draw_interior (PIKA_RESOURCE_CHOOSER (self));
return self;
}
/* private functions */
/* Get array of samples from self's gradient.
* Return array and size at given handles.
* Return success.
*/
static gboolean
get_gradient_data (PikaGradientChooser *self,
gint allocation_width,
gint *sample_count,
gdouble **sample_array)
{
PikaGradient *gradient;
gboolean result;
gdouble *samples;
gint n_samples;
g_object_get (self, "resource", &gradient, NULL);
result = pika_gradient_get_uniform_samples (gradient,
allocation_width,
FALSE, /* not reversed. */
&n_samples,
&samples);
if (result)
{
/* Return array of samples to dereferenced handles. */
*sample_array = samples;
*sample_count = n_samples;
}
g_object_unref (gradient);
/* When result is true, caller must free the array. */
return result;
}
/* Called on widget resized. */
static void
pika_gradient_select_preview_size_allocate (GtkWidget *widget,
GtkAllocation *allocation,
PikaGradientChooser *self)
{
/* Do nothing.
*
* In former code, we cached the gradient data in self, on allocate event.
* But allocate event always seems to be paired with a draw event,
* so there is no point in caching the gradient data.
* And caching gradient data is a premature optimization,
* without deep knowledge of Gtk and actual performance testing,
* you can't know caching helps performance.
*/
}
/* Draw array of samples.
* This understands mostly cairo, and little about gradient.
*/
static void
pika_gradient_select_preview_draw (cairo_t *cr,
gint src_width,
gint dest_width,
gdouble *src)
{
cairo_pattern_t *pattern;
cairo_surface_t *surface;
guchar *dest;
gint x;
pattern = pika_cairo_checkerboard_create (cr, PIKA_CHECK_SIZE_SM, NULL, NULL);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
cairo_paint (cr);
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, src_width, 1);
for (x = 0, dest = cairo_image_surface_get_data (surface);
x < src_width;
x++, src += 4, dest += 4)
{
PikaRGB color;
guchar r, g, b, a;
pika_rgba_set (&color, src[0], src[1], src[2], src[3]);
pika_rgba_get_uchar (&color, &r, &g, &b, &a);
PIKA_CAIRO_ARGB32_SET_PIXEL (dest, r, g, b, a);
}
cairo_surface_mark_dirty (surface);
pattern = cairo_pattern_create_for_surface (surface);
cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REFLECT);
cairo_surface_destroy (surface);
cairo_scale (cr, (gdouble) dest_width / (gdouble) src_width, 1.0);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
cairo_paint (cr);
}
/* Handles a draw signal.
* Draw self, i.e. interior of button.
*
* Always returns FALSE, but doesn't draw when fail to get gradient data.
*
* Is passed neither gradient nor attributes of gradient: get them now from self.
*/
static gboolean
pika_gradient_select_preview_draw_handler (GtkWidget *widget,
cairo_t *cr,
PikaGradientChooser *self)
{
GtkAllocation allocation;
/* Attributes of the source.*/
gdouble *src;
gint n_samples;
gint src_width;
gtk_widget_get_allocation (widget, &allocation);
if (!get_gradient_data (self, allocation.width, &n_samples, &src))
return FALSE;
/* Width in pixels of src, since BPP is 4. */
src_width = n_samples / 4;
pika_gradient_select_preview_draw (cr, src_width, allocation.width, src);
g_free (src);
return FALSE;
}

View File

@ -0,0 +1,43 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikagradientchooser.h
*
* 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_GRADIENT_CHOOSER_H__
#define __PIKA_GRADIENT_CHOOSER_H__
#include <libpika/pikaresourcechooser.h>
G_BEGIN_DECLS
#define PIKA_TYPE_GRADIENT_CHOOSER (pika_gradient_chooser_get_type ())
G_DECLARE_FINAL_TYPE (PikaGradientChooser, pika_gradient_chooser, PIKA, GRADIENT_CHOOSER, PikaResourceChooser)
GtkWidget * pika_gradient_chooser_new (const gchar *title,
const gchar *label,
PikaResource *gradient);
G_END_DECLS
#endif /* __PIKA_GRADIENT_CHOOSER_H__ */

42
libpika/pikapalette.c Normal file
View File

@ -0,0 +1,42 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapalette.c
* Copyright (C) 2023 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
* Lesser 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 "pika.h"
#include "pikapalette.h"
struct _PikaPalette
{
PikaResource parent_instance;
};
G_DEFINE_TYPE (PikaPalette, pika_palette, PIKA_TYPE_RESOURCE);
static void pika_palette_class_init (PikaPaletteClass *klass)
{
}
static void pika_palette_init (PikaPalette *palette)
{
}

43
libpika/pikapalette.h Normal file
View File

@ -0,0 +1,43 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapalette.h
* Copyright (C) 2023 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
* Lesser 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/>.
*/
#if !defined (__PIKA_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pika.h> can be included directly."
#endif
#ifndef __PIKA_PALETTE_H__
#define __PIKA_PALETTE_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#include <libpika/pikaresource.h>
#define PIKA_TYPE_PALETTE (pika_palette_get_type ())
G_DECLARE_FINAL_TYPE (PikaPalette, pika_palette, PIKA, PALETTE, PikaResource)
G_END_DECLS
#endif /* __PIKA_PALETTE_H__ */

View File

@ -0,0 +1,154 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapalettechooser.c
* Copyright (C) 2004 Michael Natterer <mitch@gimp.org>
*
* 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 "libpikawidgets/pikawidgets.h"
#include "pika.h"
#include "pikauitypes.h"
#include "pikapalettechooser.h"
#include "pikauimarshal.h"
#include "libpika-intl.h"
/**
* SECTION: pikapalettechooser
* @title: PikaPaletteChooser
* @short_description: A button which pops up a palette selection dialog.
*
* A button which pops up a palette selection dialog.
**/
struct _PikaPaletteChooser
{
PikaResourceChooser parent_instance;
GtkWidget *label;
GtkWidget *button;
};
static void pika_palette_chooser_draw_interior (PikaResourceChooser *self);
static const GtkTargetEntry drag_target = { "application/x-pika-palette-name", 0, 0 };
G_DEFINE_FINAL_TYPE (PikaPaletteChooser, pika_palette_chooser, PIKA_TYPE_RESOURCE_CHOOSER)
static void
pika_palette_chooser_class_init (PikaPaletteChooserClass *klass)
{
PikaResourceChooserClass *superclass = PIKA_RESOURCE_CHOOSER_CLASS (klass);
superclass->draw_interior = pika_palette_chooser_draw_interior;
superclass->resource_type = PIKA_TYPE_PALETTE;
}
static void
pika_palette_chooser_init (PikaPaletteChooser *self)
{
GtkWidget *hbox;
GtkWidget *image;
self->button = gtk_button_new ();
gtk_container_add (GTK_CONTAINER (self), self->button);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
gtk_container_add (GTK_CONTAINER (self->button), hbox);
image = gtk_image_new_from_icon_name (PIKA_ICON_PALETTE,
GTK_ICON_SIZE_BUTTON);
gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
self->label = gtk_label_new ("unknown");
gtk_box_pack_start (GTK_BOX (hbox), self->label, TRUE, TRUE, 4);
gtk_widget_show_all (GTK_WIDGET (self));
pika_resource_chooser_set_drag_target (PIKA_RESOURCE_CHOOSER (self),
hbox, &drag_target);
pika_resource_chooser_set_clickable (PIKA_RESOURCE_CHOOSER (self), self->button);
}
static void
pika_palette_chooser_draw_interior (PikaResourceChooser *self)
{
PikaPaletteChooser *palette_select= PIKA_PALETTE_CHOOSER (self);
PikaResource *resource;
gchar *name = NULL;
resource = pika_resource_chooser_get_resource (self);
if (resource)
name = pika_resource_get_name (resource);
gtk_label_set_text (GTK_LABEL (palette_select->label), name);
}
/**
* pika_palette_chooser_new:
* @title: (nullable): Title of the dialog to use or %NULL to use the default title.
* @label: (nullable): Button label or %NULL for no label.
* @resource: (nullable): Initial palette.
*
* Creates a new #GtkWidget that lets a user choose a palette.
* You can put this widget in a table in a plug-in dialog.
*
* When palette is NULL, initial choice is from context.
*
* Returns: A #GtkWidget that you can use in your UI.
*
* Since: 2.4
*/
GtkWidget *
pika_palette_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource)
{
GtkWidget *self;
if (resource == NULL)
resource = PIKA_RESOURCE (pika_context_get_palette ());
if (title)
self = g_object_new (PIKA_TYPE_PALETTE_CHOOSER,
"title", title,
"label", label,
"resource", resource,
NULL);
else
self = g_object_new (PIKA_TYPE_PALETTE_CHOOSER,
"label", label,
"resource", resource,
NULL);
pika_palette_chooser_draw_interior (PIKA_RESOURCE_CHOOSER (self));
return self;
}

View File

@ -0,0 +1,43 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapaletteselectbutton.h
*
* 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_PALETTE_CHOOSER_H__
#define __PIKA_PALETTE_CHOOSER_H__
#include <libpika/pikaresourcechooser.h>
G_BEGIN_DECLS
#define PIKA_TYPE_PALETTE_CHOOSER (pika_palette_chooser_get_type ())
G_DECLARE_FINAL_TYPE (PikaPaletteChooser, pika_palette_chooser, PIKA, PALETTE_CHOOSER, PikaResourceChooser)
GtkWidget * pika_palette_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource);
G_END_DECLS
#endif /* __PIKA_PALETTE_CHOOSER_H__ */

207
libpika/pikapattern.c Normal file
View File

@ -0,0 +1,207 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapattern.c
* Copyright (C) 2023 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
* Lesser 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 "pika.h"
#include "pikapattern.h"
struct _PikaPattern
{
PikaResource parent_instance;
/* Native size buffer of the pattern contents. */
GeglBuffer *buffer;
};
static void pika_pattern_finalize (GObject *object);
static void pika_pattern_get_data (PikaPattern *pattern);
static GeglBuffer * pika_pattern_scale (GeglBuffer *buffer,
gint max_width,
gint max_height);
G_DEFINE_TYPE (PikaPattern, pika_pattern, PIKA_TYPE_RESOURCE);
static void pika_pattern_class_init (PikaPatternClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = pika_pattern_finalize;
}
static void pika_pattern_init (PikaPattern *pattern)
{
pattern->buffer = NULL;
}
static void
pika_pattern_finalize (GObject *object)
{
PikaPattern *pattern = PIKA_PATTERN (object);
g_clear_object (&pattern->buffer);
G_OBJECT_CLASS (pika_pattern_parent_class)->finalize (object);
}
/**
* pika_pattern_get_buffer:
* @pattern: a [class@Pattern].
* @max_width: a maximum width for the returned buffer.
* @max_height: a maximum height for the returned buffer.
* @format: an optional Babl format.
*
* Gets pixel data of the pattern within the bounding box specified by @max_width
* and @max_height. The data will be scaled down so that it fits within this
* size without changing its ratio. If the pattern is smaller than this size to
* begin with, it will not be scaled up.
*
* If @max_width or @max_height are %NULL, the buffer is returned in the pattern's
* native size.
*
* Make sure you called [func@Gegl.init] before calling any function using
* `GEGL`.
*
* Returns: (transfer full): a [class@Gegl.Buffer].
*/
GeglBuffer *
pika_pattern_get_buffer (PikaPattern *pattern,
gint max_width,
gint max_height,
const Babl *format)
{
pika_pattern_get_data (pattern);
g_return_val_if_fail (pattern->buffer != NULL, NULL);
if (max_width == 0 || max_height == 0 ||
(gegl_buffer_get_width (pattern->buffer) <= max_width &&
gegl_buffer_get_height (pattern->buffer) <= max_height))
return gegl_buffer_dup (pattern->buffer);
return pika_pattern_scale (pattern->buffer, max_width, max_height);
}
static void
pika_pattern_get_data (PikaPattern *pattern)
{
gint width;
gint height;
gint bpp;
GBytes *bytes;
const guchar *pixels;
gsize pixels_size;
const Babl *format;
/*
* This check assumes that the pattern contents doesn't change, which is not a
* perfect assumption. We could maybe add a PDB call which would return
* the new pattern data only if it changed since last call (which can be
* verified with some kind of internal runtime version to pass from one call
* to another). TODO
*/
if (pattern->buffer != NULL)
return;
g_clear_object (&pattern->buffer);
_pika_pattern_get_pixels (pattern, &width, &height, &bpp, &bytes);
pixels = g_bytes_unref_to_data (bytes, &pixels_size);
/* It's an ugly way to determine the proper format but pika_pattern_get_pixels()
* doesn't give more info.
*/
switch (bpp)
{
case 1:
format = babl_format ("Y' u8");
break;
case 2:
format = babl_format ("Y'A u8");
break;
case 3:
format = babl_format ("R'G'B' u8");
break;
case 4:
format = babl_format ("R'G'B'A u8");
break;
default:
g_return_if_reached ();
}
pattern->buffer = gegl_buffer_linear_new_from_data ((const gpointer) pixels, format,
GEGL_RECTANGLE (0, 0, width, height),
0, g_free, NULL);
}
static GeglBuffer *
pika_pattern_scale (GeglBuffer *buffer,
gint max_width,
gint max_height)
{
GeglBuffer *scaled = NULL;
GeglNode *graph;
GeglNode *source;
GeglNode *op;
GeglNode *sink;
gdouble width;
gdouble height;
gdouble scale;
height = (gdouble) max_height;
width = (gdouble) gegl_buffer_get_width (buffer) / gegl_buffer_get_height (buffer) * height;
if (width > (gdouble) max_width)
{
width = (gdouble) max_width;
height = (gdouble) gegl_buffer_get_height (buffer) / gegl_buffer_get_width (buffer) * width;
}
scale = width / gegl_buffer_get_width (buffer);
graph = gegl_node_new ();
source = gegl_node_new_child (graph,
"operation", "gegl:buffer-source",
"buffer", buffer,
NULL);
op = gegl_node_new_child (graph,
"operation", "gegl:scale-ratio",
"origin-x", 0.0,
"origin-y", 0.0,
"sampler", PIKA_INTERPOLATION_LINEAR,
"abyss-policy", GEGL_ABYSS_CLAMP,
"x", scale,
"y", scale,
NULL);
sink = gegl_node_new_child (graph,
"operation", "gegl:buffer-sink",
"buffer", &scaled,
"format", gegl_buffer_get_format (buffer),
NULL);
gegl_node_link_many (source, op, sink, NULL);
gegl_node_process (sink);
g_object_unref (graph);
return scaled;
}

49
libpika/pikapattern.h Normal file
View File

@ -0,0 +1,49 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapattern.h
* Copyright (C) 2023 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
* Lesser 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/>.
*/
#if !defined (__PIKA_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pika.h> can be included directly."
#endif
#ifndef __PIKA_PATTERN_H__
#define __PIKA_PATTERN_H__
G_BEGIN_DECLS
/* For information look into the C source or the html documentation */
#include <libpika/pikaresource.h>
#define PIKA_TYPE_PATTERN (pika_pattern_get_type ())
G_DECLARE_FINAL_TYPE (PikaPattern, pika_pattern, PIKA, PATTERN, PikaResource)
GeglBuffer * pika_pattern_get_buffer (PikaPattern *pattern,
gint max_width,
gint max_height,
const Babl *format) G_GNUC_WARN_UNUSED_RESULT;
G_END_DECLS
#endif /* __PIKA_PATTERN_H__ */

View File

@ -0,0 +1,381 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapatternchooser.c
* Copyright (C) 1998 Andy Thomas
*
* 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 "libpikawidgets/pikawidgets.h"
#include "pika.h"
#include "pikauitypes.h"
#include "pikapatternchooser.h"
#include "pikauimarshal.h"
#include "libpika-intl.h"
/**
* SECTION: pikapatternchooser
* @title: PikaPatternChooser
* @short_description: A button which pops up a pattern selection dialog.
*
* A button which pops up a pattern selection dialog.
*
* Note that this widget draws itself using `GEGL` code. You **must** call
* [func@Gegl.init] first to be able to use this chooser.
**/
#define CELL_SIZE 40
struct _PikaPatternChooser
{
PikaResourceChooser parent_instance;
GtkWidget *preview;
GtkWidget *popup;
PikaPattern *pattern;
GeglBuffer *buffer;
gint width;
gint height;
};
/* local */
static gboolean pika_pattern_select_on_preview_events (GtkWidget *widget,
GdkEvent *event,
PikaPatternChooser *button);
/* local drawing methods. */
static void pika_pattern_select_preview_fill_draw (PikaPatternChooser *chooser,
PikaPreviewArea *area);
static void pika_pattern_chooser_draw (PikaResourceChooser *self);
static void pika_pattern_chooser_get_pattern_image (PikaPatternChooser *self,
gint width,
gint height);
/* Popup methods. */
static void pika_pattern_chooser_open_popup (PikaPatternChooser *button,
gint x,
gint y);
static void pika_pattern_chooser_close_popup (PikaPatternChooser *button);
/* A GtkTargetEntry has a string and two ints.
* This is one, but we treat it as an array.
*/
static const GtkTargetEntry drag_target = { "application/x-pika-pattern-name", 0, 0 };
G_DEFINE_FINAL_TYPE (PikaPatternChooser, pika_pattern_chooser, PIKA_TYPE_RESOURCE_CHOOSER)
static void
pika_pattern_chooser_class_init (PikaPatternChooserClass *klass)
{
PikaResourceChooserClass *superclass = PIKA_RESOURCE_CHOOSER_CLASS (klass);
superclass->draw_interior = pika_pattern_chooser_draw;
superclass->resource_type = PIKA_TYPE_PATTERN;
}
static void
pika_pattern_chooser_init (PikaPatternChooser *self)
{
GtkWidget *frame;
GtkWidget *button;
frame = gtk_aspect_frame_new (NULL, 0.5, 0.5, 1.0, FALSE);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (self), frame, FALSE, FALSE, 0);
gtk_widget_show (frame);
self->preview = pika_preview_area_new ();
gtk_widget_add_events (self->preview,
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
gtk_widget_set_size_request (self->preview, CELL_SIZE, CELL_SIZE);
gtk_container_add (GTK_CONTAINER (frame), self->preview);
gtk_widget_show (self->preview);
g_signal_connect_swapped (self->preview, "size-allocate",
G_CALLBACK (pika_pattern_chooser_draw),
self);
g_signal_connect (self->preview, "event",
G_CALLBACK (pika_pattern_select_on_preview_events),
self);
button = gtk_button_new_with_mnemonic (_("_Browse..."));
gtk_box_pack_start (GTK_BOX (self), button, FALSE, FALSE, 0);
pika_resource_chooser_set_drag_target (PIKA_RESOURCE_CHOOSER (self),
self->preview, &drag_target);
pika_resource_chooser_set_clickable (PIKA_RESOURCE_CHOOSER (self), button);
gtk_widget_show (button);
}
/**
* pika_pattern_chooser_new:
* @title: (nullable): Title of the dialog to use or %NULL to use the default title.
* @label: (nullable): Button label or %NULL for no label.
* @resource: (nullable): Initial pattern.
*
* Creates a new #GtkWidget that lets a user choose a pattern.
* You can put this widget in a table in a plug-in dialog.
*
* When pattern is NULL, initial choice is from context.
*
* Returns: A #GtkWidget that you can use in your UI.
*
* Since: 2.4
*/
GtkWidget *
pika_pattern_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource)
{
GtkWidget *self;
if (resource == NULL)
resource = PIKA_RESOURCE (pika_context_get_pattern ());
g_return_val_if_fail (PIKA_IS_PATTERN (resource), NULL);
if (title)
self = g_object_new (PIKA_TYPE_PATTERN_CHOOSER,
"title", title,
"label", label,
"resource", resource,
NULL);
else
self = g_object_new (PIKA_TYPE_PATTERN_CHOOSER,
"label", label,
"resource", resource,
NULL);
pika_pattern_chooser_draw (PIKA_RESOURCE_CHOOSER (self));
return self;
}
static void
pika_pattern_chooser_draw (PikaResourceChooser *chooser)
{
PikaPatternChooser *pchooser = PIKA_PATTERN_CHOOSER (chooser);
GtkAllocation allocation;
gtk_widget_get_allocation (pchooser->preview, &allocation);
pika_pattern_chooser_get_pattern_image (pchooser, allocation.width, allocation.height);
pika_pattern_select_preview_fill_draw (pchooser, PIKA_PREVIEW_AREA (pchooser->preview));
}
static void
pika_pattern_chooser_get_pattern_image (PikaPatternChooser *chooser,
gint width,
gint height)
{
PikaPattern *pattern;
g_object_get (chooser, "resource", &pattern, NULL);
if (chooser->pattern == pattern &&
chooser->width == width &&
chooser->height == height)
{
/* Let's assume pattern contents is not changing in a single run. */
g_object_unref (pattern);
return;
}
g_clear_object (&chooser->buffer);
chooser->pattern = pattern;
chooser->buffer = pika_pattern_get_buffer (pattern, width, height, NULL);
chooser->width = gegl_buffer_get_width (chooser->buffer);
chooser->height = gegl_buffer_get_height (chooser->buffer);
g_object_unref (pattern);
}
/* On mouse events in self's preview, popup a zoom view of entire pattern */
static gboolean
pika_pattern_select_on_preview_events (GtkWidget *widget,
GdkEvent *event,
PikaPatternChooser *self)
{
GdkEventButton *bevent;
switch (event->type)
{
case GDK_BUTTON_PRESS:
bevent = (GdkEventButton *) event;
if (bevent->button == 1)
{
gtk_grab_add (widget);
pika_pattern_chooser_open_popup (self,
bevent->x, bevent->y);
}
break;
case GDK_BUTTON_RELEASE:
bevent = (GdkEventButton *) event;
if (bevent->button == 1)
{
gtk_grab_remove (widget);
pika_pattern_chooser_close_popup (self);
}
break;
default:
break;
}
return FALSE;
}
/* Fill a PikaPreviewArea with a image then draw. */
static void
pika_pattern_select_preview_fill_draw (PikaPatternChooser *chooser,
PikaPreviewArea *area)
{
GeglBuffer *src_buffer;
const Babl *format;
const Babl *model;
guchar *src;
PikaImageType type;
gint rowstride;
GtkAllocation allocation;
gint x = 0;
gint y = 0;
gtk_widget_get_allocation (GTK_WIDGET (area), &allocation);
/* Fill with white. */
if (chooser->width < allocation.width ||
chooser->height < allocation.height)
{
pika_preview_area_fill (area,
0, 0,
allocation.width,
allocation.height,
0xFF, 0xFF, 0xFF);
x = ((allocation.width - chooser->width) / 2);
y = ((allocation.height - chooser->height) / 2);
}
/* Draw the pattern. */
src_buffer = chooser->buffer;
format = gegl_buffer_get_format (src_buffer);
rowstride = chooser->width * babl_format_get_bytes_per_pixel (format);
model = babl_format_get_model (format);
if (model == babl_model ("R'G'B'"))
type = PIKA_RGB_IMAGE;
else if (model == babl_model ("R'G'B'A"))
type = PIKA_RGBA_IMAGE;
else if (model == babl_model ("Y'"))
type = PIKA_GRAY_IMAGE;
else if (model == babl_model ("Y'A"))
type = PIKA_GRAYA_IMAGE;
else
/* I just know that we can't have other formats because I set it up this way
* in pika_pattern_get_buffer(). If we make the latter more generic, able to
* return more types of pixel data, this should be reviewed. XXX
*/
g_return_if_reached ();
src = g_try_malloc (sizeof (guchar) * rowstride * chooser->height);
gegl_buffer_get (chooser->buffer, NULL, 1.0, format, src, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
pika_preview_area_draw (area, x, y, chooser->width, chooser->height, type, src, rowstride);
g_free (src);
}
/* popup methods. */
static void
pika_pattern_chooser_open_popup (PikaPatternChooser *chooser,
gint x,
gint y)
{
GtkWidget *frame;
GtkWidget *preview;
GdkMonitor *monitor;
GdkRectangle workarea;
gint x_org;
gint y_org;
if (chooser->popup)
pika_pattern_chooser_close_popup (chooser);
if (chooser->width <= CELL_SIZE && chooser->height <= CELL_SIZE)
return;
chooser->popup = gtk_window_new (GTK_WINDOW_POPUP);
gtk_window_set_type_hint (GTK_WINDOW (chooser->popup), GDK_WINDOW_TYPE_HINT_DND);
gtk_window_set_screen (GTK_WINDOW (chooser->popup),
gtk_widget_get_screen (GTK_WIDGET (chooser)));
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
gtk_container_add (GTK_CONTAINER (chooser->popup), frame);
gtk_widget_show (frame);
preview = pika_preview_area_new ();
gtk_widget_set_size_request (preview, chooser->width, chooser->height);
gtk_container_add (GTK_CONTAINER (frame), preview);
gtk_widget_show (preview);
/* decide where to put the popup: near the preview i.e. at mousedown coords */
gdk_window_get_origin (gtk_widget_get_window (chooser->preview),
&x_org, &y_org);
monitor = pika_widget_get_monitor (GTK_WIDGET (chooser));
gdk_monitor_get_workarea (monitor, &workarea);
x = x_org + x - (chooser->width / 2);
y = y_org + y - (chooser->height / 2);
x = CLAMP (x, workarea.x, workarea.x + workarea.width - chooser->width);
y = CLAMP (y, workarea.y, workarea.y + workarea.height - chooser->height);
gtk_window_move (GTK_WINDOW (chooser->popup), x, y);
gtk_widget_show (chooser->popup);
/* Draw popup now. Usual events do not cause a draw. */
pika_pattern_select_preview_fill_draw (chooser, PIKA_PREVIEW_AREA (preview));
}
static void
pika_pattern_chooser_close_popup (PikaPatternChooser *self)
{
g_clear_pointer (&self->popup, gtk_widget_destroy);
}

View File

@ -0,0 +1,43 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapatternchooser.h
*
* 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_PATTERN_CHOOSER_H__
#define __PIKA_PATTERN_CHOOSER_H__
#include <libpika/pikaresourcechooser.h>
G_BEGIN_DECLS
#define PIKA_TYPE_PATTERN_CHOOSER (pika_pattern_chooser_get_type ())
G_DECLARE_FINAL_TYPE (PikaPatternChooser, pika_pattern_chooser, PIKA, PATTERN_CHOOSER, PikaResourceChooser)
GtkWidget * pika_pattern_chooser_new (const gchar *title,
const gchar *label,
PikaResource *resource);
G_END_DECLS
#endif /* __PIKA_PATTERN_CHOOSER_H__ */

273
libpika/pikapropwidgets.c Normal file
View File

@ -0,0 +1,273 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapropwidgets.c
* Copyright (C) 2023 Jehan
*
* 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 library. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "libpika/pikaui.h"
#include "libpika-intl.h"
/*
* This is a complement of libpikawidgets/pikapropwidgets.c
* These are property functions for types from libpika, such as
* [class@Gimp.Resource] or [class@Gimp.Item] subclasses.
*/
typedef GtkWidget* (*PikaResourceWidgetCreator) (const gchar *title,
const gchar *label,
PikaResource *initial_resource);
/* utility function prototypes */
static GtkWidget * pika_prop_resource_chooser_factory (PikaResourceWidgetCreator widget_creator_func,
GObject *config,
const gchar *property_name,
const gchar *chooser_title);
static gchar * pika_utils_make_canonical_menu_label (const gchar *path);
/**
* pika_prop_brush_chooser_new:
* @config: Object to which property is attached.
* @property_name: Name of a [class@Gimp.Brush] property.
* @chooser_title: (nullable): title for the poppable dialog.
*
* Creates a [class@Gimp.Brush.Chooser] controlled by the specified property.
*
* Returns: (transfer full): A new [class@GimpUi.BrushChooser].
*
* Since: 3.0
*/
GtkWidget *
pika_prop_brush_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title)
{
return pika_prop_resource_chooser_factory (pika_brush_chooser_new,
config, property_name, chooser_title);
}
/**
* pika_prop_font_chooser_new:
* @config: Object to which property is attached.
* @property_name: Name of a [class@Gimp.Font] property.
* @chooser_title: (nullable): title for the poppable dialog.
*
* Creates a [class@GimpUi.FontChooser] controlled by the specified property.
*
* Returns: (transfer full): A new [class@GimpUi.FontChooser].
*
* Since: 3.0
*/
GtkWidget *
pika_prop_font_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title)
{
return pika_prop_resource_chooser_factory (pika_font_chooser_new,
config, property_name, chooser_title);
}
/**
* pika_prop_gradient_chooser_new:
* @config: Object to which property is attached.
* @property_name: Name of a [class@Gimp.Gradient] property.
* @chooser_title: (nullable): title for the poppable dialog.
*
* Creates a [class@GimpUi.GradientChooser] controlled by the specified property.
*
* Returns: (transfer full): A new [class@GimpUi.GradientChooser].
*
* Since: 3.0
*/
GtkWidget *
pika_prop_gradient_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title)
{
return pika_prop_resource_chooser_factory (pika_gradient_chooser_new,
config, property_name, chooser_title);
}
/**
* pika_prop_palette_chooser_new:
* @config: Object to which property is attached.
* @property_name: Name of a [class@Gimp.Palette] property.
* @chooser_title: (nullable): title for the poppable dialog.
*
* Creates a [class@GimpUi.PaletteChooser] controlled by the specified property.
*
* Returns: (transfer full): A new [class@GimpUi.PaletteChooser].
*
* Since: 3.0
*/
GtkWidget *
pika_prop_palette_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title)
{
return pika_prop_resource_chooser_factory (pika_palette_chooser_new,
config, property_name, chooser_title);
}
/**
* pika_prop_pattern_chooser_new:
* @config: Object to which property is attached.
* @property_name: Name of a [class@Gimp.Pattern] property.
* @chooser_title: (nullable): title for the poppable dialog.
*
* Creates a [class@GimpUi.PatternChooser] controlled by the specified property.
*
* Returns: (transfer full): A new [class@GimpUi.PatternChooser].
*
* Since: 3.0
*/
GtkWidget *
pika_prop_pattern_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title)
{
return pika_prop_resource_chooser_factory (pika_pattern_chooser_new,
config, property_name, chooser_title);
}
/**
* pika_prop_drawable_chooser_new:
* @config: Object to which property is attached.
* @property_name: Name of a [class@Gimp.Drawable] property.
* @chooser_title: (nullable): title for the poppable dialog.
*
* Creates a [class@GimpUi.DrawableChooser] controlled by the specified property.
*
* Returns: (transfer full): A new [class@GimpUi.DrawableChooser].
*
* Since: 3.0
*/
GtkWidget *
pika_prop_drawable_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title)
{
GParamSpec *param_spec;
GtkWidget *prop_chooser;
PikaDrawable *initial_drawable = NULL;
gchar *title = NULL;
const gchar *label;
param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
property_name);
g_return_val_if_fail (param_spec != NULL, NULL);
g_return_val_if_fail (g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_OBJECT) &&
g_type_is_a (param_spec->value_type, PIKA_TYPE_DRAWABLE), NULL);
g_object_get (config,
property_name, &initial_drawable,
NULL);
label = g_param_spec_get_nick (param_spec);
if (chooser_title == NULL)
{
gchar *canonical;
canonical = pika_utils_make_canonical_menu_label (label);
if (g_type_is_a (param_spec->value_type, PIKA_TYPE_LAYER))
title = g_strdup_printf (_("Choose layer: %s"), canonical);
if (g_type_is_a (param_spec->value_type, PIKA_TYPE_CHANNEL))
title = g_strdup_printf (_("Choose channel: %s"), canonical);
else
title = g_strdup_printf (_("Choose drawable: %s"), canonical);
g_free (canonical);
}
else
{
title = g_strdup (chooser_title);
}
prop_chooser = pika_drawable_chooser_new (title, label, param_spec->value_type, initial_drawable);
g_clear_object (&initial_drawable);
g_free (title);
g_object_bind_property (prop_chooser, "drawable",
config, property_name,
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
return prop_chooser;
}
/*******************************/
/* private utility functions */
/*******************************/
static GtkWidget *
pika_prop_resource_chooser_factory (PikaResourceWidgetCreator widget_creator_func,
GObject *config,
const gchar *property_name,
const gchar *chooser_title)
{
GParamSpec *param_spec;
GtkWidget *prop_chooser;
PikaResource *initial_resource;
const gchar *label;
param_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
property_name);
g_return_val_if_fail (param_spec != NULL, NULL);
g_return_val_if_fail (g_type_is_a (G_TYPE_FROM_INSTANCE (param_spec), G_TYPE_PARAM_OBJECT) &&
g_type_is_a (param_spec->value_type, PIKA_TYPE_RESOURCE), NULL);
g_object_get (config,
property_name, &initial_resource,
NULL);
label = g_param_spec_get_nick (param_spec);
/* Create the wrapped widget. For example, call pika_font_chooser_new.
* When initial_resource is NULL, the widget creator will set it's resource
* property from context.
*/
prop_chooser = widget_creator_func (chooser_title, label, initial_resource);
g_clear_object (&initial_resource);
g_object_bind_property (prop_chooser, "resource",
config, property_name,
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
return prop_chooser;
}
/* This is a copy of the similarly-named function in app/widgets/pikawidgets-utils.c
* I hesitated to put this maybe in libpikawidgets/pikawidgetsutils.h but for
* now, let's not. If it's useful to more people, it's always easier to move the
* function in rather than deprecating it.
*/
static gchar *
pika_utils_make_canonical_menu_label (const gchar *path)
{
gchar **split_path;
gchar *canon_path;
/* The first underscore of each path item is a mnemonic. */
split_path = g_strsplit (path, "_", 2);
canon_path = g_strjoinv ("", split_path);
g_strfreev (split_path);
return canon_path;
}

58
libpika/pikapropwidgets.h Normal file
View File

@ -0,0 +1,58 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* pikapropwidgets.h
* Copyright (C) 2023 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_PROP_WIDGETS_H__
#define __PIKA_PROP_WIDGETS_H__
G_BEGIN_DECLS
/* PikaParamResource */
GtkWidget * pika_prop_brush_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
GtkWidget * pika_prop_font_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
GtkWidget * pika_prop_gradient_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
GtkWidget * pika_prop_palette_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
GtkWidget * pika_prop_pattern_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
GtkWidget * pika_prop_drawable_chooser_new (GObject *config,
const gchar *property_name,
const gchar *chooser_title);
G_END_DECLS
#endif /* __PIKA_PROP_WIDGETS_H__ */

View File

@ -0,0 +1,622 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* 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 "libpikawidgets/pikawidgets.h"
#include "pika.h"
#include "pikauitypes.h"
#include "pikaresourcechooser.h"
#include "pikauimarshal.h"
#include "libpika-intl.h"
/**
* SECTION: pikaresourcechooser
* @title: PikaResourceChooser
* @short_description: Base class for buttons that popup a resource
* selection dialog.
*
* A button which pops up a resource selection dialog.
*
* Responsibilities:
*
* - implementing outer container widget,
* - managing clicks and popping up a remote chooser,
* - having a resource property,
* - signaling when user selects resource
* - receiving drag,
* - triggering draws of the button interior (by subclass) and draws of remote popup chooser.
*
* Collaborations:
*
* - owned by PikaProcedureDialog via PikaPropWidget
* - resource property usually bound to a PikaConfig for a PikaPluginProcedure.
* - communicates using PikaResourceSelect with remote PikaPDBDialog,
* to choose an installed PikaResource owned by core.
*
* Subclass responsibilities:
*
* - creating interior widgets
* - drawing the interior (a preview of the chosen resource)
* - declaring which interior widgets are drag destinations
* - declaring which interior widgets are clickable (generate "clicked" signal)
* - generate "clicked" (delegating to GtkButton or implementing from mouse events)
*
* Since: 3.0
**/
enum
{
RESOURCE_SET,
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_TITLE,
PROP_LABEL,
PROP_RESOURCE,
N_PROPS
};
typedef struct
{
PikaResource *resource;
gchar *title;
gchar *label;
gchar *callback;
GtkWidget *label_widget;
} PikaResourceChooserPrivate;
/* local function prototypes */
static void pika_resource_chooser_constructed (GObject *object);
static void pika_resource_chooser_dispose (GObject *object);
static void pika_resource_chooser_finalize (GObject *object);
static void pika_resource_chooser_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void pika_resource_chooser_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void pika_resource_chooser_clicked (PikaResourceChooser *self);
static void pika_resource_chooser_callback (PikaResource *resource,
gboolean dialog_closing,
gpointer user_data);
static void pika_resource_select_drag_data_received (PikaResourceChooser *self,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection,
guint info,
guint time);
static void pika_resource_chooser_set_remote_dialog (PikaResourceChooser *self,
PikaResource *resource);
static guint resource_button_signals[LAST_SIGNAL] = { 0 };
static GParamSpec *resource_button_props[N_PROPS] = { NULL, };
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (PikaResourceChooser, pika_resource_chooser, GTK_TYPE_BOX)
static void
pika_resource_chooser_class_init (PikaResourceChooserClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = pika_resource_chooser_constructed;
object_class->dispose = pika_resource_chooser_dispose;
object_class->finalize = pika_resource_chooser_finalize;
object_class->set_property = pika_resource_chooser_set_property;
object_class->get_property = pika_resource_chooser_get_property;
klass->resource_set = NULL;
/**
* PikaResourceChooser:title:
*
* The title to be used for the resource selection popup dialog.
*
* Since: 3.0
*/
resource_button_props[PROP_TITLE] =
g_param_spec_string ("title",
"Title",
"The title to be used for the resource selection popup dialog",
"Resource Selection",
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
/**
* PikaResourceChooser:label:
*
* Label text with mnemonic.
*
* Since: 3.0
*/
resource_button_props[PROP_LABEL] =
g_param_spec_string ("label",
"Label",
"The label to be used next to the button",
NULL,
PIKA_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
/**
* PikaResourceChooser:resource:
*
* The currently selected resource.
*
* Since: 3.0
*/
resource_button_props[PROP_RESOURCE] =
pika_param_spec_resource ("resource",
"Resource",
"The currently selected resource",
TRUE, /* none_ok */
PIKA_PARAM_READWRITE);
g_object_class_install_properties (object_class,
N_PROPS, resource_button_props);
/**
* PikaResourceChooser::resource-set:
* @widget: the object which received the signal.
* @resource: the currently selected resource.
* @dialog_closing: whether the dialog was closed or not.
*
* The ::resource-set signal is emitted when the user selects a resource.
*
* Since: 3.0
*/
resource_button_signals[RESOURCE_SET] =
g_signal_new ("resource-set",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (PikaResourceChooserClass, resource_set),
NULL, NULL,
_pikaui_marshal_VOID__POINTER_BOOLEAN,
G_TYPE_NONE, 2,
G_TYPE_OBJECT,
G_TYPE_BOOLEAN);
}
static void
pika_resource_chooser_init (PikaResourceChooser *self)
{
PikaResourceChooserPrivate *priv;
priv = pika_resource_chooser_get_instance_private (PIKA_RESOURCE_CHOOSER (self));
gtk_orientable_set_orientation (GTK_ORIENTABLE (self),
GTK_ORIENTATION_HORIZONTAL);
gtk_box_set_spacing (GTK_BOX (self), 6);
priv->label_widget = gtk_label_new (NULL);
gtk_box_pack_start (GTK_BOX (self), priv->label_widget, FALSE, FALSE, 0);
}
static void
pika_resource_chooser_constructed (GObject *object)
{
PikaResourceChooserPrivate *priv;
priv = pika_resource_chooser_get_instance_private (PIKA_RESOURCE_CHOOSER (object));
gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->label_widget), priv->label);
gtk_widget_show (GTK_WIDGET (priv->label_widget));
G_OBJECT_CLASS (pika_resource_chooser_parent_class)->constructed (object);
}
static void
pika_resource_chooser_dispose (GObject *self)
{
PikaResourceChooserPrivate *priv;
PikaResourceChooserClass *klass;
priv = pika_resource_chooser_get_instance_private (PIKA_RESOURCE_CHOOSER (self));
klass = PIKA_RESOURCE_CHOOSER_GET_CLASS (self);
if (priv->callback)
{
GType resource_type = klass->resource_type;
if (resource_type == PIKA_TYPE_FONT)
pika_fonts_close_popup (priv->callback);
else if (resource_type == PIKA_TYPE_GRADIENT)
pika_gradients_close_popup (priv->callback);
else if (resource_type == PIKA_TYPE_BRUSH)
pika_brushes_close_popup (priv->callback);
else if (resource_type == PIKA_TYPE_PALETTE)
pika_palettes_close_popup (priv->callback);
else if (resource_type == PIKA_TYPE_PATTERN)
pika_patterns_close_popup (priv->callback);
else
g_warning ("%s: unhandled resource type", G_STRFUNC);
pika_plug_in_remove_temp_procedure (pika_get_plug_in (), priv->callback);
g_clear_pointer (&priv->callback, g_free);
}
G_OBJECT_CLASS (pika_resource_chooser_parent_class)->dispose (self);
}
static void
pika_resource_chooser_finalize (GObject *object)
{
PikaResourceChooser *self = PIKA_RESOURCE_CHOOSER (object);
PikaResourceChooserPrivate *priv = pika_resource_chooser_get_instance_private (self);
g_clear_pointer (&priv->title, g_free);
g_clear_pointer (&priv->label, g_free);
G_OBJECT_CLASS (pika_resource_chooser_parent_class)->finalize (object);
}
/**
* pika_resource_chooser_set_drag_target:
* @chooser: A [class@ResourceChooser]
* @drag_region_widget: An interior widget to be a droppable region
* and emit "drag-data-received" signal
* @drag_target: The drag target to accept
*
* Called by a subclass init to specialize the instance.
*
* Subclass knows its interior widget whose region is a drop zone.
* Subclass knows what things can be dropped (target.)
* Self (super) handles the drop.
*
* Since: 3.0
**/
void
pika_resource_chooser_set_drag_target (PikaResourceChooser *chooser,
GtkWidget *drag_region_widget,
const GtkTargetEntry *drag_target)
{
g_return_if_fail (PIKA_IS_RESOURCE_CHOOSER (chooser));
g_return_if_fail (drag_target != NULL);
g_return_if_fail (drag_region_widget != NULL);
gtk_drag_dest_set (drag_region_widget,
GTK_DEST_DEFAULT_HIGHLIGHT |
GTK_DEST_DEFAULT_MOTION |
GTK_DEST_DEFAULT_DROP,
drag_target, 1, /* Pass array of size 1 */
GDK_ACTION_COPY);
/* connect drag_region_widget's drag_received signal to chooser's callback. */
g_signal_connect_swapped (drag_region_widget, "drag-data-received",
G_CALLBACK (pika_resource_select_drag_data_received),
chooser);
}
/**
* pika_resource_chooser_set_clickable:
* @chooser: A [class@ResourceChooser]
* @widget: An interior widget that emits "clicked" signal
*
* Called by a subclass init to specialize the instance.
*
* Subclass knows its interior widget whose region when clicked
* should popup remote chooser.
* Self handles the click event.
*
* Since: 3.0
**/
void
pika_resource_chooser_set_clickable (PikaResourceChooser *chooser,
GtkWidget *widget)
{
g_return_if_fail (PIKA_IS_RESOURCE_CHOOSER (chooser));
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_WIDGET (widget));
/* Require the widget have a signal "clicked", usually a button. */
g_signal_connect_swapped (widget, "clicked",
G_CALLBACK (pika_resource_chooser_clicked),
chooser);
}
/**
* pika_resource_chooser_get_resource:
* @chooser: A #PikaResourceChooser
*
* Gets the currently selected resource.
*
* Returns: (transfer none): an internal copy of the resource which must not be freed.
*
* Since: 3.0
*/
PikaResource *
pika_resource_chooser_get_resource (PikaResourceChooser *chooser)
{
PikaResourceChooserPrivate *priv;
g_return_val_if_fail (PIKA_IS_RESOURCE_CHOOSER (chooser), NULL);
priv = pika_resource_chooser_get_instance_private (chooser);
return priv->resource;
}
/**
* pika_resource_chooser_set_resource:
* @chooser: A #PikaResourceChooser
* @resource: Resource to set.
*
* Sets the currently selected resource.
* This will select the resource in both the button and any chooser popup.
*
* Since: 3.0
*/
void
pika_resource_chooser_set_resource (PikaResourceChooser *chooser,
PikaResource *resource)
{
PikaResourceChooserPrivate *priv;
g_return_if_fail (PIKA_IS_RESOURCE_CHOOSER (chooser));
g_return_if_fail (resource != NULL);
priv = pika_resource_chooser_get_instance_private (chooser);
if (priv->callback)
{
/* A popup chooser dialog is already shown.
* Call its setter to change the selection there
* (since all views of the resource must be consistent.)
* That will call back, which will change our own view of the resource.
*/
pika_resource_chooser_set_remote_dialog (chooser, resource);
}
else
{
/* Call our own setter. */
pika_resource_chooser_callback (resource, FALSE, chooser);
}
}
/**
* pika_resource_chooser_get_label:
* @widget: A [class@ResourceChooser].
*
* Returns the label widget.
*
* Returns: (transfer none): the [class@Gtk.Widget] showing the label text.
* Since: 3.0
*/
GtkWidget *
pika_resource_chooser_get_label (PikaResourceChooser *widget)
{
PikaResourceChooserPrivate *priv;
g_return_val_if_fail (PIKA_IS_RESOURCE_CHOOSER (widget), NULL);
priv = pika_resource_chooser_get_instance_private (widget);
return priv->label_widget;
}
/* private functions */
static void
pika_resource_chooser_set_remote_dialog (PikaResourceChooser *self,
PikaResource *resource)
{
PikaResourceChooserPrivate *priv;
PikaResourceChooserClass *klass;
g_return_if_fail (PIKA_IS_RESOURCE_CHOOSER (self));
g_return_if_fail (resource != NULL);
priv = pika_resource_chooser_get_instance_private (self);
klass = PIKA_RESOURCE_CHOOSER_GET_CLASS (self);
g_return_if_fail (klass->resource_type != G_TYPE_INVALID);
g_return_if_fail (klass->resource_type == G_TYPE_FROM_INSTANCE (resource));
pika_resource_select_set (priv->callback, resource);
}
static void
pika_resource_chooser_set_property (GObject *object,
guint property_id,
const GValue *gvalue,
GParamSpec *pspec)
{
PikaResourceChooser *self = PIKA_RESOURCE_CHOOSER (object);
PikaResourceChooserPrivate *priv = pika_resource_chooser_get_instance_private (self);
switch (property_id)
{
case PROP_TITLE:
priv->title = g_value_dup_string (gvalue);
break;
case PROP_LABEL:
priv->label = g_value_dup_string (gvalue);
break;
case PROP_RESOURCE:
priv->resource = g_value_get_object (gvalue);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pika_resource_chooser_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
PikaResourceChooser *self = PIKA_RESOURCE_CHOOSER (object);
PikaResourceChooserPrivate *priv = pika_resource_chooser_get_instance_private (self);
switch (property_id)
{
case PROP_TITLE:
g_value_set_string (value, priv->title);
break;
case PROP_LABEL:
g_value_set_string (value, priv->label);
break;
case PROP_RESOURCE:
g_value_set_object (value, priv->resource);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
/* A callback from the remote resource select popup.
* When user chooses a resource.
* Via a temporary PDB procedure.
*
* Set self's model (priv->resource)
* Notify any parent widget subscribed on the "resource" property
* typically a prop widget.
* Update the view, since model changed.
*/
static void
pika_resource_chooser_callback (PikaResource *resource,
gboolean dialog_closing,
gpointer user_data)
{
PikaResourceChooser *self = PIKA_RESOURCE_CHOOSER (user_data);
PikaResourceChooserPrivate *priv = pika_resource_chooser_get_instance_private (self);
priv->resource = resource;
PIKA_RESOURCE_CHOOSER_GET_CLASS (self)->draw_interior (self);
if (dialog_closing)
g_clear_pointer (&priv->callback, g_free);
g_signal_emit (self, resource_button_signals[RESOURCE_SET], 0, resource, dialog_closing);
g_object_notify_by_pspec (G_OBJECT (self), resource_button_props[PROP_RESOURCE]);
}
static void
pika_resource_chooser_clicked (PikaResourceChooser *self)
{
PikaResourceChooserPrivate *priv = pika_resource_chooser_get_instance_private (self);
PikaResourceChooserClass *klass = PIKA_RESOURCE_CHOOSER_GET_CLASS (self);
if (priv->callback)
{
/* Popup already created. Calling setter raises the popup. */
pika_resource_chooser_set_remote_dialog (self, priv->resource);
}
else
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self));
GBytes *handle = NULL;
if (PIKA_IS_DIALOG (toplevel))
handle = pika_dialog_get_native_handle (PIKA_DIALOG (toplevel));
priv->callback = g_strdup (pika_resource_select_new (priv->title,
handle,
priv->resource,
klass->resource_type,
pika_resource_chooser_callback,
self,
NULL));
pika_resource_chooser_set_remote_dialog (self, priv->resource);
}
}
/* Drag methods. */
static void
pika_resource_select_drag_data_received (PikaResourceChooser *self,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *selection,
guint info,
guint time)
{
gint length = gtk_selection_data_get_length (selection);
gchar *str;
PikaResourceChooserClass *klass;
klass = PIKA_RESOURCE_CHOOSER_GET_CLASS (self);
/* Require class resource_type was initialized. */
g_assert (klass->resource_type != 0);
/* Drag data is a string that is the ID of the resource. */
if (gtk_selection_data_get_format (selection) != 8 || length < 1)
{
g_warning ("%s: received invalid resource data", G_STRFUNC);
return;
}
str = g_strndup ((const gchar *) gtk_selection_data_get_data (selection),
length);
if (g_utf8_validate (str, -1, NULL))
{
gint pid;
gpointer unused;
gint name_offset = 0;
if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
pid == pika_getpid () && name_offset > 0)
{
gchar *name = str + name_offset;
PikaResource *resource;
resource = pika_resource_get_by_name (klass->resource_type, name);
pika_resource_chooser_set_resource (self, resource);
}
}
g_free (str);
}

View File

@ -0,0 +1,65 @@
/* LIBPIKA - The PIKA Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* 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
* Lesser 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/>.
*/
#if !defined (__PIKA_UI_H_INSIDE__) && !defined (PIKA_COMPILATION)
#error "Only <libpika/pikaui.h> can be included directly."
#endif
#ifndef __PIKA_RESOURCE_CHOOSER_H__
#define __PIKA_RESOURCE_CHOOSER_H__
G_BEGIN_DECLS
#define PIKA_TYPE_RESOURCE_CHOOSER (pika_resource_chooser_get_type ())
G_DECLARE_DERIVABLE_TYPE (PikaResourceChooser, pika_resource_chooser, PIKA, RESOURCE_CHOOSER, GtkBox)
struct _PikaResourceChooserClass
{
GtkBoxClass parent_class;
/* Signals */
void (* resource_set) (PikaResourceChooser *chooser,
PikaResource *resource,
gboolean dialog_closing);
/* Abstract methods and class variables */
void (*draw_interior) (PikaResourceChooser *chooser);
GType resource_type;
/* Padding for future expansion */
gpointer padding[8];
};
PikaResource * pika_resource_chooser_get_resource (PikaResourceChooser *chooser);
void pika_resource_chooser_set_resource (PikaResourceChooser *chooser,
PikaResource *resource);
GtkWidget * pika_resource_chooser_get_label (PikaResourceChooser *widget);
/* API from below, used by subclasses e.g. PikaBrushChooser */
void pika_resource_chooser_set_drag_target (PikaResourceChooser *chooser,
GtkWidget *drag_region_widget,
const GtkTargetEntry *drag_target);
void pika_resource_chooser_set_clickable (PikaResourceChooser *chooser,
GtkWidget *widget);
G_END_DECLS
#endif /* __PIKA_RESOURCE_CHOOSER_H__ */