color_notebook.c 34.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * color_notebook module (C) 1998 Austin Donnelly <austin@greenend.org.uk>
 *
 * 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
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
20

Tomas Ogren's avatar
Tomas Ogren committed
21
#include "config.h"
22

23 24
#include <stdio.h>
#include <stdlib.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
25 26
#include <string.h>

27 28
#include <gmodule.h>
#include <gtk/gtk.h>
29
#include <gdk/gdkkeysyms.h>
30

31
#include "libgimpcolor/gimpcolor.h"
32
#include "libgimpwidgets/gimpwidgets.h"
33

34 35
#include "apptypes.h"

36
#include "color_area.h"
37
#include "color_notebook.h"
38
#include "colormaps.h"
39

40 41
#include "libgimp/gimpcolorselector.h"

42 43
#include "libgimp/gimpintl.h"

44

45 46
#define COLOR_AREA_SIZE    20
#define COLOR_HISTORY_SIZE 16
47 48 49 50

typedef enum
{
  UPDATE_NOTEBOOK   = 1 << 0,
51 52 53 54
  UPDATE_CHANNEL    = 1 << 1,
  UPDATE_NEW_COLOR  = 1 << 2,
  UPDATE_ORIG_COLOR = 1 << 3,
  UPDATE_CALLER     = 1 << 4
55 56 57
} ColorNotebookUpdateType;


58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
/* "class" information we keep on each registered colour selector */

typedef struct _ColorSelectorInfo ColorSelectorInfo;

struct _ColorSelectorInfo
{
  gchar                       *name;    /* label used in notebook tab */
  gchar                       *help_page;
  GimpColorSelectorMethods     methods;
  gint                         refs;    /* number of instances around */
  gboolean                     active;
  GimpColorSelectorFinishedCB  death_callback;
  gpointer                     death_data;

  ColorSelectorInfo           *next;
};


/* "instance" information we keep on each notebook tab */

typedef struct _ColorSelectorInstance ColorSelectorInstance;

struct _ColorSelectorInstance
{
  ColorNotebook         *color_notebook;
  ColorSelectorInfo     *info;
  GtkWidget             *frame;   /* main widget */
  gpointer               selector_data;

  ColorSelectorInstance *next;
};


91 92 93 94 95 96 97 98 99 100 101
struct _ColorNotebook
{
  GtkWidget                *shell;
  GtkWidget                *notebook;

  GtkWidget                *new_color;
  GtkWidget                *orig_color;
  GtkWidget                *toggles[7];
  GtkObject                *slider_data[7];
  GtkWidget                *hex_entry;

102 103
  GtkWidget                *history[COLOR_HISTORY_SIZE];

104 105
  GimpHSV                   hsv;
  GimpRGB                   rgb;
106

107
  GimpRGB                   orig_rgb;
108 109 110 111 112 113

  GimpColorSelectorChannelType  active_channel;

  ColorNotebookCallback     callback;
  gpointer                  client_data;

114 115
  gboolean                  wants_updates;
  gboolean                  show_alpha;
116 117 118 119 120 121

  ColorSelectorInstance    *selectors;
  ColorSelectorInstance    *cur_page;
};


122 123 124 125 126
static void       color_notebook_ok_callback     (GtkWidget         *widget,
						  gpointer           data);
static void       color_notebook_cancel_callback (GtkWidget         *widget,
						  gpointer           data);
static void       color_notebook_update_callback (gpointer           data,
127 128
						  const GimpHSV     *hsv,
						  const GimpRGB     *rgb);
129 130 131 132 133 134 135 136
static void       color_notebook_page_switch     (GtkWidget         *widget,
						  GtkNotebookPage   *page,
						  guint              page_num,
						  gpointer           data);
static void       color_notebook_help_func       (const gchar       *help_data);

static void       color_notebook_selector_death  (ColorSelectorInfo *info);

137 138 139 140
static void       color_notebook_set_white       (ColorNotebook     *cnp);
static void       color_notebook_set_black       (ColorNotebook     *cnp);
static void       color_notebook_color_changed   (GtkWidget         *widget,
						  gpointer           data);
141 142 143
static void       color_notebook_update          (ColorNotebook     *cnp,
						  ColorNotebookUpdateType update);
static void       color_notebook_update_notebook (ColorNotebook     *cnp);
144
static void       color_notebook_update_channel  (ColorNotebook     *cnp);
145 146 147
static void       color_notebook_update_caller   (ColorNotebook     *cnp);
static void       color_notebook_update_colors   (ColorNotebook     *cnp,
						  ColorNotebookUpdateType which);
148 149 150 151
static void       color_notebook_update_rgb_values (ColorNotebook   *cnp);
static void       color_notebook_update_hsv_values (ColorNotebook   *cnp);
static void       color_notebook_update_scales     (ColorNotebook   *cnp,
						    gint             skip);
152

153 154 155 156 157 158 159 160
static void       color_notebook_toggle_update    (GtkWidget        *widget,
						   gpointer          data);
static void       color_notebook_scale_update     (GtkAdjustment    *adjustment,
						   gpointer          data);
static gint       color_notebook_hex_entry_events (GtkWidget        *widget,
						   GdkEvent         *event,
						   gpointer          data);

