File: GraphViewer.mesa, Copyright (C) 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Pradeep Sindhu April 8, 1986 6:39:17 pm PST
Sweetsun Chen, April 23, 1986 2:30:35 pm PST
Christian Jacobi, June 11, 1986 6:43:46 pm PDT
DIRECTORY
FS USING [ExpandName, GetDefaultWDir],
Icons USING [IconFlavor, NewIconFromFile],
Imager USING [ConstantColor, Context],
InputFocus USING [SetInputFocus],
Graph USING [BackgroundIndex, CaretIndex, ColorIndex, GraphColors, GraphColorsArray, GraphFonts, GraphFontsArray, GraphHandle, GraphProc, OutputType, PaintInfo, PaintInfoRec, ROPE, ValueList, Viewer],
GraphCarets USING [GetFocus, Init, SetFocus],
GraphCleanUp USING [CleanUpHandle],
GraphOps USING [Lock, Unlock],
GraphPrivate USING [ChartMenus, Draw, EraseSelEntity, EraseSelText, JumpDownAnyCurve, JumpLeftAnyCurve, JumpLeftSameCurve, JumpRightAnyCurve, JumpRightSameCurve, JumpUpAnyCurve, MoveSelText, MoveXhair2To1, PaintAll, RemoveAllCurves, RotateSelTextCCW, RotateSelTextCW, SelectPoint, SelectEntity, SelectText, ShowSelEntityOnSpec, ShowSelTextOnSpec, SlideLeftNSteps, SlideOnSameCurve, SlideRightNSteps, SwitchAuto, SwitchCaretBlinking, SwitchCaretVisibility, SwitchGrid, SwitchTarget, UserEditAllowed, Wait, ZoomIn, ZoomOut, ZoomPoint],
GraphUtil USING [HandleFromViewer, HandleNotNil, SetCursorForBackgroundIndex, UseDefaultColors, UseMyColors, UseMyFonts],
Process USING [Abort],
Rope USING [Equal],
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, GraphCarets, GraphCleanUp, GraphOps, GraphPrivate, GraphUtil, Process, Rope, TIPUser, UserProfile, ViewerEvents, ViewerOps, WindowManager
EXPORTS GraphPrivate = { OPEN Graph, GraphOps, GraphPrivate, GraphUtil;
(constant) variables
graphIcon: Icons.IconFlavor ← Icons.NewIconFromFile["Graph.icons", 0];
graphViewerClass: ViewerClasses.ViewerClass ← NEW [ViewerClasses.ViewerClassRec ←
[paint: GraphPaint,
icon: graphIcon,
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, open};
backgroundIndex: BackgroundIndex; -- {white, gray, darkGray, black, unknown}
defaultColors: PUBLIC GraphColors ← NEW[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.0], -- 1, red
[R: 0.0, G: 1.0, B: 0.0], -- 2, green
[R: 0.0, G: 0.0, B: 1.0], -- 3, blue
[R: 1.0, G: 1.0, B: 0.0], -- 4, yellow
[R: 1.0, G: 0.25, B: 0.0], -- 5, orange
[R: 0.4, G: 0.0, B: 1.0], -- 6, purple
[R: 0.0, G: 1.0, B: 1.0], -- 7, green blue
[R: 1.0, G: 0.0, B: 0.8], -- 8, purple red
[R: 1.0, G: 0.60, B: 0.0], -- 9, orange yellow
[R: 0.0, G: 0.56, B: 1.0], -- 10, greenish blue
[R: 1.0, G: 0.42, B: 0.0], -- 11, yellowish orange
[R: 0.52, G: 1.0, B: 0.0], -- 12, yellow green
gray or black
[R: 0.75, G: 0.75, B: 0.75], -- 13, gray, reserved for background. (default target color.)
[R: 0.25, G: 0.25, B: 0.25], -- 14, dark gray, reserved for background.
[R: 0.0, G: 0.0, B: 0.0] -- 15, black, reserved for color cursor, foreground or background.
]];
defaultFonts: PUBLIC GraphFonts ← NEW[GraphFontsArray ← [
[family, bold, italic, vFontSize, pFontScale]
["TimesRoman", FALSE, FALSE, 10, 11.0], -- 0, TimesRoman10, reserved.
["TimesRoman", FALSE, FALSE, 8, 9.0], -- 1, TimesRoman8, reserved.
["TimesRoman", TRUE, FALSE, 12, 13.0], -- 2, 12b, reserved.
["TimesRoman", FALSE, TRUE, 10, 11.0], -- 3, ~10i
["TimesRoman", TRUE, FALSE, 10, 11.0], -- 4, ~10b
["Helvetica", FALSE, FALSE, 10, 11.0], -- 5, Helvetica8
["Helvetica", TRUE, FALSE, 14, 15.0], -- 6, Helvetica14b
["Gacha", FALSE, FALSE, 10, 11.0], -- 7, Gacha10
["Gacha", FALSE, FALSE, 8, 9.0], -- 8, Gacha8
["Hippo", FALSE, FALSE, 10, 11.0], -- 9, Hippo10
["Logo", FALSE, FALSE, 14, 15.0], -- 10, Logo14
["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. No more, 11/13/85.
IF HandleNotNil[handle] THEN { OPEN handle;
viewer: Viewer ← chart.viewer;
IF viewer = NIL THEN {
UseMyColors[handle];
UseMyFonts[handle];
waitingGraph ← TRUE;
NewChartViewer[handle];
WHILE waitingGraph DO Wait[]; ENDLOOP;
}
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: graph.fileName,
menu: ChartMenus[],
column: initialColumn,
iconic: TRUE,
inhibitDestroy: inhibitDestroy,
data: handle],
paint: TRUE];
Unlock[handle];
SELECT createOption FROM
iconic => {
ViewerOps.PaintViewer[viewer, all, TRUE, NIL];
waitingGraph ← FALSE;
};
open => ViewerOps.OpenIcon[viewer];
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: BOOLFALSE;
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;
info: PaintInfo;
IF whatChanged = NIL THEN {
screen must have been cleared by the Viewers package, so we should make sure that the caretState's are not confused.
FOR i: CaretIndex IN CaretIndex DO
chart.caretState[i].visible ← FALSE;
ENDLOOP;
info ← NEW[all PaintInfoRec ← [data: all[clear: TRUE, updateDivBds: FALSE]]];
(action: paint, output: screen)
}
ELSE IF ISTYPE[whatChanged, PaintInfo] THEN {
info ← NARROW[whatChanged];
info.output ← screen;
IF clear THEN WITH info SELECT FROM
a: REF all PaintInfoRec => a.clear ← FALSE;
r: REF rectangle PaintInfoRec => r.clear ← FALSE;
ENDCASE;
}
ELSE IF ISTYPE[whatChanged, ViewerClasses.PaintRectangle] THEN {
vr: ViewerClasses.PaintRectangle ← NARROW[whatChanged];
info ← NEW[rectangle PaintInfoRec ← [data: rectangle[
clear: NOT clear, updateDivBds: FALSE, rect: [vr.x, vr.y, vr.w, vr.h]]]];
}
ELSE info ← NEW[all PaintInfoRec ← [data: all[clear: NOT clear]]]; -- what is it ?
Draw[handle, context, info];
IF whatChanged = NIL THEN {
mergingBounds ← FALSE;
};
}; -- 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;
}; -- GraphOpen
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];
$ShowSelTextOnSpec => ShowSelTextOnSpec[handle];
$ShowSelEntityOnSpec => ShowSelEntityOnSpec[handle];
$JumpLeftSameCurve => JumpLeftSameCurve[handle];
$JumpLeftAnyCurve => JumpLeftAnyCurve[handle];
$JumpUp => JumpUpAnyCurve[handle];
$ZoomIn => ZoomIn[handle, mx, my];
$LeftStep => SlideLeftNSteps[handle, nArg];
$RemoveCurve => IF UserEditAllowed[handle] THEN EraseSelEntity[handle];
$RemoveAllCurves => IF UserEditAllowed[handle] THEN RemoveAllCurves[handle];
$JumpRightSameCurve => JumpRightSameCurve[handle];
$JumpRightAnyCurve => JumpRightAnyCurve[handle];
$ClearText => EraseSelText[handle];
$RotateSelTextCW => RotateSelTextCW[handle, nArg];
$RotateSelTextCCW => RotateSelTextCCW[handle, nArg];
$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, FALSE, TRUE];
$Repaint => PaintAll[handle, FALSE, FALSE, TRUE];
ENDCASE;
};
ENDCASE;
Unlock[handle];
ENDLOOP;
};
};
}; -- GraphNotify
ProcessUserProfile: PUBLIC UserProfile.ProfileChangedProc = {
initialColumn
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 => color;
IF initialColumn = color AND NOT WindowManager.colorDisplayOn THEN initialColumn ← left;
createOption
rope ← UserProfile.Token[key: "Graph.CreateOption", default: "open"];
createOption ← SELECT TRUE FROM
rope.Equal["closeOtherViewers", FALSE] => closeOtherViewers,
rope.Equal["swapLastViewer", FALSE] => swapLastViewer,
rope.Equal["iconic", FALSE] => iconic,
ENDCASE => open;
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 => gray;
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.Init[];
}; -- Init
Init[];
}.
CHANGE LOG.
SChen, October 19, 1985 10:09:23 pm PDT, created.
Jacobi, June 11, 1986 6:43:10 pm PDT, Added normal option of viewer creation