gimpcurvestool.c 42.6 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 245
  GimpCurvesTool *c_tool   = GIMP_CURVES_TOOL (tool);
  GimpDrawable   *drawable = gimp_image_get_active_drawable (display->image);
246 247

  if (! drawable)
248
    return FALSE;
249

250
  if (gimp_drawable_is_indexed (drawable))
Michael Natterer's avatar
Michael Natterer committed
251
    {
252
      g_set_error (error, 0, 0,
253
                   _("Curves does not operate on indexed layers."));
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, error);
271

272 273
  /*  always pick colors  */
  gimp_color_tool_enable (GIMP_COLOR_TOOL (tool),
274
                          GIMP_COLOR_TOOL_GET_OPTIONS (tool));
275

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 299 300 301
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
302
{
303
  GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
Michael Natterer's avatar
Michael Natterer committed
304

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

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

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

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

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

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

347
  GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
348
                                               display);
349

350
  gimp_tool_pop_status (tool, display);
351 352 353 354

  if (state & GDK_SHIFT_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
355
      status = _("Click to add a control point");
356 357 358 359
    }
  else if (state & GDK_CONTROL_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
360
      status = _("Click to add control points to all channels");
361 362 363 364
    }

  GIMP_COLOR_TOOL (tool)->pick_mode = mode;

365
  if (status && proximity)
366
    gimp_tool_push_status (tool, display, status);
367 368
}

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

380
  drawable = GIMP_IMAGE_MAP_TOOL (tool)->drawable;
381

382
  gimp_rgba_get_uchar (color, &r, &g, &b, &a);
383

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

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

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

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

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

399
static void
400
curves_add_point (GimpCurvesTool *tool,
401
                  gint            channel)
402
{
403 404 405 406 407
  Curves *curves        = tool->curves;
  gint    closest_point = 0;
  gint    distance;
  gint    curvex;
  gint    i;
408

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

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

425
      if (distance > MIN_DISTANCE)
426
        closest_point = (curvex + 8) / 16;
427

428 429
      curves->points[channel][closest_point][0] = curvex;
      curves->points[channel][closest_point][1] = curves->curve[channel][curvex];
430 431

      tool->selected = closest_point;
432
      break;
433

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

440 441 442 443
static gboolean
curves_key_press (GimpCurvesTool *tool,
                  GdkEventKey    *kevent)
{
444
  Curves  *curves = tool->curves;
445
  gint     i      = tool->selected;
446
  gint     y      = curves->points[tool->channel][i][1];
447 448
  gboolean update = FALSE;

449
  if (tool->grabbed || curves->curve_type[tool->channel] == GIMP_CURVE_FREE)
450 451 452 453 454 455 456
    return FALSE;

  switch (kevent->keyval)
    {
    case GDK_Left:
      for (i = i - 1; i >= 0 && !update; i--)
        {
457
          if (curves->points[tool->channel][i][0] != -1)
458 459 460 461 462 463 464 465 466 467
            {
              tool->selected = i;
              update = TRUE;
            }
        }
      break;

    case GDK_Right:
      for (i = i + 1; i < CURVES_NUM_POINTS && !update; i++)
        {
468
          if (curves->points[tool->channel][i][0] != -1)
469 470 471 472 473 474 475 476 477 478 479
            {
              tool->selected = i;
              update = TRUE;
            }
        }
      break;

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

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

    default:
      break;
    }

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

  return update;
}

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

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

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

524

525 526 527
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
528

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

548
  vbox = image_map_tool->main_vbox;
Elliot Lee's avatar
Elliot Lee committed
549

550
  /*  The option menu for selecting channels  */
551
  hbox = gtk_hbox_new (FALSE, 6);
552 553 554
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

555
  label = gtk_label_new_with_mnemonic (_("Cha_nnel:"));
556 557 558
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

559 560 561
  store = gimp_enum_store_new_with_range (GIMP_TYPE_HISTOGRAM_CHANNEL,
                                          GIMP_HISTOGRAM_VALUE,
                                          GIMP_HISTOGRAM_ALPHA);
562
  menu = gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (store));
563 564
  g_object_unref (store);

565 566 567 568 569
  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");
570 571 572
  gtk_box_pack_start (GTK_BOX (hbox), menu, FALSE, FALSE, 0);
  gtk_widget_show (menu);

573
  tool->channel_menu = menu;
574

575 576
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), menu);