161
static void       color_history_init              (void);
162 163 164 165 166 167 168
static void       color_history_color_clicked     (GtkWidget        *widget,
						   gpointer          data);
static void       color_history_color_changed     (GtkWidget        *widget,
						   gpointer          data);
static void       color_history_add_clicked       (GtkWidget        *widget,
						   gpointer          data);

169 170 171 172

/* master list of all registered colour selectors */
static ColorSelectorInfo *selector_info = NULL;

173 174 175 176 177
static GList    *color_notebooks           = NULL;

static GimpRGB   color_history[COLOR_HISTORY_SIZE];
static gboolean  color_history_initialized = FALSE;

178

179
ColorNotebook *
180 181
color_notebook_new (const gchar           *title,
		    const GimpRGB         *color,
182
		    ColorNotebookCallback  callback,
183
		    gpointer               client_data,
184 185
		    gboolean               wants_updates,
		    gboolean               show_alpha)
186
{
187
  ColorNotebook         *cnp;
188
  GtkWidget             *main_vbox;
189
  GtkWidget             *main_hbox;
190
  GtkWidget             *right_vbox;
191
  GtkWidget             *colors_frame;
192
  GtkWidget             *hbox;
193
  GtkWidget             *vbox;
194
  GtkWidget             *table;
195
  GtkWidget             *label;
196
  GtkWidget             *button;
197
  GtkWidget             *arrow;
198 199
  GtkWidget             *color_area;
  GimpRGB                bw;
200
  GSList                *group;
Sven Neumann's avatar
Sven Neumann committed
201 202
  guchar                 r, g, b;
  gchar                  buffer[8];
203
  ColorSelectorInfo     *info;
204
  ColorSelectorInstance *csel;
205 206 207 208 209 210 211 212 213 214 215 216 217
  gint                   i;

  static gchar *toggle_titles[] = 
  { 
    N_("H"),
    N_("S"),
    N_("V"),
    N_("R"),
    N_("G"),
    N_("B"),
    N_("A")
  };
  static gchar *slider_tips[7] = 
218
  {
219 220 221 222 223 224 225 226
    N_("Hue"),
    N_("Saturation"),
    N_("Value"),
    N_("Red"),
    N_("Green"),
    N_("Blue"),
    N_("Alpha")
  };
227 228 229
  static gdouble  slider_initial_vals[] = {   0,   0,   0,   0,   0,   0,   0 };
  static gdouble  slider_max_vals[]     = { 360, 100, 100, 255, 255, 255, 100 };
  static gdouble  slider_incs[]         = {  30,  10,  10,  16,  16,  16,  10 };
230 231

  g_return_val_if_fail (selector_info != NULL, NULL);
232 233
  g_return_val_if_fail (color != NULL, NULL);

234
  if (! color_history_initialized)
235
    color_history_init ();
236

237 238
  cnp = g_new0 (ColorNotebook, 1);

239 240
  cnp->callback      = callback;
  cnp->client_data   = client_data;
241
  cnp->wants_updates = wants_updates;
242
  cnp->show_alpha    = show_alpha;
243 244
  cnp->selectors     = NULL;
  cnp->cur_page      = NULL;
245

246 247
  cnp->rgb           = *color;
  cnp->orig_rgb      = *color;
248 249

  color_notebook_update_hsv_values (cnp); 
250

251
  cnp->shell =
252
    gimp_dialog_new (title, "color_selection",
253
		     color_notebook_help_func, (const gchar *) cnp,
254
		     GTK_WIN_POS_NONE,
255
		     FALSE, TRUE, TRUE,
256

257 258
		     wants_updates ? _("Close") : _("OK"),
		     color_notebook_ok_callback,
Sven Neumann's avatar
Sven Neumann committed
259
		     cnp, NULL, NULL, TRUE, wants_updates,
260 261
		     wants_updates ? _("Revert to Old Color") : _("Cancel"),
		     color_notebook_cancel_callback,
Sven Neumann's avatar
Sven Neumann committed
262
		     cnp, NULL, NULL, FALSE, !wants_updates,
263 264

		     NULL);
265

266 267 268 269 270
  main_vbox = gtk_vbox_new (FALSE, 6);
  gtk_container_set_border_width (GTK_CONTAINER (main_vbox), 2);
  gtk_container_add (GTK_CONTAINER (GTK_DIALOG (cnp->shell)->vbox), main_vbox);
  gtk_widget_show (main_vbox);

271
  main_hbox = gtk_hbox_new (FALSE, 6);
272
  gtk_container_add (GTK_CONTAINER (main_vbox), main_hbox);
273 274
  gtk_widget_show (main_hbox);

275 276 277 278
  /* do we actually need a notebook? */
  if (selector_info->next)
    {
      cnp->notebook = gtk_notebook_new ();
279 280
      gtk_box_pack_start (GTK_BOX (main_hbox), cnp->notebook,
			  FALSE, FALSE, 0);
281 282 283 284 285 286 287
      gtk_widget_show (cnp->notebook);
    }
  else /* only one selector */
    {
      cnp->notebook = NULL;
    }

288
  /* create each registered color selector */
289 290 291 292 293
  info = selector_info;
  while (info)
    {
      if (info->active)
	{
294
	  csel = g_new (ColorSelectorInstance, 1);
295 296 297
	  csel->color_notebook = cnp;
	  csel->info = info;
	  info->refs++;
298
	  csel->frame =
299 300
	    info->methods.new (&cnp->hsv,
			       &cnp->rgb,
301 302 303
			       show_alpha,
			       color_notebook_update_callback, csel,
			       &csel->selector_data);
304 305 306 307 308 309 310
	  gtk_object_set_data (GTK_OBJECT (csel->frame), "gimp_color_notebook",
			       csel);

	  if (cnp->notebook)
	    {
	      label = gtk_label_new (info->name);
	      gtk_widget_show (label);
311 312
	      /* hide the frame, so it doesn't get selected by mistake */
	      gtk_widget_hide (csel->frame);
313
	      gtk_notebook_append_page (GTK_NOTEBOOK (cnp->notebook),
314 315
					csel->frame,
					label);
316 317 318
	    }
	  else
	    {
319 320
	      gtk_box_pack_start (GTK_BOX (main_hbox), csel->frame,
				  FALSE, FALSE, 0);
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
	    }

	  gtk_widget_show (csel->frame);

	  if (!cnp->cur_page)
	    cnp->cur_page = csel;

	  /* link into list of all selectors hanging off the new notebook */
	  csel->next = cnp->selectors;
	  cnp->selectors = csel;
	}

      info = info->next;
    }

336
  /*  The right vertical box with color areas and color space sliders  */
337 338 339 340
  right_vbox = gtk_vbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, TRUE, TRUE, 0);
  gtk_widget_show (right_vbox);

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
  /*  The hbox for the color_areas  */
  hbox = gtk_hbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (right_vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  vbox = gtk_vbox_new (TRUE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
  gtk_widget_show (vbox);
 
  /*  The white color button  */
  button = gtk_button_new ();
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
  gimp_rgba_set (&bw, 1.0, 1.0, 1.0, 1.0);
  color_area = gimp_color_area_new (&bw, 
				    GIMP_COLOR_AREA_FLAT,
				    GDK_BUTTON2_MASK);
  gtk_drag_dest_unset (color_area);
  gtk_widget_set_usize (button, COLOR_AREA_SIZE, -1);
  gtk_container_add (GTK_CONTAINER (button), color_area);
  gtk_widget_show (color_area);
  gtk_widget_show (button);
  gtk_signal_connect_object (GTK_OBJECT (button), "clicked", 
			     GTK_SIGNAL_FUNC (color_notebook_set_white),
			     (GtkObject *) cnp);

  /*  The black color button  */
  button = gtk_button_new ();
  gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 0);
  gimp_rgba_set (&bw, 0.0, 0.0, 0.0, 1.0);
  color_area = gimp_color_area_new (&bw, 
				    GIMP_COLOR_AREA_FLAT,
				    GDK_BUTTON2_MASK);
  gtk_drag_dest_unset (color_area);
  gtk_widget_set_usize (button, COLOR_AREA_SIZE, -1);
  gtk_container_add (GTK_CONTAINER (button), color_area);
  gtk_widget_show (color_area);
  gtk_widget_show (button);
  gtk_signal_connect_object (GTK_OBJECT (button), "clicked", 
			     GTK_SIGNAL_FUNC (color_notebook_set_black),
			     (GtkObject *) cnp);

382 383 384
  /*  The old/new color area frame and hbox  */
  colors_frame = gtk_frame_new (NULL);
  gtk_frame_set_shadow_type (GTK_FRAME (colors_frame), GTK_SHADOW_IN);
385
  gtk_box_pack_start (GTK_BOX (hbox), colors_frame, TRUE, TRUE, 0);
386 387
  gtk_widget_show (colors_frame);

388 389 390
  hbox = gtk_hbox_new (TRUE, 2);
  gtk_container_add (GTK_CONTAINER (colors_frame), hbox);
  gtk_widget_show (hbox);
391 392

  /*  The new color area  */
393 394 395 396 397 398 399 400
  cnp->new_color = 
    gimp_color_area_new (&cnp->rgb, 
			 show_alpha ? 
			 GIMP_COLOR_AREA_SMALL_CHECKS : GIMP_COLOR_AREA_FLAT,
			 GDK_BUTTON1_MASK | GDK_BUTTON2_MASK);
  gtk_widget_set_usize (cnp->new_color, -1, COLOR_AREA_SIZE);
  gtk_signal_connect (GTK_OBJECT (cnp->new_color), "color_changed",
                      GTK_SIGNAL_FUNC (color_notebook_color_changed),
401
                      cnp);
402
  gtk_box_pack_start (GTK_BOX (hbox), cnp->new_color, TRUE, TRUE, 0);
403 404 405
  gtk_widget_show (cnp->new_color);

  /*  The old color area  */
406 407 408 409 410 411 412
  cnp->orig_color = 
    gimp_color_area_new (&cnp->orig_rgb, 
			 show_alpha ? 
			 GIMP_COLOR_AREA_SMALL_CHECKS : GIMP_COLOR_AREA_FLAT,
			 GDK_BUTTON1_MASK | GDK_BUTTON2_MASK);
  gtk_widget_set_usize (cnp->new_color, -1, COLOR_AREA_SIZE);
  gtk_drag_dest_unset (cnp->orig_color);
413
  gtk_box_pack_start (GTK_BOX (hbox), cnp->orig_color, TRUE, TRUE, 0);
414 415
  gtk_widget_show (cnp->orig_color);

416
  /*  The color space sliders, toggle buttons and entries  */
Sven Neumann's avatar
Sven Neumann committed
417
  table = gtk_table_new (8, 4, FALSE);
418 419
  gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  gtk_table_set_col_spacings (GTK_TABLE (table), 2);
420
  gtk_table_set_col_spacing (GTK_TABLE (table), 0, 0);
421 422 423 424
  gtk_box_pack_start (GTK_BOX (right_vbox), table, TRUE, TRUE, 0);
  gtk_widget_show (table);

  group = NULL;
Sven Neumann's avatar
Sven Neumann committed
425
  for (i = 0; i < (show_alpha ? 7 : 6); i++)
426
    {
427 428 429 430 431 432 433
      if (i == 6)
	{
	  cnp->toggles[i] = NULL;
	}
      else
	{
	  cnp->toggles[i] =
434
	    gtk_radio_button_new (group);
435 436 437 438 439

	  gimp_help_set_help_data (cnp->toggles[i],
				   gettext (slider_tips[i]), NULL);
	  group = gtk_radio_button_group (GTK_RADIO_BUTTON (cnp->toggles[i]));
	  gtk_table_attach (GTK_TABLE (table), cnp->toggles[i],
440 441
			    0, 1, i, i + 1,
			    GTK_SHRINK, GTK_EXPAND, 0, 0);
442 443 444 445 446
	  gtk_signal_connect (GTK_OBJECT (cnp->toggles[i]), "toggled",
			      GTK_SIGNAL_FUNC (color_notebook_toggle_update),
			      cnp);
	  gtk_widget_show (cnp->toggles[i]);
	}
447

Sven Neumann's avatar
Sven Neumann committed
448 449
      cnp->slider_data[i] = gimp_scale_entry_new (GTK_TABLE (table), 1, i,
                                                  gettext (toggle_titles[i]), 
450
                                                  80, 55,
451
                                                  slider_initial_vals[i],
452 453 454 455 456 457 458 459 460 461 462 463
                                                  0.0, slider_max_vals[i],
                                                  1.0, slider_incs[i],
                                                  0, TRUE, 0.0, 0.0,
                                                  gettext (slider_tips[i]),
                                                  NULL);
      gtk_signal_connect (GTK_OBJECT (cnp->slider_data[i]), "value_changed",
                          GTK_SIGNAL_FUNC (color_notebook_scale_update),
                          cnp);
    }

  /* The hex triplet entry */
  hbox = gtk_hbox_new (FALSE, 3);
Sven Neumann's avatar
Sven Neumann committed
464
  gtk_table_attach (GTK_TABLE (table), hbox, 1, 4, 8, 9,
Sven Neumann's avatar
Sven Neumann committed
465
		    GTK_FILL | GTK_EXPAND, GTK_EXPAND, 0, 0);
466 467
  gtk_widget_show (hbox);

Sven Neumann's avatar
Sven Neumann committed
468 469 470
  cnp->hex_entry = gtk_entry_new_with_max_length (7);
  gimp_rgb_get_uchar (&cnp->rgb, &r, &g, &b);
  g_snprintf (buffer, sizeof (buffer), "#%.2x%.2x%.2x", r, g, b);
471
  gtk_entry_set_text (GTK_ENTRY (cnp->hex_entry), buffer);
Sven Neumann's avatar
Sven Neumann committed
472 473
  gtk_widget_set_usize (GTK_WIDGET (cnp->hex_entry), 60, 0);
  gtk_box_pack_end (GTK_BOX (hbox), cnp->hex_entry, TRUE, TRUE, 2);
474 475 476 477 478 479 480 481 482 483 484 485
  gtk_signal_connect (GTK_OBJECT (cnp->hex_entry), "focus_out_event",
                      GTK_SIGNAL_FUNC (color_notebook_hex_entry_events),
                      cnp);
  gtk_signal_connect (GTK_OBJECT (cnp->hex_entry), "key_press_event",
                      GTK_SIGNAL_FUNC (color_notebook_hex_entry_events),
                      cnp);
  gtk_widget_show (cnp->hex_entry);

  label = gtk_label_new (_("Hex Triplet:"));
  gtk_box_pack_end (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

486 487
  color_notebook_update_scales (cnp, -1);

488
  /* The color history */
489
  hbox = gtk_hbox_new (FALSE, 2);
490 491 492 493
  gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);

  button = gtk_button_new ();
494
  gtk_widget_set_usize (button, COLOR_AREA_SIZE, COLOR_AREA_SIZE);
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
  gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
  gimp_help_set_help_data (button,
			   _("Add the current color to the color history"),
			   NULL);
  gtk_widget_show (button);

  gtk_signal_connect (GTK_OBJECT (button), "clicked",
		      GTK_SIGNAL_FUNC (color_history_add_clicked),
		      cnp);

  arrow = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_OUT);
  gtk_container_add (GTK_CONTAINER (button), arrow);
  gtk_widget_show (arrow);

  for (i = 0; i < COLOR_HISTORY_SIZE; i++)
    {
      button = gtk_button_new ();
      gtk_widget_set_usize (button, COLOR_AREA_SIZE, COLOR_AREA_SIZE);
      gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0);

      cnp->history[i] = gimp_color_area_new (&color_history[i], 
					     GIMP_COLOR_AREA_SMALL_CHECKS,
					     GDK_BUTTON2_MASK);
      gtk_container_add (GTK_CONTAINER (button), cnp->history[i]);
      gtk_widget_show (cnp->history[i]);
      gtk_widget_show (button);

      gtk_signal_connect (GTK_OBJECT (button), "clicked",
			  GTK_SIGNAL_FUNC (color_history_color_clicked),
			  cnp);

      gtk_signal_connect (GTK_OBJECT (cnp->history[i]), "color_changed",
			  GTK_SIGNAL_FUNC (color_history_color_changed),
			  &color_history[i]);
    }

