TweetFollow Us on Twitter

Window Scroller
Volume Number:2
Issue Number:9
Column Tag:Advanced Mac'ing

Build a Pop-Up Window Scroller

By Scott T. Boyd, Aggieland, TX

Scott is a graduate student at Texas A&M, doing research in typesetting (TeX) and object oriented programming. He has a background in VAX/Unix and has done a lot of work on porting TeX to the Mac, but says the Lisa Compiler bugs derailed that project until another firm bought the Lisa source and fixed it. In this article, Scott presents a clever way to pup-up a minature window and scroll directly to the point of interest. This is ideal for large grafport projects and we are happy to present Scott with our Program of the Month award and $50 with thanks from MacTutor!

Fig. 1 Getting the Big Picture

Getting the Big Picture

For years computers offered no more user interface than a command line, single-keystroke menus, and full-screen text editing. The Macintosh has introduced us to the beauties of carefully designed user interfaces. The average user has come to expect pull-down menus, windows, a desktop, a pointing device, and other user interface tools. Apple has provided a set of guidelines for those who would like to extend the user's mental metaphor by providing new user interface elements.

Many solutions emerge out of the minds of frustrated users. For example, users of large computers used to have all computer output displayed on one screen in a single stream of characters. This really was not a problem until users realized they could have more than one thing going at once. To help users sort out what came from where, windows were born. Each window contains information relating specifically to the topic of interest of that window. For example, if you are editing a document in one window, you could receive mail messages in another while watching system utilization in yet another. Thus, since someone got frustrated with having input and output streams all mixed into one, windows now allow separating these streams in a sensible manner.

One element of the Macintosh interface that can be frustrating is the manner in which scroll bars operate, especially if the window uses two scroll bars, one for vertical and the other for horizontal movement. In the extreme case, consider a window containing a very large picture which takes a substantial amount of time to redraw whenever you move around with the scroll bars. If you are positioned at the top-left corner and want to move to the bottom-right corner, you are obligated to do one of two things.

First, you can move the cursor all the way to the bottom-left of the window and drag the horizontal thumb all the way to the right, move to the top of the vertical scroll bar, and move its thumb all the way to the bottom. This approach requires a great deal of mouse movement and two window updates. Remember that updating the window can take a good amount of time (depending on the complexity of the document). To make this situation worse, the user has no visual confirmation of what might be at the bottom-right of the document.

The other approach that you might be familiar with is the Show Page goodie in MacPaint. In this approach, a new window appears and a miniature version of the document is drawn. You can then move a rectangle around to show where you would like the window to be positioned. Then you hit the OK button or the Cancel button, the window goes away, and your document window reappears, showing the area you selected. This does overcome the need for a mental picture of the whole document. While this method only requires updating the window once, it has the unfortunate problem of introducing a new mode to the user. In Apple's Do's and Don'ts of a Friendly User Interface, over-using modes is the first Don't.

The problem then is a matter of giving the user some idea of what the document looks like and permitting easy access to any part of the document. Other approaches have been implemented, many of which have contributed to the ideas that go into the solution given here. This solution not only solves the primary problem, but it draws from a number of elements of the Macintosh interface that are already very familiar, hence the name: The Pop-up, Two Dimensional, Random Access, Scroll Bar Menu, or simply, OverView.

How OverView Behaves

It behaves just as it is named. At the user's request (discussed later), something akin to a menu pops up. It has the proportions of the document and it contains a miniature picture of the document. The area currently showing in the document window is highlighted. As long as the user holds down the mouse button, a gray rectangle the same size as the highlighted area moves around with the cursor, showing the area that will be selected if the button is released. Like most Macintosh controls (e.g. the scroll bar thumb), if the cursor is moved too far away from the window, the gray rectangle disappears. If the mouse is released when the rectangle is gone, the Pop-up window disappears and nothing happens. If, on the other hand, the button is released while the gray rectangle is showing, the old selection dehighlights and the new selection flashes in the same way that menu selections flash when selected. The scroll bars change appropriately, and the program gets notified that it needs to redraw the contents of the window at the new location via an update event.

With OverView, the user never perceives a change in mode, yet has access to the whole document in one easy step. Like pull-down menus, OverView will not generate activate events. It will not create update events unless a new selection is made. It appears quickly and disappears quickly. While it may seem complicated after reading this description, try using it once. It is much easier used than described.

Summoning OverView

OverView was created to view large typeset pages at varying levels of magnification. As such, it was designed to be used in a passive mode. That is, the user could only view, not change the document contents. This has an important relationship to how OverView was first used. Since clicking in the window pane (the area of the window where the actual document is, excluding controls) had no implicit meaning, that was used to activate OverView. However, in documents where text editing or object editing (e.g. MacWrite, MacDraw) takes place, clicking in the pane has other meanings. Another method of summoning OverView is called for. Andy Hertzfeld gets credit for the idea of putting an eyeball icon at the bottom-left of the window and scooting the horizontal scroll bar over to the right. Clicking on the eye could summon OverView. Another possibility is similar to the zoom box capability added in the new ROMs. The eyeball could be placed in the title bar between the close box and the title. Clicking on the eye would function almost exactly like a pull-down menu.

