File: GraphViewer.mesa, Copyright (C) 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, October 21, 1985 7:52:08 pm PDT
DIRECTORY
FS USING [ExpandName, GetDefaultWDir],
Icons USING [IconFlavor, NewIconFromFile],
Imager USING [ConstantColor, Context],
InputFocus USING [SetInputFocus],
MessageWindow USING [Append],
Graph USING [ColorIndex, GraphColorsArray, GraphFontsArray, ValueList, Viewer],
GraphCarets USING [GetFocus, SetFocus],
GraphCleanUp USING [CleanUpHandle],
GraphPrivate USING [BackgroundIndex, ChartMenus, Draw, EraseSelEntity, EraseSelText, GraphHandle, GraphProc, JumpDownAnyCurve, JumpLeftAnyCurve, JumpLeftSameCurve, JumpRightAnyCurve, JumpRightSameCurve, JumpUpAnyCurve, Lock, MoveSelText, MoveXhair2To1, OutputType, PaintAll, PaintInfo, SelectPoint, SelectEntity, SelectText, ShowSelEntityOnPanel, ShowSelTextOnPanel, SlideLeftNSteps, SlideOnSameCurve, SlideRightNSteps, SwitchAuto, SwitchCaretBlinking, SwitchCaretVisibility, SwitchGrid, SwitchTarget, Unlock, Wait, ZoomIn, ZoomOut, ZoomPoint],
GraphUtil USING [HandleFromViewer, HandleNotNil, ReplaceFileExt, SetCursorForBackgroundIndex, UseDefaultColors],
Process USING [Abort],
Rope USING [Cat, Equal, ROPE],
TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords],
UserProfile USING [CallWhenProfileChanges, ProfileChangedProc, Token],
ViewerClasses USING [Column, NotifyProc, PaintProc, PaintRectangle, Viewer, ViewerClass, ViewerClassRec],
ViewerEvents USING [EventProc, RegisterEventProc],
ViewerOps USING [CreateViewer, EnumerateViewers, EnumProc, OpenIcon, PaintViewer, RegisterViewerClass, SwapIconAndViewer, TopViewer],
WindowManager USING [colorDisplayOn];
GraphViewer:
CEDAR
PROGRAM
IMPORTS Icons, InputFocus, MessageWindow, GraphCarets, GraphCleanUp, GraphPrivate, GraphUtil, Process, Rope, TIPUser, UserProfile, ViewerEvents, ViewerOps, WindowManager
EXPORTS GraphPrivate = { OPEN Graph, GraphPrivate, GraphUtil;
(constant) variables
chartIcon: Icons.IconFlavor ← Icons.NewIconFromFile["Graph.icons", 0];
graphViewerClass: ViewerClasses.ViewerClass ←
NEW [ViewerClasses.ViewerClassRec ←
[paint: GraphPaint,
icon: chartIcon,
cursor: textPointer,
tipTable: TIPUser.InstantiateNewTIPTable["Graph.TIP"],
notify: GraphNotify
destroy: GraphDestroy
]];
parameters initialized or updated by ProcessUserProfile
initialColumn: ViewerClasses.Column; -- {static, left, right, color}
createOption: {closeOtherViewers, swapLastViewer, iconic, badCreateOption};
backgroundIndex: BackgroundIndex; -- {white, gray, darkGray, black, unknown}
defaultColorsArray:
PUBLIC GraphColorsArray ← [
[R: 1.0, G: 1.0, B: 1.0],
-- 0, white, reserved for color cursor, foreground or background.
entity colors: all vivid
[R: 1.0, G: 0.0, B: 0.8], -- 1, purple red
[R: 0.4, G: 0.0, B: 1.0], -- 2, purple
[R: 0.0, G: 0.0, B: 1.0], -- 3, blue
[R: 0.0, G: 0.56, B: 1.0], -- 4, greenish blue
[R: 0.0, G: 1.0, B: 1.0], -- 5, green blue
[R: 0.0, G: 1.0, B: 0.0], -- 6, green
[R: 0.52, G: 1.0, B: 0.0], -- 7, yellow green
[R: 1.0, G: 1.0, B: 0.0], -- 8, yellow
[R: 1.0, G: 0.60, B: 0.0], -- 9, orange yellow
[R: 1.0, G: 0.42, B: 0.0], -- 10, yellowish orange
[R: 1.0, G: 0.25, B: 0.0], -- 11, orange
[R: 1.0, G: 0.0, B: 0.0],
-- 12, red
gray or black
[R: 0.5, G: 0.5, B: 0.5], -- 13, gray, reserved for background. (default target color.)
[R: 0.17, G: 0.17, B: 0.17], -- 14, dark gray, reserved for background.
[R: 0.0, G: 0.0, B: 0.0] -- 15, black, reserved for color cursor, foreground or background.
];
defaultFontsArray:
PUBLIC GraphFontsArray ← [
[family, bold, italic, vFontSize, pFontScale]
["TimesRoman", FALSE, FALSE, 8, 9.0], -- 0, TimesRoman8, reserved.
["TimesRoman", TRUE, FALSE, 12, 13.0], -- 4, 12b, reserved.
["TimesRoman", FALSE, TRUE, 8, 9.0], -- 1, ~8i
["TimesRoman", TRUE, FALSE, 8, 9.0], -- 2, ~8b
["TimesRoman", FALSE, FALSE, 6, 7.0], -- 3, ~6
["Helvetica", FALSE, FALSE, 8, 9.0], -- 5, Helvetica8
["Helvetica", FALSE, FALSE, 6, 7.0], -- 6, ~6
["Helvetica", TRUE, FALSE, 14, 15.0], -- 7, ~14b
["Gacha", FALSE, FALSE, 8, 9.0], -- 8, Gacha8
["Hippo", FALSE, FALSE, 8, 9.0], -- 9, Hippo8
["Logo", FALSE, FALSE, 12, 14.0], -- 10, Logo12
["Math", FALSE, FALSE, 8, 9.0] -- 11, Math8, reserved.
];
systemColor: PUBLIC ARRAY ColorIndex OF Imager.ConstantColor;
public procedures
ShowChart:
PUBLIC GraphProc = {
if graph viewer already exists, then pop it up to the top;
else create the viewer for the graph on the handle.
Supposed to be locked before this is called. This proc will unlock handle before paintproc is to be invoked.
IF HandleNotNil[handle]
THEN {
OPEN handle;
viewer: Viewer ← chart.viewer;
IF viewer = NIL THEN NewChartViewer[handle]
ELSE {
Unlock[handle];
IF viewer.iconic THEN ViewerOps.OpenIcon[icon: viewer, bottom: FALSE]
ELSE ViewerOps.TopViewer[viewer];
};
};
}; -- ShowChart
NewChartViewer: GraphProc = {
OPEN handle;
viewer: Viewer ← chart.viewer ← ViewerOps.CreateViewer[
flavor: $Graph,
info: [
class: graphViewerClass,
name: ReplaceFileExt[graph.fileName, "graph"],
menu: ChartMenus[],
column: initialColumn,
iconic: TRUE,
inhibitDestroy: inhibitDestroy,
data: handle],
paint: FALSE];
Unlock[handle];
SELECT createOption
FROM
iconic => ViewerOps.PaintViewer[viewer, all, TRUE, NIL];
closeOtherViewers => ViewerOps.OpenIcon[icon: viewer,
closeOthers: TRUE, bottom: TRUE, paint: TRUE];
swapLastViewer => {
vLast: 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;
}; -- NewChartViewer
AddEntity: PUBLIC PROC [viewer: Viewer ← NIL, entity: Entity ← NIL] = {
IF vector = NIL OR NOT IsGraphViewer[viewer] THEN RETURN ELSE {
shouldPaint: BOOL ← FALSE;
expectedSize: CARDINAL;
handle: GraphHandle ← NARROW[viewer.data];
IF handle # NIL THEN {
Lock[handle];
expectedSize ← handle.graphSpec.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.graphSpec.nCurvesMax]];
Note: lineStates[n] is the state of curve # n+1.
FOR i: CARDINAL IN[0..handle.graphSpec.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];
};
}; -- AddEntity
IsGraphViewer:
PUBLIC
PROC [viewer: Viewer]
RETURNS [
BOOL] = {
IF viewer = NIL THEN RETURN[FALSE];
RETURN[viewer.class.flavor = $Graph];
}; -- IsGraphViewer
other private procedures
GraphPaint: ViewerClasses.PaintProc = {
[self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL]
IF IsGraphViewer[self]
AND context #
NIL
AND
NOT self.iconic
THEN {
there is no checking of the viewer class in Draw.
handle: GraphHandle ← HandleFromViewer[self];
IF handle #
NIL
THEN {
OPEN handle;
IF whatChanged #
NIL
AND
ISTYPE[whatChanged, PaintInfo]
THEN {
paintInfo.output ← screen;
Draw[context, handle];
}
ELSE {
Lock[handle];
paintInfo.output ← screen;
paintInfo.action ← paint;
IF whatChanged = NIL THEN paintInfo.item ← all
ELSE
IF
ISTYPE[whatChanged, ViewerClasses.PaintRectangle]
THEN {
vr: ViewerClasses.PaintRectangle ← NARROW[whatChanged];
paintInfo.item ← rectangle;
paintInfo.rect ← [vr.x, vr.y, vr.w, vr.h];
};
Draw[context, handle];
waitingGraph ← FALSE;
Unlock[handle];
};
}; -- handle # nil
};
}; -- GraphPaint
GraphBeforeDestroy: ViewerEvents.EventProc = {
handle: GraphHandle ← HandleFromViewer[viewer];
IF handle #
NIL
AND event=destroy
AND IsGraphViewer[viewer]
AND before
THEN {
Lock[handle];
IF GraphCarets.GetFocus[] = handle THEN GraphCarets.SetFocus[NIL];
IF handle.selectProc #
NIL
THEN {
TRUSTED {Process.Abort[handle.selectProc]};
Unlock[handle];
UNTIL handle.selectProc = NIL DO Wait[]; ENDLOOP;
Lock[handle];
};
handle.chart.viewer ← NIL;
Unlock[handle];
};
}; -- GraphBeforeDestroy
GraphAfterDestroy: ViewerEvents.EventProc = {
handle: GraphHandle ← HandleFromViewer[viewer];
IF handle #
NIL
AND event=destroy
AND IsGraphViewer[viewer]
AND
NOT before
THEN {
Lock[handle];
IF handle.controller = NIL THEN handle ← GraphCleanUp.CleanUpHandle[handle, TRUE]
ELSE Unlock[handle];
};
}; -- GraphAfterDestroy
GraphOpen: ViewerEvents.EventProc = {
handle: GraphHandle ← HandleFromViewer[viewer];
IF handle # NIL AND event = open AND IsGraphViewer[viewer] AND NOT before THEN
handle.graphOpened ← TRUE;
GraphNotify: ViewerClasses.NotifyProc= {
IF IsGraphViewer[self]
AND
NOT self.iconic
THEN {
handle: GraphHandle ← HandleFromViewer[self];
IF HandleNotNil[handle]
THEN {
mx, my: INTEGER;
nArg: INT;
FOR params:
LIST
OF
REF
ANY ← input, params.rest
UNTIL params =
NIL
DO
Lock[handle];
WITH params.first
SELECT
FROM
xy: TIPUser.TIPScreenCoords => {
mx ← xy.mouseX;
my ← xy.mouseY;
};
n: REF LONG INTEGER => nArg ← n^;
a:
ATOM => {
SELECT a
FROM
$SetFocus => InputFocus.SetInputFocus[handle.chart.viewer];
$SelectText => SelectText[handle, mx, my];
$Slide => SlideOnSameCurve[handle, mx, my];
$SelectCurve => SelectEntity[handle, mx, my];
$SelectPoint => SelectPoint[handle, mx, my];
$ZoomPoint => ZoomPoint[handle, mx, my];
$ShowSelTextOnPanel => ShowSelTextOnPanel[handle];
$ShowSelEntityOnPanel => ShowSelEntityOnPanel[handle];
$JumpLeftSameCurve => JumpLeftSameCurve[handle];
$JumpLeftAnyCurve => JumpLeftAnyCurve[handle];
$JumpUp => JumpUpAnyCurve[handle];
$ZoomIn => ZoomIn[handle, mx, my];
$LeftStep => SlideLeftNSteps[handle, nArg];
$RemoveCurve => EraseSelEntity[handle];
$JumpRightSameCurve => JumpRightSameCurve[handle];
$JumpRightAnyCurve => JumpRightAnyCurve[handle];
$ClearText => EraseSelText[handle];
$MoveSelText => MoveSelText[handle, mx, my];
$JumpDown => JumpDownAnyCurve[handle];
$ZoomOut => ZoomOut[handle, mx, my];
$RightStep => SlideRightNSteps[handle, nArg];
$PrimaryVisibility, $SecondaryVisibility, $TextCaretVisibility =>
SwitchCaretVisibility[handle,
SELECT a
FROM
$PrimaryVisibility => primary, $SecondaryVisibility => secondary,
ENDCASE => text
];
$PrimaryBlinking, $SecondaryBlinking, $TextCaretBlinking =>
SwitchCaretBlinking[handle,
SELECT a
FROM
$PrimaryBlinking => primary, $SecondaryBlinking => secondary,
ENDCASE => text
];
$SaveCrosshair => MoveXhair2To1[handle];
$ToggleAutoBounds => SwitchAuto[handle, bounds];
$ToggleAutoDiv => SwitchAuto[handle, divisions];
$ToggleTargetX, $ToggleTargetY =>
SwitchTarget[handle, IF a = $ToggleTargetX THEN x ELSE y];
$ToggleGridX, $ToggleGridY =>
SwitchGrid[handle, IF a = $ToggleGridX THEN x ELSE y];
$CleanAndPaint => PaintAll[handle, TRUE];
$Repaint => PaintAll[handle, FALSE];
ENDCASE;
};
ENDCASE;
Unlock[handle];
ENDLOOP;
};
};
}; -- GraphNotify
ProcessUserProfile:
PUBLIC UserProfile.ProfileChangedProc = {
initialColumn
rope: Rope.ROPE ← UserProfile.Token[key: "Graph.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, Graph.Column: ", rope, "."],
TRUE];
initialColumn ← color;
};
IF initialColumn = color AND NOT WindowManager.colorDisplayOn THEN initialColumn ← left;
createOption
rope ← UserProfile.Token[key: "Graph.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, Graph.CreateOption: ", rope, "."],
TRUE];
createOption ← closeOtherViewers;
};
backgroundType
rope ← UserProfile.Token[key: "Graph.Background", default: "white"];
backgroundIndex ←
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 backgroundIndex = unknown
THEN {
MessageWindow.Append[
Rope.Cat["Warning: Illegal entry in user profile, Graph.Background: ", rope, "."],
TRUE];
backgroundIndex ← white;
};
colors
IF reason = firstTime THEN UseDefaultColors[];
SetCursorForBackgroundIndex[backgroundIndex];
}; -- ProcessUserProfile
Init:
PROC [] = {
ViewerOps.RegisterViewerClass[$Graph, graphViewerClass];
[] ← ViewerEvents.RegisterEventProc[proc: GraphBeforeDestroy, event: destroy, before: TRUE];
[] ← ViewerEvents.RegisterEventProc[proc: GraphAfterDestroy, event: destroy, before: FALSE];
[] ← ViewerEvents.RegisterEventProc[proc: GraphOpen, event: open, before: FALSE];
ProcessUserProfile[firstTime];
UserProfile.CallWhenProfileChanges[ProcessUserProfile];
GraphCarets.SetFocus[NIL];
}; -- Init
Init[];
}.
CHANGE
LOG.
SChen, October 19, 1985 10:09:23 pm PDT, created.