531 532 533 534
  gtk_widget_show (cnp->shell);

  /* this must come after showing the widget, otherwise we get a
   * switch_page signal for a non-visible color selector, which is bad
535 536
   * news.
   */
537 538 539
  if (cnp->notebook)
    {
      gtk_signal_connect (GTK_OBJECT (cnp->notebook), "switch_page",
540
			  GTK_SIGNAL_FUNC (color_notebook_page_switch),
541
			  cnp);
542 543
    }

544 545
  color_notebooks = g_list_prepend (color_notebooks, cnp);

546 547 548 549
  return cnp;
}

void
550
color_notebook_show (ColorNotebook *cnp)
551 552
{
  g_return_if_fail (cnp != NULL);
553 554 555 556 557

  if (! GTK_WIDGET_VISIBLE (cnp->shell))
    gtk_widget_show (cnp->shell);
  else
    gdk_window_raise (cnp->shell->window);
558 559 560
}

void
561
color_notebook_hide (ColorNotebook *cnp)
562 563 564 565 566 567
{
  g_return_if_fail (cnp != NULL);
  gtk_widget_hide (cnp->shell);
}

void
568
color_notebook_free (ColorNotebook *cnp)
569 570 571 572 573
{
  ColorSelectorInstance *csel, *next;

  g_return_if_fail (cnp != NULL);

574 575
  color_notebooks = g_list_remove (color_notebooks, cnp);

576 577
  gtk_widget_destroy (cnp->shell);

578
  /* call the free functions for all the color selectors */
579 580 581 582 583
  csel = cnp->selectors;
  while (csel)
    {
      next = csel->next;

584
      csel->info->methods.free (csel->selector_data);
585 586 587

      csel->info->refs--;
      if (csel->info->refs == 0 && !csel->info->active)
588
	color_notebook_selector_death (csel->info);
589 590 591 592 593 594 595 596 597

      g_free (csel);
      csel = next;
    }

  g_free (cnp);
}

