GraphUpdate.mesa, Copyright © 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, October 22, 1985 3:43:23 pm PDT
DIRECTORY
Convert USING [RopeFromInt],
Graph USING [CaretIndex, ColorIndex, Entity, FontIndex, EntityList, EntityRec, NumberOfColors, NumberOfFonts, ROPE, Text, TextRec, Texts, UnitLineWidth, ValueList, Viewer, XY],
GraphCarets USING [Move, TurnOff, TurnOn],
GraphConvert USING [RopeOfSlope, ValueListFromRope],
GraphCleanUp USING [CleanUpVL],
GraphPrivate USING [AddEntityButton, AddTextButton, EntityHashSize, GraphAtomProc, GraphHandle, PaintAction, PaintAll, PaintEntity, PaintGrids, PaintTarget, PaintText, systemColor],
GraphUtil USING [BlinkMsg, EntityFromId, GetIntField, GetRealField, InitSegAll, RaiseError, RealToScreenI, SetColor, TextFromId, UpdateSegAll, UpdateSegEnd],
Imager USING [Error],
ImagerFont USING [Find, Font, Scale],
Rope USING [Cat, Concat],
VFonts USING [EstablishFont],
ViewerTools USING [GetContents, SetContents];
GraphUpdate: CEDAR PROGRAM
IMPORTS Convert, GraphCarets, GraphCleanUp, GraphConvert, GraphPrivate, GraphUtil, Imager, ImagerFont, Rope, VFonts, ViewerTools
EXPORTS GraphPrivate = { OPEN Graph, GraphPrivate, GraphUtil;
Update: PUBLIC GraphAtomProc = {
If handle = nil or controller = nil then noop; otherwise:
updates internal values based on users input on panel and, if chart.viewer # nil, also updates display.
IF handle # NIL THEN IF handle.controller # NIL THEN { OPEN handle;
msg: ROPE; i: INT; -- r: REAL;
SELECT atom FROM
$Divisions => UpdateDivisions[handle];
$Bounds => UpdateBounds[handle];
$Carets => UpdateCarets[handle];
$Targets => UpdateTargets[handle];
$Grids => UpdateGrids[handle];
$Color => {
[msg, i] ← GetIntField[controller.colorIndex];
IF msg = NIL THEN
IF i IN ColorIndex THEN UpdateColor[handle, i]
ELSE BlinkMsg[Rope.Cat[
"Index must be in [0..", Convert.RopeFromInt[NumberOfColors], ")"]];
};
$Font => {
[msg, i]← GetIntField[controller.fontIndex];
IF msg = NIL THEN
IF i IN FontIndex THEN UpdateFont[handle, i]
ELSE BlinkMsg[Rope.Cat[
"Index must be in [0..", Convert.RopeFromInt[NumberOfFonts], ")"]];
};
$Text => {
[msg, i] ← GetIntField[controller.textId];
IF msg = NIL THEN {
text: Text ← TextFromId[handle.allTexts, i];
UpdateText[handle, text, i]; -- text = nil => add it.
};
};
$XEntry => UpdateX[handle];
$Entity => {
[msg, i] ← GetIntField[controller.entityId];
IF msg = NIL THEN {
entity: Entity ← EntityFromId[entityHash, i];
UpdateEntity[handle, entity, i];
};
};
$CurveGroup => {
[msg, i] ← GetIntField[controller.entityId];
IF msg = NIL THEN {
eg: EntityGroup ← EntityGroupFromId[handle.entityGroupList, i];
UpdateEntityGroup[handle, eg, i];
};
};
$Crosssection => {
[msg, r] ← GetRealField[controller.entityId];
IF msg = NIL THEN UpdateCrosssection[controller, x];
};
ENDCASE => RaiseError[$UnknownAtom, "in Update"];
};
}; -- Update
UpdateDivisions: PROC[handle: GraphHandle] = { OPEN handle;
graph.auto[divisions] ← controller.auto[divisions];
graph.division[x] ← UpdateFromIntField[controller.divX, graph.division[x]];
graph.division[y] ← UpdateFromIntField[controller.divY, graph.division[x]];
IF graph.division[x] < 1 OR graph.division[y] < 1 THEN BlinkMsg["x or y divisions < 1."]
ELSE IF chart.viewer # NIL THEN PaintAll[handle, TRUE]; -- paint everything.
}; -- SetDivisionFields
UpdateBounds: PROC[handle: GraphHandle] = { OPEN handle;
graph.auto[bounds] ← controller.auto[bounds];
graph.bounds ← [
xmin: UpdateFromRealField[controller.xmin, graph.bounds.xmin],
ymin: UpdateFromRealField[controller.ymin, graph.bounds.ymin],
xmax: UpdateFromRealField[controller.xmax, graph.bounds.xmax],
ymax: UpdateFromRealField[controller.ymax, graph.bounds.ymax]
];
IF NOT graph.auto[bounds] AND (graph.bounds.xmin <= graph.bounds.xmax OR graph.bounds.ymin <= graph.bounds.ymax) THEN BlinkMsg["Illegal bounds. (xmin <= xmax or ymin <= ymax.)"]
ELSE IF chart.viewer # NIL THEN PaintAll[handle, TRUE];
}; -- UpdateBounds
UpdateCarets: PROC[handle: GraphHandle] = { OPEN handle;
UpdateCaret: PROC[index: CaretIndex] = {
vx: Viewer ← controller.caretPlace[index][x];
vy: Viewer ← controller.caretPlace[index][y];
rx: REAL ← UpdateFromRealField[vx, graph.caret[index].place.x];
ry: REAL ← UpdateFromRealField[vy, graph.caret[index].place.y];
sx: INTEGER ← RealToScreenI[handle, rx, x];
sy: INTEGER ← RealToScreenI[handle, ry, y];
graph.caret[index]^ ← [on: controller.caretOn[index], place: [rx, ry]];
IF chart.viewer # NIL THEN {
GraphCarets.Move[handle, sx, sy, index];
IF graph.caret[index].on THEN GraphCarets.TurnOn[handle, index]
ELSE GraphCarets.TurnOff[handle, index];
no fields on panel have to do with blinking.
};
}; -- UpdateCaret
FOR i: CaretIndex IN CaretIndex DO UpdateCaret[primary] ENDLOOP;
ViewerTools.SetContents[controller.slope,
IF (graph.showSlope ← controller.slopeOn) THEN
GraphConvert.RopeOfSlope[graph.caret[primary].place, graph.caret[secondary].place]
ELSE NIL];
}; -- UpdateCarets
UpdateTargets: PROC[handle: GraphHandle] = { OPEN handle;
UpdateTarget: PROC[xy: XY] = {
IF chart.viewer # NIL AND graph.target[xy].on THEN PaintTarget[handle, erase, xy];
graph.target[xy].value ← UpdateFromRealField[
controller.targetValue[xy], graph.target[xy].value];
graph.target[xy].width ← UpdateFromRealField[
controller.targetWidth[xy], graph.target[xy].width];
graph.target[xy].colorIndex ← UpdateFromIntField[
controller.targetColor[xy], graph.target[xy].colorIndex];
IF graph.target[xy].colorIndex NOT IN ColorIndex THEN {
graph.target[xy].colorIndex ← 13;
BlinkMsg["Color index out of range. Default is used."];
};
IF (graph.target[xy].on ← controller.targetOn[xy]) THEN
IF chart.viewer # NIL THEN PaintTarget[handle, paint, xy];
};
UpdateTarget[x];
UpdateTarget[y];
}; -- UpdateTargets
UpdateGrids: PROC[handle: GraphHandle] = { OPEN handle;
UpdateGrid: PROC[wasOn, isOn: BOOL, xy: XY] = {
IF wasOn # isOn THEN {
IF chart.viewer # NIL THEN {
IF wasOn THEN PaintGrids[handle, erase, xy];
PaintGrids[handle, paint, xy]; -- paint grids or ticks.
};
graph.grids[xy] ← isOn;
};
};
UpdateGrid[graph.grids[x], controller.gridOn[x], x];
UpdateGrid[graph.grids[y], controller.gridOn[y], y];
}; -- UpdateGrids
UpdateColor: PROC[handle: GraphHandle, index: ColorIndex] = { OPEN handle;
GetRGB: PROC[v: Viewer, r: REAL] RETURNS [rgb: REAL] = {
rgb ← UpdateFromRealField[v, r];
IF rgb > 1.0 OR rgb < 0.0 THEN {
rgb ← MAX[0.0, MIN[1.0, rgb]];
BlinkMsg["color value limited by [0..1]."];
};
};
graph.color[index].R ← GetRGB[controller.red, graph.color[index].R];
graph.color[index].G ← GetRGB[controller.green, graph.color[index].G];
graph.color[index].B ← GetRGB[controller.blue, graph.color[index].B];
systemColor[index] ← SetColor[index, graph.color[index]];
}; -- UpdateColor
UpdateFont: PROC[handle: GraphHandle, index: FontIndex] = { OPEN handle;
PaintTextsOfThisFont: PROC [action: PaintAction] = {
IF chart.viewer # NIL THEN FOR ts: Texts ← graph.texts, ts.rest UNTIL ts = NIL DO
text: Text ← ts.first;
IF text.fontIndex = index THEN PaintText[handle, action, text];
ENDLOOP;
}; -- PaintTextsOfThisFont
msg: ROPE;
vFontSize: INT;
pFontScale: REAL;
vFont, pFont: ImagerFont.Font ← NIL;
fontFamily: ROPE ← ViewerTools.GetContents[controller.fontFamily];
IF fontFamily = NIL THEN {BlinkMsg["Specify font family."]; RETURN};
[msg, vFontSize] ← GetIntField[controller.vFontSize];
IF msg # NIL THEN RETURN;
[msg, pFontScale] ← GetRealField[controller.pFontScale];
IF msg # NIL THEN RETURN;
vFont ← VFonts.EstablishFont[fontFamily, vFontSize, controller.boldOn, controller.italicOn, TRUE]; -- true means default on failure.
pFont ← ImagerFont.Scale[
ImagerFont.Find[Rope.Cat[
"Xerox/PressFonts/",
fontFamily,
Rope.Concat[IF controller.boldOn THEN "-b" ELSE "-m",
IF controller.italicOn THEN "ir" ELSE "rr"]]],
pFontScale
! Imager.Error => {pFont ← NIL; CONTINUE}];
IF pFont = NIL THEN BlinkMsg["Can't find font."]
ELSE {
PaintTextsOfThisFont[erase];
graph.font[index] ← [fontFamily, controller.boldOn, controller.italicOn, vFontSize, pFontScale];
imagerFonts[screen][index] ← vFont;
imagerFonts[interpress][index] ← pFont;
PaintTextsOfThisFont[paint];
};
}; -- UpdateFont
UpdateText: PROC[handle: GraphHandle, text: Text, id: INT] = { OPEN handle;
rope: ROPE ← ViewerTools.GetContents[controller.textContent];
msg: ROPENIL;
add: BOOL ← text = NIL;
int: INT;
GetTextPlace: PROC [v: Viewer, original: REAL] RETURNS [r: REAL] = {
r ← UpdateFromRealField[v, original];
IF r > 3.0 OR r < - 3.0 THEN {
r ← MAX[-3.0, MIN[3.0, r]];
BlinkMsg["Text coordinates limited by [-3..3]"];
};
}; -- GetTextPlace
IF rope = NIL THEN {BlinkMsg["name field is empty."]; RETURN};
IF add THEN text ← NEW[TextRec ← []]
ELSE IF chart.viewer # NIL THEN PaintText[handle, erase, text];
int ← UpdateFromIntField[controller.textFont, text.fontIndex];
IF int NOT IN FontIndex THEN msg ← "Illegal font index."
ELSE text.fontIndex ← int;
int ← UpdateFromIntField[controller.textColor, text.colorIndex];
IF int NOT IN ColorIndex THEN msg ← msg.Cat[" Illegal color index."]
ELSE text.colorIndex ← int;
IF msg # NIL THEN {BlinkMsg[msg]; text ← NIL; RETURN};
text^ ← [
text: rope,
place: [GetTextPlace[controller.textPlaceX, text.place.x],
GetTextPlace[controller.textPlaceY, text.place.y]],
fontIndex: text.fontIndex,
colorIndex: text.colorIndex,
justifX: controller.justifX,
justifY: controller.justifY,
id: id
];
IF chart.viewer # NIL THEN PaintText[handle, paint, text];
IF add THEN {
handle.allTexts ← CONS[text, handle.allTexts];
graph.texts ← CONS[text, graph.texts];
[] ← AddTextButton[handle, text];
};
}; -- UpdateText
UpdateX: PROC [handle: GraphHandle] = { OPEN handle; -- proc no use for now.
length, pos: INT;
vList: ValueList;
[vList, length, pos] ← GraphConvert.ValueListFromRope[
ViewerTools.GetContents[controller.xValues]];
IF pos # 0 THEN RETURN;
IF vList = NIL THEN {BlinkMsg["The values field is empty."]; RETURN};
IF length # entityGroupList.first.length THEN {
BlinkMsg[Rope.Cat["There should be ",
Convert.RopeFromInt[entityGroupList.first.length],
"numbers in the values field."]];
RETURN};
UpdateSegEnd[entityGroupList.first.x, vList];
IF chart.viewer # NIL THEN PaintAll[handle];
}; -- UpdateX
UpdateEntity: PROC [handle: GraphHandle, entity: Entity, id: INT] = { OPEN handle;
msg: ROPENIL;
add: BOOL ← entity = NIL;
vList: ValueList;
length, pos, int: INT ← 0;
painted: BOOLFALSE;
eGroup: EntityGroup;
[msg, groupId] ← GetIntField[controller.entityGroupId, "group id"];
IF msg # NIL THEN RETURN;
eGroup ← EntityGroupFromId[handle.entityGroupList, groupId];
IF eGroup = NIL THEN {
BlinkMsg[Rope.Cat["There is no group with id = ", Convert.RopeFromInt[groupId], "."]];
RETURN;
};
[vList, length, pos] ← GraphConvert.ValueListFromRope[
ViewerTools.GetContents[controller.entityValues]];
IF pos # 0 THEN RETURN;
IF vList = NIL THEN {BlinkMsg["Error in the values field."]; RETURN};
IF length # entityGroupList.first.length THEN {
BlinkMsg[Rope.Cat["There should be ",
Convert.RopeFromInt[entityGroupList.first.length],
"numbers in the values field."]];
RETURN};
IF add THEN entity ← NEW[EntityRec ← []]
ELSE {
FOR el: EntityList ← graph.entityList, el.rest UNTIL el = NIL DO
IF el.first = entity THEN painted ← TRUE;
ENDLOOP;
IF chart.viewer # NIL AND painted THEN PaintEntity[handle, erase, entity];
};
int ← UpdateFromIntField[controller.entityColor, entity.colorIndex];
IF int NOT IN ColorIndex THEN {BlinkMsg[" Illegal color index."]; RETURN};
entity^ ← [
name: ViewerTools.GetContents[controller.entityName],
colorIndex: int,
mark: controller.mark,
width: UpdateFromRealField[controller.entityWidth, entity.width],
oldValues: IF add THEN vList ELSE entity.oldValues,
segments: entity.segments,
group: IF entity.group = NIL THEN entityGroupList.first ELSE entity.group, -- for now.
id: id
];
IF add THEN { -- add it on handle and on table.
index: INT[0..EntityHashSize) ← id MOD EntityHashSize;
entityHash[index] ← CONS[entity, entityHash[index]];
entity.group.ys.first.entityList ← CONS[entity, entity.group.ys.first.entityList]; -- for now.
[] ← AddEntityButton[handle, entity]; -- for now.
InitSegAll[entity];
}
ELSE UpdateSegAll[entity, vList];
vList ← GraphCleanUp.CleanUpVL[vList];
IF add OR NOT painted THEN { -- add it on graph
graph.entityList ← CONS[entity, graph.entityList];
};
IF chart.viewer # NIL THEN PaintEntity[handle, paint, entity];
}; -- UpdateEntity
UpdateEntityGroup: PROC[handle: GraphHandle, eg: EntityGroup] = { OPEN handle;
}; -- UpdateEntityGroup
UpdateCrosssection: PROC[handle: GraphHandle, x: REAL] = { OPEN handle;
rope, tRope: ROPE;
ok: BOOL;
y: REAL;
FOR egl: EntityGroupList ← graph.entityGroupList, egl.rest UNTIL egl = NIL DO
xEntity: Entity ← egl.first.x;
rope ← IF rope = NIL THEN "[" ELSE rope.Concat[", ["];
FOR el: EntityList ← egl.first.entityList, el.rest UNTIL el = NIL DO
IF el.first = xEntity THEN LOOP;
[ok, y] ← Crosssection[xEntity.newValues, el.first, x];
tRope ← IF ok THEN Convert.RopeFromReal[y] ELSE "(*)";
rope ← IF rope = NIL THEN tRope ELSE rope.Cat[" ", tRope];
ENDLOOP;
rope.Concat["]"];
ENDLOOP;
ViewerTools.SetContent[controller.yIds, rope];
}; -- UpdateCrosssection
UpdateFromIntField: PROC[field: Viewer, origianl: INT ← 0, showMsg: BOOLTRUE] RETURNS [int: INT ← 0] = {
caller should make sure that field is not nil.
msg: ROPE;
[msg, int] ← GetIntField[field, showMsg];
IF msg # NIL THEN int ← origianl;
}; -- UpdateFromIntField
UpdateFromRealField: PROC[field: Viewer, origianl: REAL ← 0.0, showMsg: BOOLTRUE] RETURNS [real: REAL ← 0.0] = {
caller should make sure that field is not nil.
msg: ROPE;
[msg, real] ← GetRealField[field, showMsg];
IF msg # NIL THEN real ← origianl;
}; -- UpdateFromRealField
}.
LOG.
SChen, created at October 9, 1985 8:57:21 pm PDT.