Making the choice on this is left to you, the creative program designer, to see what users like best. Keep in mind that it gets tiresome having to move the cursor to an exact point on the screen to use a function, especially if you happen to spend a lot of time far away from that spot. That was the beauty of the very first approach, because it was always available whenever you were in the window of interest. A typical power-user trick could come in handy for non-passive windows. Perhaps Option- click or Command-click or Command-Option-click could be used (as in PageMaker™).

Operational Details

OverView has a few very special features. Some were mentioned above. Others are listed here. For example, the pop-up window always remains within the boundaries of the front window. This is important for the prevention of the generation of update events. OverView knows that the only window it will ever affect is the front window, so it can easily convince that window that it was not affected (see the code).

OverView provides flexibility by taking advantage of the information available to it about the front window. By looking at the size of the window pane and comparing it to the size of the document, it scales the rectangles appropriately. It also takes advantage of the available screen size (just in case bigger screens just happen to come along).

Every window which will have its own OverView Pop-up needs a WindowPtr and a BitMap. NewOverView works by creating a new window for the Pop-up window. It then creates an offscreen bitmap into which a miniature of the document can be drawn with UpdateOverView. Then, whenever it is summoned, OverViewSelect makes its window appear after saving the bits underneath it into a temporary bitmap. It shows the Pop-up window without deactivating the document window by using ShowHide and some trickery. It copies the offscreen bitmap miniature into the OverView window and calls TrackGrayRgn to get the user's selection. It then hides the window, restores the bits underneath the Pop-up window, and sets the scroll bars based on the new selection.

The Interface

The code is broken up into three procedures: InitOverView, UpdateOverView, and OverView- Select. The code is in the form of a Macintosh Workshop Pascal (MPW Pascal) Unit, which can be compiled separately and linked in just like any of the Toolbox Managers. For now, it is enough to know that MPW Pascal is essentially Lisa Pascal. OverView was initially written in TML Pascal as a straight program. The conversion to MPW Pascal took five minutes and was mostly changing the include files and compiler switches. For more information, see the MPW Section below.

NewOverView should be called once for every window that will have an OverView window. Pass in an empty bitmap, an empty window pointer, a rectangle with the size of the virtual document (origin at 0,0), and a size factor (a real number between 0 and 1). NewOverView creates a new window whose height is determined by the available screen height multiplied by the size factor. NewOverView creates an offscreen bitmap and fills in the BitMap's record.

It is your program's responsibility to keep the miniature picture up to date. You can draw into it by passing your drawing procedure and the bitmap to UpdateOverView. It will not do any drawing or erasing. It simply sets things up to do the drawing, calls your drawing routine, and resets everything to normal before exiting. Thus, rather than erasing and redrawing the entire picture, you might just draw the parts that have changed if that is less work or less time consuming.

Whenever you decide to summon OverViewSelect, call it by passing in the point from the MouseDown event, the rectangle (origin at 0,0) with the dimensions of your virtual document, the point that corresponds to the upper-left corner of the viewing window (in terms of the size of the virtual document), the bitmap from NewOverView, and handles to the horizontal and vertical scroll bars.

Beware that the viewing window should not be permitted to resize any smaller than the OverView pop-up window. The trick used for soaking up update events will not be reliable if the OverView window overlaps any other windows. Just be sure to set the pop-up window size as the smallest size allowed whenever growing a window.

Changing the size of the virtual document is fine as long as the ratio of the sides remains the same. If that changes, the old window and bitmap should be disposed of and new ones allocated.

Watch out! If you choose a Pop-up window size that is too large, CopyBits freaks out. This will lead to unpredictable behavior. The OverView code needs to be modified so that it never attempts to CopyBits any sections larger than 3K at a time. Unfortunately, deadline pressure prevented this fix. A factor of 0.5 works just fine on all currently available Mac-type hardware (except on the XL), but 0.6 is too much. The fix will certainly be available before the new hardware hits the market. The problem with the XL appeared just at press time, so it has not been tracked down yet.

Understanding the OverView Code

When you call OverViewSelect, a number of things happen. The window is positioned so it stays within the boundaries of the FrontWindow. Then, before actually displaying the window, the screen bits where it will appear are saved into an offscreen bitmap. The window appears, the miniature picture is blasted into the window, the relative size of the current selection is computed, the current viewing selection is highlighted, and the user is given an identically-sized gray rectangle to drag around, which is tracked until a MouseUp. If a selection is made, the old selection is dehighlighted and the new selection is flashed MenuFlash times. The window is hidden and the screen bits are copied back into place. If a selection was made, the scroll bars are set appropriately. Otherwise, the update events for the FrontWindow are cleared.

Note that the window position on the virtual document is stored in a scroll position variable as well as in the scroll bars themselves. Figure 1 shows the organization of the important variables and how they relate to each other. The position of the window on the document is directly related to the settings of the scroll bars. The size of the highlighted area and the draggable gray region are proportional to the size of the window pane relative to the document's overall size. Figure 2 shows a picture document and should help you get a better feel for how everything looks. Please understand that the miniature picture as printed has much higher resolution than the screen (300 dots per inch versus 72 d.p.i.), so it looks much better on paper. In addition, the sharp reader will notice that the eyeballs were not used to summon the OverView window in either Figure 1 or 2. They are shown to let you get the visual effect of the different placement.

