gimpcurvestool.c 43 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 198 199
  tool->curves  = g_new0 (Curves, 1);
  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
  if (tool->curves)
Michael Natterer's avatar
Michael Natterer committed
216
    {
217 218
      g_free (tool->curves);
      tool->curves = NULL;
Michael Natterer's avatar
Michael Natterer committed
219
    }
220
  if (tool->lut)
Michael Natterer's avatar
Michael Natterer committed
221
    {
222 223
      gimp_lut_free (tool->lut);
      tool->lut = NULL;
Michael Natterer's avatar
Michael Natterer committed
224
    }
225
  if (tool->hist)
226
    {
227 228
      gimp_histogram_free (tool->hist);
      tool->hist = NULL;
229
    }
230
  if (tool->cursor_layout)
Michael Natterer's avatar
Michael Natterer committed
231
    {
232 233
      g_object_unref (tool->cursor_layout);
      tool->cursor_layout = NULL;
Michael Natterer's avatar
Michael Natterer committed
234
    }
235
  if (tool->xpos_layout)
236
    {
237 238
      g_object_unref (tool->xpos_layout);
      tool->xpos_layout = NULL;
239
    }
Michael Natterer's avatar
Michael Natterer committed
240

241
  G_OBJECT_CLASS (parent_class)->finalize (object);
Michael Natterer's avatar
Michael Natterer committed
242 243
}

244
static gboolean
245 246 247
gimp_curves_tool_initialize (GimpTool     *tool,
                             GimpDisplay  *display,
                             GError      **error)
Michael Natterer's avatar
Michael Natterer committed
248
{
249
  GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
250 251
  GimpDrawable   *drawable;

252
  drawable = gimp_image_active_drawable (display->image);
253 254

  if (! drawable)
255
    return FALSE;
256

257
  if (gimp_drawable_is_indexed (drawable))
Michael Natterer's avatar
Michael Natterer committed
258
    {
259
      g_set_error (error, 0, 0,
260
                   _("Curves does not operate on indexed layers."));
261
      return FALSE;
262
    }
Michael Natterer's avatar
Michael Natterer committed
263

264
  if (! c_tool->hist)
265
    c_tool->hist = gimp_histogram_new ();
266

267
  curves_init (c_tool->curves);
Michael Natterer's avatar
Michael Natterer committed
268

269
  c_tool->channel = GIMP_HISTOGRAM_VALUE;
270 271
  c_tool->color   = gimp_drawable_is_rgb (drawable);
  c_tool->alpha   = gimp_drawable_has_alpha (drawable);
272

273 274 275
  c_tool->selected = 0;
  c_tool->grabbed  = FALSE;
  c_tool->last     = 0;
276

277
  GIMP_TOOL_CLASS (parent_class)->initialize (tool, display, error);
278

279 280
  /*  always pick colors  */
  gimp_color_tool_enable (GIMP_COLOR_TOOL (tool),
281
                          GIMP_COLOR_TOOL_GET_OPTIONS (tool));
282

283 284
  gimp_int_combo_box_set_sensitivity (GIMP_INT_COMBO_BOX (c_tool->channel_menu),
                                      curves_menu_sensitivity, c_tool, NULL);
285

286 287
  gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (c_tool->channel_menu),
                                 c_tool->channel);
Michael Natterer's avatar
Michael Natterer committed
288

289 290 291
  /* FIXME: hack */
  if (! c_tool->color)
    c_tool->channel = (c_tool->channel == GIMP_HISTOGRAM_ALPHA) ? 1 : 0;
292

293 294 295
  gimp_drawable_calculate_histogram (drawable, c_tool->hist);
  gimp_histogram_view_set_histogram (GIMP_HISTOGRAM_VIEW (c_tool->graph),
                                     c_tool->hist);
296

297 298
  curves_update (c_tool, ALL);

299
  return TRUE;
Michael Natterer's avatar
Michael Natterer committed
300 301 302
}