void
598
color_notebook_set_color (ColorNotebook *cnp,
599
			  const GimpRGB *color)
600 601
{
  g_return_if_fail (cnp != NULL);
602 603
  g_return_if_fail (color != NULL);

604 605
  cnp->rgb      = *color;
  cnp->orig_rgb = *color;
606

607
  color_notebook_update_hsv_values (cnp);
608
  color_notebook_update_scales (cnp, -1);
609

610 611 612 613
  color_notebook_update (cnp,
			 UPDATE_NOTEBOOK   |
			 UPDATE_ORIG_COLOR |
			 UPDATE_NEW_COLOR);
614 615
}

616 617 618 619 620 621 622 623 624 625
void
color_notebook_get_color (ColorNotebook *cnp,
			  GimpRGB       *color)
{
  g_return_if_fail (cnp != NULL);
  g_return_if_fail (color != NULL);

  *color = cnp->rgb;
}

626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
static void
color_notebook_set_white (ColorNotebook *cnp)
{
  gimp_rgb_set (&cnp->rgb, 1.0, 1.0, 1.0);

  color_notebook_update_hsv_values (cnp);
  color_notebook_update_scales (cnp, -1);

  color_notebook_update (cnp,
			 UPDATE_NOTEBOOK   |
			 UPDATE_NEW_COLOR  |
			 UPDATE_CALLER);
}

