PreViewTool.mesa
Last Edited by: Ken Pier, May 15, 1986 11:24:20 am PDT
DIRECTORY
AIS USING [Error, OpenFile, ReadRaster],
Ascii USING [Upper],
BasicTime USING [GMT, nullGMT],
BiScrollers USING [Align, BiScroller, BiScrollerClass, BiScrollerStyle, bsMenu, GetStyle, QuaBiScroller, QuaViewer, Scale],
Buttons USING [Button, ButtonProc, Create],
ChoiceButtons USING [BuildTextPrompt, PromptDataRef],
Commander USING [CommandProc, Handle, Register],
CommandTool USING [Failed, ParseToList],
Containers USING [ChildXBound, ChildYBound, Create],
FileNames USING [CurrentWorkingDirectory, Directory, GetShortName, ResolveRelativePath, Tail, StripVersionNumber],
FS USING [ComponentPositions, Error, ExpandName, FileInfo, OpenFile, OpenFileFromStream, StreamOpen],
GriffinImageUtils USING [ReadGriffinImage],
Icons USING [IconFlavor, NewIconFromFile],
Imager USING [Color, Error],
ImagerBackdoor USING [MakeStipple],
ImagerColorOperator USING [BlackColorModel, GrayLinearColorModel, RGBLinearColorModel],
ImagerMemory USING [NewMemoryContext],
ImagerPixelArray USING [Error, FromAIS, Join3AIS, MaxSampleValue],
Interpress USING [LogProc, Open, OpenMaster],
IO USING [EndOfStream, Error, GetInt, int, PutFR, real, RIS, SetIndex, STREAM],
IPMaster USING [Error, GetHeader],
List USING [Append, Remove],
Menus USING [AppendMenuEntry, CopyMenu, CreateEntry, Menu],
MessageWindow USING [Append, Blink],
PDFileReader USING [Error, FromStream, Handle, Rep, Warning],
PDImageReader USING [GetPageStructure, Handle, Rep],
PressReader USING [FromOpenFile, PressReaderError],
PreView USING [AISData, AISState, AISStateRep, BBoxStateRep, Data, DoFileOps, FileInfo, FileInfoRep, FileType, GData, IPData, NameList, PDData, PressData, PVBasicTransformProc, PVDestroy, PVExtremaProc, PVFeedback, PVGetName, PVNotify, PVPaint, Rep, Switches, SwitchRange],
PreViewClient USING [],
Process USING [Abort],
Real USING [FDiv, RoundI],
Rope USING [Cat, Concat, Equal, Fetch, Length, ROPE, SkipTo, Substr],
Rules USING [Create, Rule],
ShowPress USING [Handle, Open, ShowPressError],
Sliders USING [Create, FilterProc, SetContents, SliderProc],
TiogaMenuOps USING [Open],
TIPUser USING [InstantiateNewTIPTable, TIPTable],
UserProfile USING [Boolean, CallWhenProfileChanges, ListOfTokens, ProfileChangedProc],
ViewerClasses USING [Viewer],
ViewerOps USING [AddProp, FetchProp, PaintViewer],
ViewerSpecs USING [captionHeight, menuBarHeight, scrollBarW, windowBorderSize],
ViewerTools USING [GetContents, GetSelectionContents, MakeNewTextViewer, SetContents];
PreViewTool: CEDAR MONITOR
IMPORTS AIS, Ascii, BiScrollers, --ChoiceButtons, --Buttons, Commander, CommandTool, Containers, FileNames, FS, GriffinImageUtils, Icons, Imager, ImagerBackdoor, ImagerColorOperator, ImagerMemory, ImagerPixelArray, Interpress, IO, IPMaster, List, Menus, MessageWindow, PDFileReader, PDImageReader, PressReader, PreView, Process, Real, Rope, Rules, ShowPress, Sliders, TiogaMenuOps, TIPUser, UserProfile, ViewerOps, ViewerSpecs, ViewerTools
EXPORTS PreView, PreViewClient = BEGIN
FileType: TYPE = PreView.FileType;
Data: TYPE = PreView.Data;
Rep: TYPE = PreView.Rep;
IPData: TYPE = PreView.IPData;
PressData: TYPE = PreView.PressData;
PDData: TYPE = PreView.PDData;
AISData: TYPE = PreView.AISData;
GData: TYPE = PreView.GData;
AISState: TYPE = PreView.AISState;
pvIcon: Icons.IconFlavor = Icons.NewIconFromFile["PreView.icons", 0];
ipPrefix: Rope.ROPE = "Interpress/";
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
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];
LProc: Interpress.LogProc = {
[master: OpenMaster, class: ErrorClass, code: ATOM, explanation: ROPE]
MessageWindow.Append[message: Rope.Concat["InterpressMaster Error: ", explanation], clearFirst: TRUE];
MessageWindow.Blink[];
};
WhichFileType: PROC [fileName: Rope.ROPE] RETURNS [PreView.FileInfo] = {
expects a file name which may not have an extension; will try extensions .ip, .interpress, .press, .pd, .ais .griffin in that order
fname: Rope.ROPENIL;
created: BasicTime.GMT ← BasicTime.nullGMT;
[fullFName: fname, created: created] ← FS.FileInfo[name: fileName ! FS.Error => CONTINUE];
IF fname#NIL THEN RETURN[DiscoverFileType[fname, created]];-- named file exists
did not find full name, so try a few extensions
IF extensionList = NIL THEN extensionList ← defaultExtensionList;
FOR l: LIST OF Rope.ROPE ← extensionList, l.rest UNTIL l=NIL DO
fname ← Rope.Cat[fileName, ".", l.first];
[fullFName: fname, created: created] ← FS.FileInfo[name: fname ! FS.Error => LOOP];
RETURN[DiscoverFileType[fname, created]]; -- file with extension exists
ENDLOOP;
RETURN[NEW[PreView.FileInfoRep ← [] ]]; -- file does not exist
};
DiscoverFileType: PROC [fileName: Rope.ROPE, created: BasicTime.GMT] RETURNS [fileInfo: PreView.FileInfo] = {
given a filename, figure out if it is an interpress, press, PD, AIS, or Griffin file (or none)
uses the extension as a hint as to the filetype.
fileInfo ← NEW[PreView.FileInfoRep ← [] ]; --initialize to fileType none, etc.
{
ref: REF ANYNIL;
s: IO.STREAMFS.StreamOpen[fileName: fileName ! FS.Error => GOTO None];
cp: FS.ComponentPositions ← FS.ExpandName[fileName].cp;
ext: Rope.ROPE ← Rope.Substr[base: fileName, start: cp.ext.start, len: cp.ext.length];
SELECT TRUE FROM
Rope.Equal[s1: ext, s2: "ip", case: FALSE], Rope.Equal[s1: ext, s2: "interpress", case: FALSE] => IF (ref ← IPMaster.GetHeader[stream: s, prefix: ipPrefix ! IPMaster.Error => CONTINUE;])#NIL THEN {fileInfo^ ← [ip, fileName, created, ref]; RETURN;};
Rope.Equal[s1: ext, s2: "press", case: FALSE] => IF (ref ← PressReader.FromOpenFile[openFile: FS.OpenFileFromStream[self: s] ! PressReader.PressReaderError => CONTINUE;])#NIL THEN {fileInfo^ ← [press, fileName, created, ref]; RETURN;};
Rope.Equal[s1: ext, s2: "pd", case: FALSE] => IF (ref ← PDFileReader.FromStream[stream: s ! PDFileReader.Error, PDFileReader.Warning => CONTINUE;])#NIL THEN {fileInfo^ ← [pd, fileName, created, ref]; RETURN;};
Rope.Equal[s1: ext, s2: "ais", case: FALSE] => IF (ref ← AIS.OpenFile[name: fileName ! AIS.Error, FS.Error => CONTINUE;])#NIL THEN {fileInfo^ ← [ais, fileName, created, ref]; RETURN;};
Rope.Equal[s1: ext, s2: "griffin", case: FALSE] => IF (ref ← GriffinImageUtils.ReadGriffinImage[name: fileName ! FS.Error => CONTINUE;])#NIL THEN {fileInfo^ ← [griffin, fileName, 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 (ref ← IPMaster.GetHeader[stream: s, prefix: ipPrefix ! IPMaster.Error => CONTINUE;])#NIL THEN {fileInfo^ ← [ip, fileName, created, ref]; RETURN;};
IO.SetIndex[s,0]; --reset stream for subsequent calls
IF (ref ← PressReader.FromOpenFile[openFile: FS.OpenFileFromStream[self: s] ! PressReader.PressReaderError => CONTINUE;])#NIL THEN {fileInfo^ ← [press, fileName, created, ref]; RETURN;};
IO.SetIndex[s,0]; --reset stream for subsequent calls
IF (ref ← PDFileReader.FromStream[stream: s ! PDFileReader.Error, PDFileReader.Warning => CONTINUE;])#NIL THEN {fileInfo^ ← [pd, fileName, created, ref]; RETURN;};
IF (ref ← AIS.OpenFile[name: fileName ! AIS.Error, FS.Error => CONTINUE;])#NIL THEN {fileInfo^ ← [ais, fileName, created, ref]; RETURN;};
IF (ref ← GriffinImageUtils.ReadGriffinImage[name: fileName ! FS.Error => CONTINUE;])#NIL THEN {fileInfo^ ← [griffin, fileName, created, ref]; RETURN;};
fileInfo^ ← [none, NIL, BasicTime.nullGMT, NIL];
EXITS
None => fileInfo^ ← [none, NIL, BasicTime.nullGMT, NIL];
};
};
CreatePreViewer: PUBLIC PROC [fileNames: PreView.NameList, switches: PreView.Switches] RETURNS [preViewer: ViewerClasses.Viewer] = {
bs: BiScrollers.BiScroller ← NIL;
promptData: ChoiceButtons.PromptDataRef ← NIL;
data: Data ← NIL;
curIndent, topLine: INTEGER ← 0;
button: Buttons.Button;
menu: Menus.Menu;
rule: Rules.Rule;
fileInfo: PreView.FileInfo ← NIL;
versionSpecified: BOOL ← fileNames.rest#NIL OR Rope.SkipTo[s: fileNames.first, skip: "!"]#Rope.Length[fileNames.first]; -- don't do versions with multiple AIS files
fileInfo ← WhichFileType[fileNames.first]; -- adds file extension to the named file if needed
SELECT fileInfo.filetype FROM
ip => {
ipmaster: Interpress.OpenMaster ← Interpress.Open[fileName: fileInfo.fullFName, logProc: LProc, logData: NIL !
FS.Error => {
MessageWindow.Append[message: error.explanation, clearFirst: TRUE];
GOTO Quit;
};
IPMaster.Error => { --ErrorDesc: TYPE = RECORD[code: ATOM, explanation: ROPE, index: INT ← 0]
MessageWindow.Append[message: Rope.Cat[error.explanation, " for ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
Imager.Error => { --ErrorDesc: TYPE = RECORD [code: ATOM, explanation: ROPE]
MessageWindow.Append[message: Rope.Cat[error.explanation, " for ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
IO.Error, IO.EndOfStream => {
MessageWindow.Append[message: Rope.Cat["IO Stream Error for ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
];
IF ipmaster.pages=0 THEN {
MessageWindow.Append[message: Rope.Concat["Zero pages in ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
data ← NEW[ip Rep ← [fileInfo: fileInfo, pageNumber: 1, lastPageNumber: ipmaster.pages, pageHeight: IF switches['V] THEN versatecPageHeight ELSE defaultPageHeight, pageWidth: IF switches['V] THEN versatecPageWidth ELSE defaultPageWidth, switches: switches, kind: ip[ipMaster: ipmaster]]]; -- pages IN [1..end]
};
press => {
pressfile: ShowPress.Handle ← ShowPress.Open[fileInfo.fullFName !
FS.Error => {
MessageWindow.Append[message: error.explanation, clearFirst: TRUE];
GOTO Quit;
};
ShowPress.ShowPressError => {
SELECT code FROM
$CantReadFile => MessageWindow.Append[message: Rope.Concat["Can't open PreViewer on ", fileInfo.fullFName], clearFirst: TRUE];
$CantFindFonts => MessageWindow.Append[message: Rope.Concat["Can't Find Fonts for ", fileInfo.fullFName], clearFirst: TRUE];
ENDCASE => MessageWindow.Append[message: Rope.Concat["ShowPressError on ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
Imager.Error => {
MessageWindow.Append[message: Rope.Cat[error.explanation, " for ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
];
IF pressfile.lastPart-1=0 THEN {
MessageWindow.Append[message: Rope.Concat["Zero pages in ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
data ← NEW[press Rep ← [fileInfo: fileInfo, pageNumber: 1, lastPageNumber: pressfile.lastPart-1, pageHeight: defaultPageHeight, pageWidth: defaultPageWidth, switches: switches, kind: press[presshandle: pressfile]]];
};
pd => {
pdfile: PDFileReader.Handle ← NARROW[fileInfo.ref]; -- DiscoverFileType did a PDFileReader.FromStream already
pdData: PDData ← NEW[pd Rep ← [fileInfo: fileInfo, pageNumber: 1, pageHeight: (pdfile.herald.imageSSize/pdfile.herald.sResolution)*screenResolution, pageWidth: (pdfile.herald.imageFSize/pdfile.herald.fResolution)*screenResolution, switches: switches, kind: pd[pdhandle: pdfile, scalePD: NOT switches['F]]]];
[pdData.pageStructure, pdData.lastPageNumber] ← PDImageReader.GetPageStructure[pdfile];
pdData.scaleFactors ← [x: screenResolution/pdfile.herald.fResolution, y: screenResolution/pdfile.herald.sResolution];
IF switches['F] THEN { -- "full" scale, so ExtremaProc has to know about pixels
pdData.pageWidth ← pdData.pageWidth*(pdfile.herald.fResolution/screenResolution);
pdData.pageHeight ← pdData.pageHeight*(pdfile.herald.sResolution/screenResolution);
};
IF pdData.lastPageNumber=0 THEN {
MessageWindow.Append[message: Rope.Concat["Zero pages in ", fileInfo.fullFName], clearFirst: TRUE];
GOTO Quit;
};
data ← pdData;
};
ais => {
aisData: AISData ← NEW[ais Rep ← [fileInfo: fileInfo, pageNumber: 1, lastPageNumber: 1, switches: switches, kind: ais[state: NEW[PreView.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.
IF (fileInfo.fullFName ← InitAIS[data: aisData, fileNames: fileNames, singleName: fileInfo.fullFName]) = NIL THEN GOTO Quit;
aisData.pageHeight ← aisData.state.scans;
aisData.pageWidth ← aisData.state.pixels;
data ← aisData;
};
griffin => {
data ← NEW[griffin Rep ← [fileInfo: fileInfo, pageNumber: 1, lastPageNumber: 1, pageHeight: defaultPageHeight, pageWidth: defaultPageWidth, switches: switches, kind: griffin[image: NARROW[fileInfo.ref]]]]; -- single page
};
ENDCASE => {
IF tryTioga THEN {
[] ← TiogaMenuOps.Open[fileNames.first];
RETURN;
}
ELSE MessageWindow.Append[message: Rope.Concat["Unknown file or filetype: ", fileNames.first], clearFirst: TRUE];
GOTO Quit;
};
menu ← Menus.CopyMenu[BiScrollers.bsMenu];
Menus.AppendMenuEntry[menu: menu, entry: Menus.CreateEntry[name: "Selection", proc: SelectionOps, clientData: data, documentation: "Left: center selection, Middle: center and fit selection, Right: select entire page "]];
data.bBox ← NEW[PreView.BBoxStateRep ← []];
data.iMemContext ← ImagerMemory.NewMemoryContext[];
data.fileInfo.fullFName ← FS.ExpandName[name: data.fileInfo.fullFName].fullFName;
data.container ← preViewer ← Containers.Create[
info: [
name: IF versionSpecified THEN Rope.Concat["PreView: ", data.fileInfo.fullFName] ELSE Rope.Cat["PreView: ", FileNames.StripVersionNumber[data.fileInfo.fullFName], " (!", FileNames.Tail[data.fileInfo.fullFName, '!], ")"],
label: FileNames.GetShortName[path: data.fileInfo.fullFName, stripOffVersionNumber: TRUE],
iconic: TRUE,
menu: menu,
icon: pvIcon,
data: data,
scrollable: FALSE],
paint: FALSE ];
button ← Buttons.Create[
info: [
name: "Stuff",
wx: curIndent,
wy: topLine,
border: FALSE,
parent: data.container],
proc: Stuff,
clientData: data,
documentation: "Click to stuff an IP node at the current selection",
paint: FALSE];
curIndent ← button.wx + button.ww;
button ← Buttons.Create[
info: [
name: "ToIP",
wx: curIndent,
wy: topLine,
border: FALSE,
parent: data.container],
proc: ToIP,
clientData: data,
documentation: "Click to create an IP Master",
paint: FALSE];
curIndent ← button.wx + button.ww;
button ← Buttons.Create[
info: [
name: "FirstPage",
wx: curIndent,
wy: topLine,
border: FALSE,
parent: data.container],
proc: FirstPage,
clientData: data,
documentation: "Left-click for the first page of the file",
paint: FALSE];
curIndent ← button.wx + button.ww;
button ← Buttons.Create[
info: [
name: "TurnPage",
wx: curIndent,
wy: topLine,
border: FALSE,
parent: data.container],
proc: TurnPage,
clientData: data,
documentation: "Left-click for next page; Right-click for previous page",
paint: FALSE];
curIndent ← button.wx + button.ww;
button ← Buttons.Create[
info: [
name: "LastPage",
wx: curIndent,
wy: topLine,
border: FALSE,
parent: data.container],
proc: LastPage,
clientData: data,
documentation: "Left-click for last page of the file",
paint: FALSE];
curIndent ← button.wx + button.ww;
button ← Buttons.Create[
info: [
name: "Page",
wx: curIndent,
wy: topLine,
border: FALSE,
parent: data.container],
proc: PageNumber,
clientData: data,
documentation: "Left-click for selected page number",
paint: FALSE];
curIndent ← button.wx + button.ww;
data.pageNumberViewer ← ViewerTools.MakeNewTextViewer[
info: [
wx: curIndent,
wy: topLine+ViewerSpecs.windowBorderSize,
ww: 40,
wh: ViewerSpecs.scrollBarW+2*ViewerSpecs.windowBorderSize,
parent: data.container,
border: FALSE,
scrollable: FALSE],
paint: FALSE];
curIndent ← data.pageNumberViewer.wx + data.pageNumberViewer.ww;
data.pageNumberSlider ← Sliders.Create[
info: [
wx: curIndent,
wy: topLine,
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];
Containers.ChildXBound[data.container, data.pageNumberSlider];
rule ← Rules.Create[
info: [
parent: data.container,
wx: 0,
wy: topLine+ViewerSpecs.captionHeight+4*ViewerSpecs.windowBorderSize,
wh: ViewerSpecs.menuBarHeight]];
Containers.ChildXBound[data.container, rule];
promptData ← ChoiceButtons.BuildTextPrompt[
viewer: data.container, x: 0, y: rule.wy+rule.wh+2*ViewerSpecs.windowBorderSize, title: "ScratchPad: ", textViewerWidth: 500];
rule ← Rules.Create[
info: [
parent: data.container,
wx: 0,
wy: promptData.newy+4*ViewerSpecs.windowBorderSize,
wh: ViewerSpecs.menuBarHeight]];
Containers.ChildXBound[data.container, rule];
bs ← bsStyle.CreateBiScroller[
class: pvBSClass,
info: [
parent: data.container,
wx: 0,
wy: rule.wy+rule.wh+2*ViewerSpecs.windowBorderSize,
border: FALSE,
scrollable: FALSE,
data: data],
paint: FALSE ];
data.preViewer ← bs.QuaViewer[inner: FALSE];
ViewerOps.AddProp[viewer: bs.QuaViewer[inner: TRUE], prop: $PVWDir, val: FileNames.CurrentWorkingDirectory[]];
Containers.ChildXBound[data.container, data.preViewer];
Containers.ChildYBound[data.container, data.preViewer];
ViewerOps.PaintViewer[viewer: data.container, hint: all, clearClient: TRUE];
DeltaPage[data, 1];
PVListAdd[LIST[data]];
EXITS
Quit => {
MessageWindow.Blink[];
RETURN;
};
};
InitAIS: PROC [data: AISData, fileNames: PreView.NameList, singleName: Rope.ROPE] RETURNS [newName: Rope.ROPENIL] = {
ENABLE {
FS.Error => {
MessageWindow.Append[message: error.explanation, clearFirst: TRUE];
newName ← NIL; CONTINUE;
};
ImagerPixelArray.Error => {
MessageWindow.Append[message: error.explanation, clearFirst: TRUE];
newName ← NIL; CONTINUE;
};
};
state: AISState ← data.state;
IF fileNames.rest#NIL THEN { --multi-file color AIS image
redName: Rope.ROPE ← Rope.Concat[fileNames.rest.rest.first, ".ais"];
grnName: Rope.ROPE ← Rope.Concat[fileNames.rest.first, ".ais"];
bluName: Rope.ROPE ← Rope.Concat[fileNames.first, ".ais"];
IF data.abort THEN {data.abort ← FALSE; RETURN[NIL]}; -- set asynchronously by StopIfYouCan
state.pa← ImagerPixelArray.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
IF data.abort THEN {data.abort ← FALSE; RETURN[NIL]}; -- set asynchronously by StopIfYouCan
state.op ← ImagerColorOperator.RGBLinearColorModel[maxSampleValue: ImagerPixelArray.MaxSampleValue[pa: state.pa, i: 0]];
newName ← Rope.Concat[Rope.Substr[base: fileNames.first, start: 0, len: Rope.SkipTo[s: fileNames.first, pos: 0, skip: "-"]], "-*.ais"];
}
ELSE { -- single file AIS image
bps: [0..16] ← 0;
IF data.abort THEN {data.abort ← FALSE; RETURN[NIL]}; -- set asynchronously by StopIfYouCan
state.pa← ImagerPixelArray.FromAIS[singleName];
[scanCount: state.scans, scanLength: state.pixels, scanMode: , bitsPerPixel: bps] ← AIS.ReadRaster[state.openFile]^;
IF data.abort THEN {data.abort ← FALSE; RETURN[NIL]}; -- set asynchronously by StopIfYouCan
state.op ← IF bps#0 THEN ImagerColorOperator.GrayLinearColorModel[sWhite: ImagerPixelArray.MaxSampleValue[pa: state.pa, i: 0], sBlack: 0.0] ELSE ImagerColorOperator.BlackColorModel[clear: FALSE];
newName ← singleName;
};
state.active ← TRUE;
};
StopIfYouCan: ENTRY Buttons.ButtonProc = { --pvList is a global list of all PreView Datas
ENABLE UNWIND => NULL;
KillIt: PROC [data: Data] = TRUSTED {
IF data.process#NIL THEN {Process.Abort[data.process]; data.process ← NIL};
};
FOR l: LIST OF REF ANY ← pvList, l.rest UNTIL l=NIL DO
data: PreView.Data ← NARROW[l.first];
data.abort ← TRUE; -- this aborts PD file painting, which can't properly use Process.Abort
WITH data SELECT FROM
ipData: IPData => KillIt[ipData];
aisData: AISData => KillIt[aisData];
gData: GData => KillIt[gData];
pressData: PressData => NULL; -- punt on Press files
pdData: PDData => NULL;
ENDCASE => ERROR;
ENDLOOP;
};
ResetProcess: PUBLIC ENTRY PROC [data: Data] = {
this PROC is here solely to synchronize the Paint Proc in PreViewImpl with StopIfYouCan
ENABLE UNWIND => NULL;
data.process ← NIL;
};
SelectionOps: Buttons.ButtonProc = {
data: Data ← NARROW[clientData];
viewer: ViewerClasses.Viewer ← BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
SELECT mouseButton FROM
red => { -- 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];
};
yellow => { -- 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 NOT shift THEN BiScrollers.Scale[bs: BiScrollers.QuaBiScroller[data.preViewer], op: [variant: reset[] ], paint: FALSE ];
BiScrollers.Scale[bs: BiScrollers.QuaBiScroller[data.preViewer], op: [variant: byArg[arg: MIN[vW/sW, vH/sH]]], paint: TRUE ];
};
blue => { -- select entire page
IF data.bBox.active THEN PreView.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], mode: waitingForSecondPoint, x0: 0.0, y0: 0.0, lastX: data.pageWidth, lastY: data.pageHeight]; -- fake the box into the right state to select the whole page
PreView.PVFeedback[data: data, v: BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE], op: paint]; -- paint new selection
};
ENDCASE => ERROR;
EXITS
NoSel => MessageWindow.Append["No PreView Selection", TRUE];
};
ToIP: Buttons.ButtonProc = { --parent: REF ANY, clientData: REF ANY, mouseButton: MouseButton ← red, shift, control: BOOL ← FALSE
data: Data ← NARROW[clientData];
iv: ViewerClasses.Viewer ← BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE];
name: Rope.ROPE ← ViewerTools.GetSelectionContents[];
IF mouseButton#blue AND (data.bBox.rect.w=0 OR data.bBox.rect.h=0) THEN {
MessageWindow.Append["Specify clipping box before left clicking ToIP", TRUE];
RETURN;
};
IF name=NIL OR Rope.Length[name]=0 THEN {
MessageWindow.Append["Select a file name before buttoning ToIP", TRUE];
RETURN;
};
IF Rope.Length[FileNames.Directory[name]]=0 THEN name ← Rope.Concat[NARROW[ViewerOps.FetchProp[viewer: iv, prop: $PVWDir]], name];
PreView.DoFileOps[op: ipMaster, viewer: iv, data: data, fileName: name, clip: mouseButton#blue];
};
Stuff: Buttons.ButtonProc = {
data: Data ← NARROW[clientData];
IF data.bBox.rect.w=0 OR data.bBox.rect.h=0 THEN {
MessageWindow.Append["Specify clipping box before clicking Stuff", TRUE];
RETURN;
};
data.stuffWithBorders ← mouseButton=blue;
PreView.DoFileOps[op: stuff, viewer: BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE], data: data, fileName: NIL, clip: TRUE];
};
FirstPage: Buttons.ButtonProc = {
data: Data ← NARROW[clientData];
DeltaPage[data, 1];
};
TurnPage: Buttons.ButtonProc = {
data: Data ← NARROW[clientData];
where: INTIF mouseButton = red THEN +1 ELSE -1;
DeltaPage[data, MIN[MAX[data.pageNumber+where, 1], (data.lastPageNumber)]];
};
LastPage: Buttons.ButtonProc = {
data: Data ← NARROW[clientData];
DeltaPage[data, (data.lastPageNumber)];
};
PageNumber: Buttons.ButtonProc = {
data: Data ← NARROW[clientData];
stream: IO.STREAMIO.RIS[ViewerTools.GetContents[data.pageNumberViewer]];
where: INT ← stream.GetInt[];
DeltaPage[data, MIN[MAX[where, 1], (data.lastPageNumber)]];
};
NormalizePageNumber: Sliders.FilterProc = {
data: Data ← NARROW[clientData];
RETURN [Real.FDiv[Real.RoundI[value*(data.lastPageNumber)], (data.lastPageNumber)]]
};
PageNumberSlider: Sliders.SliderProc = {
data: Data ← NARROW[clientData];
SELECT reason FROM
abort => {
ViewerTools.SetContents[data.pageNumberViewer, IO.PutFR["%-g", IO.real[data.pageNumber]]];
};
move => {
ViewerTools.SetContents[data.pageNumberViewer, IO.PutFR["%-g", IO.real[Real.RoundI[(data.lastPageNumber)*value]]]];
};
set => {
DeltaPage[data, MIN[MAX[Real.RoundI[(data.lastPageNumber)*value], 1], (data.lastPageNumber)]];
};
ENDCASE;
};
DeltaPage: PROCEDURE [data: Data, newvalue: INT] = {
ViewerTools.SetContents[data.pageNumberViewer, IO.PutFR["%-g", IO.int[newvalue]]];
Sliders.SetContents[data.pageNumberSlider, Real.FDiv[newvalue, (data.lastPageNumber)]];
IF data.pageNumber=newvalue THEN RETURN; -- just did the initialization needed
data.pageNumber ← newvalue;
ViewerOps.PaintViewer[viewer: data.preViewer, hint: all, clearClient: TRUE];
};
MakePreViewer: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: Rope.ROPENIL];
args: PreView.NameList ← LIST[]; --LIST OF Rope.ROPE
nameList: PreView.NameList ← LIST[]; --LIST OF Rope.ROPE
switches: PreView.Switches ← ALL[FALSE];
argLength: NAT ← 0;
switchChar: CHAR = '-;
[list: args, length: argLength] ← CommandTool.ParseToList[cmd: cmd, starExpand: TRUE, switchChar: switchChar ! CommandTool.Failed => CONTINUE; ];
IF args = NIL OR argLength < 1 THEN RETURN[$Failure, "Unable to parse command line"];
IF Rope.Fetch[base: args.first, index: 0] = switchChar THEN {
tChar: CHAR;
FOR iChar: INT IN [1..Rope.Length[args.first]) DO
IF (tChar ← Ascii.Upper[Rope.Fetch[base: args.first, index: iChar]]) IN PreView.SwitchRange THEN switches[tChar] ← TRUE;
ENDLOOP;
args ← args.rest;
};
IF switches['C] THEN { -- command arguments are RGB components of a single color image
FOR a: PreView.NameList ← args, a.rest UNTIL a=NIL DO nameList ← CONS[FileNames.ResolveRelativePath[a.first], nameList];
ENDLOOP;
[] ← CreatePreViewer[fileNames: nameList, switches: switches];
}
ELSE FOR rl: PreView.NameList ← args, rl.rest UNTIL rl = NIL DO --open a PreViewer on each file
[] ← CreatePreViewer[fileNames: LIST[FileNames.ResolveRelativePath[rl.first]], switches: switches];
ENDLOOP;
};
PVChangedProc: UserProfile.ProfileChangedProc = {
extensionList ← UserProfile.ListOfTokens[key: "PreView.Extensions", default: defaultExtensionList];
tryTioga ← UserProfile.Boolean[key: "PreView.TryTiogaOpen", default: FALSE];
};
PVListRemove: PUBLIC ENTRY PROC [ref: REF ANY] = {
ENABLE UNWIND => NULL;
pvList ← List.Remove[ref: ref, list: pvList];
};
PVListAdd: PUBLIC ENTRY PROC [list: LIST OF REF ANY] = {
ENABLE UNWIND => NULL;
pvList ← List.Append[l1: pvList, l2: list];
};
pvList: LIST OF REF ANYLIST[]; --shared
defaultExtensionList: LIST OF Rope.ROPE = LIST["ip", "interpress", "press", "pd", "ais", "griffin"];
extensionList: LIST OF Rope.ROPE ← defaultExtensionList;
tryTioga: BOOLFALSE;
bsStyle: PUBLIC BiScrollers.BiScrollerStyle;
pvBSClass: BiScrollers.BiScrollerClass;
PVStart: PROC = {
pvTIP: TIPUser.TIPTable ←
TIPUser.InstantiateNewTIPTable["PreView.tip"];
bsStyle ← BiScrollers.GetStyle[]; -- default gets BiScrollersButtonned
pvBSClass ← bsStyle.NewBiScrollerClass[[
flavor: $PreViewer,
extrema: PreView.PVExtremaProc,
notify: PreView.PVNotify,
paint: PreView.PVPaint,
destroy: PreView.PVDestroy,
get: PreView.PVGetName,
tipTable: pvTIP,
cursor: crossHairsCircle,
mayStretch: FALSE, -- NOT OK to scale X and Y differently
vanilla: PreView.PVBasicTransformProc, --proc which provides the vanilla transform for BiScrollers
preserve: [X: 0.0, Y: 1.0] --this specifies point that stays fixed when viewer size changes
]];
UserProfile.CallWhenProfileChanges[proc: PVChangedProc]; -- PVChangedProc called immediately after this registration call
[] ← Buttons.Create[info: [name: "PVSTOP!"], proc: StopIfYouCan];
Commander.Register[key: "Preview", proc: MakePreViewer, doc: "Create a PreViewer for a Press, PD, Interpress, or AIS file" ];
};
PVStart[];
END.