static void
303 304 305 306 307 308
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
309
{
310
  GimpCurvesTool *c_tool = GIMP_CURVES_TOOL (tool);
311
  GimpDrawable   *drawable;
Michael Natterer's avatar
Michael Natterer committed
312

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

315
  if (state & GDK_SHIFT_MASK)
Michael Natterer's avatar
Michael Natterer committed
316
    {
317
      curves_add_point (c_tool, c_tool->channel);
318
      curves_calculate_curve (c_tool->curves, c_tool->channel);
319
      curves_update (c_tool, GRAPH | XRANGE);
Michael Natterer's avatar
Michael Natterer committed
320
    }
321
  else if (state & GDK_CONTROL_MASK)
Michael Natterer's avatar
Michael Natterer committed
322
    {
323
      gint i;
324

325 326
      for (i = 0; i < 5; i++)
        {
327
          curves_add_point (c_tool, i);
328 329
          curves_calculate_curve (c_tool->curves, c_tool->channel);
        }
330

331
      curves_update (c_tool, GRAPH | XRANGE);
Michael Natterer's avatar
Michael Natterer committed
332 333
    }

334
  /*  chain up to halt the tool */
335 336
  GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
                                                  release_type, display);
Michael Natterer's avatar
Michael Natterer committed
337 338
}

339 340 341
gboolean
gimp_curves_tool_key_press (GimpTool    *tool,
                            GdkEventKey *kevent,
342
                            GimpDisplay *display)
343 344 345 346 347 348 349 350
{
  return curves_key_press (GIMP_CURVES_TOOL (tool), kevent);
}

static void
gimp_curves_tool_oper_update (GimpTool        *tool,
                              GimpCoords      *coords,
                              GdkModifierType  state,
351
                              gboolean         proximity,
352
                              GimpDisplay     *display)
353 354 355 356
{
  GimpColorPickMode  mode   = GIMP_COLOR_PICK_MODE_NONE;
  const gchar       *status = NULL;

357
  GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
358
                                               display);
359

360
  gimp_tool_pop_status (tool, display);
361 362 363 364

  if (state & GDK_SHIFT_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
365
      status = _("Click to add a control point");
366 367 368 369
    }
  else if (state & GDK_CONTROL_MASK)
    {
      mode   = GIMP_COLOR_PICK_MODE_PALETTE;
370
      status = _("Click to add control points to all channels");
371 372 373 374
    }

  GIMP_COLOR_TOOL (tool)->pick_mode = mode;

375
  if (status && proximity)
376
    gimp_tool_push_status (tool, display, status);
377 378
}

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

390
  drawable = GIMP_IMAGE_MAP_TOOL (tool)->drawable;
391

392
  gimp_rgba_get_uchar (color, &r, &g, &b, &a);
393

394 395 396
  tool->col_value[GIMP_HISTOGRAM_RED]   = r;
  tool->col_value[GIMP_HISTOGRAM_GREEN] = g;
  tool->col_value[GIMP_HISTOGRAM_BLUE]  = b;
397

398
  if (gimp_drawable_has_alpha (drawable))
399
    tool->col_value[GIMP_HISTOGRAM_ALPHA] = a;
400

401
  if (gimp_drawable_is_indexed (drawable))
402
    tool->col_value[GIMP_HISTOGRAM_ALPHA] = color_index;
403

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

406
  curves_update (tool, GRAPH);
407
}
BST 1999 Andy Thomas's avatar
BST 1999 Andy Thomas committed
408

409
static void
410
curves_add_point (GimpCurvesTool *tool,
411
                  gint            channel)
