1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * gimpbrusheditor.c
5  * Copyright 1998 Jay Cox <jaycox@earthlink.net>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program 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
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 
28 #include "libgimpmath/gimpmath.h"
29 #include "libgimpwidgets/gimpwidgets.h"
30 
31 #include "widgets-types.h"
32 
33 #include "core/gimp.h"
34 #include "core/gimpbrushgenerated.h"
35 #include "core/gimpcontext.h"
36 
37 #include "gimpbrusheditor.h"
38 #include "gimpdocked.h"
39 #include "gimpspinscale.h"
40 #include "gimpview.h"
41 #include "gimpviewrenderer.h"
42 
43 #include "gimp-intl.h"
44 
45 
46 #define BRUSH_VIEW_SIZE 96
47 
48 
49 /*  local function prototypes  */
50 
51 static void   gimp_brush_editor_docked_iface_init (GimpDockedInterface *face);
52 
53 static void   gimp_brush_editor_constructed    (GObject            *object);
54 
55 static void   gimp_brush_editor_set_data       (GimpDataEditor     *editor,
56                                                 GimpData           *data);
57 
58 static void   gimp_brush_editor_set_context    (GimpDocked         *docked,
59                                                 GimpContext        *context);
60 
61 static void   gimp_brush_editor_update_brush   (GtkAdjustment      *adjustment,
62                                                 GimpBrushEditor    *editor);
63 static void   gimp_brush_editor_update_shape   (GtkWidget          *widget,
64                                                 GimpBrushEditor    *editor);
65 static void   gimp_brush_editor_notify_brush   (GimpBrushGenerated *brush,
66                                                 GParamSpec         *pspec,
67                                                 GimpBrushEditor    *editor);
68 
69 
70 G_DEFINE_TYPE_WITH_CODE (GimpBrushEditor, gimp_brush_editor,
71                          GIMP_TYPE_DATA_EDITOR,
72                          G_IMPLEMENT_INTERFACE (GIMP_TYPE_DOCKED,
73                                                 gimp_brush_editor_docked_iface_init))
74 
75 #define parent_class gimp_brush_editor_parent_class
76 
77 static GimpDockedInterface *parent_docked_iface = NULL;
78 
79 
80 static void
gimp_brush_editor_class_init(GimpBrushEditorClass * klass)81 gimp_brush_editor_class_init (GimpBrushEditorClass *klass)
82 {
83   GObjectClass        *object_class = G_OBJECT_CLASS (klass);
84   GimpDataEditorClass *editor_class = GIMP_DATA_EDITOR_CLASS (klass);
85 
86   object_class->constructed = gimp_brush_editor_constructed;
87 
88   editor_class->set_data    = gimp_brush_editor_set_data;
89   editor_class->title       = _("Brush Editor");
90 }
91 
92 static void
gimp_brush_editor_docked_iface_init(GimpDockedInterface * iface)93 gimp_brush_editor_docked_iface_init (GimpDockedInterface *iface)
94 {
95   parent_docked_iface = g_type_interface_peek_parent (iface);
96 
97   if (! parent_docked_iface)
98     parent_docked_iface = g_type_default_interface_peek (GIMP_TYPE_DOCKED);
99 
100   iface->set_context = gimp_brush_editor_set_context;
101 }
102 
103 static void
gimp_brush_editor_init(GimpBrushEditor * editor)104 gimp_brush_editor_init (GimpBrushEditor *editor)
105 {
106   GimpDataEditor *data_editor = GIMP_DATA_EDITOR (editor);
107   GtkWidget      *frame;
108   GtkWidget      *hbox;
109   GtkWidget      *label;
110   GtkWidget      *box;
111   GtkWidget      *scale;
112 
113   frame = gtk_frame_new (NULL);
114   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
115   gtk_box_pack_start (GTK_BOX (editor), frame, TRUE, TRUE, 0);
116   gtk_widget_show (frame);
117 
118   data_editor->view = gimp_view_new_full_by_types (NULL,
119                                                    GIMP_TYPE_VIEW,
120                                                    GIMP_TYPE_BRUSH,
121                                                    BRUSH_VIEW_SIZE,
122                                                    BRUSH_VIEW_SIZE, 0,
123                                                    FALSE, FALSE, TRUE);
124   gtk_widget_set_size_request (data_editor->view, -1, BRUSH_VIEW_SIZE);
125   gimp_view_set_expand (GIMP_VIEW (data_editor->view), TRUE);
126   gtk_container_add (GTK_CONTAINER (frame), data_editor->view);
127   gtk_widget_show (data_editor->view);
128 
129   editor->shape_group = NULL;
130 
131   editor->options_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
132   gtk_box_pack_start (GTK_BOX (editor), editor->options_box, FALSE, FALSE, 0);
133   gtk_widget_show (editor->options_box);
134 
135   /* Stock Box for the brush shape */
136   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
137   gtk_box_pack_start (GTK_BOX (editor->options_box), hbox, FALSE, FALSE, 0);
138   gtk_widget_show (hbox);
139 
140   label = gtk_label_new (_("Shape:"));
141   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
142   gtk_widget_show (label);
143 
144   box = gimp_enum_icon_box_new (GIMP_TYPE_BRUSH_GENERATED_SHAPE,
145                                 "gimp-shape",
146                                 GTK_ICON_SIZE_MENU,
147                                 G_CALLBACK (gimp_brush_editor_update_shape),
148                                 editor,
149                                 &editor->shape_group);
150   gtk_box_pack_start (GTK_BOX (hbox), box, FALSE, FALSE, 0);
151   gtk_widget_show (box);
152 
153   /*  brush radius scale  */
154   editor->radius_data =
155     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.1, 1000.0, 1.0, 10.0, 0.0));
156   scale = gimp_spin_scale_new (editor->radius_data, _("Radius"), 1);
157   gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
158   gtk_widget_show (scale);
159 
160   g_signal_connect (editor->radius_data, "value-changed",
161                     G_CALLBACK (gimp_brush_editor_update_brush),
162                     editor);
163 
164   /*  number of spikes  */
165   editor->spikes_data =
166     GTK_ADJUSTMENT (gtk_adjustment_new (2.0, 2.0, 20.0, 1.0, 1.0, 0.0));
167   scale = gimp_spin_scale_new (editor->spikes_data, _("Spikes"), 0);
168   gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
169   gtk_widget_show (scale);
170 
171   g_signal_connect (editor->spikes_data, "value-changed",
172                     G_CALLBACK (gimp_brush_editor_update_brush),
173                     editor);
174 
175   /*  brush hardness scale  */
176   editor->hardness_data =
177     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 1.0, 0.01, 0.1, 0.0));
178   scale = gimp_spin_scale_new (editor->hardness_data, _("Hardness"), 2);
179   gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
180   gtk_widget_show (scale);
181 
182   g_signal_connect (editor->hardness_data, "value-changed",
183                     G_CALLBACK (gimp_brush_editor_update_brush),
184                     editor);
185 
186   /*  brush aspect ratio scale  */
187   editor->aspect_ratio_data =
188     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 1.0, 20.0, 0.1, 1.0, 0.0));
189   scale = gimp_spin_scale_new (editor->aspect_ratio_data, _("Aspect ratio"), 1);
190   gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
191   gtk_widget_show (scale);
192 
193   g_signal_connect (editor->aspect_ratio_data,"value-changed",
194                     G_CALLBACK (gimp_brush_editor_update_brush),
195                     editor);
196 
197   /*  brush angle scale  */
198   editor->angle_data =
199     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 180.0, 0.1, 1.0, 0.0));
200   scale = gimp_spin_scale_new (editor->angle_data, _("Angle"), 1);
201   gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
202   gtk_widget_show (scale);
203 
204   g_signal_connect (editor->angle_data, "value-changed",
205                     G_CALLBACK (gimp_brush_editor_update_brush),
206                     editor);
207 
208   /*  brush spacing  */
209   editor->spacing_data =
210     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 1.0, 5000.0, 1.0, 10.0, 0.0));
211   scale = gimp_spin_scale_new (editor->spacing_data, _("Spacing"), 1);
212   gimp_spin_scale_set_scale_limits (GIMP_SPIN_SCALE (scale), 1.0, 200.0);
213   gtk_box_pack_start (GTK_BOX (editor->options_box), scale, FALSE, FALSE, 0);
214   gtk_widget_show (scale);
215 
216   gimp_help_set_help_data (scale, _("Percentage of width of brush"), NULL);
217 
218   g_signal_connect (editor->spacing_data, "value-changed",
219                     G_CALLBACK (gimp_brush_editor_update_brush),
220                     editor);
221 }
222 
223 static void
gimp_brush_editor_constructed(GObject * object)224 gimp_brush_editor_constructed (GObject *object)
225 {
226   G_OBJECT_CLASS (parent_class)->constructed (object);
227 
228   gimp_docked_set_show_button_bar (GIMP_DOCKED (object), FALSE);
229 }
230 
231 static void
gimp_brush_editor_set_data(GimpDataEditor * editor,GimpData * data)232 gimp_brush_editor_set_data (GimpDataEditor *editor,
233                             GimpData       *data)
234 {
235   GimpBrushEditor         *brush_editor = GIMP_BRUSH_EDITOR (editor);
236   GimpBrushGeneratedShape  shape        = GIMP_BRUSH_GENERATED_CIRCLE;
237   gdouble                  radius       = 0.0;
238   gint                     spikes       = 2;
239   gdouble                  hardness     = 0.0;
240   gdouble                  ratio        = 0.0;
241   gdouble                  angle        = 0.0;
242   gdouble                  spacing      = 0.0;
243 
244   if (editor->data)
245     g_signal_handlers_disconnect_by_func (editor->data,
246                                           gimp_brush_editor_notify_brush,
247                                           editor);
248 
249   GIMP_DATA_EDITOR_CLASS (parent_class)->set_data (editor, data);
250 
251   if (editor->data)
252     g_signal_connect (editor->data, "notify",
253                       G_CALLBACK (gimp_brush_editor_notify_brush),
254                       editor);
255 
256   gimp_view_set_viewable (GIMP_VIEW (editor->view), GIMP_VIEWABLE (data));
257 
258   if (editor->data)
259     {
260       spacing = gimp_brush_get_spacing (GIMP_BRUSH (editor->data));
261 
262       if (GIMP_IS_BRUSH_GENERATED (editor->data))
263         {
264           GimpBrushGenerated *brush = GIMP_BRUSH_GENERATED (editor->data);
265 
266           shape    = gimp_brush_generated_get_shape        (brush);
267           radius   = gimp_brush_generated_get_radius       (brush);
268           spikes   = gimp_brush_generated_get_spikes       (brush);
269           hardness = gimp_brush_generated_get_hardness     (brush);
270           ratio    = gimp_brush_generated_get_aspect_ratio (brush);
271           angle    = gimp_brush_generated_get_angle        (brush);
272         }
273     }
274 
275   gtk_widget_set_sensitive (brush_editor->options_box,
276                             editor->data_editable);
277 
278   gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (brush_editor->shape_group),
279                                    shape);
280 
281   gtk_adjustment_set_value (brush_editor->radius_data,       radius);
282   gtk_adjustment_set_value (brush_editor->spikes_data,       spikes);
283   gtk_adjustment_set_value (brush_editor->hardness_data,     hardness);
284   gtk_adjustment_set_value (brush_editor->aspect_ratio_data, ratio);
285   gtk_adjustment_set_value (brush_editor->angle_data,        angle);
286   gtk_adjustment_set_value (brush_editor->spacing_data,      spacing);
287 }
288 
289 static void
gimp_brush_editor_set_context(GimpDocked * docked,GimpContext * context)290 gimp_brush_editor_set_context (GimpDocked  *docked,
291                                GimpContext *context)
292 {
293   GimpDataEditor *data_editor = GIMP_DATA_EDITOR (docked);
294 
295   parent_docked_iface->set_context (docked, context);
296 
297   gimp_view_renderer_set_context (GIMP_VIEW (data_editor->view)->renderer,
298                                   context);
299 }
300 
301 
302 /*  public functions  */
303 
304 GtkWidget *
gimp_brush_editor_new(GimpContext * context,GimpMenuFactory * menu_factory)305 gimp_brush_editor_new (GimpContext     *context,
306                        GimpMenuFactory *menu_factory)
307 {
308   g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL);
309 
310   return g_object_new (GIMP_TYPE_BRUSH_EDITOR,
311                        "menu-factory",    menu_factory,
312                        "menu-identifier", "<BrushEditor>",
313                        "ui-path",         "/brush-editor-popup",
314                        "data-factory",    context->gimp->brush_factory,
315                        "context",         context,
316                        "data",            gimp_context_get_brush (context),
317                        NULL);
318 }
319 
320 
321 /*  private functions  */
322 
323 static void
gimp_brush_editor_update_brush(GtkAdjustment * adjustment,GimpBrushEditor * editor)324 gimp_brush_editor_update_brush (GtkAdjustment   *adjustment,
325                                 GimpBrushEditor *editor)
326 {
327   GimpBrushGenerated *brush;
328   gdouble             value;
329 
330   if (! GIMP_IS_BRUSH_GENERATED (GIMP_DATA_EDITOR (editor)->data))
331     return;
332 
333   brush = GIMP_BRUSH_GENERATED (GIMP_DATA_EDITOR (editor)->data);
334 
335   g_signal_handlers_block_by_func (brush,
336                                    gimp_brush_editor_notify_brush,
337                                    editor);
338 
339   value = gtk_adjustment_get_value (adjustment);
340 
341   if (adjustment == editor->radius_data)
342     {
343       if (value != gimp_brush_generated_get_radius (brush))
344         gimp_brush_generated_set_radius (brush, value);
345     }
346   else if (adjustment == editor->spikes_data)
347     {
348       if (ROUND (value) != gimp_brush_generated_get_spikes (brush))
349         gimp_brush_generated_set_spikes (brush, ROUND (value));
350     }
351   else if (adjustment == editor->hardness_data)
352     {
353       if (value != gimp_brush_generated_get_hardness (brush))
354         gimp_brush_generated_set_hardness (brush, value);
355     }
356   else if (adjustment == editor->aspect_ratio_data)
357     {
358       if (value != gimp_brush_generated_get_aspect_ratio (brush))
359         gimp_brush_generated_set_aspect_ratio (brush, value);
360     }
361   else if (adjustment == editor->angle_data)
362     {
363       if (value != gimp_brush_generated_get_angle (brush))
364         gimp_brush_generated_set_angle (brush, value);
365     }
366   else if (adjustment == editor->spacing_data)
367     {
368       if (value != gimp_brush_get_spacing (GIMP_BRUSH (brush)))
369         gimp_brush_set_spacing (GIMP_BRUSH (brush), value);
370     }
371 
372   g_signal_handlers_unblock_by_func (brush,
373                                      gimp_brush_editor_notify_brush,
374                                      editor);
375 }
376 
377 static void
gimp_brush_editor_update_shape(GtkWidget * widget,GimpBrushEditor * editor)378 gimp_brush_editor_update_shape (GtkWidget       *widget,
379                                 GimpBrushEditor *editor)
380 {
381   GimpBrushGenerated *brush;
382 
383   if (! GIMP_IS_BRUSH_GENERATED (GIMP_DATA_EDITOR (editor)->data))
384     return;
385 
386   brush = GIMP_BRUSH_GENERATED (GIMP_DATA_EDITOR (editor)->data);
387 
388   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
389     {
390       GimpBrushGeneratedShape shape;
391 
392       shape = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
393                                                   "gimp-item-data"));
394 
395       if (gimp_brush_generated_get_shape (brush) != shape)
396         gimp_brush_generated_set_shape (brush, shape);
397     }
398 }
399 
400 static void
gimp_brush_editor_notify_brush(GimpBrushGenerated * brush,GParamSpec * pspec,GimpBrushEditor * editor)401 gimp_brush_editor_notify_brush (GimpBrushGenerated *brush,
402                                 GParamSpec         *pspec,
403                                 GimpBrushEditor    *editor)
404 {
405   GtkAdjustment *adj   = NULL;
406   gdouble        value = 0.0;
407 
408   if (! strcmp (pspec->name, "shape"))
409     {
410       g_signal_handlers_block_by_func (editor->shape_group,
411                                        gimp_brush_editor_update_shape,
412                                        editor);
413 
414       gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (editor->shape_group),
415                                        brush->shape);
416 
417       g_signal_handlers_unblock_by_func (editor->shape_group,
418                                          gimp_brush_editor_update_shape,
419                                          editor);
420     }
421   else if (! strcmp (pspec->name, "radius"))
422     {
423       adj   = editor->radius_data;
424       value = gimp_brush_generated_get_radius (brush);
425     }
426   else if (! strcmp (pspec->name, "spikes"))
427     {
428       adj   = editor->spikes_data;
429       value = gimp_brush_generated_get_spikes (brush);
430     }
431   else if (! strcmp (pspec->name, "hardness"))
432     {
433       adj   = editor->hardness_data;
434       value = gimp_brush_generated_get_hardness (brush);
435     }
436   else if (! strcmp (pspec->name, "angle"))
437     {
438       adj   = editor->angle_data;
439       value = gimp_brush_generated_get_angle (brush);
440     }
441   else if (! strcmp (pspec->name, "aspect-ratio"))
442     {
443       adj   = editor->aspect_ratio_data;
444       value = gimp_brush_generated_get_aspect_ratio (brush);
445     }
446   else if (! strcmp (pspec->name, "spacing"))
447     {
448       adj   = editor->spacing_data;
449       value = gimp_brush_get_spacing (GIMP_BRUSH (brush));
450     }
451 
452   if (adj)
453     {
454       g_signal_handlers_block_by_func (adj,
455                                        gimp_brush_editor_update_brush,
456                                        editor);
457 
458       gtk_adjustment_set_value (adj, value);
459 
460       g_signal_handlers_unblock_by_func (adj,
461                                          gimp_brush_editor_update_brush,
462                                          editor);
463     }
464 }
465