1
/* LIBGIMP - The GIMP Library
2
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
4
* gimpbrushselectbutton.c
5
* Copyright (C) 1998 Andy Thomas
7
* This library is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public
9
* License as published by the Free Software Foundation; either
10
* version 2 of the License, or (at your option) any later version.
12
* This library is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
* Library General Public License for more details.
17
* You should have received a copy of the GNU Lesser General Public
18
* License along with this library; if not, write to the
19
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20
* Boston, MA 02111-1307, USA.
27
#include "libgimpwidgets/gimpwidgets.h"
31
#include "gimpuitypes.h"
32
#include "gimpbrushselectbutton.h"
33
#include "gimpuimarshal.h"
35
#include "libgimp-intl.h"
41
#define GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GIMP_TYPE_BRUSH_SELECT_BUTTON, GimpBrushSelectButtonPrivate))
43
typedef struct _GimpBrushSelectButtonPrivate GimpBrushSelectButtonPrivate;
45
struct _GimpBrushSelectButtonPrivate
49
gchar *brush_name; /* Local copy */
52
GimpLayerModeEffects paint_mode;
55
guchar *mask_data; /* local copy */
79
/* local function prototypes */
81
static void gimp_brush_select_button_finalize (GObject *object);
83
static void gimp_brush_select_button_set_property (GObject *object,
87
static void gimp_brush_select_button_get_property (GObject *object,
92
static void gimp_brush_select_button_clicked (GimpBrushSelectButton *button);
94
static void gimp_brush_select_button_callback (const gchar *brush_name,
97
GimpLayerModeEffects paint_mode,
100
const guchar *mask_data,
101
gboolean dialog_closing,
104
static void gimp_brush_select_preview_resize (GimpBrushSelectButton *button);
105
static gboolean gimp_brush_select_preview_events (GtkWidget *widget,
107
GimpBrushSelectButton *button);
108
static void gimp_brush_select_preview_draw (GimpPreviewArea *area,
113
const guchar *mask_data,
115
static void gimp_brush_select_preview_update (GtkWidget *preview,
118
const guchar *mask_data);
120
static void gimp_brush_select_button_open_popup (GimpBrushSelectButton *button,
123
static void gimp_brush_select_button_close_popup (GimpBrushSelectButton *button);
125
static void gimp_brush_select_drag_data_received (GimpBrushSelectButton *button,
126
GdkDragContext *context,
129
GtkSelectionData *selection,
133
static GtkWidget * gimp_brush_select_button_create_inside (GimpBrushSelectButton *button);
136
static const GtkTargetEntry target = { "application/x-gimp-brush-name", 0 };
138
static guint brush_button_signals[LAST_SIGNAL] = { 0 };
141
G_DEFINE_TYPE (GimpBrushSelectButton, gimp_brush_select_button,
142
GIMP_TYPE_SELECT_BUTTON)
146
gimp_brush_select_button_class_init (GimpBrushSelectButtonClass *klass)
148
GObjectClass *object_class = G_OBJECT_CLASS (klass);
149
GimpSelectButtonClass *select_button_class = GIMP_SELECT_BUTTON_CLASS (klass);
151
object_class->finalize = gimp_brush_select_button_finalize;
152
object_class->set_property = gimp_brush_select_button_set_property;
153
object_class->get_property = gimp_brush_select_button_get_property;
155
select_button_class->select_destroy = gimp_brush_select_destroy;
157
klass->brush_set = NULL;
160
* GimpBrushSelectButton:title:
162
* The title to be used for the brush selection popup dialog.
166
g_object_class_install_property (object_class, PROP_TITLE,
167
g_param_spec_string ("title",
169
"The title to be used for the brush selection popup dialog",
170
_("Brush Selection"),
171
GIMP_PARAM_READWRITE |
172
G_PARAM_CONSTRUCT_ONLY));
175
* GimpBrushSelectButton:brush-name:
177
* The name of the currently selected brush.
181
g_object_class_install_property (object_class, PROP_BRUSH_NAME,
182
g_param_spec_string ("brush-name",
184
"The name of the currently selected brush",
186
GIMP_PARAM_READWRITE));
189
* GimpBrushSelectButton:opacity:
191
* The opacity of the currently selected brush.
195
g_object_class_install_property (object_class, PROP_BRUSH_OPACITY,
196
g_param_spec_double ("brush-opacity",
198
"The opacity of the currently selected brush",
200
GIMP_PARAM_READWRITE));
203
* GimpBrushSelectButton:spacing:
205
* The spacing of the currently selected brush.
209
g_object_class_install_property (object_class, PROP_BRUSH_SPACING,
210
g_param_spec_int ("brush-spacing",
212
"The spacing of the currently selected brush",
214
GIMP_PARAM_READWRITE));
217
* GimpBrushSelectButton:paint-mode:
219
* The name of the currently selected brush.
223
g_object_class_install_property (object_class, PROP_BRUSH_PAINT_MODE,
224
g_param_spec_int ("brush-paint-mode",
226
"The paint mode of the currently selected brush",
227
-1, GIMP_COLOR_ERASE_MODE,
229
GIMP_PARAM_READWRITE));
232
* GimpBrushSelectButton::brush-set:
233
* @widget: the object which received the signal.
234
* @brush_name: the name of the currently selected brush.
235
* @opacity: opacity of the brush
236
* @spacing: spacing of the brush
237
* @paint_mode: paint mode of the brush
238
* @width: width of the brush
239
* @height: height of the brush
240
* @mask_data: brush mask data
241
* @dialog_closing: whether the dialog was closed or not.
243
* The ::brush-set signal is emitted when the user selects a brush.
247
brush_button_signals[BRUSH_SET] =
248
g_signal_new ("brush-set",
249
G_TYPE_FROM_CLASS (klass),
251
G_STRUCT_OFFSET (GimpBrushSelectButtonClass, brush_set),
253
_gimpui_marshal_VOID__STRING_DOUBLE_INT_INT_INT_INT_POINTER_BOOLEAN,
264
g_type_class_add_private (object_class,
265
sizeof (GimpBrushSelectButtonPrivate));
269
gimp_brush_select_button_init (GimpBrushSelectButton *button)
271
GimpBrushSelectButtonPrivate *priv;
275
gint color_data_size;
278
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
280
priv->brush_name = gimp_context_get_brush ();
281
gimp_brush_get_pixels (priv->brush_name,
294
priv->inside = gimp_brush_select_button_create_inside (button);
295
gtk_container_add (GTK_CONTAINER (button), priv->inside);
301
* gimp_brush_select_button_new:
302
* @title: Title of the dialog to use or %NULL means to use the default
304
* @brush_name: Initial brush name or %NULL to use current selection.
305
* @opacity: Initial opacity. -1 means to use current opacity.
306
* @spacing: Initial spacing. -1 means to use current spacing.
307
* @paint_mode: Initial paint mode. -1 means to use current paint mode.
309
* Creates a new #GtkWidget that completely controls the selection of
310
* a #GimpBrush. This widget is suitable for placement in a table in
313
* Returns: A #GtkWidget that you can use in your UI.
318
gimp_brush_select_button_new (const gchar *title,
319
const gchar *brush_name,
322
GimpLayerModeEffects paint_mode)
327
button = g_object_new (GIMP_TYPE_BRUSH_SELECT_BUTTON,
329
"brush-name", brush_name,
330
"brush-opacity", opacity,
331
"brush-spacing", spacing,
332
"brush-paint-mode", paint_mode,
335
button = g_object_new (GIMP_TYPE_BRUSH_SELECT_BUTTON,
336
"brush-name", brush_name,
337
"brush-opacity", opacity,
338
"brush-spacing", spacing,
339
"brush-paint-mode", paint_mode,
346
* gimp_brush_select_button_get_brush:
347
* @button: A #GimpBrushSelectButton
348
* @opacity: Opacity of the selected brush.
349
* @spacing: Spacing of the selected brush.
350
* @paint_mode: Paint mode of the selected brush.
352
* Retrieves the properties of currently selected brush.
354
* Returns: an internal copy of the brush name which must not be freed.
359
gimp_brush_select_button_get_brush (GimpBrushSelectButton *button,
362
GimpLayerModeEffects *paint_mode)
364
GimpBrushSelectButtonPrivate *priv;
366
g_return_val_if_fail (GIMP_IS_BRUSH_SELECT_BUTTON (button), NULL);
368
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
371
*opacity = priv->opacity;
374
*spacing = priv->spacing;
377
*paint_mode = priv->paint_mode;
379
return priv->brush_name;
383
* gimp_brush_select_button_set_brush:
384
* @button: A #GimpBrushSelectButton
385
* @brush_name: Brush name to set; %NULL means no change.
386
* @opacity: Opacity to set. -1.0 means no change.
387
* @spacing: Spacing to set. -1 means no change.
388
* @paint_mode: Paint mode to set. -1 means no change.
390
* Sets the current brush and other values for the brush select
396
gimp_brush_select_button_set_brush (GimpBrushSelectButton *button,
397
const gchar *brush_name,
400
GimpLayerModeEffects paint_mode)
402
GimpSelectButton *select_button;
404
g_return_if_fail (GIMP_IS_BRUSH_SELECT_BUTTON (button));
406
select_button = GIMP_SELECT_BUTTON (button);
408
if (select_button->temp_callback)
410
gimp_brushes_set_popup (select_button->temp_callback, brush_name,
411
opacity, spacing, paint_mode);
422
gint color_data_size;
425
if (brush_name && *brush_name)
426
name = g_strdup (brush_name);
428
name = gimp_context_get_brush ();
430
if (gimp_brush_get_pixels (name,
444
opacity = gimp_context_get_opacity ();
447
gimp_brush_get_spacing (name, &spacing);
449
if (paint_mode == -1)
450
paint_mode = gimp_context_get_paint_mode ();
452
gimp_brush_select_button_callback (name,
453
opacity, spacing, paint_mode,
454
width, height, mask_data,
465
/* private functions */
468
gimp_brush_select_button_finalize (GObject *object)
470
GimpBrushSelectButtonPrivate *priv;
472
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (object);
474
g_free (priv->brush_name);
475
priv->brush_name = NULL;
477
g_free (priv->mask_data);
478
priv->mask_data = NULL;
480
g_free (priv->title);
483
G_OBJECT_CLASS (gimp_brush_select_button_parent_class)->finalize (object);
487
gimp_brush_select_button_set_property (GObject *object,
492
GimpBrushSelectButton *button = GIMP_BRUSH_SELECT_BUTTON (object);
493
GimpBrushSelectButtonPrivate *priv;
498
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
503
priv->title = g_value_dup_string (value);
505
case PROP_BRUSH_NAME:
506
gimp_brush_select_button_set_brush (button,
507
g_value_get_string (value),
510
case PROP_BRUSH_OPACITY:
511
opacity = g_value_get_double (value);
513
priv->opacity = opacity;
515
case PROP_BRUSH_SPACING:
516
spacing = g_value_get_int (value);
518
priv->spacing = spacing;
520
case PROP_BRUSH_PAINT_MODE:
521
paint_mode = g_value_get_int (value);
522
if (paint_mode != -1)
523
priv->paint_mode = paint_mode;
526
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
532
gimp_brush_select_button_get_property (GObject *object,
537
GimpBrushSelectButton *button = GIMP_BRUSH_SELECT_BUTTON (object);
538
GimpBrushSelectButtonPrivate *priv;
540
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
545
g_value_set_string (value, priv->title);
547
case PROP_BRUSH_NAME:
548
g_value_set_string (value, priv->brush_name);
550
case PROP_BRUSH_OPACITY:
551
g_value_set_double (value, priv->opacity);
553
case PROP_BRUSH_SPACING:
554
g_value_set_int (value, priv->spacing);
556
case PROP_BRUSH_PAINT_MODE:
557
g_value_set_int (value, priv->paint_mode);
560
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
566
gimp_brush_select_button_callback (const gchar *name,
569
GimpLayerModeEffects paint_mode,
572
const guchar *mask_data,
573
gboolean dialog_closing,
576
GimpBrushSelectButton *button;
577
GimpBrushSelectButtonPrivate *priv;
578
GimpSelectButton *select_button;
580
button = GIMP_BRUSH_SELECT_BUTTON (data);
582
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
583
select_button = GIMP_SELECT_BUTTON (button);
585
g_free (priv->brush_name);
586
g_free (priv->mask_data);
588
priv->brush_name = g_strdup (name);
590
priv->height = height;
591
priv->mask_data = g_memdup (mask_data, width * height);
592
priv->opacity = opacity;
593
priv->spacing = spacing;
594
priv->paint_mode = paint_mode;
596
gimp_brush_select_preview_update (priv->preview,
597
width, height, mask_data);
600
select_button->temp_callback = NULL;
602
g_signal_emit (button, brush_button_signals[BRUSH_SET], 0,
603
name, opacity, spacing, paint_mode, width, height, mask_data,
605
g_object_notify (G_OBJECT (button), "brush-name");
609
gimp_brush_select_button_clicked (GimpBrushSelectButton *button)
611
GimpBrushSelectButtonPrivate *priv;
612
GimpSelectButton *select_button;
614
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
615
select_button = GIMP_SELECT_BUTTON (button);
617
if (select_button->temp_callback)
619
/* calling gimp_brushes_set_popup() raises the dialog */
620
gimp_brushes_set_popup (select_button->temp_callback,
628
select_button->temp_callback =
629
gimp_brush_select_new (priv->title, priv->brush_name,
630
priv->opacity, priv->spacing, priv->paint_mode,
631
gimp_brush_select_button_callback,
637
gimp_brush_select_preview_resize (GimpBrushSelectButton *button)
639
GimpBrushSelectButtonPrivate *priv;
641
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
643
if (priv->width > 0 && priv->height > 0)
644
gimp_brush_select_preview_update (priv->preview,
651
gimp_brush_select_preview_events (GtkWidget *widget,
653
GimpBrushSelectButton *button)
655
GimpBrushSelectButtonPrivate *priv;
656
GdkEventButton *bevent;
658
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
664
case GDK_BUTTON_PRESS:
665
bevent = (GdkEventButton *) event;
667
if (bevent->button == 1)
669
gtk_grab_add (widget);
670
gimp_brush_select_button_open_popup (button,
671
bevent->x, bevent->y);
675
case GDK_BUTTON_RELEASE:
676
bevent = (GdkEventButton *) event;
678
if (bevent->button == 1)
680
gtk_grab_remove (widget);
681
gimp_brush_select_button_close_popup (button);
694
gimp_brush_select_preview_draw (GimpPreviewArea *area,
699
const guchar *mask_data,
707
buf = g_new (guchar, width * height);
712
for (j = 0; j < height; j++)
714
const guchar *s = src;
716
for (i = 0; i < width; i++, s++, dest++)
722
gimp_preview_area_draw (area,
732
gimp_brush_select_preview_update (GtkWidget *preview,
735
const guchar *mask_data)
737
GimpPreviewArea *area = GIMP_PREVIEW_AREA (preview);
741
width = MIN (brush_width, preview->allocation.width);
742
height = MIN (brush_height, preview->allocation.height);
744
x = ((preview->allocation.width - width) / 2);
745
y = ((preview->allocation.height - height) / 2);
748
gimp_preview_area_fill (area,
750
preview->allocation.width,
751
preview->allocation.height,
754
gimp_brush_select_preview_draw (area,
756
mask_data, brush_width);
760
gimp_brush_select_button_open_popup (GimpBrushSelectButton *button,
764
GimpBrushSelectButtonPrivate *priv;
773
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
776
gimp_brush_select_button_close_popup (button);
778
if (priv->width <= CELL_SIZE && priv->height <= CELL_SIZE)
781
priv->popup = gtk_window_new (GTK_WINDOW_POPUP);
783
frame = gtk_frame_new (NULL);
784
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
785
gtk_container_add (GTK_CONTAINER (priv->popup), frame);
786
gtk_widget_show (frame);
788
preview = gimp_preview_area_new ();
789
gtk_widget_set_size_request (preview, priv->width, priv->height);
790
gtk_container_add (GTK_CONTAINER (frame), preview);
791
gtk_widget_show (preview);
793
/* decide where to put the popup */
794
gdk_window_get_origin (priv->preview->window, &x_org, &y_org);
796
screen = gtk_widget_get_screen (priv->popup);
798
scr_w = gdk_screen_get_width (screen);
799
scr_h = gdk_screen_get_height (screen);
801
x = x_org + x - (priv->width / 2);
802
y = y_org + y - (priv->height / 2);
805
x = (x + priv->width > scr_w) ? scr_w - priv->width : x;
806
y = (y + priv->height > scr_h) ? scr_h - priv->height : y;
808
gtk_window_move (GTK_WINDOW (priv->popup), x, y);
810
gtk_widget_show (priv->popup);
813
gimp_brush_select_preview_draw (GIMP_PREVIEW_AREA (preview),
814
0, 0, priv->width, priv->height,
815
priv->mask_data, priv->width);
819
gimp_brush_select_button_close_popup (GimpBrushSelectButton *button)
821
GimpBrushSelectButtonPrivate *priv;
823
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (button);
827
gtk_widget_destroy (priv->popup);
833
gimp_brush_select_drag_data_received (GimpBrushSelectButton *button,
834
GdkDragContext *context,
837
GtkSelectionData *selection,
843
if ((selection->format != 8) || (selection->length < 1))
845
g_warning ("Received invalid brush data!");
849
str = g_strndup ((const gchar *) selection->data, selection->length);
851
if (g_utf8_validate (str, -1, NULL))
855
gint name_offset = 0;
857
if (sscanf (str, "%i:%p:%n", &pid, &unused, &name_offset) >= 2 &&
858
pid == gimp_getpid () && name_offset > 0)
860
gchar *name = str + name_offset;
862
gimp_brush_select_button_set_brush (button, name, -1.0, -1, -1);
870
gimp_brush_select_button_create_inside (GimpBrushSelectButton *brush_button)
875
GimpBrushSelectButtonPrivate *priv;
877
priv = GIMP_BRUSH_SELECT_BUTTON_GET_PRIVATE (brush_button);
879
gtk_widget_push_composite_child ();
881
hbox = gtk_hbox_new (FALSE, 6);
883
frame = gtk_frame_new (NULL);
884
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
885
gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
887
priv->preview = gimp_preview_area_new ();
888
gtk_widget_add_events (priv->preview,
889
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
890
gtk_widget_set_size_request (priv->preview, CELL_SIZE, CELL_SIZE);
891
gtk_container_add (GTK_CONTAINER (frame), priv->preview);
893
g_signal_connect_swapped (priv->preview, "size-allocate",
894
G_CALLBACK (gimp_brush_select_preview_resize),
896
g_signal_connect (priv->preview, "event",
897
G_CALLBACK (gimp_brush_select_preview_events),
900
gtk_drag_dest_set (GTK_WIDGET (priv->preview),
901
GTK_DEST_DEFAULT_HIGHLIGHT |
902
GTK_DEST_DEFAULT_MOTION |
903
GTK_DEST_DEFAULT_DROP,
907
g_signal_connect (priv->preview, "drag-data-received",
908
G_CALLBACK (gimp_brush_select_drag_data_received),
911
button = gtk_button_new_with_mnemonic (_("_Browse..."));
912
gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);
914
g_signal_connect_swapped (button, "clicked",
915
G_CALLBACK (gimp_brush_select_button_clicked),
918
gtk_widget_show_all (hbox);
920
gtk_widget_pop_composite_child ();