412 413
{
  /* Add point onto the curve */
414 415 416 417
  gint closest_point = 0;
  gint distance;
  gint curvex;
  gint i;
418

419
  switch (tool->curves->curve_type[channel])
420
    {
421
    case GIMP_CURVE_SMOOTH:
422
      curvex   = tool->col_value[channel];
423
      distance = G_MAXINT;
424

425
      for (i = 0; i < CURVES_NUM_POINTS; i++)
426
        {
427 428
          if (tool->curves->points[channel][i][0] != -1)
            if (abs (curvex - tool->curves->points[channel][i][0]) < distance)
429
              {
430
                distance = abs (curvex - tool->curves->points[channel][i][0]);
431 432 433
                closest_point = i;
              }
        }
434

435
      if (distance > MIN_DISTANCE)
436
        closest_point = (curvex + 8) / 16;
437

438 439 440 441
      tool->curves->points[channel][closest_point][0] = curvex;
      tool->curves->points[channel][closest_point][1] = tool->curves->curve[channel][curvex];

      tool->selected = closest_point;
442
      break;
443

444
    case GIMP_CURVE_FREE:
445
      /* do nothing for free form curves */
446
      break;
447
    }
Elliot Lee's avatar
Elliot Lee committed
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 511 512 513 514 515 516 517 518
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;
}

519 520
static void
gimp_curves_tool_map (GimpImageMapTool *image_map_tool)
Elliot Lee's avatar
Elliot Lee committed
521
{
522
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (image_map_tool);
523

524
  gimp_lut_setup (tool->lut,
525
                  (GimpLutFunc) curves_lut_func,
526
                  tool->curves,
527
                  gimp_drawable_bytes (image_map_tool->drawable));
528

529 530 531 532 533 534 535 536
  if (tool->channel == GIMP_HISTOGRAM_VALUE)
    gimp_image_map_apply (image_map_tool->image_map,
                          (GimpImageMapApplyFunc) gimp_lut_process_value,
                          tool->lut);
  else
    gimp_image_map_apply (image_map_tool->image_map,
                          (GimpImageMapApplyFunc) gimp_lut_process,
                          tool->lut);
Elliot Lee's avatar
Elliot Lee committed
537 538
}

539

540 541 542
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
543

544 545
static void
gimp_curves_tool_dialog (GimpImageMapTool *image_map_tool)
Elliot Lee's avatar
Elliot Lee committed
546
{
547 548
  GimpCurvesTool  *tool         = GIMP_CURVES_TOOL (image_map_tool);
  GimpToolOptions *tool_options = GIMP_TOOL_GET_OPTIONS (image_map_tool);
549
  GtkListStore    *store;
550
  GtkWidget       *vbox;
551 552 553 554 555
  GtkWidget       *vbox2;
  GtkWidget       *hbox;
  GtkWidget       *hbox2;
  GtkWidget       *label;
  GtkWidget       *bbox;
556 557 558 559
  GtkWidget       *frame;
  GtkWidget       *menu;
  GtkWidget       *table;
  GtkWidget       *button;
560
  GtkWidget       *bar;
561
  gint             padding;
562

563
  vbox = image_map_tool->main_vbox;
Elliot Lee's avatar
Elliot Lee committed
564

565
  /*  The option menu for selecting channels  */
566
  hbox = gtk_hbox_new (FALSE, 6);
567 568 569
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

570
  label = gtk_label_new_with_mnemonic (_("Cha_nnel:"));
571 572 573
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

574 575 576
  store = gimp_enum_store_new_with_range (GIMP_TYPE_HISTOGRAM_CHANNEL,
                                          GIMP_HISTOGRAM_VALUE,
                                          GIMP_HISTOGRAM_ALPHA);
577
  menu = gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (store));
578 579
  g_object_unref (store);

580 581 582 583 584
  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");
585 586 587
  gtk_box_pack_start (GTK_BOX (hbox), menu, FALSE, FALSE, 0);
  gtk_widget_show (menu);

588
  tool->channel_menu = menu;
589

590 591
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), menu);

592
  button = gtk_button_new_with_mnemonic (_("R_eset Channel"));
593
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
594
  gtk_widget_show (button);
595

596
  g_signal_connect (button, "clicked",
597
                    G_CALLBACK (curves_channel_reset_callback),
598
                    tool);
Elliot Lee's avatar
Elliot Lee committed
599

600 601 602 603 604
  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);
