gimpcurvestool.c 42.7 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15
 * 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 "libgimpmath/gimpmath.h"
29
#include "libgimpcolor/gimpcolor.h"
30
#include "libgimpconfig/gimpconfig.h"
31
#include "libgimpwidgets/gimpwidgets.h"
32

33
#include "tools-types.h"
Michael Natterer's avatar
Michael Natterer committed
34

35
#include "config/gimpguiconfig.h"
36

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

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

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

53 54
#include "display/gimpdisplay.h"

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

59
#include "gimp-intl.h"
60

61

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

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


Michael Natterer's avatar
Michael Natterer committed
73 74
/*  local function prototypes  */

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
static void     gimp_curves_tool_finalize       (GObject              *object);

static gboolean gimp_curves_tool_initialize     (GimpTool             *tool,
                                                 GimpDisplay          *display,
                                                 GError              **error);
static void     gimp_curves_tool_button_release (GimpTool             *tool,
                                                 GimpCoords           *coords,
                                                 guint32               time,
                                                 GdkModifierType       state,
                                                 GimpButtonReleaseType release_type,
                                                 GimpDisplay          *display);
static gboolean gimp_curves_tool_key_press      (GimpTool             *tool,
                                                 GdkEventKey          *kevent,
                                                 GimpDisplay          *display);
static void     gimp_curves_tool_oper_update    (GimpTool             *tool,
                                                 GimpCoords           *coords,
                                                 GdkModifierType       state,
                                                 gboolean              proximity,
                                                 GimpDisplay          *display);

static void     gimp_curves_tool_color_picked   (GimpColorTool        *color_tool,
                                                 GimpColorPickState    pick_state,
                                                 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);
static gboolean gimp_curves_tool_settings_load  (GimpImageMapTool     *image_map_tool,
                                                 gpointer              fp,
                                                 GError              **error);
static gboolean gimp_curves_tool_settings_save  (GimpImageMapTool     *image_map_tool,
                                                 gpointer              fp);

static void     curves_add_point                (GimpCurvesTool       *tool,
                                                 gint                  channel);
static gboolean curves_key_press                (GimpCurvesTool       *tool,
                                                 GdkEventKey          *kevent);
static void     curves_update                   (GimpCurvesTool       *tool,
                                                 gint                  update);

static void     curves_channel_callback         (GtkWidget            *widget,
                                                 GimpCurvesTool       *tool);
static void     curves_channel_reset_callback   (GtkWidget            *widget,
                                                 GimpCurvesTool       *tool);

static gboolean curves_menu_sensitivity         (gint                  value,
                                                 gpointer              data);

static void     curves_curve_type_callback      (GtkWidget            *widget,
                                                 GimpCurvesTool       *tool);
static gboolean curves_graph_events             (GtkWidget            *widget,
                                                 GdkEvent             *event,
                                                 GimpCurvesTool       *tool);
static gboolean curves_graph_expose             (GtkWidget            *widget,
                                                 GdkEventExpose       *eevent,
                                                 GimpCurvesTool       *tool);
132

Michael Natterer's avatar
Michael Natterer committed
133

134
G_DEFINE_TYPE (GimpCurvesTool, gimp_curves_tool, GIMP_TYPE_IMAGE_MAP_TOOL)
135 136

#define parent_class gimp_curves_tool_parent_class
137

Michael Natterer's avatar
Michael Natterer committed
138

139
/*  public functions  */
Michael Natterer's avatar
Michael Natterer committed
140 141

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

158 159 160

/*  private functions  */

Michael Natterer's avatar
Michael Natterer committed
161 162 163
static void
gimp_curves_tool_class_init (GimpCurvesToolClass *klass)
{
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
  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
190 191 192
}