The code uses two offscreen bitmaps. The first stores the miniature picture. Create it with NewOverView before using any of the other OverView routines. Dispose of it when you are done with it. The second bitmap stores the bits underneath the pop-up window whenever it appears. It is allocated dynamically before showing the pop-up window and deallocated right after hiding the window. The Mac's menu manager works in a similar manner, allocating and deallocating on demand.

The trick used to mask out update events relies on the following argument. OverViewSelect should not be called if there are outstanding update events for the front window. Update events must be handled prior to invoking OverViewSelect. Since we assume there will not be any update events for the front window, we can safely assume that any that are generated during the time that OverView is in use were generated by us. By doing a BeginUpdate and an EndUpdate for the FrontWindow, the update events are eliminated. This means that we must be sure to restore the bits that were underneath the OverView window since the window manager will not ask for the window to be updated. You should also consult Mike Schuster's C Workshop: Try Pop-Up Menus! (MacTutor, December 1985, pp.15-22) for an excellent discussion of handling bitmap allocation, rectangle placement, and other issues important to pop-ups.

As always, an intimate knowledge of Inside Macintosh will aid you immensely in understanding the code. CopyBits, DragGrayRgn, the Window Manager, the Control Manager, MapRect, MapPt, and SetPortBits should provide some interesting reading topics for you.

The Sample Program

In addition to the OverView unit, this article includes a sample program to give you a feel for how you can include OverView in your own programs. The sample program brings up a window to fill the screen. It sets up scroll bars on the right and bottom, leaving room for the eyeball at the bottom-left corner. It then waits for a KeyDown event, which will exit the program, or a MouseDown event, which will summon the OverView window.

The sample also has a menu for changing the size of the virtual document. The three sizes given are for a typeset 8.5x11" page at screen resolution, ImageWriter resolution, and LaserWriter resolution. Once again, you can see the typesetting origin of OverView and its associated tools. Notice the differences when you change sizes. The pop-up window does not change its size, but the size of the selection rectangle inside it does change. For example, the selection rectangle is much smaller when LaserWriter resolution is selected than when screen resolution is selected. This is because the window pane shows a different portion of the overall document.

A couple of general programming notes follow. First, note that the viewing window (the one that stays visible with the picture in it all the time) is sized to the full size of the screen. Thus, if it runs on a Lisa, it takes full advantage of the screen size. And, if Apple decides to come out with larger Mac screens, no problem. With the simple technique in SetUpWindow, any program can be prepared to use all available screen space. If Apple decides to offer different screen resolutions, this program will not know about it. However, with some additional code, it could use the screen resolution globals to compute the pixel size of MenuBarHeight, TitleBarHeight, ScreenMargin, SBarHeight, and SBarWidth to keep them the same size that we are all used to.

This sample shows some of the beauty of Workshop Pascal units. Since OverView is in its own separately compiled unit, the sample only needs to include it and the programmer can safely forget about the details of OverView and instead treat it just like any other Mac manager.

You might notice in the MacTutor article referenced earlier that his code does not use a window. Rather, he uses only an offscreen bitmap. While an OverView could be made in a like manner, this implementation uses a window intentionally. It seemed more natural at the time, and still does. If you have comments on this approach, please send them.

MPW -- What is it?

MPW stands for Macintosh Programmer's Workshop. It is the development environment many of us have been waiting for. Its power tools include a unix-like shell, a 68xxx assembler, a linker, a debugger, a resource editor, and a resource compiler/decompiler. With the shell, you get an environment where you can edit, manipulate files, compile, link, and run programs in multiple windows. The shell offers integration, command scripting, regular expression processing, and extensibility.

Macintosh Workshop Pascal, Macintosh Workshop C, and MacApp are available separately as MPW tools. The Pascal and C have migrated from the XL Workshop to a truly on-the-mac, for-the-mac environment.

MPW runs on just about anything except the old 128K machines. It will be coming available on the market from Apple reportedly by the end of the year. No cost has been given, but guesses are in the $500 range, depending on whether you get Pascal, C, and/or MacApp.

MacApp stands for the expandable Macintosh application. Finally, all of us who wondered why it was so hard just to put some text in a window will have a tool where you spend your time describing the differences between your application and the standard application. Things like windows, menus, updating, text editing, AppleTalk, filing, and all the ordinary things are taken care of automatically. MacApp is a revolutionary product you should pay careful attention to. It, too, will be coming available hopefully by year's end.

Workshop Pascal and TML Pascal

The simple, plain truth is, if you program in Pascal on the Mac, you are probably using TML Pascal. OverView was originally programmed with TML Pascal, as were all of my programs. At least, that is, until recently. If you want to use OverView with TML Pascal, read on.

Workshop Pascal brings a number of good things to the Mac that other Pascals just do not offer. Two in particular are Objects and Units. Objects allow you to program in an object oriented fashion. You are familiar with the objects used in the Mac interface: windows, menus, boxes, scroll bars, etc. With object programming, you can write your programs to treat objects as somewhat intelligent creatures with the ability to communicate with you. For a better description than space allows, consult the new book from Hayden Press, Object Oriented Programming for the Macintosh, by Kurt J. Schmucker. It is an excellent book, and one of the first to comprehensively address the issue of object programming.

