File: GGWindowImpl.mesa
Author: Eric Bier on June 5, 1985 11:34:50 pm PDT
Copyright © 1985 by Xerox Corporation. All rights reserved.
Last edited by Bier on August 17, 1985 5:39:02 pm PDT
Contents: Code to create a Gargoyle window. Calls GGContainer to make the container and adds menus at the top. The graphics part of the Gargoyle window is a new viewer of class $ActionArea. The paint proc for a $ActionArea is defined herein.
Stone, July 15, 1985 11:39:29 am PDT
DIRECTORY
Commander, FileNames, FS, GGCaret, GGContainer, GGError, GGInterfaceTypes, GGMenus, GGModelTypes, GGObjects, GGRefresh, GGUserInput, GGVector, GGViewerOps, GGWindow, Icons, Imager, ImagerBackdoor, IO, Menus, Rope, TiogaButtons, TIPUser, ViewerClasses, ViewerOps;
GGWindowImpl: CEDAR PROGRAM
IMPORTS Commander, FileNames, FS, GGCaret, Icons, Imager, ImagerBackdoor, GGContainer, GGError, GGMenus, GGObjects, GGRefresh, GGUserInput, GGVector, GGViewerOps, Menus, Rope, TIPUser, ViewerOps
EXPORTS GGWindow
SHARES ViewerClasses =
BEGIN
Camera: TYPE = REF CameraObj;
CameraObj: TYPE = GGModelTypes.CameraObj;
Caret: TYPE = REF CaretObj;
CaretObj: TYPE = GGInterfaceTypes.CaretObj;
Cluster: TYPE = GGModelTypes.Cluster;
ImagerProc: TYPE = GGInterfaceTypes.ImagerProc;
MouseButton: TYPE = Menus.MouseButton;
Outline: TYPE = GGModelTypes.Outline;
PaintQueue: TYPE = REF PaintQueueObj;
PaintQueueObj: TYPE = GGInterfaceTypes.PaintQueueObj;
Point: TYPE = GGModelTypes.Point;
Scene: TYPE = REF SceneObj;
SceneObj: TYPE = GGModelTypes.SceneObj;
Segment: TYPE = GGModelTypes.Segment;
Sequence: TYPE = GGModelTypes.Sequence;
Traj: TYPE = GGModelTypes.Traj;
Viewer: TYPE = ViewerClasses.Viewer;
GargoyleData: TYPE = REF GargoyleDataObj;
GargoyleDataObj: TYPE = GGInterfaceTypes.GargoyleDataObj;
entryHeight: CARDINAL = 15; -- height of a line of items
entryVSpace: CARDINAL = 2; -- vertical leading between lines
entryHSpace: CARDINAL = 2; -- horizontal space between items on a line
column1: CARDINAL = 200; -- horizontal space between margin and column 1;
column2: CARDINAL = 250; -- horizontal space between margin and column 2.
column3: CARDINAL = 500; -- horizontal space between margin and column 3;
nameSize: CARDINAL = 140;
smallNumberSize: CARDINAL = 45;
numberSize: CARDINAL = 80;
bigNumberSize: CARDINAL = 160;
pointSize: CARDINAL = 160;
ActionAreaPaint: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOLFALSE] = TRUSTED {
ViewerClasses.PaintProc
whatChanged is a GargoyleData. self is an ActionArea.
gargoyleData: GargoyleData;
IF whatChanged = NIL THEN { --we are being called by Window Manager
gargoyleData ← NARROW[self.data];
PlaceOrigin[self];
Painter[$PaintEntireScene, gargoyleData];
}
ELSE {
gargoyleData ← NARROW[whatChanged];
GGRefresh.ActionAreaPaint[context, gargoyleData.refresh.paintAction, gargoyleData];
};
};
Painter: PUBLIC PROC [paintAction: ATOM, gargoyleData: GargoyleData] = {
gargoyleData.refresh.paintAction ← paintAction;
ViewerOps.PaintViewer[
viewer: gargoyleData.actionArea,
hint: client,
whatChanged: gargoyleData,
clearClient: FALSE];
}; -- end of Painter
PlaceOrigin: PROC [viewer: Viewer] = TRUSTED {
viewer is the ActionArea. The camera for this view represents the translation from the center of the window in screen dots and a scaling factor.
Find the center of the ActionArea in viewer coordinates.
originX, originY: INTEGER;
rect: Imager.Rectangle;
gargoyleData: GargoyleData ← NARROW[viewer.data];
originX ← viewer.cw; -- convert window width to real
originY ← viewer.ch; -- convert window height to real
originX ← originX/2; -- find the midpoint in window coords
originY ← originY/2;
gargoyleData.camera.cameraScreen ← [originX, originY];
gargoyleData.refresh.backgroundBitmap ← ImagerBackdoor.NewBitmap[viewer.cw, viewer.ch];
gargoyleData.refresh.backgroundContext ← ImagerBackdoor.BitmapContext[gargoyleData.refresh.backgroundBitmap];
gargoyleData.refresh.chunkingBitmap ← ImagerBackdoor.NewBitmap[viewer.cw, viewer.ch];
gargoyleData.refresh.chunkingContext ← ImagerBackdoor.BitmapContext[gargoyleData.refresh.chunkingBitmap];
Initialize the context to white.
rect ← ImagerBackdoor.GetBounds[gargoyleData.refresh.backgroundContext];
Imager.SetColor[gargoyleData.refresh.backgroundContext, Imager.white];
Imager.MaskRectangle[gargoyleData.refresh.backgroundContext, rect];
};
CameraToScreen: PUBLIC PROC [pointCamera: Point, camera: Camera] RETURNS [pointScreen: Point] = {
pointScreen ← GGVector.Add[pointCamera, camera.cameraScreen];
};
ScreenToCamera: PUBLIC PROC [pointScreen: Point, camera: Camera] RETURNS [pointCamera: Point] = {
pointCamera ← GGVector.Sub[pointScreen, camera.cameraScreen];
};
ScreenToWorld: PUBLIC PROC [pointScreen: Point, camera: Camera] RETURNS [pointWorld: Point] = {
pointCamera: Point;
pointCamera ← GGVector.Sub[pointScreen, camera.cameraScreen];
pointWorld ← GGVector.Add[pointCamera, camera.cameraWorld];
};
Init: PROC = {
actionAreaClass: ViewerClasses.ViewerClass;
actionAreaClass ←
NEW[ViewerClasses.ViewerClassRec ← [
paint: ActionAreaPaint,
notify: GGUserInput.InputNotify,
tipTable: TIPUser.InstantiateNewTIPTable["Gargoyle.TIP"]
]];
ViewerOps.RegisterViewerClass[$ActionArea, actionAreaClass];
Commander.Register[
key: "Gargoyle",
proc: NewWindow,
doc: "Create a Gargoyle Graphics Window",
clientData: NIL
];
};
NewWindow: Commander.CommandProc = {
scene: Scene ← GGObjects.CreateScene[];
[] ← CreateWindow[scene, TRUE, TRUE, FileNames.CurrentWorkingDirectory[]];
};
CreateWindow: PUBLIC PROC [scene: Scene, iconic: BOOL, paint: BOOL, workingDirectory: Rope.ROPE] RETURNS [gargoyleData: GargoyleData] = {
Create the Container and the top menu which looks like this:
Clear, Reset, Get, Store, Save, Split, Erase
windowMenu: Menus.Menu ← Menus.CreateMenu[3];
iconFileName: Rope.ROPE;
gargoyleData ← NEW[GargoyleDataObj];
gargoyleData.originalWorkingDirectory ← FileNames.CurrentWorkingDirectory[];
gargoyleData.scene ← scene;
gargoyleData.camera ← NEW[CameraObj ← [[0.0, 0.0], 1.0, [0.0, 0.0]]];
gargoyleData.caret ← NEW[CaretObj];
gargoyleData.anchor ← NEW[CaretObj];
gargoyleData.currentAction ← $None;
Construct the outer container.
iconFileName ← Rope.Concat[gargoyleData.originalWorkingDirectory, "Gargoyle.icons"];
gargoyleData.outer ← GGContainer.Create[
info: [
name: "Gargoyle",
menu: windowMenu,
data: gargoyleData,
iconic: TRUE,
column: left,
scrollable: FALSE,
icon: Icons.NewIconFromFile[iconFileName, 0]
],
paint: FALSE];
GGMenus.BuildControlPanel[gargoyleData, windowMenu];
BuildActionArea[gargoyleData];
ViewerOps.OpenIcon[icon: gargoyleData.outer, closeOthers: FALSE, bottom: FALSE, paint: TRUE];
};
BuildActionArea: PRIVATE PROC [gargoyleData: GargoyleData] = {
Create an ActionArea which fills the bottom of the Gargoyle Container
gargoyleData.actionArea ← ViewerOps.CreateViewer[
flavor: $ActionArea,
info: [
parent: gargoyleData.outer,
wx: 0, wy: gargoyleData.height,
ww: gargoyleData.outer.ww,
wh: gargoyleData.outer.wh, -- only initial values for ww and wh. They are constrained below
data: gargoyleData,
scrollable: FALSE
]
];
GGContainer.ChildXBound[gargoyleData.outer, gargoyleData.actionArea];
GGContainer.ChildYBound[gargoyleData.outer, gargoyleData.actionArea];
gargoyleData.height ← gargoyleData.height + gargoyleData.actionArea.wh;
}; -- end of BuildActionArea
ReloadTipTable: PUBLIC PROC [gargoyleData: GargoyleData] = {
newTable: TIPUser.TIPTable;
actionArea: ViewerClasses.Viewer;
bad: BOOLFALSE;
tableName, msg: Rope.ROPE;
GGError.AppendHerald["Reloading tip table...", TRUE];
tableName ← Rope.Concat[gargoyleData.originalWorkingDirectory, "Gargoyle.TIP"];
newTable ← TIPUser.InstantiateNewTIPTable[tableName
! FS.Error => {
bad ← TRUE;
msg ← Rope.Concat["Cannot read TIP table file: ", tableName];
CONTINUE};
TIPUser.InvalidTable => {
bad ← TRUE;
msg ← Rope.Concat["Error(s) saved on TIP.Errors for: ", tableName];
CONTINUE}];
IF bad THEN {GGError.Append[msg, oneLiner]; RETURN};
GGError.AppendHerald["Done.", FALSE];
IF newTable = NIL THEN ERROR;
actionArea ← gargoyleData.actionArea;
actionArea.tipTable ← newTable;
};
NewCaretPos: PUBLIC PROC [gargoyleData: GargoyleData] = {
oldCaretPos, newCaretPos: Point;
success: BOOL;
distance: REAL;
[oldCaretPos, success] ← GGViewerOps.GetPoint[gargoyleData.distanceLine.oldCaret];
newCaretPos ← GGCaret.GetPoint[gargoyleData.caret];
IF NOT success THEN RETURN;
distance ← GGVector.Distance[oldCaretPos, newCaretPos];
GGViewerOps.SetReal[gargoyleData.distanceLine.distance, distance];
GGViewerOps.SetPoint[gargoyleData.distanceLine.newCaret, newCaretPos];
};
SaveCaretPos: PUBLIC PROC [gargoyleData: GargoyleData] = {
caretPos: Point;
caretPos ← GGCaret.GetPoint[gargoyleData.caret];
GGViewerOps.SetPoint[gargoyleData.distanceLine.oldCaret, caretPos];
};
Init[];
END.