gimpcurvestool.c 41.6 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
Sven Neumann's avatar
Sven Neumann committed
18

19 20 21
#include "config.h"

#include <stdio.h>
22 23
#include <stdlib.h>
#include <string.h>
24

Sven Neumann's avatar
Sven Neumann committed
25
#include <gtk/gtk.h>
26
#include <gdk/gdkkeysyms.h>
Sven Neumann's avatar
Sven Neumann committed
27

28
#include "libgimpcolor/gimpcolor.h"
29
#include "libgimpwidgets/gimpwidgets.h"
30

31
#include "tools-types.h"
Michael Natterer's avatar
Michael Natterer committed
32

33
#include "config/gimpguiconfig.h"
34

35
#include "base/curves.h"
Michael Natterer's avatar
Michael Natterer committed
36 37 38
#include "base/gimphistogram.h"
#include "base/gimplut.h"

39
#include "core/gimp.h"
Michael Natterer's avatar
Michael Natterer committed
40
#include "core/gimpdrawable.h"
41
#include "core/gimpdrawable-histogram.h"
Michael Natterer's avatar
Michael Natterer committed
42
#include "core/gimpimage.h"
43
#include "core/gimpimagemap.h"
44
#include "core/gimptoolinfo.h"
Michael Natterer's avatar
Michael Natterer committed
45

46
#include "widgets/gimpcolorbar.h"
Michael Natterer's avatar
Michael Natterer committed
47
#include "widgets/gimpcursor.h"
48
#include "widgets/gimphelp-ids.h"
49
#include "widgets/gimphistogramview.h"
Michael Natterer's avatar
Michael Natterer committed
50

51 52
#include "display/gimpdisplay.h"

Michael Natterer's avatar
Michael Natterer committed
53
#include "gimpcurvestool.h"
54
#include "gimphistogramoptions.h"
55
#include "gimptoolcontrol.h"
Sven Neumann's avatar
Sven Neumann committed
56

57
#include "gimp-intl.h"
58

59

60 61
#define XRANGE   (1 << 0)
#define YRANGE   (1 << 1)
62 63
#define GRAPH    (1 << 2)
#define ALL      (XRANGE | YRANGE | GRAPH)
Elliot Lee's avatar
Elliot Lee committed
64

65 66
#define GRAPH_SIZE    256
#define BAR_SIZE       12
67
#define RADIUS          4
68
#define MIN_DISTANCE    8
Elliot Lee's avatar
Elliot Lee committed
69 70


Michael Natterer's avatar
Michael Natterer committed
71 72
/*  local function prototypes  */

73 74 75
static void     gimp_curves_tool_finalize       (GObject          *object);

static gboolean gimp_curves_tool_initialize     (GimpTool         *tool,
76
                                                 GimpDisplay      *display);
77
static void     gimp_curves_tool_button_release (GimpTool         *tool,
78
                                                 GimpCoords       *coords,
79
                                                 guint32           time,
80
                                                 GdkModifierType   state,
81
                                                 GimpDisplay      *display);
82 83
static gboolean gimp_curves_tool_key_press      (GimpTool         *tool,
                                                 GdkEventKey      *kevent,
84
                                                 GimpDisplay      *display);
85
static void     gimp_curves_tool_oper_update    (GimpTool         *tool,
86 87
                                                 GimpCoords       *coords,
                                                 GdkModifierType   state,
88
                                                 gboolean          proximity,
89
                                                 GimpDisplay      *display);
90 91

static void     gimp_curves_tool_color_picked   (GimpColorTool    *color_tool,
92
                                                 GimpColorPickState pick_state,
93 94 95 96 97 98
                                                 GimpImageType     sample_type,
                                                 GimpRGB          *color,
                                                 gint              color_index);
static void     gimp_curves_tool_map            (GimpImageMapTool *image_map_tool);
static void     gimp_curves_tool_dialog         (GimpImageMapTool *image_map_tool);
static void     gimp_curves_tool_reset          (GimpImageMapTool *image_map_tool);
99 100 101 102
static gboolean gimp_curves_tool_settings_load  (GimpImageMapTool *image_map_tool,
                                                 gpointer          fp);
