XTSetterImpl.mesa
Copyright Ó 1987, 1992 by Xerox Corporation. All rights reserved.
Jean-Marc Frailong, November 22, 1988 9:21:33 am PST
Bloomenthal, November 25, 1987 3:26:42 pm PST
Viewer tool for XNS printing
Weiser, January 22, 1991 0:11 am PST
Kenneth A. Pier, April 1, 1991 3:31 pm PST
Chauser, April 15, 1991 11:13 am PDT
Willie-s, April 6, 1992 1:01 pm PDT
DIRECTORY
Args USING [Arg, ArgsGet, Error],
Buttons USING [Button, ButtonProc, Create, SetDisplayStyle],
ChoiceButtons USING [BuildEnumTypeSelection, ChoiceDoesntExist, GetSelectedButton, EnumTypeRef, UpdateChoiceButtons],
Commander USING [CommandProc, Register],
Containers USING [Create],
Convert USING [Error, IntFromRope, RopeFromInt, RealFromRope, RopeFromReal],
FileDWIM USING [ResolveHint],
Icons USING [IconFlavor, NewIconFromFile],
IO,
IPMaster USING [Version],
Labels USING [Create, Set, SetDisplayStyle],
Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuEntry, MenuProc],
PieViewers USING [Create, Set],
PFS USING [Error],
PrintingP4V3Aux USING [ExposeFormatterStatus, ExposeInterpressMasterStatus, ExposeMarkingEngineStatus, ExposeSpoolerStatus],
Process USING [Detach, SecondsToTicks, SetTimeout],
Rope,
RopeList USING [Memb, Reverse],
Rules USING [Create],
SymTab USING [Create, Fetch, Ref, Store],
TypeScript USING [Create],
UserProfile USING [Boolean, CallWhenProfileChanges, ListOfTokens, ProfileChangedProc, Token],
ViewerClasses USING [AdjustProc, Viewer, ViewerClass, ViewerClassRec, ViewerRec],
ViewerIO USING [CreateMessageWindowStream, CreateViewerStreams],
ViewerLocks USING [CallUnderWriteLock],
ViewerOps USING [AddProp, CreateViewer, DestroyViewer, EnumProc, EnumerateViewers, EstablishViewerPosition, FetchProp, MoveViewer, PaintHint, PaintViewer, RegisterViewerClass, SetMenu],
ViewerTools USING [GetContents, GetSelectedViewer, GetSelectionContents, InhibitUserEdits, MakeNewTextViewer, SetContents, SetSelection],
VFonts USING [CharWidth, StringWidth],
WindowManager USING [colorDisplayOn],
XNSCredentials USING [XNSCredentialsChangeProc, RegisterForChange],
XNSPrint USING [Context, Error, GetDefaults, GetPrinterProperties, GetPrinterStatus, Properties, PrinterStatus, PrintRequest, RequestStatus, StatusChangedProc, UnRegisterPrintRequest],
XTSetter, XTSetterPrivate;
Menu buttons
NewButton: Menus.MenuProc ~ {
Create a new occurence of the tool with the currently selected rope as printer name
tool: Tool = NARROW[clientData];
printer: ROPE = ViewerTools.GetSelectionContents[];
IF ~Rope.IsEmpty[printer] THEN [] ¬ NewTool[printer, tool.feedBack ! Error => CONTINUE];
};
LeftScreenButton: Menus.MenuProc ~ {
Print the left half of the B&W screen
tool: Tool = NARROW[clientData];
XTSetter.PrintScreen[tool, left];
};
ScreenButton: Menus.MenuProc ~ {
Print the B&W screen
tool: Tool = NARROW[clientData];
XTSetter.PrintScreen[tool, bw];
};
RightScreenButton: Menus.MenuProc ~ {
Print the right half of the B&W screen
tool: Tool = NARROW[clientData];
XTSetter.PrintScreen[tool, right];
};
ColorScreenButton: Menus.MenuProc ~ {
Print the color screen
tool: Tool = NARROW[clientData];
IF WindowManager.colorDisplayOn THEN XTSetter.PrintScreen[tool, color]
ELSE tool.feedBack.PutRope["Color display is off! Nothing to print...\n"];
};
PrintButton: Menus.MenuProc ~ {
Print the currently selected object, whatever it may be:
- if something looking like a filename is currently selected, print this file after applying FileDWIM hints
- if a Tioga/Typescript viewer is currently selected, print its contents
- else, try something intelligent (no guarantee whatsoever)
FileNameSelected:
PROC [self:
ROPE]
RETURNS [
BOOL] ~ {
Returns TRUE if this looks like a valid file name
length: INT = self.Length[];
IF length < 2 THEN RETURN [FALSE]; -- rope is too short
IF Rope.SkipTo[s: self, skip: " \t"] # length THEN RETURN [FALSE]; -- whitespace in rope
RETURN [TRUE]; -- All is OK
};
FileFromHint:
PROC [hint:
ROPE, v: Viewer]
RETURNS [fName:
ROPE] ~ {
Apply hint to find a contextFileName from the viewer, then apply FileDWIM.ResolveHint to derive a full file name. All FS errors result in hint being sent back as the result.
ENABLE PFS.Error => GO TO Failed;
ctxFN: ROPE;
commandToolName: ROPE = "CommandTool: WD = ";
IF v=NIL THEN RETURN [fName: hint]; -- no hope...
ctxFN ¬ v.file;
IF Rope.IsEmpty[ctxFN]
AND v.name#
NIL
THEN {
-- hack for CommandTool wDir
ctxFN ¬ Rope.Substr[v.name, 0, Rope.SkipTo[v.name, 0, " "]];
ctxFN ¬ XTSetter.ExpandName["Dummy.dummy", ctxFN].fullFName;
};
fName ¬ FileDWIM.ResolveHint[hint: hint, contextFileName: ctxFN, searchHack: FALSE, tryAll: TRUE].fullFileName;
IF Rope.IsEmpty[fName] THEN fName ¬ hint;
EXITS Failed => fName ¬ hint;
};
InvalidFile:
PROC [why:
ROPE] ~ {
IO.PutF1[tool.feedBack, "File error: %g\n", IO.rope[why]];
};
tool: Tool = NARROW[clientData];
curSel: ROPE = ViewerTools.GetSelectionContents[];
curViewer: Viewer = ViewerTools.GetSelectedViewer[];
SELECT
TRUE
FROM
FileNameSelected[curSel] => {
-- Print the file name selected
fName: ROPE = FileFromHint[curSel, curViewer];
XTSetter.PrintFile[tool, fName !
PFS.Error => { InvalidFile[error.explanation]; GOTO Failed }];
};
curViewer#
NIL => {
-- Print a viewer
XTSetter.PrintViewer[tool, curViewer];
};
ENDCASE => {
-- No way to print anything
IO.PutRope[tool.feedBack, "Nothing to print has been selected!\n"];
};
EXITS Failed => NULL;
};
OptionsButton: Menus.MenuProc ~ {
Toggle visibility of options panel
tool: Tool = NARROW[clientData];
IF tool.optionsViewer=NIL OR tool.optionsViewer.destroyed THEN CreateOptionsViewer[tool]
ELSE DestroyOptionsViewer[tool];
};
ServerStatusButton: Menus.MenuProc ~ {
Toggle visibility of server status panel
tool: Tool = NARROW[clientData];
IF tool.printerStatusViewer=NIL OR tool.printerStatusViewer.destroyed THEN CreateServerStatusViewer[tool]
ELSE DestroyServerStatusViewer[tool];
};
Print request status viewers management
CreateStatusReport:
PUBLIC
ENTRY
PROC [tool: Tool, title:
ROPE]
RETURNS [state: StatusReport] ~ {
Create a status report handle and the associated viewer. Recomputes screen positions for comfort.
ENABLE UNWIND => NULL;
x: INT ¬ 2; -- current position to insert a new viewer inside status viewer
yOffset: INT = 2; -- offset from viewer top
state ¬ NEW [StatusReportRep ¬ [tool: tool]];
IF tool.requestStatusContainer=
NIL
OR tool.requestStatusContainer.destroyed
THEN {
-- Create the container that includes all status reports
tool.requestStatusContainer ¬ Containers.Create[
info: [parent: tool.viewer, scrollable: TRUE, ww: 9999, wh: 0, border: FALSE],
paint: FALSE];
tool.requestStatusRule ¬ Rules.Create[info: [parent: tool.viewer, ww: 9999, wh: 1], paint: FALSE];
};
Create the status viewer itself
state.viewer ¬ Containers.Create[
info: [parent: tool.requestStatusContainer, scrollable: FALSE, wx: 0, wy: 0, ww: 9999, wh: 24, border: TRUE],
paint: FALSE];
state.info ¬ Labels.Create[
info:[parent: state.viewer, wx: x, wy: yOffset, ww: 0, wh: 16, scrollable: FALSE, border: FALSE],
paint: FALSE]; -- pie/stop and server status are not used simultaneously
state.pie ¬ PieViewers.Create[parent: state.viewer, x: x, y: yOffset, diameter: 16, total: 100.0];
PieViewers.Set[state.pie, 100.0];
x ¬ x + state.pie.ww + 4;
state.stop ¬ Buttons.Create[
info: [parent: state.viewer, name: "STOP", wx: x, wy: yOffset, scrollable: FALSE, border: TRUE],
proc: StopButtonProc, clientData: state,
fork: FALSE,
guarded: TRUE,
paint: FALSE];
state.comment ¬ Labels.Create[
info:[parent: state.viewer, name: title, wx: 70, wy: yOffset, scrollable: FALSE, border: FALSE],
paint: FALSE];
};
ResizeStatusContainer:
PUBLIC
PROC [tool: Tool] ~ {
Reposition all the status elements in the container passed
It's assumed that the viewer is not properly painted at this moment
yOffset: INT = 2; -- separate status reports by that amount
Resize:
PROC [] ~ {
-- protected by a viewer lock
curY: INT ¬ 0;
FOR v: Viewer ¬ tool.requestStatusContainer.child, v.sibling
UNTIL v=
NIL
DO
IF v.destroyed THEN LOOP;
curY ¬ curY+yOffset;
ViewerOps.MoveViewer[viewer: v, x: v.wx, y: curY, w: v.ww, h: v.wh, paint: FALSE];
ViewerOps.EstablishViewerPosition[viewer: v, x: v.wx, y: curY, w: v.ww, h: v.wh];
curY ¬ curY+v.wh;
ENDLOOP;
IF curY#0
THEN {
-- the container is not empty
PlaceChild[viewer: tool.requestStatusContainer, rank: statusContainer, minSize: 0, maxSize: curY];
PlaceChild[viewer: tool.requestStatusRule, rank: statusRule, minSize: 1, maxSize: 1];
}
ELSE {
ViewerOps.DestroyViewer[viewer: tool.requestStatusContainer, paint: FALSE];
tool.requestStatusContainer ¬ NIL;
ViewerOps.DestroyViewer[viewer: tool.requestStatusRule, paint: FALSE];
tool.requestStatusRule ¬ NIL;
};
IF NOT RedisplayTool[tool] THEN ViewerOps.PaintViewer[tool.requestStatusContainer, client];
};
IF tool.viewer=NIL OR tool.viewer.destroyed THEN RETURN; -- don't bother...
ViewerLocks.CallUnderWriteLock[Resize, tool.viewer];
StopButtonProc: Buttons.ButtonProc ~ {
Just set the stop flag in the state, clients will look it up
state: StatusReport = NARROW [clientData];
state.stopRequired ¬ TRUE;
Buttons.SetDisplayStyle[state.stop, $BlackOnGrey];
};
SetSendingStatus:
PUBLIC
PROC [state: StatusReport] ~ {
Kill pie & stop viewers, readjust info width, set it to Sending.
w: INT = state.comment.wx-state.info.wx-4; -- Hum Hum ...
ViewerOps.DestroyViewer[state.pie]; state.pie ¬ NIL;
ViewerOps.DestroyViewer[state.stop]; state.stop ¬ NIL;
ViewerOps.MoveViewer[viewer: state.info, x: state.info.wx, y: state.info.wy, w: w, h: state.info.wh];
Labels.Set[label: state.info, value: "Sending"];
};
PrintStatusUpdate:
PUBLIC XNSPrint.StatusChangedProc ~ {
Modify the current print status reported by the print server
state: StatusReport ¬ NARROW [clientData];
notExplicit: BOOL ¬ Rope.IsEmpty[request.lastStatus.statusMessage]; -- no explicit message
msg: ROPE ¬ PrintingAux.ExposeInterpressMasterStatus[request.lastStatus.status, 0];
IF state.viewer=NIL OR state.viewer.destroyed THEN RETURN;
IF ~notExplicit THEN IO.PutF[state.tool.feedBack, "Status of %g: %g -> %g\n", IO.rope[request.context.printObjectName], IO.rope[msg], IO.rope[request.lastStatus.statusMessage]];
SELECT request.lastStatus.status
FROM
pending, inProgress, held => Labels.Set[label: state.info, value: msg];
completed
, completedWithWarning
, rejected
, aborted
, canceled, unknown => {
-- Destroy status report and indicate in log
IF notExplicit THEN IO.PutF[state.tool.feedBack, "%g: %g\n", IO.rope[request.context.printObjectName], IO.rope[msg]];
XNSPrint.UnRegisterPrintRequest[request];
ViewerOps.DestroyViewer[viewer: state.viewer, paint: FALSE];
ResizeStatusContainer[state.tool];
request.context ¬ NIL; -- Avoid circularities
state.tool ¬ NIL;
};
ENDCASE => ERROR; -- not supported status value, call maintenance...
};
Viewer class
The XTSetter viewer class offers dynamic repositioning and sizing of subviewers. All subviewers must have a property set inidcating their display rank, minimum size, maximum useful size. Inifinitely stretchable children may also be used.
XTSetterPositionInfo: TYPE ~ REF XTSetterPositionInfoRep;
XTSetterPositionInfoRep:
TYPE ~
RECORD [
minSize: INT, -- minimum size for viewer
maxSize: INT, -- maximum useful size for viewer, <0 if infinitely extensible
rank: INT -- the display order in the window
];
PlaceChild:
PROC [viewer: Viewer, rank: ViewerRanking, minSize, maxSize:
INT] ~ {
ViewerOps.AddProp[viewer, $XTSViewerPositionInfo, NEW[XTSetterPositionInfoRep ¬
[minSize: minSize, maxSize: maxSize, rank: rank.ORD]]];
};
XTSViewerAdjust: ViewerClasses.AdjustProc ~ {
Element: TYPE ~ REF ElementRep;
ElementRep:
TYPE ~
RECORD [
v: Viewer ¬ NIL, -- this position element is for this viewer
rank: INT ¬ -1, -- at which viewer should be displayed, should be >=0
height: INT ¬ 0, -- computed
delta: INT ¬ 0, -- extensible by that much more, <0 means infinitely
next: Element ¬ NIL -- chain to next in list
];
head: Element ¬ NEW [ElementRep]; -- dummy head element, rank = -1
free: INT ¬ self.ch; -- how much free space is available in the client space
sumDelta: INT ¬ 0; -- how much the limited extension could be
infinite: INT ¬ 0; -- number of viewers with infinite stretch
curY: INT ¬ 0; -- current y position when relocating children
step: INT = 2; -- Free space between sub-viewers
FOR v: Viewer ¬ self.child, v.sibling
UNTIL v=
NIL
DO
-- Sort by rank
IF v.destroyed THEN adjusted ¬ TRUE
ELSE
WITH ViewerOps.FetchProp[v, $XTSViewerPositionInfo]
SELECT
FROM
positionInfo: XTSetterPositionInfo => {
current: Element ¬
NEW [ElementRep ¬ [
v: v,
rank: positionInfo.rank,
delta: positionInfo.maxSize - positionInfo.minSize,
height: positionInfo.minSize
]];
free ¬ free - (current.height + step);
IF current.delta>0 THEN sumDelta ¬ sumDelta+current.delta;
IF current.delta<0 THEN infinite ¬ infinite+1;
FOR e: Element ¬ head, e.next
WHILE e#
NIL
DO
-- Insert in list according to rank
IF e.next=
NIL
OR e.next.rank>current.rank
THEN {
current.next ¬ e.next;
e.next ¬ current;
EXIT;
};
ENDLOOP;
};
ENDCASE => NULL; -- All children must have the position property...
ENDLOOP;
IF free>0
THEN {
-- there is still some free space to allocate
total: INT = MIN [free, sumDelta]; -- total stretch to allocate for finite-stretch viewers
FOR e: Element ¬ head.next, e.next
WHILE e#
NIL
DO
-- allocate to finite-stretch viewers
add: INT = IF e.delta>0 THEN (total*e.delta)/sumDelta ELSE 0;
e.height ¬ e.height+add;
free ¬ free-add;
ENDLOOP;
IF free>0
AND infinite#0
THEN {
-- allocate remainder to infinite-stretch viewers
add: INT = free/infinite;
FOR e: Element ¬ head.next, e.next
WHILE e#
NIL
DO
IF e.delta<0 THEN e.height ¬ e.height+add;
ENDLOOP;
};
};
FOR e: Element ¬ head.next, e.next
WHILE e#
NIL
DO
-- relocate all subviewers
ww: INT = MAX [5, self.cw - e.v.wx];
IF e.v.wy#curY
OR e.v.wh#e.height
OR e.v.ww#ww
THEN {
adjusted ¬ TRUE;
ViewerOps.EstablishViewerPosition[viewer: e.v, x: e.v.wx, y: curY, w: ww, h: e.height];
};
curY ¬ curY+e.height +step;
ENDLOOP;
};
RedisplayTool:
PROC [tool: Tool]
RETURNS [painted:
BOOL ¬
FALSE] ~ {
DoIt:
PROC [] ~ {
-- Under viewer lock
painted ¬ XTSViewerAdjust[tool.viewer];
IF painted THEN ViewerOps.PaintViewer[tool.viewer, client];
};
IF tool.viewer=NIL OR tool.viewer.destroyed THEN RETURN[painted: TRUE]; -- don't bother...
ViewerLocks.CallUnderWriteLock[DoIt, tool.viewer];
};
Initialization
NewProfile: UserProfile.ProfileChangedProc ~ {
DefaultOptionsFromProfile[];
};
NewUser: XNSCredentials.XNSCredentialsChangeProc ~ {
DefaultOptionsFromProfile[];
};
Init:
PROC [] ~ {
Define:
PROC [type:
ROPE, version: IPMaster.Version, mayCompress:
BOOL, shouldCompress:
BOOL, hasPressFonts:
BOOL] ~ {
[] ¬ SymTab.Store[ropeToVersion, type, NEW [PrinterVersionRep ¬ [version, mayCompress, mayCompress AND shouldCompress, hasPressFonts]]];
};
xTSViewerIcon ¬ Icons.NewIconFromFile["XTSetter.icons", 0];
xTSViewerClass ¬
NEW[ViewerClasses.ViewerClassRec ¬[
adjust: XTSViewerAdjust,
icon: xTSViewerIcon,
topDownCoordSys: TRUE]];
ViewerOps.RegisterViewerClass[$XTSetter, xTSViewerClass];
ropeToVersion ¬ SymTab.Create[case: FALSE];
Define[" DEFAULT ", [3, 0], TRUE, FALSE, TRUE]; -- default for undeclared rope versions
Define["3.0-PGS", [3, 0], TRUE, FALSE, TRUE]; -- Quoth
Define["3.0-CS", [3, 0], TRUE, TRUE, FALSE]; -- Zipper
Define["2.1-RLS", [2, 1], FALSE, FALSE, FALSE]; -- Perfector
Define["1.0", [1, 0], FALSE, FALSE, FALSE]; -- Scripto
DefinePrinterVersions[standardVersions];
UserProfile.CallWhenProfileChanges[NewProfile];
XNSCredentials.RegisterForChange[NewUser];
Commander.Register[key: "XTSetter", proc: XTSetterCommand, doc: cmdDoc];
};