ColorToolViewerImpl.mesa
Written by Darlene Plebon on August 30, 1983 5:28 pm
Last Edited by: Stone, July 8, 1983 11:59 am
DIRECTORY
ColorToolViewer,
Buttons USING [Button, ButtonProc, Create],
NewCGColor USING [HSVToRGB, RGBToHSV, HSLToRGB, RGBToHSL, undefined],
ChoiceButtons USING [BuildEnumTypeSelection, ButtonList, EnumTypeRef, GetSelectedButton, SelectionNotifierProc, UpdateChoiceButtons],
CNSColor USING [CSLToCNS, CNSToCSL, CSLChroma, CSLLightness, CSLSaturation, CSLToHSL, HSLToCSL, medium, red, vivid],
ColorMap USING [SetRGBColor],
Containers USING [Container, Create],
Graphics USING [Box, Context, DrawBox, GetBounds, SetColor],
GraphicsBasic USING [Color],
GraphicsColor USING [RGBToColor, red, green, blue],
IO USING [PutFR, real],
Menus USING [Menu],
MessageWindow USING [Append],
Real USING [ReadReal],
Rope USING [Equal, Fetch, Length, ROPE],
Sliders USING [Create, SetContents, SliderProc, sliderGray],
UserExec USING [CommandProc, RegisterCommand],
VFonts USING [CharWidth],
ViewerClasses USING [Column, PaintProc, Viewer, ViewerClass, ViewerClassRec],
ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, SetOpenHeight ],
ViewerTools USING [MakeNewTextViewer, GetContents, SetContents],
WindowManager USING [colorDisplayOn, ScreenPos, StartColorViewers, StopColorViewers];
ColorToolViewerImpl: CEDAR PROGRAM
IMPORTS Buttons, NewCGColor, ChoiceButtons, CNSColor, ColorMap, Containers, Graphics, GraphicsColor, IO, MessageWindow, Real, Rope, Sliders, UserExec, VFonts, ViewerOps, ViewerTools, WindowManager
EXPORTS ColorToolViewer =
BEGIN
entryHeight: CARDINAL = 15;  -- how tall to make each line of text items
entryVSpace: CARDINAL = 8;   -- vertical leading space between lines
entryHSpace: CARDINAL = 10;   -- horizontal space between items in a line
columnSpacing: INTEGER = 20; -- space between columns
sliderWidth: CARDINAL = 20;  -- width of all sliders in color tool
sliderHeight: CARDINAL = 102;  -- height of all sliders in color tool
initialRed: REAL = 0.75;    -- initial color values for color patch
initialGreen: REAL = 0.75;
initialBlue: REAL = 0.2;
lastHandle: Handle ← NIL;  --so the viewer can be manipulated by a program
Handle: TYPE = REF ColorToolRec;
ColorToolRec: TYPE = RECORD [
outer: Containers.Container ← NIL, -- handle for the enclosing container
height: CARDINAL ← 0,     -- height measured from the top of the container
height2: CARDINAL ← 0,    -- height of second column from top of the container
rgb: RGBViewer,      -- the RGB viewer's state
hsv: HSVViewer,      -- the HSV viewer's state
hsl: HSLViewer,       -- the HSL viewer's state
cns: CNSViewer,       -- the CNS viewer's state
colorPatch: ColorPatchViewer   -- the color patch viewer's state
];
Side: WindowManager.ScreenPos = left;
GetRGBValue: PUBLIC PROC RETURNS [red,green,blue: REAL] = {
data: ColorPatchData ← NARROW[lastHandle.colorPatch.data];
RETURN[red: data.rgbR, green: data.rgbG, blue: data.rgbB];
};
GetCSLValue: PUBLIC PROC RETURNS[cslChroma: CNSColor.CSLChroma, cslSaturation: CNSColor.CSLSaturation, cslLightness: CNSColor.CSLLightness] = {
data: ColorPatchData ← NARROW[lastHandle.colorPatch.data];
RETURN[cslChroma: data.cslC, cslSaturation: data.cslS, cslLightness: data.cslL];
};
SetRGBValue: PUBLIC PROC [red,green,blue: REAL] = {
UpdateColorViewers[handle: lastHandle, color: [rgb[red,green,blue]]];
};
SetCSLValue: PUBLIC PROC [cslChroma: CNSColor.CSLChroma, cslSaturation: CNSColor.CSLSaturation, cslLightness: CNSColor.CSLLightness] = {
UpdateColorViewers[handle: lastHandle, color: [csl[cslChroma, cslSaturation, cslLightness]]];
};
MakeColorTool: UserExec.CommandProc = {
my: Handle ← NEW[ColorToolRec];
column: ViewerClasses.Column ← color;
IF WindowManager.colorDisplayOn = FALSE THEN column ← left;
my.outer ← Containers.Create[ [
name: "Color Tool",
iconic: FALSE,
column: column,
scrollable: FALSE ] ];
MakeRGB[my];
MakeHSV[my];
MakeHSL[my];
MakeCNS[my];
MakeColorPatch[my];
MakeOnOffButtons[my];
ViewerOps.SetOpenHeight[my.outer, my.height];
UpdateColorViewers[handle: my, color: [rgb[initialRed, initialGreen, initialBlue]]];
lastHandle ← my;
};
RGBViewer: TYPE = RECORD [
redText: ViewerClasses.Viewer ← NIL,
greenText: ViewerClasses.Viewer ← NIL,
blueText: ViewerClasses.Viewer ← NIL,
redSlider: ViewerClasses.Viewer ← NIL,
greenSlider: ViewerClasses.Viewer ← NIL,
blueSlider: ViewerClasses.Viewer ← NIL
];
MakeRGB: PROCEDURE [handle: Handle] = {
[handle.rgb.redText, handle.rgb.greenText, handle.rgb.blueText, handle.rgb.redSlider, handle.rgb.greenSlider, handle.rgb.blueSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "RGB",
buttonProc: ComputeFromRGB,
sliderProc1: RedSliderUpdate,
sliderProc2: GreenSliderUpdate,
sliderProc3: BlueSliderUpdate,
color1: GraphicsColor.red,
color2: GraphicsColor.green,
color3: GraphicsColor.blue
];
};
HSVViewer: TYPE = RECORD [
hueText: ViewerClasses.Viewer ← NIL,
saturationText: ViewerClasses.Viewer ← NIL,
valueText: ViewerClasses.Viewer ← NIL,
hueSlider: ViewerClasses.Viewer ← NIL,
saturationSlider: ViewerClasses.Viewer ← NIL,
valueSlider: ViewerClasses.Viewer ← NIL
];
MakeHSV: PROCEDURE [handle: Handle] = {
[handle.hsv.hueText, handle.hsv.saturationText, handle.hsv.valueText, handle.hsv.hueSlider, handle.hsv.saturationSlider, handle.hsv.valueSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "HSV",
buttonProc: ComputeFromHSV,
sliderProc1: HueSliderUpdate,
sliderProc2: SaturationSliderUpdate,
sliderProc3: ValueSliderUpdate
];
};
HSLViewer: TYPE = RECORD [
hueText: ViewerClasses.Viewer ← NIL,
saturationText: ViewerClasses.Viewer ← NIL,
lightnessText: ViewerClasses.Viewer ← NIL,
hueSlider: ViewerClasses.Viewer ← NIL,
saturationSlider: ViewerClasses.Viewer ← NIL,
lightnessSlider: ViewerClasses.Viewer ← NIL
];
MakeHSL: PROCEDURE [handle: Handle] = {
[handle.hsl.hueText, handle.hsl.saturationText, handle.hsl.lightnessText, handle.hsl.hueSlider, handle.hsl.saturationSlider, handle.hsl.lightnessSlider] ← MakeColorSchemeViewer[
handle: handle,
name: "HSL",
buttonProc: ComputeFromHSL,
sliderProc1: HSLHueSliderUpdate,
sliderProc2: HSLSaturationSliderUpdate,
sliderProc3: HSLLightnessSliderUpdate
];
};
The following procedure gets a compiler warning: long REF-containing arg/return record is unsafe, at MakeColorSchemeViewer. Nothing has been done about it as everything seems to work!
MakeColorSchemeViewer: PROCEDURE [handle: Handle, name: Rope.ROPE, buttonProc: Buttons.ButtonProc, sliderProc1, sliderProc2, sliderProc3: Sliders.SliderProc, color1, color2, color3: GraphicsBasic.Color ← Sliders.sliderGray] RETURNS [text1, text2, text3, slider1, slider2, slider3: ViewerClasses.Viewer] = {
valueEntryWidth: CARDINAL = 8 * VFonts.CharWidth['0]; -- eight digits worth of width
button: Buttons.Button;
handle.height ← handle.height + entryVSpace;
button ← Buttons.Create[
info: [
name: name,
wy: handle.height,
wh: entryHeight,
parent: handle.outer,
border: TRUE ],
clientData: handle,
proc: buttonProc ];
text1 ← ViewerTools.MakeNewTextViewer[ [
parent: handle.outer,
wx: button.wx + button.ww + entryHSpace,
wy: handle.height+2,
ww: valueEntryWidth,
wh: entryHeight,
data: " ",
scrollable: FALSE,
border: FALSE ] ];
text2 ← ViewerTools.MakeNewTextViewer[ [
parent: handle.outer,
wx: text1.wx + text1.ww + entryHSpace,
wy: handle.height+2,
ww: valueEntryWidth,
wh: entryHeight,
data: " ",
scrollable: FALSE,
border: FALSE ] ];
text3 ← ViewerTools.MakeNewTextViewer[ [
parent: handle.outer,
wx: text2.wx + text2.ww + entryHSpace,
wy: handle.height+2,
ww: valueEntryWidth,
wh: entryHeight,
data: " ",
scrollable: FALSE,
border: FALSE ] ];
handle.height ← handle.height + entryHeight + entryVSpace;
slider1 ← Sliders.Create[
info: [
parent: handle.outer,
wx: button.wx + button.ww + entryHSpace,
wy: handle.height,
ww: sliderWidth,
wh: sliderHeight
],
foreground: color1,
clientData: handle,
sliderProc: sliderProc1
];
slider2 ← Sliders.Create[
info: [
parent: handle.outer,
wx: text1.wx + text1.ww + entryHSpace,
wy: handle.height,
ww: sliderWidth,
wh: sliderHeight
],
foreground: color2,
clientData: handle,
sliderProc: sliderProc2
];
slider3 ← Sliders.Create[
info: [
parent: handle.outer,
wx: text2.wx + text2.ww + entryHSpace,
wy: handle.height,
ww: sliderWidth,
wh: sliderHeight
],
foreground: color3,
clientData: handle,
sliderProc: sliderProc3
];
handle.height ← handle.height + sliderHeight + entryVSpace;
RETURN[text1, text2, text3, slider1, slider2, slider3];
};
The color naming scheme.
CNSHues: ChoiceButtons.ButtonList ← LIST[
"Red", "OrangishRed", "RedOrange", "ReddishOrange",
"Orange", "YellowishOrange", "OrangeYellow", "OrangishYellow",
"Yellow", "GreenishYellow", "YellowGreen", "YellowishGreen",
"Green", "BluishGreen", "GreenBlue", "GreenishBlue",
"Blue", "PurplishBlue", "BluePurple", "BluishPurple",
"Purple", "ReddishPurple", "PurpleRed", "PurplishRed",
"BrownishRed", "RedBrown", "ReddishBrown",
"Brown", "YellowishBrown", "BrownYellow", "BrownishYellow",
"Black", "Gray", "White"
];
CNSLightnesses: ChoiceButtons.ButtonList ← LIST[
"VeryDark", "Dark", "Medium", "Light", "VeryLight"
];
CNSSaturations: ChoiceButtons.ButtonList ← LIST[
"Grayish", "Moderate", "Strong", "Vivid"
];
CNSViewer: TYPE = RECORD [
saturationChoices: ChoiceButtons.EnumTypeRef,
lightnessChoices: ChoiceButtons.EnumTypeRef,
hueChoices: ChoiceButtons.EnumTypeRef
];
MakeCNS: PROCEDURE [handle: Handle] = {
maxWidth: CARDINAL = 625;
button: Buttons.Button;
handle.height2 ← handle.height2 + entryVSpace;
button ← Buttons.Create[
info: [
name: "CNS",
wx: handle.rgb.blueText.wx + handle.rgb.blueText.ww + columnSpacing,
wy: handle.height2,
wh: entryHeight,
parent: handle.outer,
border: TRUE ],
clientData: handle,
proc: ComputeFromCNS ];
handle.cns.saturationChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: button.wx + button.ww + entryHSpace,
y: handle.height2,
title: "Saturation:",
buttonNames: CNSSaturations,
default: "Vivid",
borderOnButtons: FALSE,
notifyClientProc: CNSSaturationUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
handle.height2 ← handle.height2 + entryHeight;
handle.cns.lightnessChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: button.wx + button.ww + entryHSpace,
y: handle.height2,
title: "Lightness:",
buttonNames: CNSLightnesses,
default: "Medium",
borderOnButtons: FALSE,
notifyClientProc: CNSLightnessUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: TRUE ];
handle.height2 ← handle.height2 + entryHeight;
handle.cns.hueChoices ← ChoiceButtons.BuildEnumTypeSelection[
viewer: handle.outer,
x: button.wx + button.ww + entryHSpace,
y: handle.height2,
title: "Hue:",
buttonNames: CNSHues,
borderOnButtons: FALSE,
notifyClientProc: CNSHueUpdate,
clientdata: handle,
style: menuSelection,
allInOneRow: FALSE,
maxWidth: maxWidth ];
handle.height2 ← handle.height2 + 8*entryHeight + entryVSpace;
};
The color patch reflecting the currently selected color.
ColorPatchViewer: TYPE = RECORD [viewer: ViewerClasses.Viewer ← NIL];
ColorPatchData: TYPE = REF ColorPatchDataRec;
ColorPatchDataRec: TYPE = RECORD [
index: CARDINAL ← 51,  -- color map index
rgbR: REAL ← 1.0,
rgbG: REAL ← 1.0,
rgbB: REAL ← 0.0,
hsvH: REAL ← 0.0,
hsvS: REAL ← 0.0,
hsvV: REAL ← 0.0,
hslH: REAL ← 0.0,
hslS: REAL ← 0.0,
hslL: REAL ← 0.0,
cslC: CNSColor.CSLChroma ← CNSColor.red,
cslS: CNSColor.CSLSaturation ← CNSColor.vivid,
cslL: CNSColor.CSLLightness ← CNSColor.medium
];
MakeColorPatch: PROCEDURE [handle: Handle] = {
width: INTEGER = 100;
height: INTEGER = 100;
instanceData: ColorPatchData ← NEW[ColorPatchDataRec];
handle.height2 ← handle.height2 + entryVSpace;
handle.colorPatch.viewer ← ViewerOps.CreateViewer[
flavor: $ColorPatch,
info: [
parent: handle.outer,
wx: handle.rgb.blueText.wx + handle.rgb.blueText.ww + columnSpacing,
wy: handle.height2,
ww: width,
wh: height,
data: instanceData,
scrollable: FALSE]
];
handle.height2 ← handle.height2 + height + entryVSpace;
};
PaintColorPatch: ViewerClasses.PaintProc = {
box: Graphics.Box ← Graphics.GetBounds[context];
patchData: ColorPatchData ← NARROW[self.data];
color: GraphicsBasic.Color ← GraphicsColor.RGBToColor[r: patchData.rgbR, g: patchData.rgbG, b: patchData.rgbB];
ColorMap.SetRGBColor[index: patchData.index, r: patchData.rgbR, g: patchData.rgbG, b: patchData.rgbB];
Graphics.SetColor[context, color];
Graphics.DrawBox[context, box];
};
MakeOnOffButtons: PROCEDURE [handle: Handle] = {
onButton, offButton: Buttons.Button;
handle.height ← handle.height + entryVSpace;
onButton ← Buttons.Create[
info: [
name: "Color On",
wy: handle.height,
wh: entryHeight,
parent: handle.outer,
border: TRUE ],
clientData: handle,
proc: TurnOnColorDisplay ];
offButton ← Buttons.Create[
info: [
name: "Color Off",
wy: handle.height,
wx: onButton.wx + onButton.ww + entryHSpace,
wh: entryHeight,
parent: handle.outer,
border: TRUE ],
clientData: handle,
proc: TurnOffColorDisplay ];
handle.height ← handle.height + entryHeight + entryVSpace;
};
TurnOnColorDisplay: Buttons.ButtonProc = {
IF WindowManager.colorDisplayOn = FALSE THEN
WindowManager.StartColorViewers[screenPos: Side, bitsPerPixel: 8];
};
TurnOffColorDisplay: Buttons.ButtonProc = {
WindowManager.StopColorViewers[];
};
ComputeFromRGB: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
redValue: REAL ← GetColorValue[ViewerTools.GetContents[handle.rgb.redText]];
greenValue: REAL ← GetColorValue[ViewerTools.GetContents[handle.rgb.greenText]];
blueValue: REAL ← GetColorValue[ViewerTools.GetContents[handle.rgb.blueText]];
UpdateColorViewers[handle: handle, color: [rgb[redValue, greenValue, blueValue]]];
};
RedSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [rgb[value, patchData.rgbG, patchData.rgbB]]];
};
GreenSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [rgb[patchData.rgbR, value, patchData.rgbB]]];
};
BlueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [rgb[patchData.rgbR, patchData.rgbG, value]]];
};
ComputeFromHSV: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
hueContents: Rope.ROPE ← ViewerTools.GetContents[handle.hsv.hueText];
saturationContents: Rope.ROPE ← ViewerTools.GetContents[handle.hsv.saturationText];
valueContents: Rope.ROPE ← ViewerTools.GetContents[handle.hsv.valueText];
hueValue: REAL ← GetColorValue[hueContents];
saturationValue: REAL ← GetColorValue[saturationContents];
valueValue: REAL ← GetColorValue[valueContents];
UpdateColorViewers[handle: handle, color: [hsv[hueValue, saturationValue, valueValue]]];
};
HueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [hsv[value, patchData.hsvS, patchData.hsvV]]];
};
SaturationSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [hsv[patchData.hsvH, value, patchData.hsvV]]];
};
ValueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [hsv[patchData.hsvH, patchData.hsvS, value]]];
};
ComputeFromHSL: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
hueContents: Rope.ROPE ← ViewerTools.GetContents[handle.hsl.hueText];
saturationContents: Rope.ROPE ← ViewerTools.GetContents[handle.hsl.saturationText];
lightnessContents: Rope.ROPE ← ViewerTools.GetContents[handle.hsl.lightnessText];
hueValue: REAL ← GetColorValue[hueContents];
saturationValue: REAL ← GetColorValue[saturationContents];
lightnessValue: REAL ← GetColorValue[lightnessContents];
UpdateColorViewers[handle: handle, color: [hsv[hueValue, saturationValue, lightnessValue]]];
};
HSLHueSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [hsl[value, patchData.hslS, patchData.hslL]]];
};
HSLSaturationSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [hsl[patchData.hslH, value, patchData.hslL]]];
};
HSLLightnessSliderUpdate: Sliders.SliderProc = {
handle: Handle ← NARROW[clientData];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
UpdateColorViewers[handle: handle, color: [hsl[patchData.hslH, patchData.hslS, value]]];
};
GetColorValue: PROCEDURE [rope: Rope.ROPE] RETURNS [value: REAL] = {
value ← ConvertRopeToReal[rope];
IF value > 1.0 OR value < 0.0 THEN {
MessageWindow.Append[message: "Invalid value ", clearFirst: TRUE];
MessageWindow.Append[message: rope, clearFirst: FALSE];
value ← 1.0;
};
RETURN[value];
};
ComputeFromCNS: Buttons.ButtonProc = {
handle: Handle ← NARROW[clientData];
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
hue: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.cns.hueChoices];
saturation: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.cns.saturationChoices];
lightness: Rope.ROPE ← ChoiceButtons.GetSelectedButton[handle.cns.lightnessChoices];
[c, s, l] ← CNSColor.CNSToCSL[hue, saturation, lightness];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
CNSHueUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
hue, saturation, lightness: Rope.ROPE;
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
[hue, saturation, lightness] ← CNSColor.CSLToCNS[patchData.cslC, patchData.cslS, patchData.cslL];
[c, s, l] ← CNSColor.CNSToCSL[ name, saturation, lightness];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
CNSSaturationUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
hue, saturation, lightness: Rope.ROPE;
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
[hue, saturation, lightness] ← CNSColor.CSLToCNS[patchData.cslC, patchData.cslS, patchData.cslL];
[c, s, l] ← CNSColor.CNSToCSL[ hue, name, lightness];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
CNSLightnessUpdate: ChoiceButtons.SelectionNotifierProc = {
handle: Handle ← NARROW[clientdata];
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
hue, saturation, lightness: Rope.ROPE;
c: CNSColor.CSLChroma;
s: CNSColor.CSLSaturation;
l: CNSColor.CSLLightness;
[hue, saturation, lightness] ← CNSColor.CSLToCNS[patchData.cslC, patchData.cslS, patchData.cslL];
[c, s, l] ← CNSColor.CNSToCSL[ hue, saturation, name];
UpdateColorViewers[handle: handle, color: [csl[c, s, l]]];
};
ColorSpecification: TYPE = RECORD [
color: SELECT type: ColorType FROM
hsv => [h, s, v: REAL],
hsl => [h, s, l: REAL],
rgb => [r, g, b: REAL],
csl => [c: CNSColor.CSLChroma, s: CNSColor.CSLSaturation, l: CNSColor.CSLLightness],
ENDCASE
];
ColorType: TYPE = {hsv, rgb, csl, hsl};
UpdateColorViewers: PROCEDURE [handle: Handle, color: ColorSpecification] RETURNS [] = {
UpdateColorValue: PROCEDURE [oldValue, newValue: REAL, textViewer, sliderViewer: ViewerClasses.Viewer] RETURNS [new: REAL] = {
IF newValue=NewCGColor.undefined THEN RETURN[oldValue];
IF newValue # oldValue THEN {
ViewerTools.SetContents[viewer: textViewer, contents: IO.PutFR["%-5f", IO.real[newValue]]];
Sliders.SetContents[slider: sliderViewer, contents: newValue];
};
RETURN[newValue];
};
patchData: ColorPatchData ← NARROW[handle.colorPatch.viewer.data];
h, s, v: REAL;
r, g, b: REAL;
hl,sl,l: REAL;
chroma: CNSColor.CSLChroma;
saturation: CNSColor.CSLSaturation;
lightness: CNSColor.CSLLightness;
cnsHue, cnsSaturation, cnsLightness: Rope.ROPE;
WITH col: color SELECT FROM
hsv => {
h ← col.h; s ← col.s; v ← col.v;
[r, g, b] ← NewCGColor.HSVToRGB[h, s, v];
[hl, sl, l] ← NewCGColor.RGBToHSL[r, g, b];
[chroma, saturation, lightness] ← CNSColor.HSLToCSL[hl, sl, l];
};
hsl => {
hl ← col.h; sl ← col.s; l ← col.l;
[r, g, b] ← NewCGColor.HSLToRGB[hl, sl, l];
[h, s, v] ← NewCGColor.RGBToHSV[r, g, b];
[chroma, saturation, lightness] ← CNSColor.HSLToCSL[hl, sl, l];
};
rgb => {
r ← col.r; g ← col.g; b ← col.b;
[h, s, v] ← NewCGColor.RGBToHSV[ r, g, b ];
[hl, sl, l] ← NewCGColor.RGBToHSL[r, g, b];
[chroma, saturation, lightness] ← CNSColor.HSLToCSL[hl, sl, l];
};
csl => {
chroma ← col.c; saturation ← col.s; lightness ← col.l;
[hl, sl, l] ← CNSColor.CSLToHSL[ chroma, saturation, lightness];
[r, g, b] ← NewCGColor.HSLToRGB[hl, sl, l];
[h, s, v] ← NewCGColor.RGBToHSV[r, g, b];
}
ENDCASE;
patchData.rgbR ← UpdateColorValue[oldValue: patchData.rgbR, newValue: r,
textViewer: handle.rgb.redText, sliderViewer: handle.rgb.redSlider];
patchData.rgbG ← UpdateColorValue[oldValue: patchData.rgbG, newValue: g,
textViewer: handle.rgb.greenText, sliderViewer: handle.rgb.greenSlider];
patchData.rgbB ← UpdateColorValue[oldValue: patchData.rgbB, newValue: b,
textViewer: handle.rgb.blueText, sliderViewer: handle.rgb.blueSlider];
patchData.hsvH ← UpdateColorValue[oldValue: patchData.hsvH, newValue: h,
textViewer: handle.hsv.hueText, sliderViewer: handle.hsv.hueSlider];
patchData.hsvS ← UpdateColorValue[oldValue: patchData.hsvS, newValue: s,
textViewer: handle.hsv.saturationText, sliderViewer: handle.hsv.saturationSlider];
patchData.hsvV ← UpdateColorValue[oldValue: patchData.hsvV, newValue: v,
textViewer: handle.hsv.valueText, sliderViewer: handle.hsv.valueSlider];
patchData.hslH ← UpdateColorValue[oldValue: patchData.hslH, newValue: hl,
textViewer: handle.hsl.hueText, sliderViewer: handle.hsl.hueSlider];
patchData.hslS ← UpdateColorValue[oldValue: patchData.hslS, newValue: sl,
textViewer: handle.hsl.saturationText, sliderViewer: handle.hsl.saturationSlider];
patchData.hslL ← UpdateColorValue[oldValue: patchData.hslL, newValue: l,
textViewer: handle.hsl.lightnessText, sliderViewer: handle.hsl.lightnessSlider];
[cnsHue, cnsSaturation, cnsLightness] ← CNSColor.CSLToCNS[chroma, saturation, lightness];
IF patchData.cslC # chroma AND ~Rope.Equal[cnsHue, ""] THEN {
patchData.cslC ← chroma;
ChoiceButtons.UpdateChoiceButtons[viewer: handle.outer, enumTypeInfo: handle.cns.hueChoices, newName: cnsHue];
};
IF patchData.cslS # saturation AND ~Rope.Equal[cnsSaturation, ""] THEN {
patchData.cslS ← saturation;
ChoiceButtons.UpdateChoiceButtons[viewer: handle.outer, enumTypeInfo: handle.cns.saturationChoices, newName: cnsSaturation];
};
IF patchData.cslL # lightness AND ~Rope.Equal[cnsLightness, ""] THEN {
patchData.cslL ← lightness;
ChoiceButtons.UpdateChoiceButtons[viewer: handle.outer, enumTypeInfo: handle.cns.lightnessChoices, newName: cnsLightness];
};
ViewerOps.PaintViewer[viewer: handle.colorPatch.viewer, hint: client, clearClient: FALSE];
};
ConvertRopeToReal: PROCEDURE [rope: Rope.ROPE] RETURNS [value: REAL] = {
i : INT ← 0;
Get: PROCEDURE RETURNS [c: CHARACTER] = {
c ← IF i >= rope.Length THEN ' ELSE rope.Fetch[i];
i ← i + 1;
RETURN[c];
};
RETURN[Real.ReadReal[Get]];
};
colorPatchClass is a record containing the procedures and data common to all ColorPatch viewer instances (class record).
colorPatchClass: ViewerClasses.ViewerClass ←
NEW[ViewerClasses.ViewerClassRec ← [paint: PaintColorPatch]];
Register a command with the UserExec that will create an instance of this tool.
UserExec.RegisterCommand[name: "ColorTool", proc: MakeColorTool,
briefDoc: "Create a color tool" ];
Register the ColorPatch class of viewer with the Window Manager
ViewerOps.RegisterViewerClass[$ColorPatch, colorPatchClass];
IF WindowManager.colorDisplayOn = FALSE THEN
WindowManager.StartColorViewers[screenPos: Side, bitsPerPixel: 8];
ColorMap.SetRGBColor[index: 1, r: 1.0, g: 1.0, b: 1.0];
[ ] ← MakeColorTool[NIL, NIL]; -- and create an instance
END.