static gboolean gimp_curves_tool_settings_save  (GimpImageMapTool *image_map_tool,
                                                 gpointer          fp);
103

104
static void     curves_add_point                (GimpCurvesTool   *tool,
105 106 107
                                                 gint              channel);
static gboolean curves_key_press                (GimpCurvesTool   *tool,
                                                 GdkEventKey      *kevent);
108
static void     curves_update                   (GimpCurvesTool   *tool,
109 110 111
                                                 gint              update);

static void     curves_channel_callback         (GtkWidget        *widget,
112
                                                 GimpCurvesTool   *tool);
113
static void     curves_channel_reset_callback   (GtkWidget        *widget,
114
                                                 GimpCurvesTool   *tool);
115

116
static gboolean curves_menu_sensitivity         (gint              value,
Sven Neumann's avatar
Sven Neumann committed
117
                                                 gpointer          data);
118

119
static void     curves_curve_type_callback      (GtkWidget        *widget,
120
                                                 GimpCurvesTool   *tool);
121 122
static gboolean curves_graph_events             (GtkWidget        *widget,
                                                 GdkEvent         *event,
123
                                                 GimpCurvesTool   *tool);
124 125
static gboolean curves_graph_expose             (GtkWidget        *widget,
                                                 GdkEventExpose   *eevent,
126
                                                 GimpCurvesTool   *tool);
127

Michael Natterer's avatar
Michael Natterer committed
128

129
G_DEFINE_TYPE (GimpCurvesTool, gimp_curves_tool, GIMP_TYPE_IMAGE_MAP_TOOL)
130 131

#define parent_class gimp_curves_tool_parent_class
132

Michael Natterer's avatar
Michael Natterer committed
133

134
/*  public functions  */
Michael Natterer's avatar
Michael Natterer committed
135 136

void
Nate Summers's avatar
Nate Summers committed
137
gimp_curves_tool_register (GimpToolRegisterCallback  callback,
138
                           gpointer                  data)
Michael Natterer's avatar
Michael Natterer committed
139
{
Nate Summers's avatar
Nate Summers committed
140
  (* callback) (GIMP_TYPE_CURVES_TOOL,
141
                GIMP_TYPE_HISTOGRAM_OPTIONS,
142
                gimp_color_options_gui,
143
                0,
144
                "gimp-curves-tool",
145 146
                _("Curves"),
                _("Adjust color curves"),
147
                N_("_Curves..."), NULL,
148
                NULL, GIMP_HELP_TOOL_CURVES,
Nate Summers's avatar
Nate Summers committed
149
                GIMP_STOCK_TOOL_CURVES,
150
                data);
Michael Natterer's avatar
Michael Natterer committed
151 152
}

153 154 155

/*  private functions  */

Michael Natterer's avatar
Michael Natterer committed
156 157 158
static void
gimp_curves_tool_class_init (GimpCurvesToolClass *klass)
{
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
  GObjectClass          *object_class     = G_OBJECT_CLASS (klass);
  GimpToolClass         *tool_class       = GIMP_TOOL_CLASS (klass);
  GimpColorToolClass    *color_tool_class = GIMP_COLOR_TOOL_CLASS (klass);
  GimpImageMapToolClass *im_tool_class    = GIMP_IMAGE_MAP_TOOL_CLASS (klass);

  object_class->finalize           = gimp_curves_tool_finalize;

  tool_class->initialize           = gimp_curves_tool_initialize;
  tool_class->button_release       = gimp_curves_tool_button_release;
  tool_class->key_press            = gimp_curves_tool_key_press;
  tool_class->oper_update          = gimp_curves_tool_oper_update;

  color_tool_class->picked         = gimp_curves_tool_color_picked;

  im_tool_class->shell_desc        = _("Adjust Color Curves");
  im_tool_class->settings_name     = "curves";
  im_tool_class->load_dialog_title = _("Load Curves");
  im_tool_class->load_button_tip   = _("Load curves settings from file");
  im_tool_class->save_dialog_title = _("Save Curves");
  im_tool_class->save_button_tip   = _("Save curves settings to file");

  im_tool_class->map               = gimp_curves_tool_map;
  im_tool_class->dialog            = gimp_curves_tool_dialog;
  im_tool_class->reset             = gimp_curves_tool_reset;
  im_tool_class->settings_load     = gimp_curves_tool_settings_load;
  im_tool_class->settings_save     = gimp_curves_tool_settings_save;
Michael Natterer's avatar
Michael Natterer committed
185 186 187
}