static void
color_notebook_set_black (ColorNotebook *cnp)
{
  gimp_rgb_set (&cnp->rgb, 0.0, 0.0, 0.0);

  color_notebook_update_hsv_values (cnp);
  color_notebook_update_scales (cnp, -1);

  color_notebook_update (cnp,
			 UPDATE_NOTEBOOK   |
			 UPDATE_NEW_COLOR  |
			 UPDATE_CALLER);
}

static void
color_notebook_color_changed (GtkWidget *widget,
			      gpointer   data)
{
  ColorNotebook *cnp;

  cnp = (ColorNotebook *) data;

  gimp_color_area_get_color (GIMP_COLOR_AREA (widget), &cnp->rgb);

  color_notebook_update_hsv_values (cnp);
  color_notebook_update_scales (cnp, -1);

  color_notebook_update (cnp,
			 UPDATE_NOTEBOOK   |
			 UPDATE_NEW_COLOR  |
			 UPDATE_CALLER);
}

673 674 675
/*
 * Called by a color selector on user selection of a color
 */
676
static void
677 678 679
color_notebook_update_callback (gpointer       data,
				const GimpHSV *hsv,
				const GimpRGB *rgb)
680 681
{
  ColorSelectorInstance *csel;
682
  ColorNotebook         *cnp;
683 684

  g_return_if_fail (data != NULL);
685 686
  g_return_if_fail (hsv != NULL);
  g_return_if_fail (rgb != NULL);
687 688 689 690

  csel = (ColorSelectorInstance *) data;
  cnp = csel->color_notebook;

691 692
  cnp->hsv = *hsv;
  cnp->rgb = *rgb;
693

694 695 696 697 698
  color_notebook_update_scales (cnp, -1);

  color_notebook_update (cnp,
			 UPDATE_NEW_COLOR |
			 UPDATE_CALLER);
699 700 701
}