static void
193
gimp_curves_tool_init (GimpCurvesTool *tool)
Michael Natterer's avatar
Michael Natterer committed
194
{
195
  gint i;
196

197
  tool->curves  = g_slice_new0 (Curves);
198 199
  tool->lut     = gimp_lut_new ();
  tool->channel = GIMP_HISTOGRAM_VALUE;
200

201
  curves_init (tool->curves);
202

203 204
  for (i = 0; i < G_N_ELEMENTS (tool->col_value); i++)
    tool->col_value[i] = -1;
205

206 207
  tool->cursor_x = -1;
  tool->cursor_y = -1;
Michael Natterer's avatar
Michael Natterer committed
208 209 210
}

static void
211
gimp_curves_tool_finalize (GObject *object)
Michael Natterer's avatar
Michael Natterer committed
212
{
213
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (object);
214

215 216 217
  g_slice_free (Curves, tool->curves);
  gimp_lut_free (tool->lut);

218
  if (tool->hist)
219
    {
220 221
      gimp_histogram_free (tool->hist);
      tool->hist = NULL;
222
    }
223

224
  if (tool->cursor_layout)
Michael Natterer's avatar
Michael Natterer committed
225
    {
226 227
      g_object_unref (tool->cursor_layout);
      tool->cursor_layout = NULL;
Michael Natterer's avatar
Michael Natterer committed
228
    }
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 241 242
gimp_curves_tool_initialize (GimpTool     *tool,
                             GimpDisplay  *display,
                             GError      **error)
Michael Natterer's avatar
Michael Natterer committed
243
{
244
  GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
245 246
  GimpDrawable   *drawable;

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

  if (! drawable)
250
    return FALSE;
251

252
  if (gimp_drawable_is_indexed (drawable))
Michael Natterer's avatar
Michael Natterer committed
253
    {
254
      g_set_error (error, 0, 0,
255
                   _("Curves does not operate on indexed layers."));
256
      return FALSE;
257
    }
Michael Natterer's avatar
Michael Natterer committed
258

259
  if (! c_tool->hist)
260
    c_tool->hist = gimp_histogram_new ();
261

262
  curves_init (c_tool->curves);
Michael Natterer's avatar
Michael Natterer committed
263

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

268 269 270
  c_tool->selected = 0;
  c_tool->grabbed  = FALSE;
  c_tool->last     = 0;
271

272
  GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error);
273

274 275
  /*  always pick colors  */
  gimp_color_tool_enable (GIMP_COLOR_TOOL (tool),
276
                          GIMP_COLOR_TOOL_GET_OPTIONS (tool));
277

278 279
  gimp_int_combo_box_set_sensitivity (GIMP_INT_COMBO_BOX (c_tool->channel_menu),
                                      curves_menu_sensitivity, c_tool, NULL);
280

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

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

288 289 290
  gimp_drawable_calculate_histogram (drawable, c_tool->hist);
  gimp_histogram_view_set_histogram (GIMP_HISTOGRAM_VIEW (c_tool->graph),
                                     c_tool->hist);
291

292 293
  curves_update (c_tool, ALL);

294
  return TRUE;
Michael Natterer's avatar
Michael Natterer committed
295 296 297
}

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

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

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

320 321
      for (i = 0; i < 5; i++)
        {
322
          curves_add_point (c_tool, i);
323 324
          curves_calculate_curve (c_tool->curves, c_tool->channel);
        }
325

326
      curves_update (c_tool, GRAPH | XRANGE);
Michael Natterer's avatar
Michael Natterer committed
327 328
    }

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

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

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

352
  GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
353
                                               display);
354

355
  gimp_tool_pop_status (tool, display);
356 357 358 359

  if (state & GDK_SHIFT_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
360
      status = _("Click to add a control point");
361 362 363 364
    }
  else if (state & GDK_CONTROL_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
365
      status = _("Click to add control points to all channels");
366 367 368 369
    }

  GIMP_COLOR_TOOL (tool)->pick_mode = mode;

370
  if (status && proximity)