static void
188
gimp_curves_tool_init (GimpCurvesTool *tool)
Michael Natterer's avatar
Michael Natterer committed
189
{
190
  gint i;
191

192 193 194
  tool->curves  = g_new0 (Curves, 1);
  tool->lut     = gimp_lut_new ();
  tool->channel = GIMP_HISTOGRAM_VALUE;
195

196
  curves_init (tool->curves);
197

198 199
  for (i = 0; i < G_N_ELEMENTS (tool->col_value); i++)
    tool->col_value[i] = -1;
200

201 202
  tool->cursor_x = -1;
  tool->cursor_y = -1;
Michael Natterer's avatar
Michael Natterer committed
203 204 205
}

static void
206
gimp_curves_tool_finalize (GObject *object)
Michael Natterer's avatar
Michael Natterer committed
207
{
208
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (object);
209

210
  if (tool->curves)
Michael Natterer's avatar
Michael Natterer committed
211
    {
212 213
      g_free (tool->curves);
      tool->curves = NULL;
Michael Natterer's avatar
Michael Natterer committed
214
    }
215
  if (tool->lut)
Michael Natterer's avatar
Michael Natterer committed
216
    {
217 218
      gimp_lut_free (tool->lut);
      tool->lut = NULL;
Michael Natterer's avatar
Michael Natterer committed
219
    }
220
  if (tool->hist)
221
    {
222 223
      gimp_histogram_free (tool->hist);
      tool->hist = NULL;
224
    }
225
  if (tool->cursor_layout)
Michael Natterer's avatar
Michael Natterer committed
226
    {
227 228
      g_object_unref (tool->cursor_layout);
      tool->cursor_layout = NULL;
Michael Natterer's avatar
Michael Natterer committed
229
    }
230
  if (tool->xpos_layout)
231
    {
232 233
      g_object_unref (tool->xpos_layout);
      tool->xpos_layout = NULL;
234
    }
Michael Natterer's avatar
Michael Natterer committed
235

236
  G_OBJECT_CLASS (parent_class)->finalize (object);
Michael Natterer's avatar
Michael Natterer committed
237 238
}

239
static gboolean
240
gimp_curves_tool_initialize (GimpTool    *tool,
241
                             GimpDisplay *display)
Michael Natterer's avatar
Michael Natterer committed
242
{
243
  GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
244 245
  GimpDrawable   *drawable;

246
  drawable = gimp_image_active_drawable (display->image);
247 248

  if (! drawable)
249
    return FALSE;
250

251
  if (gimp_drawable_is_indexed (drawable))
Michael Natterer's avatar
Michael Natterer committed
252
    {
253
      g_message (_("Curves for indexed layers cannot be adjusted."));
254
      return FALSE;
255
    }
Michael Natterer's avatar
Michael Natterer committed
256

257
  if (! c_tool->hist)
258
    c_tool->hist = gimp_histogram_new ();
259

260
  curves_init (c_tool->curves);
Michael Natterer's avatar
Michael Natterer committed
261

262
  c_tool->channel = GIMP_HISTOGRAM_VALUE;
263 264
  c_tool->color   = gimp_drawable_is_rgb (drawable);
  c_tool->alpha   = gimp_drawable_has_alpha (drawable);
265

266 267 268
  c_tool->selected = 0;
  c_tool->grabbed  = FALSE;
  c_tool->last     = 0;
269

270
  GIMP_TOOL_CLASS (parent_class)->initialize (tool, display);
271

272 273 274 275
  /*  always pick colors  */
  gimp_color_tool_enable (GIMP_COLOR_TOOL (tool),
                          GIMP_COLOR_OPTIONS (tool->tool_info->tool_options));

276 277
  gimp_int_combo_box_set_sensitivity (GIMP_INT_COMBO_BOX (c_tool->channel_menu),
                                      curves_menu_sensitivity, c_tool, NULL);
278

279 280
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (c_tool->channel_menu),
                                 c_tool->channel);