static void
702 703
color_notebook_ok_callback (GtkWidget *widget,
			    gpointer   data)
704
{
705
  ColorNotebook *cnp;
706

707
  cnp = (ColorNotebook *) data;
708

Sven Neumann's avatar
Sven Neumann committed
709 710
  color_history_add_clicked (NULL, cnp);

711
  if (cnp->callback)
712
    {
713 714
      (* cnp->callback) (cnp,
			 &cnp->rgb,
715 716 717
			 COLOR_NOTEBOOK_OK,
			 cnp->client_data);
    }
718 719 720
}

static void
721 722
color_notebook_cancel_callback (GtkWidget *widget,
				gpointer   data)
723
{
724
  ColorNotebook *cnp;
725

726
  cnp = (ColorNotebook *) data;
727 728

  if (cnp->callback)
729
    {
730 731
      (* cnp->callback) (cnp,
			 &cnp->orig_rgb,
732 733 734
			 COLOR_NOTEBOOK_CANCEL,
			 cnp->client_data);
    }
735 736 737
}

static void
738
color_notebook_page_switch (GtkWidget       *widget,
739
			    GtkNotebookPage *page,
740 741
			    guint            page_num,
			    gpointer         data)
742
{
743
  ColorNotebook         *cnp;
744
  ColorSelectorInstance *csel;
Sven Neumann's avatar
Sven Neumann committed
745
  gint                   i;
746

747 748 749
  cnp = (ColorNotebook *) data;

  csel = gtk_object_get_data (GTK_OBJECT (page->child), "gimp_color_notebook");
750 751 752 753

  g_return_if_fail (cnp != NULL && csel != NULL);

  cnp->cur_page = csel;
754

Sven Neumann's avatar
Sven Neumann committed
755
  for (i = 0; i < 6; i++)
Sven Neumann's avatar
Sven Neumann committed
756
    {
Sven Neumann's avatar
Sven Neumann committed
757 758
      gtk_widget_set_sensitive (cnp->toggles[i], 
				csel->info->methods.set_channel != NULL);
Sven Neumann's avatar
Sven Neumann committed
759 760
    }

761
  color_notebook_update (cnp, UPDATE_CHANNEL | UPDATE_NOTEBOOK);
762 763
}

764
static void
765
color_notebook_help_func (const gchar *data)
766
{
767
  ColorNotebook *cnp;
768
  gchar         *help_path;
769

770
  cnp = (ColorNotebook *) data;
771 772 773 774

  help_path = g_strconcat ("dialogs/color_selectors/",
			   cnp->cur_page->info->help_page,
			   NULL);
775
  gimp_standard_help_func (help_path);
776 777 778
  g_free (help_path);
}

779
/**************************/
780 781
/* Registration functions */

782
G_MODULE_EXPORT
783
GimpColorSelectorID
784 785
gimp_color_selector_register (const gchar              *name,
			      const gchar              *help_page,
786 787 788 789 790 791 792 793 794 795 796 797 798
			      GimpColorSelectorMethods *methods)
{
  ColorSelectorInfo *info;

  /* check the name is unique */
  info = selector_info;
  while (info)
    {
      if (!strcmp (info->name, name))
	return NULL;
      info = info->next;
    }

799
  info = g_new (ColorSelectorInfo, 1);
800

801
  info->name      = g_strdup (name);
802
  info->help_page = g_strdup (help_page);
803 804 805 806 807
  info->methods   = *methods;
  info->refs      = 0;
  info->active    = TRUE;

  info->next      = selector_info;
808 809 810 811 812 813 814

  selector_info = info;
  
  return info;
}


815
G_MODULE_EXPORT
816
gboolean
817 818 819
gimp_color_selector_unregister (GimpColorSelectorID          id,
				GimpColorSelectorFinishedCB  callback,
				gpointer                     data)
820 821 822 823 824 825 826 827
{
  ColorSelectorInfo *info;

  info = selector_info;
  while (info)
    {
      if (info == id)
	{
828
	  info->active         = FALSE;
829
	  info->death_callback = callback;
830
	  info->death_data     = data;
831
	  if (info->refs == 0)
832
	    color_notebook_selector_death (info);
833 834 835 836 837 838 839 840 841 842
	  return TRUE;
	}
      info = info->next;
    }

  g_warning ("unknown color selector id %p", id);
  return FALSE;
}

