PPreViewTool.mesa
Copyright Ó 1989, 1992 by Xerox Corporation. All rights reserved.
Pier, July 17, 1992 4:45 pm PDT
Carl Hauser, August 18, 1989 4:38:21 pm PDT
Michael Plass, March 25, 1992 12:36 pm PST
Chauser, June 25, 1992 4:06 pm PDT
DIRECTORY
Ascii, BasicTime, BiScrollers, Buttons, Commander, CommanderOps, MJSContainers, Convert, FileNames, FS, Icons, Imager, ImagerBackdoor, ImagerColor, ImagerMemory, ImagerPixel, ImagerPixelArray, ImagerPixelArrayAIS, ImagerSample, ImagerTransformation, InterpressInterpreter, IO, IPMaster, Labels, MessageWindow, PFS, PFSNames, PopUpButtons, PPreView, PPreViewClient, PPreViewOps, RasterEncodingStandardIO, Real, Rope, RopeList, Rules, Sliders, TIPUser, UserProfile, ViewerClasses, ViewerOps, ViewerSpecs, ViewerTools;
PPreViewTool: CEDAR MONITOR
IMPORTS Ascii, BiScrollers, Commander, CommanderOps, MJSContainers, Convert, FileNames, FS, Icons, Imager, ImagerBackdoor, ImagerColor, ImagerMemory, ImagerPixel, ImagerPixelArray, ImagerPixelArrayAIS, ImagerSample, ImagerTransformation, InterpressInterpreter, IO, IPMaster, Labels, MessageWindow, PFS, PFSNames, PopUpButtons, PPreView, RasterEncodingStandardIO, Real, Rope, RopeList, Rules, Sliders, TIPUser, UserProfile, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS PPreView, PPreViewClient, PPreViewOps ~ BEGIN
ROPE: TYPE ~ Rope.ROPE;
FileType: TYPE ~ PPreView.FileType;
Data: TYPE ~ PPreView.Data;
Rep: TYPE ~ PPreView.Rep;
IPData: TYPE ~ PPreView.IPData;
AISData: TYPE ~ PPreView.AISData;
RESData: TYPE ~ PPreView.RESData;
AISState: TYPE ~ PPreView.AISState;
Viewer: TYPE ~ ViewerClasses.Viewer;
pvIcon: Icons.IconFlavor ~ Icons.NewIconFromFile["PreView.icons", 0];
defaultPageHeight: REAL ~ 11.0*72.0; -- points
defaultPageWidth: REAL ~ 8.5*72.0;  -- points
versatecPageHeight: REAL ~ 40.0*72.0; -- points
versatecPageWidth: REAL ~ 40.0*72.0; -- points
screenResolution: REAL ~ 72.0;   -- points per inch
pointsPerInch: REAL ~ 72.0;
oneOverPointsPerInch: REAL ~ 1.0/72.0;
pointsPerMica: REAL ~ 72.0/2540.0;
micasPerPoint: REAL ~ 2540.0/72.0;
pointsPerMeter: REAL ~ 72.0/.0254;
metersPerPoint: REAL ~ .0254/72.0;
visibleGrey: Imager.Color ~ ImagerBackdoor.MakeStipple[122645B];
invisibleGrey: Imager.Color ~ ImagerBackdoor.MakeStipple[100040B];
buttonAlign: INTEGER ¬ -2; -- align standard viewer with PopUp buttons
labelAlign: INTEGER ¬ -1; -- align label with PopUp buttons
entryHSpace: INTEGER ¬ 2; -- horizontal space between items on a line
secondLine: INTEGER ¬ 14;
bsY: INTEGER ¬ 31;
ipY: INTEGER ¬ 32;
xFudge: INTEGER ¬ 2;
yFudge: INTEGER ¬ 2;
ipPrefix: ROPE ~ "Interpress/";
ipHeader: ROPE ~ "Interpress/Xerox/";
defaultExtensionList: LIST OF ROPE ~ LIST["ip", "ais", "res", "interpress", "IP", "Interpress"];
extensionList: LIST OF ROPE ¬ defaultExtensionList;
bsStyle: PUBLIC BiScrollers.BiScrollerStyle;
pvBSClass: BiScrollers.BiScrollerClass;
selClass, stuffClass: PopUpButtons.Class;
toIPClass, pageClass: PopUpButtons.Class;
IPLogError: PUBLIC InterpressInterpreter.LogProc ~ { -- [class: INT, code: ATOM, explanation: ROPE]
Complain[message: Rope.Concat["InterpressMaster Error: ", explanation]];
};
WhichFileTypeAndPath: PROC [fileName: ROPE, mustBe: PPreView.FileType ¬ any, data: Data ¬ NIL]
RETURNS [fileInfo: PPreView.FileInfo ← NIL, fullPath: PFS.PATHNIL]
~ {
ENABLE PFS.Error => GOTO PFSError;
Expects a file name which may not have an extension
bytes: INT ¬ -1;
created: BasicTime.GMT ¬ BasicTime.nullGMT; -- no longer used
path: PFS.PATH ¬ PFS.PathFromRope[fileName];
IF data#NIL THEN {
wDir: PFS.PATH ¬ PFSNames.NarrowPath[ViewerOps.FetchProp[viewer: data.container, prop: $PVWDir]];
path ¬ PFSNames.ExpandName[path, wDir];
};
[fullFName: fullPath, bytes: bytes] ¬ PFS.FileInfo[name: path ! PFS.Error => CONTINUE];
IF bytes#-1 THEN { -- named file exists
IF PFSNames.IsADirectory[fullPath] THEN {
Complain[message: Rope.Concat[PFS.RopeFromPath[fullPath], " is a directory"]];
RETURN[NIL, fullPath];
};
fileInfo ¬ DiscoverFileType[fullPath, created, mustBe];
RETURN;
};
Didn't find full name, so try a few extensions
IF extensionList = NIL THEN extensionList ¬ defaultExtensionList;
FOR l: LIST OF ROPE ¬ extensionList, l.rest UNTIL l=NIL DO
name: ROPE ¬ Rope.Cat[fileName, ".", l.first];
path ¬ PFS.PathFromRope[name];
[fullFName: fullPath, bytes: bytes] ¬ PFS.FileInfo[name: path ! PFS.Error => LOOP];
IF bytes <= -1 THEN LOOP;
fileInfo ¬ DiscoverFileType[fullPath, created, mustBe];
RETURN[fileInfo, fullPath]; -- file with extension exists
ENDLOOP;
RETURN[NIL, NIL]; -- file does not exist
EXITS
PFSError => {
Complain[message: Rope.Concat["PFS Error while processing ", fileName]];
};
};
DiscoverFileType: PROC [fullPath: PFS.PATH, created: BasicTime.GMT, mustBe: PPreView.FileType]
RETURNS [fileInfo: PPreView.FileInfo]
~ {
Given fullPath, determine if it is an interpress, press, PD, AIS, Griffin, Gargoyle file (or none).
Uses the extension as a hint as to the filetype.
fileInfo ¬ NEW[PPreView.FileInfoRep ¬ []]; --initialize to fileType none, etc.
{
ref: REF ANY;
s: IO.STREAM;
fullName: ROPE;
short: PFSNames.Component; -- looking for base.ext
baseAndExtension: ROPE; -- should have base.ext
whereExt: INT;
ext: ROPE;
s ¬ PFS.StreamOpen[fileName: fullPath ! IO.Error, PFS.Error => GOTO None];
fullName ¬ PFS.RopeFromPath[fullPath];
short ¬ PFSNames.ShortName[fullPath]; -- looking for base.ext
baseAndExtension¬ Rope.Substr[base: short.name.base, start: short.name.start, len: short.name.len]; -- should have base.ext
whereExt ¬ Rope.FindBackward[s1: baseAndExtension, s2: ".", case: FALSE];
ext ¬ IF whereExt<0 THEN NIL ELSE Rope.Substr[base: baseAndExtension, start: whereExt+1];
SELECT mustBe FROM
any => {
SELECT TRUE FROM
Rope.Equal[s1: ext, s2: "ip", case: FALSE], Rope.Equal[s1: ext, s2: "interpress", case: FALSE] => IF Rope.Equal[IO.GetRope[s, Rope.Size[ipHeader]], ipHeader] THEN {fileInfo­ ¬ [ip, fullName, created, s]; RETURN;};
Rope.Equal[s1: ext, s2: "ais", case: FALSE] => IF (ref ¬ ImagerPixelArrayAIS.FromAIS[name: fullName ! UNCAUGHT => CONTINUE;])#NIL THEN {fileInfo­ ¬ [ais, fullName, created, ref]; RETURN;};
Rope.Equal[s1: ext, s2: "res", case: FALSE] => IF (ref ¬ RasterEncodingStandardIO.Read[fileName: fullName ! UNCAUGHT => CONTINUE])#NIL THEN {fileInfo­ ¬ [res, fullName, created, ref]; RETURN;};
ENDCASE => NULL;
Extension as hint did not help; so try all other possibilities
IO.SetIndex[s, 0]; --reset stream for subsequent calls
IF Rope.Equal[IO.GetRope[s, Rope.Size[ipHeader]], ipHeader] THEN {fileInfo­ ¬ [ip, fullName, created, s]; RETURN;};
IO.SetIndex[s,0]; --reset stream for subsequent calls
IF (ref ¬ ImagerPixelArrayAIS.FromAIS[name: fullName ! UNCAUGHT => CONTINUE;])#NIL THEN {fileInfo­ ¬ [ais, fullName, created, ref]; RETURN;};
IF (ref ¬ RasterEncodingStandardIO.Read[fileName: fullName ! UNCAUGHT => CONTINUE;])#NIL THEN {fileInfo­ ¬ [res, fullName, created, ref]; RETURN;};
fileInfo­ ¬ [none, NIL, BasicTime.nullGMT, NIL];
};
ip => {
IF Rope.Equal[IO.GetRope[s, Rope.Size[ipHeader]], ipHeader] THEN fileInfo­ ¬ [ip, fullName, created, s] ELSE GOTO None;
};
ais => {
IF (ref ¬ ImagerPixelArrayAIS.FromAIS[name: fullName ! UNCAUGHT => CONTINUE;])#NIL THEN fileInfo­ ¬ [ais, fullName, created, ref] ELSE GOTO None;
};
res => {
IF (ref ¬ RasterEncodingStandardIO.Read[fileName: fullName ! UNCAUGHT => CONTINUE;])#NIL THEN fileInfo­ ¬ [res, fullName, created, ref] ELSE GOTO None;
};
ENDCASE => GOTO None;
EXITS
None => fileInfo­ ¬ [none, NIL, BasicTime.nullGMT, NIL];
};
};
CreatePreViewer: PUBLIC PROC [fileNames: PPreView.NameList, switches: PPreView.Switches, xSize, ySize: REAL, mustBe: PPreView.FileType ¬ any] RETURNS [preViewer: Viewer] = {
data: Data ¬ NIL;
versionSpecified: BOOL ¬ FALSE;
[data, versionSpecified] ¬ LoadPreViewData[fileNames, switches, xSize, ySize, mustBe, 1];
preViewer ¬ CreatePreViewerFromData[data, versionSpecified]
};
LoadPreViewData: PROC [fileNames: PPreView.NameList, switches: PPreView.Switches, xSize, ySize: REAL, mustBe: PPreView.FileType ¬ any, pageNumber: INTEGER ¬ 1] RETURNS [data: Data ¬ NIL, versionSpecified: BOOL ¬ FALSE] = {
path: PFS.PATH;
fileInfo: PPreView.FileInfo ¬ NIL;
lastPageNumber: INTEGER ¬ 1;
fileName: ROPE ¬ NIL;
IF fileNames=NIL THEN {
Complain[message: "Empty file list"];
RETURN;
};
IF switches['P] THEN {
This is a multipage image document with pages in separate files
thisPage: INTEGER ¬ 1;
lastPageNumber ¬ RopeList.Length[fileNames];
Find filename for this page of image data
FOR list: LIST OF ROPE ¬ fileNames, list.rest UNTIL list=NIL DO
IF thisPage = pageNumber THEN {fileName ¬ list.first; EXIT};
thisPage ¬ thisPage +1;
ENDLOOP;
}
ELSE fileName ¬ fileNames.first;
[fileInfo, path] ¬ WhichFileTypeAndPath[fileName, mustBe]; -- add file extension to named file if needed
IF fileInfo=NIL OR path=NIL THEN {
Complain[message: Rope.Concat["Unknown file: ", fileName]];
RETURN;
};
IF fileInfo.filetype=none THEN {
Complain[message: Rope.Concat["Unknown file type for: ", fileName]];
RETURN;
};
IF mustBe # any AND fileInfo.filetype # mustBe THEN {
Complain[Rope.Cat["Can't display ", fileName, " with currently loaded PreView packages"]];
RETURN;
};
SELECT fileInfo.filetype FROM
ip => {
stream: IO.STREAM ~ NARROW[fileInfo.ref];
ipmaster: InterpressInterpreter.Master ¬ InterpressInterpreter.FromStream[stream: stream, log: IPLogError !
PFS.Error => {
Complain[message: Rope.Cat[error.explanation, " for ", fileInfo.fullFName]];
GOTO Quit;
};
IPMaster.Error => { --ErrorDesc: TYPE = RECORD[code: ATOM, explanation: ROPE, index: INT ¬ 0]
Complain[message: Rope.Cat[error.explanation, " for ", fileInfo.fullFName]];
GOTO Quit;
};
Imager.Error => { --ErrorDesc: TYPE = RECORD [code: ATOM, explanation: ROPE]
Complain[message: Rope.Cat[error.explanation, " for ", fileInfo.fullFName]];
GOTO Quit;
};
IO.Error, IO.EndOfStream => {
Complain[message: Rope.Concat["IO Stream Error for ", fileInfo.fullFName]];
GOTO Quit;
};
];
IF ipmaster.pages=0 THEN {
Complain[message: Rope.Concat["Zero pages in ", fileInfo.fullFName]];
RETURN;
};
data ¬ NEW[ip Rep ¬ [fileInfo: fileInfo, pageNumber: 1, lastPageNumber: ipmaster.pages, pageHeight: IF switches['V] THEN versatecPageHeight ELSE ySize*72.0, pageWidth: IF switches['V] THEN versatecPageWidth ELSE xSize*72.0, switches: switches, kind: ip[ipMaster: ipmaster]]]; -- pages IN [1..end]
};
ais => {
aisData: AISData ¬ NEW[ais Rep ¬ [fileInfo: fileInfo, pageNumber: pageNumber, lastPageNumber: lastPageNumber, switches: switches, kind: ais[state: NEW[PPreView.AISStateRep ¬ []]]]];
aisData.state.openFile ← NARROW[fileInfo.ref]; -- DiscoverFileType did an AIS.OpenFile already
--read the AIS file(s) and get ready for display. aisData.state will be further filled in by this call.
[fileInfo.fullFName, versionSpecified] ¬ InitAIS[data: aisData, fileNames: fileNames, singleName: fileInfo.fullFName, fromAIS: NARROW[fileInfo.ref]];
IF fileInfo.fullFName = NIL THEN RETURN;
aisData.pageHeight ¬ aisData.state.scans;
aisData.pageWidth ¬ aisData.state.pixels;
data ¬ aisData;
};
res => {
res: RasterEncodingStandardIO.RES ~ NARROW[fileInfo.ref];
data ¬ NEW[res Rep ¬ [fileInfo: fileInfo, pageNumber: 1, lastPageNumber: lastPageNumber, pageHeight: res.yDimension, pageWidth: res.xDimension, switches: switches, kind: res[image: res]]];
};
ENDCASE => {
Complain[message: Rope.Concat["Unknown file type for: ", fileName]];
RETURN;
};
versionSpecified ¬ PFSNames.ShortName[path].version.versionKind=numeric;
data.fileNames ¬ fileNames;
data.xSize ¬ xSize;
data.ySize ¬ ySize;
data.mustBe ¬ mustBe;
RETURN[data, versionSpecified];
EXITS
Quit => NULL;
};
NewPageData: PROC [data: Data, pageNumber: INTEGER ¬ 1] = {
fileInfo: PPreView.FileInfo ¬ NIL;
fileName: ROPE ¬ NIL;
path: PFS.PATH;
versionSpecified: BOOL ¬ FALSE;
IF data.switches['P] AND data.fileNames # NIL THEN {
This is a multipage image document with pages in separate files
thisPage: INTEGER ¬ 1;
Find filename for this page of image data
FOR list: LIST OF ROPE ¬ data.fileNames, list.rest UNTIL list=NIL DO
IF thisPage = pageNumber THEN {fileName ¬ list.first; EXIT};
thisPage ¬ thisPage +1;
ENDLOOP;
}
ELSE IF data.fileNames # NIL THEN fileName ¬ data.fileNames.first;
[fileInfo, path] ¬ WhichFileTypeAndPath[fileName, data.mustBe, data]; -- add file extension to named file if needed
IF fileInfo=NIL THEN {
Complain[message: Rope.Concat["Unknown file: ", fileName]];
RETURN;
};
IF fileInfo.filetype=none THEN {
Complain[message: Rope.Concat["Unknown file type for: ", fileName]];
RETURN;
};
IF data.mustBe # any AND fileInfo.filetype # data.mustBe THEN {
Complain[Rope.Cat["Can't display ", fileName, " with currently loaded PreView packages"]];
RETURN;
};
data.fileInfo ¬ fileInfo;
versionSpecified ¬ PFSNames.ShortName[path].version.versionKind=numeric;
data.container.name ¬ IF versionSpecified THEN Rope.Concat["PreView: ", data.fileInfo.fullFName] ELSE Rope.Cat["PreView: ", FileNames.StripVersionNumber[data.fileInfo.fullFName], " (!", FileNames.Tail[data.fileInfo.fullFName, '!], ")"];
data.container.label ¬ FileNames.GetShortName[path: data.fileInfo.fullFName, stripOffVersionNumber: NOT versionSpecified];
data.container.file ¬ data.fileInfo.fullFName;
SELECT fileInfo.filetype FROM
res => {
resData: RESData ¬ NARROW[data];
res: RasterEncodingStandardIO.RES ~ NARROW[fileInfo.ref];
resData.pageHeight ¬ res.yDimension;
resData.pageWidth ¬ res.xDimension;
resData.image ¬ NARROW[res];
};
ais => {
aisData: AISData ¬ NARROW[data];
aisData.state.openFile ← NARROW[fileInfo.ref]; -- DiscoverFileType did an AIS.OpenFile already
--read the AIS file(s) and get ready for display. aisData.state will be further filled in by this call.
[fileInfo.fullFName, ] ¬ InitAIS[data: aisData, fileNames: data.fileNames, singleName: fileInfo.fullFName, fromAIS: NARROW[fileInfo.ref]];
IF fileInfo.fullFName = NIL THEN RETURN;
aisData.pageHeight ¬ aisData.state.scans;
aisData.pageWidth ¬ aisData.state.pixels;
};
ENDCASE => {
Complain[message: Rope.Concat["Paging not supported for filetype of: ", fileName]];
RETURN;
};
};
CreatePreViewerFromData: PUBLIC PROC [data: Data, versionSpecified: BOOL]
RETURNS [preViewer: Viewer]
~ {
ENABLE PFS.Error => GOTO PFSError;
MakeNewTextViewer: PROC [x, y, w, h: INTEGER] RETURNS [v: Viewer] ~ {
v ¬ ViewerTools.MakeNewTextViewer[
info: [wx: x, wy: y, ww: w, wh: h, parent: data.container, border: FALSE, scrollable: FALSE],
paint: FALSE];
ViewerTools.InhibitUserEdits[v];
};
curIndent: INTEGER ¬ 0;
bs: BiScrollers.BiScroller ¬ NIL;
tV: Viewer;
button: Buttons.Button;
rule: Rules.Rule;
path: PFS.PATH;
unversionedPath: PFS.PATH;
short: PFSNames.Component;
version: CARDINAL ¬ 0;
IF data = NIL THEN RETURN[NIL];
IF data.fileInfo = NIL THEN data.fileInfo ¬ NEW[PPreView.FileInfoRep];
path ¬ PFS.PathFromRope[data.fileInfo.fullFName];
unversionedPath ¬ PFSNames.StripVersionNumber[path];
short ¬ PFSNames.ShortName[path];
version ¬ short.version.version;
data.bBox ¬ NEW[PPreView.BBoxStateRep ¬ []];
data.iMemContext ¬ ImagerMemory.NewMemoryContext[];
data.container ¬ preViewer ¬ MJSContainers.Create[
viewerFlavor: $VanillaMJSContainer,
info: [
name: IF versionSpecified THEN Rope.Concat["PreView: ", data.fileInfo.fullFName] ELSE Rope.Cat["PreView: ", PFS.RopeFromPath[unversionedPath], IO.PutFR1[" (!%g)", [cardinal[version]] ] ],
label: Rope.Concat[Rope.Substr[short.name.base, short.name.start, short.name.len], IF versionSpecified THEN IO.PutFR1["!%g", [cardinal[version]] ] ELSE ""],
file: data.fileInfo.fullFName,
iconic: TRUE,
menu: NIL,
icon: pvIcon,
data: data,
scrollable: FALSE],
paint: FALSE ];
bs ¬ bsStyle.CreateBiScroller[
class: pvBSClass,
info: [
parent: data.container,
wx: 0,
wy: bsY,
border: FALSE,
scrollable: FALSE,
data: data],
paint: FALSE ];
tV ¬ BiScrollers.CreateScale[[parent: preViewer, wx: 0, wy: 0, border: FALSE], bs];
tV ¬ BiScrollers.CreateRotate[[parent: preViewer, wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], bs];
tV ¬ BiScrollers.CreateFit[[parent: preViewer, wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], bs];
tV ¬ BiScrollers.CreateReset[[parent: preViewer, wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], bs];
tV ¬ BiScrollers.CreateEdge[[parent: preViewer, wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], bs];
tV ¬ BiScrollers.CreatePrev[[parent: preViewer, wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], bs];
button ¬ stuffClass.Instantiate[viewerInfo: [name: "Stuff", wx: curIndent, wy: secondLine, border: FALSE, parent: data.container], instanceData: data, paint: FALSE];
curIndent ¬ button.wx + button.ww;
button ¬ toIPClass.Instantiate[viewerInfo: [name: "ToIP", wx: curIndent, wy: secondLine, border: FALSE, parent: data.container], instanceData: data, paint: FALSE];
curIndent ¬ button.wx + button.ww;
button ¬ selClass.Instantiate[viewerInfo: [name: "Selection", wx: curIndent, wy: secondLine, border: FALSE, parent: data.container], instanceData: data, paint: FALSE];
curIndent ¬ button.wx + button.ww;
button ¬ pageClass.Instantiate[viewerInfo: [name: "Page", wx: curIndent, wy: secondLine, border: FALSE, parent: data.container], instanceData: data, paint: FALSE];
curIndent ¬ button.wx + button.ww;
button ¬ Labels.Create[
info: [
name: "AtPage:",
wx: curIndent,
wy: secondLine+labelAlign,
border: FALSE,
parent: data.container],
paint: FALSE];
curIndent ¬ button.wx + button.ww;
data.pageNumberViewer ¬ MakeNewTextViewer[
curIndent, secondLine+ViewerSpecs.windowBorderSize+buttonAlign,
40, ViewerSpecs.scrollBarW+2*ViewerSpecs.windowBorderSize];
curIndent ¬ data.pageNumberViewer.wx + data.pageNumberViewer.ww;
data.pageNumberSlider ¬ Sliders.Create[
info: [
wx: curIndent,
wy: secondLine,
ww: data.container.ww-curIndent,
wh: ViewerSpecs.scrollBarW+2*ViewerSpecs.windowBorderSize,
border: FALSE,
parent: data.container,
scrollable: FALSE],
filterProc: NormalizePageNumber,
sliderProc: PageNumberSlider,
orientation: horizontal,
foreground: visibleGrey,
background: invisibleGrey,
clientData: data,
paint: FALSE];
MJSContainers.ChildXBound[data.container, data.pageNumberSlider];
button ¬ Labels.Create[
info: [
name: "Scaled Size: w: h: inches",
wx: data.pageNumberViewer.wx+data.pageNumberViewer.ww-3,
wy: labelAlign,
border: FALSE,
parent: data.container],
paint: FALSE];
data.selectionWViewer ¬ MakeNewTextViewer[
button.wx+95, ViewerSpecs.windowBorderSize+buttonAlign,
45, ViewerSpecs.scrollBarW+2*ViewerSpecs.windowBorderSize];
data.selectionHViewer ¬ MakeNewTextViewer[
button.wx+165, ViewerSpecs.windowBorderSize+buttonAlign,
45, ViewerSpecs.scrollBarW+2*ViewerSpecs.windowBorderSize];
rule ¬ Rules.Create[
info: [
parent: data.container,
wx: 0,
wy: secondLine+ViewerSpecs.captionHeight+4*ViewerSpecs.windowBorderSize,
wh: ViewerSpecs.menuBarHeight]];
MJSContainers.ChildXBound[data.container, rule];
data.preViewer ¬ bs.QuaViewer[inner: FALSE];
ViewerOps.AddProp[viewer: bs.QuaViewer[inner: TRUE], prop: $PVWDir, val: PFS.GetWDir[]];
ViewerOps.AddProp[viewer: data.container, prop: $PVWDir, val: PFS.GetWDir[]];
MJSContainers.ChildXBound[data.container, data.preViewer];
MJSContainers.ChildYBound[data.container, data.preViewer];
ViewerOps.PaintViewer[viewer: data.container, hint: all, clearClient: TRUE];
DeltaPage[data, 1];
EXITS
PFSError => {
Complain[message: Rope.Concat["PFS Error while processing ", data.fileInfo.fullFName]];
RETURN[NIL]; -- probably garbage fileName
};
};
InitAIS: PROC [data: AISData, fileNames: PPreView.NameList, singleName: ROPE, fromAIS: ImagerPixelArray.PixelArray] RETURNS [newName: ROPE ¬ NIL, versionSpecified: BOOL ¬ FALSE] = {
ENABLE {
FS.Error => {
Complain[message: error.explanation];
newName ¬ NIL; CONTINUE;
};
PFS.Error => {
Complain[message: error.explanation];
newName ¬ NIL; CONTINUE;
};
ImagerPixelArray.Error => {
Complain[message: error.explanation];
newName ¬ NIL; CONTINUE;
};
};
state: AISState ¬ data.state;
IF ~data.switches['P] AND fileNames.rest#NIL
THEN { --multi-file color AIS image
fullFName: ROPE;
redName: ROPE ¬ fileNames.rest.rest.first;
grnName: ROPE ¬ fileNames.rest.first;
bluName: ROPE ¬ fileNames.first;
fullCP: FS.ComponentPositions; -- a full FName
grnCP: FS.ComponentPositions; -- not a full FName
[fullFName, fullCP, ] ¬ FS.ExpandName[singleName]; -- a full FName
grnCP ¬ FS.ExpandName[grnName].cp; -- not a full FName
state.pa ¬ ImagerPixelArrayAIS.Join3AIS[name1: redName, name2: grnName, name3: bluName]; -- in red, green, blue order !!
[scanCount: state.scans, scanLength: state.pixels] ← AIS.ReadRaster[state.openFile]^; -- must be identical raster info in all separations
state.scans ¬ state.pa.sSize; -- must be identical raster info in all separations
state.pixels¬ state.pa.fSize; -- must be identical raster info in all separations
state.op ¬ ImagerColor.NewColorOperatorRGB[ImagerPixelArray.MaxSampleValue[pa: state.pa, i: 0]];
Now construct a viewer caption like Foo-*.ais!33
newName ¬ Rope.Concat[Rope.Substr[base: fileNames.first, start: 0, len: Rope.SkipTo[s: fileNames.first, pos: 0, skip: "-"]], "-*.ais"]; -- everything thru the name base
IF fullCP.ver.length#0 THEN newName ¬ Rope.Cat[newName, "!", Rope.Substr[base: fullFName, start: fullCP.ver.start, len: fullCP.ver.length] ]; -- should be some version number, even 0, on a full name
IF grnCP.ver.length#0 THEN versionSpecified ¬ TRUE; -- explicit version if not a full FName
}
ELSE { -- single file AIS image
bps: [0..16] ← 0;
msv: [0..256) ¬ 0;
state.pa ¬ fromAIS;
[scanCount: state.scans, scanLength: state.pixels, scanMode: , bitsPerPixel: bps] ← AIS.ReadRaster[state.openFile]^;
state.scans ¬ state.pa.sSize;
state.pixels¬ state.pa.fSize;
msv ¬ ImagerPixelArray.MaxSampleValue[state.pa, 0];
SELECT msv FROM
0 => state.op ¬ NIL;
1 => { -- need complement of the pa for imaging with MaskPixel
newpa: ImagerPixelArray.PixelArray;
pa: ImagerPixelArray.PixelArray ¬ state.pa;
box: ImagerSample.Box ¬ [min: [s: 0, f: 0], max: [s: state.scans, f: state.pixels]];
sm: ImagerSample.SampleMap ¬ ImagerSample.NewSampleMap[box: box, bitsPerSample: 1];
ImagerPixelArray.Transfer[pa: pa, i: 0, s: 0, f: 0, dst: sm, function: [null, complement]];
newpa ¬ ImagerPixelArray.FromPixelMap[pixelMap: ImagerPixel.MakePixelMap[s0: sm], box: box, scanMode: [slow: right, fast: up], immutable: TRUE];
newpa.m ¬ pa.m;
state.pa ¬ newpa;
state.op ¬ NIL;
};
ENDCASE => state.op ¬ ImagerColor.NewColorOperatorGrayLinear[sWhite: ImagerPixelArray.MaxSampleValue[pa: state.pa, i: 0], sBlack: 0.0];
newName ¬ singleName;
};
state.active ¬ TRUE;
};
SelectionOps: PopUpButtons.PopUpButtonProc = { --PROC [viewer: Viewer, instanceData, classData, key: REF ANY]
data: Data ¬ NARROW[instanceData];
viewer: Viewer ¬ BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
SELECT key FROM
$Center => { -- center selection in viewer
IF NOT data.bBox.active THEN GOTO NoSel;
BiScrollers.Align[bs: BiScrollers.QuaBiScroller[data.preViewer], client: [variant: coord[x: data.bBox.rect.x+(data.bBox.rect.w/2.0), y: data.bBox.rect.y+(data.bBox.rect.h/2.0)]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], paint: TRUE];
};
$CenterAndFit, $CenterAndScale => { -- center selection in viewer then scale to fit inside viewer
vH, vW, sH, sW: REAL ¬ 1.0; -- viewer and selection dimensions
IF NOT data.bBox.active THEN GOTO NoSel;
sH ¬ MAX[sH, data.bBox.rect.h]; sW ¬ MAX[sW, data.bBox.rect.w];
vH ¬ MAX[vH, viewer.ch]; vW ¬ MAX[vW, viewer.cw];
BiScrollers.Align[bs: BiScrollers.QuaBiScroller[data.preViewer], client: [variant: coord[x: data.bBox.rect.x+(data.bBox.rect.w/2.0), y: data.bBox.rect.y+(data.bBox.rect.h/2.0)]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], paint: FALSE];
IF key=$CenterAndFit THEN BiScrollers.Scale[bs: BiScrollers.QuaBiScroller[data.preViewer], op: [variant: reset[] ], paint: FALSE ]; -- reset scale before fitting
BiScrollers.Scale[bs: BiScrollers.QuaBiScroller[data.preViewer], op: [variant: byArg[arg: MIN[vW/sW, vH/sH]]], paint: TRUE ];
};
$SelectPage => { -- select entire page
IF data.bBox.active THEN PPreView.PVFeedback[data: data, v: BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE], op: remove]; -- remove any old selection
data.bBox­ ¬ [active: TRUE, rect: [x: 0.0, y: 0.0, w: data.pageWidth, h: data.pageHeight], prev: [x: 0.0, y: 0.0, w: data.pageWidth, h: data.pageHeight], p0: [0.0, 0.0], p1: [data.pageWidth, data.pageHeight]]; -- fake the box into the right state to select the whole page
PPreView.PVFeedback[data: data, v: BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE], op: paint]; -- paint new selection
};
$SetSelection => { -- select x y w h (in inches)
selAsStream: IO.STREAM = IO.RIS[ViewerTools.GetSelectionContents[]];
{
ENABLE IO.Error, IO.EndOfStream => {
Complain["Select \"x y w h\" (in inches) before clicking"];
GOTO NeverMind;
};
x: REAL ¬ IO.GetReal[selAsStream]*pointsPerInch;
y: REAL ¬ IO.GetReal[selAsStream]*pointsPerInch;
w: REAL ¬ IO.GetReal[selAsStream]*pointsPerInch;
h: REAL ¬ IO.GetReal[selAsStream]*pointsPerInch;
data.bBox.rect ¬ [x, y, w, h];
data.bBox.p0 ¬ [data.bBox.rect.x, data.bBox.rect.y];
data.bBox.p1 ¬ [data.bBox.p0.x+data.bBox.rect.w, data.bBox.p0.y+data.bBox.rect.h];
data.bBox.active ¬ TRUE;
};
PPreView.PVFeedback[data: data, v: viewer, op: change];
EXITS NeverMind => NULL;
};
$ShowSelection => NULL; -- show x y w h (in inches)
ENDCASE => ERROR;
ShowSelection[data, viewer];
EXITS
NoSel => Complain["No PreView Selection"];
};
GetTransformation: PUBLIC PROC [v: Viewer] RETURNS [Imager.Transformation] ~ {
RETURN[PPreView.bsStyle.GetTransforms[BiScrollers.QuaBiScroller[v]].clientToViewer];
};
GetTransformedRect: PUBLIC PROC [data: PPreView.Data, v: ViewerClasses.Viewer]
RETURNS [Imager.Rectangle]
~ {
ctv: ImagerTransformation.Transformation ¬ GetTransformation[v];
RETURN[ImagerTransformation.TransformRectangle[m: ctv, r: data.bBox.rect]];
};
ShowSelection: PUBLIC PROC [data: Data, v: Viewer] ~ {
IF data.bBox.active THEN {
rect: Imager.Rectangle ¬ GetTransformedRect[data, v];
ViewerTools.SetContents[data.selectionWViewer, IO.PutFR1["%6.3f",
IO.real[oneOverPointsPerInch*rect.w]]];
ViewerTools.SetContents[data.selectionHViewer, IO.PutFR1["%6.3f",
IO.real[oneOverPointsPerInch*rect.h]]];
MessageWindow.Append[
IO.PutFLR["Selection = [x: %g, y: %g, w: %g, h: %g] inches", LIST[
IO.real[oneOverPointsPerInch*data.bBox.rect.x],
IO.real[oneOverPointsPerInch*data.bBox.rect.y],
IO.real[oneOverPointsPerInch*data.bBox.rect.w],
IO.real[oneOverPointsPerInch*data.bBox.rect.h]]],
TRUE];
}
ELSE MessageWindow.Append["No PreView selection", TRUE];
};
Routines added for export to PPreViewOps
GetSelection: PUBLIC PROC [data: PPreView.Data] RETURNS [x, y, w, h: REAL] = {
IF data#NIL AND data.bBox.active THEN {
v: Viewer ¬ BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
x ¬ oneOverPointsPerInch*data.bBox.rect.x;
y ¬ oneOverPointsPerInch*data.bBox.rect.y;
w ¬ oneOverPointsPerInch*data.bBox.rect.w;
h ¬ oneOverPointsPerInch*data.bBox.rect.h;
}
ELSE RETURN [ 0, 0, 0, 0];
};
SetSelection: PUBLIC PROC [data: PPreView.Data, x, y, w, h: REAL] = {
units are inches for x, y, w and h
IF data#NIL THEN {
v: Viewer ¬ BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
rx: REAL ¬ x*pointsPerInch;
ry: REAL ¬ y*pointsPerInch;
rw: REAL ¬ w*pointsPerInch;
rh: REAL ¬ h*pointsPerInch;
data.bBox.rect ¬ [rx, ry, rw, rh];
data.bBox.p0 ¬ [data.bBox.rect.x, data.bBox.rect.y];
data.bBox.p1 ¬ [data.bBox.p0.x+data.bBox.rect.w, data.bBox.p0.y+data.bBox.rect.h];
data.bBox.active ¬ TRUE;
PPreView.PVFeedback[data: data, v: v, op: change];
};
};
ClearSelection: PUBLIC PROC [data: PPreView.Data] = {
IF data#NIL THEN {
v: Viewer ¬ BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
b: PPreView.BBoxState ¬ data.bBox;
IF b.active THEN PPreView.PVFeedback[data: data, v: v, op: remove]; -- kill grey bbox
b­ ¬ []; -- initialization default values cancel current clipping rectangle
};
};
DoSelectionOp: PUBLIC PROC [data: PPreView.Data, op: ATOM] = {
IF data#NIL THEN {
v: Viewer ¬ BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
SelectionOps[view: v, instanceData: data, classData: NIL, key: op];
};
};
GetTransform: PUBLIC PROC [data: PPreView.Data] RETURNS [Imager.Transformation] = {
RETURN[IF data#NIL THEN
bsStyle.GetTransforms[BiScrollers.QuaBiScroller[data.preViewer]].clientToViewer
ELSE NIL];
};
SetTransform: PUBLIC PROC [data: PPreView.Data, clientToViewer: Imager.Transformation] = {
IF data#NIL THEN
bsStyle.ChangeTransform[BiScrollers.QuaBiScroller[data.preViewer], clientToViewer, remember];
};
Stuff: PopUpButtons.PopUpButtonProc = { --PROC [viewer: Viewer, instanceData, classData, key: REF ANY]
data: Data ¬ NARROW[instanceData];
IF data.bBox.rect.w=0 OR data.bBox.rect.h=0 THEN {
Complain["Specify clipping box before clicking Stuff"];
RETURN;
};
data.stuffWithBorders ← mouseButton=blue;
data.stuffWithFit ← shift;
IF key=$Page OR key=$PageFitted OR key=$PageBordered OR key=$PageBorderedAndFitted THEN {
select entire page
IF data.bBox.active THEN PPreView.PVFeedback[data: data, v: BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE], op: remove]; -- remove any old selection
data.bBox­ ¬ [active: TRUE, rect: [x: 0.0, y: 0.0, w: data.pageWidth, h: data.pageHeight], p0: [0.0, 0.0], p1: [data.pageWidth, data.pageHeight]]; -- fake the box into the right state to select the whole page
PPreView.PVFeedback[data: data, v: BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE], op: paint]; -- paint new selection
}
ELSE IF data.bBox.rect.w=0 OR data.bBox.rect.h=0 THEN {
Complain["Specify clipping box before clicking Stuff"];
RETURN;
};
data.stuffWithBorders ¬ key=$Bordered OR key=$BorderedAndFitted OR key=$PageBordered OR key=$PageBorderedAndFitted;
data.stuffWithFit ¬ key=$Fitted OR key=$BorderedAndFitted OR key=$PageFitted OR key=$PageBorderedAndFitted;
PPreView.DoFileOps[op: stuff, viewer: BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE], data: data, fileName: NIL, clip: TRUE];
};
ToIP: PopUpButtons.PopUpButtonProc = { --PROC [viewer: Viewer, instanceData, classData, key: REF ANY]
ENABLE PFS.Error => GOTO PFSError;
success: BOOL ¬ FALSE;
data: Data ¬ NARROW[instanceData];
iv: Viewer ¬ BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
name: ROPE ¬ ViewerTools.GetSelectionContents[];
IF key=$Selection AND (data.bBox.rect.w=0 OR data.bBox.rect.h=0) THEN {
Complain["Specify clipping box before left clicking ToIP"];
RETURN;
};
IF Rope.Length[name]=0 OR Rope.Equal[name, data.container.file] THEN { -- no user specified name
dotIndex: INT ¬ Rope.SkipTo[data.container.label, 0, "."]; -- find the label, strip the dot
name ¬ IF dotIndex=Rope.Length[data.container.label] THEN data.container.label ELSE Rope.Substr[data.container.label, 0, Rope.SkipTo[data.container.label, 0, "."] ];
};
{
path: PFS.PATH ¬ PFS.PathFromRope[name];
wDir: PFS.PATH ¬ PFSNames.NarrowPath[ViewerOps.FetchProp[viewer: iv, prop: $PVWDir]];
path ¬ PFSNames.ExpandName[path, wDir];
name ¬ PFS.RopeFromPath[path];
};
[name, success] ¬ GetGenericFileName[name, "ip", LIST["mesa", "tioga", "script", "ais", "press", "pd", "res", "griffin", "gargoyle"] ];
IF success THEN {
PPreView.DoFileOps[op: ipMaster, viewer: iv, data: data, fileName: name, clip: key=$Selection];
DoFileOps will set data.abort if it fails to write
IF data.abort THEN data.abort ¬ FALSE
ELSE MessageWindow.Append[Rope.Cat["PreView: ", name, " written"], TRUE];
};
EXITS
PFSError => {
Complain[message: Rope.Concat["PFS Error while processing IP file name ", ViewerTools.GetSelectionContents[] ]];
RETURN; -- probably garbage fileName
};
};
GetGenericFileName: PROC [fileName, defaultExt: ROPE, illegalExts: LIST OF ROPE] RETURNS [fullName: ROPE ¬ NIL, success: BOOL ¬ TRUE] = {
path: PFS.PATH;
IF Rope.Length[fileName]=0 THEN {
Complain["No filename specified"];
RETURN[NIL, FALSE];
};
path ¬ PFS.PathFromRope[fileName ! PFS.Error => {
success ¬ FALSE;
Complain[IO.PutFR1["PFS Error during name expansion of %g", [rope[fileName]]]];
CONTINUE;
};
];
IF success THEN {
short: PFSNames.Component ¬ PFSNames.ShortName[path]; -- looking for base.ext
baseAndExtension: ROPE ¬ Rope.Substr[base: short.name.base, start: short.name.start, len: short.name.len]; -- should have base.ext
whereExt: INT ¬ Rope.FindBackward[s1: baseAndExtension, s2: ".", case: FALSE];
extRope: ROPE ¬ IF whereExt<0 THEN NIL ELSE Rope.Substr[base: baseAndExtension, start: whereExt+1];
IF extRope#NIL THEN FOR ropeList: LIST OF ROPE ¬ illegalExts, ropeList.rest UNTIL ropeList=NIL DO
IF Rope.Equal[ropeList.first, extRope, FALSE] THEN {
Complain[IO.PutFR["%g extension for %g files not allowed", [rope[extRope]], [rope[defaultExt]]] ];
success ¬ FALSE;
RETURN;
};
ENDLOOP
ELSE {
baseAndExtension ¬ Rope.Cat[baseAndExtension, ".", defaultExt];
path ¬ PFSNames.ReplaceShortName[path, [name: [base: baseAndExtension, start: 0, len: Rope.Length[baseAndExtension]], version: [versionKind: none, version: 0] ]]; -- can't provide a version here
};
fullName ¬ PFS.RopeFromPath[path];
};
};
Complain: PROC [message: ROPE, clearFirst: BOOL ¬ TRUE] = {
MessageWindow.Append[Rope.Concat["PreView: ", message], clearFirst];
MessageWindow.Blink[];
};
PageTurn: PopUpButtons.PopUpButtonProc = { --PROC [viewer: Viewer, instanceData, classData, key: REF ANY]
data: Data ¬ NARROW[instanceData];
SELECT key FROM
$FirstPage => DeltaPage[data, 1];
$TurnForward => DeltaPage[data, MIN[MAX[data.pageNumber+1, 1], (data.lastPageNumber)]];
$TurnBackward => DeltaPage[data, MIN[MAX[data.pageNumber-1, 1], (data.lastPageNumber)]];
$LastPage => DeltaPage[data, data.lastPageNumber];
$FromTiogaSelection => {
stream: IO.STREAM ¬ IO.RIS[ViewerTools.GetSelectionContents[]];
where: INT ¬ stream.GetInt[ ! IO.Error, IO.EndOfStream => {
Complain["Select a valid page number"];
GOTO Abort;
};
];
DeltaPage[data, MIN[MAX[where, 1], (data.lastPageNumber)]];
};
ENDCASE => ERROR;
EXITS
Abort => NULL;
};
NormalizePageNumber: Sliders.FilterProc = {
data: Data ¬ NARROW[clientData];
lastPageNumber: REAL ¬ data.lastPageNumber;
RETURN [Real.Round[value*lastPageNumber]/lastPageNumber];
};
PageNumberSlider: Sliders.SliderProc = {
data: Data ¬ NARROW[clientData];
lastPageNumber: REAL ¬ data.lastPageNumber;
SELECT reason FROM
abort => {
ViewerTools.SetContents[data.pageNumberViewer, IO.PutFR1["%-g", IO.int[data.pageNumber]]];
};
move => {
ViewerTools.SetContents[data.pageNumberViewer, IO.PutFR1["%-g", IO.int[Real.Round[lastPageNumber*value]]]];
};
set => {
DeltaPage[data, MIN[MAX[Real.Round[lastPageNumber*value], 1], data.lastPageNumber]];
};
ENDCASE;
};
DeltaPage: PROCEDURE [data: Data, newvalue: INT] = {
lastPageNumber: REAL ¬ data.lastPageNumber;
ViewerTools.SetContents[data.pageNumberViewer, IO.PutFR1["%-g", IO.int[newvalue]]];
Sliders.SetContents[data.pageNumberSlider, newvalue/lastPageNumber];
IF data.pageNumber=newvalue THEN RETURN; -- just did the initialization needed
data.pageNumber ¬ newvalue;
WITH data SELECT FROM
ipData: IPData => NULL;
ENDCASE => NewPageData[data, newvalue]; -- Load new page into data
ViewerOps.PaintViewer[viewer: data.preViewer, hint: all, clearClient: TRUE];
repaint caption in case of multiple files being viewed
ViewerOps.PaintViewer[viewer: data.container, hint: caption, clearClient: FALSE];
};
IPPreView: PUBLIC Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPE ← NIL];
[result, msg] ¬ LetHerRip[cmd, ip];
};
AISPreView: PUBLIC Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPE ← NIL];
[result, msg] ¬ LetHerRip[cmd, ais];
};
RESPreView: PUBLIC Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPE ← NIL];
[result, msg] ¬ LetHerRip[cmd, res];
};
AnyPreView: PUBLIC Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPE ← NIL];
[result, msg] ¬ LetHerRip[cmd, any];
};
LetHerRip: PROC [cmd: Commander.Handle, mustBe: PPreView.FileType ¬ ip] RETURNS [result: REF ¬ NIL, msg: ROPE ¬ NIL] = {
ENABLE Convert.Error => {
result ¬ $Failure;
msg ¬ "PreView: Specify positive real numbers for page sizes";
GOTO Abort;
};
xSize: REAL ¬ 8.5; -- default size in inches
ySize: REAL ¬ 11.0; -- default size in inches
args: PPreView.NameList ¬ LIST[]; --LIST OF ROPE
nameList: PPreView.NameList ¬ LIST[]; --LIST OF ROPE
switches: PPreView.Switches ¬ ALL[FALSE];
argLength: NAT ¬ 0;
[list: args, length: argLength] ¬ CommanderOps.ParseToList[cmd];
IF args = NIL OR argLength < 1 THEN RETURN[$Failure, "Unable to parse command line"];
UNTIL args=NIL DO
IF Rope.Fetch[base: args.first, index: 0] = '- THEN {
tChar: CHAR;
IF (tChar ¬ Ascii.Upper[Rope.Fetch[base: args.first, index: 1]]) IN PPreView.SwitchRange THEN switches[tChar] ¬ TRUE;
SELECT tChar FROM
'S => { -- command arguments are two real numbers specifying page size
args ¬ args.rest;
xSize ¬ Convert.RealFromRope[args.first];
args ¬ args.rest;
ySize ¬ Convert.RealFromRope[args.first];
IF xSize<=0.1 OR ySize <=0.1 THEN {
result ¬ $Failure;
msg ¬ "PreView: Specify positive real numbers for page sizes";
GOTO Abort;
}
ELSE args ¬ args.rest;
};
'C => {
command arguments are RGB components of a single color image
FOR a: PPreView.NameList ¬ args.rest, a.rest UNTIL a=NIL DO nameList ¬ CONS[a.first, nameList];
ENDLOOP;
[] ¬ CreatePreViewer[fileNames: nameList, switches: switches, xSize: xSize, ySize: ySize, mustBe: mustBe];
GOTO Done;
};
'P => {
command arguments are separate pages of a multipage image document
allow StarExpansion for the individual arguments
FOR rl: PPreView.NameList ¬ args.rest, rl.rest UNTIL rl = NIL DO
expandedList: LIST OF ROPE ¬ ExpandStars[rl.first, cmd.err];
nameList ¬ RopeList.Append[nameList, expandedList];
ENDLOOP;
[] ¬ CreatePreViewer[fileNames: nameList, switches: switches, xSize: xSize, ySize: ySize, mustBe: mustBe];
GOTO Done;
};
ENDCASE => args ¬ args.rest;
}
ELSE EXIT; -- hit first non-switch argument
ENDLOOP;
FOR rl: PPreView.NameList ¬ args, rl.rest UNTIL rl = NIL DO --open a PreViewer on each file
expandedList: LIST OF ROPE ¬ ExpandStars[rl.first, cmd.err];
FOR nl: PPreView.NameList ¬ expandedList, nl.rest UNTIL nl = NIL DO
[] ¬ CreatePreViewer[fileNames: nl, switches: switches, xSize: xSize, ySize: ySize, mustBe: mustBe];
ENDLOOP;
ENDLOOP;
EXITS
Done, Abort => NULL;
};
ExpandStars: PROC [rope: ROPE, err: IO.STREAM] RETURNS [LIST OF ROPE] ~ {
ENABLE PFS.Error => {CommanderOps.Failed[error.explanation]};
head: LIST OF ROPE ~ LIST[NIL];
last: LIST OF ROPE ¬ head;
path: PFS.PATH ¬ PFS.PathFromRope[rope];
short: PFSNames.Component ~ path.ShortName[];
Each: PFS.InfoProc ~ {
IF fileType # PFS.tDirectory THEN last ¬ last.rest ¬ LIST[PFS.RopeFromPath[fullFName]];
};
IF short.version=[none] THEN path ¬ path.ReplaceShortName[[short.name, [highest]]];
PFS.EnumerateForInfo[path, Each];
IF head.rest = NIL THEN {
IF Rope.Find[rope, "*"] >= 0 THEN {
IO.PutRope[err, "No files for pattern "];
IO.PutRope[err, rope];
IO.PutRope[err, "\n"];
}
ELSE last ¬ last.rest ¬ LIST[rope]; -- return the original rope which may later be extended and a file actually found
};
RETURN [head.rest]
};
PVChangedProc: UserProfile.ProfileChangedProc = {
extensionList ¬ UserProfile.ListOfTokens[key: "PreView.Extensions", default: defaultExtensionList];
};
PVUserAction: BiScrollers.BSUserActionProc = {
BSUserActionProc: TYPE = PROC [bs: BiScroller, input: LORA];
iv: Viewer ¬ bs.style.QuaViewer[bs: bs, inner: TRUE];
data: Data ¬ NARROW[BiScrollers.ClientDataOfViewer[iv]];
BiScrollers.DoBSUserAction[bs, input];
ShowSelection[data, iv];
};
Init: PROC = {
pvTIP: TIPUser.TIPTable ¬
TIPUser.InstantiateNewTIPTable["PreView.tip"];
bsStyle ¬ BiScrollers.GetStyle[]; -- default gets BiScrollersButtonned
pvBSClass ¬ bsStyle.NewBiScrollerClass[[
flavor: $PreViewer,
extrema: PPreView.PVExtremaProc,
notify: PPreView.PVNotify,
paint: PPreView.PVPaint,
destroy: PPreView.PVDestroy,
get: PPreView.PVGetName,
bsUserAction: PVUserAction,
tipTable: pvTIP,
cursor: crossHairsCircle,
mayStretch: TRUE, -- OK to scale X and Y differently
vanilla: PPreView.PVBasicTransformProc, --proc which provides the vanilla transform for BiScrollers
preserve: [X: 0.5, Y: 0.5] --this specifies that the center of the viewers stays fixed when viewer size changes. Also, scale/rotate around the center
]];
selClass ¬ PopUpButtons.MakeClass[[
proc: SelectionOps,
choices: LIST[
[$Center, "Center PreView selection in viewer"],
[$CenterAndFit, "Center and fit PreView selection in viewer"],
[$CenterAndScale, "Center and scale PreView selection to viewer"],
[$SelectPage, "Set PreView selection to entire page"],
[$SetSelection, "Set PreView selection to \"x y w h\" (in inches)"],
[$ShowSelection, "Show PreView selection (in inches) in Message Window"]
],
doc: "Selection: selection operations for current page"
]];
stuffClass ¬ PopUpButtons.MakeClass[[
proc: Stuff,
choices: LIST[
[$Selection, "Stuff PreView selection"],
[$Page, "Stuff entire page"],
PopUpButtons.nullChoice,
[$Fitted, "Fit and Stuff PreView selection"],
[$PageFitted, "Fit and Stuff entire page"],
PopUpButtons.nullChoice,
[$Bordered, "Stuff PreView selection with borders"],
[$PageBordered, "Stuff entire page with borders"],
PopUpButtons.nullChoice,
[$BorderedAndFitted, "Fit and Stuff PreView selection with borders"],
[$PageBorderedAndFitted, "Fit and Stuff entire page with borders"],
PopUpButtons.nullChoice
],
doc: "Stuff: stuff PreView images into Tioga document"
]];
toIPClass ¬ PopUpButtons.MakeClass[[
proc: ToIP,
choices: LIST[
[$Selection, "PreView selection to IP file"],
[$Page, "Entire page to IP file"]
],
doc: "ToIP: convert to Interpress and store in (Tioga) selected filename "
]];
pageClass ¬ PopUpButtons.MakeClass[[
proc: PageTurn,
choices: LIST[
[$TurnForward, "Display next page"],
PopUpButtons.nullChoice,
[$TurnBackward, "Display previous page"],
[$FirstPage, "Display first page"],
[$FromTiogaSelection, "Display (Tioga) selected page number"],
[$LastPage, "Display last page"]
],
doc: "Page: move among pages in PreView document"
]];
UserProfile.CallWhenProfileChanges[proc: PVChangedProc]; -- PVChangedProc called immediately after this registration call
Commander.Register[key: "IPPreView", proc: IPPreView, doc: "Create a PreViewer for an Interpress file" ];
Commander.Register[key: "AISPreView", proc: AISPreView, doc: "Create a PreViewer for an AIS file" ];
Commander.Register[key: "RESPreView", proc: RESPreView, doc: "Create a PreViewer for an RES file(s)" ];
Commander.Register[key: "PreView", proc: AnyPreView, doc: "Create a PreViewer for an image file(s)" ];
};
Init[];
END.