<<>> <> <> <> <> <<>> <> <> <> <> <> <<>> 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}; <> 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"]; <> Error: PUBLIC ERROR [why: ROPE] = CODE; GetTool: PUBLIC PROC [printer: ROPE ¬ NIL, new: BOOL ¬ FALSE] RETURNS [tool: Tool ¬ NIL] ~ { <> 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] ~ { <> <> <<- 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] ~ { <> <> <<- 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] = { <> 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; <> 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 ]; <> 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]; <> 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; <> 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]; }; <> NewButton: Menus.MenuProc ~ { <> tool: Tool = NARROW[clientData]; printer: ROPE = ViewerTools.GetSelectionContents[]; IF ~Rope.IsEmpty[printer] THEN [] ¬ NewTool[printer, tool.feedBack ! Error => CONTINUE]; }; LeftScreenButton: Menus.MenuProc ~ { <> tool: Tool = NARROW[clientData]; XTSetter.PrintScreen[tool, left]; }; ScreenButton: Menus.MenuProc ~ { <> tool: Tool = NARROW[clientData]; XTSetter.PrintScreen[tool, bw]; }; RightScreenButton: Menus.MenuProc ~ { <> tool: Tool = NARROW[clientData]; XTSetter.PrintScreen[tool, right]; }; ColorScreenButton: Menus.MenuProc ~ { <> 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 ~ { <> <<- 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] ~ { <> 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] ~ { <> ENABLE PFS.Error => GO TO Failed; ctxFN: ROPE; <> 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 ~ { <> tool: Tool = NARROW[clientData]; IF tool.optionsViewer=NIL OR tool.optionsViewer.destroyed THEN CreateOptionsViewer[tool] ELSE DestroyOptionsViewer[tool]; }; ServerStatusButton: Menus.MenuProc ~ { <> tool: Tool = NARROW[clientData]; IF tool.printerStatusViewer=NIL OR tool.printerStatusViewer.destroyed THEN CreateServerStatusViewer[tool] ELSE DestroyServerStatusViewer[tool]; }; <> <> Pos: TYPE ~ RECORD [x, y: INT]; entryHSpace: INT ¬ 6; entryVSpace: INT ¬ 16; textFuzz: INT ¬ 1; <> <> <<>> CreateBoolButton: PROC [name: ROPE, parent: Viewer, pos: Pos, init, possible: BOOL, click: BoolButtonActionProc ¬ NIL] RETURNS [state: BoolButton, newx: INT] ~ { <> <> 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 ~ { <> state: BoolButton = NARROW [clientData]; SetBoolButton[state, NOT state.flag]; IF state.click#NIL THEN state.click[state]; }; GetBoolButton: PROC [state: BoolButton] RETURNS [BOOL] ~ { <> RETURN [state.flag]; }; SetBoolButton: PROC [state: BoolButton, value: BOOL]~ { <> state.flag ¬ value; Buttons.SetDisplayStyle[state.button, IF value THEN state.trueStyle ELSE $BlackOnWhite]; }; <> <> <<>> CreateIntButton: PROC [name: ROPE, parent: Viewer, pos: Pos, w: INT ¬ 9999, default: INT, defined: BOOL ¬ TRUE, init: INT ¬ 0] RETURNS [state: IntButton, newx: INT] ~ { <> 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] ~ { <> val ¬ state.default; val ¬ Convert.IntFromRope[ViewerTools.GetContents[state.text] ! Convert.Error => CONTINUE]; }; SetIntButton: PROC [state: IntButton, defined: BOOL ¬ TRUE, init: INT ¬ 0] ~ { <> val: ROPE ¬ NIL; IF defined AND init#state.default THEN val ¬ Convert.RopeFromInt[init]; ViewerTools.SetContents[state.text, val]; }; ClickIntButton: Buttons.ButtonProc ~ { <> state: IntButton = NARROW [clientData]; ViewerTools.SetSelection[state.text]; }; <> <> <<>> 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] ~ { <> 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] ~ { <> val ¬ state.default; val ¬ Convert.RealFromRope[ViewerTools.GetContents[state.text] ! Convert.Error => CONTINUE]; }; SetRealButton: PROC [state: RealButton, defined: BOOL ¬ TRUE, init: REAL ¬ 0] ~ { <> val: ROPE ¬ NIL; IF defined AND init#state.default THEN val ¬ Convert.RopeFromReal[init]; ViewerTools.SetContents[state.text, val]; }; ClickRealButton: Buttons.ButtonProc ~ { <> state: RealButton = NARROW [clientData]; ViewerTools.SetSelection[state.text]; }; <> <> <<>> CreateRopeButton: PROC [name: ROPE, parent: Viewer, pos: Pos, w: INT ¬ 9999, values: LIST OF ROPE ¬ NIL, init: ROPE ¬ NIL] RETURNS [state: RopeButton, newx: INT] ~ { <> 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 [ViewerTools.GetContents[state.text]]; }; SetRopeButton: PROC [state: RopeButton, val: ROPE] ~ { <> ViewerTools.SetContents[state.text, val]; }; ClickRopeButton: Buttons.ButtonProc ~ { <> 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]]; }; <> ClickCompress: BoolButtonActionProc ~ { tool: Tool ¬ NARROW [state.button.parent.parent.data]; }; CreateOptionsViewer: PUBLIC ENTRY PROC [tool: Tool] ~ { <> 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]; <> 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]; <> 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]; <> 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]; <> curPos ¬ [entryHSpace, curPos.y + entryVSpace]; [tool.optionsDevice, curPos.x] ¬ CreateRopeButton[name: "Device:", parent: tool.optionsViewer, pos: curPos, values: deviceList, init: options.device]; <> [] ¬ 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]; <> [] ¬ RedisplayTool[tool]; }; DestroyOptionsViewer: PUBLIC ENTRY PROC [tool: 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] ~ { <> ENABLE UNWIND => NULL; options ¬ GetOptionsInternal[tool, reset]; }; GetOptionsInternal: PUBLIC PROC [tool: Tool, reset: BOOL ¬ TRUE] RETURNS [options: Options] ~ { <> 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] ~ { <> 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]; <> 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] ~ { <> 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 [] ~ { <> 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]; <> <> <> <> <> options.tiogaStyle ¬ UserProfile.Token["XTSetter.Style", NIL]; options.device ¬ UserProfile.Token["XTSetter.Device", IF UserProfile.Boolean["XTSetter.UsePressFonts", FALSE] THEN "press" ELSE NIL]; <> 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 }; <> CreateServerStatusViewer: PUBLIC ENTRY PROC [tool: Tool] ~ { <> ENABLE UNWIND => NULL; NewNamedLabel: PROC [name: ROPE, size: ROPE ¬ NIL, w: INT ¬ 0] RETURNS [v: Viewer] ~ { <> 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]; <> posX ¬ xBase; posY ¬ yBase; tool.printerStatusMedia ¬ NewNamedLabel[name: "Paper:", w: 9999]; <> 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 "]; <> posY ¬ posY+ySpace; v ¬ Rules.Create[ info: [parent: tool.printerStatusViewer, wx: 0, wy: posY, ww: 9999, wh: 1], paint: FALSE]; <> PlaceChild[viewer: tool.printerStatusViewer, rank: serverStatus, minSize: posY+1, maxSize: posY+1]; <> [] ¬ RedisplayTool[tool]; TRUSTED {Process.Detach[FORK ServerStatusWatcher[tool, 30]]}; -- will compute initial display }; DestroyServerStatusViewer: PUBLIC ENTRY PROC [tool: Tool] ~ { <> 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] ~ { <> <> <> SetServerStatusTimer: ENTRY PROC [tool: Tool, value: INT] ~ TRUSTED { <> ENABLE UNWIND => NULL; Process.SetTimeout[@(tool.printerStatusTimer), Process.SecondsToTicks[value]]; BROADCAST tool.printerStatusTimer; }; WaitServerStatusTimer: ENTRY PROC [tool: Tool] ~ { <> ENABLE UNWIND => NULL; WAIT tool.printerStatusTimer; }; Report: PROC [v: Viewer, new: ROPE] ~ { <> 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; }; <> CreateStatusReport: PUBLIC ENTRY PROC [tool: Tool, title: ROPE] RETURNS [state: StatusReport] ~ { <> 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]; }; <> 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] ~ { <> <> 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.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 ~ { <> state: StatusReport = NARROW [clientData]; state.stopRequired ¬ TRUE; Buttons.SetDisplayStyle[state.stop, $BlackOnGrey]; }; SetSendingStatus: PUBLIC PROC [state: StatusReport] ~ { <> 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 ~ { <> 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... }; <> <> 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]; }; <> NoViewer: PROC [v: Viewer] RETURNS [BOOL] ~ INLINE { <> RETURN [v=NIL OR v.destroyed] }; SetupPrinterVersion: PROC [tool: 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; }; <> cmdDoc: ROPE = "[]\nCreate a viewer to print things on an XNS based printer.\nArguments:\n Print on that service"; cmdUsage: ROPE = Rope.Concat["Usage: XTSetter ", cmdDoc]; XTSetterCommand: Commander.CommandProc ~ { <> <> 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]; }; <> 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.