static void
843
color_notebook_selector_death (ColorSelectorInfo *info)
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
{
  ColorSelectorInfo *here, *prev;

  here = selector_info;
  prev = NULL;
  while (here)
    {
      if (here == info)
	{	  
	  if (prev)
	    prev->next = info->next;
	  else
	    selector_info = info->next;

	  if (info->death_callback)
	    (*info->death_callback) (info->death_data);

	  g_free (info->name);
	  g_free (info);

	  return;
	}
      prev = here;
      here = here->next;
    }

  g_warning ("color selector %p not found, can't happen!", info);
}
872 873 874 875 876 877 878 879 880 881 882

static void
color_notebook_update (ColorNotebook           *cnp,
		       ColorNotebookUpdateType  update)
{
  if (!cnp)
    return;

  if (update & UPDATE_NOTEBOOK)
    color_notebook_update_notebook (cnp);

883 884 885
  if (update & UPDATE_CHANNEL)
    color_notebook_update_channel (cnp);

886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
  if (update & UPDATE_NEW_COLOR)
    color_notebook_update_colors (cnp, UPDATE_NEW_COLOR);

  if (update & UPDATE_ORIG_COLOR)
    color_notebook_update_colors (cnp, UPDATE_ORIG_COLOR);

  if (update & UPDATE_CALLER && cnp->wants_updates)
    color_notebook_update_caller (cnp);
}

static void
color_notebook_update_notebook (ColorNotebook *cnp)
{
  ColorSelectorInstance *csel;

  g_return_if_fail (cnp != NULL);

  csel = cnp->cur_page;
Sven Neumann's avatar
Sven Neumann committed
904 905 906 907 908

  if (csel->info->methods.set_color)   
    csel->info->methods.set_color (csel->selector_data,
				   &cnp->hsv,
				   &cnp->rgb);
909 910 911 912 913 914 915 916 917 918
}

static void
color_notebook_update_channel (ColorNotebook *cnp)
{
  ColorSelectorInstance *csel;

  g_return_if_fail (cnp != NULL);

  csel = cnp->cur_page;
Sven Neumann's avatar
Sven Neumann committed
919 920 921 922

  if (csel->info->methods.set_channel)   
    csel->info->methods.set_channel (csel->selector_data,
				     cnp->active_channel);
923 924 925 926 927 928 929
}

static void
color_notebook_update_caller (ColorNotebook *cnp)
{
  if (cnp && cnp->callback)
    {
930 931
      (* cnp->callback) (cnp,
			 &cnp->rgb,
932 933 934 935 936 937 938 939 940 941 942 943
			 COLOR_NOTEBOOK_UPDATE,
			 cnp->client_data);
    }
}

static void
color_notebook_update_colors (ColorNotebook           *cnp,
			      ColorNotebookUpdateType  which)
{
  if (!cnp)
    return;

944
  switch (which)
945
    {
946 947 948 949 950 951 952 953 954 955 956 957 958
    case UPDATE_ORIG_COLOR:
      gimp_color_area_set_color (GIMP_COLOR_AREA (cnp->orig_color), 
				 &cnp->orig_rgb);
      break;
      
    case UPDATE_NEW_COLOR:
      gtk_signal_handler_block_by_data (GTK_OBJECT (cnp->new_color), cnp);
      gimp_color_area_set_color (GIMP_COLOR_AREA (cnp->new_color), 
				 &cnp->rgb);
      gtk_signal_handler_unblock_by_data (GTK_OBJECT (cnp->new_color), cnp);
      break;
      
    default:
959 960 961 962
      return;
    }
}

963 964 965
static void
color_notebook_update_rgb_values (ColorNotebook *cnp)
{
966
  if (! cnp)
967 968
    return;

969
  gimp_hsv_to_rgb (&cnp->hsv, &cnp->rgb);
970 971 972 973 974
}

static void
color_notebook_update_hsv_values (ColorNotebook *cnp)
{
975
  if (! cnp)
976 977
    return;

978
  gimp_rgb_to_hsv (&cnp->rgb, &cnp->hsv);
979 980 981 982 983 984
}

