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

19 20 21
#include "config.h"

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

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

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

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

34
#include "config/gimpguiconfig.h"
35

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

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

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

52 53
#include "display/gimpdisplay.h"

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

58
#include "gimp-intl.h"
59

60

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

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


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

74 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
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);
131

Michael Natterer's avatar
Michael Natterer committed
132

133
G_DEFINE_TYPE (GimpCurvesTool, gimp_curves_tool, GIMP_TYPE_IMAGE_MAP_TOOL)
134 135

#define parent_class gimp_curves_tool_parent_class
136

Michael Natterer's avatar
Michael Natterer committed
137

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

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

157 158 159

/*  private functions  */

Michael Natterer's avatar
Michael Natterer committed
160 161 162
static void
gimp_curves_tool_class_init (GimpCurvesToolClass *klass)
{
163 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
  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
189 190 191
}

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

196 197 198
  tool->curves  = g_new0 (Curves, 1);
  tool->lut     = gimp_lut_new ();
  tool->channel = GIMP_HISTOGRAM_VALUE;
199

200
  curves_init (tool->curves);
201

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

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

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

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

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

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

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

  if (! drawable)
254
    return FALSE;
255

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

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

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

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

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

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

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

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

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

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

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

296 297
  curves_update (c_tool, ALL);

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

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

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

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

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

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

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

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

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

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

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

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

  GIMP_COLOR_TOOL (tool)->pick_mode = mode;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      tool->selected = closest_point;
441
      break;
442

443
    case GIMP_CURVE_FREE:
444
      /* do nothing for free form curves */
445
      break;
446
    }
Elliot Lee's avatar
Elliot Lee committed
447 448
}

449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
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;
}

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

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

528
  gimp_image_map_apply (image_map_tool->image_map,
529
                        (GimpImageMapApplyFunc) gimp_lut_process,
530
                        tool->lut);
Elliot Lee's avatar
Elliot Lee committed
531 532
}

533

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

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

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

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

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

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

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

582
  tool->channel_menu = menu;
583

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

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

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

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

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

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

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

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

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

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

654 655

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

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

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

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

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

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

684

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

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

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

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

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

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

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

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

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

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

734
  tool->grabbed = FALSE;
735

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

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

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

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

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

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

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

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

  return TRUE;
801 802 803
}

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

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

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

      fprintf (file, "\n");
    }

  return TRUE;
838
}
839

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

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