The other benefit of Workshop Pascal is Units. The toolbox managers you know and love were written as units. A unit is a separate chunk of code. OverView is a sample of such a chunk. It consists of an interface part and an implementation part. The interface part declares constants, types, variables, and procedures that are available to the outside world. The implementation part has the actual working code, and can contain its own private constants, types, variables, and procedures.

A unit stands alone by itself. It is compiled separately. When your program says uses identifier, the compiler goes out and reads in the interface part, treating the procedure declarations like Forward declarations. You then link in the separately compiled unit with your program in the link step, just like the toolbox interfaces.

Now, what do you do if you have TML Pascal? No problem. Include the text of OverView.p directly in your program. Remove the words UNIT OverView, INTERFACE and IMPLEMENTATION and the final END. Remove the compiler switch settings. In the interface section, add FORWARD; at the end of the three procedures. Modify the uses clause to the TML equivalent. Put the interface section into the front of your program. Put the implementation section anywhere in your program.

According to a TML spokesperson, the Object Pascal extensions and separate unit compilation will be in TML Pascal Version 2.0, expected some time before you read this! Tom has added some staff and seems to moving right along. If you have 2.0, you should not need to make any changes other than the compiler directives and the Uses clause.

Summary

This addition to the Mac interface has caught on here in Aggieland. Several people have commented that the idea was so natural that they had tried (in vain) to summon OverView while they were using MacDraw and MacPaint! If you find a good way to add an OverView capability to Draw or Paint, please let me know. My only solution right now involves FKEYs and searching the heap for windows and scroll bars, and it looks highly involved. Please use this code in your own programs if you like the way it feels.

Until the Mac has the hardware capability to zoom in and out on a document (like the Symbolics 3600), this kind of tool will give the rest of us some ability to get the big picture and feel in control of our large documents.

UNIT OverView; 
{Version 1.0 Saturday, July 12, 1986 9:47:53 PM by Scott T. Boyd of the 
MacHax™ Group.
 Many thanks to Greg Marriott of SoftWare To Go, also a member of the 
MacHax™ Group}

INTERFACE

{Compiler Switch Settings}
{$R+}
{$OV+}
uses MemTypes, QuickDraw, OSIntf, ToolIntf, SANE;
const
 MenuBarHeight=  20; {Height menu bar in pixels}
 TitleBarHeight  =  18; {Height of title bar in pixels}
 ScreenMargin  =   4;{Width of "safety margin" }
 SBarHeight =  15; {Height of scroll bar}
 SBarWidth=  15; {Width of scroll bar}

Procedure NewOverView( var OV_pagePict : bitMap; 
 var OV : WindowPtr; viewRect : Rect; factor : Real );
Procedure UpdateOverView( 
 Procedure drawProc; OV_pagePict : bitMap);
Procedure OverViewSelect( where: Point; viewRect : Rect; 
 VAR scrollPosition : Point; VAR OV : WindowPtr; 
 VAR OV_pagePict : bitMap; 
 HScrollBar,VScrollBar : ControlHandle);

IMPLEMENTATION

Procedure NewOverView {( var OV_pagePict : bitMap; 
 var OV : WindowPtr; viewRect : Rect; factor : Real )};
var
 dummyRect: Rect;
 horizontal,{horiz. pixel size of OverView }
 vertical:Extended;{vert. pixel size }
 sizeOfOff: Size;{bytes for offscreen bitmap}
 offRowBytes:  Integer; {row bytes for offscreen bitmap}
 bitRect: Rect;  {size of OV window, bitmap}
 dummy: Point;
 offPort, {temporary working port}
 oldPort: GrafPtr; {temporary storage}
begin {compute available vertical screen space}
 vertical := ScreenBits.bounds.bottom- ScreenBits.bounds.top-MenuBarHeight;
 vertical := vertical * factor;
 {compute horizontal to proportion}
 horizontal := vertical * viewRect.right / viewRect.bottom;
 {create the new window record}
 SetRect(dummyRect, 0, 0, Num2Integer(horizontal), Num2Integer(vertical));
 OV := NewWindow( nil, dummyRect, '', FALSE, altDBoxProc, WindowPtr(-1), 
FALSE, LongInt(0) );
 {create offscreen bitmap}
 bitRect := OV^.portRect;
 offRowBytes := ((bitRect.right-bitRect.left) div 8) + 1;
 if Odd( offRowBytes ) then 
 offRowBytes := offRowBytes - 1;
 sizeOfOff := (bitRect.bottom-bitRect.top) * offRowBytes;
 with OV_pagePict do
 begin
 baseAddr := QDPtr( NewPtr( sizeOfOff ));
 rowBytes := offRowBytes;
 bounds   := bitRect;
 end;
 
 {fill the bitmap with white}
 GetPort( oldPort ); 
 offPort := GrafPtr( NewPtr( sizeof( GrafPort )));
 OpenPort( offPort );
 SetPortBits( OV_pagePict );
 FillRect( bitRect, white );
 SetPort( oldPort );
 ClosePort( offPort );
 DisposPtr( Ptr( offPort ));
