DIRECTORY AIS, Ascii, BasicTime, BiScrollers, Buttons, Commander, CommandTool, Containers, Convert, FileNames, FS, GriffinImageUtils, Icons, Imager, ImagerBackdoor, ImagerColorOperator, ImagerMemory, ImagerPixelArray, Interminal, Interpress, IO, IPMaster, Labels, List, MessageWindow, PDFileReader, PDImageReader, PopUpButtons, PressReader, PreView, PreViewClient, Process, Real, Rope, Rules, ShowPress, Sliders, Terminal, TiogaMenuOps, TIPUser, UserProfile, ViewerClasses, ViewerOps, ViewerSpecs, ViewerTools; PreViewTool: CEDAR MONITOR IMPORTS AIS, Ascii, BiScrollers, --Buttons,-- Commander, CommandTool, Containers, Convert, FileNames, FS, GriffinImageUtils, Icons, Imager, ImagerBackdoor, ImagerColorOperator, ImagerMemory, ImagerPixelArray, Interminal, Interpress, IO, IPMaster, Labels, List, MessageWindow, PDFileReader, PDImageReader, PopUpButtons, PressReader, PreView, Process, Real, Rope, Rules, ShowPress, Sliders, Terminal, 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 pointsPerInch: REAL = 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 curIndent: INTEGER _ 0; secondLine: INTEGER _ 14; bsY: INTEGER _ 31; selClass: PopUpButtons.Class = 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)"] ], doc: "Selection: selection operations for current page" ]]; stuffClass: PopUpButtons.Class = 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.Class = 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.Class = 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" ]]; IPLogError: PUBLIC Interpress.LogProc = { MessageWindow.Append[message: Rope.Concat["InterpressMaster Error: ", explanation], clearFirst: TRUE]; MessageWindow.Blink[]; }; WhichFileType: PROC [fileName: Rope.ROPE] RETURNS [PreView.FileInfo] = { fname: Rope.ROPE _ NIL; 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 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] = { fileInfo _ NEW[PreView.FileInfoRep _ [] ]; --initialize to fileType none, etc. { ref: REF ANY _ NIL; s: IO.STREAM _ FS.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; 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, xSize, ySize: REAL] RETURNS [preViewer: ViewerClasses.Viewer] = { bs: BiScrollers.BiScroller _ NIL; tV: ViewerClasses.Viewer; data: Data _ NIL; fileInfo: PreView.FileInfo _ NIL; button: Buttons.Button; rule: Rules.Rule; versionSpecified: BOOL _ Rope.SkipTo[s: fileNames.first, skip: "!"]#Rope.Length[fileNames.first]; curIndent _ 0; fileInfo _ WhichFileType[fileNames.first]; -- adds file extension to the named file if needed SELECT fileInfo.filetype FROM ip => { ipmaster: Interpress.Master _ Interpress.Open[fileName: fileInfo.fullFName, log: IPLogError ! 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 ySize*72.0, pageWidth: IF switches['V] THEN versatecPageWidth ELSE xSize*72.0, 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: ySize*72.0, pageWidth: xSize*72.0, 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 [fileInfo.fullFName, versionSpecified] _ InitAIS[data: aisData, fileNames: fileNames, singleName: fileInfo.fullFName]; IF 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: ySize*72.0, pageWidth: xSize*72.0, 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; }; data.bBox _ NEW[PreView.BBoxStateRep _ []]; data.iMemContext _ ImagerMemory.NewMemoryContext[]; 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: FALSE], 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 _ ViewerTools.MakeNewTextViewer[ info: [ wx: curIndent, wy: secondLine+ViewerSpecs.windowBorderSize+buttonAlign, ww: 40, wh: ViewerSpecs.scrollBarW+2*ViewerSpecs.windowBorderSize, parent: data.container, border: FALSE, scrollable: FALSE], paint: FALSE]; ViewerTools.InhibitUserEdits[data.pageNumberViewer]; 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]; Containers.ChildXBound[data.container, data.pageNumberSlider]; rule _ Rules.Create[ info: [ parent: data.container, wx: 0, wy: secondLine+ViewerSpecs.captionHeight+4*ViewerSpecs.windowBorderSize, wh: ViewerSpecs.menuBarHeight]]; Containers.ChildXBound[data.container, rule]; 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.ROPE _ NIL, versionSpecified: BOOL _ FALSE] = { 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 _ fileNames.rest.rest.first; grnName: Rope.ROPE _ fileNames.rest.first; bluName: Rope.ROPE _ fileNames.first; fullCP: FS.ComponentPositions _ FS.ExpandName[singleName].cp; -- a full FName grnCP: FS.ComponentPositions _ FS.ExpandName[grnName].cp; -- not a full FName IF data.abort THEN {data.abort _ FALSE; RETURN[NIL]}; -- set asynchronously by CheckForAbort 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 CheckForAbort 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!"]; -- everything thru the name base newName _ Rope.Concat[newName, Rope.Substr[base: singleName, start: fullCP.ver.start, len: fullCP.ver.length] ]; IF grnCP.ver.length#0 THEN versionSpecified _ TRUE; } ELSE { -- single file AIS image bps: [0..16] _ 0; IF data.abort THEN {data.abort _ FALSE; RETURN[NIL]}; -- set asynchronously by CheckForAbort 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 CheckForAbort 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; }; CheckForAbort: PROC ~ { tsc: TIPUser.TIPScreenCoords ~ NEW[TIPUser.TIPScreenCoordsRec]; Process.SetPriority[Process.priorityRealTime]; --VERY high! DO --Forever vt: Terminal.Virtual ~ Terminal.Current[]; keyBits: Terminal.KeyBits ~ Terminal.GetKeys[vt: vt]; IF (keyBits[LeftShift]=down OR keyBits[RightShift]=down) AND keyBits[Spare3]=down THEN { CheckForAbortInternal: ENTRY PROC ~ { ENABLE UNWIND => NULL; ref: REF ~ BiScrollers.ClientDataOfViewer[viewer]; IF ref#NIL THEN WITH ref SELECT FROM data: Data => { KillIt: PROC [data: Data] = TRUSTED { IF data.process#NIL THEN {Process.Abort[data.process]; data.process _ NIL}; }; WITH data SELECT FROM ipData: IPData => KillIt[ipData]; aisData: AISData => KillIt[aisData]; gData: GData => KillIt[gData]; pressData: PressData => KillIt[pressData]; pdData: PDData => data.abort _ TRUE; -- don't use Process.Abort ENDCASE => ERROR; }; ENDCASE; }; mouse: Interminal.MousePosition ~ Interminal.GetMousePosition[]; viewer: ViewerClasses.Viewer; tsc^ _ [ mouseX: mouse.mouseX, mouseY: (IF mouse.color THEN vt.colorHeight ELSE vt.bwHeight) - mouse.mouseY, color: mouse.color ]; viewer _ ViewerOps.MouseInViewer[tsc: tsc].viewer; IF viewer#NIL AND BiScrollers.ViewerIsABiScroller[viewer] THEN CheckForAbortInternal[]; }; Process.Pause[ticks: Process.SecondsToTicks[2]]; ENDLOOP; }; ResetProcess: PUBLIC ENTRY PROC [data: Data] = { ENABLE UNWIND => NULL; data.process _ NIL; }; SelectionOps: PopUpButtons.PopUpButtonProc = { --PROC [viewer: Viewer, instanceData, classData, key: REF ANY] data: Data _ NARROW[instanceData]; viewer: ViewerClasses.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 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 }; $SetSelection => { -- select x y w h (in inches) selAsRope: Rope.ROPE _ ViewerTools.GetSelectionContents[]; selAsStream: IO.STREAM = IO.RIS[selAsRope]; sel: Imager.Rectangle; {ENABLE IO.Error, IO.EndOfStream => { MessageWindow.Append["Select \"x y w h\" (in inches) before clicking", TRUE]; GOTO NeverMind; }; sel.x _ selAsStream.GetReal[] * pointsPerInch; sel.y _ selAsStream.GetReal[] * pointsPerInch; sel.w _ selAsStream.GetReal[] * pointsPerInch; sel.h _ selAsStream.GetReal[] * pointsPerInch; }; IF data.bBox.active THEN PreView.PVFeedback[data: data, v: viewer, op: remove]; data.bBox^ _ [active: TRUE, rect: sel, mode: waitingForSecondPoint, lastX: sel.x+sel.w, lastY: sel.y+sel.h, x0: sel.x, y0: sel.y]; PreView.PVFeedback[data: data, v: viewer, op: paint]; EXITS NeverMind => NULL; }; ENDCASE => ERROR; EXITS NoSel => MessageWindow.Append["No PreView Selection", TRUE]; }; Stuff: PopUpButtons.PopUpButtonProc = { --PROC [viewer: Viewer, instanceData, classData, key: REF ANY] data: Data _ NARROW[instanceData]; IF key=$Page OR key=$PageFitted OR key=$PageBordered OR key=$PageFitted THEN { 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 } ELSE 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 _ key=$Bordered OR key=$BorderedAndFitted OR key=$PageBordered OR key=$PageBorderedAndFitted; data.stuffWithFit _ key=$Fitted OR key=$BorderedAndFitted OR key=$PageFitted OR key=$PageBorderedAndFitted; PreView.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] data: Data _ NARROW[instanceData]; iv: ViewerClasses.Viewer _ BiScrollers.QuaBiScroller[data.preViewer].QuaViewer[inner: TRUE]; name: Rope.ROPE _ ViewerTools.GetSelectionContents[]; IF key=$Selection 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: key=$Selection]; }; 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 => { MessageWindow.Append["Select a valid page number", TRUE]; GOTO Abort; }; ]; DeltaPage[data, MIN[MAX[where, 1], (data.lastPageNumber)]]; }; ENDCASE => ERROR; EXITS Abort => NULL; }; 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 = { 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: 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['S] THEN { -- command arguments are two real numbers specifying page size xSize _ Convert.RealFromRope[args.first]; args _ args.rest; ySize _ Convert.RealFromRope[args.first]; args _ args.rest; IF xSize<=0.1 OR ySize <=0.1 THEN { result _ $Failure; msg _ "Preview: Specify positive real numbers for page sizes"; GOTO Abort; }; }; 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, xSize: xSize, ySize: ySize]; } 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, xSize: xSize, ySize: ySize]; ENDLOOP; EXITS Abort => NULL; }; 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 ANY _ LIST[]; --shared defaultExtensionList: LIST OF Rope.ROPE = LIST["ip", "interpress", "press", "pd", "ais", "griffin"]; extensionList: LIST OF Rope.ROPE _ defaultExtensionList; tryTioga: BOOL _ FALSE; 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 TRUSTED {Process.Detach[FORK CheckForAbort[]]}; Commander.Register[key: "Preview", proc: MakePreViewer, doc: "Create a PreViewer for a Press, PD, Interpress, or AIS file" ]; }; PVStart[]; END. ξPreViewTool.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Ken Pier, October 9, 1986 11:47:31 am PDT Mike Spreitzer November 14, 1986 8:18:29 pm PST Bland, August 28, 1986 4:25:55 pm PDT Eric Nickell, June 27, 1986 11:22:18 am PDT Doug Wyatt, June 7, 1986 1:52:25 pm PDT [class: INT, code: ATOM, explanation: ROPE] expects a file name which may not have an extension; will try extensions .ip, .interpress, .press, .pd, .ais .griffin in that order did not find full name, so try a few extensions 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. Extension as hint did not help; so try all other possibilities --read the AIS file(s) and get ready for display. aisData.state will be further filled in by this call. Now construct a viewer caption like Foo-*.ais!33 this PROC is here solely to synchronize with the Paint Proc in PreViewImpl 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; data.stuffWithFit _ shift; select entire page [cmd: Handle] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL]; ΚΏ˜codešœ™Kšœ Οmœ1™˜>KšœB˜BKšœ6˜6KšœC˜CK˜—Kšœ7˜7Kšœ˜—K˜šœ9˜9K˜ šœ žœ˜Kšœ(˜(Kšœ˜Kšœ˜Kšœ-˜-Kšœ+˜+Kšœ˜Kšœ4˜4Kšœ2˜2Kšœ˜KšœE˜EKšœC˜CKšœ˜K˜—Kšœ6˜6Kšœ˜—K˜šœ8˜8Kšœ ˜ šœ žœ˜Kšœ-˜-Kšœ!˜!K˜—KšœJ˜JKšœ˜—K˜šœ8˜8K˜šœ žœ˜Kšœ$˜$Kšœ˜Kšœ)˜)Kšœ#˜#Kšœ>˜>Kšœ ˜ K˜—Kšœ1˜1Kšœ˜—K˜K˜šŸ œžœ˜)Kšœžœžœžœ™+Kšœ`žœ˜fKšœ˜K˜K˜—šŸ œžœžœžœ˜HKšœƒ™ƒKšœ žœžœ˜Kšœžœ˜+Kšœ'žœžœ žœ˜ZKš žœžœžœžœ# ˜OK™/Kšžœžœžœ&˜Aš žœžœžœžœžœžœž˜?Kšœ)˜)Kšœ'žœžœ žœ˜SKšžœ$ ˜GKšžœ˜—Kšžœžœ ˜>Kšœ˜K˜—š Ÿœžœžœžœžœ!˜mK™^K™0Kšœ žœ #˜N˜Kšœžœžœžœ˜Kš œžœžœžœ!žœ žœ˜IK•StartOfExpansion[s: ROPE, char: CHAR]šœžœžœ˜7K–9[base: ROPE, start: INT _ 0, len: INT _ 2147483647]šœ žœH˜Všžœžœž˜K–-[s1: ROPE, s2: ROPE, case: BOOL _ TRUE]šœ$žœ/žœžœKžœžœžœ,žœ˜ψK–-[s1: ROPE, s2: ROPE, case: BOOL _ TRUE]šœ'žœžœ+žœ?žœžœžœ/žœ˜λK–-[s1: ROPE, s2: ROPE, case: BOOL _ TRUE]š œ$žœžœXžœžœžœ,žœ˜ΡK–-[s1: ROPE, s2: ROPE, case: BOOL _ TRUE]šœ%žœžœžœžœžœ žœžœžœ-žœ˜ΈK–-[s1: ROPE, s2: ROPE, case: BOOL _ TRUE]šœ)žœžœ<žœ žœžœžœ1žœ˜ΜKšžœžœ˜—K™>Kšžœ #˜5Kš žœKžœžœžœ,žœ˜–Kšžœ #˜5K–[openFile: FS.OpenFile]š žœ+žœ?žœžœžœ/žœ˜ΊKšžœ #˜5K–[stream: STREAM]š žœXžœžœžœ,žœ˜£K–[stream: STREAM]šžœžœžœžœ žœžœžœ-žœ˜‰K–[stream: STREAM]š žœ<žœ žœžœžœ1žœ˜™Kšœžœžœ˜0šž˜Kšœžœžœ˜8—K˜—Kšœ˜K˜—š ŸœžœžœIžœžœ&˜˜Kšœžœ˜!K˜Kšœ žœ˜Kšœžœ˜!K˜K˜K˜KšœžœK˜aKšœ˜Kšœ+ 2˜]šžœž˜šœ˜šœ]˜]šžœ ˜ Kšœ=žœ˜CKšžœ˜ Kšœ˜—šœ I˜]Kšœdžœ˜jKšžœ˜ Kšœ˜—šœ :˜LKšœdžœ˜jKšžœ˜ Kšœ˜—šžœžœ˜Kšœ`žœ˜fKšžœ˜ Kšœ˜—Kšœ˜—šžœžœ˜Kšœ]žœ˜cKšžœ˜ Kšœ˜—KšœžœZžœžœžœžœžœžœA ˜¨Kšœ˜—˜ šœA˜Ašžœ ˜ Kšœ=žœ˜CKšžœ˜ Kšœ˜—šœ˜šžœž˜Kšœxžœ˜~Kšœvžœ˜|Kšžœežœ˜r—Kšžœ˜ Kšœ˜—šœ˜Kšœdžœ˜jKšžœ˜ Kšœ˜—K˜—šžœžœ˜ Kšœ]žœ˜cKšžœ˜ Kšœ˜—Kšœžœΐ˜ΚK˜—˜Kšœžœ 9˜mKšœžœ‹žœ˜³KšœW˜WKšœu˜ušžœžœ 8˜OKšœQ˜QKšœS˜SK˜—šžœžœ˜!Kšœ]žœ˜cKšžœ˜ Kšœ˜—Kšœ˜K˜—˜Kšœžœgžœ˜žKšœžœ /˜^Kšœ g™hKšœv˜vKšžœžœžœžœ˜+Kšœ)˜)Kšœ)˜)Kšœ˜K˜—˜ Kšœžœžžœ ˜ΟK˜—šžœ˜ šžœ žœ˜Kšœ(˜(Kšžœ˜K˜—Kšžœgžœ˜qKšžœ˜ Kšœ˜——K˜Kšœ žœ˜+Kšœ3˜3šœ/˜/šœ˜K–"[name: ROPE, wDir: ROPE _ NIL]šœžœžœ3žœ‡˜άK–4[path: ROPE, stripOffVersionNumber: BOOL _ TRUE]šœTžœ˜[Kšœžœ˜ Kšœžœ˜ Kšœ ˜ Kšœ ˜ Kšœ žœ˜—Kšœžœ˜—K˜šœ˜Kšœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ˜Kšœ žœ˜Kšœ ˜ —Kšœžœ˜K˜—KšœHžœ˜TKšœ_žœ˜kKšœ\žœ˜hKšœ^žœ˜jKšœ]žœ˜iKšœ]žœ˜iK˜Kšœdžœ6žœ˜¦K˜"K˜Kšœbžœ6žœ˜€K˜"K˜Kšœfžœ6žœ˜¨K˜"K˜Kšœbžœ6žœ˜€K˜"K˜šœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ˜Kšœ˜—Kšœžœ˜—Kšœ"˜"K˜šœ6˜6šœ˜Kšœ˜Kšœ8˜8Kšœ˜Kšœ:˜:Kšœ˜Kšœžœ˜Kšœ žœ˜—Kšœžœ˜—Kšœ4˜4Kšœ@˜@K˜šœ'˜'šœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ:˜:Kšœžœ˜Kšœ˜Kšœ žœ˜—K˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœžœ˜—Kšœ>˜>K˜˜˜Kšœ˜K˜KšœH˜HKšœ ˜ ——Kšœ-˜-K™Kšœ%žœ˜,K–>[viewer: ViewerClasses.Viewer, prop: ATOM, val: REF ANY]šœ.žœ<˜nKšœ7˜7Kšœ7˜7KšœFžœ˜LKšœ˜K–4[l1: LIST OF REF ANY, l2: LIST OF REF ANY _ NIL]šœ žœ˜šž˜šœ ˜ Kšœ˜Kšžœ˜K˜——Kšœ˜K˜—šŸœžœ?žœžœžœžœžœžœ˜˜šžœ˜šžœ ˜ Kšœ=žœ˜CKšœ žœžœ˜Kšœ˜—šœ˜Kšœ=žœ˜CKšœ žœžœ˜Kšœ˜—Kšœ˜—Kšœ˜šžœžœžœ ˜9Kšœžœ˜/Kšœžœ˜*Kšœžœ˜%Kšœžœžœ ˜MKšœžœžœ ˜MKš žœ žœžœžœžœ &˜\KšœU ˜tKšœ5žœ 3˜‰Kš žœ žœžœžœžœ &˜\K–'[maxSampleValue: ImagerSample.Sample]šœx˜xK™0K–)[s: ROPE, pos: INT _ 0, skip: ROPE]šœ‰  ˜©Kšœp˜pKšžœžœžœ˜3K˜—šžœ ˜Kšœ˜Kš žœ žœžœžœžœ &˜\Kšœ/˜/KšœTžœ˜tKš žœ žœžœžœžœ &˜\K–[clear: BOOL]š œ žœžœtžœ,žœ˜ΓKšœ˜K˜—Kšœžœ˜K˜K˜—šŸ œžœ˜Kšœžœ˜?Kšœ/  ˜;šžœ  ˜ K–[]˜*K˜5šžœžœžœžœ˜X–[]šŸœž œ˜%Kšžœžœžœ˜Kšœ2˜2š žœžœžœžœžœž˜$šœ˜– [PROCESS]šŸœžœžœ˜%Kšžœžœžœ.žœ˜KKšœ˜—šžœžœž˜Kšœ!˜!Kšœ$˜$Kšœ˜Kšœ*˜*Kšœžœ ˜@Kšžœžœ˜—Kšœ˜—Kšžœ˜—K˜—K˜@K˜šœ˜K˜Kšœ žœ žœžœ˜MK˜Kšœ˜—K– [tsc: TIPUser.TIPScreenCoords]˜2K–[ViewerClasses.Viewer]šžœžœžœ)žœ˜WKšœ˜—K˜0Kšžœ˜—K˜K˜—šŸ œžœžœžœ˜0KšœžœA™JKšžœžœžœ˜Kšœžœ˜K˜K˜—šΠan œ# >˜mKšœ žœ˜"KšœZžœ˜`šžœž˜šœ  ˜*Kšžœžœžœžœ˜(K–œ[bs: BiScrollers.BiScroller, client: BiScrollers.Location, viewer: BiScrollers.Location, doX: BOOL _ TRUE, doY: BOOL _ TRUE, paint: BOOL _ TRUE]šœιžœ˜οK˜—šœ$ =˜aKšœžœ "˜>Kšžœžœžœžœ˜(K–œ[bs: BiScrollers.BiScroller, client: BiScrollers.Location, viewer: BiScrollers.Location, doX: BOOL _ TRUE, doY: BOOL _ TRUE, paint: BOOL _ TRUE]šœžœžœ˜@Kšœžœžœ˜2Kšœιžœ˜πK–O[bs: BiScrollers.BiScroller, op: BiScrollers.ScaleOp, paint: BOOL _ TRUE]šžœžœbžœ ˜‘KšœZžœžœ˜}K˜—šœ ˜&Kšžœžœ^žœ ˜₯Kšœžœ  =˜χKšœ]žœ ˜†K˜—šœ ˜0K–.[viewer: ViewerClasses.Viewer, prop: ATOM]šœžœ&˜:Kš œ žœžœžœžœ ˜+Kšœ˜šœžœžœžœ˜%KšœGžœ˜MKšžœ ˜K˜—Kšœ.˜.Kšœ.˜.Kšœ.˜.Kšœ.˜.K˜Kšžœžœ7˜OKšœžœh˜‚Kšœ5˜5Kšžœžœ˜K˜—Kšžœžœ˜—šž˜Kšœ6žœ˜<—K˜K˜—š‘œ# >˜fKšœ žœ˜"šžœžœžœ™2KšœCžœ™IKšžœ™Kšœ™—K™)Kšœ™š žœ žœžœžœžœ˜NK™Kšžœžœ^žœ ˜₯Kšœžœ  =˜χKšœ]žœ ˜†K˜—šžœžœžœžœ˜7KšœCžœ˜IKšžœ˜Kšœ˜—Kšœ&žœžœžœ˜sKšœ žœžœžœ˜kKšœ`žœžœžœ˜ŽK˜K˜—š‘œ# >˜eKšœ žœ˜"KšœVžœ˜\K–.[viewer: ViewerClasses.Viewer, prop: ATOM]šœ žœ&˜5šžœžœžœžœ˜GKšœGžœ˜MKšžœ˜K˜—šžœžœžœžœ˜)KšœAžœ˜GKšžœ˜K˜—Kšžœ*žœžœ8˜‚Kšœ^˜^K˜K˜—šŸœ# >˜iKšœ žœ˜"šžœž˜Kšœ!˜!Kšœ žœžœ0˜WKšœ!žœžœ0˜XKšœ2˜2šœ˜Kš œžœžœžœžœ%˜?šœžœžœžœ˜;Kšœ3žœ˜9Kšžœ˜ Kšœ˜Kšœ˜—Kšœžœžœ$˜;K˜—Kšžœžœ˜—šž˜Kšœ žœ˜—K˜K˜—š‘œ˜+Kšœ žœ ˜ KšžœM˜SK˜K˜—š‘œ˜(Kšœ žœ ˜ šžœž˜šœ ˜ Kšœ/žœžœ˜ZKšœ˜—šœ ˜ Kšœ/žœžœ2˜sKšœ˜—šœ˜KšœžœžœG˜^Kšœ˜—Kšžœ˜—K˜K˜—šŸ œž œžœ˜4Kšœ/žœžœ˜RK˜WKšžœžœžœ %˜NKšœ˜KšœFžœ˜LK˜K˜—š‘ œ˜(Kš œžœ žœžœ žœžœ™@šžœ˜Kšœ˜Kšœ>˜>Kšžœ˜ K˜—Kšœžœ ˜,Kšœžœ  ˜-Kšœžœ ˜4Kšœžœ ˜8Kšœžœžœ˜(Kšœ žœ˜Kšœ žœ˜KšœPžœ1žœ˜‘Kš žœžœžœžœžœ+˜U– [base: ROPE, index: INT _ 0]šžœ5žœ˜=Kšœžœ˜ šžœžœžœž˜1KšžœCžœžœžœ˜xKšžœ˜—K˜K˜—šžœžœ >˜UKšœ)˜)K˜Kšœ)˜)K˜šžœ žœ žœ˜#Kšœ˜Kšœ>˜>Kšžœ˜ K˜—Kšœ˜—šžœžœ ?˜VKš žœ$žœžœžœ žœ3˜xKšžœ˜KšœZ˜ZKšœ˜—š žœžœ&žœžœžœ ˜_Kšœ žœ[˜Kšžœ˜—šž˜Kšœ žœ˜—Kšœ˜K˜—šŸ œ$˜1Kšœc˜cKšœEžœ˜LK˜K˜—š Ÿ œžœžœžœžœžœ˜2Kšžœžœžœ˜Kšœ-˜-K˜K˜—šŸ œžœžœžœžœžœžœžœ˜8Kšžœžœžœ˜Kšœ+˜+K˜—K˜Kš œžœžœžœžœžœ ˜*Kš œžœžœžœžœ6˜dKšœžœžœžœ˜8Kšœ žœžœ˜Kšœ žœ˜,Kšœ'˜'K˜šŸœžœ˜˜Kšœ.˜.—Kšœ" $˜Fšœ(˜(Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ žœ &˜9Kšœ' ;˜bKšœ @˜[K˜—Kšœ9 @˜yKšžœžœ˜/Kšœ}˜}Kšœ˜—K˜K˜ K˜Kšžœ˜K˜K˜—…—v|›)