Michael Natterer's avatar
Michael Natterer committed
281

282 283 284
  /* FIXME: hack */
  if (! c_tool->color)
    c_tool->channel = (c_tool->channel == GIMP_HISTOGRAM_ALPHA) ? 1 : 0;
285

286 287 288
  gimp_drawable_calculate_histogram (drawable, c_tool->hist);
  gimp_histogram_view_set_histogram (GIMP_HISTOGRAM_VIEW (c_tool->graph),
                                     c_tool->hist);
289

290 291
  curves_update (c_tool, ALL);

292
  return TRUE;
Michael Natterer's avatar
Michael Natterer committed
293 294 295
}

static void
296 297 298
gimp_curves_tool_button_release (GimpTool        *tool,
                                 GimpCoords      *coords,
                                 guint32          time,
299
                                 GdkModifierType  state,
300
                                 GimpDisplay     *display)
Michael Natterer's avatar
Michael Natterer committed
301
{
302
  GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
303
  GimpDrawable   *drawable;
Michael Natterer's avatar
Michael Natterer committed
304

305
  drawable = gimp_image_active_drawable (display->image);
Michael Natterer's avatar
Michael Natterer committed
306

307
  if (state & GDK_SHIFT_MASK)
Michael Natterer's avatar
Michael Natterer committed
308
    {
309
      curves_add_point (c_tool, c_tool->channel);
310
      curves_calculate_curve (c_tool->curves, c_tool->channel);
311
      curves_update (c_tool, GRAPH | XRANGE);
Michael Natterer's avatar
Michael Natterer committed
312
    }
313
  else if (state & GDK_CONTROL_MASK)
Michael Natterer's avatar
Michael Natterer committed
314
    {
315
      gint i;
316

317 318
      for (i = 0; i < 5; i++)
        {
319
          curves_add_point (c_tool, i);
320 321
          curves_calculate_curve (c_tool->curves, c_tool->channel);
        }
322

323
      curves_update (c_tool, GRAPH | XRANGE);
Michael Natterer's avatar
Michael Natterer committed
324 325
    }

326 327
  /*  chain up to halt the tool */
  GIMP_TOOL_CLASS (parent_class)->button_release (tool,
328
                                                  coords, time, state, display);
Michael Natterer's avatar
Michael Natterer committed
329 330
}

331 332 333
gboolean
gimp_curves_tool_key_press (GimpTool    *tool,
                            GdkEventKey *kevent,
334
                            GimpDisplay *display)
335 336 337 338 339 340 341 342
{
  return curves_key_press (GIMP_CURVES_TOOL (tool), kevent);
}

static void
gimp_curves_tool_oper_update (GimpTool        *tool,
                              GimpCoords      *coords,
                              GdkModifierType  state,
343
                              gboolean         proximity,
344
                              GimpDisplay     *display)
345 346 347 348
{
  GimpColorPickMode  mode   = GIMP_COLOR_PICK_MODE_NONE;
  const gchar       *status = NULL;

349
  GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
350
                                               display);
351

352
  gimp_tool_pop_status (tool, display);
353 354 355 356 357 358 359 360 361 362 363 364 365 366

  if (state & GDK_SHIFT_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
      status = _("Click to add a control point.");
    }
  else if (state & GDK_CONTROL_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
      status = _("Click to add control points to all channels.");
    }

  GIMP_COLOR_TOOL (tool)->pick_mode = mode;

367
  if (status && proximity)
368
    gimp_tool_push_status (tool, display, status);
369 370
}

Michael Natterer's avatar
Michael Natterer committed
371
static void
372 373
gimp_curves_tool_color_picked (GimpColorTool      *color_tool,
                               GimpColorPickState  pick_state,
374 375 376
                               GimpImageType       sample_type,
                               GimpRGB            *color,
                               gint                color_index)
