File: PlotViewer.mesa, Copyright (C) 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, August 2, 1985 9:41:16 pm PDT
DIRECTORY
BasicTime USING [GMT, nullGMT],
Convert USING [Error, RealFromRope],
FS USING [Error],
Icons USING [IconFlavor, NewIconFromFile],
Imager USING [Box, ConstantColor, Context, RotateT, ScaleT],
ImagerColor USING [GrayFromColor, RGB],
ImagerColorMap USING [LoadEntries, MapEntry, SetStandardColorMap],
ImagerDitheredDevice USING [ColorFromSpecialRGB],
ImagerInterpress USING [Close, Create, DoPage, Ref],
ImagerPress USING [Close, SimpleCreate],
IO USING [int, PutFR],
Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc],
MessageWindow USING [Append, Blink],
Plot USING [Curves, PlotSpec, PlotSpecRec, ReadPlotFile, RopeSequence, SavePlot, Vector],
PlotOps USING [BackgroundType, ColorMap, ColorMode, DrawMe, Handle, HandleData, LineState, LineStateRec, LineStep, Lock, NColors, OutputType, StateSequence, Unlock],
PopUpMenu USING [RequestSelection],
Real USING [RoundC],
Rope USING [Cat, Equal, Find, IsEmpty, ROPE, Substr],
Terminal USING [ColorCursorPresentation, Current, GetColorCursorPresentation, SetColorCursorPresentation, Virtual],
UserProfile USING [CallWhenProfileChanges, ListOfTokens, ProfileChangedProc, Token],
ViewerClasses USING [Column, PaintProc, Viewer, ViewerClass, ViewerClassRec],
ViewerOps USING [CreateViewer, EnumerateViewers, EnumProc, OpenIcon, PaintViewer, RegisterViewerClass, SwapIconAndViewer],
ViewerTools USING [GetSelectionContents],
WindowManager USING [colorDisplayOn, RestoreCursor];
PlotViewer: CEDAR PROGRAM
IMPORTS Convert, FS, Imager, ImagerColor, ImagerColorMap, ImagerDitheredDevice, ImagerInterpress, ImagerPress, Icons, IO, Menus, MessageWindow, Plot, PlotOps, PopUpMenu, Real, Rope, Terminal, UserProfile, ViewerOps, ViewerTools, WindowManager
EXPORTS Plot, PlotOps = {
OPEN Plot, PlotOps;
constants
coordRatio: REAL = 0.25/600.0; -- 0.25 m ~ 600 pixels
(constant) variables
plotIcon: Icons.IconFlavor ← Icons.NewIconFromFile["Plot.icons", 0];
plotViewerClass: ViewerClasses.ViewerClass ← NEW [ViewerClasses.ViewerClassRec ←
[paint: Display, icon: plotIcon, cursor: textPointer]];
virtual: Terminal.Virtual = Terminal.Current[];
parameters initialized or updated by ProcessUserProfile
initialColumn: ViewerClasses.Column; -- {static, left, right, color}
createOption: {closeOtherViewers, swapLastViewer, iconic, badCreateOption};
backgroundType: BackgroundType; -- {white, gray, darkGray, black, unknown}
color: PUBLIC ARRAY NColors OF Imager.ConstantColor;
public procedures
CreateViewer: PUBLIC PROC [spec: PlotSpec ← NIL, iconic, inhibitDestroy: BOOLFALSE] RETURNS [viewer: ViewerClasses.Viewer ← NIL] = {
handle
handle: Handle ← NEW[HandleData ← [
plotSpec: spec,
background: backgroundType
] ];
viewer ← ViewerOps.CreateViewer[
flavor: $Plot,
info: [
name: spec.file,
menu: MakeMenu[],
icon: plotIcon,
column: initialColumn,
iconic: TRUE,
inhibitDestroy: inhibitDestroy,
data: handle],
paint: FALSE];
IF iconic OR (createOption = iconic) THEN ViewerOps.PaintViewer[viewer, all, TRUE, NIL]
ELSE {
SELECT createOption FROM
closeOtherViewers => ViewerOps.OpenIcon[
icon: viewer,
closeOthers: TRUE,
bottom: TRUE,
paint: TRUE];
swapLastViewer => {
vLast: ViewerClasses.Viewer ← NIL;
LastViewer: ViewerOps.EnumProc = {
IF v.column = initialColumn AND NOT v.iconic THEN {vLast ← v; RETURN[FALSE]};
}; -- LastViewer
ViewerOps.EnumerateViewers[LastViewer];
IF vLast = NIL THEN ViewerOps.OpenIcon[
icon: viewer,
closeOthers: TRUE,
bottom: TRUE,
paint: TRUE]
ELSE {
ViewerOps.SwapIconAndViewer[
icon: viewer,
openViewer: vLast,
paint: TRUE]; -- this paints the new viewer only.
ViewerOps.PaintViewer[vLast, all, TRUE, NIL]; -- paint the last viewer.
};
};
ENDCASE;
};
}; -- CreateViewer
AddVector: PUBLIC PROC [viewer: ViewerClasses.Viewer ← NIL, vector: Vector ← NIL] = {
IF vector = NIL OR NOT IsPlotViewer[viewer] THEN RETURN ELSE {
shouldPaint: BOOLFALSE;
expectedSize: CARDINAL;
handle: Handle ← NARROW[viewer.data];
IF handle # NIL THEN {
Lock[handle];
expectedSize ← handle.plotSpec.nCurvesMax + 1;
IF vector.size # expectedSize THEN MessageWindow.Append[
IO.PutFR["Only %g elements in vector when %g expected.",
IO.int[vector.size], IO.int[expectedSize]], TRUE]
ELSE {
IF handle.curves = NIL THEN {
handle.lineStates ← NEW[StateSequence[handle.plotSpec.nCurvesMax]];
Note: lineStates[n] is the state of curve # n+1.
FOR i: CARDINAL IN[0..handle.plotSpec.nCurvesMax) DO
handle.lineStates[i] ← NEW[LineStateRec ← []];
ENDLOOP;
}
ELSE IF handle.curves.first # NIL THEN shouldPaint ← TRUE;
handle.curves ← CONS[vector, handle.curves]
};
Unlock[handle];
};
IF shouldPaint THEN ViewerOps.PaintViewer[viewer, client, FALSE, handle.curves];
};
}; -- AddVector
CreateSpec: PUBLIC PROC[file, title: Rope.ROPENIL,
time: BasicTime.GMT ← BasicTime.nullGMT,
bounds: Imager.Box ← [0, 0, 0, 0], -- [xmin, xmax, ymin, ymax]
nCurvesMax: CARDINAL ← 0,
legendEntries: REF RopeSequence ← NIL
] RETURNS[spec: PlotSpec ← NIL] = {
spec ← NEW[PlotSpecRec ← [file, title, time, bounds, nCurvesMax, legendEntries]];
}; -- CreateSpec
SetSpec: PUBLIC PROC [viewer: ViewerClasses.Viewer ← NIL, newSpec: PlotSpec ← NIL] = {
Note: if original nCurvesMax # new one, then old data will be cleared.
IF IsPlotViewer[viewer] THEN {
handle: Handle ← NARROW[viewer.data];
IF handle = NIL THEN RETURN;
Lock[handle];
handle.curves ← NIL;
handle.plotSpec ← newSpec;
viewer.name ← handle.plotSpec.file;
Unlock[handle];
ViewerOps.PaintViewer[viewer, caption, FALSE, NIL];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL]; -- update the display.
};
}; -- SetSpec
Clear: PUBLIC PROC [viewer: ViewerClasses.Viewer] = {
IF IsPlotViewer[viewer] THEN {
handle: Handle ← NARROW[viewer.data];
Lock[handle];
handle.plotSpec^ ← [];
handle.curves ← NIL;
Unlock[handle];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- Clear
Zoom: PUBLIC PROC [viewer: ViewerClasses.Viewer, newBounds: Imager.Box] = {
IF IsPlotViewer[viewer] THEN {
handle: Handle ← NARROW[viewer.data];
Lock[handle];
handle.plotSpec.bounds ← newBounds;
Unlock[handle];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- Zoom
IsPlotViewer: PUBLIC PROC [viewer: ViewerClasses.Viewer] RETURNS [BOOL] = {
IF viewer = NIL THEN RETURN[FALSE];
RETURN[viewer.class.flavor = $Plot];
}; -- IsPlotViewer
menu commands
SetColorMap: Menus.MenuProc = {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
IF handle # NIL THEN {
newColorMap: ColorMap = SELECT PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["mine", "Cedar"],
default: 1] FROM
1 => mine, 2 => cedar, ENDCASE => handle.colorMap;
Lock[handle];
SetCursorForBackgroundType[handle.background];
IF handle.colorMap # newColorMap THEN {
handle.colorMap ← newColorMap;
SELECT newColorMap FROM
mine => SetMyColorMap[];
cedar => SetCedarColorMap[];
ENDCASE;
};
Unlock[handle];
};
}; -- SetColorMap
SetDisplayMode: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
IF handle # NIL THEN {
newColorMode: ColorMode = SELECT PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["color", "black and white"],
default: 1] FROM
1 => color, 2 => bw, ENDCASE => handle.colorMode;
changed: BOOL;
Lock[handle];
SetCursorForBackgroundType[handle.background];
changed ← handle.colorMode # newColorMode;
handle.colorMode ← newColorMode;
Unlock[handle];
IF changed THEN ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- SetDisplayMode
SetCursorColor: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
IF handle # NIL THEN {
SELECT PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["black", "white"],
default: 1] FROM
1 => [] ← Terminal.SetColorCursorPresentation[virtual, onesAreBlack];
2 => [] ← Terminal.SetColorCursorPresentation[virtual, onesAreWhite];
ENDCASE => {
Lock[handle];
SetCursorForBackgroundType[handle.background];
Unlock[handle];
};
};
}; -- SetCursorColor
SetBackgroundType: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
IF handle # NIL THEN {
newBkGndType: BackgroundType = SELECT PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["white", "gray", "dark gray", "black"],
default: 1] FROM
1 => white,
2 => gray,
3 => darkGray,
4 => black,
ENDCASE => handle.background;
changed: BOOL;
Lock[handle];
changed ← handle.background # newBkGndType;
handle.background ← newBkGndType;
SetCursorForBackgroundType[handle.background];
Unlock[handle];
IF changed THEN ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- SetBackgroundType
Get: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
name: Rope.ROPE ← ViewerTools.GetSelectionContents[];
msg: Rope.ROPE
IF name.IsEmpty[] THEN "Please select a plot file name."
ELSE IF handle = NIL THEN "Not a good plot viewer."
ELSE ReadPlotFile[name: name, viewer: viewer, iconic: FALSE];
IF msg # NIL THEN {
MessageWindow.Blink[];
MessageWindow.Append[msg, TRUE];
};
}; -- Get
Store: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
name: Rope.ROPE ← ViewerTools.GetSelectionContents[];
msg: Rope.ROPE ← SavePlot[viewer, name]; -- viewer type checked there
IF msg # NIL THEN {
MessageWindow.Blink[];
MessageWindow.Append[message: msg, clearFirst: TRUE];
};
}; -- Store
Press: Menus.MenuProc = {Print[NARROW[parent], press]}; -- Press
Interpress: Menus.MenuProc = {Print[NARROW[parent], interpress]}; -- Interpress
AppendExtension: PROC[old, ext: Rope.ROPENIL] RETURNS [Rope.ROPE]= {
excl: INT ← old.Find["!"];
RETURN[Rope.Cat[
SELECT excl FROM
< 0 => old,
> 0 => old.Substr[0, excl],
ENDCASE => "Plot", -- unlikely, though.
".",
ext]];
}; -- AppendExtension
GetPrintingColorMode: PROC[] RETURNS [type: ColorMode, ok: BOOLTRUE] = {
colorChoice: NAT = PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["color", "black and white"],
default: 1];
SELECT colorChoice FROM
1 => type ← color;
2 => type ← bw;
ENDCASE => {
MessageWindow.Blink[];
MessageWindow.Append[
"You must choose either color or black-and-white.", TRUE];
ok ← FALSE;
};
}; -- GetPrintingColor
Print: PROC[viewer: ViewerClasses.Viewer, output: OutputType ← interpress] = {
handle: Handle ← NARROW[viewer.data];
printingColorMode: ColorMode;
okToPrint: BOOL;
IF handle = NIL THEN RETURN;
[printingColorMode, okToPrint] ← GetPrintingColorMode[];
Lock[handle];
SetCursorForBackgroundType[handle.background];
IF okToPrint THEN {
context: Imager.Context ← NIL;
interpress: ImagerInterpress.Ref ← NIL;
fileName: Rope.ROPE;
colorModeSave: ColorMode;
colorModeSave ← handle.colorMode;
handle.colorMode ← printingColorMode;
SELECT output FROM
interpress => {
InterpressDrawMe: PROC[context: Imager.Context] ~ {
context.RotateT[90];
context.ScaleT[coordRatio];
DrawMe[context, handle, NIL, interpress];
};
fileName ← AppendExtension[handle.plotSpec.file, "ip"];
interpress ← ImagerInterpress.Create[fileName
! FS.Error => {
MessageWindow.Append[error.explanation, TRUE];
CONTINUE;
}
];
ImagerInterpress.DoPage[interpress, InterpressDrawMe];
MessageWindow.Append[Rope.Cat[fileName, " is written."], TRUE];
};
press => {
fileName ← AppendExtension[handle.plotSpec.file, "press"];
context ← ImagerPress.SimpleCreate[fileName
! FS.Error => {
MessageWindow.Append[error.explanation, TRUE];
CONTINUE;
}
];
context.RotateT[90];
context.ScaleT[coordRatio];
DrawMe[context, handle, NIL, press];
MessageWindow.Append[Rope.Cat[fileName, " is written."], TRUE];
};
ENDCASE; -- (error)
IF output = interpress AND interpress # NIL THEN ImagerInterpress.Close[interpress]
ELSE IF output = press AND context # NIL THEN ImagerPress.Close[context];
handle.colorMode ← colorModeSave;
};
Unlock[handle];
}; -- Print
SetCedarColorMap: PROC [] = {
ImagerColorMap.SetStandardColorMap[virtual];
WindowManager.RestoreCursor[];
IF Terminal.GetColorCursorPresentation[virtual] # onesAreBlack THEN
[] ← Terminal.SetColorCursorPresentation[virtual, onesAreBlack];
}; -- SetCedarColorMap
SetMyColorMap: PROC [] = {
defaultRGB: ARRAY NColors OF ImagerColor.RGB ← [
white
[R: 1.0, G: 1.0, B: 1.0],
forgeround colors, all vivid
[R: 1.0, G: 0.0, B: 0.8], -- purple red
[R: 0.4, G: 0.0, B: 1.0], -- purple
[R: 0.0, G: 0.0, B: 1.0], -- blue
[R: 0.0, G: 0.56, B: 1.0], -- greenish blue
[R: 0.0, G: 1.0, B: 1.0], -- green blue
[R: 0.0, G: 1.0, B: 0.0], -- green
[R: 0.52, G: 1.0, B: 0.0], -- yellow green
[R: 1.0, G: 1.0, B: 0.0], -- yellow
[R: 1.0, G: 0.60, B: 0.0], -- orange yellow
[R: 1.0, G: 0.42, B: 0.0], -- yellowish orange
[R: 1.0, G: 0.25, B: 0.0], -- orange
[R: 1.0, G: 0.0, B: 0.0], -- red
gray
[R: 0.5, G: 0.5, B: 0.5], -- gray
[R: 0.17, G: 0.17, B: 0.17], -- dark gray
black
[R: 0.0, G: 0.0, B: 0.0] -- black
];
GetColor: PROC [num: NColors] RETURNS [ImagerColor.RGB] = {
get color spec from user profile, or return default
ColorSpec: TYPE = LIST OF Rope.ROPE;
list: ColorSpec ← UserProfile.ListOfTokens[
key: IO.PutFR["Plot.Color%g", IO.int[num]],
default: NIL
];
c: ARRAY[1..3] OF REAL ← [defaultRGB[num].R, defaultRGB[num].G, defaultRGB[num].B];
i: CARDINAL ← 1;
FOR lr: ColorSpec ← list, list.rest UNTIL list = NIL OR i > 3 DO
c[i] ← Convert.RealFromRope[list.first ! Convert.Error => EXIT];
c[i] ← MAX[0, MIN[1.0, c[i]]];
i ← i + 1;
ENDLOOP;
RETURN[[R: c[1], G: c[2], B: c[3]]];
}; -- GetColor
FOR i: NColors IN NColors DO
c: ImagerColor.RGB ← GetColor[i];
entry: ImagerColorMap.MapEntry ← [
mapIndex: i+100,
red: Real.RoundC[c.R*255],
green: Real.RoundC[c.G*255],
blue: Real.RoundC[c.B*255]
];
ImagerColorMap.LoadEntries[
vt: virtual,
mapEntries: LIST[entry],
shared: FALSE];
color[i] ← ImagerDitheredDevice.ColorFromSpecialRGB[
specialPixel: [i+100, null], rgb: c];
ENDLOOP;
};
};
}; -- SetMyColorMap
other private procedures
Display: ViewerClasses.PaintProc = {
[self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL]
IF IsPlotViewer[self] AND NOT self.iconic THEN {
there will be no checking of the viewer class in DrawMe.
handle: Handle ← NARROW[self.data];
IF handle # NIL THEN {
Lock[handle];
DrawMe[context, handle, whatChanged, screen];
Unlock[handle];
};
};
}; -- Display
MakeMenu: PROC [] RETURNS [menu: Menus.Menu] = {
menu ← Menus.CreateMenu[];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["DisplayMode", SetDisplayMode]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["ColorMap", SetColorMap]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Cursor", SetCursorColor]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Background", SetBackgroundType]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry[name: "Get", proc: Get, guarded: TRUE]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Store", Store]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Press", Press]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Interpress", Interpress]];
}; -- MakeMenus
ProcessUserProfile: UserProfile.ProfileChangedProc = {
initialColumn
rope: Rope.ROPE ← UserProfile.Token[key: "Plot.Column", default: "color"];
initialColumn ← SELECT TRUE FROM
rope.Equal["color", FALSE] => color,
rope.Equal["left", FALSE] => left,
rope.Equal["right", FALSE] => right,
ENDCASE => static;
IF initialColumn = static THEN {
MessageWindow.Append[
Rope.Cat["Warning: Illegal entry in user profile, Plot.Column: ", rope, "."],
TRUE];
initialColumn ← color;
};
IF initialColumn = color AND NOT WindowManager.colorDisplayOn THEN initialColumn ← left;
createOption
rope ← UserProfile.Token[key: "Plot.CreateOption", default: "closeOtherViewers"];
createOption ← SELECT TRUE FROM
rope.Equal["closeOtherViewers", FALSE] => closeOtherViewers,
rope.Equal["swapLastViewer", FALSE] => swapLastViewer,
rope.Equal["iconic", FALSE] => iconic,
ENDCASE => badCreateOption;
IF createOption = badCreateOption THEN {
MessageWindow.Append[
Rope.Cat["Warning: Illegal entry in user profile, Plot.CreateOption: ", rope, "."],
TRUE];
createOption ← closeOtherViewers;
};
backgroundType
rope ← UserProfile.Token[key: "Plot.Background", default: "white"];
backgroundType ← SELECT TRUE FROM
rope.Equal["white", FALSE] => white,
rope.Equal["gray", FALSE] => gray,
rope.Equal["darkGray", FALSE] => darkGray,
rope.Equal["black", FALSE] => black,
ENDCASE => unknown;
IF backgroundType = unknown THEN {
MessageWindow.Append[
Rope.Cat["Warning: Illegal entry in user profile, Plot.Background: ", rope, "."],
TRUE];
backgroundType ← white;
};
colors
SetMyColorMap[];
SetCursorForBackgroundType[backgroundType];
}; -- ProcessUserProfile
SetCursorForBackgroundType: PROC [bkgnd: BackgroundType ← white] = {
bkGndColor: Imager.ConstantColor ← SELECT bkgnd FROM
gray => color[13], darkGray => color[14], black => color[15], ENDCASE => color[0];
cursorType: Terminal.ColorCursorPresentation ←
IF ImagerColor.GrayFromColor[bkGndColor] > 0.5 THEN onesAreWhite ELSE onesAreBlack;
IF cursorType # Terminal.GetColorCursorPresentation[virtual] THEN
[] ← Terminal.SetColorCursorPresentation[virtual, cursorType];
}; -- SetCursorForBackgroundType
Init: PROC [] = {
ViewerOps.RegisterViewerClass[$Plot, plotViewerClass];
ProcessUserProfile[firstTime];
UserProfile.CallWhenProfileChanges[ProcessUserProfile];
}; -- Init
Init[];
}.
CHANGE LOG.
Created by: SChen in Cedar5.
SChen, May 14, 1985 5:39:02 pm PDT, custom icon.
SChen, July 11, 1985 11:03:15 pm PDT, => Cedar6.0 (CG, press => Imager, Interpress).
SChen, August 2, 1985 7:08:18 pm PDT, split it into PlotViewer and PlotViewerPaint, cleaned up some stuff related to color display and color map, added pop up menus.