dennis@dennis.colorado.edu (03/30/89)
Some time ago I posted the code for PlaneWindow, which provides the illusion of a window over a larger window. It provided the ability to pan/scroll/zoom over that larger window. That code had some errors in it, most notably that it had a cyclic reference so that when the window was killed, the window didn't disappear from the screen. The following version corrects that problem. ================================================== systemdict /LiteWindow known not { (NeWS/litewin.ps) run } if % -------------------------------------------------- % Define a subwindow of scroll window in which % the client canvas acts like a window onto a larger canvas (called the plane). % Moving the scrollbars appears to pan and scroll over the plane canvas. % When the scroll bar buttons are in the bottom and left, then they % cause the origin of the client canvas to appear in the bottom left. % As the canvas grows, the scale and location of the scroll bar % buttons may change independent of user actions % This relies on the assumption all drawing is done on the client % canvas and that no canvases overlay the client canvas % (except when rubberbanding or dragging). % This operates by redrawing onto the client canvas with appropriate % scale and translation. % Methods: % 1. "minx miny maxx maxy SetPlaneSize => -" % Set the size of the plane canvas over which the client % canvas can pan/scroll. % This may visibly affect the scroll bar button locations. % 2. "xscale yscale SetPlaneScale => -" % Set the scale used on the plane. % Changing this will have the effect of a zoom. % 3. "- CenterPlane => -" % Put center of the plane at the origin of the client canvas % 4. "factor ExpandPlane => -" % Expand the size of the plane by the factor as a multiplier % in the +X -X, +Y -Y directions. % 5. "x y MovePlane => -" % Put origin of the client canvas at x,y of the plane. /PlaneWindow ScrollWindow dictbegin % portion of the plane currently in use (needed for scrollbars) /minX 0 def /minY 0 def /maxX 1 def /maxY 1 def /ScaleX 1.0 def /ScaleY 1.0 def % Client origin wrt plane origin /PlaneX 0 def /PlaneY 0 def % Make the following a per-object value % because it will change as scale changes /ClientFont /Screen-Bold findfont 16 scalefont def dictend classbegin % /ForkPaintClient? false def % ??????????????????? /PlaneWidth {maxX minX sub} def /PlaneHeight {maxY minY sub} def /Xrange { [minX maxX PlaneWidth .01 mul round PlaneWidth .1 mul round null] } def /Yrange { [minY maxY PlaneHeight .01 mul round PlaneHeight .1 mul round null] } def % Enlarge (or shrink) the plane canvas by specifying new x and y dimensions % This will visibly affect only the scroll bar button locations /SetPlaneSize { % minx miny maxx maxy => - % keep things to integers 0.5 add truncate /maxY exch store 0.5 add truncate /maxX exch store 0.5 sub truncate /minY exch store 0.5 sub truncate /minX exch store % Make sure that the plane size is at least as big as % the client canvas. Extend equally in +x/y and -x/y directions to % achieve this affect ClientWidth PlaneWidth sub dup 0 gt { % deltax 2 div 0.5 add truncate dup % deltax/2 deltax/2 maxX add % deltax/2 maxX+deltax/2 /maxX exch store % deltax/2 minX exch sub /minX exch store } { pop } ifelse ClientHeight PlaneHeight sub dup 0 gt { % deltay 2 div 0.5 add truncate dup maxY add /maxY exch store minY exch sub /minY exch store }{ pop } ifelse rerange } def /SetPlaneScale { % scalex scaley => - /ScaleY exch store /ScaleX exch store /ClientFont ClientFont ScaleX ScaleY min 16 mul 0.5 add truncate scalefont store redisplay } def /reshape { % x y w h => - /reshape super send % when the client canvas is reshaped, we need to % make sure that some portion of the previous % view is left visible. % We will assume that the point in the plane % that is at the client canvas origin before the reshape % is still at the origin after the reshape % This means that % if the client canvas gets bigger than the plane, % then the plane is extended in the positive x and % y directions only. % Doing it this way does have the funny effect % that the window may be refreshed twice. Seems no obvious fix. minX minY maxX maxY SetPlaneSize % will enforce constraint % and may force redisplay } def /rerange { % - => - Xrange /setrange HScrollbar send Yrange /setrange VScrollbar send % re paint scrollbars in toto PlaneX {/Itemvalue exch store paint} HScrollbar send PlaneY {/Itemvalue exch store paint} VScrollbar send } def /redisplay { % - => - eraseclient ShapeClientCanvas paintclient } def /setplaneorigin { % x y => - /PlaneY exch def /PlaneX exch def } def /MovePlane { % x y => - (set client canvas origin wrt plane origin setplaneorigin PlaneX HScrollbar movescroll PlaneY VScrollbar movescroll redisplay } def /CenterPlane { % - => - (put center of the plane at origin of clientcanvas) minX PlaneWidth 2 div add % xc minY PlaneHeight 2 div add % xc yc MovePlane } def /ExpandPlane { % expansion => - [/minX /minY /maxX /maxY] { % expansion variable dup load % expansion var val 2 index mul store } forall rerange } def % client canvas manipulation /ClientPath { /ClientPath super send PlaneX neg PlaneY neg translate ScaleX ScaleY scale } def /PaintClient { % - => - /PaintClient super send ClientFont setfont } def /eraseclient { % - => - gsave ClientCanvas setcanvas erasepage grestore } def % scroll bar manipulations /createscrollbars { % - => - (Create scrollbar canvases/items) /HScrollbar Xrange PlaneX {/planenotify MyPlane send} FrameCanvas /new SimpleScrollbar send dup /BarVertical? false put dup /MyPlane self put % Kludge: connect bar to this window store /VScrollbar Yrange PlaneY {/planenotify MyPlane send} FrameCanvas /new SimpleScrollbar send dup /MyPlane self put % Kludge: connect bar to this window store } def % repaint the button at a given location /movescroll { % value scrollbar => - { CheckValueBounds % setvalue /ItemValue exch store gsave ItemCanvas setcanvas ItemPaintedValue null ne {EraseBox} if PaintBox grestore /ItemPaintedValue ItemValue store } % value bar proc exch send } def /planenotify { % - => - /getvalue HScrollbar send /getvalue VScrollbar send MovePlane } def % Remove the cycle induced by the MyPlane % variable added to the Scrollbars. % Thanks to Tom Sheffler of Mitre for pointing this out. /destroy { {/MyPlane null store} HScrollbar send {/MyPlane null store} VScrollbar send /destroy super send } def classend def pause % Test out plane window % This test class operates as follows: % 1.The user creates the window % 2. The user gives a bitmap to the window using SetBackGround % 3. the user pans/scrolls over the image using the scroll bars % Note that if dithering is invoked (by zoom in/out), % and the image is complicated, then the refresh time will be very slow. /testclass PlaneWindow [] classbegin /Background null def /ZoomFactor 2 def /SetBackGround { % imagefilename => - (load file into /BackGround) readcanvas pause % get actual image /BackGround exch def } def /PaintClient { /PaintClient super send PaintBackGround } def /PaintBackGround { % - => - gsave ClientCanvas setcanvas minX neg minY neg translate PlaneWidth PlaneHeight scale BackGround null ne { BackGround imagecanvas pause } if grestore } def /Enlarge { ScaleX ZoomFactor mul ScaleY ZoomFactor mul SetPlaneScale } def /Reduce { ScaleX ZoomFactor div ScaleY ZoomFactor div SetPlaneScale } def /Normal { 1.0 1.0 SetPlaneScale } def /ClientMenu [ (Center) {/CenterPlane testwin send} (Zoom Normal) {/Normal testwin send} (Zoom Out) {/Reduce testwin send} (Zoom In) {/Enlarge testwin send} ] /new DefaultMenu send store classend def /testwin framebuffer /new testclass send def % set location and shape of the window % Make it a 1192/2 by 900/2 window located at location (400,400) 400 400 1192 2 div 900 2 div /reshape testwin send % Make the plane window with x coordinates ranging over 0 -> 1192 % and y coordinates ranging over 0 -> 900 % (note, this is same as framebuffer size, so no dithering occurs, % so screen rewrite speed is fast) % Origin of the plane window will be at the origin of the view. 0 0 1152 900 /SetPlaneSize testwin send % Fill in the path for some image in the following string (./picture) /SetBackGround testwin send /map testwin send