static void
color_notebook_update_scales (ColorNotebook *cnp,
			      gint           skip)
{
985
  gint  values[7];
Sven Neumann's avatar
Sven Neumann committed
986
  gchar buffer[8];
987 988
  gint  i;

989
  if (! cnp)
990 991
    return;

992 993 994 995 996 997 998 999
  values[GIMP_COLOR_SELECTOR_HUE]        = (gint) (cnp->hsv.h * 360.999);
  values[GIMP_COLOR_SELECTOR_SATURATION] = (gint) (cnp->hsv.s * 100.999);
  values[GIMP_COLOR_SELECTOR_VALUE]      = (gint) (cnp->hsv.v * 100.999);
  values[GIMP_COLOR_SELECTOR_RED]        = (gint) (cnp->rgb.r * 255.999);
  values[GIMP_COLOR_SELECTOR_GREEN]      = (gint) (cnp->rgb.g * 255.999);
  values[GIMP_COLOR_SELECTOR_BLUE]       = (gint) (cnp->rgb.b * 255.999);
  values[GIMP_COLOR_SELECTOR_ALPHA]      = (gint) (cnp->rgb.a * 100.999);

1000
  for (i = 0; i < (cnp->show_alpha ? 7 : 6); i++)
1001 1002
    if (i != skip)
      {
1003 1004
	gtk_signal_handler_block_by_data (GTK_OBJECT (cnp->slider_data[i]),
					  cnp);
1005
	gtk_adjustment_set_value (GTK_ADJUSTMENT (cnp->slider_data[i]),
1006 1007 1008
				  values[i]);
	gtk_signal_handler_unblock_by_data (GTK_OBJECT (cnp->slider_data[i]),
					    cnp);
1009 1010
      }

Sven Neumann's avatar
Sven Neumann committed
1011 1012 1013 1014 1015 1016 1017 1018 1019
  g_print ("RGB: %d %d %d HSV: %d %d %d ALPHA: %d\n", 
	   values[GIMP_COLOR_SELECTOR_RED],
	   values[GIMP_COLOR_SELECTOR_GREEN],
	   values[GIMP_COLOR_SELECTOR_BLUE],
	   values[GIMP_COLOR_SELECTOR_HUE],
	   values[GIMP_COLOR_SELECTOR_SATURATION],
	   values[GIMP_COLOR_SELECTOR_VALUE],
	   values[GIMP_COLOR_SELECTOR_ALPHA]);

1020
  g_snprintf (buffer, sizeof (buffer), "#%.2x%.2x%.2x",
1021 1022 1023
              values[GIMP_COLOR_SELECTOR_RED],
              values[GIMP_COLOR_SELECTOR_GREEN],
              values[GIMP_COLOR_SELECTOR_BLUE]);
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042

  gtk_entry_set_text (GTK_ENTRY (cnp->hex_entry), buffer);
}

static void
color_notebook_toggle_update (GtkWidget *widget,
			      gpointer   data)
{
  ColorNotebook *cnp;
  gint           i;

  if (! GTK_TOGGLE_BUTTON (widget)->active)
    return;

  cnp = (ColorNotebook *) data;

  if (!cnp)
    return;

1043
  for (i = 0; i < 6; i++)
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
    if (widget == cnp->toggles[i])
      cnp->active_channel = (GimpColorSelectorChannelType) i;

  color_notebook_update (cnp, UPDATE_CHANNEL);
}

static void
color_notebook_scale_update (GtkAdjustment *adjustment,
			     gpointer       data)
{
  ColorNotebook *cnp;
1055
  gint           i;
1056 1057 1058 1059 1060 1061

  cnp = (ColorNotebook *) data;

  if (!cnp)
    return;

1062
  for (i = 0; i < (cnp->show_alpha ? 7 : 6); i++)
1063 1064 1065
    if (cnp->slider_data[i] == GTK_OBJECT (adjustment))
      break;

1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
  switch (i)
    {
    case GIMP_COLOR_SELECTOR_HUE:
      cnp->hsv.h = GTK_ADJUSTMENT (adjustment)->value / 360.0;
      break;

    case GIMP_COLOR_SELECTOR_SATURATION:
      cnp->hsv.s = GTK_ADJUSTMENT (adjustment)->value / 100.0;
      break;

    case GIMP_COLOR_SELECTOR_VALUE:
      cnp->hsv.v = GTK_ADJUSTMENT (adjustment)->value / 100.0;
      break;

    case GIMP_COLOR_SELECTOR_RED:
      cnp->rgb.r = GTK_ADJUSTMENT (adjustment)->value / 255.0;
      break;

    case GIMP_COLOR_SELECTOR_GREEN:
      cnp->rgb.g = GTK_ADJUSTMENT (adjustment)->value / 255.0;
      break;

    case GIMP_COLOR_SELECTOR_BLUE:
      cnp->rgb.b = GTK_ADJUSTMENT (adjustment)->value / 255.0;
      break;

    case GIMP_COLOR_SELECTOR_ALPHA:
      cnp->hsv.a = cnp->rgb.a = GTK_ADJUSTMENT (adjustment)->value / 100.0;
      break;
    }
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122

  if ((i >= GIMP_COLOR_SELECTOR_HUE) && (i <= GIMP_COLOR_SELECTOR_VALUE))
    {
      color_notebook_update_rgb_values (cnp);
    }
  else if ((i >= GIMP_COLOR_SELECTOR_RED) && (i <= GIMP_COLOR_SELECTOR_BLUE))
    {
      color_notebook_update_hsv_values (cnp);
    }

  color_notebook_update_scales (cnp, i);

  color_notebook_update (cnp,
			 UPDATE_NOTEBOOK  |
			 UPDATE_NEW_COLOR |
			 UPDATE_CALLER);
}

static gint
color_notebook_hex_entry_events (GtkWidget *widget,
				 GdkEvent  *event,
				 gpointer   data)
{
  ColorNotebook *cnp;
  gchar          buffer[8];
  gchar         *hex_color;
  guint          hex_rgb;
Sven Neumann's avatar
Sven Neumann committed
1123
  guchar         r, g, b;
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133