605

606
  /*  The table for the color bars and the graph  */
Elliot Lee's avatar
Elliot Lee committed
607
  table = gtk_table_new (2, 2, FALSE);
608 609
  gtk_table_set_col_spacings (GTK_TABLE (table), 2);
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
610 611 612 613 614
  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,
615
                    GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
616
  gtk_widget_show (vbox2);
Elliot Lee's avatar
Elliot Lee committed
617 618

  frame = gtk_frame_new (NULL);
619
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
620
  gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
621 622
  gtk_widget_show (frame);

623 624 625 626
  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);
627

Elliot Lee's avatar
Elliot Lee committed
628 629
  /*  The curves graph  */
  frame = gtk_frame_new (NULL);
630
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
Elliot Lee's avatar
Elliot Lee committed
631
  gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1,
632
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
633
  gtk_widget_show (frame);
Elliot Lee's avatar
Elliot Lee committed
634

635 636 637 638
  tool->graph = gimp_histogram_view_new (FALSE);
  gtk_widget_set_size_request (tool->graph,
                               GRAPH_SIZE + RADIUS * 2,
                               GRAPH_SIZE + RADIUS * 2);
639
  GTK_WIDGET_SET_FLAGS (tool->graph, GTK_CAN_FOCUS);
640 641 642
  gtk_widget_add_events (tool->graph, (GDK_BUTTON_PRESS_MASK   |
                                       GDK_BUTTON_RELEASE_MASK |
                                       GDK_POINTER_MOTION_MASK |
643
                                       GDK_KEY_PRESS_MASK      |
644 645
                                       GDK_LEAVE_NOTIFY_MASK));
  g_object_set (tool->graph,
646 647 648
                "border-width", RADIUS,
                "subdivisions", 1,
                NULL);
649 650 651
  GIMP_HISTOGRAM_VIEW (tool->graph)->light_histogram = TRUE;
  gtk_container_add (GTK_CONTAINER (frame), tool->graph);
  gtk_widget_show (tool->graph);
652

653
  g_signal_connect (tool->graph, "event",
654 655
                    G_CALLBACK (curves_graph_events),
                    tool);
656
  g_signal_connect_after (tool->graph, "expose-event",
657
                          G_CALLBACK (curves_graph_expose),
658
                          tool);
Elliot Lee's avatar
Elliot Lee committed
659

660 661

  gimp_histogram_options_connect_view (GIMP_HISTOGRAM_OPTIONS (tool_options),
662 663 664 665 666
                                       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,
667
                    GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
668
  gtk_widget_show (hbox2);
669

Elliot Lee's avatar
Elliot Lee committed
670
  frame = gtk_frame_new (NULL);
671
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
672
  gtk_box_pack_start (GTK_BOX (hbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
673 674
  gtk_widget_show (frame);

675 676 677 678
  vbox2 = gtk_vbox_new (TRUE, 0);
  gtk_container_add (GTK_CONTAINER (frame), vbox2);
  gtk_widget_show (vbox2);

679 680 681 682
  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);
683

684 685 686 687
  bar = gimp_color_bar_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_box_pack_start (GTK_BOX (vbox2), bar, TRUE, TRUE, 0);
  gtk_widget_show (bar);

688
  gtk_widget_show (table);
Elliot Lee's avatar
Elliot Lee committed
689

690

691 692 693
  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
694

695
  /*  Horizontal button box for load / save */
696
  frame = gimp_frame_new (_("All Channels"));
697
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
698 699
  gtk_widget_show (frame);

700 701
  bbox = gtk_hbutton_box_new ();
  gtk_box_set_spacing (GTK_BOX (bbox), 4);
702
  gtk_container_add (GTK_CONTAINER (frame), bbox);
703
  gtk_widget_show (bbox);
704

705 706 707
  gtk_box_pack_start (GTK_BOX (bbox), image_map_tool->load_button,
                      FALSE, FALSE, 0);
  gtk_widget_show (image_map_tool->load_button);
708

709 710 711
  gtk_box_pack_start (GTK_BOX (bbox), image_map_tool->save_button,
                      FALSE, FALSE, 0);
  gtk_widget_show (image_map_tool->save_button);
712 713

  /*  The radio box for selecting the curve type  */
714
  frame = gimp_frame_new (_("Curve Type"));
715 716 717 718
  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,
719 720 721 722
                                  "gimp-curve", GTK_ICON_SIZE_MENU,
                                  G_CALLBACK (curves_curve_type_callback),
                                  tool,
                                  &tool->curve_type);
723

724
  gtk_widget_style_get (bbox, "child-internal-pad-x", &padding, NULL);
725 726

  gimp_enum_stock_box_set_child_padding (hbox, padding, -1);
727 728 729 730 731

  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
732 733
}