Michael Natterer's avatar
Michael Natterer committed
377
{
378
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (color_tool);
379
  GimpDrawable   *drawable;
380
  guchar          r, g, b, a;
Michael Natterer's avatar
Michael Natterer committed
381

382
  drawable = GIMP_IMAGE_MAP_TOOL (tool)->drawable;
383

384
  gimp_rgba_get_uchar (color, &r, &g, &b, &a);
385

386 387 388
  tool->col_value[GIMP_HISTOGRAM_RED]   = r;
  tool->col_value[GIMP_HISTOGRAM_GREEN] = g;
  tool->col_value[GIMP_HISTOGRAM_BLUE]  = b;
389

390
  if (gimp_drawable_has_alpha (drawable))
391
    tool->col_value[GIMP_HISTOGRAM_ALPHA] = a;
392

393
  if (gimp_drawable_is_indexed (drawable))
394
    tool->col_value[GIMP_HISTOGRAM_ALPHA] = color_index;
395

396
  tool->col_value[GIMP_HISTOGRAM_VALUE] = MAX (MAX (r, g), b);
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
397

398
  curves_update (tool, GRAPH);
399
}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
400

401
static void
402
curves_add_point (GimpCurvesTool *tool,
403
                  gint            channel)
404 405
{
  /* Add point onto the curve */
406 407 408 409
  gint closest_point = 0;
  gint distance;
  gint curvex;
  gint i;
410

411
  switch (tool->curves->curve_type[channel])
412
    {
413
    case GIMP_CURVE_SMOOTH:
414
      curvex   = tool->col_value[channel];
415
      distance = G_MAXINT;
416

417
      for (i = 0; i < CURVES_NUM_POINTS; i++)
418
        {
419 420
          if (tool->curves->points[channel][i][0] != -1)
            if (abs (curvex - tool->curves->points[channel][i][0]) < distance)
421
              {
422
                distance = abs (curvex - tool->curves->points[channel][i][0]);
423 424 425
                closest_point = i;
              }
        }
426

427
      if (distance > MIN_DISTANCE)
428
        closest_point = (curvex + 8) / 16;
429

430 431 432 433
      tool->curves->points[channel][closest_point][0] = curvex;
      tool->curves->points[channel][closest_point][1] = tool->curves->curve[channel][curvex];

      tool->selected = closest_point;
434
      break;
435

436
    case GIMP_CURVE_FREE:
437
      /* do nothing for free form curves */
438
      break;
439
    }
Elliot Lee's avatar
Elliot Lee committed
440 441
}

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
static gboolean
curves_key_press (GimpCurvesTool *tool,
                  GdkEventKey    *kevent)
{
  gint     i      = tool->selected;
  gint     y      = tool->curves->points[tool->channel][i][1];
  gboolean update = FALSE;

  if (tool->grabbed ||
      tool->curves->curve_type[tool->channel] == GIMP_CURVE_FREE)
    return FALSE;

  switch (kevent->keyval)
    {
    case GDK_Left:
      for (i = i - 1; i >= 0 && !update; i--)
        {
          if (tool->curves->points[tool->channel][i][0] != -1)
            {
              tool->selected = i;
              update = TRUE;
            }
        }
      break;

    case GDK_Right:
      for (i = i + 1; i < CURVES_NUM_POINTS && !update; i++)
        {
          if (tool->curves->points[tool->channel][i][0] != -1)
            {
              tool->selected = i;
              update = TRUE;
            }
        }
      break;

    case GDK_Up:
      if (y < 255)
        {
          y = y + (kevent->state & GDK_SHIFT_MASK ? 16 : 1);
          tool->curves->points[tool->channel][i][1] = MIN (y, 255);
          curves_calculate_curve (tool->curves, tool->channel);
          update = TRUE;
        }
      break;

    case GDK_Down:
      if (y > 0)
        {
          y = y - (kevent->state & GDK_SHIFT_MASK ? 16 : 1);
          tool->curves->points[tool->channel][i][1] = MAX (y, 0);
          curves_calculate_curve (tool->curves, tool->channel);
          update = TRUE;
        }
      break;

    default:
      break;
    }

  if (update)
    {
      curves_update (tool, GRAPH);
      gimp_image_map_tool_preview (GIMP_IMAGE_MAP_TOOL (tool));
    }

  return update;
}