371
    gimp_tool_push_status (tool, display, status);
372 373
}

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

385
  drawable = GIMP_IMAGE_MAP_TOOL (tool)->drawable;
386

387
  gimp_rgba_get_uchar (color, &r, &g, &b, &a);
388

389 390 391
  tool->col_value[GIMP_HISTOGRAM_RED]   = r;
  tool->col_value[GIMP_HISTOGRAM_GREEN] = g;
  tool->col_value[GIMP_HISTOGRAM_BLUE]  = b;
392

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

396
  if (gimp_drawable_is_indexed (drawable))
397
    tool->col_value[GIMP_HISTOGRAM_ALPHA] = color_index;
398

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

401
  curves_update (tool, GRAPH);
402
}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
403

404
static void
405
curves_add_point (GimpCurvesTool *tool,
406
                  gint            channel)
407
{
408 409 410 411 412
  Curves *curves        = tool->curves;
  gint    closest_point = 0;
  gint    distance;
  gint    curvex;
  gint    i;
413

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

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

430
      if (distance > MIN_DISTANCE)
431
        closest_point = (curvex + 8) / 16;
432

433 434
      curves->points[channel][closest_point][0] = curvex;
      curves->points[channel][closest_point][1] = curves->curve[channel][curvex];
435 436

      tool->selected = closest_point;
437
      break;
438

439
    case GIMP_CURVE_FREE:
440
      /* do nothing for free form curves */
441
      break;
442
    }
Elliot Lee's avatar
Elliot Lee committed
443 444
}

445 446 447 448
static gboolean
curves_key_press (GimpCurvesTool *tool,
                  GdkEventKey    *kevent)
{
449
  Curves  *curves = tool->curves;
450
  gint     i      = tool->selected;
451
  gint     y      = curves->points[tool->channel][i][1];
452 453
  gboolean update = FALSE;

454
  if (tool->grabbed || curves->curve_type[tool->channel] == GIMP_CURVE_FREE)
455 456 457 458 459 460 461
    return FALSE;

  switch (kevent->keyval)
    {
    case GDK_Left:
      for (i = i - 1; i >= 0 && !update; i--)
        {
462
          if (curves->points[tool->channel][i][0] != -1)
463 464 465 466 467 468 469 470 471 472
            {
              tool->selected = i;
              update = TRUE;
            }
        }
      break;

    case GDK_Right:
      for (i = i + 1; i < CURVES_NUM_POINTS && !update; i++)
        {
473
          if (curves->points[tool->channel][i][0] != -1)
474 475 476 477 478 479 480 481 482 483 484
            {
              tool->selected = i;
              update = TRUE;
            }
        }
      break;

    case GDK_Up:
      if (y < 255)
        {
          y = y + (kevent->state & GDK_SHIFT_MASK ? 16 : 1);
485 486
          curves->points[tool->channel][i][1] = MIN (y, 255);
          curves_calculate_curve (curves, tool->channel);
487 488 489 490 491 492 493 494
          update = TRUE;
        }
      break;

    case GDK_Down:
      if (y > 0)
        {
          y = y - (kevent->state & GDK_SHIFT_MASK ? 16 : 1);
495 496
          curves->points[tool->channel][i][1] = MAX (y, 0);
          curves_calculate_curve (curves, tool->channel);
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513
          update = TRUE;
        }
      break;

    default:
      break;
    }

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

  return update;
}

514 515
static void
gimp_curves_tool_map (GimpImageMapTool *image_map_tool)
Elliot Lee's avatar
Elliot Lee committed
516
{
517
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (image_map_tool);
518

519
  gimp_lut_setup (tool->lut,
520
                  (GimpLutFunc) curves_lut_func,
521
                  tool->curves,
522
                  gimp_drawable_bytes (image_map_tool->drawable));
523

524 525 526
  gimp_image_map_apply (image_map_tool->image_map,
                        (GimpImageMapApplyFunc) gimp_lut_process,
                        tool->lut);
Elliot Lee's avatar
Elliot Lee committed
527 528
}