end;  {MakeOverView}

Procedure UpdateOverView {( drawProc : Procedure;
 OV_pagePict : bitMap)};
var
 offPort,
 oldPort: GrafPtr;
begin
 GetPort( oldPort );
 offPort := GrafPtr( NewPtr( sizeof( GrafPort )));
 OpenPort( offPort );
 SetPortBits( OV_pagePict );{make drawing offscreen}
 drawProc;{let the user draw}
 SetPort( oldPort ); {return drawing to normal}
 ClosePort( offPort );
 DisposPtr( Ptr( offPort ));
end; {UpdateOverView}

Procedure OverViewSelect{( where: Point; 
 viewRect : Rect; VAR scrollPosition : Point; 
 VAR OV : WindowPtr; VAR OV_pagePict : bitMap; 
 HScrollBar,VScrollBar : ControlHandle)};
var
 MenuFlash: ^Integer;{system global}
 value, {value returned by TrackGrayRgn}
 h, v:  LongInt; {}
 pane,  {}
 tempPt:  Point; {}
 scope, {size of window pane scaled into OV window}
 tempRect,
 limitRect, {limit for drag region}
 slopRect:  Rect;{slopiness allowance for dragging}
 dragRectRgn: RgnHandle;  {the region the user drags }
 oldPort: GrafPtr;
 theWindow: WindowPtr;  {holds frontWindow}
 underScope: BitMap; {offscreen bitmap}
 whichWindow :   WindowPtr; {for save and restore bits}
 {----------------------------------}
 Procedure OV_Prepare;
 begin
 MenuFlash := pointer($A24);
 GetPort( oldPort );
 theWindow := FrontWindow;{the active window }
 BringToFront( OV ); {make OV appear, but not really}
 SetPort( OV );  {it's now the current port}
 ShowHide( OV, FALSE );   {it's also not visible}
 MoveWindow( OV,0,0,FALSE );{home the window}

 {compute the size of the current window pane}
 pane.h := theWindow^.portRect.right - theWindow^.portRect.left - sBarWidth;
 pane.v := theWindow^.portRect.bottom - theWindow^.portRect.top - sBarHeight;

 {scale the pane into the OV window to show size }
 SetRect( tempRect, 0, 0, pane.h, pane.v );
 MapRect( tempRect, viewRect, OV^.portRect );
 scope := tempRect;
 {make the region to drag around.  same size as scope}
 dragRectRgn := NewRgn;
 RectRgn( dragRectRgn,scope );
 {this works to limit the movement of dragRectRgn}
 SetRect( limitRect, 0, 0, OV^.portRect.right-scope.right+1,
OV^.portRect.bottom-scope.bottom+1 );
 {scale scrollPosition into OV for placing scope in OV}
 tempPt := scrollPosition;
 MapPt( tempPt, viewRect, OV^.portRect );
 OffSetRect( scope, tempPt.h, tempPt.v );

 end; {OV_Prepare}
 {----------------------------------}
 Procedure OV_PositionOverView;
 var offset : Point;
 begin
 SetPort( oldPort );
 offset := where;
 GlobalToLocal ( offset );
 h := offset.h;  {local value of mousedown}
 v := offset.v;
 {make sure it doesn't go off the bottom of the window}
 if (v + OV^.portRect.bottom) >= theWindow^.portRect.bottom
 then v := theWindow^.portRect.bottom - OV^.portRect.bottom - 1;
 {make sure it doesn't go off the right of the window}
 if (h + OV^.portRect.right) >= theWindow^.portRect.right
 then h := theWindow^.portRect.right - OV^.portRect.right - 1;
 {make sure it doesn't go off the top of the window}
 if v < theWindow^.portRect.top then v := theWindow^.portRect.top;
 {make sure it doesn't go off the left of the window}
 if h < theWindow^.portRect.left then h := theWindow^.portRect.left;
 
 SetPt ( offset, h, v);
 LocalToGlobal( offset );
 h := offset.h;
 v := offset.v;
 
 SetPort( OV );
 MoveWindow( OV, h, v, FALSE );
 end; {OV_PositionOverView}
 {----------------------------------}
 procedure OV_SaveBits;
 var
 sizeOfOff: Size;
 offRowBytes:  Integer;
 underRect,
 bitRect: Rect;
 dummy  : Point;
 offPort,
 oldPort: GrafPtr;
 begin
 GetPort( oldPort );
 {put window magager port into offport as a windowptr}
 GetWMgrPort( offPort );
 whichWindow := WindowPtr( offPort );
 {allocate a new grafport}
 offPort := GrafPtr( NewPtr( sizeof( GrafPort )));
 {home a copy of the bounds of the OV window}
 bitRect := OV^.portBits.bounds;
 offsetRect( bitRect, -bitrect.left, -bitrect.top );
 {compute memory necessary for offscreen bitmap}
 {allocate it and setup bitmap record}
 offRowBytes := ( bitRect.right div 8 ) + 1;
 if Odd( offRowBytes ) then offRowBytes := offRowBytes -1;
 sizeOfOff := bitRect.bottom * offRowBytes;
 with underScope do
 begin
 baseAddr := QDPtr( NewPtr( sizeOfOff ));
 rowbytes := offRowBytes;
 bounds   := bitRect;{using HOMEd rectangle}
 end;

 {move a copy  back where OV will appear}
 underRect := underScope.bounds;
 OffsetRect( underRect, h-1, v-1 );
 {actually save the bits}
 OpenPort( offPort );
 SetPortBits( underScope );
 SetClip( offPort^.visRgn );
 CopyBits( whichWindow^.portBits, underScope,
underRect, underScope.bounds,srcCopy, NIL);
 SetPort( oldPort );
 ClosePort( offPort );
 DisposPtr( Ptr( offPort ));
 end; {OV_SaveBits}
 {----------------------------------}
 Procedure OV_ShowOverView;
 var
 boxWidth, 
 boxHeight: integer;
 begin 
 OV_SaveBits;
 ShowHide( OV, TRUE );  {now window appears}
 {blast miniature picture into OV}
 CopyBits(OV_pagePict, OV^.portBits, 
OV_pagePict.bounds, OV^.portRect,srcCopy,nil);
 {highlight the current selection}
 InvertRect( scope );
 {give the user some room to be sloppy}
 slopRect := OV^.portRect;
 InsetRect( slopRect, -25, -25 );
 GlobalToLocal( where );
 {compute size of draggable region and center it }
 boxWidth := scope.right - scope.left;
 boxHeight := scope.bottom - scope.top;
 OffsetRgn( dragRectRgn, where.h-(boxWidth div 2), 
where.v-(boxHeight div 2) );
 OffSetRect( limitRect, boxWidth div 2, boxHeight div 2); 
 {let the user drag it around}
 value := DragGrayRgn( dragRectRgn,where,limitRect, slopRect,0,nil );
 
 end; {OV_ShowOverView}
 {----------------------------------}
 Procedure OV_RestoreBits;
 var underRect: Rect;
 begin
   underRect := underScope.bounds;
 {home the rectangle}
   OffsetRect(underRect,-underRect.left, -underRect.top);
 {position it correctly}
   OffSetRect( underRect, h-1, v-1);
 {blast stuff under window back into position}
 CopyBits( underScope, whichWindow^.portBits, 
underScope.bounds, underRect,srcCopy, NIL);
 {deallocate the bitmap space (be nice and clean)}
 DisposPtr( Ptr( underScope.baseAddr ));
 end; {OV_RestoreBits}
 {----------------------------------}
 Procedure OV_HandleSelection;
 Procedure OV_FlashSelection;
 var i: Integer;
 j: LongInt;
 begin
 HLock( Handle( dragRectRgn ));  {good habit!}
 for i := 1 to 2*MenuFlash^ do begin
 InvertRect( dragRectRgn^^.rgnBBox );
 delay( 4,j );
 end;
 HUnLock( Handle( dragRectRgn ));  {yep!}
 end;
 {------------------------}
 begin {OV_HandleSelection}
 if (HiWord(value)<>-32768) or (LoWord(value)<>-32768)
 then   {user actually made a selection}
 begin
 InvertRect( scope );{turn off current selection}
 OV_FlashSelection;
 ShowHide( OV, FALSE ); {hide the OV window}
 SetPort( oldPort );
 OV_RestoreBits; {blast bits back into place}

 {figure the new scrollPosition based on the top-left corner}
 {of the draggable region}
 SetPt( tempPt, dragRectRgn^^.rgnBBox.left, dragRectRgn^^.rgnBBox.top 
);
 MapPt( tempPt, OV^.portRect, viewRect );
 scrollPosition := tempPt;
 tempRect := viewRect;
 tempRect.bottom := tempRect.bottom - pane.v;
 tempRect.right := tempRect.right - pane.h;
 MapPt ( tempPt, tempRect, viewRect );
 {set the new scroll bar values}
 SetCtlValue( HScrollBar, tempPt.h );
 SetCtlValue( VScrollBar, tempPt.v );
 InvalRect( theWindow^.portRect );
 end
 else   {no selection was made}
 begin
 ShowHide( OV, FALSE ); {hide window}
 SetPort( oldPort );
 OV_RestoreBits; {replace underneath bits}
 BeginUpdate( theWindow );
 {steal update events}
 EndUpdate  ( theWindow );
 end;
 end; {OV_HandleSelection}
 {----------------------------------}
 Procedure OV_TidyUp;
 begin
 DisposeRgn( dragRectRgn );
 end; {OV_TidyUp}
 {----------------------------------}
begin
 OV_Prepare;
 OV_PositionOverView;
 OV_ShowOverView;
 OV_HandleSelection;
 OV_TidyUp;
end; {OverViewSelect}
END. {UNIT OverView}


program OverView_Sample;
{OverView Sample
 Version 1.0Scott T. Boyd
 Monday, July 14, 1986 3:32:00 AM }

{Compiler Switch Settings}
{$R+}   {range checking on}
{$OV+}  {overflow checking on}

uses MemTypes, QuickDraw, OSIntf, ToolIntf, {$U OverView.p}  OverView;

const
 AppleID = 1;    {Menu ID for Apple menu}
    AboutItem  = 1;  {Item number for About... command}
 FileID  = 2;             {Menu ID for File menu}
    QuitItem    = 1;    {Item number for Quit command}
 EditID  = 3;             {Menu ID for Edit menu}
    UndoItem   = 1;       {Item number for Undo command}
    CutItem    = 3;       {Item number for Cut command}
    CopyItem   = 4;       {Item number for Copy command}
    PasteItem  = 5;       {Item number for Paste command}
    ClearItem  = 6;       {Item number for Clear command}
 ViewID = 4;{Menu ID for for View menu}
    ScreenItem = 1;{Item for Screen res command}
    ImageItem  = 2;{Item for 2 times magnify view cmd}
    LaserItem  = 3;{Item for 4 times magnify view cmd}

var
 AppleMenu     : MenuHandle;
 FileMenu        : MenuHandle;
 EditMenu      : MenuHandle;
 ViewMenu : MenuHandle;

type modes = (screen,image,laser);

var 
 MScope,{for the OverView window record}
 TheWindow: windowPtr;  {fordocument view window}
 OldPort  : GrafPtr; {to keep things tidy}
 TheEvent : EventRecord;
 LaserViewRect,  {the different document sizes}
 ImageViewRect,  {the different document sizes}
 ScreenViewRect, {the different document sizes}
 viewRect : Rect;{the most current size}
 viewmode : modes; {the most current mode}
 myBitMap : BitMap;{OverView offscreen minipict}

var
 VScrollBar,{Handle to vert scroll bar}
 HScrollBar : ControlHandle;     {Handle to hor scroll bar}
 scrollPosition : Point;  {Current scroll position}

{--------------------------------------------------------------------------}

procedure SetUpWindow;
{ Create TheWindow, the "viewing" window that stays up all the time }

var
 dummyRect : Rect;
begin {SetUpWindow}
 with ScreenBits.bounds do {use all avail screen space}
 begin
 dummyRect.top    := top+MenuBarHeight+ScreenMargin+TitleBarHeight;
 dummyRect.left   := left+ScreenMargin;
 dummyRect.bottom := bottom-ScreenMargin;
 dummyRect.right  := right-ScreenMargin;
 end;
 TheWindow := NewWindow (nil, dummyRect, 'PopUp 2D Random Access Scroll 
Bar Menu',true, NoGrowDocProc, WindowPtr(-1), true, longint(0));
 SelectWindow(TheWindow);
 SetPort(TheWindow);
end;  {SetUpWindow}

{--------------------------------------------------------------------------}

procedure SetUpControls;
{ Create and initialize TheWindow's verti and hor scroll bars }
var
 VScrollRect, HScrollRect : Rect;
begin   {SetUpControls}
 with TheWindow^.portRect do 
 SetRect( VScrollRect, right-15, top-1, right+1, bottom-14 );
 with TheWindow^.portRect do 
 SetRect( HScrollRect, left+15, bottom-15, right-14,  bottom+1 );
 VScrollBar  := NewControl(TheWindow, VScrollRect,'',TRUE, 0, 0, viewRect.bottom,ScrollBarProc, 
Longint(0));
 HScrollBar  := NewControl(TheWindow, HScrollRect,'',TRUE, 0, 0, viewRect.right,ScrollBarProc, 
Longint(0));
end;  {SetUpControls}

{--------------------------------------------------------------------------}

procedure SetUpMenus;
var 
 appleTitle: String[1];
begin  {SetUpMenus}
 appleTitle := ' ';
 appleTitle[1] := chr(appleMark);
 AppleMenu := NewMenu(AppleID,appleTitle);   {Create Apple menu}
 AppendMenu(AppleMenu, 'Sorry, just for looks ;(-');
 AddResMenu(AppleMenu, 'DRVR');  {Add desk acc}
 InsertMenu(AppleMenu, 0);{Install at end bar}
 FileMenu := NewMenu(FileID,'File'); {create File menu}
 InsertMenu(FileMenu, 0); {Install at end of bar}
 AppendMenu(FileMenu,'Quit');
 EditMenu := NewMenu(EditID,'Edit'); {create Edit menu}
 InsertMenu(EditMenu, 0); {Install at end of bar}
 AppendMenu(EditMenu,'Undo;(-;Cut;Copy;Paste;Clear');
 ViewMenu := NewMenu(ViewID,'Document Size');
 InsertMenu(ViewMenu, 0); {Install at end of menu bar}
 AppendMenu(ViewMenu,'Screen Resolution');
 AppendMenu(ViewMenu,'ImageWriter Resolution');
 AppendMenu(ViewMenu,'LaserWriter Resolution');
 
 DrawMenuBar;                      {Show new menu bar on screen}
end;  {SetUpMenus}

{--------------------------------------------------------------------------}

procedure DoViewChoice(theItem: INTEGER);
{ Handle a selection from the Document Size menu }
{ NOTE:  It's a bit of work to support multiple document sizes! }

var 
 paneSize : Point;
begin
 case theItem of {select the new virtual document size}
 ScreenItem: viewRect := screenViewRect;
 ImageItem: viewRect := imageViewRect;
 LaserItem: viewRect := laserViewRect;
 end; {case}

 CheckItem(ViewMenu, ScreenItem, false);     {turn off the checkmarks}
 CheckItem(ViewMenu, ImageItem, false);{definitely a brute force method}
 CheckItem(ViewMenu, LaserItem, false);
 CheckItem(ViewMenu, theItem, true);         { checkmark}
 {Reset the scroll bars for the new rectangle size}
 paneSize.h:=TheWindow^.portrect.right - TheWindow^.portrect.left - SBarWidth;
 paneSize.v:=TheWindow^.portrect.bottom - TheWindow^.portrect.top - SBarHeight;
 SetPt(ScrollPosition,0,0);
 SetCtlValue(VScrollBar,0);
 SetCtlValue(HScrollBar,0);
 SetCtlMax(VScrollBar,viewRect.bottom - paneSize.v);
 if viewRect.right < paneSize.h  {Lisa's wide screen}
 then SetCtlMax(HScrollBar,0) {can't scroll if it shows}
 else SetCtlMax(HScrollBar,viewRect.right - paneSize.h);

 ShowControl(VScrollBar);
 ShowControl(HScrollBar);
 DrawControls(TheWindow);
 HiliteControl(VScrollBar, 0);
 HiliteControl(HScrollBar, 0);
end; { DoViewChoice}

{--------------------------------------------------------------------------}

procedure DoMenuClick;
{  Handle mouse-down event in menu bar.  }
var
 menuChoice : LONGINT;    {Menu ID and item number}
 theMenu : INTEGER;{Menu ID of selected menu}
 theItem : INTEGER;{Item number of selected item}
begin {DoMenuClick}
 menuChoice := MenuSelect(TheEvent.where);   {Track mouse}
 if menuChoice <> 0 then  {Nothing to do if 0}
 begin
 theMenu := HiWord(menuChoice); {Get menu ID}
 theItem := LoWord(menuChoice);   {Get item}
 case theMenu of
 AppleID: {DoAppleChoice(theItem)};
 FileID:  if theItem = QuitItem then ExitToShell;
 EditID:  {DoEditChoice(theItem)};
 ViewID:  DoViewChoice(theItem); {only menu}
 end;
 HiliteMenu(0);                      {Unhighlight menu title}
 end; {if MenuChoice}
end; {DoMenuClick}

{----------------------------------------------------------}

procedure DoMouseDown;
{  Handle mouse-down event.  }
var
 whichWindow : WindowPtr; {Window mouse pressed in}
 thePart : INTEGER;{Part of screen }
begin {DoMouseDown}
 {Where on the screen was mouse pressed?}
 thePart := FindWindow(TheEvent.where, whichWindow); 
 case thePart of
 InDesk:         {Do nothing};
 InMenuBar:    DoMenuClick;
 InSysWindow:  {Do nothing};
 InContent: OverViewSelect( TheEvent.where,viewRect, scrollPosition,
MScope,myBitMap,HScrollBar,VScrollBar);
 InDrag:         {Do nothing};
 InGrow:         {Do nothing};
 InGoAway:    if TrackGoAway(TheWindow, TheEvent.where) 
 then ExitToShell;
 end; {case}
end;  {DoMouseDown}

{----------------------------------------------------------}

Procedure DrawPoop;
begin
 {just enough here to show you how to call it}
end;
{----------------------------------------------------------}

begin{main}
 InitGraf(@ThePort); {obligatory material goes here}
 InitFonts;
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs( NIL );
 InitCursor;
 
 SetRect ( LaserViewRect, 0, 0, 2549, 3299 );
 SetRect ( ImageViewRect, 0, 0, 1274, 1649 );
 SetRect ( ScreenViewRect, 0, 0, 636, 824 );
 SetPt ( scrollPosition, 0, 0 );

 SetUpMenus;

 viewmode := screen;
 CheckItem(ViewMenu, ScreenItem, true);{turn on the checkmark}
 case viewmode of
 screen: viewRect := screenViewRect;
 image : viewRect := imageViewRect;
 laser : viewRect := laserViewRect;
 end;
 
 {SetUpControls is dependent on ViewRect being defined}
 SetUpWindow;
 SetUpControls;

 {Create a window for TheWindow.  Make it 1/2 the available
  screen height.  Use myBitMap as the BitMap record, and       point 
to the new window with MScope}

 NewOverView( myBitMap, MScope, viewRect, 0.5 );
 {Draw into the offscreen bitmap with DrawPoop}
 UpdateOverView ( DrawPoop, myBitMap );
 
 repeat
 if GetNextEvent(EveryEvent, TheEvent) then
 case TheEvent.what of
 MouseDown:  DoMouseDown;
   KeyDown:     {Do nothing};
   UpdateEvt:   {Do nothing};
   ActivateEvt: {Do nothing};
 otherwise {Do nothing};
 end {case}
 until TheEvent.what = KeyDown;  {quit if key pressed}
end.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »
Explore some of BBCs' most iconic s...
Despite your personal opinion on the BBC at a managerial level, it is undeniable that it has overseen some fantastic British shows in the past, and now thanks to a partnership with Roblox, players will be able to interact with some of these... | Read more »

Price Scanner via MacPrices.net

You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more

Jobs Board

IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
Top Secret *Apple* System Admin - Insight G...
Job Description Day to Day: * Configure and maintain the client's Apple Device Management (ADM) solution. The current solution is JAMF supporting 250-500 end points, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.