511 512
static void
gimp_curves_tool_map (GimpImageMapTool *image_map_tool)
Elliot Lee's avatar
Elliot Lee committed
513
{
514
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (image_map_tool);
515

516
  gimp_lut_setup (tool->lut,
517
                  (GimpLutFunc) curves_lut_func,
518
                  tool->curves,
519
                  gimp_drawable_bytes (image_map_tool->drawable));
520

521
  gimp_image_map_apply (image_map_tool->image_map,
522
                        (GimpImageMapApplyFunc) gimp_lut_process,
523
                        tool->lut);
Elliot Lee's avatar
Elliot Lee committed
524 525
}

526

527 528 529
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
530

531 532
static void
gimp_curves_tool_dialog (GimpImageMapTool *image_map_tool)
Elliot Lee's avatar
Elliot Lee committed
533
{
534
  GimpCurvesTool  *tool = GIMP_CURVES_TOOL (image_map_tool);
535
  GimpToolOptions *tool_options;
536
  GtkListStore    *store;
537
  GtkWidget       *vbox;
538 539 540 541 542
  GtkWidget       *vbox2;
  GtkWidget       *hbox;
  GtkWidget       *hbox2;
  GtkWidget       *label;
  GtkWidget       *bbox;
543 544 545 546
  GtkWidget       *frame;
  GtkWidget       *menu;
  GtkWidget       *table;
  GtkWidget       *button;
547
  GtkWidget       *bar;
548
  gint             padding;
549

550 551
  tool_options = GIMP_TOOL (tool)->tool_info->tool_options;

552
  vbox = image_map_tool->main_vbox;
Elliot Lee's avatar
Elliot Lee committed
553

554
  /*  The option menu for selecting channels  */
555
  hbox = gtk_hbox_new (FALSE, 6);
556 557 558
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

559
  label = gtk_label_new_with_mnemonic (_("Cha_nnel:"));
560 561 562
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

563 564 565
  store = gimp_enum_store_new_with_range (GIMP_TYPE_HISTOGRAM_CHANNEL,
                                          GIMP_HISTOGRAM_VALUE,
                                          GIMP_HISTOGRAM_ALPHA);
566
  menu = gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (store));
567 568
  g_object_unref (store);

569 570 571 572 573
  g_signal_connect (menu, "changed",
                    G_CALLBACK (curves_channel_callback),
                    tool);
  gimp_enum_combo_box_set_stock_prefix (GIMP_ENUM_COMBO_BOX (menu),
                                        "gimp-channel");
574 575 576
  gtk_box_pack_start (GTK_BOX (hbox), menu, FALSE, FALSE, 0);
  gtk_widget_show (menu);

577
  tool->channel_menu = menu;
578

579 580
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), menu);

581
  button = gtk_button_new_with_mnemonic (_("R_eset Channel"));
582
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
583
  gtk_widget_show (button);
584

585
  g_signal_connect (button, "clicked",
586
                    G_CALLBACK (curves_channel_reset_callback),
587
                    tool);
Elliot Lee's avatar
Elliot Lee committed
588

589 590 591 592 593
  menu = gimp_prop_enum_stock_box_new (G_OBJECT (tool_options),
                                       "histogram-scale", "gimp-histogram",
                                       0, 0);
  gtk_box_pack_end (GTK_BOX (hbox), menu, FALSE, FALSE, 0);
  gtk_widget_show (menu);
594

595
  /*  The table for the color bars and the graph  */
Elliot Lee's avatar
Elliot Lee committed
596
  table = gtk_table_new (2, 2, FALSE);
597 598
  gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