529

530 531 532
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
533

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

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

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

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

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

570 571 572 573 574
  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");
575 576 577
  gtk_box_pack_start (GTK_BOX (hbox), menu, FALSE, FALSE, 0);
  gtk_widget_show (menu);

578
  tool->channel_menu = menu;
579

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

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

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

590 591 592 593 594
  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);
595

596
  /*  The table for the color bars and the graph  */
Elliot Lee's avatar
Elliot Lee committed
597
  table = gtk_table_new (2, 2, FALSE);
598 599
  gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
600 601 602 603 604
  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,
605
                    GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
606
  gtk_widget_show (vbox2);
Elliot Lee's avatar
Elliot Lee committed
607 608

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

613 614 615 616
  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);
617

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

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

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

650 651

  gimp_histogram_options_connect_view (GIMP_HISTOGRAM_OPTIONS (tool_options),
652 653 654 655 656
                                       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,
657
                    GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
658
  gtk_widget_show (hbox2);
659

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

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

669 670 671 672
  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);
673

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

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

680

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

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

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

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

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

  /*  The radio box for selecting the curve type  */
704
  frame = gimp_frame_new (_("Curve Type"));
705 706 707 708
  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,
709 710 711 712
                                  "gimp-curve", GTK_ICON_SIZE_MENU,
                                  G_CALLBACK (curves_curve_type_callback),
                                  tool,
                                  &tool->curve_type);
713

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

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

  gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);

  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 743
gimp_curves_tool_settings_load (GimpImageMapTool  *image_map_tool,
                                gpointer           fp,
                                GError           **error)
744
{
745 746 747
  GimpCurvesTool *tool   = GIMP_CURVES_TOOL (image_map_tool);
  Curves         *curves = tool->curves;
  FILE           *file   = fp;
748 749 750
  gint            i, j;
  gint            fields;
  gchar           buf[50];
751 752
  gint            index[5][CURVES_NUM_POINTS];
  gint            value[5][CURVES_NUM_POINTS];
753

754 755 756 757 758 759 760
  if (! fgets (buf, sizeof (buf), file) ||
      strcmp (buf, "# GIMP Curves File\n") != 0)
    {
      g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
                   _("not a GIMP Levels file"));
      return FALSE;
    }
761 762 763

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

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

782
      for (j = 0; j < CURVES_NUM_POINTS; j++)
783
        {
784 785
          curves->points[i][j][0] = index[i][j];
          curves->points[i][j][1] = value[i][j];
786
        }
787 788 789
    }

  for (i = 0; i < 5; i++)
790
    curves_calculate_curve (curves, i);
791 792 793 794

  curves_update (tool, ALL);

  gimp_int_radio_group_set_active (GTK_RADIO_BUTTON (tool->curve_type),
795
                                   GIMP_CURVE_SMOOTH);
796 797

  return TRUE;
798 799 800
}

static gboolean
801 802
gimp_curves_tool_settings_save (GimpImageMapTool *image_map_tool,
                                gpointer          fp)
803
{
804 805 806
  GimpCurvesTool *tool   = GIMP_CURVES_TOOL (image_map_tool);
  Curves         *curves = tool->curves;
  FILE           *file   = fp;
807 808 809 810
  gint            i, j;
  gint32          index;

  for (i = 0; i < 5; i++)
811
    if (curves->curve_type[i] == GIMP_CURVE_FREE)
812
      {
813
        /*  pick representative points from the curve
814
            and make them control points  */
815 816 817
        for (j = 0; j <= 8; j++)
          {
            index = CLAMP0255 (j * 32);
818 819
            curves->points[i][j * 2][0] = index;
            curves->points[i][j * 2][1] = curves->curve[i][index];
820
          }
821 822 823 824 825 826
      }

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

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