gimpcurvestool.c 42.8 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
  gimp_image_map_apply (image_map_tool->image_map,
                        (GimpImageMapApplyFunc) gimp_lut_process,
                        tool->lut);
Elliot Lee's avatar
Elliot Lee committed
532 533
}

534

535 536 537
/*******************/
/*  Curves dialog  */
/*******************/
Elliot Lee's avatar
Elliot Lee committed
538

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

558
  vbox = image_map_tool->main_vbox;
Elliot Lee's avatar
Elliot Lee committed
559

560
  /*  The option menu for selecting channels  */
561
  hbox = gtk_hbox_new (FALSE, 6);
562 563 564
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

565
  label = gtk_label_new_with_mnemonic (_("Cha_nnel:"));
566 567 568
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

569 570 571
  store = gimp_enum_store_new_with_range (GIMP_TYPE_HISTOGRAM_CHANNEL,
                                          GIMP_HISTOGRAM_VALUE,
                                          GIMP_HISTOGRAM_ALPHA);
572
  menu = gimp_enum_combo_box_new_with_model (GIMP_ENUM_STORE (store));
573 574
  g_object_unref (store);

575 576 577 578 579
  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");
580 581 582
  gtk_box_pack_start (GTK_BOX (hbox), menu, FALSE, FALSE, 0);
  gtk_widget_show (menu);

583
  tool->channel_menu = menu;
584

585 586
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), menu);

587
  button = gtk_button_new_with_mnemonic (_("R_eset Channel"));
588
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
589
  gtk_widget_show (button);
590

591
  g_signal_connect (button, "clicked",
592
                    G_CALLBACK (curves_channel_reset_callback),
593
                    tool);
Elliot Lee's avatar
Elliot Lee committed
594

595 596 597 598 599
  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);
600

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

  frame = gtk_frame_new (NULL);
614
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
615
  gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
616 617
  gtk_widget_show (frame);

618 619 620 621
  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);
622

Elliot Lee's avatar
Elliot Lee committed
623 624
  /*  The curves graph  */
  frame = gtk_frame_new (NULL);
625
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
Elliot Lee's avatar
Elliot Lee committed
626
  gtk_table_attach (GTK_TABLE (table), frame, 1, 2, 0, 1,
627
                    GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
628
  gtk_widget_show (frame);
Elliot Lee's avatar
Elliot Lee committed
629

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

648
  g_signal_connect (tool->graph, "event",
649 650
                    G_CALLBACK (curves_graph_events),
                    tool);
651
  g_signal_connect_after (tool->graph, "expose-event",
652
                          G_CALLBACK (curves_graph_expose),
653
                          tool);
Elliot Lee's avatar
Elliot Lee committed
654

655 656

  gimp_histogram_options_connect_view (GIMP_HISTOGRAM_OPTIONS (tool_options),
657 658 659 660 661
                                       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,
662
                    GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
663
  gtk_widget_show (hbox2);
664

Elliot Lee's avatar
Elliot Lee committed
665
  frame = gtk_frame_new (NULL);
666
  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
667
  gtk_box_pack_start (GTK_BOX (hbox2), frame, TRUE, TRUE, RADIUS);
Elliot Lee's avatar
Elliot Lee committed
668 669
  gtk_widget_show (frame);

670 671 672 673
  vbox2 = gtk_vbox_new (TRUE, 0);
  gtk_container_add (GTK_CONTAINER (frame), vbox2);
  gtk_widget_show (vbox2);

674 675 676 677
  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);
678

679 680 681 682
  bar = gimp_color_bar_new (GTK_ORIENTATION_HORIZONTAL);
  gtk_box_pack_start (GTK_BOX (vbox2), bar, TRUE, TRUE, 0);
  gtk_widget_show (bar);

683
  gtk_widget_show (table);
Elliot Lee's avatar
Elliot Lee committed
684

685

686 687 688
  hbox = gtk_hbox_new (FALSE, 6);
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
689

690
  /*  Horizontal button box for load / save */
691
  frame = gimp_frame_new (_("All Channels"));
692
  gtk_box_pack_start (GTK_BOX (hbox), frame, FALSE, FALSE, 0);
693 694
  gtk_widget_show (frame);

695 696
  bbox = gtk_hbutton_box_new ();
  gtk_box_set_spacing (GTK_BOX (bbox), 4);
697
  gtk_container_add (GTK_CONTAINER (frame), bbox);
698
  gtk_widget_show (bbox);
699

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

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

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

719
  gtk_widget_style_get (bbox, "child-internal-pad-x", &padding, NULL);
720 721

  gimp_enum_stock_box_set_child_padding (hbox, padding, -1);
722 723 724 725 726

  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
727 728
}

729
static void
730
gimp_curves_tool_reset (GimpImageMapTool *image_map_tool)
731
{
732
  GimpCurvesTool       *tool = GIMP_CURVES_TOOL (image_map_tool);
733
  GimpHistogramChannel  channel;
734

735
  tool->grabbed = FALSE;
736

737 738 739
  for (channel =  GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
740
    curves_channel_reset (tool->curves, channel);
741

742
  curves_update (tool, XRANGE | GRAPH);
743 744
}

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

758 759 760 761 762 763 764
  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;
    }
765 766 767

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

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

786
      for (j = 0; j < CURVES_NUM_POINTS; j++)
787 788 789 790
        {
          tool->curves->points[i][j][0] = index[i][j];
          tool->curves->points[i][j][1] = value[i][j];
        }
791 792 793 794 795 796 797 798
    }

  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),
799
                                   GIMP_CURVE_SMOOTH);
800 801

  return TRUE;
802 803 804
}

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

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

  for (i = 0; i < 5; i++)
    {
830
      for (j = 0; j < CURVES_NUM_POINTS; j++)
831
        fprintf (file, "%d %d ",
832 833 834 835 836 837 838
                 tool->curves->points[i][j][0],
                 tool->curves->points[i][j][1]);

      fprintf (file, "\n");
    }

  return TRUE;
839
}
840

841
/* TODO: preview alpha channel stuff correctly.  -- austin, 20/May/99 */
Elliot Lee's avatar
Elliot Lee committed
842
static void
843
curves_update (GimpCurvesTool *tool,
844
               gint            update)
Elliot Lee's avatar
Elliot Lee committed
845
{
846
  GimpHistogramChannel channel;
847

848
  if (tool->color)
849
    {
850
      channel = tool->channel;
851
    }
852
  else