599 600 601 602 603
  gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);

  /*  The left color bar  */
  vbox2 = gtk_vbox_new (FALSE, 0);
  gtk_table_attach (GTK_TABLE (table), vbox2, 0, 1, 0, 1,
604
                    GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
605
  gtk_widget_show (vbox2);
Elliot Lee's avatar
Elliot Lee committed
606 607

  frame = gtk_frame_new (NULL);
608
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
609
  gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
610 611
  gtk_widget_show (frame);

612 613 614 615
  tool->yrange = gimp_color_bar_new (GTK_ORIENTATION_VERTICAL);
  gtk_widget_set_size_request (tool->yrange, BAR_SIZE, -1);
  gtk_container_add (GTK_CONTAINER (frame), tool->yrange);
  gtk_widget_show (tool->yrange);
616

Elliot Lee's avatar
Elliot Lee committed
617 618
  /*  The curves graph  */
  frame = gtk_frame_new (NULL);
619
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
Elliot Lee's avatar
Elliot Lee committed
620
  gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1,
621
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
622
  gtk_widget_show (frame);
Elliot Lee's avatar
Elliot Lee committed
623

624 625 626 627
  tool->graph = gimp_histogram_view_new (FALSE);
  gtk_widget_set_size_request (tool->graph,
                               GRAPH_SIZE + RADIUS * 2,
                               GRAPH_SIZE + RADIUS * 2);
628
  GTK_WIDGET_SET_FLAGS (tool->graph, GTK_CAN_FOCUS);
629 630 631
  gtk_widget_add_events (tool->graph, (GDK_BUTTON_PRESS_MASK   |
                                       GDK_BUTTON_RELEASE_MASK |
                                       GDK_POINTER_MOTION_MASK |
632
                                       GDK_KEY_PRESS_MASK      |
633 634
                                       GDK_LEAVE_NOTIFY_MASK));
  g_object_set (tool->graph,
635 636 637
                "border-width", RADIUS,
                "subdivisions", 1,
                NULL);
638 639 640
  GIMP_HISTOGRAM_VIEW (tool->graph)->light_histogram = TRUE;
  gtk_container_add (GTK_CONTAINER (frame), tool->graph);
  gtk_widget_show (tool->graph);
641

642
  g_signal_connect (tool->graph, "event",
643 644
                    G_CALLBACK (curves_graph_events),
                    tool);
645
  g_signal_connect_after (tool->graph, "expose-event",
646
                          G_CALLBACK (curves_graph_expose),
647
                          tool);
Elliot Lee's avatar
Elliot Lee committed
648

649 650

  gimp_histogram_options_connect_view (GIMP_HISTOGRAM_OPTIONS (tool_options),
651 652 653 654 655
                                       GIMP_HISTOGRAM_VIEW (tool->graph));

  /*  The bottom color bar  */
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_table_attach (GTK_TABLE (table), hbox2, 1, 2, 1, 2,
656
                    GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
657
  gtk_widget_show (hbox2);
658

Elliot Lee's avatar
Elliot Lee committed
659
  frame = gtk_frame_new (NULL);
660
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
661
  gtk_box_pack_start (GTK_BOX (hbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
662 663
  gtk_widget_show (frame);

664 665 666 667
  vbox2 = gtk_vbox_new (TRUE, 0);
  gtk_container_add (GTK_CONTAINER (frame), vbox2);
  gtk_widget_show (vbox2);

668 669 670 671
  tool->xrange = gimp_color_bar_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_widget_set_size_request (tool->xrange, -1, BAR_SIZE / 2);
  gtk_box_pack_start (GTK_BOX (vbox2), tool->xrange, TRUE, TRUE, 0);
  gtk_widget_show (tool->xrange);
672

673 674 675 676
  bar = gimp_color_bar_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_box_pack_start (GTK_BOX (vbox2), bar, TRUE, TRUE, 0);
  gtk_widget_show (bar);

677
  gtk_widget_show (table);
Elliot Lee's avatar
Elliot Lee committed
678

679

680 681 682
  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
683

684
  /*  Horizontal button box for load / save */
685
  frame = gimp_frame_new (_("All Channels"));
686
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
687 688
  gtk_widget_show (frame);

689 690
  bbox = gtk_hbutton_box_new ();
  gtk_box_set_spacing (GTK_BOX (bbox), 4);
691
  gtk_container_add (GTK_CONTAINER (frame), bbox);
692
  gtk_widget_show (bbox);
693

694 695 696
  gtk_box_pack_start (GTK_BOX (bbox), image_map_tool->load_button,
                      FALSE, FALSE, 0);
  gtk_widget_show (image_map_tool->load_button);
697

698 699 700
  gtk_box_pack_start (GTK_BOX (bbox), image_map_tool->save_button,
                      FALSE, FALSE, 0);
  gtk_widget_show (image_map_tool->save_button);
701 702

  /*  The radio box for selecting the curve type  */
703
  frame = gimp_frame_new (_("Curve Type"));
704 705 706 707
  gtk_box_pack_end (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
  gtk_widget_show (frame);

  hbox = gimp_enum_stock_box_new (GIMP_TYPE_CURVE_TYPE,
708 709 710 711
                                  "gimp-curve", GTK_ICON_SIZE_MENU,
                                  G_CALLBACK (curves_curve_type_callback),
                                  tool,
                                  &tool->curve_type);
712

713
  gtk_widget_style_get (bbox, "child-internal-pad-x", &padding, NULL);
714 715

  gimp_enum_stock_box_set_child_padding (hbox, padding, -1);
716 717 718 719 720 721

  gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
  gtk_box_set_spacing (GTK_BOX (hbox), 4);

  gtk_container_add (GTK_CONTAINER (frame), hbox);
  gtk_widget_show (hbox);
Elliot Lee's avatar
Elliot Lee committed
722 723
}

724
static void
725
gimp_curves_tool_reset (GimpImageMapTool *image_map_tool)
726
{
727
  GimpCurvesTool       *tool = GIMP_CURVES_TOOL (image_map_tool);
728
  GimpHistogramChannel  channel;
729

730
  tool->grabbed = FALSE;
731

732 733 734
  for (channel =  GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
735
    curves_channel_reset (tool->curves, channel);
736

737
  curves_update (tool, XRANGE | GRAPH);
738 739
}

740
static gboolean
741 742
gimp_curves_tool_settings_load (GimpImageMapTool *image_map_tool,
                                gpointer          fp)
743
{
744 745 746 747 748
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (image_map_tool);
  FILE           *file = fp;
  gint            i, j;
  gint            fields;
  gchar           buf[50];
749 750
  gint            index[5][CURVES_NUM_POINTS];
  gint            value[5][CURVES_NUM_POINTS];
751 752 753 754 755 756 757 758 759

  if (! fgets (buf, sizeof (buf), file))
    return FALSE;

  if (strcmp (buf, "# GIMP Curves File\n") != 0)
    return FALSE;

  for (i = 0; i < 5; i++)
    {
760
      for (j = 0; j < CURVES_NUM_POINTS; j++)
761 762 763 764
        {
          fields = fscanf (file, "%d %d ", &index[i][j], &value[i][j]);
          if (fields != 2)
            {
765
              /*  FIXME: should have a helpful error message here  */
766 767 768 769
              g_printerr ("fields != 2");
              return FALSE;
            }
        }
770 771 772 773 774 775
    }

  for (i = 0; i < 5; i++)
    {
      tool->curves->curve_type[i] = GIMP_CURVE_SMOOTH;

776
      for (j = 0; j < CURVES_NUM_POINTS; j++)
777 778 779 780
        {
          tool->curves->points[i][j][0] = index[i][j];
          tool->curves->points[i][j][1] = value[i][j];
        }
781 782 783 784 785 786 787 788
    }

  for (i = 0; i < 5; i++)
    curves_calculate_curve (tool->curves, i);

  curves_update (tool, ALL);

  gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (tool->curve_type),
789
                                   GIMP_CURVE_SMOOTH);
790 791

  return TRUE;
792 793 794
}

static gboolean
795 796
gimp_curves_tool_settings_save (GimpImageMapTool *image_map_tool,
                                gpointer          fp)
797
{
798 799 800 801 802 803 804 805
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (image_map_tool);
  FILE           *file = fp;
  gint            i, j;
  gint32          index;

  for (i = 0; i < 5; i++)
    if (tool->curves->curve_type[i] == GIMP_CURVE_FREE)
      {
806
        /*  pick representative points from the curve
807
            and make them control points  */
808 809 810 811 812 813
        for (j = 0; j <= 8; j++)
          {
            index = CLAMP0255 (j * 32);
            tool->curves->points[i][j * 2][0] = index;
            tool->curves->points[i][j * 2][1] = tool->curves->curve[i][index];
          }
814 815 816 817 818 819
      }

  fprintf (file, "# GIMP Curves File\n");

  for (i = 0; i < 5; i++)
    {
820
      for (j = 0; j < CURVES_NUM_POINTS; j