DIRECTORY
Atom USING [PutPropOnList],
CD USING [Design, Number, Rect],
CDCommandOps USING [BoundingBox, CallWithResource],
CDPanel USING [DefineButton, DefineNewLine],
CDPanelExtras USING [DefineTextEntry, FromDisplayRope],
CDProperties USING [PutDesignProp, RegisterProperty],
CDSequencer USING [Command, ImplementCommand],
CDValue USING [RegisterKey, Store],
CDViewer USING [ViewerList, ViewersOf, VisibleRect],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [NextArgument],
CStitching USING [NewTesselation, Tesselation, FindTile],
Imager USING [metersPerPoint, Rectangle],
IO USING [int, PutF, PutFR, real, rope],
MessageWindow USING [Append, Blink, Clear],
Nectarine USING [DoInterpress, invalidPrinter, Print],
Rope USING [ActionType, Cat, Equal, Fetch, IsEmpty, Length, Map, ROPE, SkipTo, Substr],
RopeFile USING [Create],
SafeStorage USING [GetCanonicalReferentType, Type],
SweepCollectableStorage USING [EnumerateCollectableStorage, InfoProc],
TEditDocument USING [Selection],
TEditInputOps USING [Break, CallWithLocks],
TerminalIO USING [WriteRope],
TextNode USING [Ref],
TiogaAccess USING [Create, GetInternalProp, Put, TiogaChar, Writer, WriteSelection, WriteViewer],
UserProfile USING [Token],
ViewerClasses USING [Viewer],
ViewerOps USING [BlinkIcon, CreateViewer];
~
BEGIN
masterKey: ATOM = $Nectarine;
whatKey: ATOM = $NectarineWhat;
whereKey: ATOM = $NectarineWhere;
copiesKey: ATOM = $NectarineCopies;
CSTile: SafeStorage.Type;
ROPE: TYPE ~ Rope.ROPE;
lastPrint:
RECORD [ip:
ROPE, key:
ATOM, q:
INT, w: Imager.Rectangle];
This global variable is used to implement the Redo command.
NectarineCommand:
PROC [comm: CDSequencer.Command] ~
BEGIN
Called by ChipNDale upon activation of the command.
abort: REF BOOL ← NEW [BOOL ← FALSE];
DoIt:
PROC [comm: CDSequencer.Command] ~
BEGIN
Protected procedure.
what: ROPE ~ CDPanelExtras.FromDisplayRope [comm.design, whatKey];
where: ROPE ~ CDPanelExtras.FromDisplayRope [comm.design, whereKey];
howMany: INT ← 0;
cropKey, printKey: ATOM;
masterName: ROPE ← NIL;
usedField: Imager.Rectangle;
rollPaperDeviceHint: REF Imager.Rectangle ← NIL;
The exception: the user does not know what he is doing (may be he is just nervous).
IF where.Equal ["Explain", FALSE] THEN {ExplainGoals []; RETURN};
Interpret what.
SELECT
TRUE
FROM
what.Equal ["Design", FALSE] => cropKey ← $NectarineAll;
what.Equal ["Wysiwyg", FALSE] => cropKey ← $NectarineWysiwyg;
what.Equal ["Selection", FALSE] => cropKey ← $NectarineSel;
ENDCASE =>
BEGIN
TerminalIO.WriteRope ["Unknown cropping option.\n"]; ERROR ABORTED
END;
Interpret where.
SELECT
TRUE
FROM
where.Equal ["ColorVersatec",
FALSE] =>
BEGIN
ImportantMessage ["Are you sure you did not want to use the printer server 'PeachExpand' (see doc)?"];
printKey ← $NColorVersatec
END;
where.Equal ["Color400", FALSE] => printKey ← $NColor400;
where.Equal ["Raven300", FALSE] => printKey ← $NRaven300;
where.Equal ["Bw400", FALSE] => printKey ← $NBw400;
where.Equal ["Versatec", FALSE] => printKey ← $NVersatec;
where.Equal ["PlateMaker", FALSE] => printKey ← $NPlateMaker;
where.Equal ["Tioga doc", FALSE] => printKey ← $NPasteInTioga;
where.Equal ["IP only", FALSE] => printKey ← $NmasterOnly;
where.Equal ["PeachExpand",
FALSE] =>
BEGIN
TerminalIO.WriteRope ["I will create an Interpress master, which I will use to produce a PD file. I will then send it to the expansion server to create a bitmap, and from there I will send it to the color Versatec. You will have to delete the temporary files manually.\n"];
printKey ← $NPeachExpand
END;
ENDCASE => {TerminalIO.WriteRope ["Unknown printer.\n"]; ERROR ABORTED};
Interpret how many.
BEGIN
nrRopeRaw: ROPE ~ CDPanelExtras.FromDisplayRope [comm.design, copiesKey];
nrPos: INTEGER ~ nrRopeRaw.SkipTo [0, "0123456789"];
nrRope: ROPE ~ nrRopeRaw.Substr [nrPos, nrRopeRaw.Length[]-nrPos];
IF (
NOT nrRope.IsEmpty [])
THEN
BEGIN
-- default: 0
howMany ← (ORD [nrRope.Fetch[0]] - ORD ['0]);
FOR i:
INT
IN [1 .. nrRopeRaw.Length[])
DO
howMany ← howMany * 10 + (ORD [nrRopeRaw.Fetch[i]] - ORD ['0])
ENDLOOP
END
END; -- Interpret how many
Create an Interpress master if necessary.
IF (masterName.IsEmpty [])
THEN
BEGIN
cW: CD.Rect; -- ChipNDale window
l: CD.Number ~ comm.design.technology.lambda;
SELECT cropKey
FROM
$NectarineAll => cW ← CDCommandOps.BoundingBox [comm.design];
$NectarineSel => cW ← CDCommandOps.BoundingBox [comm.design, TRUE];
$NectarineWysiwyg =>
BEGIN
viewers: CDViewer.ViewerList = CDViewer.ViewersOf [comm.design];
goodViewer: ViewerClasses.Viewer;
good: INT ← 0;
IF (viewers =
NIL)
THEN
BEGIN
TerminalIO.WriteRope ["You do not see anything of this design !\n"];
ERROR ABORTED
END;
FOR v: CDViewer.ViewerList ← viewers, v.rest
WHILE v #
NIL
DO
IF NOT v.first.iconic THEN {goodViewer ← v.first; good ← good.SUCC}
ENDLOOP;
SELECT good
FROM
0 => {TerminalIO.WriteRope ["You do not see anything !\n"]; ERROR ABORTED};
> 1 => TerminalIO.WriteRope ["WARNING: you see more than one viewer. I pick one at random\n"];
ENDCASE => NULL;
cW ← CDViewer.VisibleRect [goodViewer]
END;
ENDCASE => ERROR;
The following is not a hack. In schematics the cells may have a border. On the screen this border is one pixel wide, but with Interpress we have to specify a concrete width in millimetres. We choose to make it ¼ l wide. If the border has a width, it takes some finite amount of space on the paper. It would be natural to use this space inside the cell. However, the designers exploit the fact that on the screen the border has no width and put text flush to the border. In order to preserve such text, Nectarine is forced to print the border outside the cell. This means, that the border can now be outside the field on the medium. This is why here we cheat and increase the window by l on each side.
cW ← [x1: cW.x1 - l, x2: cW.x2 + l, y1: cW.y1 - l, y2: cW.y2 + l];
[masterName, usedField] ← Nectarine.DoInterpress [design: comm.design,
chipNDaleWindow: cW,
clip: (cropKey = $NectarineWysiwyg) OR (cropKey = $NectarineSel),
onlySel: (cropKey = $NectarineSel),
abortFlag: abort];
CDProperties.PutDesignProp [comm.design, masterKey, masterName]
END; -- create Interpress master
Print if asked to do so.
lastPrint ← [ip: masterName, key: printKey, q: howMany, w: usedField]; -- for Redo
SELECT printKey
FROM
$NmasterOnly => NULL;
$NPasteInTioga => Paste [masterName, usedField];
ENDCASE =>
BEGIN
IF (usedField.h > usedField.w)
THEN
rollPaperDeviceHint ← NEW [Imager.Rectangle ← usedField];
[] ← Nectarine.Print [masterName, printKey, howMany, rollPaperDeviceHint
! Nectarine.invalidPrinter => ERROR]
END;
TerminalIO.WriteRope ["Design unlocked by Nectarine.\n"]
END; -- DoIt
[] ← CDCommandOps.CallWithResource [DoIt, comm, $Nectarine, abort]
You would probably expect here a direct call of Nectarine, since the implementation is reentrant. However, I wanted to avoid two print commands on the same design, because this would force me to implement a file name manager and to place on the user the burden of descrying it. What I really wanted to do was to prevent multiple calls on the same design, but to allow parallel calls on different designs. Unfortunately, currently there are no provisions for this in ChipNDale: resources are global to ChipNDale and there are none local to designs (at least as long as you imperatively need a lightweight abort capability). Surely I could implement a hack, but there are no hacks in Nectarine.
END; -- NectarineCommand
Paste:
PROC [name:
ROPE, bb: Imager.Rectangle, newViewer:
BOOL ←
FALSE] ~
BEGIN
Pastes the Interpress master in file name and with bounding box bb at the current Tioga selection. The appropriate scaling is being implemented in Tioga and in TiogaToInterpress.
I misteri della vita e della morte.
master: ROPE ~ RopeFile.Create [name]; -- the Interpress master desguised as a rope
document: TiogaAccess.Writer ← TiogaAccess.Create []; -- a working area Tioga doc
vanillaChar: TiogaAccess.TiogaChar = [0, 0C, ALL [FALSE], NIL, TRUE, FALSE, 0, NIL];
dummy, ip, caption: TiogaAccess.TiogaChar ← vanillaChar;
space: REAL ~ 8.0 * Imager.metersPerPoint * 1000.0; -- in mm
topCalcRope: ROPE; -- for postfix property calculating top leading and ident
WriteRope:
PROC [r:
ROPE, attr: TiogaAccess.TiogaChar] ~
BEGIN
Writes into document the characters in r using the attributes of the sample character attr.
PutChar: Rope.ActionType ~ {attr.char ← c; document.Put [attr]};
[] ← Rope.Map [base: r, action: PutChar]
END; -- WriteRope
PutProp:
PROC [char: TiogaAccess.TiogaChar, prop:
ATOM, val:
ROPE]
RETURNS [TiogaAccess.TiogaChar] ~
BEGIN
Safely attaches a property with key prop and value val to the character char.
char.propList ← Atom.PutPropOnList [propList: char.propList, prop: prop, val: TiogaAccess.GetInternalProp [prop, val]];
RETURN [char]
END; -- PutProp
Stuff:
PROC [root: TextNode.Ref, tSel:TEditDocument.Selection] ~
BEGIN
Takes the working area Tioga document document and stuffs it into the current selection.
IF newViewer
THEN
BEGIN
repository: ViewerClasses.Viewer ← ViewerOps.CreateViewer [$Text];
document.WriteViewer [repository]; ViewerOps.BlinkIcon [repository, 0]
END
ELSE {TEditInputOps.Break []; TiogaAccess.WriteSelection [document]}
END; -- Stuff
Insert block node containing a CR. Why???
dummy.endOfNode ← TRUE; WriteRope ["\n", dummy];
Insert an artworkFigure node, with a postfix prop for lead and ident, bounds and with Artwork containing the IP master. The node contains a rope which becomes visible when ArtworkInterpress is off.
dummy.endOfNode ← FALSE; dummy.looks['n] ← TRUE;
WriteRope ["[Artwork node; type 'ArtworkInterpress on' to command tool]", dummy];
ip.char ← '\n; ip.endOfNode ← TRUE; ip.deltaLevel ← 1;
ip.format ← $artworkFigure; -- it is an artwork node
ip ← PutProp [ip, $Postfix, IO.PutFR ["%g mm topLeading %g mm topIndent %g mm bottomLeading", IO.real [bb.y+bb.h+space], IO.real [bb.y+bb.h+space], IO.real [-bb.y+space/2.0]]]; -- margin in the document
topCalcRope ← IO.PutFR ["%g the lineLength .mul %g .add", IO.real [bb.h/bb.w], IO.real [space]];
ip ← PutProp [ip, $Postfix, IO.PutFR ["%g topLeading %g topIndent %g mm bottomLeading",
IO.rope [topCalcRope], IO.rope [topCalcRope], IO.real [space/2.0]]]; -- margin in the document
ip ← PutProp [ip, $Bounds, IO.PutFR ["%g mm xmin %g mm ymin %g mm xmax %g mm ymax", IO.real [bb.x], IO.real [bb.y], IO.real [bb.x+bb.w], IO.real [bb.y+bb.h]]]; -- set the bounding box
ip ← PutProp [ip, $Artwork, "Interpress"]; -- the artwork type is "Interpress"
ip ← PutProp [ip, $Interpress, master]; -- the Interpress master desguised as a rope
ip ← PutProp [ip, $Fit, "TRUE"]; -- fit the figure to the margins of the sibling node
document.Put [ip];
Insert a nested artworkCaption node with a hint rope.
caption.looks['i] ← TRUE; caption.looks['o] ← TRUE; -- should be in the style
WriteRope ["Insert caption here", caption];
caption.char ← 0C; caption.format ← $artworkCaption; caption.endOfNode ← TRUE;
document.Put [caption];
Take the contents of document and stuff it into the current Tioga selection. If there is no selection, nothing happens.
TEditInputOps.CallWithLocks [Stuff]
END; -- Paste
Registations:
PROC [] ~
BEGIN
defaultCrops: LIST OF ROPE ~ LIST ["Design", "Selection", "Wysiwyg"];
defaultPrinters: LIST OF ROPE ~ LIST ["Explain", "PeachExpand", "Tioga doc", "Raven300", "Bw400", "Versatec", "ColorVersatec", "Color400", "PlateMaker", "IP only"];
[] ← CDProperties.RegisterProperty [masterKey, masterKey];
CDValue.RegisterKey [key: whatKey, registrationKey: masterKey];
CDValue.RegisterKey [key: whereKey, registrationKey: masterKey];
CDValue.RegisterKey [key: copiesKey, registrationKey: masterKey];
CDValue.Store [key: whatKey, value: defaultCrops];
CDValue.Store [key: whereKey, value: defaultPrinters];
CDValue.Store [key: copiesKey, value: "1"];
CDPanel.DefineButton [name: "Nectarine", border: TRUE, command: $NectarineCmd];
CDPanelExtras.DefineTextEntry [cdValueKey: whatKey, button: "What:", width: 60, editable: FALSE];
CDPanelExtras.DefineTextEntry [cdValueKey: whereKey, button: "Where:", width: 90, editable: FALSE];
CDPanelExtras.DefineTextEntry [cdValueKey: copiesKey, button: "Copies:", width: 30];
CDPanel.DefineNewLine [];
CDSequencer.ImplementCommand [key: $NectarineCmd, proc: NectarineCommand, queue: doQueue];
Commander.Register [key: "NCount", proc: Count, doc: "Counts corner stitching tiles"];
Commander.Register [key: "NRedo", proc: Redo, doc: "Syntax: NRedo <goal>. Redoes the last Nectarine Print command on the same Interpress master but with the newly specified goal. The goal must be one of those specified in the <where> field of the Nectarine line in the ChipNDale control panel. Default: the same as the last time around."]
END; -- Registrations
Count: Commander.CommandProc ~
BEGIN
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
PerObject: SweepCollectableStorage.InfoProc ~
BEGIN
[type: SafeStorage.Type, size: INT, object: LONG CARDINAL] RETURNS [continue: BOOLEAN]
IF type = CODE [CStitching.Tesselation] THEN tessCount ← tessCount + 1
ELSE IF type = CSTile THEN tileCount ← tileCount + 1;
RETURN [TRUE]
END; -- PerObject
tessCount: INT ← 0;
tileCount: INT ← 0;
TRUSTED { SweepCollectableStorage.EnumerateCollectableStorage [PerObject] };
cmd.out.PutF ["tesselations = %g\n", IO.int [tessCount]];
cmd.out.PutF ["tiles = %g\n", IO.int [tileCount]]
END; -- Count
Redo: Commander.CommandProc ~
BEGIN
[cmd: Commander.Handle] RETURNS [result: REF ANY ← NIL, msg: ROPE ← NIL]
Syntax: NRedo <goal>. Redoes the last Print command on the same Interpress master but on the newly specified goal. The goal must be one of those specified in the <where> field of the Nectarine line in the ChipNDale control panel. Default: the same as the last time around.
where: ROPE ~ CommandTool.NextArgument [cmd];
savedKey: ATOM ~ lastPrint.key;
IF
NOT where.IsEmpty []
THEN lastPrint.key
← SELECT
TRUE
FROM
where.Equal ["ColorVersatec", FALSE] => $NColorVersatec,
where.Equal ["Color400", FALSE] => $NColor400,
where.Equal ["Raven300", FALSE] => $NRaven300,
where.Equal ["Bw400", FALSE] => $NBw400,
where.Equal ["Versatec", FALSE] => $NVersatec,
where.Equal ["PlateMaker", FALSE] => $NPlateMaker,
where.Equal ["Tioga", FALSE] => $NPasteInTioga,
where.Equal ["Tioga doc", FALSE] => $NPasteInTioga,
where.Equal ["Paste", FALSE] => $NPasteInTioga,
where.Equal ["Stuff", FALSE] => $NPasteInTioga,
where.Equal ["IP only", FALSE] => $NmasterOnly,
where.Equal ["IP", FALSE] => $NmasterOnly,
where.Equal ["PeachExpand", FALSE] => $NPeachExpand,
ENDCASE => $cacca;
SELECT lastPrint.key
FROM
$NmasterOnly => msg ← lastPrint.ip;
$NPasteInTioga =>
BEGIN
The problem here is that now the input focus is in the Command Tool viewer, which surely is not the one into which the poor user wanted to paste his image. Therefore, a new Tioga viewer is created and used as a temporary repository for the image.
Paste [lastPrint.ip, lastPrint.w, TRUE];
msg ← "Image deposed in temporary blinking iconic Tioga viewer."
END;
$cacca =>
BEGIN
lastPrint.key ← savedKey; result ← $Failure; msg ← cmd.procData.doc
END;
ENDCASE =>
BEGIN
rollPaperDeviceHint: REF Imager.Rectangle ← NIL;
IF (lastPrint.w.h > lastPrint.w.w)
THEN
rollPaperDeviceHint ← NEW [Imager.Rectangle ← lastPrint.w];
[] ← Nectarine.Print [lastPrint.ip, lastPrint.key, lastPrint.q, rollPaperDeviceHint
! Nectarine.invalidPrinter => {result ← $Failure; msg ← cmd.procData.doc}]
END
END; -- Redo
ExplainGoals:
PROC [] ~
BEGIN
Upon popular demand, we explain the entries in the Where menu.
Plop:
PROC [plup, plip, plap:
ROPE] ~
BEGIN
Plaps plop for plop at plip.
TerminalIO.WriteRope [plup.Cat [": print on ", UserProfile.Token [Rope.Cat ["Nectarine.", plup], plip]]];
TerminalIO.WriteRope [Rope.Cat [", a ", plap, " printer.\n"]]
END; -- Plop
TerminalIO.WriteRope ["The following goals are available for the output:\n"];
TerminalIO.WriteRope ["PeachExpand: expand the PD file on "];
TerminalIO.WriteRope [UserProfile.Token ["Nectarine.PeachExpand", "Kearsarge"]];
TerminalIO.WriteRope [" and send it to "];
TerminalIO.WriteRope [UserProfile.Token ["Nectarine.ColorVersatec", "Sleepy"]];
TerminalIO.WriteRope [".\n"];
TerminalIO.WriteRope ["Tioga doc: copy in the Tioga document at the caret.\n"];
Plop ["Raven300", "Quoth", "black & white Interpress"];
Plop ["Bw400", "MtFuji", "black & white thermal"];
Plop ["Versatec", "Sleepy", "black & white Versatec"];
Plop ["ColorVersatec", "Sleepy", "colour Versatec"];
Plop ["Color400", "MtFuji", "colour thermal"];
Plop ["PlateMaker", "PlateMaker", "special"];
TerminalIO.WriteRope ["Explain: jot down this explanation.\n\n"]
END; -- ExplainGoals
ImportantMessage:
PROC [msg: Rope.
ROPE] ~
BEGIN
Writes a message in the ChipNDale terminal viewer and in the Message Window at the top of the LF screen and makes it blink.
TerminalIO.WriteRope [msg]; TerminalIO.WriteRope ["\n"];
MessageWindow.Clear []; MessageWindow.Append [msg]; MessageWindow.Blink []
END; -- ImportantMessage
CSTile ← SafeStorage.GetCanonicalReferentType [CStitching.NewTesselation[].FindTile[[0,0]]];
Registations [];
TerminalIO.WriteRope ["Nectarine package loaded.\n"];
ExplainGoals []