1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18 #include "config.h"
19
20 #include <string.h>
21
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24 #include <gdk/gdkkeysyms.h>
25
26 #include "libgimpmath/gimpmath.h"
27 #include "libgimpcolor/gimpcolor.h"
28 #include "libgimpconfig/gimpconfig.h"
29 #include "libgimpwidgets/gimpwidgets.h"
30
31 #include "widgets-types.h"
32
33 #include "core/gimp.h"
34 #include "core/gimpcurve.h"
35 #include "core/gimpcurve-map.h"
36 #include "core/gimpmarshal.h"
37
38 #include "gimpclipboard.h"
39 #include "gimpcurveview.h"
40 #include "gimpwidgets-utils.h"
41
42
43 #define POINT_MAX_DISTANCE 16.0
44
45
46 enum
47 {
48 PROP_0,
49 PROP_GIMP,
50 PROP_BASE_LINE,
51 PROP_GRID_ROWS,
52 PROP_GRID_COLUMNS,
53 PROP_X_AXIS_LABEL,
54 PROP_Y_AXIS_LABEL
55 };
56
57 enum
58 {
59 SELECTION_CHANGED,
60 CUT_CLIPBOARD,
61 COPY_CLIPBOARD,
62 PASTE_CLIPBOARD,
63 LAST_SIGNAL
64 };
65
66
67 typedef struct
68 {
69 GimpCurve *curve;
70 GimpRGB color;
71 gboolean color_set;
72 } BGCurve;
73
74
75 static void gimp_curve_view_finalize (GObject *object);
76 static void gimp_curve_view_dispose (GObject *object);
77 static void gimp_curve_view_set_property (GObject *object,
78 guint property_id,
79 const GValue *value,
80 GParamSpec *pspec);
81 static void gimp_curve_view_get_property (GObject *object,
82 guint property_id,
83 GValue *value,
84 GParamSpec *pspec);
85
86 static void gimp_curve_view_style_set (GtkWidget *widget,
87 GtkStyle *prev_style);
88 static gboolean gimp_curve_view_expose (GtkWidget *widget,
89 GdkEventExpose *event);
90 static gboolean gimp_curve_view_button_press (GtkWidget *widget,
91 GdkEventButton *bevent);
92 static gboolean gimp_curve_view_button_release (GtkWidget *widget,
93 GdkEventButton *bevent);
94 static gboolean gimp_curve_view_motion_notify (GtkWidget *widget,
95 GdkEventMotion *bevent);
96 static gboolean gimp_curve_view_leave_notify (GtkWidget *widget,
97 GdkEventCrossing *cevent);
98 static gboolean gimp_curve_view_key_press (GtkWidget *widget,
99 GdkEventKey *kevent);
100
101 static void gimp_curve_view_cut_clipboard (GimpCurveView *view);
102 static void gimp_curve_view_copy_clipboard (GimpCurveView *view);
103 static void gimp_curve_view_paste_clipboard (GimpCurveView *view);
104
105 static void gimp_curve_view_curve_dirty (GimpCurve *curve,
106 GimpCurveView *view);
107 static void gimp_curve_view_curve_notify_n_points (GimpCurve *curve,
108 GParamSpec *pspec,
109 GimpCurveView *view);
110
111 static void gimp_curve_view_set_cursor (GimpCurveView *view,
112 gdouble x,
113 gdouble y);
114 static void gimp_curve_view_unset_cursor (GimpCurveView *view);
115
116
117 G_DEFINE_TYPE (GimpCurveView, gimp_curve_view,
118 GIMP_TYPE_HISTOGRAM_VIEW)
119
120 #define parent_class gimp_curve_view_parent_class
121
122 static guint curve_view_signals[LAST_SIGNAL] = { 0 };
123
124
125 static void
gimp_curve_view_class_init(GimpCurveViewClass * klass)126 gimp_curve_view_class_init (GimpCurveViewClass *klass)
127 {
128 GObjectClass *object_class = G_OBJECT_CLASS (klass);
129 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
130 GtkBindingSet *binding_set;
131
132 object_class->finalize = gimp_curve_view_finalize;
133 object_class->dispose = gimp_curve_view_dispose;
134 object_class->set_property = gimp_curve_view_set_property;
135 object_class->get_property = gimp_curve_view_get_property;
136
137 widget_class->style_set = gimp_curve_view_style_set;
138 widget_class->expose_event = gimp_curve_view_expose;
139 widget_class->button_press_event = gimp_curve_view_button_press;
140 widget_class->button_release_event = gimp_curve_view_button_release;
141 widget_class->motion_notify_event = gimp_curve_view_motion_notify;
142 widget_class->leave_notify_event = gimp_curve_view_leave_notify;
143 widget_class->key_press_event = gimp_curve_view_key_press;
144
145 klass->selection_changed = NULL;
146 klass->cut_clipboard = gimp_curve_view_cut_clipboard;
147 klass->copy_clipboard = gimp_curve_view_copy_clipboard;
148 klass->paste_clipboard = gimp_curve_view_paste_clipboard;
149
150 g_object_class_install_property (object_class, PROP_GIMP,
151 g_param_spec_object ("gimp",
152 NULL, NULL,
153 GIMP_TYPE_GIMP,
154 GIMP_PARAM_READWRITE));
155
156 g_object_class_install_property (object_class, PROP_BASE_LINE,
157 g_param_spec_boolean ("base-line",
158 NULL, NULL,
159 TRUE,
160 GIMP_PARAM_READWRITE |
161 G_PARAM_CONSTRUCT_ONLY));
162
163 g_object_class_install_property (object_class, PROP_GRID_ROWS,
164 g_param_spec_int ("grid-rows", NULL, NULL,
165 0, 100, 8,
166 GIMP_PARAM_READWRITE |
167 G_PARAM_CONSTRUCT_ONLY));
168
169 g_object_class_install_property (object_class, PROP_GRID_COLUMNS,
170 g_param_spec_int ("grid-columns", NULL, NULL,
171 0, 100, 8,
172 GIMP_PARAM_READWRITE |
173 G_PARAM_CONSTRUCT_ONLY));
174
175 g_object_class_install_property (object_class, PROP_X_AXIS_LABEL,
176 g_param_spec_string ("x-axis-label", NULL, NULL,
177 NULL,
178 GIMP_PARAM_READWRITE));
179
180 g_object_class_install_property (object_class, PROP_Y_AXIS_LABEL,
181 g_param_spec_string ("y-axis-label", NULL, NULL,
182 NULL,
183 GIMP_PARAM_READWRITE));
184
185 curve_view_signals[SELECTION_CHANGED] =
186 g_signal_new ("selection-changed",
187 G_TYPE_FROM_CLASS (klass),
188 G_SIGNAL_RUN_FIRST,
189 G_STRUCT_OFFSET (GimpCurveViewClass, selection_changed),
190 NULL, NULL,
191 gimp_marshal_VOID__VOID,
192 G_TYPE_NONE, 0);
193
194 curve_view_signals[CUT_CLIPBOARD] =
195 g_signal_new ("cut-clipboard",
196 G_TYPE_FROM_CLASS (klass),
197 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
198 G_STRUCT_OFFSET (GimpCurveViewClass, cut_clipboard),
199 NULL, NULL,
200 gimp_marshal_VOID__VOID,
201 G_TYPE_NONE, 0);
202
203 curve_view_signals[COPY_CLIPBOARD] =
204 g_signal_new ("copy-clipboard",
205 G_TYPE_FROM_CLASS (klass),
206 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
207 G_STRUCT_OFFSET (GimpCurveViewClass, copy_clipboard),
208 NULL, NULL,
209 gimp_marshal_VOID__VOID,
210 G_TYPE_NONE, 0);
211
212 curve_view_signals[PASTE_CLIPBOARD] =
213 g_signal_new ("paste-clipboard",
214 G_TYPE_FROM_CLASS (klass),
215 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
216 G_STRUCT_OFFSET (GimpCurveViewClass, paste_clipboard),
217 NULL, NULL,
218 gimp_marshal_VOID__VOID,
219 G_TYPE_NONE, 0);
220
221 binding_set = gtk_binding_set_by_class (klass);
222
223 gtk_binding_entry_add_signal (binding_set, GDK_KEY_x, GDK_CONTROL_MASK,
224 "cut-clipboard", 0);
225 gtk_binding_entry_add_signal (binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
226 "copy-clipboard", 0);
227 gtk_binding_entry_add_signal (binding_set, GDK_KEY_v, GDK_CONTROL_MASK,
228 "paste-clipboard", 0);
229 }
230
231 static void
gimp_curve_view_init(GimpCurveView * view)232 gimp_curve_view_init (GimpCurveView *view)
233 {
234 view->curve = NULL;
235 view->selected = -1;
236 view->offset_x = 0.0;
237 view->offset_y = 0.0;
238 view->last_x = 0.0;
239 view->last_y = 0.0;
240 view->cursor_type = -1;
241 view->xpos = -1.0;
242 view->cursor_x = -1.0;
243 view->cursor_y = -1.0;
244 view->range_x_min = 0.0;
245 view->range_x_max = 1.0;
246 view->range_y_min = 0.0;
247 view->range_y_max = 1.0;
248
249 view->x_axis_label = NULL;
250 view->y_axis_label = NULL;
251
252 gtk_widget_set_can_focus (GTK_WIDGET (view), TRUE);
253 gtk_widget_add_events (GTK_WIDGET (view),
254 GDK_BUTTON_PRESS_MASK |
255 GDK_BUTTON_RELEASE_MASK |
256 GDK_BUTTON1_MOTION_MASK |
257 GDK_POINTER_MOTION_MASK |
258 GDK_KEY_PRESS_MASK |
259 GDK_LEAVE_NOTIFY_MASK);
260 }
261
262 static void
gimp_curve_view_finalize(GObject * object)263 gimp_curve_view_finalize (GObject *object)
264 {
265 GimpCurveView *view = GIMP_CURVE_VIEW (object);
266
267 g_clear_object (&view->orig_curve);
268
269 g_clear_object (&view->layout);
270 g_clear_object (&view->cursor_layout);
271
272 g_clear_pointer (&view->x_axis_label, g_free);
273 g_clear_pointer (&view->y_axis_label, g_free);
274
275 G_OBJECT_CLASS (parent_class)->finalize (object);
276 }
277
278 static void
gimp_curve_view_dispose(GObject * object)279 gimp_curve_view_dispose (GObject *object)
280 {
281 GimpCurveView *view = GIMP_CURVE_VIEW (object);
282
283 gimp_curve_view_set_curve (view, NULL, NULL);
284
285 if (view->bg_curves)
286 gimp_curve_view_remove_all_backgrounds (view);
287
288 G_OBJECT_CLASS (parent_class)->dispose (object);
289 }
290
291 static void
gimp_curve_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)292 gimp_curve_view_set_property (GObject *object,
293 guint property_id,
294 const GValue *value,
295 GParamSpec *pspec)
296 {
297 GimpCurveView *view = GIMP_CURVE_VIEW (object);
298
299 switch (property_id)
300 {
301 case PROP_GIMP:
302 view->gimp = g_value_get_object (value); /* don't ref */
303 break;
304 case PROP_GRID_ROWS:
305 view->grid_rows = g_value_get_int (value);
306 break;
307 case PROP_GRID_COLUMNS:
308 view->grid_columns = g_value_get_int (value);
309 break;
310 case PROP_BASE_LINE:
311 view->draw_base_line = g_value_get_boolean (value);
312 break;
313 case PROP_X_AXIS_LABEL:
314 gimp_curve_view_set_x_axis_label (view, g_value_get_string (value));
315 break;
316 case PROP_Y_AXIS_LABEL:
317 gimp_curve_view_set_y_axis_label (view, g_value_get_string (value));
318 break;
319 default:
320 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
321 break;
322 }
323 }
324
325 static void
gimp_curve_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)326 gimp_curve_view_get_property (GObject *object,
327 guint property_id,
328 GValue *value,
329 GParamSpec *pspec)
330 {
331 GimpCurveView *view = GIMP_CURVE_VIEW (object);
332
333 switch (property_id)
334 {
335 case PROP_GIMP:
336 g_value_set_object (value, view->gimp);
337 break;
338 case PROP_GRID_ROWS:
339 g_value_set_int (value, view->grid_rows);
340 break;
341 case PROP_GRID_COLUMNS:
342 g_value_set_int (value, view->grid_columns);
343 break;
344 case PROP_BASE_LINE:
345 g_value_set_boolean (value, view->draw_base_line);
346 break;
347 case PROP_X_AXIS_LABEL:
348 g_value_set_string (value, view->x_axis_label);
349 break;
350 case PROP_Y_AXIS_LABEL:
351 g_value_set_string (value, view->y_axis_label);
352 break;
353 default:
354 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
355 break;
356 }
357 }
358
359 static void
gimp_curve_view_style_set(GtkWidget * widget,GtkStyle * prev_style)360 gimp_curve_view_style_set (GtkWidget *widget,
361 GtkStyle *prev_style)
362 {
363 GimpCurveView *view = GIMP_CURVE_VIEW (widget);
364
365 GTK_WIDGET_CLASS (parent_class)->style_set (widget, prev_style);
366
367 g_clear_object (&view->layout);
368 g_clear_object (&view->cursor_layout);
369 }
370
371 static void
gimp_curve_view_draw_grid(GimpCurveView * view,cairo_t * cr,gint width,gint height,gint border)372 gimp_curve_view_draw_grid (GimpCurveView *view,
373 cairo_t *cr,
374 gint width,
375 gint height,
376 gint border)
377 {
378 gint i;
379
380 for (i = 1; i < view->grid_rows; i++)
381 {
382 gint y = i * (height - 1) / view->grid_rows;
383
384 if ((view->grid_rows % 2) == 0 && (i == view->grid_rows / 2))
385 continue;
386
387 cairo_move_to (cr, border, border + y);
388 cairo_line_to (cr, border + width - 1, border + y);
389 }
390
391 for (i = 1; i < view->grid_columns; i++)
392 {
393 gint x = i * (width - 1) / view->grid_columns;
394
395 if ((view->grid_columns % 2) == 0 && (i == view->grid_columns / 2))
396 continue;
397
398 cairo_move_to (cr, border + x, border);
399 cairo_line_to (cr, border + x, border + height - 1);
400 }
401
402 if (view->draw_base_line)
403 {
404 cairo_move_to (cr, border, border + height - 1);
405 cairo_line_to (cr, border + width - 1, border);
406 }
407
408 cairo_set_line_width (cr, 0.6);
409 cairo_stroke (cr);
410
411 if ((view->grid_rows % 2) == 0)
412 {
413 gint y = (height - 1) / 2;
414
415 cairo_move_to (cr, border, border + y);
416 cairo_line_to (cr, border + width - 1, border + y);
417 }
418
419 if ((view->grid_columns % 2) == 0)
420 {
421 gint x = (width - 1) / 2;
422
423 cairo_move_to (cr, border + x, border);
424 cairo_line_to (cr, border + x, border + height - 1);
425 }
426
427 cairo_set_line_width (cr, 1.0);
428 cairo_stroke (cr);
429 }
430
431 static void
gimp_curve_view_draw_point(GimpCurveView * view,cairo_t * cr,gint i,gint width,gint height,gint border)432 gimp_curve_view_draw_point (GimpCurveView *view,
433 cairo_t *cr,
434 gint i,
435 gint width,
436 gint height,
437 gint border)
438 {
439 gdouble x, y;
440
441 gimp_curve_get_point (view->curve, i, &x, &y);
442
443 y = 1.0 - y;
444
445 #define CIRCLE_RADIUS 3
446 #define DIAMOND_RADIUS (G_SQRT2 * CIRCLE_RADIUS)
447
448 switch (gimp_curve_get_point_type (view->curve, i))
449 {
450 case GIMP_CURVE_POINT_SMOOTH:
451 cairo_move_to (cr,
452 border + (gdouble) (width - 1) * x + CIRCLE_RADIUS,
453 border + (gdouble) (height - 1) * y);
454 cairo_arc (cr,
455 border + (gdouble) (width - 1) * x,
456 border + (gdouble) (height - 1) * y,
457 CIRCLE_RADIUS,
458 0, 2 * G_PI);
459 break;
460
461 case GIMP_CURVE_POINT_CORNER:
462 cairo_move_to (cr,
463 border + (gdouble) (width - 1) * x,
464 border + (gdouble) (height - 1) * y - DIAMOND_RADIUS);
465 cairo_line_to (cr,
466 border + (gdouble) (width - 1) * x + DIAMOND_RADIUS,
467 border + (gdouble) (height - 1) * y);
468 cairo_line_to (cr,
469 border + (gdouble) (width - 1) * x,
470 border + (gdouble) (height - 1) * y + DIAMOND_RADIUS);
471 cairo_line_to (cr,
472 border + (gdouble) (width - 1) * x - DIAMOND_RADIUS,
473 border + (gdouble) (height - 1) * y);
474 cairo_close_path (cr);
475 break;
476 }
477 }
478
479 static void
gimp_curve_view_draw_curve(GimpCurveView * view,cairo_t * cr,GimpCurve * curve,gint width,gint height,gint border)480 gimp_curve_view_draw_curve (GimpCurveView *view,
481 cairo_t *cr,
482 GimpCurve *curve,
483 gint width,
484 gint height,
485 gint border)
486 {
487 gdouble x, y;
488 gint i;
489
490 x = 0.0;
491 y = 1.0 - gimp_curve_map_value (curve, 0.0);
492
493 cairo_move_to (cr,
494 border + (gdouble) (width - 1) * x,
495 border + (gdouble) (height - 1)* y);
496
497 for (i = 1; i < 256; i++)
498 {
499 x = (gdouble) i / 255.0;
500 y = 1.0 - gimp_curve_map_value (curve, x);
501
502 cairo_line_to (cr,
503 border + (gdouble) (width - 1) * x,
504 border + (gdouble) (height - 1) * y);
505 }
506
507 cairo_stroke (cr);
508 }
509
510 static gboolean
gimp_curve_view_expose(GtkWidget * widget,GdkEventExpose * event)511 gimp_curve_view_expose (GtkWidget *widget,
512 GdkEventExpose *event)
513 {
514 GimpCurveView *view = GIMP_CURVE_VIEW (widget);
515 GdkWindow *window = gtk_widget_get_window (widget);
516 GtkStyle *style = gtk_widget_get_style (widget);
517 GtkAllocation allocation;
518 cairo_t *cr;
519 GList *list;
520 gint border;
521 gint width;
522 gint height;
523 gint layout_x;
524 gint layout_y;
525 gdouble x, y;
526 gint i;
527
528 GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
529
530 if (! view->curve)
531 return FALSE;
532
533 gtk_widget_get_allocation (widget, &allocation);
534
535 border = GIMP_HISTOGRAM_VIEW (view)->border_width;
536 width = allocation.width - 2 * border;
537 height = allocation.height - 2 * border;
538
539 cr = gdk_cairo_create (gtk_widget_get_window (widget));
540
541 gdk_cairo_region (cr, event->region);
542 cairo_clip (cr);
543
544 if (gtk_widget_has_focus (widget))
545 {
546 gtk_paint_focus (style, window,
547 gtk_widget_get_state (widget),
548 &event->area, widget, NULL,
549 border - 2, border - 2,
550 width + 4, height + 4);
551 }
552
553 cairo_set_line_width (cr, 1.0);
554 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
555 cairo_translate (cr, 0.5, 0.5);
556
557 /* Draw the grid lines */
558 gdk_cairo_set_source_color (cr, &style->text_aa[GTK_STATE_NORMAL]);
559
560 gimp_curve_view_draw_grid (view, cr, width, height, border);
561
562 /* Draw the axis labels */
563
564 if (view->x_axis_label)
565 {
566 if (! view->layout)
567 view->layout = gtk_widget_create_pango_layout (widget, NULL);
568
569 pango_layout_set_text (view->layout, view->x_axis_label, -1);
570 pango_layout_get_pixel_size (view->layout, &layout_x, &layout_y);
571
572 cairo_move_to (cr,
573 width - border - layout_x,
574 height - border - layout_y);
575
576 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
577 pango_cairo_show_layout (cr, view->layout);
578 }
579
580 if (view->y_axis_label)
581 {
582 if (! view->layout)
583 view->layout = gtk_widget_create_pango_layout (widget, NULL);
584
585 pango_layout_set_text (view->layout, view->y_axis_label, -1);
586 pango_layout_get_pixel_size (view->layout, &layout_x, &layout_y);
587
588 cairo_save (cr);
589
590 cairo_move_to (cr,
591 2 * border,
592 2 * border + layout_x);
593 cairo_rotate (cr, - G_PI / 2);
594
595 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
596 pango_cairo_show_layout (cr, view->layout);
597
598 cairo_restore (cr);
599 }
600
601
602 /* Draw the background curves */
603 for (list = view->bg_curves; list; list = g_list_next (list))
604 {
605 BGCurve *bg = list->data;
606
607 if (bg->color_set)
608 {
609 cairo_set_source_rgba (cr,
610 bg->color.r,
611 bg->color.g,
612 bg->color.b,
613 0.5);
614 }
615 else
616 {
617 cairo_set_source_rgba (cr,
618 style->text[GTK_STATE_NORMAL].red / 65535.0,
619 style->text[GTK_STATE_NORMAL].green / 65535.0,
620 style->text[GTK_STATE_NORMAL].blue / 65535.0,
621 0.5);
622 }
623
624 gimp_curve_view_draw_curve (view, cr, bg->curve,
625 width, height, border);
626 }
627
628 /* Draw the curve */
629 if (view->curve_color)
630 gimp_cairo_set_source_rgb (cr, view->curve_color);
631 else
632 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
633
634 gimp_curve_view_draw_curve (view, cr, view->curve,
635 width, height, border);
636
637 /* Draw the points */
638 if (gimp_curve_get_curve_type (view->curve) == GIMP_CURVE_SMOOTH)
639 {
640 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
641
642 /* Draw the unselected points */
643 for (i = 0; i < view->curve->n_points; i++)
644 {
645 if (i == view->selected)
646 continue;
647
648 gimp_curve_view_draw_point (view, cr, i, width, height, border);
649 }
650
651 cairo_stroke (cr);
652
653 /* Draw the selected point */
654 if (view->selected != -1)
655 {
656 gimp_curve_view_draw_point (view, cr, view->selected,
657 width, height, border);
658 cairo_fill (cr);
659 }
660 }
661
662 if (view->xpos >= 0.0)
663 {
664 gchar buf[32];
665
666 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
667
668 /* draw the color line */
669 cairo_move_to (cr,
670 border + ROUND ((gdouble) (width - 1) * view->xpos),
671 border + 1);
672 cairo_line_to (cr,
673 border + ROUND ((gdouble) (width - 1) * view->xpos),
674 border + height - 1);
675 cairo_stroke (cr);
676
677 if (view->range_x_max == 255.0)
678 {
679 /* stupid heuristic: special-case for 0..255 */
680
681 g_snprintf (buf, sizeof (buf), "x:%3d",
682 (gint) (view->xpos *
683 (view->range_x_max - view->range_x_min) +
684 view->range_x_min));
685 }
686 else if (view->range_x_max == 100.0)
687 {
688 /* and for 0..100 */
689
690 g_snprintf (buf, sizeof (buf), "x:%0.2f",
691 view->xpos *
692 (view->range_x_max - view->range_x_min) +
693 view->range_x_min);
694 }
695 else
696 {
697 g_snprintf (buf, sizeof (buf), "x:%0.3f",
698 view->xpos *
699 (view->range_x_max - view->range_x_min) +
700 view->range_x_min);
701 }
702
703 if (! view->layout)
704 view->layout = gtk_widget_create_pango_layout (widget, NULL);
705
706 pango_layout_set_text (view->layout, buf, -1);
707 pango_layout_get_pixel_size (view->layout, &layout_x, &layout_y);
708
709 if (view->xpos < 0.5)
710 layout_x = border;
711 else
712 layout_x = -(layout_x + border);
713
714 cairo_move_to (cr,
715 border + (gdouble) width * view->xpos + layout_x,
716 border + height - border - layout_y);
717 pango_cairo_show_layout (cr, view->layout);
718 }
719
720 if (view->cursor_x >= 0.0 && view->cursor_x <= 1.0 &&
721 view->cursor_y >= 0.0 && view->cursor_y <= 1.0)
722 {
723 gchar buf[32];
724 gint w, h;
725
726 if (! view->cursor_layout)
727 view->cursor_layout = gtk_widget_create_pango_layout (widget, NULL);
728
729 if (view->range_x_max == 255.0 &&
730 view->range_y_max == 255.0)
731 {
732 /* stupid heuristic: special-case for 0..255 */
733
734 g_snprintf (buf, sizeof (buf), "x:%3d y:%3d",
735 (gint) round (view->cursor_x *
736 (view->range_x_max - view->range_x_min) +
737 view->range_x_min),
738 (gint) round ((1.0 - view->cursor_y) *
739 (view->range_y_max - view->range_y_min) +
740 view->range_y_min));
741 }
742 else if (view->range_x_max == 100.0 &&
743 view->range_y_max == 100.0)
744 {
745 /* and for 0..100 */
746
747 g_snprintf (buf, sizeof (buf), "x:%0.2f y:%0.2f",
748 view->cursor_x *
749 (view->range_x_max - view->range_x_min) +
750 view->range_x_min,
751 (1.0 - view->cursor_y) *
752 (view->range_y_max - view->range_y_min) +
753 view->range_y_min);
754 }
755 else
756 {
757 g_snprintf (buf, sizeof (buf), "x:%0.3f y:%0.3f",
758 view->cursor_x *
759 (view->range_x_max - view->range_x_min) +
760 view->range_x_min,
761 (1.0 - view->cursor_y) *
762 (view->range_y_max - view->range_y_min) +
763 view->range_y_min);
764 }
765
766 pango_layout_set_text (view->cursor_layout, buf, -1);
767 pango_layout_get_pixel_extents (view->cursor_layout,
768 NULL, &view->cursor_rect);
769
770 x = border * 2 + 3;
771 y = border * 2 + 3;
772 w = view->cursor_rect.width;
773 h = view->cursor_rect.height;
774
775 if (view->x_axis_label)
776 x += border + view->cursor_rect.height; /* coincidentially the right value */
777
778 cairo_push_group (cr);
779
780 gdk_cairo_set_source_color (cr, &style->text[GTK_STATE_NORMAL]);
781 cairo_rectangle (cr, x + 0.5, y + 0.5, w, h);
782 cairo_fill_preserve (cr);
783
784 cairo_set_line_width (cr, 6);
785 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
786 cairo_stroke (cr);
787
788 gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]);
789 cairo_move_to (cr, x, y);
790 pango_cairo_show_layout (cr, view->cursor_layout);
791
792 cairo_pop_group_to_source (cr);
793 cairo_paint_with_alpha (cr, 0.6);
794 }
795
796 cairo_destroy (cr);
797
798 return FALSE;
799 }
800
801 static void
set_cursor(GimpCurveView * view,GdkCursorType new_cursor)802 set_cursor (GimpCurveView *view,
803 GdkCursorType new_cursor)
804 {
805 if (new_cursor != view->cursor_type)
806 {
807 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (view));
808 GdkCursor *cursor = gdk_cursor_new_for_display (display, new_cursor);
809
810 gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (view)), cursor);
811 gdk_cursor_unref (cursor);
812
813 view->cursor_type = new_cursor;
814 }
815 }
816
817 static gboolean
gimp_curve_view_button_press(GtkWidget * widget,GdkEventButton * bevent)818 gimp_curve_view_button_press (GtkWidget *widget,
819 GdkEventButton *bevent)
820 {
821 GimpCurveView *view = GIMP_CURVE_VIEW (widget);
822 GimpCurve *curve = view->curve;
823 GtkAllocation allocation;
824 gint border;
825 gint width, height;
826 gdouble x;
827 gdouble y;
828 gint point;
829 gdouble point_x;
830 gdouble point_y;
831
832 if (! curve || bevent->button != 1)
833 return TRUE;
834
835 gtk_widget_get_allocation (widget, &allocation);
836
837 border = GIMP_HISTOGRAM_VIEW (view)->border_width;
838 width = allocation.width - 2 * border;
839 height = allocation.height - 2 * border;
840
841 x = (gdouble) (bevent->x - border) / (gdouble) width;
842 y = (gdouble) (bevent->y - border) / (gdouble) height;
843
844 x = CLAMP (x, 0.0, 1.0);
845 y = CLAMP (y, 0.0, 1.0);
846
847 view->grabbed = TRUE;
848
849 view->orig_curve = GIMP_CURVE (gimp_data_duplicate (GIMP_DATA (curve)));
850
851 set_cursor (view, GDK_TCROSS);
852
853 switch (gimp_curve_get_curve_type (curve))
854 {
855 case GIMP_CURVE_SMOOTH:
856 point = gimp_curve_get_closest_point (curve, x, 1.0 - y,
857 POINT_MAX_DISTANCE /
858 MAX (width, height));
859
860 if (point < 0)
861 {
862 GimpCurvePointType type = GIMP_CURVE_POINT_SMOOTH;
863
864 if (bevent->state & gimp_get_constrain_behavior_mask ())
865 y = 1.0 - gimp_curve_map_value (view->orig_curve, x);
866
867 if (view->selected >= 0)
868 type = gimp_curve_get_point_type (curve, view->selected);
869
870 point = gimp_curve_add_point (curve, x, 1.0 - y);
871
872 gimp_curve_set_point_type (curve, point, type);
873 }
874
875 if (point > 0)
876 gimp_curve_get_point (curve, point - 1, &view->leftmost, NULL);
877 else
878 view->leftmost = -1.0;
879
880 if (point < gimp_curve_get_n_points (curve) - 1)
881 gimp_curve_get_point (curve, point + 1, &view->rightmost, NULL);
882 else
883 view->rightmost = 2.0;
884
885 gimp_curve_view_set_selected (view, point);
886
887 gimp_curve_get_point (curve, point, &point_x, &point_y);
888
889 view->offset_x = point_x - x;
890 view->offset_y = (1.0 - point_y) - y;
891
892 view->point_type = gimp_curve_get_point_type (curve, point);
893 break;
894
895 case GIMP_CURVE_FREE:
896 view->last_x = x;
897 view->last_y = y;
898
899 gimp_curve_set_curve (curve, x, 1.0 - y);
900 break;
901 }
902
903 if (! gtk_widget_has_focus (widget))
904 gtk_widget_grab_focus (widget);
905
906 return TRUE;
907 }
908
909 static gboolean
gimp_curve_view_button_release(GtkWidget * widget,GdkEventButton * bevent)910 gimp_curve_view_button_release (GtkWidget *widget,
911 GdkEventButton *bevent)
912 {
913 GimpCurveView *view = GIMP_CURVE_VIEW (widget);
914
915 if (bevent->button != 1)
916 return TRUE;
917
918 g_clear_object (&view->orig_curve);
919
920 view->offset_x = 0.0;
921 view->offset_y = 0.0;
922
923 view->grabbed = FALSE;
924
925 set_cursor (view, GDK_FLEUR);
926
927 return TRUE;
928 }
929
930 static gboolean
gimp_curve_view_motion_notify(GtkWidget * widget,GdkEventMotion * mevent)931 gimp_curve_view_motion_notify (GtkWidget *widget,
932 GdkEventMotion *mevent)
933 {
934 GimpCurveView *view = GIMP_CURVE_VIEW (widget);
935 GimpCurve *curve = view->curve;
936 GtkAllocation allocation;
937 GdkCursorType new_cursor = GDK_X_CURSOR;
938 gint border;
939 gint width, height;
940 gdouble x;
941 gdouble y;
942 gdouble point_x;
943 gdouble point_y;
944 gint point;
945
946 if (! curve)
947 return TRUE;
948
949 gtk_widget_get_allocation (widget, &allocation);
950
951 border = GIMP_HISTOGRAM_VIEW (view)->border_width;
952 width = allocation.width - 2 * border;
953 height = allocation.height - 2 * border;
954
955 x = (gdouble) (mevent->x - border) / (gdouble) width;
956 y = (gdouble) (mevent->y - border) / (gdouble) height;
957
958 x += view->offset_x;
959 y += view->offset_y;
960
961 x = CLAMP (x, 0.0, 1.0);
962 y = CLAMP (y, 0.0, 1.0);
963
964 switch (gimp_curve_get_curve_type (curve))
965 {
966 case GIMP_CURVE_SMOOTH:
967 if (! view->grabbed) /* If no point is grabbed... */
968 {
969 point = gimp_curve_get_closest_point (curve, x, 1.0 - y,
970 POINT_MAX_DISTANCE /
971 MAX (width, height));
972
973 if (point >= 0)
974 {
975 gimp_curve_get_point (curve, point, &point_x, &point_y);
976
977 new_cursor = GDK_FLEUR;
978
979 x = point_x;
980 y = 1.0 - point_y;
981 }
982 else
983 {
984 new_cursor = GDK_TCROSS;
985
986 if (mevent->state & gimp_get_constrain_behavior_mask ())
987 y = 1.0 - gimp_curve_map_value (view->curve, x);
988 }
989 }
990 else /* Else, drag the grabbed point */
991 {
992 new_cursor = GDK_TCROSS;
993
994 if (mevent->state & gimp_get_constrain_behavior_mask ())
995 y = 1.0 - gimp_curve_map_value (view->orig_curve, x);
996
997 gimp_data_freeze (GIMP_DATA (curve));
998
999 if (x > view->leftmost && x < view->rightmost)
1000 {
1001 if (view->selected < 0)
1002 {
1003 gimp_curve_view_set_selected (
1004 view,
1005 gimp_curve_add_point (curve, x, 1.0 - y));
1006
1007 gimp_curve_set_point_type (curve,
1008 view->selected, view->point_type);
1009 }
1010 else
1011 {
1012 gimp_curve_set_point (curve, view->selected, x, 1.0 - y);
1013 }
1014 }
1015 else
1016 {
1017 if (view->selected >= 0)
1018 {
1019 gimp_curve_delete_point (curve, view->selected);
1020
1021 gimp_curve_view_set_selected (view, -1);
1022 }
1023 }
1024
1025 gimp_data_thaw (GIMP_DATA (curve));
1026 }
1027 break;
1028
1029 case GIMP_CURVE_FREE:
1030 if (view->grabbed)
1031 {
1032 gint n_samples = gimp_curve_get_n_samples (curve);
1033 gdouble x1, x2;
1034 gdouble y1, y2;
1035
1036 if (view->last_x > x)
1037 {
1038 x1 = x;
1039 x2 = view->last_x;
1040 y1 = y;
1041 y2 = view->last_y;
1042 }
1043 else
1044 {
1045 x1 = view->last_x;
1046 x2 = x;
1047 y1 = view->last_y;
1048 y2 = y;
1049 }
1050
1051 if (x2 != x1)
1052 {
1053 gint from = ROUND (x1 * (gdouble) (n_samples - 1));
1054 gint to = ROUND (x2 * (gdouble) (n_samples - 1));
1055 gint i;
1056
1057 gimp_data_freeze (GIMP_DATA (curve));
1058
1059 for (i = from; i <= to; i++)
1060 {
1061 gdouble xpos = (gdouble) i / (gdouble) (n_samples - 1);
1062 gdouble ypos = (y1 + ((y2 - y1) * (xpos - x1)) / (x2 - x1));
1063
1064 xpos = CLAMP (xpos, 0.0, 1.0);
1065 ypos = CLAMP (ypos, 0.0, 1.0);
1066
1067 gimp_curve_set_curve (curve, xpos, 1.0 - ypos);
1068 }
1069
1070 gimp_data_thaw (GIMP_DATA (curve));
1071 }
1072 else
1073 {
1074 gimp_curve_set_curve (curve, x, 1.0 - y);
1075 }
1076
1077 view->last_x = x;
1078 view->last_y = y;
1079 }
1080
1081 if (mevent->state & GDK_BUTTON1_MASK)
1082 new_cursor = GDK_TCROSS;
1083 else
1084 new_cursor = GDK_PENCIL;
1085
1086 break;
1087 }
1088
1089 set_cursor (view, new_cursor);
1090
1091 gimp_curve_view_set_cursor (view, x, y);
1092
1093 return TRUE;
1094 }
1095
1096 static gboolean
gimp_curve_view_leave_notify(GtkWidget * widget,GdkEventCrossing * cevent)1097 gimp_curve_view_leave_notify (GtkWidget *widget,
1098 GdkEventCrossing *cevent)
1099 {
1100 GimpCurveView *view = GIMP_CURVE_VIEW (widget);
1101
1102 gimp_curve_view_unset_cursor (view);
1103
1104 return TRUE;
1105 }
1106
1107 static gboolean
gimp_curve_view_key_press(GtkWidget * widget,GdkEventKey * kevent)1108 gimp_curve_view_key_press (GtkWidget *widget,
1109 GdkEventKey *kevent)
1110 {
1111 GimpCurveView *view = GIMP_CURVE_VIEW (widget);
1112 GimpCurve *curve = view->curve;
1113 gboolean handled = FALSE;
1114
1115 if (! view->grabbed &&
1116 curve &&
1117 gimp_curve_get_curve_type (curve) == GIMP_CURVE_SMOOTH &&
1118 view->selected >= 0)
1119 {
1120 gint i = view->selected;
1121 gdouble x, y;
1122
1123 gimp_curve_get_point (curve, i, NULL, &y);
1124
1125 switch (kevent->keyval)
1126 {
1127 case GDK_KEY_Left:
1128 for (i = i - 1; i >= 0 && ! handled; i--)
1129 {
1130 gimp_curve_get_point (curve, i, &x, NULL);
1131
1132 if (x >= 0.0)
1133 {
1134 gimp_curve_view_set_selected (view, i);
1135
1136 handled = TRUE;
1137 }
1138 }
1139 break;
1140
1141 case GDK_KEY_Right:
1142 for (i = i + 1; i < curve->n_points && ! handled; i++)
1143 {
1144 gimp_curve_get_point (curve, i, &x, NULL);
1145
1146 if (x >= 0.0)
1147 {
1148 gimp_curve_view_set_selected (view, i);
1149
1150 handled = TRUE;
1151 }
1152 }
1153 break;
1154
1155 case GDK_KEY_Up:
1156 if (y < 1.0)
1157 {
1158 y = y + (kevent->state & GDK_SHIFT_MASK ?
1159 (16.0 / 255.0) : (1.0 / 255.0));
1160
1161 gimp_curve_move_point (curve, i, CLAMP (y, 0.0, 1.0));
1162
1163 handled = TRUE;
1164 }
1165 break;
1166
1167 case GDK_KEY_Down:
1168 if (y > 0)
1169 {
1170 y = y - (kevent->state & GDK_SHIFT_MASK ?
1171 (16.0 / 255.0) : (1.0 / 255.0));
1172
1173 gimp_curve_move_point (curve, i, CLAMP (y, 0.0, 1.0));
1174
1175 handled = TRUE;
1176 }
1177 break;
1178
1179 case GDK_KEY_Delete:
1180 gimp_curve_delete_point (curve, i);
1181 break;
1182
1183 default:
1184 break;
1185 }
1186 }
1187
1188 if (handled)
1189 {
1190 set_cursor (view, GDK_TCROSS);
1191
1192 return TRUE;
1193 }
1194
1195 return GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, kevent);
1196 }
1197
1198 static void
gimp_curve_view_cut_clipboard(GimpCurveView * view)1199 gimp_curve_view_cut_clipboard (GimpCurveView *view)
1200 {
1201 g_printerr ("%s\n", G_STRFUNC);
1202
1203 if (! view->curve || ! view->gimp)
1204 {
1205 gtk_widget_error_bell (GTK_WIDGET (view));
1206 return;
1207 }
1208
1209 gimp_curve_view_copy_clipboard (view);
1210
1211 gimp_curve_reset (view->curve, FALSE);
1212 }
1213
1214 static void
gimp_curve_view_copy_clipboard(GimpCurveView * view)1215 gimp_curve_view_copy_clipboard (GimpCurveView *view)
1216 {
1217 GimpCurve *copy;
1218
1219 g_printerr ("%s\n", G_STRFUNC);
1220
1221 if (! view->curve || ! view->gimp)
1222 {
1223 gtk_widget_error_bell (GTK_WIDGET (view));
1224 return;
1225 }
1226
1227 copy = GIMP_CURVE (gimp_data_duplicate (GIMP_DATA (view->curve)));
1228 gimp_clipboard_set_curve (view->gimp, copy);
1229 g_object_unref (copy);
1230 }
1231
1232 static void
gimp_curve_view_paste_clipboard(GimpCurveView * view)1233 gimp_curve_view_paste_clipboard (GimpCurveView *view)
1234 {
1235 GimpCurve *copy;
1236
1237 g_printerr ("%s\n", G_STRFUNC);
1238
1239 if (! view->curve || ! view->gimp)
1240 {
1241 gtk_widget_error_bell (GTK_WIDGET (view));
1242 return;
1243 }
1244
1245 copy = gimp_clipboard_get_curve (view->gimp);
1246
1247 if (copy)
1248 {
1249 gimp_config_copy (GIMP_CONFIG (copy),
1250 GIMP_CONFIG (view->curve), 0);
1251 g_object_unref (copy);
1252 }
1253 }
1254
1255 static void
gimp_curve_view_curve_dirty(GimpCurve * curve,GimpCurveView * view)1256 gimp_curve_view_curve_dirty (GimpCurve *curve,
1257 GimpCurveView *view)
1258 {
1259 gtk_widget_queue_draw (GTK_WIDGET (view));
1260 }
1261
1262 static void
gimp_curve_view_curve_notify_n_points(GimpCurve * curve,GParamSpec * pspec,GimpCurveView * view)1263 gimp_curve_view_curve_notify_n_points (GimpCurve *curve,
1264 GParamSpec *pspec,
1265 GimpCurveView *view)
1266 {
1267 gimp_curve_view_set_selected (view, -1);
1268 }
1269
1270
1271 /* public functions */
1272
1273 GtkWidget *
gimp_curve_view_new(void)1274 gimp_curve_view_new (void)
1275 {
1276 return g_object_new (GIMP_TYPE_CURVE_VIEW, NULL);
1277 }
1278
1279 void
gimp_curve_view_set_curve(GimpCurveView * view,GimpCurve * curve,const GimpRGB * color)1280 gimp_curve_view_set_curve (GimpCurveView *view,
1281 GimpCurve *curve,
1282 const GimpRGB *color)
1283 {
1284 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1285 g_return_if_fail (curve == NULL || GIMP_IS_CURVE (curve));
1286
1287 if (view->curve == curve)
1288 return;
1289
1290 if (view->curve)
1291 {
1292 g_signal_handlers_disconnect_by_func (view->curve,
1293 gimp_curve_view_curve_dirty,
1294 view);
1295 g_signal_handlers_disconnect_by_func (view->curve,
1296 gimp_curve_view_curve_notify_n_points,
1297 view);
1298 g_object_unref (view->curve);
1299 }
1300
1301 view->curve = curve;
1302
1303 if (view->curve)
1304 {
1305 g_object_ref (view->curve);
1306 g_signal_connect (view->curve, "dirty",
1307 G_CALLBACK (gimp_curve_view_curve_dirty),
1308 view);
1309 g_signal_connect (view->curve, "notify::n-points",
1310 G_CALLBACK (gimp_curve_view_curve_notify_n_points),
1311 view);
1312 }
1313
1314 if (view->curve_color)
1315 g_free (view->curve_color);
1316
1317 if (color)
1318 view->curve_color = g_memdup (color, sizeof (GimpRGB));
1319 else
1320 view->curve_color = NULL;
1321
1322 gimp_curve_view_set_selected (view, -1);
1323
1324 gtk_widget_queue_draw (GTK_WIDGET (view));
1325 }
1326
1327 GimpCurve *
gimp_curve_view_get_curve(GimpCurveView * view)1328 gimp_curve_view_get_curve (GimpCurveView *view)
1329 {
1330 g_return_val_if_fail (GIMP_IS_CURVE_VIEW (view), NULL);
1331
1332 return view->curve;
1333 }
1334
1335 void
gimp_curve_view_add_background(GimpCurveView * view,GimpCurve * curve,const GimpRGB * color)1336 gimp_curve_view_add_background (GimpCurveView *view,
1337 GimpCurve *curve,
1338 const GimpRGB *color)
1339 {
1340 GList *list;
1341 BGCurve *bg;
1342
1343 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1344 g_return_if_fail (GIMP_IS_CURVE (curve));
1345
1346 for (list = view->bg_curves; list; list = g_list_next (list))
1347 {
1348 bg = list->data;
1349
1350 g_return_if_fail (curve != bg->curve);
1351 }
1352
1353 bg = g_slice_new0 (BGCurve);
1354
1355 bg->curve = g_object_ref (curve);
1356
1357 if (color)
1358 {
1359 bg->color = *color;
1360 bg->color_set = TRUE;
1361 }
1362
1363 g_signal_connect (bg->curve, "dirty",
1364 G_CALLBACK (gimp_curve_view_curve_dirty),
1365 view);
1366
1367 view->bg_curves = g_list_append (view->bg_curves, bg);
1368
1369 gtk_widget_queue_draw (GTK_WIDGET (view));
1370 }
1371
1372 void
gimp_curve_view_remove_background(GimpCurveView * view,GimpCurve * curve)1373 gimp_curve_view_remove_background (GimpCurveView *view,
1374 GimpCurve *curve)
1375 {
1376 GList *list;
1377
1378 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1379 g_return_if_fail (GIMP_IS_CURVE (curve));
1380
1381 for (list = view->bg_curves; list; list = g_list_next (list))
1382 {
1383 BGCurve *bg = list->data;
1384
1385 if (bg->curve == curve)
1386 {
1387 g_signal_handlers_disconnect_by_func (bg->curve,
1388 gimp_curve_view_curve_dirty,
1389 view);
1390 g_object_unref (bg->curve);
1391
1392 view->bg_curves = g_list_remove (view->bg_curves, bg);
1393
1394 g_slice_free (BGCurve, bg);
1395
1396 gtk_widget_queue_draw (GTK_WIDGET (view));
1397
1398 break;
1399 }
1400 }
1401
1402 if (! list)
1403 g_return_if_reached ();
1404 }
1405
1406 void
gimp_curve_view_remove_all_backgrounds(GimpCurveView * view)1407 gimp_curve_view_remove_all_backgrounds (GimpCurveView *view)
1408 {
1409 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1410
1411 while (view->bg_curves)
1412 {
1413 BGCurve *bg = view->bg_curves->data;
1414
1415 g_signal_handlers_disconnect_by_func (bg->curve,
1416 gimp_curve_view_curve_dirty,
1417 view);
1418 g_object_unref (bg->curve);
1419
1420 view->bg_curves = g_list_remove (view->bg_curves, bg);
1421
1422 g_slice_free (BGCurve, bg);
1423 }
1424
1425 gtk_widget_queue_draw (GTK_WIDGET (view));
1426 }
1427
1428 void
gimp_curve_view_set_selected(GimpCurveView * view,gint selected)1429 gimp_curve_view_set_selected (GimpCurveView *view,
1430 gint selected)
1431 {
1432 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1433
1434 if (selected != view->selected)
1435 {
1436 view->selected = selected;
1437
1438 g_signal_emit (view, curve_view_signals[SELECTION_CHANGED], 0);
1439
1440 gtk_widget_queue_draw (GTK_WIDGET (view));
1441 }
1442 }
1443
1444 gint
gimp_curve_view_get_selected(GimpCurveView * view)1445 gimp_curve_view_get_selected (GimpCurveView *view)
1446 {
1447 g_return_val_if_fail (GIMP_IS_CURVE_VIEW (view), -1);
1448
1449 if (view->curve && view->selected < gimp_curve_get_n_points (view->curve))
1450 return view->selected;
1451 else
1452 return -1;
1453 }
1454
1455 void
gimp_curve_view_set_range_x(GimpCurveView * view,gdouble min,gdouble max)1456 gimp_curve_view_set_range_x (GimpCurveView *view,
1457 gdouble min,
1458 gdouble max)
1459 {
1460 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1461
1462 view->range_x_min = min;
1463 view->range_x_max = max;
1464
1465 gtk_widget_queue_draw (GTK_WIDGET (view));
1466 }
1467
1468 void
gimp_curve_view_set_range_y(GimpCurveView * view,gdouble min,gdouble max)1469 gimp_curve_view_set_range_y (GimpCurveView *view,
1470 gdouble min,
1471 gdouble max)
1472 {
1473 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1474
1475 view->range_y_min = min;
1476 view->range_y_max = max;
1477
1478 gtk_widget_queue_draw (GTK_WIDGET (view));
1479 }
1480
1481 void
gimp_curve_view_set_xpos(GimpCurveView * view,gdouble x)1482 gimp_curve_view_set_xpos (GimpCurveView *view,
1483 gdouble x)
1484 {
1485 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1486
1487 view->xpos = x;
1488
1489 gtk_widget_queue_draw (GTK_WIDGET (view));
1490 }
1491
1492 void
gimp_curve_view_set_x_axis_label(GimpCurveView * view,const gchar * label)1493 gimp_curve_view_set_x_axis_label (GimpCurveView *view,
1494 const gchar *label)
1495 {
1496 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1497
1498 if (view->x_axis_label)
1499 g_free (view->x_axis_label);
1500
1501 view->x_axis_label = g_strdup (label);
1502
1503 g_object_notify (G_OBJECT (view), "x-axis-label");
1504
1505 gtk_widget_queue_draw (GTK_WIDGET (view));
1506 }
1507
1508 void
gimp_curve_view_set_y_axis_label(GimpCurveView * view,const gchar * label)1509 gimp_curve_view_set_y_axis_label (GimpCurveView *view,
1510 const gchar *label)
1511 {
1512 g_return_if_fail (GIMP_IS_CURVE_VIEW (view));
1513
1514 if (view->y_axis_label)
1515 g_free (view->y_axis_label);
1516
1517 view->y_axis_label = g_strdup (label);
1518
1519 g_object_notify (G_OBJECT (view), "y-axis-label");
1520
1521 gtk_widget_queue_draw (GTK_WIDGET (view));
1522 }
1523
1524
1525 /* private functions */
1526
1527 static void
gimp_curve_view_set_cursor(GimpCurveView * view,gdouble x,gdouble y)1528 gimp_curve_view_set_cursor (GimpCurveView *view,
1529 gdouble x,
1530 gdouble y)
1531 {
1532 view->cursor_x = x;
1533 view->cursor_y = y;
1534
1535 /* TODO: only invalidate the cursor label area */
1536 gtk_widget_queue_draw (GTK_WIDGET (view));
1537 }
1538
1539 static void
gimp_curve_view_unset_cursor(GimpCurveView * view)1540 gimp_curve_view_unset_cursor (GimpCurveView *view)
1541 {
1542 view->cursor_x = -1.0;
1543 view->cursor_y = -1.0;
1544
1545 /* TODO: only invalidate the cursor label area */
1546 gtk_widget_queue_draw (GTK_WIDGET (view));
1547 }
1548