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;
XTSetterImpl: CEDAR MONITOR LOCKS tool USING tool: Tool
IMPORTS Args, Buttons, ChoiceButtons, Commander, Containers, Convert, FileDWIM, Icons, IO, Labels, Menus, PFS, PieViewers, PrintingP4V3Aux, Process, Rope, RopeList, Rules, SymTab, TypeScript, UserProfile, ViewerIO, ViewerLocks, ViewerOps, ViewerTools, VFonts, WindowManager, XNSCredentials, XNSPrint, XTSetter
EXPORTS XTSetter, XTSetterPrivate
~ BEGIN
OPEN XTSetterPrivate, PrintingAux: PrintingP4V3Aux;
ToolRep: PUBLIC TYPE ~ XTSetterPrivate.ToolRep;
ViewerRanking: TYPE ~ {serverStatus, options, statusContainer, statusRule, typeScript};
Global static variables
xTSViewerIcon: Icons.IconFlavor; -- XTSetter icon
xTSViewerClass: ViewerClasses.ViewerClass; -- XTSetter viewer class
defaultOptions: Options; -- default set of options, not including those derived from default XNSPrint context
styleList: LIST OF ROPE; -- list of styles to be suggested to the user
deviceList: LIST OF ROPE; -- list of devices to be suggested to the user
printerVersions: SymTab.Ref; -- printer name -> version
ropeToVersion: SymTab.Ref; -- maps 3.0-PGS to version
standardVersions: LIST OF ROPE ¬ LIST [ -- versions for usual printers
"Quoth", "3.0-PGS",
"Quill", "3.0-PGS",
"TimsPrinter", "3.0-PGS",
"Zipper", "3.0-CS",
"Perfector", "2.1-RLS",
"Scripto", "1.0",
"Papermate", "1.0"];
Tool initialization and access
Error: PUBLIC ERROR [why: ROPE] = CODE;
GetTool: PUBLIC PROC [printer: ROPE ¬ NIL, new: BOOL ¬ FALSE] RETURNS [tool: Tool ¬ NIL] ~ {
Return a tool for the required printer, force subviewers if required. A tool may be found among existing viewers. printer default to profile entry.
LookUpTool: ViewerOps.EnumProc ~ {
IF v.class#xTSViewerClass OR v.destroyed OR ~Rope.Equal[v.name, printer] THEN
RETURN [TRUE];
tool ¬ NARROW [v.data];
RETURN [FALSE];
};
IF Rope.IsEmpty[printer] THEN printer ¬ XNSPrint.GetDefaults[].printerName;
IF ~new THEN ViewerOps.EnumerateViewers[LookUpTool]; -- search a candidate tool
IF tool=NIL THEN tool ¬ NewTool[printer, ViewerIO.CreateMessageWindowStream[]];
};
NewTool: PROC [printer: ROPE, feedBack: STREAM] RETURNS [tool: Tool] ~ {
Create a new tool for the specified server. feedBack is used to log errors.
The viewer is structured as follows:
- a menu line
- an options viewer (Not initially created, added later on by clicking the options cmd)
- a status container containing one status viewer per pending operation (Created when needed)
- a typescript for user feedback and error messages
ENABLE XNSPrint.Error => {
feedBack.PutF["Unable to connect to printer %g: %g\n", IO.rope[printer], IO.rope[explanation]];
IO.Flush[feedBack];
ERROR Error[why: explanation];
};
tool ¬ NEW [ToolRep ¬ [printerName: printer]];
tool.printerProperties ¬ XNSPrint.GetPrinterProperties[tool.printerName].answer;
SetupPrinterVersion[tool];
tool.currentOptions ¬ DefaultOptions[];
tool.currentOptions.compress ¬ tool.version.shouldCompress;
CreateViewer[tool]; -- Create all the viewer structure and display it
tool.feedBack.PutF1["Connection with %g established.\n", IO.rope[tool.printerName]];
};
CreateViewer: PROC [tool: Tool] ~ {
Create a viewer for the tool based on the current info data structure
The viewer is structured as follows:
- a menu line
- an options viewer (Not initially created, added later on by clicking the options cmd)
- a status container containing one status viewer per pending operation (Created when needed)
- a typescript for user feedback and error messages
AddMenuEntry: PROCEDURE [name: ROPE,
proc: Menus.MenuProc, fork: BOOL ¬ FALSE, row: INTEGER ¬ 0,
guardDoc: ROPE ¬ NIL] = {
Add a new button in the menu line(s).
menuEntry: Menus.MenuEntry = Menus.CreateEntry[name: name, proc: proc, clientData: tool, documentation: guardDoc, fork: fork, guarded: guardDoc#NIL];
Menus.AppendMenuEntry[menu, menuEntry, row];
};
menu: Menus.Menu = Menus.CreateMenu[];
typeScript: Viewer;
Create the basic viewer
tool.viewer ¬ ViewerOps.CreateViewer[flavor: $XTSetter,
info: [name: tool.printerName, label: GetLabel[tool], column: right, scrollable: FALSE, data: tool],
paint: FALSE -- We'll paint it after it has been built
];
Create the menu
AddMenuEntry[name: "New", proc: NewButton, fork: TRUE];
AddMenuEntry[name: "<--", proc: LeftScreenButton];
AddMenuEntry[name: "Screen", proc: ScreenButton];
AddMenuEntry[name: "-->", proc: RightScreenButton];
AddMenuEntry[name: "Color", proc: ColorScreenButton];
AddMenuEntry[name: "Print", proc: PrintButton, fork: TRUE];
AddMenuEntry[name: "Options", proc: OptionsButton, fork: TRUE];
AddMenuEntry[name: "ServerStatus", proc: ServerStatusButton, fork: TRUE];
ViewerOps.SetMenu[tool.viewer, menu];
Create the typescript area
typeScript ¬ TypeScript.Create[
info: [parent: tool.viewer, wx: 0, wy: 0, ww: 9999, wh: 0, border: FALSE],
paint: FALSE];
ViewerTools.InhibitUserEdits[typeScript];
PlaceChild[viewer: typeScript, rank: typeScript, minSize: 30, maxSize: -1];
tool.feedBack ¬ ViewerIO.CreateViewerStreams[name: NIL, viewer: typeScript].out;
Display the viewer properly
ViewerOps.PaintViewer[tool.viewer, all];
};
GetLabel: PROC [tool: Tool] RETURNS [label: ROPE] ~ {
name: ROPE ¬ tool.printerName;
RETURN[ name.Substr[0, name.Index[s2:":"]] ];
};
DestroyTool: PUBLIC ENTRY PROC [tool: Tool] ~ {
ENABLE UNWIND => NULL;
ViewerOps.DestroyViewer[tool.viewer];
};
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];
};
Options menu management
Types, constants and utilities
Pos: TYPE ~ RECORD [x, y: INT];
entryHSpace: INT ¬ 6;
entryVSpace: INT ¬ 16;
textFuzz: INT ¬ 1;
Boolean buttons
Boolean buttons control directly a boolean variable.
CreateBoolButton: PROC [name: ROPE, parent: Viewer, pos: Pos, init, possible: BOOL, click: BoolButtonActionProc ¬ NIL] RETURNS [state: BoolButton, newx: INT] ~ {
Create a button that controls a boolean variable and is initially located at pos in parent
Init is the initial value. If possible is false, the true state is displayed as BlackOnGrey instead of the expected WhiteOnBlack
state ¬ NEW [BoolButtonRep ¬ [NIL, FALSE, IF possible THEN $WhiteOnBlack ELSE $BlackOnGrey, click]];
state.button¬ Buttons.Create[
info: [name: name, wx: pos.x, wy: pos.y, parent: parent, border: TRUE],
proc: ClickBoolButton,
fork: FALSE,
paint: FALSE,
clientData: state
];
newx ¬ pos.x + state.button.ww + entryHSpace;
SetBoolButton[state, init AND possible];
};
ClickBoolButton: Buttons.ButtonProc ~ {
Toggle a boolean-valued button
state: BoolButton = NARROW [clientData];
SetBoolButton[state, NOT state.flag];
IF state.click#NIL THEN state.click[state];
};
GetBoolButton: PROC [state: BoolButton] RETURNS [BOOL] ~ {
Return the current value of a boolean button
RETURN [state.flag];
};
SetBoolButton: PROC [state: BoolButton, value: BOOL]~ {
Set the current value of a boolean button
state.flag ¬ value;
Buttons.SetDisplayStyle[state.button, IF value THEN state.trueStyle ELSE $BlackOnWhite];
};
Integer buttons
Integer buttons control an integer value that may be absent (defaulted by the client when getting them)
CreateIntButton: PROC [name: ROPE, parent: Viewer, pos: Pos, w: INT ¬ 9999, default: INT, defined: BOOL ¬ TRUE, init: INT ¬ 0] RETURNS [state: IntButton, newx: INT] ~ {
Create a button with integer values. Initial state is empty if defined is FALSE, else it gets the init value.
state ¬ NEW [IntButtonRep ¬ [default: default]];
state.button ¬ Buttons.Create[
info: [name: name, wx: pos.x, wy: pos.y, parent: parent, border: FALSE],
proc: ClickIntButton,
fork: FALSE,
clientData: state,
paint: FALSE
];
pos ¬ [pos.x + state.button.ww, pos.y+textFuzz]; -- adjust text viewer to button!!!
state.text ¬ ViewerTools.MakeNewTextViewer[
info: [parent: parent, wx: pos.x, wy: pos.y, ww: w, wh: state.button.wh, border: FALSE],
paint: FALSE];
newx ¬ pos.x + w;
SetIntButton[state, defined, init];
};
GetIntButton: PROC [state: IntButton] RETURNS [val: INT] ~ {
Return the current state of an integer-valued button
val ¬ state.default;
val ¬ Convert.IntFromRope[ViewerTools.GetContents[state.text] !
Convert.Error => CONTINUE];
};
SetIntButton: PROC [state: IntButton, defined: BOOL ¬ TRUE, init: INT ¬ 0] ~ {
Set the value of an integer-valued button
val: ROPE ¬ NIL;
IF defined AND init#state.default THEN val ¬ Convert.RopeFromInt[init];
ViewerTools.SetContents[state.text, val];
};
ClickIntButton: Buttons.ButtonProc ~ {
Click an integer-valued button: make it pending delete
state: IntButton = NARROW [clientData];
ViewerTools.SetSelection[state.text];
};
Real buttons
Real buttons control a real value that may be absent (defaulted by the client when getting them)
CreateRealButton: PROC [name: ROPE, parent: Viewer, pos: Pos, w: INT ¬ 9999, default: REAL, defined: BOOL ¬ TRUE, init: REAL ¬ 0.0] RETURNS [state: RealButton, newx: INT] ~ {
Create a button with real values. Initial state is empty if defined is FALSE, else it gets the init value.
state ¬ NEW [RealButtonRep ¬ [default: default]];
state.button ¬ Buttons.Create[
info: [name: name, wx: pos.x, wy: pos.y, parent: parent, border: FALSE],
proc: ClickRealButton,
fork: FALSE,
clientData: state,
paint: FALSE
];
pos ¬ [pos.x + state.button.ww, pos.y+textFuzz]; -- adjust text viewer to button!!!
state.text ¬ ViewerTools.MakeNewTextViewer[
info: [parent: parent, wx: pos.x, wy: pos.y, ww: w, wh: state.button.wh, border: FALSE],
paint: FALSE];
newx ¬ pos.x + w;
SetRealButton[state, defined, init];
};
GetRealButton: PROC [state: RealButton] RETURNS [val: REAL] ~ {
Return the current state of a real-valued button
val ¬ state.default;
val ¬ Convert.RealFromRope[ViewerTools.GetContents[state.text] !
Convert.Error => CONTINUE];
};
SetRealButton: PROC [state: RealButton, defined: BOOL ¬ TRUE, init: REAL ¬ 0] ~ {
Set the value of a real-valued button
val: ROPE ¬ NIL;
IF defined AND init#state.default THEN val ¬ Convert.RopeFromReal[init];
ViewerTools.SetContents[state.text, val];
};
ClickRealButton: Buttons.ButtonProc ~ {
Click a real-valued button: make it pending delete
state: RealButton = NARROW [clientData];
ViewerTools.SetSelection[state.text];
};
Rope buttons
Rope buttons control a rope with a set of standard default values
CreateRopeButton: PROC [name: ROPE, parent: Viewer, pos: Pos, w: INT ¬ 9999, values: LIST OF ROPE ¬ NIL, init: ROPE ¬ NIL] RETURNS [state: RopeButton, newx: INT] ~ {
Create a button with rope values
state ¬ NEW [RopeButtonRep ¬ [defaults: values]];
state.button ¬ Buttons.Create[
info: [name: name, wx: pos.x, wy: pos.y, parent: parent, border: FALSE],
proc: ClickRopeButton,
fork: FALSE,
clientData: state,
paint: FALSE
];
pos ¬ [pos.x + state.button.ww, pos.y+textFuzz]; -- adjust text viewer to button!!!
state.text ¬ ViewerTools.MakeNewTextViewer[
info: [parent: parent, wx: pos.x, wy: pos.y, ww: w, wh: state.button.wh, border: FALSE],
paint: FALSE];
newx ¬ pos.x + w + entryHSpace;
SetRopeButton[state, init];
};
GetRopeButton: PROC [state: RopeButton] RETURNS [ROPE] ~ {
Return the current state of a rope-valued button
RETURN [ViewerTools.GetContents[state.text]];
};
SetRopeButton: PROC [state: RopeButton, val: ROPE] ~ {
Set the value of a rope-valued button
ViewerTools.SetContents[state.text, val];
};
ClickRopeButton: Buttons.ButtonProc ~ {
Click a rope-valued button: progress in list or make it pending-delete
NextName: PROC [state: RopeButton] RETURNS [next: ROPE ¬ NIL] = {
current: ROPE = ViewerTools.GetContents[state.text];
IF state.defaults = NIL THEN RETURN;
FOR l: LIST OF ROPE ¬ state.defaults, l.rest UNTIL l = NIL DO
IF current.Equal[l.first, FALSE] THEN
RETURN[IF l.rest = NIL THEN state.defaults.first ELSE l.rest.first];
ENDLOOP;
RETURN[state.defaults.first]
};
state: RopeButton = NARROW [clientData];
IF mouseButton = blue THEN ViewerTools.SetSelection[state.text]
ELSE ViewerTools.SetContents[state.text, NextName[state]];
};
Options handling
ClickCompress: BoolButtonActionProc ~ {
tool: Tool ¬ NARROW [state.button.parent.parent.data];
};
CreateOptionsViewer: PUBLIC ENTRY PROC [tool: Tool] ~ {
Create a new subviewer for options management. All initial values taken from current tool options.
ENABLE UNWIND => NULL;
curPos: Pos;
em: INT = VFonts.CharWidth['m];
options: Options ¬ tool.currentOptions;
paperButtons: LIST OF ROPE ¬ NIL; -- the list of buttons to select paper
defaultPaper: ROPE; -- the default thereof
IF ~NoViewer[tool.optionsViewer] THEN RETURN; -- Just being paranoid
tool.optionsViewer ¬ Containers.Create[
info: [parent: tool.viewer, scrollable: FALSE, wx: 0, wy: 0, ww: 9999, wh: 0, border: FALSE],
paint: FALSE];
First line of panel: Compress, keepIP, scale and style selection
curPos ¬ [entryHSpace, 4];
IF tool.version.mayCompress THEN
[tool.optionsCompress, curPos.x] ¬ CreateBoolButton[name: "Compress", parent: tool.optionsViewer, pos: curPos, init: options.compress, possible: TRUE, click: ClickCompress];
[tool.optionsKeepIP, curPos.x] ¬ CreateBoolButton[name: "KeepIP", parent: tool.optionsViewer, pos: curPos, init: options.keepIP, possible: TRUE];
[tool.optionsScale, curPos.x] ¬ CreateRealButton[name: "Scale:", parent: tool.optionsViewer, pos: curPos, w: 6*em, default: 1.0, init: options.tiogaScale];
[tool.optionsStyle, curPos.x] ¬ CreateRopeButton[name: "Style:", parent: tool.optionsViewer, pos: curPos, values: styleList, init: options.tiogaStyle];
Second line of panel: Flags, numbers
curPos ¬ [entryHSpace, curPos.y + entryVSpace];
[tool.optionsStaple, curPos.x] ¬ CreateBoolButton[name: "Staple", parent: tool.optionsViewer, pos: curPos, init: options.stapled, possible: tool.printerProperties.staple];
[tool.optionsTwoSided, curPos.x] ¬ CreateBoolButton[name: "2-sided", parent: tool.optionsViewer, pos: curPos, init: options.twoSided, possible: tool.printerProperties.twoSided];
[tool.optionsCopies, curPos.x] ¬ CreateIntButton[name: "Copies:", parent: tool.optionsViewer, pos: curPos, w: 4*em, default: 1, init: options.copyCount];
[tool.optionsFirstpage, curPos.x] ¬ CreateIntButton[name: "Page:", parent: tool.optionsViewer, pos: curPos, w: 7*em, default: 1, init: options.pageFirst];
[tool.optionsLastpage, curPos.x] ¬ CreateIntButton[name: "To:", parent: tool.optionsViewer, pos: curPos, w: 7*em, default: LAST [CARD16], init: options.pageLast];
Third line of panel: Paper selection
curPos ¬ [entryHSpace, curPos.y + entryVSpace];
FOR mList: LIST OF ROPE ¬ tool.printerProperties.media, mList.rest WHILE mList#NIL DO
IF Rope.Equal[mList.first, options.mediumHint] THEN defaultPaper ¬ mList.first;
IF NOT RopeList.Memb[paperButtons, mList.first] THEN
paperButtons ¬ CONS [mList.first, paperButtons];
ENDLOOP;
paperButtons ¬ RopeList.Reverse[paperButtons];
IF Rope.IsEmpty[defaultPaper] THEN defaultPaper ¬ paperButtons.first; -- valid default
tool.optionsPaper ¬ ChoiceButtons.BuildEnumTypeSelection[
title: "Paper:", style: menuSelection, viewer: tool.optionsViewer,
x: curPos.x, y: curPos.y,
buttonNames: paperButtons, default: defaultPaper];
Third line of panel: Paper selection
curPos ¬ [entryHSpace, curPos.y + entryVSpace];
[tool.optionsDevice, curPos.x] ¬ CreateRopeButton[name: "Device:", parent: tool.optionsViewer, pos: curPos, values: deviceList, init: options.device];
Terminating rule of options panel and adjust the viewer height correctly
[] ¬ Rules.Create[
info: [parent: tool.optionsViewer, wx: 0, wy: curPos.y+entryVSpace, ww: 9999, wh: 1],
paint: FALSE];
PlaceChild[viewer: tool.optionsViewer, rank: options, minSize: curPos.y+entryVSpace+1, maxSize: curPos.y+entryVSpace+1];
Repaint everything
[] ¬ RedisplayTool[tool];
};
DestroyOptionsViewer: PUBLIC ENTRY PROC [tool: Tool] ~ {
Returns current options from tool.
ENABLE UNWIND => NULL;
[] ¬ GetOptionsInternal[tool, FALSE];
ViewerOps.DestroyViewer[viewer: tool.optionsViewer, paint: FALSE];
tool.optionsViewer ¬ NIL;
[] ¬ RedisplayTool[tool];
};
GetOptions: PUBLIC ENTRY PROC [tool: Tool, reset: BOOL ¬ TRUE] RETURNS [options: Options] ~ {
Returns current options from tool.
ENABLE UNWIND => NULL;
options ¬ GetOptionsInternal[tool, reset];
};
GetOptionsInternal: PUBLIC PROC [tool: Tool, reset: BOOL ¬ TRUE] RETURNS [options: Options] ~ {
Returns current options from tool.
IF ~NoViewer[tool.optionsViewer] THEN { -- Actualize options from viewer if present
tool.currentOptions.compress ¬ IF tool.optionsCompress#NIL THEN GetBoolButton[state: tool.optionsCompress] ELSE FALSE;
tool.currentOptions.keepIP ¬ GetBoolButton[state: tool.optionsKeepIP];
tool.currentOptions.copyCount ¬ GetIntButton[state: tool.optionsCopies];
tool.currentOptions.pageFirst ¬ GetIntButton[state: tool.optionsFirstpage];
tool.currentOptions.pageLast ¬ GetIntButton[state: tool.optionsLastpage];
tool.currentOptions.mediumHint ¬ ChoiceButtons.GetSelectedButton[tool.optionsPaper];
tool.currentOptions.stapled ¬ GetBoolButton[state: tool.optionsStaple];
tool.currentOptions.twoSided ¬ GetBoolButton[state: tool.optionsTwoSided];
tool.currentOptions.telephone ¬ NIL; -- Not yet defined
tool.currentOptions.tiogaStyle ¬ GetRopeButton[state: tool.optionsStyle];
tool.currentOptions.device ¬ GetRopeButton[state: tool.optionsDevice];
tool.currentOptions.tiogaScale ¬ GetRealButton[state: tool.optionsScale];
IF reset THEN { -- Reset volatile options in viewer to their defaults
SetIntButton[state: tool.optionsCopies, defined: FALSE];
SetIntButton[state: tool.optionsFirstpage, defined: FALSE];
SetIntButton[state: tool.optionsLastpage, defined: FALSE];
SetRealButton[state: tool.optionsScale, defined: FALSE];
};
};
options ¬ NEW [OptionsRep ¬ tool.currentOptions­]; -- return copy of tool options
};
SetOptions: PUBLIC ENTRY PROC [tool: Tool, options: Options] ~ {
Sets all the tool options
ENABLE UNWIND => NULL;
tool.currentOptions­ ¬ options­; -- copy all options into tool
IF ~NoViewer[tool.optionsViewer] THEN { -- Actualize options to viewer if present
IF tool.optionsCompress#NIL THEN {
SetBoolButton[state: tool.optionsCompress, value: options.compress];
ClickCompress[tool.optionsCompress];
};
SetBoolButton[state: tool.optionsKeepIP, value: options.keepIP];
SetBoolButton[state: tool.optionsStaple, value: options.stapled];
SetBoolButton[state: tool.optionsTwoSided, value: options.twoSided];
SetIntButton[state: tool.optionsCopies, init: options.copyCount];
SetIntButton[state: tool.optionsFirstpage, init: options.pageFirst];
SetIntButton[state: tool.optionsLastpage, init: options.pageLast];
ChoiceButtons.UpdateChoiceButtons[viewer: tool.optionsViewer, enumTypeInfo: tool.optionsPaper, newName: options.mediumHint ! ChoiceButtons.ChoiceDoesntExist => CONTINUE];
Need to do something about telephone here
SetRopeButton[state: tool.optionsStyle, val: options.tiogaStyle];
SetRealButton[state: tool.optionsScale, init: options.tiogaScale];
SetRopeButton[state: tool.optionsDevice, val: options.device];
};
};
DefaultOptions: PUBLIC PROC [] RETURNS [options: Options] ~ {
Returns a reasonable set of default options
context: XNSPrint.Context ¬ XNSPrint.GetDefaults[];
options ¬ NEW [OptionsRep ¬ defaultOptions­]; -- maintained through user profile
options.mediumHint ¬ context.mediumHint;
options.stapled ¬ context.stapled;
options.twoSided ¬ context.twoSided;
options.telephone ¬ context.telephone;
};
DefaultOptionsFromProfile: PROC [] ~ {
Initialize profile-derived entries in defaultOptions. Entries derived from context are not setup here because the order of Profile call-backs is not specified!
EachTool: ViewerOps.EnumProc ~ {
tool: Tool;
IF v.class#xTSViewerClass OR v.destroyed THEN RETURN [TRUE];
tool ¬ NARROW [v.data];
SetupPrinterVersion[tool];
};
options: Options ¬ NEW [OptionsRep];
options.keepIP ¬ UserProfile.Boolean["XTSetter.KeepIP", FALSE];
backward compatibility for XTSetter.UsePressFonts user profile option
options.compress ← default from type;
options.copyCount ← default from type;
options.pageFirst ← default from type;
options.pageLast ← default from type;
options.tiogaStyle ¬ UserProfile.Token["XTSetter.Style", NIL];
options.device ¬ UserProfile.Token["XTSetter.Device",
IF UserProfile.Boolean["XTSetter.UsePressFonts", FALSE] THEN "press" ELSE NIL];
options.tiogaScale ← default from type;
defaultOptions ¬ options;
styleList ¬ UserProfile.ListOfTokens["XTSetter.StyleList", LIST ["Slides", "TwoColumnLandscapeCedar", "TwoColumnCedar", "AnacapaCedar", "AnacapaSlides", "BlueAndWhite", "CedarACM", "33UpLabels"]];
deviceList ¬ UserProfile.ListOfTokens["XTSetter.DeviceList", LIST ["press", "xcc", "anacapa"]];
DefinePrinterVersions[standardVersions];
DefinePrinterVersions[UserProfile.ListOfTokens["XTSetter.PrinterVersions", NIL]];
ViewerOps.EnumerateViewers[EachTool]; -- recompute correct IP version for existing tools
};
Print server status viewer management
CreateServerStatusViewer: PUBLIC ENTRY PROC [tool: Tool] ~ {
Create the server status viewer and insert it in the tool.
ENABLE UNWIND => NULL;
NewNamedLabel: PROC [name: ROPE, size: ROPE ¬ NIL, w: INT ¬ 0] RETURNS [v: Viewer] ~ {
Insert 2 labels at posX, posY, 1st carries name, second will carry variable info. Size of 2nd label is set by the display size of size, or w if NIL.
width: INT = IF Rope.IsEmpty[size] THEN w ELSE VFonts.StringWidth[size];
v ¬ Labels.Create[
info:[parent: tool.printerStatusViewer, name: name, wx: posX, wy: posY, border: FALSE],
paint: FALSE];
posX ¬ posX+v.ww+xBase;
v ¬ Labels.Create[
info:[parent: tool.printerStatusViewer, wx: posX, wy: posY, ww: width, border: FALSE],
paint: FALSE];
posX ¬ posX+v.ww+xBase;
};
xBase: INT = 2; -- offset from left edge
yBase: INT = 2; -- offset from viewer top
ySpace: INT = 16; -- vertical space between labels
posX, posY: INT; -- keeps track of various x,y positions
v: Viewer; -- work pointer for viewers that are not kept permanently
tool.printerStatusViewer ¬ Containers.Create[
info: [parent: tool.viewer, scrollable: FALSE, wx: 0, wy: 0, ww: 9999, wh: 0, border: FALSE],
paint: FALSE];
First line of the printer status viewer: Media availability
posX ¬ xBase; posY ¬ yBase;
tool.printerStatusMedia ¬ NewNamedLabel[name: "Paper:", w: 9999];
Second line of the printer status viewer
posX ¬ xBase; posY ¬ posY+ySpace;
tool.printerStatusSpooler ¬ NewNamedLabel[name: "Spooler:", size: "Available "];
tool.printerStatusFormatter ¬ NewNamedLabel[name: "Formatter:", size: "Available "];
tool.printerStatusMarkingEngine ¬ NewNamedLabel[name: "Marking engine:", size: "NeedsKeyOperator "];
Add the terminating rule
posY ¬ posY+ySpace;
v ¬ Rules.Create[
info: [parent: tool.printerStatusViewer, wx: 0, wy: posY, ww: 9999, wh: 1],
paint: FALSE];
Setup sizing and position information
PlaceChild[viewer: tool.printerStatusViewer, rank: serverStatus, minSize: posY+1, maxSize: posY+1];
Repaint tool and start the update process
[] ¬ RedisplayTool[tool];
TRUSTED {Process.Detach[FORK ServerStatusWatcher[tool, 30]]}; -- will compute initial display
};
DestroyServerStatusViewer: PUBLIC ENTRY PROC [tool: Tool] ~ {
Destroy the printer status viewer.
ENABLE UNWIND => NULL;
IF ~NoViewer[tool.printerStatusViewer] THEN ViewerOps.DestroyViewer[viewer: tool.printerStatusViewer, paint: FALSE];
[] ¬ RedisplayTool[tool];
BROADCAST tool.printerStatusTimer; -- will wake up the process and kill it since viewer destroyed
};
ServerStatusWatcher: PROC [tool: Tool, delay: INT] ~ {
This runs as a separate process that maintains the server status window
Repaints the window each time the timer is exhausted/modified. To reduce load on XNS, no queries are made while the viewer is not visible. On the reverse, a redisplay will be forced shortly after the viewer is visible again.
Process ends when the window has been destroyed, either directly by the status button or because the master viewer itself has been destroyed.
SetServerStatusTimer: ENTRY PROC [tool: Tool, value: INT] ~ TRUSTED {
Updates the time-out between server status viewer updates and forces the viewer to be updated immediately.
ENABLE UNWIND => NULL;
Process.SetTimeout[@(tool.printerStatusTimer), Process.SecondsToTicks[value]];
BROADCAST tool.printerStatusTimer;
};
WaitServerStatusTimer: ENTRY PROC [tool: Tool] ~ {
Wait until the timer is exhausted or forced
ENABLE UNWIND => NULL;
WAIT tool.printerStatusTimer;
};
Report: PROC [v: Viewer, new: ROPE] ~ {
Update one of the 3 status reports
Labels.Set[v, new];
SELECT TRUE FROM
Rope.Equal[new, "available", FALSE] => Labels.SetDisplayStyle[v, $BlackOnWhite];
Rope.Equal[new, "busy", FALSE] => Labels.SetDisplayStyle[v, $BlackOnGrey];
ENDCASE => Labels.SetDisplayStyle[v, $WhiteOnBlack];
};
previousStatus: XNSPrint.PrinterStatus;
first: BOOL ¬ TRUE; -- First time thru
previousMedia: ROPE ¬ "?";
UNTIL NoViewer[tool.printerStatusViewer] DO -- Till death do us part ...
IF tool.printerStatusViewer.visible THEN { -- do the display, maybe restore sampling delay
currentStatus: XNSPrint.PrinterStatus;
statusSucceded: BOOL ¬ TRUE;
currentMedia: ROPE ¬ NIL;
SetServerStatusTimer[tool, delay];
currentStatus ¬ XNSPrint.GetPrinterStatus[tool.printerName !
XNSPrint.Error => {tool.feedBack.PutF1["Problem with printer: %g\n", IO.rope[explanation]]; statusSucceded ¬ FALSE; CONTINUE}
].answer;
IF statusSucceded THEN { -- the printer responded
IF first OR previousStatus.spooler#currentStatus.spooler THEN
Report[tool.printerStatusSpooler, PrintingAux.ExposeSpoolerStatus[currentStatus.spooler, 0]];
IF first OR previousStatus.formatter#currentStatus.formatter THEN
Report[tool.printerStatusFormatter, PrintingAux.ExposeFormatterStatus[currentStatus.formatter, 0]];
IF first OR previousStatus.printer#currentStatus.printer THEN
Report[tool.printerStatusMarkingEngine, PrintingAux.ExposeMarkingEngineStatus[currentStatus.printer, 0]];
FOR mList: LIST OF ROPE ¬ currentStatus.media, mList.rest WHILE mList#NIL DO
IF Rope.IsEmpty[currentMedia] THEN currentMedia ¬ mList.first
ELSE currentMedia ¬ Rope.Cat[currentMedia, ",", mList.first];
ENDLOOP;
IF NOT Rope.Equal[currentMedia, previousMedia] THEN Labels.Set[tool.printerStatusMedia, currentMedia];
previousStatus ¬ currentStatus; previousMedia ¬ currentMedia; first ¬ FALSE;
};
}
ELSE { -- don't display anything, but reduce timeout to catch visible again
SetServerStatusTimer[tool, 1];
};
WaitServerStatusTimer[tool];
ENDLOOP;
};
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];
};
Various utilities
NoViewer: PROC [v: Viewer] RETURNS [BOOL] ~ INLINE {
Returns TRUE if the viewer does not exist (i.e. is NIL) or has been destroyed.
RETURN [v=NIL OR v.destroyed]
};
SetupPrinterVersion: PROC [tool: Tool] ~ {
Setup the printer version field of the tool.
pVers: PrinterVersion ¬ NARROW [SymTab.Fetch[printerVersions, tool.printerName].val];
IF pVers=NIL THEN pVers ¬ NARROW [SymTab.Fetch[ropeToVersion, " DEFAULT "].val];
IF pVers=NIL THEN ERROR; -- call support...
tool.version ¬ pVers;
};
DefinePrinterVersions: PROC [versions: LIST OF ROPE] ~ {
IF printerVersions=NIL THEN printerVersions ¬ SymTab.Create[case: FALSE];
FOR l: LIST OF ROPE ¬ versions, l.rest.rest UNTIL l=NIL OR l.rest=NIL DO
name: ROPE = l.first; attribute: ROPE = l.rest.first;
version: PrinterVersion ¬ NARROW [SymTab.Fetch[ropeToVersion, l.rest.first].val];
IF version=NIL THEN
version ¬ NARROW [SymTab.Fetch[ropeToVersion, " DEFAULT "].val];
[] ¬ SymTab.Store[printerVersions, name, version];
ENDLOOP;
};
CommandTool interface
cmdDoc: ROPE = "[<serviceName>]\nCreate a viewer to print things on an XNS based printer.\nArguments:\n <serviceName> Print on that service";
cmdUsage: ROPE = Rope.Concat["Usage: XTSetter ", cmdDoc];
XTSetterCommand: Commander.CommandProc ~ {
PROC [cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
The command tool interface to create a new XTSetter
printer: Args.Arg;
[printer] ¬ Args.ArgsGet[cmd, "[s" ! Args.Error => {msg ¬ reason; CONTINUE}];
IF msg # NIL THEN RETURN[$Failure, cmdUsage];
IF Rope.IsEmpty[printer.rope] THEN printer.rope ¬ XNSPrint.GetDefaults[].printerName;
[] ¬ NewTool[printer.rope, cmd.err ! Error => GOTO failed];
EXITS
failed => RETURN [$Failure];
};
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];
};
Init[];
END.