executor/src/qColorPicker.c

1411 lines
35 KiB
C

/* Copyright 1995, 1996 by Abacus Research and
* Development, Inc. All rights reserved.
*/
#if !defined (OMIT_RCSID_STRINGS)
char ROMlib_rcsid_qColorPicker[] =
"$Id: qColorPicker.c 88 2005-05-25 03:59:37Z ctm $";
#endif
#include "rsys/common.h"
#include "WindowMgr.h"
#include "ControlMgr.h"
#include "QuickDraw.h"
#include "CQuickDraw.h"
#include "TextEdit.h"
#include "EventMgr.h"
#include "ToolboxEvent.h"
#include "OSEvent.h"
#include "rsys/cquick.h"
#include "rsys/ctl.h"
/* ### hack for `image_bits ()' */
#include "rsys/menu.h"
#include "rsys/color_wheel_bits.h"
#include "rsys/osevent.h"
#include "rsys/options.h"
#include <ctype.h>
#include <math.h>
/* sanity defines */
#define _NewCWindow(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
((WindowPtr) NewCWindow (arg0, arg1, arg2, arg3, arg4, \
(CWindowPtr) (arg5), arg6, arg7))
#define _FindControl(arg0, arg1, arg2) \
({ \
int16 retval; \
HIDDEN_ControlHandle bogo_c; \
\
retval = FindControl (arg0, arg1, &bogo_c); \
*(arg2) = MR (bogo_c.p); \
\
retval; \
})
#define sqr(v) ({ typeof (v) _v = (v); _v * _v; })
#define color_picker_window_bounds (_bounds)
#define ok_button_bounds (&_bounds[1])
#define cancel_button_bounds (&_bounds[2])
#define template_text_box_bounds (&_bounds[3])
#define template_compare_box_bounds (&_bounds[9])
#define prompt_bounds (&_bounds[12])
#define template_color_wheel_bounds (&_bounds[13])
static Rect _bounds[] =
{
/* top, left, bottom, right */
/* color picker window bounds; filled in by `compute_bounds' */
{ CWC (-1), CWC (-1), CWC (-1), CWC (-1) },
/* ok button bounds */
{ CWC (330), CWC (410), CWC (350), CWC (470) },
/* cancel button bounds */
{ CWC (330), CWC (337), CWC (350), CWC (397) },
/* text entry box rects */
{ CWC (324), CWC (10), CWC (350), CWC (90) },
{ CWC (299), CWC (10), CWC (325), CWC (90) },
{ CWC (274), CWC (10), CWC (300), CWC (90) },
{ CWC (228), CWC (10), CWC (254), CWC (90) },
{ CWC (203), CWC (10), CWC (229), CWC (90) },
{ CWC (178), CWC (10), CWC (204), CWC (90) },
/* rectangle for the color comparison boxes */
/* frame */
{ CWC (78), CWC (0), CWC (138), CWC (100) },
/* orig, current */
{ CWC (108), CWC (2), CWC (136), CWC (98) },
{ CWC (80), CWC (2), CWC (108), CWC (98) },
/* prompt bounds */
{ CWC (10), CWC (10), CWC (60), CWC (240) },
/* color wheel bounds */
{ CWC (20), CWC (268), CWC (212), CWC (460) },
};
#define compare_box_frame_bounds (compare_box_bounds)
#define orig_compare_box_bounds (&compare_box_bounds[1])
#define current_compare_box_bounds (&compare_box_bounds[2])
static Rect compare_box_bounds[3];
static Rect color_wheel_bounds[1];
static int color_wheel_center_x, color_wheel_center_y;
#define N_TEXT_BOXES (6)
struct text_box
{
Point title_pt;
StringPtr title;
Point label_pt;
StringPtr label;
Rect frame_rect;
Rect te_rect;
Rect miniarrow_rect;
/* ### maintaining seperate integer/factional portions is way too
complicated nad messy. i did this because originally i wanted to
avoid using floating point (but that became necessary to
generate/interpret the color wheel target location), and having
all the color values stored in 16bit quanities resulted in
roundoff when converting to text and back again */
int integer, fractional;
int *value;
int max;
int continuous_p;
int i;
} text_boxes[N_TEXT_BOXES];
TEHandle te;
/* index into `text_boxes' which `te' currently represents */
static struct text_box *te_box;
static WindowPtr color_picker_window;
static ControlHandle cancel_button, ok_button;
static StringPtr prompt_text;
static int font_height, font_ascent;
static int space_width;
static void
text_box_update_value (struct text_box *box, int newval,
boolean_t update_if_p);
static void
text_box_update_value_1 (struct text_box *box, int newval,
boolean_t update_if_p,
boolean_t update_color_wheel_target_p);
static void
text_box_update_if_value (struct text_box *box,
int new_integer, int new_fractional);
static void
text_box_update_if_value_1 (struct text_box *box,
int new_integer, int new_fractional);
static void val_if (int val, int max,
int *integer_return, int *fractional_return);
static int if_val (int max, int integer, int fractional);
static void color_wheel_update (void);
static void color_wheel_notice_lightness_change (void);
static void color_wheel_target_update (boolean_t short_cut_p);
#define red_index (0)
#define green_index (1)
#define blue_index (2)
#define hue_index (3)
#define saturation_index (4)
#define lightness_index (5)
static int red;
static int green;
static int blue;
static int hue;
static int saturation;
static int lightness;
static RGBColor current_color;
static RGBColor orig_color;
static void
compute_bounds (Point maybe_top_left)
{
int width, height;
int top, left;
/* note: must match width/height for `color_picker_window_bounds' */
width = 480;
height = 360;
if ((! maybe_top_left.v && ! maybe_top_left.h)
/* #### ignore the application suggestion, and always center the
color picker sanely
for some reason, Tex-Edit passes in `-1, -1' as the suggested
location */
|| 1)
{
Rect *gd_rect;
int gd_width;
int gd_height;
gd_rect = &GD_RECT (MR (MainDevice));
gd_width = RECT_WIDTH (gd_rect);
gd_height = RECT_HEIGHT (gd_rect);
/* centered horizontally, with a third of the space above the
window, and two-thirds below */
top = CW (gd_rect->top) + (gd_height - height) / 3;
left = CW (gd_rect->left) + (gd_width - width) / 2;
}
else
{
top = maybe_top_left.v;
left = maybe_top_left.h;
}
color_picker_window_bounds->top = CW (top);
color_picker_window_bounds->left = CW (left);
color_picker_window_bounds->bottom = CW (top + height);
color_picker_window_bounds->right = CW (left + width);
}
typedef enum miniarrow_hilite
{
miniarrow_no_hilite,
miniarrow_up_hilite,
miniarrow_down_hilite,
} miniarrow_hilite_t;
static boolean_t integer_increment_p;
static int track_kount;
static void
miniarrow_track (struct text_box *box, miniarrow_hilite_t _hilite)
{
int integer, fractional, max;
boolean_t continuous_p;
integer = box->integer;
fractional = box->fractional;
max = box->max;
continuous_p = box->continuous_p;
switch (_hilite)
{
case miniarrow_up_hilite:
if (integer_increment_p)
{
if (integer == max)
{
if (continuous_p)
integer = 0;
}
else
integer ++;
}
else if (fractional == 0
&& integer == max)
{
if (continuous_p)
integer = 0;
}
else if (fractional == 9)
{
if (track_kount > 5)
integer_increment_p = TRUE;
integer ++;
fractional = 0;
}
else
fractional ++;
break;
case miniarrow_down_hilite:
if (integer_increment_p)
{
if (integer == 0)
{
if (continuous_p)
integer = max;
}
else
integer --;
}
else if (fractional == 0
&& integer == 0)
{
if (continuous_p)
integer = max;
}
else if (fractional == 0)
{
integer --;
if (track_kount > 5)
integer_increment_p = TRUE;
else
fractional = 9;
}
else
fractional --;
break;
default:
break;
}
/* note that the integer and/or fractional values have changed */
text_box_update_if_value (box, integer, fractional);
track_kount ++;
}
static void
text_box_init (void)
{
static StringPtr titles[N_TEXT_BOXES] =
{
(StringPtr) "\005Red: ",
(StringPtr) "\007Green: ",
(StringPtr) "\006Blue: ",
(StringPtr) "\013Hue Angle: ",
(StringPtr) "\014Saturation: ",
(StringPtr) "\013Lightness: ",
};
static StringPtr labels[N_TEXT_BOXES] =
{
(StringPtr) "\001%",
(StringPtr) "\001%",
(StringPtr) "\001%",
(StringPtr) "\001\241", /* degree symbol? */
(StringPtr) "\001%",
(StringPtr) "\001%",
};
int *values[N_TEXT_BOXES] =
{
&red, &green, &blue, &hue, &saturation, &lightness,
};
int maxi[N_TEXT_BOXES] = { 100, 100, 100, 360, 100, 100 };
int offset, label_width;
int i;
/* compute the text box offset caused by the titles */
offset = -1;
label_width = -1;
for (i = 0; i < (int) NELEM (titles); i ++)
{
StringPtr title, label;
title = titles[i];
label = labels[i];
offset = MAX (offset, StringWidth (title));
label_width = MAX (label_width, StringWidth (label));
}
for (i = 0; i < N_TEXT_BOXES; i ++)
{
struct text_box *box;
Rect *template_bounds, *frame_bounds, *te_bounds;
Rect *miniarrow_bounds;
int baseline;
Point *title_pt, *label_pt;
StringPtr title, label;
template_bounds = &template_text_box_bounds[i];
title = titles[i];
label = labels[i];
box = &text_boxes[i];
box->i = i;
box->title = title;
box->label = label;
box->max = maxi[i];
box->value = values[i];
box->continuous_p = (i == hue_index);
/* set up the default integer/factional values based on the
default value */
val_if (*(box->value), box->max, &box->integer, &box->fractional);
frame_bounds = &box->frame_rect;
te_bounds = &box->te_rect;
*frame_bounds = *template_bounds;
OffsetRect (frame_bounds, offset, 0);
*te_bounds = *frame_bounds;
InsetRect (te_bounds, 4, 4);
baseline = ( ( CW (frame_bounds->top)
+ CW (frame_bounds->bottom)) / 2
- font_height / 2 + font_ascent);
title_pt = &box->title_pt;
label_pt = &box->label_pt;
title_pt->v = label_pt->v = baseline;
title_pt->h = CW (template_bounds->left) + offset - StringWidth (title);
label_pt->h = CW (frame_bounds->right) + space_width / 2;
miniarrow_bounds = &box->miniarrow_rect;
*miniarrow_bounds = *frame_bounds;
miniarrow_bounds->left = CW ( label_pt->h
+ label_width
+ space_width / 2);
miniarrow_bounds->right = CW ( label_pt->h
+ label_width
+ space_width / 2
+ 22);
}
for (i = 0; i < 3; i ++)
{
Rect *template_bounds, *bounds;
bounds = &compare_box_bounds[i];
template_bounds = &template_compare_box_bounds[i];
*bounds = *template_bounds;
OffsetRect (bounds, offset, 0);
}
}
static void
str_if (char *text, int len,
int *return_integer, int *return_fractional)
{
char intbuf[16], fractbuf[16];
int i;
for (i = 0; i < 15 && i < len && isdigit (text[i]); i ++)
intbuf[i] = text[i];
intbuf[i] = '\000';
if (i < 16 && i < len && text[i] == '.' && isdigit (text[i + 1]))
{
fractbuf[0] = text[i + 1];
fractbuf[1] = '\000';
}
else
*fractbuf = '\000';
*return_integer = atoi (intbuf);
*return_fractional = atoi (fractbuf);
}
static char normal_bits[] =
{
image_bits (00000000), image_bits (00000000),
image_bits (00011111), image_bits (11000000),
image_bits (00100000), image_bits (00100000),
image_bits (01000000), image_bits (00010000),
image_bits (01000010), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01001111), image_bits (10010000),
image_bits (01011111), image_bits (11010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000000), image_bits (00010000),
image_bits (01000000), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01011111), image_bits (11010000),
image_bits (01001111), image_bits (10010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000010), image_bits (00010000),
image_bits (01000000), image_bits (00010000),
image_bits (00100000), image_bits (00100000),
image_bits (00011111), image_bits (11000000),
image_bits (00000000), image_bits (00000000),
};
static char lower_highlighted_bits[] =
{
image_bits (00000000), image_bits (00000000),
image_bits (00011111), image_bits (11000000),
image_bits (00100000), image_bits (00100000),
image_bits (01000000), image_bits (00010000),
image_bits (01000010), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01001111), image_bits (10010000),
image_bits (01011111), image_bits (11010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000000), image_bits (00010000),
image_bits (01111111), image_bits (11110000),
image_bits (01111000), image_bits (11110000),
image_bits (01111000), image_bits (11110000),
image_bits (01100000), image_bits (00110000),
image_bits (01110000), image_bits (01110000),
image_bits (01111000), image_bits (11110000),
image_bits (01111101), image_bits (11110000),
image_bits (01111111), image_bits (11110000),
image_bits (00111111), image_bits (11100000),
image_bits (00011111), image_bits (11000000),
image_bits (00000000), image_bits (00000000),
};
static char upper_highlighted_bits[] =
{
image_bits (00000000), image_bits (00000000),
image_bits (00011111), image_bits (11000000),
image_bits (00111111), image_bits (11100000),
image_bits (01111111), image_bits (11110000),
image_bits (01111101), image_bits (11110000),
image_bits (01111000), image_bits (11110000),
image_bits (01110000), image_bits (01110000),
image_bits (01100000), image_bits (00110000),
image_bits (01111000), image_bits (11110000),
image_bits (01111000), image_bits (11110000),
image_bits (01111111), image_bits (11110000),
image_bits (01000000), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000111), image_bits (00010000),
image_bits (01011111), image_bits (11010000),
image_bits (01001111), image_bits (10010000),
image_bits (01000111), image_bits (00010000),
image_bits (01000010), image_bits (00010000),
image_bits (01000000), image_bits (00010000),
image_bits (00100000), image_bits (00100000),
image_bits (00011111), image_bits (11000000),
image_bits (00000000), image_bits (00000000),
};
static void
text_box_miniarrow_update (struct text_box *box,
miniarrow_hilite_t _hilite)
{
Rect dst_rect, *miniarrow_rect;
BitMap miniarrow_bitmap;
int center_x, center_y;
char *bits = NULL;
switch (_hilite)
{
case miniarrow_no_hilite:
bits = normal_bits;
break;
case miniarrow_up_hilite:
bits = upper_highlighted_bits;
break;
case miniarrow_down_hilite:
bits = lower_highlighted_bits;
break;
}
miniarrow_bitmap.baseAddr = RM ((Ptr) bits);
miniarrow_bitmap.rowBytes = CWC (2);
SetRect (&miniarrow_bitmap.bounds, 0, 0, 13, 22);
miniarrow_rect = &box->miniarrow_rect;
center_x = (CW (miniarrow_rect->left) + CW (miniarrow_rect->right)) / 2;
center_y = (CW (miniarrow_rect->top) + CW (miniarrow_rect->bottom)) / 2;
SetRect (&dst_rect,
center_x - 13 / 2, center_y - 22 / 2,
center_x - 13 / 2 + 13, center_y - 22 / 2 + 22);
CopyBits (&miniarrow_bitmap, PORT_BITS_FOR_COPY (thePort),
&miniarrow_bitmap.bounds, &dst_rect, srcCopy, NULL);
}
static void
text_box_update (struct text_box *box, boolean_t update_text_p)
{
char buf[16];
MoveTo (box->title_pt.h, box->title_pt.v);
DrawString (box->title);
sprintf (buf, "%d.%d", box->integer, box->fractional);
if (te_box == box)
{
if (update_text_p)
{
Rect dummy_rect;
te_box = NULL;
TEDeactivate (te);
memset (&dummy_rect, '\000', sizeof dummy_rect);
TE_DEST_RECT (te) = dummy_rect;
TE_VIEW_RECT (te) = dummy_rect;
TextBox ((Ptr) buf, strlen (buf), &box->te_rect, teFlushRight);
}
else
TEUpdate (&box->te_rect, te);
}
else
TextBox ((Ptr) buf, strlen (buf), &box->te_rect, teFlushRight);
PenSize (1, 1);
FrameRect (&box->frame_rect);
MoveTo (box->label_pt.h, box->label_pt.v);
DrawString (box->label);
}
static int
if_val (int max, int integer, int fractional)
{
return ((integer * 0xFFFF) + (fractional * 0x1999)) / max;
}
static void
val_if (int val, int max,
int *integer_return, int *fractional_return)
{
*integer_return = (val * max) / 0xFFFF;
*fractional_return = (((val * max) - (*integer_return * 0xFFFF)) * 10) / 0xFFFF;
}
static void
text_box_update_value_1 (struct text_box *box, int newval,
boolean_t update_if_p,
boolean_t update_color_wheel_target_p)
{
int *value;
value = box->value;
if (*value == newval)
{
/* redisplay even if the values are same, so the display always
appears `cannonical' (even if, say, the user sets the text
box to `' (whic defaults to zero), and `*value' is zero) */
text_box_update (box, TRUE);
return;
}
*value = newval;
if (update_if_p)
{
int integer, fractional;
val_if (newval, box->max, &integer, &fractional);
text_box_update_if_value_1 (box, integer, fractional);
}
switch (box->i)
{
case red_index:
current_color.red = CW (red);
break;
case green_index:
current_color.green = CW (green);
break;
case blue_index:
current_color.blue = CW (blue);
break;
case hue_index:
case saturation_index:
if (update_color_wheel_target_p)
color_wheel_target_update (TRUE);
case lightness_index:
color_wheel_notice_lightness_change ();
}
/* redraw the box with the `cannonicalized' value */
text_box_update (box, TRUE);
}
static void
compare_box_update (void)
{
/* ### titles? */
/* draw the color comparison box */
RGBForeColor (&orig_color);
FillRect (orig_compare_box_bounds, black);
RGBForeColor (&current_color);
FillRect (current_compare_box_bounds, black);
PenSize (2, 2);
ForeColor (blackColor);
FrameRect (compare_box_frame_bounds);
}
static void
hue_saturation_update (int new_hue, int new_saturation,
boolean_t full_update_p)
{
RGBColor rgb_color;
HSLColor hsl_color;
if (hue == new_hue && saturation == new_saturation)
return;
text_box_update_value_1 (&text_boxes[hue_index], new_hue,
TRUE, FALSE);
text_box_update_value_1 (&text_boxes[saturation_index], new_saturation,
TRUE, FALSE);
if (full_update_p)
{
hsl_color.hue = CW (hue);
hsl_color.saturation = CW (saturation);
hsl_color.lightness = CW (lightness);
HSL2RGB (&hsl_color, &rgb_color);
text_box_update_value_1 (&text_boxes[red_index],
CW (rgb_color.red), TRUE, TRUE);
text_box_update_value_1 (&text_boxes[green_index],
CW (rgb_color.green), TRUE, TRUE);
text_box_update_value_1 (&text_boxes[blue_index],
CW (rgb_color.blue), TRUE, TRUE);
compare_box_update ();
}
color_wheel_target_update (TRUE);
}
static void
text_box_update_if_value_1 (struct text_box *box,
int new_integer, int new_fractional)
{
box->integer = new_integer;
box->fractional = new_fractional;
text_box_update (box, TRUE);
}
static void
text_box_update_if_value (struct text_box *box,
int new_integer, int new_fractional)
{
if (box->integer == new_integer
&& box->fractional == new_fractional)
{
/* to keep the box `cannonical' */
text_box_update (box, TRUE);
return;
}
text_box_update_if_value_1 (box, new_integer, new_fractional);
text_box_update_value (box, if_val (box->max, new_integer, new_fractional),
FALSE);
}
/* the value of `box' has changed, and we need to recompute the
values for the `dual' color because of it */
static void
text_box_update_value (struct text_box *box, int newval,
boolean_t update_if_p)
{
RGBColor rgb_color;
HSLColor hsl_color;
switch (box->i)
{
case red_index:
case green_index:
case blue_index:
{
text_box_update_value_1 (box, newval, update_if_p, TRUE);
rgb_color.red = CW (red);
rgb_color.green = CW (green);
rgb_color.blue = CW (blue);
RGB2HSL (&rgb_color, &hsl_color);
hue_saturation_update (CW (hsl_color.hue), CW (hsl_color.saturation),
FALSE);
text_box_update_value_1 (&text_boxes[lightness_index],
CW (hsl_color.lightness), TRUE, TRUE);
break;
}
case hue_index:
case saturation_index:
case lightness_index:
{
text_box_update_value_1 (box, newval, update_if_p, TRUE);
hsl_color.hue = CW (hue);
hsl_color.saturation = CW (saturation);
hsl_color.lightness = CW (lightness);
HSL2RGB (&hsl_color, &rgb_color);
text_box_update_value_1 (&text_boxes[red_index],
CW (rgb_color.red), TRUE, TRUE);
text_box_update_value_1 (&text_boxes[green_index],
CW (rgb_color.green), TRUE, TRUE);
text_box_update_value_1 (&text_boxes[blue_index],
CW (rgb_color.blue), TRUE, TRUE);
break;
}
}
compare_box_update ();
}
static void
text_box_set_te (struct text_box *box)
{
struct text_box *orig_te_box;
char buf[16], *text;
Handle texth;
if (box == te_box)
return;
orig_te_box = te_box;
te_box = box;
TEDeactivate (te);
if (orig_te_box)
{
int integer, fractional;
int max;
texth = TE_HTEXT (te);
text = (char *) STARH (texth);
str_if (text, TE_LENGTH (te), &integer, &fractional);
max = orig_te_box->max;
if (integer > max
|| (integer == max && fractional))
{
integer = max;
fractional = 0;
}
text_box_update_if_value (orig_te_box, integer, fractional);
}
/* slimy */
TE_DEST_RECT (te) = box->te_rect;
TE_VIEW_RECT (te) = box->te_rect;
sprintf (buf, "%d.%d", box->integer, box->fractional);
TESetText ((Ptr) buf, strlen (buf), te);
TESetSelect (0, TE_LENGTH (te), te);
TEActivate (te);
}
static boolean_t
event_loop (void)
{
EventRecord evt;
for (;;)
{
GetNextEvent (( mDownMask | mUpMask
| keyDownMask | keyUpMask | autoKeyMask
| updateMask | activMask), &evt);
TEIdle (te);
switch (CW (evt.what))
{
case mouseDown:
{
Point local_pt;
boolean_t control_p;
ControlHandle c;
int16 release_part;
int i;
/* ### beep if the mousedown is not in
`color_picker_window' */
local_pt = evt.where;
GlobalToLocal (&local_pt);
local_pt = SWAP_POINT (local_pt);
control_p = _FindControl (local_pt, color_picker_window, &c);
if (control_p)
{
release_part = TrackControl (c, local_pt, (ProcPtr) -1);
if (release_part == inButton && c == ok_button)
return TRUE;
if (release_part == inButton && c == cancel_button)
return FALSE;
break;
}
for (i = 0; i < N_TEXT_BOXES; i ++)
{
struct text_box *box;
box = &text_boxes[i];
if (PtInRect (local_pt, &box->te_rect))
{
text_box_set_te (box);
TEClick (local_pt, ((evt.modifiers & CWC (shiftKey))
? TRUE
: FALSE),
te);
break;
}
else if (PtInRect (local_pt, &box->miniarrow_rect))
{
int center_y;
center_y = ( CW (box->miniarrow_rect.top)
+ CW (box->miniarrow_rect.bottom)) / 2;
integer_increment_p = FALSE;
track_kount = 0;
goto handle_pt_1;
while (! GetOSEvent (mUpMask, &evt))
{
miniarrow_hilite_t _hilite;
local_pt = evt.where;
GlobalToLocal (&local_pt);
local_pt = SWAP_POINT (local_pt);
handle_pt_1:
if (PtInRect (local_pt, &box->miniarrow_rect))
{
if (local_pt.v < center_y)
_hilite = miniarrow_up_hilite;
else
_hilite = miniarrow_down_hilite;
}
else
_hilite = miniarrow_no_hilite;
text_box_miniarrow_update (box, _hilite);
miniarrow_track (box, _hilite);
}
text_box_miniarrow_update (box, miniarrow_no_hilite);
}
}
if (PtInRect (local_pt, color_wheel_bounds))
{
goto handle_pt_2;
while (! GetOSEvent (mUpMask, &evt))
{
int x, y;
double saturation_fp;
local_pt = evt.where;
GlobalToLocal (&local_pt);
local_pt = SWAP_POINT (local_pt);
handle_pt_2:
x = local_pt.h - color_wheel_center_x;
y = local_pt.v - color_wheel_center_y;
saturation_fp = sqrt (sqr (x) + sqr (y));
if (saturation_fp < (192 / 2))
{
int hue, saturation;
hue = (atan2 (y, -x) + M_PI) / M_PI / 2 * 0xFFFF;
saturation = saturation_fp / (192 / 2) * 0xFFFF;
hue_saturation_update (hue, saturation, TRUE);
}
}
}
break;
}
case mouseUp:
break;
case updateEvt:
{
BeginUpdate (color_picker_window);
DrawControls (color_picker_window);
{
/* draw bold border around the default button */
Rect r = *ok_button_bounds;
int oval;
InsetRect (&r, -4, -4);
PenSize (3, 3);
oval = RECT_HEIGHT (&r) / 2 - 4;
/* ### frame in the frame color for the `ok_button'
control */
if (!(ROMlib_options & ROMLIB_RECT_SCREEN_BIT))
FrameRoundRect (&r, oval, oval);
else
FrameRect (&r);
}
/* update the text boxes */
{
int i;
for (i = 0; i < N_TEXT_BOXES; i ++)
{
struct text_box *box;
box = &text_boxes[i];
text_box_update (box, FALSE);
text_box_miniarrow_update (box, miniarrow_no_hilite);
}
}
/* draw the compare box */
compare_box_update ();
/* draw the prompt */
TextBox ((Ptr) &prompt_text[1], *prompt_text,
prompt_bounds, teFlushLeft);
color_wheel_update ();
/* compare box labels */
#define orig_compare_box_label ((StringPtr) "\012Original: ")
#define current_compare_box_label ((StringPtr) "\005New: ")
MoveTo (( CW (orig_compare_box_bounds->left)
- StringWidth (orig_compare_box_label)),
( ( CW (orig_compare_box_bounds->top)
+ CW (orig_compare_box_bounds->bottom)) / 2
- font_height / 2 + font_ascent));
DrawString (orig_compare_box_label);
MoveTo (( CW (current_compare_box_bounds->left)
- StringWidth (current_compare_box_label)),
( ( CW (current_compare_box_bounds->top)
+ CW (current_compare_box_bounds->bottom)) / 2
- font_height / 2 + font_ascent));
DrawString (current_compare_box_label);
EndUpdate (color_picker_window);
break;
}
case keyDown:
case autoKey:
{
char ch;
ch = CL (evt.message) & 0xFF;
switch (ch)
{
case '\r':
case NUMPAD_ENTER:
return TRUE;
/* ESC */
case '\033':
return FALSE;
default:
if (te_box)
{
if (ch == '\t')
text_box_set_te (&text_boxes[((te_box->i + 5)
% N_TEXT_BOXES)]);
else
TEKey (ch, te);
}
}
break;
}
case activateEvt:
case keyUp:
break;
default:
warning_unexpected ("unknown event.what `%d'",
CW (evt.what));
break;
}
}
}
static PixMap color_wheel_pixmap;
static CTabHandle color_wheel_color_table;
static ColorSpec *color_wheel_colors;
static int current_target_x, current_target_y;
static StringPtr label_0_degs = (StringPtr) "\0020\241";
static StringPtr label_90_degs = (StringPtr) "\00390\241";
static StringPtr label_180_degs = (StringPtr) "\004180\241";
static StringPtr label_270_degs = (StringPtr) "\004270\241";
static void
color_wheel_init (void)
{
int bpp;
*color_wheel_bounds = *template_color_wheel_bounds;
OffsetRect (color_wheel_bounds, - StringWidth (label_0_degs), font_height);
color_wheel_center_y = ( CW (color_wheel_bounds->top)
+ CW (color_wheel_bounds->bottom)) / 2;
color_wheel_center_x = ( CW (color_wheel_bounds->left)
+ CW (color_wheel_bounds->right)) / 2;
/* the colorwheel appears to be 192 pixels on a side, but it is
really 208 pixels to handle target overlap */
InsetRect (color_wheel_bounds, -8, -8);
bpp = PIXMAP_PIXEL_SIZE (GD_PMAP (MR (MainDevice)));
color_wheel_pixmap.baseAddr = RM ((bpp == 8)
? (Ptr) color_wheel_bits_8
: (bpp == 4
? (Ptr) color_wheel_bits_4
: (gui_abort (), NULL)));
color_wheel_pixmap.rowBytes = CW ((bpp == 8)
? 0x80D0
: (bpp == 4
? 0x8068
: (gui_abort (), -1)));
SetRect (&color_wheel_pixmap.bounds, 0, 0, 208, 208);
pixmap_set_pixel_fields (&color_wheel_pixmap, bpp);
color_wheel_pixmap.pmTable = RM (no_stdbits_color_conversion_color_table);
current_target_x = color_wheel_center_x;
current_target_y = color_wheel_center_y;
}
static void
color_wheel_target_update (boolean_t short_cut_p)
{
double dist, angle;
Rect src_rect, dst_rect;
int x, y;
/* draw the target at point corresponding to the current
hue/saturation */
angle = (double) hue / 0xFFFF * 2 * M_PI;
dist = (double) saturation / 0xFFFF * 96.0;
x = color_wheel_center_x + dist * cos (angle);
y = color_wheel_center_y - dist * sin (angle);
if (short_cut_p
&& x == current_target_x
&& y == current_target_y)
return;
/* erase the current target */
dst_rect.top = CW (current_target_y - 7);
dst_rect.left = CW (current_target_x - 7);
dst_rect.bottom = CW (current_target_y + 9);
dst_rect.right = CW (current_target_x + 9);
src_rect = dst_rect;
OffsetRect (&src_rect, - CW (color_wheel_bounds->left),
- CW (color_wheel_bounds->top));
CopyBits ((BitMap *) &color_wheel_pixmap, PORT_BITS_FOR_COPY (thePort),
&src_rect, &dst_rect, srcCopy, NULL);
PenSize (1, 1);
ForeColor (whiteColor);
MoveTo (x - 7, y); Line (6, 0);
MoveTo (x + 1, y); Line (6, 0);
MoveTo (x, y - 7); Line (0, 6);
MoveTo (x, y + 1); Line (0, 6);
ForeColor (blackColor);
MoveTo (x - 6, y + 1); Line (6, 0);
MoveTo (x + 2, y + 1); Line (6, 0);
MoveTo (x + 1, y - 6); Line (0, 6);
MoveTo (x + 1, y + 2); Line (0, 6);
current_target_x = x;
current_target_y = y;
}
static void
color_wheel_update (void)
{
CopyBits ((BitMap *) &color_wheel_pixmap, PORT_BITS_FOR_COPY (thePort),
&color_wheel_pixmap.bounds, color_wheel_bounds, srcCopy, NULL);
MoveTo (470 - StringWidth (label_0_degs),
color_wheel_center_y - font_height / 2 + font_ascent);
DrawString (label_0_degs);
MoveTo (color_wheel_center_x - StringWidth (label_90_degs) / 2,
10 + font_ascent);
DrawString (label_90_degs);
MoveTo ( 470 - StringWidth (label_0_degs) - 10
- 192
- 10 - StringWidth (label_180_degs),
color_wheel_center_y - font_height / 2 + font_ascent);
DrawString (label_180_degs);
MoveTo (color_wheel_center_x - StringWidth (label_270_degs) / 2,
10 + font_height + 10 + 192 + 10 + font_ascent);
DrawString (label_270_degs);
color_wheel_target_update (FALSE);
}
static int color_wheel_noticed_lightness;
static void
color_wheel_notice_lightness_change (void)
{
struct { double angle; double dist; } color_desc[13] =
{
{ 0.0, 0.9375 },
{ 60.0, 0.9375 },
{ 120.0, 0.9375 },
{ 180.0, 0.9375 },
{ 240.0, 0.9375 },
{ 300.0, 0.9375 },
{ 30.0, 0.625, },
{ 90.0, 0.625, },
{ 150.0, 0.625, },
{ 210.0, 0.625, },
{ 270.0, 0.625, },
{ 330.0, 0.625, },
{ 0.0, 0.0 }
};
HSLColor hsl_color;
RGBColor rgb_color;
int i;
if (color_wheel_noticed_lightness == lightness)
return;
color_wheel_noticed_lightness = lightness;
for (i = 0; i < (int) NELEM (color_desc); i ++)
{
/* one half */
hsl_color.lightness = CW (lightness);
hsl_color.hue = CW ( color_desc[i].angle
* 0xFFFF
/ 360.0);
hsl_color.saturation = CW ( color_desc[i].dist
* 0xFFFF);
HSL2RGB (&hsl_color, &rgb_color);
color_wheel_colors[i].rgb = rgb_color;
}
AnimatePalette (color_picker_window, color_wheel_color_table,
0, 1, 13);
}
/* color picker entry point */
P4 (PUBLIC pascal trap, BOOLEAN, GetColor,
Point, where,
Str255, prompt,
RGBColor *, in_color,
RGBColor *, out_color)
{
PaletteHandle palette;
boolean_t retval;
/* compute relevent font information */
{
FontInfo font_info;
GetFontInfo (&font_info);
font_height = ( CW (font_info.ascent)
+ CW (font_info.descent)
+ CW (font_info.leading));
font_ascent = CW (font_info.ascent);
space_width = CharWidth (' ');
}
{
HSLColor *hsl_color = alloca (sizeof *hsl_color);
red = CW (in_color->red);
green = CW (in_color->green);
blue = CW (in_color->blue);
RGB2HSL (in_color, hsl_color);
hue = CW (hsl_color->hue);
saturation = CW (hsl_color->saturation);
lightness = CW (hsl_color->lightness);
color_wheel_noticed_lightness = -1;
orig_color = *in_color;
current_color = *in_color;
}
prompt_text = prompt;
compute_bounds (where);
color_wheel_init ();
color_picker_window = _NewCWindow (NULL, color_picker_window_bounds,
/* no title */
NULL,
/* visible */
TRUE,
dBoxProc,
(WindowPtr) -1,
FALSE, /* dummy */ -1);
/* #### the correct thing to do is have a 24bpp color wheel, and
draw it to the screen dithered, with a reasonable colormap
guaranteed by a reasonable palette.
instead, we are going to use an animated palette and a constant
dither pattern, which won't work on non-clut devices (palette
manager will probably just eat flaming death)
it also won't work very well on 16bpp devices (which the mac does
reasonably well)
of course, even the mac doesn't try it on 4bpp or b/w devices */
palette = NewPalette (16, NULL, pmAnimated | pmExplicit, -1);
NSetPalette (color_picker_window, palette, pmAllUpdates);
SetEntryUsage (palette, 0, pmCourteous, -1);
SetEntryColor (palette, 0, &ROMlib_white_rgb_color);
SetEntryUsage (palette, 15, pmCourteous, -1);
SetEntryColor (palette, 15, &ROMlib_black_rgb_color);
/* sometimes the animated palette entries suck up the hilite color */
SetEntryUsage (palette, 14, pmTolerant, 1);
SetEntryColor (palette, 14, &HiliteRGB);
/* #### add palette entries for the {current, orig}_colors? */
color_wheel_color_table = (CTabHandle) NewHandle (CTAB_STORAGE_FOR_SIZE (23));
LOCK_HANDLE_EXCURSION_1
(color_wheel_color_table,
{
CTAB_SIZE_X (color_wheel_color_table) = CW (23);
color_wheel_colors = CTAB_TABLE (color_wheel_color_table);
color_wheel_notice_lightness_change ();
ActivatePalette (color_picker_window);
THEPORT_SAVE_EXCURSION
(color_picker_window,
{
Rect *dummy_rect = alloca (sizeof *dummy_rect);
memset (dummy_rect, '\000', sizeof *dummy_rect);
ok_button = NewControl (color_picker_window,
ok_button_bounds, (StringPtr) "\002OK",
/* visible */
TRUE,
0, 0, 1, pushButProc, -1);
cancel_button = NewControl (color_picker_window,
cancel_button_bounds,
(StringPtr) "\006Cancel",
/* visible */
TRUE,
0, 0, 1, pushButProc, -1);
te = TENew (dummy_rect, dummy_rect);
TESetJust (teFlushRight, te);
text_box_init ();
/* event loop exits when one of `Cancel' (returning zero) or
`OK' (returning one) are clicked */
retval = event_loop ();
TEDispose (te);
te_box = NULL;
});
});
DisposHandle ((Handle) color_wheel_color_table);
DisposePalette (palette);
DisposeWindow (color_picker_window);
*out_color = retval ? current_color : orig_color;
return retval;
}