577
  button = gtk_button_new_with_mnemonic (_("R_eset Channel"));
578
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
579
  gtk_widget_show (button);
580

581
  g_signal_connect (button, "clicked",
582
                    G_CALLBACK (curves_channel_reset_callback),
583
                    tool);
Elliot Lee's avatar
Elliot Lee committed
584

585 586 587 588 589
  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);
590

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

  frame = gtk_frame_new (NULL);
604
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
605
  gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
606 607
  gtk_widget_show (frame);

608 609 610 611
  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);
612

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

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

638
  g_signal_connect (tool->graph, "event",
639 640
                    G_CALLBACK (curves_graph_events),
                    tool);
641
  g_signal_connect_after (tool->graph, "expose-event",
642
                          G_CALLBACK (curves_graph_expose),
643
                          tool);
Elliot Lee's avatar
Elliot Lee committed
644

645 646

  gimp_histogram_options_connect_view (GIMP_HISTOGRAM_OPTIONS (tool_options),
647 648 649 650 651
                                       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,
652
                    GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
653
  gtk_widget_show (hbox2);
654

Elliot Lee's avatar
Elliot Lee committed
655
  frame = gtk_frame_new (NULL);
656
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
657
  gtk_box_pack_start (GTK_BOX (hbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
658 659
  gtk_widget_show (frame);

660 661 662 663
  vbox2 = gtk_vbox_new (TRUE, 0);
  gtk_container_add (GTK_CONTAINER (frame), vbox2);
  gtk_widget_show (vbox2);

664 665 666 667
  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);
668

669 670 671 672
  bar = gimp_color_bar_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_box_pack_start (GTK_BOX (vbox2), bar, TRUE, TRUE, 0);
  gtk_widget_show (bar);

673
  gtk_widget_show (table);
Elliot Lee's avatar
Elliot Lee committed
674

675

676 677 678
  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
679

680
  /*  Horizontal button box for load / save */
681
  frame = gimp_frame_new (_("All Channels"));
682
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
683 684
  gtk_widget_show (frame);

685 686
  bbox = gtk_hbutton_box_new ();
  gtk_box_set_spacing (GTK_BOX (bbox), 4);
687
  gtk_container_add (GTK_CONTAINER (frame), bbox);
688
  gtk_widget_show (bbox);
689

690 691 692
  gtk_box_pack_start (GTK_BOX (bbox), image_map_tool->load_button,
                      FALSE, FALSE, 0);
  gtk_widget_show (image_map_tool->load_button);
693

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

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

709
  gtk_widget_style_get (bbox, "child-internal-pad-x", &padding, NULL);
710 711

  gimp_enum_stock_box_set_child_padding (hbox, padding, -1);
712 713 714 715 716

  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
717 718
}

719
static void
720
gimp_curves_tool_reset (GimpImageMapTool *image_map_tool)
721
{
722
  GimpCurvesTool       *tool = GIMP_CURVES_TOOL (image_map_tool);
723
  GimpHistogramChannel  channel;
724

725
  tool->grabbed = FALSE;
726

727 728 729
  for (channel =  GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
730
    curves_channel_reset (tool->curves, channel);
731

732
  curves_update (tool, XRANGE | GRAPH);
733 734
}

735
static gboolean
736 737 738
gimp_curves_tool_settings_load (GimpImageMapTool  *image_map_tool,
                                gpointer           fp,
                                GError           **error)
739
{
740 741 742
  GimpCurvesTool *tool   = GIMP_CURVES_TOOL (image_map_tool);
  Curves         *curves = tool->curves;
  FILE           *file   = fp;
743 744 745
  gint            i, j;
  gint            fields;
  gchar           buf[50];
746 747
  gint            index[5][CURVES_NUM_POINTS];
  gint            value[5][CURVES_NUM_POINTS];
748

749 750 751 752 753 754 755
  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;
    }
756 757 758

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

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

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

  for (i = 0; i < 5; i++)
785
    curves_calculate_curve (curves, i);
786 787 788 789

  curves_update (tool, ALL);

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

  return TRUE;
793 794 795
}

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

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

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

  for (i = 0; i < 5; i++)
    {
822
      for (j = 0; j < CURVES_NUM_POINTS; j++)
823
        fprintf (file, "%d %d ",
824 825
                 curves->points[i][j][0],
                 curves->points[i][j][1]);
826 827 828 829 830

      fprintf (file, "\n");
    }

  return TRUE;
831
}