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.PATH ← NIL]
~ {
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];
};
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;
};
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: REF ← NIL, msg: ROPE ← NIL];
[result, msg] ¬ LetHerRip[cmd, ip];
};
AISPreView:
PUBLIC Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL];
[result, msg] ¬ LetHerRip[cmd, ais];
};
RESPreView:
PUBLIC Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL];
[result, msg] ¬ LetHerRip[cmd, res];
};
AnyPreView:
PUBLIC Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, 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)" ];
};
END.