734
static void
735
gimp_curves_tool_reset (GimpImageMapTool *image_map_tool)
736
{
737
  GimpCurvesTool       *tool = GIMP_CURVES_TOOL (image_map_tool);
738
  GimpHistogramChannel  channel;
739

740
  tool->grabbed = FALSE;
741

742 743 744
  for (channel =  GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
745
    curves_channel_reset (tool->curves, channel);
746

747
  curves_update (tool, XRANGE | GRAPH);
748 749
}

750
static gboolean
751 752 753
gimp_curves_tool_settings_load (GimpImageMapTool  *image_map_tool,
                                gpointer           fp,
                                GError           **error)
754
{
755 756 757 758 759
  GimpCurvesTool *tool = GIMP_CURVES_TOOL (image_map_tool);
  FILE           *file = fp;
  gint            i, j;
  gint            fields;
  gchar           buf[50];
760 761
  gint            index[5][CURVES_NUM_POINTS];
  gint            value[5][CURVES_NUM_POINTS];
762

763 764 765 766 767 768 769
  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;
    }
770 771 772

  for (i = 0; i < 5; i++)
    {
773
      for (j = 0; j < CURVES_NUM_POINTS; j++)
774 775 776 777
        {
          fields = fscanf (file, "%d %d ", &index[i][j], &value[i][j]);
          if (fields != 2)
            {
778
              /*  FIXME: should have a helpful error message here  */
779
              g_printerr ("fields != 2");
780 781
              g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
                           _("parse error"));
782 783 784
              return FALSE;
            }
        }
785 786 787 788 789 790
    }

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

791
      for (j = 0; j < CURVES_NUM_POINTS; j++)
792 793 794 795
        {
          tool->curves->points[i][j][0] = index[i][j];
          tool->curves->points[i][j][1] = value[i][j];
        }
796 797 798 799 800 801 802 803
    }

  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),
804
                                   GIMP_CURVE_SMOOTH);
805 806

  return TRUE;
807 808 809
}

static gboolean
810 811
gimp_curves_tool_settings_save (GimpImageMapTool *image_map_tool,
                                gpointer          fp)
812
{
813 814 815 816 817 818 819 820
  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)
      {
821
        /*  pick representative points from the curve
822
            and make them control points  */
823 824 825 826 827 828
        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];
          }
829 830 831 832 833 834
      }

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

  for (i = 0; i < 5; i++)
    {
835
      for (j = 0; j < CURVES_NUM_POINTS; j++)
836
        fprintf (file, "%d %d ",
837 838 839 840 841 842 843
                 tool->curves->points[i][j][0],
                 tool->curves->points[i][j][1]);

      fprintf (file, "\n");
    }

  return TRUE;
844
}
845

846
/* TODO: preview alpha channel stuff correctly.  -- austin, 20/May/99 */
Elliot Lee's avatar
Elliot Lee committed
847
static void
848
curves_update (GimpCurvesTool *tool,
849
               gint            update)
Elliot Lee's avatar
Elliot Lee committed
850
{
851
  GimpHistogramChannel channel;
852

853
  if (tool->color)
854
    {
855
      channel = tool->channel;
856
    }