ViewerContextsImpl.Mesa
Copyright Ó 1990, 1992 by Xerox Corporation. All rights reserved.
Last tweaked by Mike Spreitzer on February 20, 1992 11:34 am PST
Willie-s, April 21, 1992 5:24 pm PDT
DIRECTORY Atom, BasicTime, Icons, Imager, ImagerBackdoor, ImagerColor, ImagerPath, ImagerPrivate, ImagerTransformation, InputFocus, IO, MessageWindow, MJSContainers, PopUpButtons, Process, Real, Rope, Rules, SF, TIPUser, ViewerClasses, ViewerContexts, ViewerLocks, ViewerOps, ViewerPrivate, ViewerTools;
ViewerContextsImpl: CEDAR MONITOR
IMPORTS Atom, Icons, Imager, ImagerBackdoor, ImagerColor, ImagerPath, ImagerPrivate, ImagerTransformation, InputFocus, IO, MessageWindow, MJSContainers, PopUpButtons, Process, Real, Rules, TIPUser, ViewerLocks, ViewerOps, ViewerPrivate, ViewerTools
EXPORTS Imager, ViewerContexts
=
BEGIN OPEN Ctrs:MJSContainers, IT:ImagerTransformation, PUB:PopUpButtons, ViewerContexts;
LORA: TYPE ~ LIST OF REF ANY;
ROPE: TYPE ~ Rope.ROPE;
VEC: TYPE ~ Imager.VEC;
Rectangle: TYPE ~ Imager.Rectangle;
Xfm: TYPE ~ IT.Transformation;
PixelArray: TYPE ~ Imager.PixelArray;
SampleMap: TYPE ~ Imager.SampleMap;
PixelMap: TYPE ~ Imager.PixelMap;
ScanMode: TYPE ~ Imager.ScanMode;
Color: TYPE ~ Imager.Color;
Font: TYPE ~ Imager.Font;
ColorOperator: TYPE ~ Imager.ColorOperator;
XStringProc: TYPE ~ Imager.XStringProc;
Clipper: TYPE ~ ImagerBackdoor.Clipper;
Outline: TYPE ~ ImagerPath.Outline;
PathProc: TYPE ~ ImagerPath.PathProc;
ViewerList: TYPE ~ LIST OF Viewer;
ContextList: TYPE ~ LIST OF Context;
Class: TYPE ~ REF ClassRep;
ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep;
ContextViewer: TYPE ~ REF ContextViewerPrivate;
ContextViewerPrivate: PUBLIC TYPE ~ RECORD [
size: VECI,
NoteClear: PROC [clientData: REF ANY],
NoteChange: PROC [clientData: REF ANY, idealToViewer, viewerToScreen, viewerSize, idealSize: VECI],
NoteDestruction: PROC [clientData: REF ANY],
clientData: REF ANY,
scrollable: BOOL,
ctr, inner, rule, menuPane: Viewer ¬ NIL,
sizeButton, scrollCtl: Viewer ¬ NIL,
panes: ViewerList ¬ NIL, --includes menu pane
menuWidth: INTEGER ¬ 0,
ctxs: ContextList ¬ NIL,
i2w, w2s, i2s: VECI ¬ [0, 0], --ideal to viewer, viewer to screen, ideal to screen
vcs: VECI ¬ [0, 0], --viewer clip size
scbKnown: BOOL ¬ FALSE,
scb: BoxI ¬ [0, 0, 0, 0], --screen clip box
focused: BOOL ¬ FALSE, --have input focus
needClear: BOOL ¬ FALSE, --need to clear before painting
wantOut: BOOL ¬ FALSE, --paint would like to exit
in: BOOL ¬ FALSE, --the viewer lock is held
cIn: ViewerContext ¬ NIL, --the context, if any, accessing viewer data
wantIn: INTEGER ¬ 0, --how many contexts want the viewer lock to be held
stayIn: StayList ¬ NIL, --other reasons to hold the viewer lock
destroyed: BOOL ¬ FALSE,
change: CONDITION];
ViewerContext: TYPE ~ REF ViewerContextPrivate;
ViewerContextPrivate: TYPE ~ RECORD [
cv: ContextViewer,
base: Context,
baseClass: Class,
viewToIdeal: VECI ¬ [0, 0],
clipList: ClipList ¬ NIL,
valid: BOOL ¬ TRUE,
destroyed: BOOL ¬ FALSE];
StayList: TYPE ~ LIST OF Stay;
Stay: TYPE ~ RECORD [def: StayDef, name: ROPE ¬ NIL];
StayDef: TYPE ~ RECORD [vc: ViewerContext, reason: StayReason, name: ATOM ¬ NIL, where: RectI ¬ [0, 0, 0, 0]];
StayReason: TYPE ~ {savedBuffer, doWithBuffer};
ClipList: TYPE ~ LIST OF ClipItem;
ClipItem: TYPE ~ RECORD [
exclude: BOOL,
shape: SELECT kind: * FROM
rect => [ri: RectI],
outline => [o: Outline, oddWrap: BOOL],
ENDCASE];
RectI: TYPE ~ RECORD [x, y, w, h: INTEGER];
BoxI: TYPE ~ RECORD [xmin, ymin, xmax, ymax: INTEGER];
idXfm: Xfm ~ IT.Translate[[0, 0]];
msHold: INTEGER ¬ 1000;
ruleHeight: INTEGER ¬ 3;
tip: TIPUser.TIPTable ~ TIPUser.InstantiateNewTIPTable["ContextViewer.tip"];
icon: Icons.IconFlavor ¬ Icons.NewIconFromFile["ContextViewer.icons", 0];
ctrFlavor: ATOM ~ $ContextViewerContainer;
ctrClass: Ctrs.MJSContainerClass ~ NEW [Ctrs.MJSContainerClassRep ¬ [
icon: icon]];
alignerName: PUB.Image ~ PUB.ImageForRope["Align"];
alignerClass: PUB.Class ~ PUB.MakeClass[[
proc: CVPop,
choices: LIST[
[LIST[$Left, $Top]],
[LIST[$Top]],
[LIST[$Right, $Top]],
[LIST[$Left]],
[LIST[$HCenter, $VCenter]],
[LIST[$Right]],
[LIST[$Left, $Bottom]],
[LIST[$Bottom]],
[LIST[$Right, $Bottom]]
],
doc: "various alignment ops"]];
menuHeight: INTEGER ¬ Real.Ceiling[alignerName.size.y];
menuSep: INTEGER ¬ menuHeight;
destroyerClass: PUB.Class ~ PUB.MakeClass[[
proc: CVPop,
choices: LIST[ [$Destroy] ],
doc: "destroy this viewer"]];
sizeClass: PUB.Class ~ PUB.MakeClass[[
proc: CVPop,
choices: LIST[[$SetSize, "Set size"], [$FitSize, "Set ideal size to current visible size"]]
]];
toScrollableClass: PUB.Class ~ PUB.MakeClass[[
proc: CVPop,
choices: LIST[[$ToScrollable, "-> Scrollable"]],
image: PUB.ImageForRope["-> Scrollable"]
]];
toUnscrollableClass: PUB.Class ~ PUB.MakeClass[[
proc: CVPop,
choices: LIST[[$ToUnscrollable, "-> Unscrollable"]],
image: PUB.ImageForRope["-> Unscrollable"]
]];
vcClass: Class ~ NEW [ClassRep ¬ [
type: $ViewerContext,
Save: VCSave,
Restore: VCRestore,
SetInt: VCSetInt,
SetReal: VCSetReal,
SetT: VCSetT,
SetFont: VCSetFont,
SetColor: VCSetColor,
SetClipper: VCSetClipper,
GetInt: VCGetInt,
GetReal: VCGetReal,
GetT: VCGetT,
GetFont: VCGetFont,
GetColor: VCGetColor,
GetClipper: VCGetClipper,
ConcatT: VCConcatT,
Scale2T: VCScale2T,
RotateT: VCRotateT,
TranslateT: VCTranslateT,
Move: VCMove,
SetXY: VCSetXY,
SetXYRel: VCSetXYRel,
GetCP: VCGetCP,
StartUnderline: VCStartUnderline,
MaskUnderline: VCMaskUnderline,
CorrectMask: VCCorrectMask,
CorrectSpace: VCCorrectSpace,
Space: VCSpace,
SetCorrectMeasure: VCSetCorrectMeasure,
SetCorrectTolerance: VCSetCorrectTolerance,
Correct: VCCorrect,
DontCorrect: VCDontCorrect,
SetGray: VCSetGray,
SetSampledColor: VCSetSampledColor,
SetSampledBlack: VCSetSampledBlack,
Clip: VCClip,
ClipRectangle: VCClipRectangle,
ClipRectangleI: VCClipRectangleI,
Show: VCShow,
ShowBackward: VCShowBackward,
MaskFill: VCMaskFill,
MaskRectangle: VCMaskRectangle,
MaskStroke: VCMaskStroke,
MaskPixel: VCMaskPixel,
ShowAndFixedXRel: VCShowAndFixedXRel,
ShowText: VCShowText,
MaskRectangleI: VCMaskRectangleI,
MaskVector: VCMaskVector,
MaskDashedStroke: VCMaskDashedStroke,
MaskBitmap: VCMaskBitmap,
DrawBitmap: VCDrawBitmap,
DrawPixels: VCDrawPixels,
DoIfVisible: VCDoIfVisible,
DoWithBuffer: VCDoWithBuffer,
DrawObject: VCDrawObject,
GetBounds: VCGetBounds,
ViewReset: VCViewReset,
ViewTranslateI: VCViewTranslateI,
ViewClip: VCViewClip,
ViewClipRectangleI: VCViewClipRectangleI,
GetTransformation: VCGetTransformation,
Transform: ImagerPrivate.DefaultTransform,
MoveViewRectangle: VCMoveViewRectangle,
TestViewRectangle: VCTestViewRectangle,
GetBufferColorOperator: VCGetBufferColorOperator,
AccessBuffer: ImagerPrivate.DefaultAccessBuffer,
SaveBuffer: VCSaveBuffer,
RestoreBuffer: VCRestoreBuffer,
DiscardBuffer: VCDiscardBuffer,
propList: NIL]];
Create: PUBLIC PROC
[viewerInit: ViewerClasses.ViewerRec,
size: VECI,
NoteClear: PROC [clientData: REF ANY] ¬ NIL,
NoteChange: PROC [clientData: REF ANY, idealToViewer, viewerToScreen, viewerSize, idealSize: VECI] ¬ NIL,
NoteDestruction: PROC [clientData: REF ANY] ¬ NIL,
clientData: REF ANY ¬ NIL]
RETURNS [cv: ContextViewer] ~ {
unique: REF ROPE ~ NEW [ROPE ¬ "Another Context Viewer Class"];
ua: CARD ~ LOOPHOLE[unique];
cvClass: ViewerClasses.ViewerClass ~ NEW [ViewerClasses.ViewerClassRec ¬ [
flavor: Atom.MakeAtom[IO.PutFR1["ContextViewer[%g]", [cardinal[ua]] ]],
notify: Notify,
paint: Paint,
modify: CVModify,
destroy: CVDestroy,
scroll: Scroll,
hscroll: HScroll,
tipTable: tip,
icon: icon,
props: NIL]];
innerScroll: RECORD [x, y: BOOL] ~ [viewerInit.hscrollable, viewerInit.scrollable];
ViewerOps.RegisterViewerClass[cvClass.flavor, cvClass];
cv ¬ NEW [ContextViewerPrivate ¬ [size, NoteClear, NoteChange, NoteDestruction, clientData, viewerInit.scrollable]];
TRUSTED {Process.InitializeCondition[@cv.change, Process.MsecToTicks[5000]];
Process.EnableAborts[@cv.change]};
viewerInit.hscrollable ¬ viewerInit.scrollable ¬ FALSE;
viewerInit.data ¬ cv;
cv.ctr ¬ Ctrs.Create[ctrFlavor, viewerInit];
cv.menuPane ¬ CreateMenuPane[cv];
Ctrs.ChildXBound[cv.ctr, cv.menuPane];
cv.panes ¬ LIST[cv.menuPane];
cv.rule ¬ Rules.Create[[name: "the rule", parent: cv.ctr, wy: cv.menuPane.wh, ww: cv.ctr.cw, wh: ruleHeight]];
Ctrs.ChildXBound[cv.ctr, cv.rule];
cv.inner ¬ ViewerOps.CreateViewer[cvClass.flavor, [wy: cv.menuPane.wh+1, ww: cv.ctr.cw, wh: cv.ctr.ch-cv.menuPane.wh-1, name: "inner", scrollable: TRUE, hscrollable: TRUE, border: FALSE, parent: cv.ctr, data: cv]];
Ctrs.ChildXBound[cv.ctr, cv.inner];
Ctrs.ChildYBound[cv.ctr, cv.inner];
TRUSTED {Process.Detach[FORK PaintDriver[cv]]};
RETURN};
QuaViewer: PUBLIC PROC [cv: ContextViewer, which: WhichViewer] RETURNS [Viewer] ~ {
SELECT which FROM
inner => RETURN [cv.inner];
outer => RETURN [cv.ctr];
ENDCASE => ERROR};
QuaContextViewer: PUBLIC PROC [v: Viewer] RETURNS [MaybeContextViewer] ~ {
IF v.class.flavor = ctrFlavor THEN RETURN [[TRUE, NARROW[Ctrs.GetClientData[v], ContextViewer]]];
WITH v.data SELECT FROM
x: ContextViewer => RETURN [[TRUE, x]];
ENDCASE => NULL;
RETURN [[FALSE, NIL]]};
DoWithInfo: PUBLIC PROC [cv: ContextViewer, With: PROC [idealToViewer, viewerToScreen, viewerSize, idealSize: VECI, destroyed: BOOL]] ~ {
XfmsWithLock: PROC ~ {
With[cv.i2w, cv.w2s, [cv.inner.cw, cv.inner.ch], cv.size, cv.destroyed OR cv.ctr.destroyed];
RETURN};
ViewerLocks.CallUnderWriteLock[XfmsWithLock, cv.inner];
RETURN};
SetIdealSize: PUBLIC PROC [cv: ContextViewer, size: VECI] ~ {
SetIdealSizeWithLock: ENTRY PROC ~ {
ENABLE UNWIND => NULL;
cv.size ¬ size;
PUB.AmbushInstance[button: cv.sizeButton, image: ImageForSize[cv.size], specImage: TRUE];
IF cv.NoteChange#NIL THEN cv.NoteChange[cv.clientData, cv.i2w, cv.w2s, cv.vcs, cv.size];
RETURN};
ViewerLocks.CallUnderWriteLock[SetIdealSizeWithLock, cv.sizeButton];
RETURN};
GetMenuPane: PUBLIC PROC [cv: ContextViewer] RETURNS [Viewer]
~ {RETURN [cv.menuPane]};
AddMenuElt: PUBLIC PROC [cv: ContextViewer, elt: Viewer, paint: BOOL] ~ {
AddEltWithLock: PROC ~ {
ViewerOps.EstablishViewerPosition[elt, cv.menuWidth+menuSep, 0, elt.ww, elt.wh];
cv.menuWidth ¬ cv.menuWidth + menuSep + elt.ww;
IF elt.wh > cv.menuPane.ch THEN {
ViewerOps.EstablishViewerPosition[cv.menuPane, 0, cv.menuPane.wy, cv.menuPane.ww, elt.wh];
Reposition[cv, paint]}
ELSE IF paint THEN ViewerOps.PaintViewer[elt, all, TRUE];
RETURN};
ViewerLocks.CallUnderWriteLock[AddEltWithLock, cv.ctr];
RETURN};
AddPane: PUBLIC PROC [cv: ContextViewer, pane: Viewer, above, paint: BOOL] ~ {
AddWithLock: PROC ~ {
IF above THEN cv.panes ¬ CONS[pane, cv.panes]
ELSE {last: ViewerList ¬ cv.panes;
WHILE last.rest#NIL DO last ¬ last.rest ENDLOOP;
last.rest ¬ LIST[pane]};
Ctrs.ChildXBound[cv.ctr, pane];
Reposition[cv, paint];
RETURN};
ViewerLocks.CallUnderWriteLock[AddWithLock, cv.ctr];
RETURN};
DeletePane: PUBLIC PROC [cv: ContextViewer, pane: Viewer, paint: BOOL] ~ {
err: BOOL ¬ FALSE;
DeleteWithLock: PROC ~ {
last: ViewerList ¬ NIL;
FOR pl: ViewerList ¬ cv.panes, pl.rest WHILE pl#NIL DO
IF pl.first=pane THEN {
IF last=NIL THEN cv.panes ¬ pl.rest ELSE last.rest ¬ pl.rest;
ViewerOps.DestroyViewer[pane, FALSE];
Reposition[cv, paint];
RETURN};
ENDLOOP;
err ¬ TRUE};
IF pane = cv.menuPane THEN ERROR;
ViewerLocks.CallUnderWriteLock[DeleteWithLock, cv.ctr];
IF err THEN ERROR;
RETURN};
Reposition: PROC [cv: ContextViewer, paint: BOOL] ~ {
y: INTEGER ¬ 0;
FOR pl: ViewerList ¬ cv.panes, pl.rest WHILE pl#NIL DO
ViewerOps.EstablishViewerPosition[pl.first, 0, y, cv.ctr.cw, pl.first.wh];
y ¬ y + pl.first.wh;
ENDLOOP;
ViewerOps.EstablishViewerPosition[cv.rule, 0, y, cv.ctr.cw, ruleHeight];
ViewerOps.EstablishViewerPosition[cv.inner, 0, y+ruleHeight, cv.ctr.cw, cv.ctr.ch-y-ruleHeight];
IF paint THEN ViewerOps.PaintViewer[cv.ctr, client, TRUE];
RETURN};
CreateContext: PUBLIC PROC [cv: ContextViewer] RETURNS [ctx: Context] ~ {
base: Context ~ ViewerPrivate.CreateContext[ViewerPrivate.Screen.FIRST];
vc: ViewerContext ~ NEW [ViewerContextPrivate ¬ [cv, base, base.class]];
IF NOT cv.scbKnown THEN {
br: Rectangle ~ ImagerBackdoor.GetBounds[base];
cv.scb ¬ [Real.Ceiling[br.x], Real.Ceiling[br.y], Real.Floor[br.x+br.w], Real.Floor[br.y+br.h]];
cv.scbKnown ¬ TRUE};
ctx ¬ NEW [Imager.ContextRep ¬ [class: vcClass, state: NIL, data: vc]];
cv.ctxs ¬ CONS[ctx, cv.ctxs];
RETURN};
QuaViewerContext: PUBLIC PROC [c: Context] RETURNS [MaybeContextViewer] ~ {
IF c.class=vcClass AND c.data#NIL THEN WITH c.data SELECT FROM
x: ViewerContext => RETURN [[TRUE, x.cv]];
ENDCASE => NULL;
RETURN [[FALSE, NIL]]};
SetCursor: PUBLIC PROC [cv: ContextViewer, ct: ViewerClasses.CursorType] ~ {
cv.inner.class.cursor ¬ ct;
RETURN};
CVDestroy: PROC [self: Viewer] --ViewerClasses.DestroyProc-- ~ {
cv: ContextViewer ~ NARROW[self.data];
Destroy[cv];
RETURN};
Destroy: PUBLIC PROC [cv: ContextViewer] ~ {
IF NOT cv.destroyed THEN {
cv.destroyed ¬ TRUE;
IF NOT cv.ctr.destroyed THEN TRUSTED {Process.Detach[FORK ViewerOps.DestroyViewer[cv.ctr]]};
IF cv.NoteDestruction#NIL THEN cv.NoteDestruction[cv.clientData];
};
RETURN};
CreateMenuPane: PROC [cv: ContextViewer] RETURNS [menuPane: Viewer] ~ {
aligner, destroyer: Viewer;
menuPane ¬ Ctrs.Create[$VanillaMJSContainer, [parent: cv.ctr, ww: cv.ctr.cw, wh: menuHeight, border: FALSE, scrollable: FALSE], FALSE];
aligner ¬ alignerClass.Instantiate[viewerInfo: [parent: menuPane, name: "Align", border: FALSE], instanceData: cv, paint: FALSE];
destroyer ¬ destroyerClass.Instantiate[viewerInfo: [parent: menuPane, name: "Destroy", wx: aligner.ww+menuSep, border: FALSE], instanceData: cv, paint: FALSE];
cv.sizeButton ¬ sizeClass.Instantiate[viewerInfo: [parent: menuPane, name: "Size", wx: destroyer.wx+destroyer.ww+menuSep, border: FALSE], instanceData: cv, image: ImageForSize[cv.size], paint: FALSE];
cv.scrollCtl ¬ (IF cv.scrollable THEN toUnscrollableClass ELSE toScrollableClass).Instantiate[viewerInfo: [parent: menuPane, wx: cv.sizeButton.wx+cv.sizeButton.ww+menuSep, border: FALSE], instanceData: cv, paint: FALSE];
cv.menuWidth ¬ cv.scrollCtl.wx + cv.scrollCtl.ww;
RETURN};
ImageForSize: PROC [size: VECI] RETURNS [PUB.Image]
~ {RETURN PUB.ImageForRope[IO.PutFR["Size: [w: %g, h: %g]", [integer[size.x]], [integer[size.y]] ]]};
CVPop: PROC [view, instanceData, classData, key: REF ANY] --PUB.PopUpButtonProc-- ~ {
cv: ContextViewer ~ NARROW[instanceData];
WITH key SELECT FROM
a: ATOM => SELECT a FROM
$Destroy => Destroy[cv];
$SetSize => {
seln: ROPE ~ ViewerTools.GetSelectionContents[];
in: IO.STREAM ~ IO.RIS[seln];
newSize: RECORD [x, y: INT] ¬ [0, 0];
{ENABLE IO.Error, IO.EndOfStream => GOTO Bitch;
newSize.x ¬ in.GetInt[];
newSize.y ¬ in.GetInt[];
IF NOT (newSize.x IN [5..5000] AND newSize.y IN [5..5000] ) THEN GOTO Bitch;
EXITS Bitch => {
MessageWindow.Append["Selection should be two whitespace-separated integers (of reasonable value)", TRUE];
RETURN};
};
in.Close[];
SetIdealSize[cv, [newSize.x, newSize.y]];
};
$FitSize => {
SetIdealSize[cv, [cv.inner.cw, cv.inner.ch]];
};
$ToScrollable => {
cv.scrollable ¬ TRUE;
PUB.AmbushInstance[cv.scrollCtl, toUnscrollableClass]};
$ToUnscrollable => {
cv.scrollable ¬ FALSE;
PUB.AmbushInstance[cv.scrollCtl, toScrollableClass]};
ENDCASE => ERROR;
lora: LORA => {
h, v: REAL ¬ -1.0;
Alignit: PROC ~ {
IF h>=0.0 THEN cv.i2w.x ¬ Real.Round[h * (cv.vcs.x-cv.size.x)];
IF v>=0.0 THEN cv.i2w.y ¬ Real.Round[v * (cv.vcs.y-cv.size.y)];
cv.i2s ¬ ViAdd[cv.i2w, cv.w2s];
cv.needClear ¬ TRUE;
IF cv.NoteChange#NIL THEN cv.NoteChange[cv.clientData, cv.i2w, cv.w2s, cv.vcs, cv.size];
InvalidateEm[cv];
RETURN};
FOR l: LORA ¬ lora, l.rest WHILE l#NIL DO
SELECT l.first FROM
$Left => h ¬ 0.0;
$HCenter => h ¬ 0.5;
$Right => h ¬ 1.0;
$Bottom => v ¬ 0.0;
$VCenter => v ¬ 0.5;
$Top => v ¬ 1.0;
ENDCASE => ERROR;
ENDLOOP;
ViewerLocks.CallUnderWriteLock[Alignit, cv.inner];
RETURN};
ENDCASE => ERROR;
};
NestedEnter: ERROR ~ CODE;
Enter: PROC [vc: ViewerContext] ~ {
cv: ContextViewer ~ vc.cv;
destroy: BOOL ¬ FALSE;
GetIn: ENTRY PROC [cv: ContextViewer] ~ {
ENABLE UNWIND => NULL;
IF cv.in AND cv.cIn=vc THEN RETURN WITH ERROR NestedEnter;
cv.wantIn ¬ cv.wantIn.SUCC;
Wait[];
cv.cIn ¬ vc;
RETURN};
Wait: INTERNAL PROC ~ {
ENABLE UNWIND => cv.wantIn ¬ cv.wantIn.PRED;
BROADCAST cv.change;
UNTIL cv.destroyed OR cv.ctr.destroyed OR cv.in AND cv.cIn=NIL AND (cv.stayIn#NIL OR NOT cv.wantOut) DO
WAIT cv.change;
ENDLOOP;
destroy ¬ cv.destroyed OR cv.ctr.destroyed;
RETURN};
GetIn[cv];
IF destroy AND NOT vc.destroyed
THEN {vc.destroyed ¬ TRUE; vc.base.SetNoImage[TRUE]};
IF NOT vc.valid THEN {
xmin: INTEGER ~ MAX[cv.w2s.x, cv.i2s.x];
ymin: INTEGER ~ MAX[cv.w2s.y, cv.i2s.y];
xmax: INTEGER ~ MIN[cv.w2s.x+cv.vcs.x, cv.i2s.x+cv.size.x];
ymax: INTEGER ~ MIN[cv.w2s.y+cv.vcs.y, cv.i2s.y+cv.size.y];
cp: VEC ~ vc.baseClass.GetCP[vc.base, FALSE];
ImagerBackdoor.ViewReset[vc.base];
ImagerBackdoor.ViewClipRectangleI[vc.base, xmin, ymin, MAX[xmax-xmin, 0], MAX[ymax-ymin, 0]];
ImagerBackdoor.ViewTranslateI[vc.base, cv.i2s.x, cv.i2s.y];
FOR cr: ClipList ¬ vc.clipList, cr.rest WHILE cr#NIL DO
WITH cr.first SELECT FROM
q: rect ClipItem => vc.baseClass.ViewClipRectangleI[vc.base, q.ri.x, q.ri.y, q.ri.w, q.ri.h, cr.first.exclude];
q: outline ClipItem => {
ClipPath: ImagerPath.PathProc ~ {
ImagerPath.MapOutline[q.o, moveTo, lineTo, curveTo, conicTo, arcTo];
RETURN};
vc.baseClass.ViewClip[vc.base, ClipPath, q.oddWrap, cr.first.exclude]};
ENDCASE => ERROR;
ENDLOOP;
ImagerBackdoor.ViewTranslateI[vc.base, vc.viewToIdeal.x, vc.viewToIdeal.y];
vc.baseClass.SetXY[vc.base, cp];
vc.valid ¬ TRUE};
RETURN};
Exit: ENTRY PROC [vc: ViewerContext] ~ {
ENABLE UNWIND => NULL;
IF vc.cv.cIn # vc THEN ERROR;
vc.cv.wantIn ¬ vc.cv.wantIn.PRED;
vc.cv.cIn ¬ NIL;
BROADCAST vc.cv.change;
RETURN};
PaintDriver: PROC [cv: ContextViewer] ~ {
Test: ENTRY PROC RETURNS [BOOL] ~ {
ENABLE UNWIND => NULL;
UNTIL cv.destroyed OR cv.ctr.destroyed OR (cv.needClear OR cv.wantIn>0) AND NOT cv.ctr.iconic DO WAIT cv.change ENDLOOP;
RETURN [cv.destroyed OR cv.ctr.destroyed]};
exit: BOOL;
DO ENABLE ABORTED => CONTINUE;
exit ¬ Test[];
IF exit THEN EXIT;
ViewerOps.PaintViewer[cv.inner, client, FALSE];
ENDLOOP;
RETURN};
Paint: ENTRY PROC [self: Viewer, context: Context, whatChanged: REF, clear: BOOL] RETURNS [quit: BOOL ¬ FALSE] --ViewerClasses.PaintProc-- ~ {
ENABLE UNWIND => {cv: ContextViewer ~ NARROW[self.data]; cv.wantOut ¬ cv.in ¬ FALSE};
cv: ContextViewer ~ NARROW[self.data];
wts: VECI;
IF self.destroyed THEN RETURN;
IF cv.needClear AND NOT clear THEN {
context.SetColor[Imager.white];
context.MaskRectangleI[0, 0, self.cw, self.ch];
clear ¬ TRUE};
IF clear THEN {
cv.needClear ¬ FALSE;
IF cv.NoteClear#NIL THEN cv.NoteClear[cv.clientData]};
[wts.x, wts.y] ¬ ViewerOps.UserToScreenCoords[self, 0, 0];
IF cv.w2s#wts OR cv.vcs#[self.cw, self.ch] THEN {
cv.w2s ¬ wts;
cv.i2s ¬ ViAdd[cv.i2w, cv.w2s];
cv.vcs ¬ [self.cw, self.ch];
IF cv.NoteChange#NIL THEN cv.NoteChange[cv.clientData, cv.i2w, cv.w2s, cv.vcs, cv.size];
InvalidateEm[cv]};
IF cv.wantIn>0 THEN TRUSTED {
hold: CONDITION;
Process.InitializeCondition[@hold, Process.MsecToTicks[msHold]];
Process.EnableAborts[@hold];
cv.in ¬ TRUE;
BROADCAST cv.change;
WAIT hold;
cv.wantOut ¬ TRUE;
UNTIL cv.destroyed OR cv.cIn=NIL AND cv.stayIn=NIL DO WAIT cv.change ENDLOOP;
cv.wantOut ¬ cv.in ¬ FALSE};
RETURN};
Scroll: PROC [self: Viewer, op: ViewerClasses.ScrollOp, amount: INTEGER, shift, control: BOOL ¬ FALSE] RETURNS [top, bottom: INTEGER ¬ LAST[INTEGER]] --ViewerClasses.ScrollProc-- ~ {
cv: ContextViewer ~ NARROW[self.data];
UpdateY: PROC ~ {
cv.i2s ¬ ViAdd[cv.i2w, cv.w2s];
cv.needClear ¬ TRUE;
IF cv.NoteChange#NIL THEN cv.NoteChange[cv.clientData, cv.i2w, cv.w2s, cv.vcs, cv.size];
InvalidateEm[cv];
RETURN};
IF op#query AND NOT cv.scrollable THEN {
MessageWindow.Append["This viewer is not currently scrollable.", TRUE];
RETURN};
SELECT op FROM
query => {
botr: REAL ~ REAL[cv.size.y+cv.i2w.y]/cv.size.y;
topr: REAL ~ REAL[cv.size.y+cv.i2w.y-cv.vcs.y]/cv.size.y;
RETURN [MIN[MAX[Real.Round[topr*100.0], 0], 100], MIN[MAX[Real.Round[botr*100.0], 0], 100] ]};
up, down => cv.i2w.y ¬ cv.i2w.y + amount * (IF op=up THEN 1 ELSE -1);
thumb => cv.i2w.y ¬ Real.Round[(amount/100.0 - 1.0)*cv.size.y] + cv.vcs.y;
ENDCASE => ERROR;
ViewerLocks.CallUnderWriteLock[UpdateY, self];
RETURN};
HScroll: PROC [self: Viewer, op: ViewerClasses.HScrollOp, amount: INTEGER, shift, control: BOOL ¬ FALSE] RETURNS [left, right: INTEGER ¬ LAST[INTEGER]] --ViewerClasses.HScrollProc-- ~ {
cv: ContextViewer ~ NARROW[self.data];
UpdateX: PROC ~ {
cv.i2s ¬ ViAdd[cv.i2w, cv.w2s];
cv.needClear ¬ TRUE;
IF cv.NoteChange#NIL THEN cv.NoteChange[cv.clientData, cv.i2w, cv.w2s, cv.vcs, cv.size];
InvalidateEm[cv];
RETURN};
IF op#query AND NOT cv.scrollable THEN {
MessageWindow.Append["This viewer is not currently scrollable.", TRUE];
RETURN};
SELECT op FROM
query => {
leftr: REAL ~ REAL[-cv.i2w.x]/cv.size.x;
rightr: REAL ~ REAL[cv.vcs.x-cv.i2w.x]/cv.size.x;
RETURN [MIN[MAX[Real.Round[leftr*100.0], 0], 100], MIN[MAX[Real.Round[rightr*100.0], 0], 100] ]};
left, right => cv.i2w.x ¬ cv.i2w.x + amount * (IF op=right THEN 1 ELSE -1);
thumb => cv.i2w.x ¬ Real.Round[(-amount/100.0)*cv.size.y];
ENDCASE => ERROR;
ViewerLocks.CallUnderWriteLock[UpdateX, self];
RETURN};
InvalidateEm: --Viewer INTERNAL-- PROC [cv: ContextViewer] ~ {
cv.in ¬ cv.in;
FOR cl: ContextList ¬ cv.ctxs, cl.rest WHILE cl#NIL DO
vc: ViewerContext ~ NARROW[cl.first.data];
vc.valid ¬ FALSE;
ENDLOOP;
cv.ctxs ¬ NIL;
RETURN};
Notify: ViewerClasses.NotifyProc ~ {
cv: ContextViewer ~ NARROW[self.data];
IF NOT cv.focused THEN InputFocus.SetInputFocus[self];
RETURN};
CVModify: PROC [self: Viewer, change: ViewerClasses.ModifyAction] ~ {
cv: ContextViewer ~ NARROW[self.data];
SELECT change FROM
set, pop => cv.focused ¬ TRUE;
kill, push => cv.focused ¬ FALSE;
ENDCASE => ERROR;
RETURN};
VCSave: PROC [context: Context, all: BOOL] RETURNS [REF] ~ {
vc: ViewerContext ~ NARROW[context.data];
ans: REF;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.Save[vc.base, all]};
Exit[vc]; RETURN [ans]};
VCRestore: PROC [context: Context, ref: REF] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.Restore[vc.base, ref]};
Exit[vc]; RETURN};
VCSetInt: PROC [context: Context, key: ImagerBackdoor.IntKey, val: INT] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetInt[vc.base, key, val]};
Exit[vc]; RETURN};
VCSetReal: PROC [context: Context, key: ImagerBackdoor.RealKey, val: REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetReal[vc.base, key, val]};
Exit[vc]; RETURN};
VCSetT: PROC [context: Context, m: Xfm] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetT[vc.base, m]};
Exit[vc]; RETURN};
VCSetFont: PROC [context: Context, font: Imager.Font] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetFont[vc.base, font]};
Exit[vc]; RETURN};
VCSetColor: PROC [context: Context, color: Imager.Color] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetColor[vc.base, color]};
Exit[vc]; RETURN};
VCSetClipper: PROC [context: Context, clipper: ImagerBackdoor.Clipper] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetClipper[vc.base, clipper]};
Exit[vc]; RETURN};
VCGetInt: PROC [context: Context, key: ImagerBackdoor.IntKey] RETURNS [INT] ~ {
vc: ViewerContext ~ NARROW[context.data];
ans: INT;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.GetInt[vc.base, key]};
Exit[vc]; RETURN [ans]};
VCGetReal: PROC [context: Context, key: ImagerBackdoor.RealKey] RETURNS [REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
ans: REAL;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.GetReal[vc.base, key]};
Exit[vc]; RETURN [ans]};
VCGetT: PROC [context: Context] RETURNS [Xfm] ~ {
vc: ViewerContext ~ NARROW[context.data];
ans: Xfm;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.GetT[vc.base]};
Exit[vc]; RETURN [ans]};
VCGetFont: PROC [context: Context] RETURNS [Imager.Font] ~ {
vc: ViewerContext ~ NARROW[context.data];
ans: Imager.Font;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.GetFont[vc.base]};
Exit[vc]; RETURN [ans]};
VCGetColor: PROC [context: Context] RETURNS [Imager.Color] ~ {
vc: ViewerContext ~ NARROW[context.data];
ans: Imager.Color;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.GetColor[vc.base]};
Exit[vc]; RETURN [ans]};
VCGetClipper: PROC [context: Context] RETURNS [ImagerBackdoor.Clipper] ~ {
vc: ViewerContext ~ NARROW[context.data];
ans: ImagerBackdoor.Clipper;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.GetClipper[vc.base]};
Exit[vc]; RETURN [ans]};
VCConcatT: PROC [context: Context, m: Xfm] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.ConcatT[vc.base, m]};
Exit[vc]; RETURN};
VCScale2T: PROC [context: Context, s: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.Scale2T[vc.base, s]};
Exit[vc]; RETURN};
VCRotateT: PROC [context: Context, a: REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.RotateT[vc.base, a]};
Exit[vc]; RETURN};
VCTranslateT: PROC [context: Context, t: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.TranslateT[vc.base, t]};
Exit[vc]; RETURN};
VCMove: PROC [context: Context, rounded: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.Move[vc.base, rounded]};
Exit[vc]; RETURN};
VCSetXY: PROC [context: Context, p: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetXY[vc.base, p]};
Exit[vc]; RETURN};
VCSetXYRel: PROC [context: Context, v: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetXYRel[vc.base, v]};
Exit[vc]; RETURN};
VCGetCP: PROC [context: Context, rounded: BOOL] RETURNS [ans: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
ans ¬ vc.baseClass.GetCP[vc.base, rounded]};
Exit[vc]; RETURN};
VCStartUnderline: PROC [context: Context] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.StartUnderline[vc.base]};
Exit[vc]; RETURN};
VCMaskUnderline: PROC [context: Context, dy, h: REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskUnderline[vc.base, dy, h]};
Exit[vc]; RETURN};
VCCorrectMask: PROC [context: Context] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.CorrectMask[vc.base]};
Exit[vc]; RETURN};
VCCorrectSpace: PROC [context: Context, v: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.CorrectSpace[vc.base, v]};
Exit[vc]; RETURN};
VCSpace: PROC [context: Context, x: REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.Space[vc.base, x]};
Exit[vc]; RETURN};
VCSetCorrectMeasure: PROC [context: Context, v: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetCorrectMeasure[vc.base, v]};
Exit[vc]; RETURN};
VCSetCorrectTolerance: PROC [context: Context, v: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetCorrectTolerance[vc.base, v]};
Exit[vc]; RETURN};
VCCorrect: PROC [context: Context, action: PROC] ~ {
vc: ViewerContext ~ NARROW[context.data];
vc.baseClass.Correct[vc.base, action];
RETURN};
VCDontCorrect: PROC [context: Context, action: PROC, saveCP: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
IF saveCP THEN {
cp: VEC;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
cp ¬ vc.baseClass.GetCP[vc.base, FALSE]};
Exit[vc];
vc.baseClass.DontCorrect[vc.base, action, saveCP];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetXY[vc.base, cp]};
Exit[vc]}
ELSE vc.baseClass.DontCorrect[vc.base, action, saveCP];
RETURN};
VCSetGray: PROC [context: Context, f: REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetGray[vc.base, f]};
Exit[vc]; RETURN};
VCSetSampledColor: PROC [context: Context, pa: PixelArray, m: Xfm, colorOperator: ColorOperator] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetSampledColor[vc.base, pa, m, colorOperator]};
Exit[vc]; RETURN};
VCSetSampledBlack: PROC [context: Context, pa: PixelArray, m: Xfm, clear: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.SetSampledBlack[vc.base, pa, m, clear]};
Exit[vc]; RETURN};
VCClip: PROC [context: Context, path: PathProc, oddWrap: BOOL, exclude: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.Clip[vc.base, path, oddWrap, exclude]};
Exit[vc]; RETURN};
VCClipRectangle: PROC [context: Context, r: Imager.Rectangle, exclude: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.ClipRectangle[vc.base, r, exclude]};
Exit[vc]; RETURN};
VCClipRectangleI: PROC [context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.ClipRectangleI[vc.base, x, y, w, h, exclude]};
Exit[vc]; RETURN};
VCShow: PROC [context: Context, string: XStringProc, xrel: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.Show[vc.base, string, xrel]};
Exit[vc]; RETURN};
VCShowBackward: PROC [context: Context, string: XStringProc] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.ShowBackward[vc.base, string]};
Exit[vc]; RETURN};
VCMaskFill: PROC [context: Context, path: PathProc, oddWrap: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskFill[vc.base, path, oddWrap]};
Exit[vc]; RETURN};
VCMaskRectangle: PROC [context: Context, r: Imager.Rectangle] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskRectangle[vc.base, r]};
Exit[vc]; RETURN};
VCMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskStroke[vc.base, path, closed]};
Exit[vc]; RETURN};
VCMaskPixel: PROC [context: Context, pa: PixelArray] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskPixel[vc.base, pa]};
Exit[vc]; RETURN};
VCShowAndFixedXRel: PROC [context: Context, string: XStringProc, x: REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.ShowAndFixedXRel[vc.base, string, x]};
Exit[vc]; RETURN};
VCShowText: PROC [context: Context, text: REF READONLY TEXT, start, len: NAT, xrel: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.ShowText[vc.base, text, start, len, xrel]};
Exit[vc]; RETURN};
VCMaskRectangleI: PROC [context: Context, x, y, w, h: INTEGER] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskRectangleI[vc.base, x, y, w, h]};
Exit[vc]; RETURN};
VCMaskVector: PROC [context: Context, p1, p2: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskVector[vc.base, p1, p2]};
Exit[vc]; RETURN};
VCMaskDashedStroke: PROC [context: Context, path: PathProc, patternLen: NAT, pattern: PROC [NAT] RETURNS [REAL], offset, length: REAL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskDashedStroke[vc.base, path, patternLen, pattern, offset, length]};
Exit[vc]; RETURN};
VCMaskBitmap: PROC [context: Context, bitmap: SampleMap, referencePoint: SF.Vec, scanMode: ScanMode, position: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.MaskBitmap[vc.base, bitmap, referencePoint, scanMode, position]};
Exit[vc]; RETURN};
VCDrawBitmap: PROC [context: Context, bitmap: SampleMap, referencePoint: SF.Vec, scanMode: ScanMode, position: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.DrawBitmap[vc.base, bitmap, referencePoint, scanMode, position]};
Exit[vc]; RETURN};
VCDrawPixels: PROC [context: Context, pixelMap: PixelMap, colorOperator: ColorOperator, referencePoint: SF.Vec, scanMode: ScanMode, position: VEC] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.DrawPixels[vc.base, pixelMap, colorOperator, referencePoint, scanMode, position]};
Exit[vc]; RETURN};
VCDoIfVisible: PROC [context: Context, r: Rectangle, action: PROC] ~ {
vc: ViewerContext ~ NARROW[context.data];
vc.baseClass.DoIfVisible[vc.base, r, action];
RETURN};
VCDoWithBuffer: PROC [context: Context, action: PROC, x, y, w, h: INTEGER, backgroundColor: Color] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
AddStay[vc.cv, [vc, doWithBuffer, NIL, [x, y, w, h]]]};
Exit[vc];
vc.baseClass.DoWithBuffer[vc.base, action, x, y, w, h, backgroundColor];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
RemStay[vc.cv, [vc, doWithBuffer, NIL, [x, y, w, h]]]};
Exit[vc]; RETURN};
VCDrawObject: PROC [context: Context, object: Imager.Object, position: VEC, interactive: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.DrawObject[vc.base, object, position, interactive]};
Exit[vc]; RETURN};
VCGetBounds: PROC [context: Context] RETURNS [Rectangle] ~ {
vc: ViewerContext ~ NARROW[context.data];
RETURN vc.baseClass.GetBounds[vc.base]};
VCViewReset: PROC [context: Context] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.viewToIdeal ¬ [0, 0];
vc.clipList ¬ NIL;
vc.valid ¬ FALSE};
Exit[vc]; RETURN};
VCViewTranslateI: PROC [context: Context, x, y: INTEGER] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
cp: VEC ~ vc.baseClass.GetCP[vc.base, FALSE];
vc.viewToIdeal ¬ ViAdd[vc.viewToIdeal, [x, y]];
vc.baseClass.ViewTranslateI[vc.base, x, y];
vc.baseClass.SetXY[vc.base, cp]};
Exit[vc]; RETURN};
VCViewClip: PROC [context: Context, path: PathProc, oddWrap: BOOL, exclude: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
m: Xfm ~ IT.Translate[[vc.viewToIdeal.x, vc.viewToIdeal.y]];
o: Outline ~ ImagerPath.OutlineFromPath[path, oddWrap, m];
vc.clipList ¬ CONS[[exclude, outline[o, oddWrap]], vc.clipList];
vc.baseClass.ViewClip[vc.base, path, oddWrap, exclude]};
Exit[vc]; RETURN};
VCViewClipRectangleI: PROC [context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.clipList ¬ CONS[[exclude, rect[[x+vc.viewToIdeal.x, y+vc.viewToIdeal.y, w, h]]], vc.clipList];
vc.baseClass.ViewClipRectangleI[vc.base, x, y, w, h, exclude]};
Exit[vc]; RETURN};
VCGetTransformation: PROC [context: Context, from, to: ImagerBackdoor.CoordSys] RETURNS [Xfm] ~ {
vc: ViewerContext ~ NARROW[context.data];
RETURN vc.baseClass.GetTransformation[vc.base, from, to]};
VCMoveViewRectangle: PROC [context: Context, width, height, fromX, fromY, toX, toY: INTEGER] ~ {
vc: ViewerContext ~ NARROW[context.data];
cv: ContextViewer ~ vc.cv;
Enter[vc]; {ENABLE UNWIND => Exit[vc];
bClipIdeal: BoxI ~ [0, 0, cv.size.x, cv.size.y];
bClipViewer: BoxI ~ BiOffset[[0, 0, cv.vcs.x, cv.vcs.y], [-cv.i2w.x, -cv.i2w.y]];
bClipScreen: BoxI ~ BiOffset[cv.scb, [-cv.i2s.x, -cv.i2s.y]];
bClip: BoxI ~ BiOffset[BiIntersect[BiIntersect[bClipIdeal, bClipViewer], bClipScreen], [-vc.viewToIdeal.x, -vc.viewToIdeal.y]];
bFrom: BoxI ¬ BiIntersect[bClip, [fromX, fromY, fromX+width, fromY+height]];
bTo: BoxI ¬ BiIntersect[bClip, [toX, toY, toX+width, toY+height]];
toW: INTEGER ¬ bTo.xmax - bTo.xmin;
toH: INTEGER ¬ bTo.ymax - bTo.ymin;
dx: INTEGER ¬ toX - fromX;
dy: INTEGER ¬ toY - fromY;
bFrom ¬ BiIntersect[bFrom, BiOffset[bTo, [-dx, -dy]]];
{fromW: INTEGER ¬ bFrom.xmax - bFrom.xmin;
fromH: INTEGER ¬ bFrom.ymax - bFrom.ymin;
IF fromW>0 AND fromH>0 THEN vc.baseClass.MoveViewRectangle[vc.base, fromW, fromH, bFrom.xmin, bFrom.ymin, bFrom.xmin+dx, bFrom.ymin+dy];
IF toW>0 AND toH>0 AND (toW#fromW OR toH#fromH) THEN {
dun: BoxI ~ BiOffset[bFrom, [dx, dy]];
MarkForgotten: PROC ~ {
midY: INTEGER ~ dun.ymin;
midH: INTEGER ~ fromH;
vc.baseClass.SetT[vc.base, idXfm];
vc.baseClass.SetColor[vc.base, forgottenColor];
IF dun.ymin > bTo.ymin THEN vc.baseClass.MaskRectangleI[vc.base, bTo.xmin, bTo.ymin, toW, dun.ymin - bTo.ymin];
IF bTo.ymax > dun.ymax THEN vc.baseClass.MaskRectangleI[vc.base, bTo.xmin, dun.ymax, toW, bTo.ymax - dun.ymax];
IF midH>0 THEN {
IF dun.xmin > bTo.xmin THEN vc.baseClass.MaskRectangleI[vc.base, bTo.xmin, midY, dun.xmin - bTo.xmin, midH];
IF bTo.xmax > dun.xmax THEN vc.baseClass.MaskRectangleI[vc.base, dun.xmax, midY, bTo.xmax - dun.xmax, midH];
};
RETURN};
vc.base.DoSave[MarkForgotten];
width ¬ width};
}};
Exit[vc]; RETURN};
forgottenColor: Color ¬ ImagerColor.ColorFromRGB[[1, 0, 0]];
VCTestViewRectangle: PROC [context: Context, x, y, w, h: INTEGER] RETURNS [ImagerBackdoor.Visibility] ~ {
vc: ViewerContext ~ NARROW[context.data];
RETURN vc.baseClass.TestViewRectangle[vc.base, x, y, w, h]};
VCGetBufferColorOperator: PROC [context: Context] RETURNS [ColorOperator] ~ {
vc: ViewerContext ~ NARROW[context.data];
RETURN vc.baseClass.GetBufferColorOperator[vc.base]};
VCSaveBuffer: PROC [context: Context, id: ATOM, path: PathProc, oddWrap: BOOL] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
AddStay[vc.cv, [vc, savedBuffer, id]];
vc.baseClass.SaveBuffer[vc.base, id, path, oddWrap]};
Exit[vc]; RETURN};
VCRestoreBuffer: PROC [context: Context, id: ATOM] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
vc.baseClass.RestoreBuffer[vc.base, id]};
Exit[vc]; RETURN};
VCDiscardBuffer: PROC [context: Context, id: ATOM] ~ {
vc: ViewerContext ~ NARROW[context.data];
Enter[vc]; {ENABLE UNWIND => Exit[vc];
RemStay[vc.cv, [vc, savedBuffer, id]];
vc.baseClass.DiscardBuffer[vc.base, id]};
Exit[vc]; RETURN};
AddStay: --Viewer INTERNAL-- PROC [cv: ContextViewer, stay: StayDef]
~ {cv.stayIn ¬ CONS[[stay, IF stay.name#NIL THEN Atom.GetPName[stay.name] ELSE NIL], cv.stayIn]};
RemStay: --Viewer INTERNAL-- PROC [cv: ContextViewer, stay: StayDef] ~ {
last: StayList ¬ NIL;
FOR this: StayList ¬ cv.stayIn, this.rest WHILE this#NIL DO
IF this.first.def = stay THEN {
IF last=NIL THEN cv.stayIn ¬ this.rest ELSE last.rest ¬ this.rest;
RETURN};
last ¬ this; ENDLOOP;
ERROR};
BiIntersect: PROC [a, b: BoxI] RETURNS [BoxI] ~ {
xmin: INTEGER ~ MAX[a.xmin, b.xmin];
ymin: INTEGER ~ MAX[a.ymin, b.ymin];
RETURN[[xmin, ymin, MAX[xmin, MIN[a.xmax, b.xmax]], MAX[ymin, MIN[a.ymax, b.ymax]]]]};
BiOffset: PROC [b: BoxI, o: VECI] RETURNS [BoxI]
~ {RETURN[[b.xmin+o.x, b.ymin+o.y, b.xmax+o.x, b.ymax+o.y]]};
ViAdd: PROC [a, b: VECI] RETURNS [VECI]
~ INLINE {RETURN [[a.x+b.x, a.y+b.y]]};
Ctrs.RegisterClass[ctrFlavor, ctrClass];
END.