TweetFollow Us on Twitter

Object Shell 1
Volume Number:6
Issue Number:8
Column Tag:MacOOPs!

Object Shell, Part I

By Dr. Christian Stratowa, Vienna, Austria

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

[C. S. started his scientific career as a nuclear physicist (where he has made his Ph.D.), which involved a lot of programming on mainframes. Later on his interests changed, and he moved to Germany to start a career in molecular biology. Beginning 1984, he spent two years at UCSF in San Francisco, where he bought his first Macintosh and became a Mac-addict. Back in Austria he is currently working in the gene technology department of an international pharmaceutical company.]

Part1: Objects, Objects Everywhere!

“If you don’t learn object-oriented programming now, you will not be able to program the Macintosh later.” I think this statement from the technical editor of Apple Direct, a monthly newsletter from Apple Developer Services, is a good way to start my article. It is also supposed to answer your obvious question why present here another shell program although MacTutor has already published so many shells. But this is only part of the story.

I really enjoy reading MacTutor, although as someone who does not have so much time to program the Mac, I find it often hard to follow all the exciting advanced stuff. Maybe others feel like me. Therefore I decided to write a program for “a bimp” (“advanced beginner in Mac programming”) which should be useful as a basis to build more advanced applications. However, when I started to write my own shell I found none of the published code really satisfiable, including the one I had put (i.e. stolen) together from different articles and books. Therefore I decided to restart from the very beginning. Besides that, I may be a little old fashioned but as someone who has started programming back in the bad old days on an IBM 1130 mainframe with 16 k(!)byte core memory (do you remember this word?), I prefer to understand every bit of code, and the best way is still to write your own. So here it is. Using OOPS (object oriented programming style) it is a fairly complete shell program written in Object Pascal (not MacApp, which I don’t have, but using mainly LS-Pascal 2.0 and sometimes TML Pascal II running under MPW), which contains the following features:

• All menus, windows, scrollbars, documents and dialogs are defined as objects which are kept in separate units (e.g. StdMenus, MyMenus, StdWindows, MyDocuments). These units are put together as building blocks to make the final program. Therefore to develop your own application you only need to exchange or modify the units of interest or add your own units (e.g. MyWindows), without changing any code in other units or in the main program.

• The program contains a texteditor with Font, Size, Style and Justification submenus (using old Textedit) and a graphics demo program (drawing ovals, spirals). For both text and graphics fore- and background color can be selected from submenus (using old style QuickDraw).

• An unlimited number of windows can be opened. Text and graphics windows can be open at the same time with the menubar changing dependent on the type of Frontwindow.

• In a submenu you can decide if the window should contain only a grow box or also horizontal and/or vertical scrollbars. To be able to scroll one line a time, an initial scroll delay is established.

• Cut, Copy and Paste is established between different text windows and different graphics windows (but not between text and graphics windows, which would probably be an article by itself).

• The program can save and open ‘TEXT’ and ‘PICT’ documents, whereby PICT documents are saved with a 512 byte header so that MacDraw can open them too. (The program can even open and save MacDraw documents which were saved as PICT files.)

• Both TEXT and PICT documents can be printed on an ImageWriter or LaserWriter using the standard PageSetup and Print dialog boxes.

• All Dialog and Alert boxes are automatically centered for every screen size used.

• Documents can be opened from the Finder by double-clicking.

• Multifinder is supported.

• Program, Text and Pict documents have their own icons.

Although ObjectShell should even run on an old 64k ROM Mac when you don’t use hierarchical menus in unit MyMenus, I have only tested it on a Mac SE or II using System 6.02, and printing on an ImageWriter (I and LQ) or a LaserWriter (Plus and II NTX).

I hope this ObjectShell program, shown in Fig.1, will be a good starting point for the beginning Mac programmer, which s/he could easily modify to write her/his own applications. However, it should be clear by now, that there is no way to write a program for the Mac without having at least the five volume set of Inside Macintosh (IM). It would also be a good idea to have some of the books covering programming on the Mac. A good starting point is to buy Steven Chernicoff’s Macintosh Revealed (MR), which does at the moment (June 1989) consist of three volumes. Last but not least, subscribing to MacTutor (MT) is a must! Supposing, that you have now everything you need, let’s begin to cover the most important features of ObjectShell.

The World of Objects

Almost everything in the world is an object, a house, a door, windows, even more abstract things like documents or menus. Our brain does even treat the stuff it sees on our Macscreen like menus, windows or scrollbars as different objects. So it seems quite naturally to handle these things already during program development as objects, which can communicate together. This is the philosophy behind object oriented programming. Each object does have its own kind of behavior, which can be inherited by sub-objects, and can send messages to other objects.

In Object Pascal you define an object of type window the following way:

{1}
 
type
 TWindow = object(TObject)
 fWPtr: WindowPtr;
 procedure DoOpen;
 end;
 var
 oWindow: TWindow;

Here fWPtr is a field variable like the ones in a Pascal record, and DoOpen is a method. As you can see, in Pascal an object is simply the natural extension of a record. The actual variable you use is called oWindow. Let me state here the first important point: Never forget, that oWindow is not the object itself but a handle to object TWindow! (Actually it’s called object reference variable.) Therefore you have first to create an object before you can use its fields and methods:

{2}

 New(oWindow);
 oWindow.fWPtr:= ....;
 oWindow.DoNew;

Actually, to be able to use variable oWindow throughout my code, regardless of the type of window I want to use, i.e. class TWindow or any of its subclasses (descendants) TGrowWindow, TScrollWindow, etc. I create most of the objects the following way:

{3}

 var
 oWindow: TWindow;
 begin
 New(TScrollWindow(oWindow));
 ...
 oWindow.DoOpen;
 ...
 end;

This sets oWindow to the window type I want and has the advantage, that further code can use variable oWindow without a need to know which subclass of type TWindow (e.g. TScrollWindow) we are definitively using. (Sorrowly, this elegant way can not be used in TML-Pascal II. You have first to create a temporary variable for each subclass and then set oWindow equal to this variable.)

After we have called a method from “outside”, that method begins to execute, and we are “inside” the object. From within a method it is not only possible to call other methods of that object (self.Do...) but we can also take advantage of a feature called inheritance. This has the advantage that you often need only add the code specific to the subclass that overrides a method of its ancestor. In our example:

{4}

procedure TScrollWindow.DoOpen;
 begin
 self.DoNew;
 inherited DoOpen;
 {code specific to subclass}
 end;

Before a scrollwindow can be opened, it has to be created with method self.DoNew. To open it, first the method of its immediate ancestor is executed and then the subclass specific code.

Once you have understood the concept of objects you may find it much easier to write your own code that way. At least this was true for me. For a deeper coverage of objects see earlier issues of MacTutor (MT 12/86, 2/87, 8/87).

Globals and the Main Program

As I have already said earlier ObjectShell is made of self-contained units, which serve as building blocks to put together the final application, as shown in Fig.2 and 3. At the top is unit “GlobalStuff” which does not only contain all global constants and variables but also some procedures, which can be used throughout the program. The main unit, program “ObjectShell”, is located at the bottom level.

The constants at the beginning of unit “GlobalStuff” are diverse program parameters and can be changed by the programmer to fit her/his own needs. As an example it is possible to limit the number of windows the application can open at once to four by setting kMaxWindows=4. With kWindowKind you can determine if your window should only have a growbox or also horizontal and/or vertical scroll bars. (In our demonstration shell this choice is left to the user.) Similarly, kMaxEditItems is the maximum number of editable text fields in any of the dialog boxes we are using.

By the way, for better readability of my code I use following conventions for constants and variables:

 kName  ... global constant
 cName  ... local constant
 gName  ... global variable
 vName  ... local variable
 uName  ... variable global within unit
 oName  ... object type variable
 fName  ... field variable of object or record

The main unit, program ObjectShell itself is designed as general as possible. More specific code is handled in units StdMenus and StdWindows, and application-specific code in units MyMenus, MyDocuments (and if you design your own windows, in unit MyWindows). Therefore, under normal circumstances there should not be any need to change this code when you adopt ObjectShell to your own needs.

The main program does first call routines to initialize the Mac, different globals, printing, and menus, and does then call CheckFinder in case ObjectShell has been launched by double-clicking a document or by selecting a number of documents and choosing “Open” from the Finder. When you select more than one document to open from the Finder, procedure CheckFinder has to take care that the previously opened window will be deactivated properly before creating a new window for the next document. We have therefore to force a deactivation event to make sure the scrollbars are unhighlited. Try to open some MacDraw documents together from the Finder to see that this is not an obvious feature.

The heart of every Mac program is the main event loop, which repeats until gDone returns TRUE, at which time procedure ShutDown is called to dispose of all handles and regions still in use. The main event loop keeps track, if we press a key or the mouse button, if a window needs to be activated or updated, or if other events have occurred. For example, procedure DoDiskEvent makes sure that our program is able to initialize new disks.

Let’s suppose we have clicked in the menubar. Procedure DoMouseDown does then call DoMenuClick, which checks if we have clicked in one of the standard menus (Apple, File, Edit) every application should support (ClickInStdMenus), or in one of the menus specific to our application (ClickInMyMenus). At this point you can see, that both the routines for setting up menus (InitStdMenus, InitMyMenus) and for selecting a menu (ClickInStdMenus, ClickInMyMenus) are implemented in the appropriate units StdMenus and MyMenus, respectively. So let’s first talk about menus.

Attaching Menus

Menus are implemented as objects in unit StdMenus. Class TMenu does contain a menuhandle fMenuHdl as field variable and two self-explanatory methods Create and Choose. Subclass TAppleMenu does contain an additional variable and method for the “About...”box (see MT 6/88 p.85).

In addition to InitStdMenus and ClickInStdMenus three other global procedures do exist. SetStdMenuItems keeps track of the state of the menus, e.g. vMaxWFlag will disable “New” in the File menu if the number of windows open equals kMaxWindows. It is called immediately before calling MenuSelect or MenuKey, since these are the only times the user can see the menu items. DisposeStdMenus is called at ShutDown, although I don’t know if it is really necessary. However, it is always a good programming habit to dispose of all handles yourself. Finally, procedure DoNew, which is normally called from TFileMenu.Choose, must also be made accessible to procedure CheckFinder to be able to supply a window when the user opens a document directly from the Finder.

Windows to the World

Most computers have only one window to display information to the user, the whole screen. This has also been true for the Mac when it became alive the first time by displaying the words “Hello World” on Burell Smith’s screen. Since then the Mac has changed the way people are using computers. Having covered already menus, let’s now see how to handle windows. (Always refer to Inside Macintosh or Macintosh Revealed for additional information!)

Analogous to menus we are defining windows as objects in unit StdWindows. A window consists of the window frame containing title bar, drag region and close region, and the content region, the area our application draws in. When defining object TWindow we divide this content region into a control region (fControlRgn), where growbox, scroll bars and maybe other controls can be placed, and a display region (fDisplayRgn), where the contents of field oDocument are drawn. Placing object oDocument as field variable in TWindow has the advantage that each document is automatically attached to the window it belongs to. In the same way we attach scroll bars oHBar and oVBar to TScrollWindow. (This is a good example of the power and elegance of object oriented programming.)

Once “handle” oWindow is created in DoNew in unit StdMenus, procedure TWindow.DoNew will create the actual window with Toolbox function NewWindow. Most of the window parameters are set in method Initialze, only its port rectangle is calculated in function NewRect, which takes care that further windows will be offset by kWOffset (see Fig.1). After having created the windowpointer fWPtr, we set the window reference constant to self, which is the reference value of the object calling the method. This will enable us later to find the window connected with windowpointer vWPtr by calling:

{5}

 oWindow:= TWindow(GetWRefCon(vWPtr));

This statement will be used by us whenever we need to know which window to work with. After defining window’s control and display region, we finally create the document oDocument attached to the window. Let me state here again, that once created in this way, further coding can be done without knowing the type of document that has to be handled.

When you click the mouse in a window method ClickInWindow will determine in which part of the window the mouse button was pressed, in grow box, scroll bars, drag region, zoom box or within the document. But before the desired action will be executed the window has to be activated.

Activate and Update Events

Activate events take priority over all others. Procedure DoActivate in the main program extracts the window pointer from gEvent.message and calls oWindow.Activate, which sets the new GrafPort. The current port will be changed permanently only here! Depending on the window subclass method Activate will also redraw the Growbox icon and take care, that the scroll bars are highlighted properly.

In contrast to activate events update events have the lowest priority. After extracting the window pointer in DoUpdate, TWindow.Update makes the window the current port, calls BeginUpdate, redraws the window contents and finally calls EndUpdate [see IM I-279]. BeginUpdate temporarily restricts the visible region to the portion of the window that needs redrawing. First we clip to fControlRgn and call DrawGrowIcon and DrawControls. Then we clip to and erase fDisplayRgn and do the actual updating before resetting to the port rectangle. (We will treat updating pictures in more detail in the second part of our article.) Finally, EndUpdate restores the normal visRgn.

The Secret of Resizing Windows

I have played around a lot to find out the best code to resize windows. Although the simplest way would be to resize the window and to invalidate its portRect afterwards therefore updating the whole window, this would mean that e.g. a picture would always be redrawn completely, which could take quite a time in the case of complex pictures. Furthermore, in case you enlarge the window, you would see the scroll bars for a moment within the window. Many of you have probable experienced just this behavior when using MacDraw. To avoid this I have coded method Grow the following way:

After calling GrowWindow to get the new window size I check if the window will be enlarged or made smaller by setting a horizontal and vertical flag. In case the window is enlarged I erase first fControlRgn to prevent scroll bars from cluttering the window and invalidate then fControlRgn before calling SizeWindow; in case it was made smaller I erase and invalidate fControlRgn afterwards only (see Fig.4). That this is the best way to do is also clearly stated in [IM I-292, Figure 10]. Procedure self.ReDrawContent called immediately after ResizeWindow calculates the new sizes of fControlRgn and fDisplayRgn before calling oDocument.ReDraw to redraw the actual document. In this way only the newly exposed part of the window will be updated. That this cannot always be done, is also shown in our application: Because we can change Font, Size etc. of TEXT documents, we have to call TECalText and invalidate fViewRgn afterwards. The same holds true for our PICT documents, where we are able to change fore- and background colors. Try to delete InvalRgn(fViewRgn) at end of TPictDocument.ReDraw and look what happens when you change colors first and enlarge the window afterwards.

For TScrollWindow method ReDrawContent does also calculate the new scrollbar sizes using variable uRect. However, in TScrollBar.ReDraw, uRect is used differently. TopLeft is used to move scroll bars to their new positions, BottomRight to resize them.

Sorrowly, zooming cannot be handled the same way as growing because Toolbox procedure ZoomWindow seems to invalidate always window’s complete portRect. Therefore I erase the portRect only before calling ZoomWindow. No invalidating would be needed to be done by us.

A Problem called “Scrolling”

To supply scrolling for a window has always been the hardest part to program. Recently, two very good articles about scrolling have appeared in MacTutor (MT 3/89 p.62 and MT 4/89 p.11). Especially nice seems to be the “Scrolling Manager”, which probably could be used with small changes instead of my unit StdScroll. The only problem is that for someone who has never programmed in C it is not easy to follow. (Who needs cryptic C or C++? The Mac is a Pascal (!) machine (for how long?), and when you need to speed up part of your code, e.g. to establish Bezier curves, and don’t want to use Assembler, still nothing can beat Fortran! Absoft’s RAT MacFortran compiler and TML Pascal II, both running under MPW, would probably be a combination hard to beat. If Apple would develop ObjectPascal further by e.g. establishing multiple inheritance and adding a Smalltalk like Class Browser, instead of becoming a “me too” C++ user, Pascal could become an even more powerful and elegant development system, much better than for example C-Talk, which looks already a lot like Smalltalk.)

My scroll code is fairly conventional, e.g. not following Joe’s “Golden Ratio of Scrolling”, therefore I will mainly cover here the differences to other people’s code. Besides defining TScrollBar as object, probably the most important difference is the following:

There is really no need to implement scrolling differently for text or graphics! The sole exception is adjusting scroll bars during typing (method Adjust). To understand this let’s see how scrolling is done principally. Every document, be it text or graphics, is drawn on a “sheet of paper” we name fDocRect (also called destination rectangle). We look at this paper through the peephole fViewRect (view rectangle) of our window. Scrolling now means displacing the paper containing our document with respect to our window so that another part of the document comes to view. This is exactly what our core-procedure ScrollContent does:

{6}

OffsetRect(oDocument.fDocRect, dH, dV);
ScrollRect(oDocument.fViewRect, dH, dV);
oDocument.Update;

For text all this is normally done by Toolbox procedure TEScroll (see MR 2-246), but since we have to implement this sequence of commands for graphics anyhow, so why not use this code also for text. The problem of scrolling text one line a time is taken care of by simply setting vAmount to fTextHdl^^.lineHeight in procedure DoScroll. This is our action procedure, which is called every time the user presses the mouse button in one of the scroll bars, or when s/he scrolls with the mouse to the window edge to select text beyond the window boundary (DoAutoScroll).

Autoscrolling is supported both horizontally and vertically for text only. If you find procedure SetupAutoScroll, which is called every time the mouse is clicked in a document (see TScrollWindow.ClickInContent) a little strange, then you are right. The reason for this is that DoAutoScroll is a glue routine called from within procedure SetClikLoop, and DoScroll is an action procedure called from within TrackControl when the mouse button was pressed in the up or down arrow of a scrollbar. Therefore we cannot define DoAutoScroll and DoScroll as methods in TScrollBar. The only way to make the field variables fCtlHdl and oDocument accessible to these procedures is by setting both fields equal to temporary variables in the implementation part of unit StdScroll, and this is done in SetupAutoScroll.

As stated earlier method Adjust is set up for text only. It allows automatic scrolling of text during typing so that the text remains visible and the user can not blame us when s/he types junk. While looking for an easy way to scroll text horizontally when typing past the right end of the window I discovered, that textedit record field selRect does return the caret position in pixels. Therefore I have only to check if the coordinates of the caret are still within the limit of window’s fViewRect otherwise the document has to be scrolled. (selRect.left is limited to -20000 to prevent integer overflow.) When calculating the maximum vertical scroll value function AdjustToCR does correct a bug in Text Edit, where the number of lines is not correct if carriage return is the last character.

On a Mac II text is often scrolled so fast that it is hard to scroll one line a time. Therefore DoScroll implements an initial scroll delay the duration of which in number of ticks is set in global constant kScrollDelay.

On the Mac SE I have made a strange observation, the reason of which I would like to know. When I change e.g. ForeColor to white and BackColor to black, and scroll afterwards, I get a striped pattern as if some region is not updated properly. On a Mac II everything works as it is supposed to. By the way, I have realized that it was always a good idea to change background color to e.g. yellow when testing my program. Doing this I could eliminate some strange bugs I have found when resizing a window, which I would not have detected if the background would have remained white.

A Short Preview

So much for now. Next time we will talk about the units specific to our application. We will cover TEXT and PICT documents, dialog boxes, saving and printing, and Multifinder support.

Meanwhile I hope after studying the code you will be able to enjoy the elegance of using objects as much as I do.

Listing:  GlobalStuff.p

UNIT GlobalStuff;
{***********************************}
INTERFACE

 USES
 MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, MacPrint, ObjIntf;

 CONST
{Constants allowed to change}
 kMasters = 12;  {# MoreMasters}
 kMaxWindows = 0;{# windows: 0 = unlimited}
 kScreenMargin = 2;{screen margin}
 kTextMargin = 10; {text margin}
 kPrintMargin = 0.5;{margin around printed page}
 kHBarMargin = 40; {left margin of horiz.
 scroll bar, usually 0}
 kWOffset = 18;  {# pixels to offset windows}
 kWindowKind = 8;{# see Window constants}
 kScrollDelay = 15;{# ticks for initial scroll delay}
 kMaxEditItems = 6;{max # of editable text in dialogboxes}
 kCreator = 'CSOS';{Christian Stratowa Object Shell}

{Document constants}
 kPictDoc = 'PICT';{First document in typelist}
 kTextDoc = 'TEXT';{Second document in typelist}
 kDocKind = 'TEXT';{Document type in File menu}
 kPictHeader = 512;{#bytes for header}

{Window constants}
 kNoGrow = 1;
 kGrow = 2;
 kHScroll = 3;
 kVScroll = 4;
 kScroll = 5;
 kHScrollZoom = 6;
 kVScrollZoom = 7;
 kScrollZoom = 8;

{Scroll bar constants}
 kHBar = 1;
 kVBar = 2;
 kSBarWidth = 15;

{ASCII constants}
 kCR = 13;
 kBS = 8;
 kEnter = 3;

{Apple Menu}
 kAppleID = 1;
 kAbout = 1;

{File Menu}
 kFileID = 2;
 kNew = 1;
 kOpen = 2;
 kClose = 3;
   {----}
 kSave = 5;
 kSaveAs = 6;
   {----}
 kPageSetUp = 8;
 kPrint = 9;
   {----}
 kQuit = 11;

{Edit Menu}
 kEditID = 3;
 kUndo = 1;
   {----}
 kCut = 3;
 kCopy = 4;
 kPaste = 5;
 kClear = 6;

{*begin MyMenus*}
{New Menu}
 kNewID = 21;

{New Picture Menu}
 kNewPictID = 211;

{New Text Menu}
 kNewTextID = 212;

{Graphics Menu}
 kGraphID = 4;
 kOvals = 1;
 kSpirals = 2;

{Color Menu}
 kColorID = 5;
 kBlack = 1;
 kWhite = 2;
 kRed = 3;
 kGreen = 4;
 kBlue = 5;
 kCyan = 6;
 kMagenta = 7;
 kYellow = 8;

{ForeColor Menu}
 kForeCID = 51;

{BackColor Menu}
 kBackCID = 52;

{Text Menu}
 kTextID = 6;

{Font Menu}
 kFontID = 61;

{Size Menu}
 kSizeID = 62;

{Style Menu}
 kStyleID = 63;
 kPlain = 1;
 kBold = 2;
 kItalic = 3;
 kUnderline = 4;
 kOutline = 5;
 kShadow = 6;
 kCondense = 7;
 kExtend = 8;

{Align Menu}
 kJustID = 64;
 kLeft = 1;
 kCenter = 2;
 kRight = 3;

{PopUp Menus}
 kPopUpID1 = 10;
 kFrameOval = 1;
 kInvertOval = 2;
 kFrameRect = 3;
 kInvertRect = 4;
{*end MyMenus*}

{Alert constants}
 kAboutID = 1000;
 kErrID = 1001;
 kSizeErrID = 1002;
 kSaveID = 1003;

{Dialog constants}
 kPrintID = 2000;

{Str# constants}
 kStrListID = 1000;
 kOSErrID = 1001;

{low memory constants}
 ROM85 = $28E;
 MBarHeight = $BAA;

 VAR
{Rectangle variables}
 gDeskTopRect: Rect;
 gDragRect: Rect;
 gGrowRect: Rect;

{Window variables}
 gMinWidth: Integer;
 gMinHeight: Integer;
 gWCount: Integer;
 gCloseFlag: BOOLEAN;

{menu variables}
 gMBarHeight: Integer;

{Cursor variables}
 gWatch: Cursor;
 gCross: Cursor;
 gIBeam: Cursor;

{Print variables}
 gPrintHdl: THPrint;

{Event variables}
 gQuitting: BOOLEAN;
 gDone: BOOLEAN;
 gMFEvent: BOOLEAN;
 gNextEvent: BOOLEAN;
 gEvent: EventRecord;
 gSleep: LongInt;
 gMouseRgn: RgnHandle;
{----------------------------------------}
 PROCEDURE SetEnable(vRsrcID, vItem: Integer;
 vFlag: BOOLEAN);
 PROCEDURE CenterDialogBox(vWidth,vHeight: Integer;
 VAR vPt: Point);
 PROCEDURE CenterMyDialog(vType: OSType; vResID: Integer);
 FUNCTION IsAppWindow(vWPtr: WindowPtr): BOOLEAN;
 FUNCTION OSError(vErr: OSErr): BOOLEAN;

{****************************************}
IMPLEMENTATION

PROCEDURE SetEnable(vRsrcID, vItem: Integer; vFlag: BOOLEAN);
BEGIN
 IF vFlag THEN
 EnableItem(GetMHandle(vRsrcID), vItem)
 ELSE
 DisableItem(GetMHandle(vRsrcID), vItem)
END;  {SetEnable}

{========================================}
PROCEDURE CenterDialogBox(vWidth,vHeight: Integer;
 VAR vPt: Point);
BEGIN
 WITH screenBits.bounds DO
 BEGIN
 vPt.h:= (right - left - vWidth) DIV 2;
 vPt.v:= (bottom - top - vHeight) DIV 3;
 END;
END;  {CenterDialogBox}

{========================================}
PROCEDURE CenterMyDialog(vType: OSType; vResID: Integer);
 VAR
 vHdl: Handle;
 vRect: Rect;
 vWidth, vHeight: Integer;
 vPt: Point;
BEGIN
 vHdl:= GetResource(vType, vResID);
 HNoPurge(vHdl);
 IF (vType = 'DLOG') THEN
 vRect:= DialogTHndl(vHdl)^^.boundsRect
 ELSE IF (vType = 'ALRT') THEN
 vRect:= AlertTHndl(vHdl)^^.boundsRect;

 WITH vRect DO
 BEGIN
 vWidth:= (right - left);
 vHeight:= (bottom - top);
 CenterDialogBox(vWidth, vHeight, vPt);
 left:= vPt.h;
 right:= vPt.h + vWidth;
 top:= vPt.v;
 bottom:= vPt.v + vHeight;
 END;

 IF (vType = 'DLOG') THEN
 DialogTHndl(vHdl)^^.boundsRect:= vRect
 ELSE IF (vType = 'ALRT') THEN
 AlertTHndl(vHdl)^^.boundsRect:= vRect;
 SetResPurge(TRUE);
 HPurge(vHdl);
 InitCursor;
END;  {CenterMyDialog}

{========================================}
 FUNCTION IsAppWindow(vWPtr: WindowPtr):BOOLEAN;
 BEGIN
 IF vWPtr = NIL THEN
 IsAppWindow:= FALSE
 ELSE
 WITH WindowPeek(vWPtr)^ DO
 IsAppWindow:= (windowkind = userkind);
 END; {IsAppWindow}

{========================================}
FUNCTION OSError(vErr: OSErr): BOOLEAN;
 VAR
 vErrNr: Str255;
 vErrMess: Str255;
 vItem: Integer;
BEGIN
 OSError:= (vErr <> noErr);
 CASE vErr OF
 DupFNErr: 
 GetIndString(vErrMess, kOSErrID, 1);
 OpWrErr: 
 GetIndString(vErrMess, kOSErrID, 2);
 IPrAbort: 
 Exit(OSError);
 OTHERWISE
 vErrMess:= '';
 END;

 NumToString(vErr, vErrNr);
 ParamText(vErrNr, vErrMess, '', '');
 IF vErr <> noErr THEN
 BEGIN
 CenterMyDialog('ALRT', kErrID);
 IF StopAlert(kErrID, NIL) = OK THEN
 END;
END;  {OSError}

END. {unit GlobalStuff}
{****************************************}
Listing:  StdMenus.p

UNIT StdMenus;
{****************************************}
INTERFACE

 USES
 MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, MacPrint, ObjIntf, 
GlobalStuff, AboutBox, StdWindows;

 TYPE
 TMenu = OBJECT(TObject)
 fMenuHdl: MenuHandle;
 PROCEDURE Create(vRsrcID: Integer);
 PROCEDURE Choose(vItem: Integer);
 END;

 TAppleMenu = OBJECT(TMenu)
 fAboutID: Integer;
 PROCEDURE Setup(vAlertID: Integer);
 PROCEDURE Create(vRsrcID: Integer);
 override;
 PROCEDURE Choose(vItem: Integer);
 override;
 END;

 TFileMenu = OBJECT(TMenu)
 PROCEDURE Choose(vItem: Integer);
 override;
 END;

 TEditMenu = OBJECT(TMenu)
 PROCEDURE Choose(vItem: Integer);
 override;
 END;

 VAR
 oAppleMenu: TAppleMenu;
 oFileMenu: TFileMenu;
 oEditMenu: TEditMenu;

 PROCEDURE InitStdMenus;
 PROCEDURE ClickInStdMenus(vMenu,vItem: Integer);
 PROCEDURE SetStdMenuItems;
 PROCEDURE DisposeStdMenus;
 PROCEDURE DoNew(vWindowKind: Integer; vDocType: OSType);

{****************************************}
IMPLEMENTATION

PROCEDURE InitStdMenus;
BEGIN
 New(oAppleMenu);
 oAppleMenu.Setup(kAboutID);
 oAppleMenu.Create(kAppleID);

 New(oFileMenu);
 oFileMenu.Create(kFileID);

 New(oEditMenu);
 oEditMenu.Create(kEditID);
END;  {InitStdMenus}

{========================================}
PROCEDURE ClickInStdMenus(vMenu, vItem: Integer);
BEGIN
 CASE vMenu OF
 kAppleID: 
 oAppleMenu.Choose(vItem);
 kFileID: 
 oFileMenu.Choose(vItem);
 kEditID: 
 oEditMenu.Choose(vItem);
 OTHERWISE
 END;
END;  {ClickInStdMenus}

{========================================}
PROCEDURE SetStdMenuItems;
 VAR
 vFrontWFlag: BOOLEAN;
 vUserWFlag: BOOLEAN;
 vMaxWFlag: BOOLEAN;

BEGIN
 vFrontWFlag:= (FrontWindow <> NIL);
 vUserWFlag := IsAppWindow(FrontWindow);
 vMaxWFlag:= NOT ((kMaxWindows <> 0) AND
 (gWCount = kMaxWindows));

 SetEnable(kFileID, kNew, vMaxWFlag);
 SetEnable(kFileID, kClose, vFrontWFlag);
 SetEnable(kFileID, kSave, vUserWFlag);
 SetEnable(kFileID, kSaveAs, vUserWFlag);
 SetEnable(kFileID, kPrint, vUserWFlag);
 SetEnable(kEditID, 0, vFrontWFlag);

  IF vFrontWFlag AND NOT vUserWFlag THEN
   BEGIN
    SetEnable(kEditID, kCut, TRUE);
    SetEnable(kEditID, kCopy, TRUE);
    SetEnable(kEditID, kPaste, TRUE);
   END;
END;  {SetStdMenuItems}

{========================================}
PROCEDURE DisposeStdMenus;
BEGIN
 oAppleMenu.Free;
 oFileMenu.Free;
 oEditMenu.Free;
END;  {DisposeStdMenus}

{========================================}
PROCEDURE TMenu.Create(vRsrcID: Integer);
BEGIN
 HLock(Handle(Self));
 fMenuHdl:= GetMenu(vRsrcID);
 InsertMenu(fMenuHdl, 0);
 HUnlock(Handle(Self));
END; {Create}

{----------------------------------------}
PROCEDURE TMenu.Choose(vItem: Integer);
BEGIN
END; {Choose}

{========================================}
PROCEDURE TAppleMenu.Setup(vAlertID: Integer);
BEGIN
 fAboutID:= vAlertID
END; {Setup}

{----------------------------------------}
PROCEDURE TAppleMenu.Create(vRsrcID: Integer);
BEGIN
 HLock(Handle(Self));
 fMenuHdl:= GetMenu(vRsrcID);
 AddResMenu(fMenuHdl, 'DRVR');
 InsertMenu(fMenuHdl, 0);
 HUnlock(Handle(Self));
END; {Create}

{----------------------------------------}
PROCEDURE TAppleMenu.Choose(vItem: Integer);
 VAR
 vDAName: Str255;
 vDANumber: Integer;
 vSavePort: GrafPtr;
BEGIN
 IF vItem = kAbout THEN
 DoAbout(fAboutID)
 ELSE
 BEGIN
 GetPort(vSavePort);
 GetItem(fMenuHdl, vItem, vDAName);
 vDANumber:= OpenDeskAcc(vDAName);
 SetPort(vSavePort);
 END;
END; {Choose}

{========================================}
PROCEDURE DoNew(vWindowKind: Integer; vDocType: OSType);
BEGIN
{for TML create tempvariables!}
 CASE vWindowKind OF
 kNoGrow: 
 New(TWindow(oWindow));
 kGrow: 
 New(TGrowWindow(oWindow));
 kHScroll..kScroll: 
 New(TScrollWindow(oWindow));
 kHScrollZoom..kScrollZoom: 
 IF BitTst(Ptr(ROM85), 0) THEN
 New(TScrollWindow(oWindow))
 ELSE
 New(TScrollZoomWindow(oWindow));
 OTHERWISE
 END;
 oWindow.fWKind:= vWindowKind;
 oWindow.DoNew(vDocType);
 ShowWindow(oWindow.fWPtr);
END; {DoNew}

{----------------------------------------}
PROCEDURE DoOpen;
 VAR
 vPt: Point;
 vNumTypes: Integer;
 vTypeList: SFTypeList;
 vReply: SFReply;
 vWPeek: WindowPeek;

BEGIN
 CenterDialogBox(348, 200, vPt);
 vNumTypes:= 2;
 vTypeList[0]:= kPictDoc;
 vTypeList[1]:= kTextDoc;
 SFGetFile(vPt, '', NIL, vNumTypes, vTypeList, NIL, vReply);

 WITH vReply DO
 IF good THEN
 BEGIN
 IF IsAppWindow(FrontWindow) THEN
 IF DuplicateFileName(FrontWindow, fName, vFrontW) THEN
 IF OSError(opWRErr) THEN
 Exit(DoOpen);
 DoNew(kWindowKind, fType);
 oWindow.DoOpen(fName, vRefNum);
 END;
END; {DoOpen}

{----------------------------------------}
PROCEDURE DoClose;
 VAR
 vDANumber: Integer;
BEGIN
 IF IsAppWindow(FrontWindow) THEN
 BEGIN
 oWindow:= TWindow(GetWRefCon(FrontWindow));
 oWindow.DoClose;
 END
 ELSE
 BEGIN
 vDANumber:= WindowPeek(FrontWindow)^.windowkind;
 CloseDeskAcc(vDANumber);
 END;
END; {DoClose}

{----------------------------------------}
PROCEDURE DoSave(vSaveAs: BOOLEAN);
BEGIN
 oWindow:= TWindow(GetWRefCon(FrontWindow));
 IF oWindow.Saved(vSaveAs) THEN
END; {DoSave}

{----------------------------------------}
PROCEDURE DoPageSetup;
 VAR
 vFlag: BOOLEAN;
BEGIN
 PrOpen;
 IF OSError(PrError) THEN
 Exit(DoPageSetup);
 SetCursor(arrow);
 vFlag:= PrValidate(gPrintHdl);
 vFlag:= PrStlDialog(gPrintHdl);
 vFlag:= OSError(PrError);
 PrClose;
 IF OSError(PrError) THEN
END; {DoPageSetup}

{----------------------------------------}
PROCEDURE DoPrint;
 VAR
 vFlag: BOOLEAN;
BEGIN
 oWindow:= TWindow(GetWRefCon(FrontWindow));

 PrOpen;
 IF OSError(PrError) THEN
 Exit(DoPrint);
 SetCursor(arrow);
 vFlag:= PrValidate(gPrintHdl);
 IF PrJobDialog(gPrintHdl) THEN
 oWindow.DoPrint;
 PrClose;

 IF OSError(PrError) THEN
END; {DoPrint}

{----------------------------------------}
PROCEDURE DoQuit;
BEGIN
 gQuitting:= TRUE;
 WHILE (FrontWindow <> NIL) AND gQuitting DO
 DoClose;
 IF gQuitting THEN
 gDone:= TRUE;
END; {DoQuit}

{----------------------------------------}
PROCEDURE TFileMenu.Choose(vItem: Integer);
BEGIN
 CASE vItem OF
 kNew: 
 DoNew(kWindowKind, kDocKind);
 kOpen: 
 DoOpen;
 kClose: 
 DoClose;
 kSave: 
 DoSave(FALSE);
 kSaveAs: 
 DoSave(TRUE);
 kPageSetUp: 
 DoPageSetup;
 kPrint: 
 DoPrint;
 kQuit: 
 DoQuit;
 END;
END; {Choose}

{========================================}
PROCEDURE TEditMenu.Choose(vItem: Integer);
BEGIN
 IF NOT SystemEdit(vItem - 1) THEN
 IF FrontWindow <> NIL THEN
 BEGIN
 oWindow:= TWindow(GetWRefCon(FrontWindow));
 oWindow.Edit(vItem);
 END;
END; {Choose}

END. {unit StdMenus}
{****************************************}
Listings:  StdWindows.p

UNIT StdWindows;
{****************************************}
INTERFACE

 USES
 MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, MacPrint, ObjIntf, 
GlobalStuff, StdScroll, MyDocuments;

 TYPE
 TWindow = OBJECT(TObject)
 fWKind: Integer;
 fWPtr: WindowPtr;
 fControlRgn: RgnHandle;
 fDisplayRgn: RgnHandle;
 oDocument: TDocument;
 FUNCTION ControlRgn: RgnHandle;
 FUNCTION DisplayRgn(vCtlRgn:RgnHandle): RgnHandle;
 PROCEDURE Initialize;
 PROCEDURE DoNew(vDocType: OSType);
 PROCEDURE DoOpen(vFileName: Str255;
 vVolNum: Integer);
 FUNCTION Saved(vSaveAs: BOOLEAN): BOOLEAN;
 PROCEDURE DoClose;
 PROCEDURE DoPrint;
 PROCEDURE ApplTask;
 PROCEDURE ReDrawContent;
 PROCEDURE Grow;
 PROCEDURE Zoom(vPart: Integer);
 PROCEDURE ClickInContent(vPt: Point);
 PROCEDURE ClickInWindow(vPart: Integer);
 PROCEDURE KeyPress(vChar: CHAR);
 PROCEDURE Update;
 PROCEDURE Activate;
 PROCEDURE Edit(vItem: Integer);
 PROCEDURE Free;
 override;
 END;

 TGrowWindow = OBJECT(TWindow)
 FUNCTION ControlRgn: RgnHandle;
 override;
 PROCEDURE Initialize;
 override;
 PROCEDURE Grow;
 override;
 PROCEDURE Activate;
 override;
 END;

 TScrollWindow = OBJECT(TGrowWindow)
 oVBar: TScrollBar;
 oHBar: TScrollBar;
 FUNCTION ControlRgn: RgnHandle;
 override;
 PROCEDURE DoNew(vDocType: OSType);
 override;
 PROCEDURE DoOpen(vFileName: Str255;
 vVolNum: Integer);
 override;
 PROCEDURE ReDrawContent;
 override;
 PROCEDURE ClickInContent(vPt: Point);
 override;
 PROCEDURE KeyPress(vChar: CHAR);
 override;
 PROCEDURE Activate;
 override;
 PROCEDURE Edit(vItem: Integer);
 override;
 PROCEDURE Free;
 override;
 END;

 TScrollZoomWindow = OBJECT(TScrollWindow)
 PROCEDURE Initialize;
 override;
 PROCEDURE Zoom(vPart: Integer);
 override;
 END;

 VAR
 oWindow: TWindow;

 FUNCTION DuplicateFileName(vWPtr: WindowPtr;
 vFileName: Str255;
 VAR vNextWindow: WindowPeek): BOOLEAN;

{****************************************}
IMPLEMENTATION

 VAR
 uNr: Str255;
 uRect: Rect;
 uTitle: Str255;
 uVisible: BOOLEAN;
 uWindID: Integer;
 uGoAway: BOOLEAN;
 uRefVal: longint;

{========================================}
FUNCTION DuplicateFileName(vWPtr: WindowPtr;
 vFileName: Str255;
 VAR vNextWindow: WindowPeek): BOOLEAN;
BEGIN
 DuplicateFileName:= FALSE;
 vNextWindow:= WindowPeek(vWPtr);
 REPEAT
 uTitle:= vNextWindow^.titleHandle^^;
 IF vFileName = uTitle THEN
 BEGIN
 DuplicateFileName := TRUE;
 leave;
 END;
 vNextWindow:= vNextWindow^.nextWindow;
 UNTIL (vNextWindow = NIL);
END;  {DuplicateFileName}

{========================================}
FUNCTION TWindow.ControlRgn: RgnHandle;
 VAR
 vRgn: RgnHandle;
BEGIN
 vRgn:= NewRgn;
 SetEmptyRgn(vRgn);
 ControlRgn:= vRgn;
END;  {ControlRgn}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
FUNCTION TWindow.DisplayRgn(vCtlRgn: RgnHandle): RgnHandle;
 VAR
 vRgn: RgnHandle;
BEGIN
 uRect:= fWPtr^.portRect;
 vRgn:= NewRgn;
 RectRgn(vRgn, uRect);
 DiffRgn(vRgn, vCtlRgn, vRgn);
 DisplayRgn:= vRgn;
END;  {DisplayRgn}

{----------------------------------------}
PROCEDURE TWindow.Initialize;
BEGIN
 GetIndString(uTitle, kStrListID, 1);
 uTitle:= Concat(uTitle, uNr);
 uVisible:= FALSE;
 uWindID:= noGrowDocProc;
 uGoAway:= TRUE;
 uRefVal:= 0;
END; {Initialize}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
FUNCTION NewRect(vCount: Integer): Rect;
 VAR
 vOffset: Integer;
BEGIN
 vOffset:= (vCount - 1) * kWOffset;
 IF gMinWidth > gMinHeight THEN
 vOffset:= vOffset MOD (gMinWidth -kWOffset)
 ELSE
 vOffset:= vOffset MOD (gMinHeight -2*kWOffset);

 WITH ScreenBits.Bounds DO
 BEGIN
 NewRect.left:= kScreenMargin + vOffset;
 NewRect.top:= 2 * gMBarHeight + vOffset;
 NewRect.right:= right - kScreenMargin - gMinWidth + vOffset;
 NewRect.bottom:= bottom - kScreenMargin - gMinHeight + vOffset;
 END;
END;  {NewRect}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE TWindow.DoNew(vDocType: OSType);
BEGIN
 gWCount:= gWCount + 1;
 uRect:= NewRect(gWCount);
 NumToString(gWCount, uNr);
 self.Initialize;

 fWPtr:= NewWindow(NIL, uRect, uTitle, uVisible, uWindID, POINTER(-1), 
uGoAway, uRefVal);
 IF (fWPtr = NIL) THEN
 Exit(DoNew);
 SetWRefCon(fWPtr, Ord4(self));
 SetPort(fWPtr);

 fControlRgn:= self.ControlRgn;
 fDisplayRgn:= self.DisplayRgn(fControlRgn);

{for TML define tempvariables!}
 IF vDocType = kTextDoc THEN
 New(TTextDocument(oDocument))
 ELSE IF vDocType = kPictDoc THEN
 New(TPictDocument(oDocument))
 ELSE
 ; {Initialize your own type of document}

 WITH oDocument DO
 BEGIN
 fViewRgn := self.fDisplayRgn;
 fViewRect := self.fDisplayRgn^^.rgnBBox;
 fDocType := vDocType;
 DoNew;
 END;
END; {DoNew}

{----------------------------------------}
PROCEDURE TWindow.DoOpen(vFileName: Str255;
 vVolNum: Integer);
 VAR
 vFileNum: Integer;
BEGIN
 IF OSError(FSOpen(vFileName,vVolNum,vFileNum)) THEN
 Exit(DoOpen);
 SetCursor(gWatch);
 oDocument.DoOpen(vVolNum, vFileNum);
 SetCursor(arrow);
 IF oDocument.fFileNum <> 0 THEN
 BEGIN
 SetWTitle(fWPtr, vFileName);
 InvalRgn(fDisplayRgn);
 END;
 IF OSError(FSClose(vFileNum)) THEN
END; {DoOpen}

{----------------------------------------}
FUNCTION TWindow.Saved(vSaveAs:BOOLEAN): BOOLEAN;
 VAR
 vNewFile: BOOLEAN;
 vPt: Point;
 vText: Str255;
 vReply: SFReply;
 vErr: OSErr;
 vFInfo: FInfo;
 vFileNum: Integer;
 vVolNum: Integer;
 vType: OSType;
 vWPeek: WindowPeek;
 vOldWPtr: WindowPtr;
 oOldDoc: TDocument;
BEGIN
 Saved:= FALSE;
 GetWTitle(fWPtr, uTitle);
 vNewFile:= (oDocument.fFileNum = 0);
 IF vSaveAs OR vNewFile THEN
 BEGIN
 CenterDialogBox(304, 184, vPt);
 GetIndString(vText, kStrListID, 2);
 SFPutFile(vPt, vText, uTitle, NIL, vReply);
 WITH vReply DO
 IF good THEN
 BEGIN
 vErr:= GetFInfo(fName, vRefNum, vFInfo);

 IF DuplicateFileName(fWPtr, fName, vWPeek) THEN
 BEGIN
 self.Initialize;
 vOldWPtr:= WindowPtr(vWPeek);
 SetWTitle(vOldWPtr, uTitle);
 oOldDoc:= TWindow(GetWRefCon(vOldWPtr)).oDocument;
 vVolNum:= oOldDoc.fVolNum;
        IF oOldDoc.fFileNum <> 0 THEN
 IF OSError(FSDelete(fName, vVolNum)) THEN
 Exit(Saved);
 oOldDoc.fFileNum:= 0;
 oOldDoc.fChanged:= TRUE;
 vErr:= GetFInfo(fName, vRefNum, vFInfo);
 END;

 CASE vErr OF
 noErr: 
 ;
 fnfErr: 
 BEGIN
 vType:= oDocument.fDocType;
 IF OSError(Create(fName, vRefNum, kCreator, vType)) THEN
 Exit(Saved);
 END;
 OTHERWISE
 IF OSError(vErr) THEN
 Exit(Saved);
 END;

 uTitle:= fName;
 SetWTitle(fWPtr, uTitle);
 oDocument.fVolNum:= vRefNum;
 END
 ELSE
 Exit(Saved);
 END;
 IF OSError(FSOpen(uTitle, oDocument.fVolNum, vFileNum)) THEN
 Exit(Saved);

 SetCursor(gWatch);
 oDocument.DoSave(vFileNum);
 SetCursor(arrow);

 IF OSError(FSClose(vFileNum)) THEN
 Exit(Saved);
 IF OSError(FlushVol(NIL, oDocument.fVolNum)) THEN
 Exit(Saved);
 Saved:= TRUE;
END; {Saved}

{----------------------------------------}
PROCEDURE TWindow.DoClose;
 VAR
 vNextWindow: WindowPeek;
BEGIN
 IF oDocument.fChanged THEN
 BEGIN
 GetWTitle(fWPtr, uTitle);
 ParamText(uTitle, '', '', '');
 CenterMyDialog('ALRT', kSaveID);
 CASE Alert(kSaveID, NIL) OF
 OK: 
 IF NOT self.Saved(FALSE) THEN
 Exit(DoClose);
 cancel: 
 BEGIN
 gQuitting:= FALSE;
 Exit(DoClose);
 END;
 OTHERWISE
 END;
 END;

 vNextWindow:= WindowPeek(FrontWindow)^.nextWindow;
 IF (gWCount = 1) OR (vNextWindow^.windowkind <> userkind) THEN
 gCloseFlag:= TRUE;
 gWCount:= gWCount - 1;
 self.Free;
END; {DoClose}

{----------------------------------------}
PROCEDURE DisplayPrintDialog(VAR vDPtr: DialogPtr);
 VAR
 vFlag: BOOLEAN;
 vDEvent: EventRecord;
 vDItem: Integer;
BEGIN
 CenterMyDialog('DLOG', kPrintID);
 vDPtr:= GetNewDialog(kPrintID, NIL, POINTER(-1));
 vFlag:= GetNextEvent(updateMask, vDEvent);
 vFlag:= DialogSelect(vDEvent, vDPtr, vDItem);
END; {DisplayPrintDialog}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE TWindow.DoPrint;
 VAR
 vSavePort: GrafPtr;
 vPrPort: TPPrPort;
 vFlag: BOOLEAN;
 vPrStatus: TPrStatus;
 vDPtr: DialogPtr;
BEGIN
 GetPort(vSavePort);
 SetCursor(gWatch);

 vPrPort:= PrOpenDoc(gPrintHdl, NIL, NIL);
 IF OSError(PrError) THEN
 Exit(DoPrint);
 DisplayPrintDialog(vDPtr);
 oDocument.DoPrint(vPrPort);
 PrCloseDoc(vPrPort);

 vFlag:= NOT OSError(PrError);
 vFlag:= (gPrintHdl^^.prJob.bJDocLoop = bSpoolLoop) AND vFlag;
 IF vFlag THEN
 BEGIN
 SetCursor(gWatch);
 PrPicFile(gPrintHdl, NIL, NIL, NIL, vPrStatus);
 END;
 vFlag:= OSError(PrError);
 DisposDialog(vDPtr);
 SetPort(vSavePort);
END; {DoPrint}

{----------------------------------------}
PROCEDURE TWindow.ApplTask;
BEGIN
 oDocument.ApplTask;
END; {ApplTask}

{----------------------------------------}
PROCEDURE TWindow.ReDrawContent;
BEGIN
 DisposeRgn(fControlRgn);
 DisposeRgn(fDisplayRgn);
 fControlRgn:= self.ControlRgn;
 fDisplayRgn:= self.DisplayRgn(fControlRgn);

 WITH oDocument DO
 BEGIN
 fViewRgn := self.fDisplayRgn;
 fViewRect := self.fDisplayRgn^^.rgnBBox;
 ReDraw;
 END;
END; {ReDrawContent}

{----------------------------------------}
PROCEDURE TWindow.Grow;
BEGIN
END; {Grow}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE TWindow.Zoom;
BEGIN
END; {Zoom}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE TWindow.ClickInContent(vPt: Point);
BEGIN
 GlobalToLocal(vPt);
 IF PtInRgn(vPt, fDisplayRgn) THEN
 oDocument.ClickInDoc(vPt);
END; {ClickInContent}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE TWindow.ClickInWindow (vPart: Integer);
 VAR
 vSavePort: GrafPtr;
BEGIN
 GetPort(vSavePort);
 SetPort(fWPtr);
 WITH gEvent DO
 CASE vPart OF
 inDrag: 
 IF fWPtr <> FrontWindow THEN
 SelectWindow(fWPtr)
 ELSE
 DragWindow(fWPtr, where, gDragRect);
 inGoAway: 
 IF TrackGoAway(fWPtr, where) THEN
 self.DoClose;
 inGrow: 
 self.Grow;
 inZoomIn, inZoomOut: 
 IF TrackBox(fWPtr, where, vPart) THEN
 self.Zoom(vPart);
 inContent: 
 IF fWPtr <> FrontWindow THEN
 SelectWindow(fWPtr)
 ELSE
 self.ClickInContent(where);
 OTHERWISE
 END;
 SetPort(vSavePort);
END; {ClickInWindow}

{----------------------------------------}
PROCEDURE TWindow.KeyPress(vChar: CHAR);
BEGIN
 oDocument.KeyPress(vChar);
END; {KeyPress}

{----------------------------------------}
PROCEDURE TWindow.Update;
 VAR
 vSavePort: GrafPtr;
BEGIN
 GetPort(vSavePort);
 SetPort(fWPtr);

 BeginUpdate(fWPtr);
 SetClip(fControlRgn);
 IF fWKind <> kNoGrow THEN
 DrawGrowIcon(fWPtr);
 DrawControls(fWPtr);
 SetClip(fDisplayRgn);
 EraseRgn(fDisplayRgn);
 oDocument.Update;
 ClipRect(fWPtr^.portRect);
 EndUpdate(fWPtr);

 SetPort(vSavePort);
END; {Update}

{----------------------------------------}
PROCEDURE TWindow.Activate;
BEGIN
 SetPort(fWPtr);
 oDocument.Activate;
END; {Activate}

{----------------------------------------}
PROCEDURE TWindow.Edit(vItem: Integer);
BEGIN
 oDocument.Edit(vItem);
END; {Edit}

{----------------------------------------}
PROCEDURE TWindow.Free;
BEGIN
 oDocument.Free;
 DisposeRgn(fControlRgn);
 DisposeRgn(fDisplayRgn);
 DisposeWindow(fWPtr);
 INHERITED Free;
END; {Free}

{========================================}
FUNCTION TGrowWindow.ControlRgn: RgnHandle;
 VAR
 vRgn: RgnHandle;
BEGIN
 vRgn:= NewRgn;
 uRect:= fWPtr^.portRect;
 WITH uRect DO
 BEGIN
 left:= right - kSBarWidth;
 top:= bottom - kSBarWidth;
 END;
 RectRgn(vRgn, uRect);

 ControlRgn:= vRgn;
END;  {ControlRgn}

{----------------------------------------}
PROCEDURE TGrowWindow.Initialize;
BEGIN
 GetIndString(uTitle, kStrListID, 1);
 uTitle:= Concat(uTitle, uNr);
 uVisible:= FALSE;
 uWindID:= documentProc;
 uGoAway:= TRUE;
 uRefVal:= 0;
END; {Initialize}

{----------------------------------------}
PROCEDURE TGrowWindow.Grow;
 VAR
 vNewSize: LongInt;
 vWidth: Integer;
 vHeight: Integer;
 vHFlag, vVFlag: BOOLEAN;
BEGIN
 vNewSize:= GrowWindow(fWPtr, gEvent.where, gGrowRect);
 IF vNewSize <> 0 THEN
 BEGIN
 vWidth:= LoWord(vNewSize);
 vHeight:= HiWord(vNewSize);
 WITH fWPtr^.portRect DO
 BEGIN
 vHFlag:= (vWidth > (right - left));
 vVFlag:= (vHeight > (bottom - top));
 END;

 IF vHFlag OR vVFlag THEN
 BEGIN
 EraseRgn(fControlRgn);
 InvalRgn(fControlRgn);
 END;
 SizeWindow(fWPtr, vWidth, vHeight, TRUE);
 self.ReDrawContent;
 IF NOT vHFlag OR NOT vVFlag THEN
 BEGIN
 EraseRgn(fControlRgn);
 InvalRgn(fControlRgn);
 END;
 END;
END; {Grow}

{----------------------------------------}
PROCEDURE TGrowWindow.Activate;
BEGIN
 INHERITED Activate;
 SetClip(fControlRgn);
 DrawGrowIcon(fWPtr);
 ClipRect(fWPtr^.portRect);
END; {Activate}

{========================================}
FUNCTION TScrollWindow.ControlRgn: RgnHandle;
 VAR
 vRgn1, vRgn2: RgnHandle;
BEGIN
 vRgn1:= INHERITED ControlRgn;
 IF fWKind IN [kHScroll, kScroll, kHScrollZoom, kScrollZoom] THEN
 BEGIN
 vRgn2:= NewRgn;
 WITH fWPtr^.portRect DO
 SetRect(uRect, left, bottom - kSBarWidth, right - kSBarWidth, bottom);
 RectRgn(vRgn2, uRect);
 UnionRgn(vRgn1, vRgn2, vRgn1);
 DisposeRgn(vRgn2);
 END;

 IF fWKind IN [kVScroll, kScroll, kVScrollZoom, kScrollZoom] THEN
 BEGIN
 vRgn2:= NewRgn;
 WITH fWPtr^.portRect DO
 SetRect(uRect, right - kSBarWidth, top, right, bottom - kSBarWidth);
 RectRgn(vRgn2, uRect);
 UnionRgn(vRgn1, vRgn2, vRgn1);
 DisposeRgn(vRgn2);
 END;

 ControlRgn:= vRgn1;
END;  {ControlRgn}

{----------------------------------------}
PROCEDURE TScrollWindow.DoNew(vDocType: OSType);
BEGIN
 INHERITED DoNew(vDocType);
 oHBar:= NIL;
 oVBar:= NIL;
 IF fWKind IN [kHScroll, kScroll, kHScrollZoom, kScrollZoom] THEN
 BEGIN
 New(oHBar);
 oHBar.oDocument:= self.oDocument;
 WITH fWPtr^.portRect DO
 SetRect(uRect, -1 + kHBarMargin, bottom - kSBarWidth, right - kSBarWidth 
+ 1, bottom + 1);
 oHBar.DoNew(kHBar, fWPtr, uRect);
 END;

 IF fWKind IN [kVScroll, kScroll, kVScrollZoom, kScrollZoom] THEN
 BEGIN
 New(oVBar);
 oVBar.oDocument:= self.oDocument;
 WITH fWPtr^.portRect DO
 SetRect(uRect, right - kSBarWidth, -1, right + 1, bottom - kSBarWidth 
+ 1);
 oVBar.DoNew(kVBar, fWPtr, uRect);
 END;

 InitAutoScroll(oDocument);
END; {DoNew}

{----------------------------------------}
PROCEDURE TScrollWindow.DoOpen(vFileName:Str255;
 vVolNum: Integer);
BEGIN
 INHERITED DoOpen(vFileName, vVolNum);
 IF oHBar <> NIL THEN
 oHBar.Adjust;
 IF oVBar <> NIL THEN
 oVBar.Adjust;
END; {DoOpen}

{----------------------------------------}
PROCEDURE TScrollWindow.ReDrawContent;
BEGIN
 INHERITED ReDrawContent;

 IF oHBar <> NIL THEN
 BEGIN
 WITH fWPtr^.portRect DO
 SetRect(uRect, -1 + kHBarMargin, bottom - kSBarWidth, right - kSBarWidth 
+ 2 - kHBarMargin, kSBarWidth + 1);
 oHBar.ReDraw(uRect);
 END;

 IF oVBar <> NIL THEN
 BEGIN
 WITH fWPtr^.portRect DO
 SetRect(uRect, right - kSBarWidth, -1, kSBarWidth + 1, bottom - kSBarWidth 
+ 2);
 oVBar.ReDraw(uRect);
 END;
END; {ReDrawContent}

{----------------------------------------}
PROCEDURE TScrollWindow.ClickInContent(vPt: Point);
 VAR
 vPart: Integer;
 vCtlHdl: ControlHandle;
BEGIN
 GlobalToLocal(vPt);
 vPart:= FindControl(vPt, fWPtr, vCtlHdl);
 CASE vPart OF
 inUpButton..inPageDown, inThumb: 
 CASE GetCRefCon(vCtlHdl) OF
 kHBar: 
 oHBar.ClickInSBar(vPart, vPt);
 kVBar: 
 oVBar.ClickInSBar(vPart, vPt);
 END;
 inButton: 
 ;
 inCheckBox: 
 ;
 OTHERWISE
 BEGIN
 IF oHBar <> NIL THEN
 oHBar.SetupAutoScroll(fWKind);
 IF oVBar <> NIL THEN
 oVBar.SetupAutoScroll(fWKind);
 LocalToGlobal(vPt);
 INHERITED ClickInContent(vPt);
 END;
 END;
END; {ClickInContent}

{----------------------------------------}
PROCEDURE TScrollWindow.KeyPress(vChar: CHAR);
BEGIN
 INHERITED KeyPress(vChar);
 IF oHBar <> NIL THEN
 oHBar.Adjust;
 IF oVBar <> NIL THEN
 oVBar.Adjust;
END; {KeyPress}

{----------------------------------------}
PROCEDURE TScrollWindow.Activate;
BEGIN
 INHERITED Activate;
 IF oHBar <> NIL THEN
 oHBar.Activate;
 IF oVBar <> NIL THEN
 oVBar.Activate;
END; {Activate}

{----------------------------------------}
PROCEDURE TScrollWindow.Edit(vItem: Integer);
BEGIN
 INHERITED Edit(vItem);
 IF oHBar <> NIL THEN
 oHBar.Adjust;
 IF oVBar <> NIL THEN
 oVBar.Adjust;
END; {Edit}

{----------------------------------------}
PROCEDURE TScrollWindow.Free;
BEGIN
 KillControls(fWPtr);
 IF oHBar <> NIL THEN
 oHBar.Free;
 IF oVBar <> NIL THEN
 oVBar.Free;
 INHERITED Free;
END; {Free}

{========================================}
PROCEDURE TScrollZoomWindow.Initialize;
BEGIN
 GetIndString(uTitle, kStrListID, 1);
 uTitle:= Concat(uTitle, uNr);
 uVisible:= FALSE;
 uWindID:= documentProc + kScrollZoom;
 uGoAway:= TRUE;
 uRefVal:= 0;
END; {Initialize}

{----------------------------------------}
PROCEDURE TScrollZoomWindow.Zoom(vPart: Integer);
BEGIN
 EraseRect(fWPtr^.portRect);
 ZoomWindow(fWPtr, vPart, TRUE);
 self.ReDrawContent;
END; {Zoom}

END.  {unit StdWindows}
{****************************************}

Listing:  StdScroll.p

UNIT StdScroll;
{****************************************}
INTERFACE

 USES
 MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, MacPrint, ObjIntf, 
GlobalStuff, MyDocuments;

 TYPE
 TScrollBar = OBJECT(TObject)
 fCtlHdl: ControlHandle;
 oDocument: TDocument;
 FUNCTION ScrollMax(vBar,vMargin: Integer): Integer;
 PROCEDURE DoNew(vBar: Integer;
 vWPtr: WindowPtr;
 vRect: Rect);
 PROCEDURE ClickInSBar(vCtlItem: Integer;
 vPt: Point);
 PROCEDURE SetupAutoScroll(vSBars: Integer);
 PROCEDURE Adjust;
 PROCEDURE ReDraw(vRect: Rect);
 PROCEDURE Activate;
 END;

 PROCEDURE InitAutoScroll(oDoc: TDocument);

{****************************************}
IMPLEMENTATION

 VAR
 uHHdl: ControlHandle;
 uVHdl: ControlHandle;
 uSBars: Integer;
 oDoc: TDocument;
 uScrollDelay, uDelay: LongInt;

{========================================}
FUNCTION TScrollBar.ScrollMax(vBar, vMargin: Integer): Integer;
 VAR
 vDocWidth: Integer;
 vDocHeight: Integer;
 vMax: Integer;
BEGIN
 WITH oDocument.fDocRect DO
 BEGIN
 vDocWidth:= right - left;
 vDocHeight:= bottom - top;
 END;

 vMax:= 0;
 WITH oDocument.fViewRect DO
 CASE vBar OF
 kHBar: 
 vMax:= vDocWidth - (right-left) + vMargin;
 kVBar: 
 vMax:= vDocHeight - (bottom-top) + vMargin;
 END;
 IF vMax < 0 THEN
 vMax:= 0;
 ScrollMax:= vMax;
END; {ScrollMax}

{----------------------------------------}
PROCEDURE TScrollBar.DoNew (vBar: Integer;
 vWPtr: WindowPtr;
 vRect: Rect);
 VAR
 vTitle: Str255;
 vVisible: BOOLEAN;
 vCtlValue: Integer;
 vCtlMin: Integer;
 vCtlMax: Integer;
 vProcID: Integer;
 vRefVal: longint;
BEGIN
 vTitle:= '';
 vVisible:= TRUE;
 vCtlValue:= 0;
 vCtlMin:= 0;
 vCtlMax:= ScrollMax(vBar, 0);
 vProcID:= scrollBarProc;
 vRefVal:= vBar;
 fCtlHdl:= NewControl(vWPtr, vRect, vTitle, vVisible, vCtlValue, vCtlMin, 
vCtlMax, vProcID, vRefVal);
END; {DoNew}

{----------------------------------------}
PROCEDURE ScrollContent(vCtlHdl: ControlHandle);
 VAR
 vCtlValue: Integer;
 vDocRect: Rect;
 vDeltaH, vDeltaV: Integer;
 vUpdateRgn: RgnHandle;
BEGIN
 HLock(Handle(oDoc));
 WITH oDoc DO
 BEGIN
 vCtlValue:= GetCtlValue(vCtlHdl);
 SetClip(fViewRgn);
 vDeltaH:= 0;
 vDeltaV:= 0;
 vUpdateRgn:= NewRgn;
 CASE GetCRefCon(vCtlHdl) OF
 kHBar: 
 vDeltaH:= fViewRect.left - fDocRect.left - vCtlValue;
 kVBar: 
 vDeltaV:= fViewRect.top - fDocRect.top - vCtlValue;
 END;
 vDocRect := fDocRect;
 OffsetRect(vDocRect, vDeltaH, vDeltaV);
 fDocRect := vDocRect;
 ScrollRect(fViewRect, vDeltaH, vDeltaV, vUpdateRgn);
 SetClip(vUpdateRgn);
 Update;
 ClipRect(thePort^.portRect);
 DisposeRgn(vUpdateRgn);
 END;
 HUnlock(Handle(oDoc));
END;  {ScrollContent}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE DoScroll (vCtlHdl: ControlHandle;
 vPart: Integer);
 VAR
 vCtlValue: Integer;
 vCtlMax: Integer;
 vCtlMin: Integer;
 vSize: Integer;
 vPageSize: Integer;
 vAmount: Integer;

BEGIN
 vCtlValue:= GetCtlValue(vCtlHdl);
 vCtlMax:= GetCtlMax(vCtlHdl);
 vCtlMin:= GetCtlMin(vCtlHdl);

 vAmount:= 0;
 IF oDoc.fDocType = kTextDoc THEN
 vSize:= oDoc.fTextHdl^^.lineHeight
 ELSE
 vSize:= 5;

 WITH oDoc.fViewRect DO
 CASE GetCRefCon(vCtlHdl) OF
 kHBar: 
 vPageSize:= (right-left) DIV 2;
 kVBar: 
 vPageSize:= ((bottom-top) DIV vSize -1)* vSize
 END;

 CASE vPart OF
 inUpButton: 
 IF vCtlValue > vCtlMin THEN
 vAmount:= -vSize;
 inDownButton: 
 IF vCtlValue < vCtlMax THEN
 vAmount:= +vSize;
 inPageUp: 
 IF vCtlValue > vCtlMin THEN
 vAmount:= -vPageSize;
 inPageDown: 
 IF vCtlValue < vCtlMax THEN
 vAmount:= +vPageSize;
 OTHERWISE
 END;

 IF vAmount <> 0 THEN
 BEGIN
 SetCtlValue(vCtlHdl, vCtlValue + vAmount);
 ScrollContent(vCtlHdl);
 IF uScrollDelay > 0 THEN
 uScrollDelay:= uScrollDelay - 5
 ELSE
 uScrollDelay:= 0;
 Delay(uScrollDelay, uDelay);
 END;
END; {DoScroll}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE TScrollBar.ClickInSBar(vCtlItem: Integer;
 vPt: Point);
BEGIN
 oDoc:= self.oDocument;
 CASE vCtlItem OF
 inUpButton..inPageDown: 
 BEGIN
 uScrollDelay:= kScrollDelay;
 vCtlItem:= TrackControl(fCtlHdl, vPt, @DoScroll);
 END;
 inThumb: 
 BEGIN
 vCtlItem:= TrackControl(fCtlHdl, vPt, NIL);
 ScrollContent(fCtlHdl);
 END;
 END;
END; {ClickInSBar}

{----------------------------------------}
FUNCTION DoAutoScroll: BOOLEAN;
 VAR
 vOldClip: RgnHandle;
 vPt: Point;
 vViewRect: Rect;

BEGIN
 vOldClip:= NewRgn;
 GetClip(vOldClip);
 ClipRect(thePort^.portRect);
 GetMouse(vPt);
 vViewRect:= oDoc.fViewRgn^^.rgnBBox;

 IF uSBars IN [kHScroll, kScroll,
 kHScrollZoom, kScrollZoom] THEN
 IF vPt.h < vViewRect.left THEN
 DoScroll(uHHdl, InUpButton)
 ELSE IF vPt.h > vViewRect.right THEN
 DoScroll(uHHdl, InDownButton);

 IF uSBars IN [kVScroll, kScroll,
 kVScrollZoom, kScrollZoom] THEN
 IF vPt.v < vViewRect.top THEN
 DoScroll(uVHdl, InUpButton)
 ELSE IF vPt.v > vViewRect.bottom THEN
 DoScroll(uVHdl, InDownButton);

 SetClip(vOldClip);
 DisposeRgn(vOldClip);
 DoAutoScroll:= TRUE;
END; {DoAutoScroll}

{----------------------------------------}
PROCEDURE InitAutoScroll (oDoc: TDocument);
BEGIN
 IF oDoc.fDocType = kTextDoc THEN
 SetClikLoop(@DoAutoScroll, oDoc.fTextHdl)
 ELSE IF oDoc.fDocType = kPictDoc THEN
 ; {Install Autoscroll for graphics}
END; {InitAutoScroll}

{----------------------------------------}
PROCEDURE TScrollBar.SetupAutoScroll(vSBars: Integer);
BEGIN
 oDoc:= self.oDocument;
 uSBars:= vSBars;
 uScrollDelay:= 0;
 CASE GetCRefCon(fCtlHdl) OF
 kHBar: 
 uHHdl:= fCtlHdl;
 kVBar: 
 uVHdl:= fCtlHdl;
 END;

 IF vSBars IN [kHScroll, kHScrollZoom] THEN
 uVHdl:= NIL;
 IF vSBars IN [kVScroll, kVScrollZoom] THEN
 uHHdl:= NIL;
END; {SetupAutoScroll}

{----------------------------------------}
FUNCTION AdjustToCR(vTextHdl: TEHandle): Integer;
 VAR
 vCharHdl: CharsHandle;

BEGIN
 WITH vTextHdl^^ DO
 BEGIN
 AdjustToCR:= 0;
 vCharHdl:= CharsHandle(hText);
 IF teLength > 0 THEN
 IF vCharHdl^^[teLength - 1] = Chr(kCR) THEN
 AdjustToCR:= lineHeight;
 END;
END; {AdjustToCR}

{-  -  -  -  -  -  -  -  -  -  -  -  -  -}
PROCEDURE TScrollBar.Adjust;
 VAR
 vViewWidth: Integer;
 vViewHeight: Integer;
 vCtlValue: Integer;
 vCtlMax: Integer;
 vScrollFlag: BOOLEAN;

BEGIN
 oDoc:= self.oDocument;
 WITH oDocument DO
 IF fDocType = kTextDoc THEN
 BEGIN
 WITH fViewRect DO
 BEGIN
 vViewWidth:= (right-left) - kTextMargin;
 vViewHeight:= (bottom-top);
 END;

 WITH fTextHdl^^.selRect DO
 CASE GetCRefCon(fCtlHdl) OF
 kHBar: 
 BEGIN
 vCtlMax:= self.ScrollMax(kHBar, 0);
 SetCtlMax(fCtlHdl, vCtlMax);
 vScrollFlag:=(left < fViewRect.left) 
 OR (right > fViewRect.right);
 IF vScrollFlag THEN
 BEGIN
 IF left < -20000 THEN
 left:= -20000;
 vCtlValue:= left - fDocRect.left - vViewWidth;
 SetCtlValue(fCtlHdl, vCtlValue);
 ScrollContent(fCtlHdl);
 END;
 END;

 kVBar: 
 BEGIN
 vCtlMax:= self.ScrollMax(kVBar, AdjustToCR(fTextHdl));
 SetCtlMax(fCtlHdl, vCtlMax);

 vScrollFlag:= (top < fViewRect.top) OR (bottom > fViewRect.bottom);
 vScrollFlag:= vScrollFlag OR (fDocRect.bottom < fViewRect.bottom);

 IF vScrollFlag THEN
 BEGIN
 vCtlValue:= bottom - fDocRect.top - vViewHeight;
 SetCtlValue(fCtlHdl, vCtlValue);
 ScrollContent(fCtlHdl);
 END;
 END;
 END;
 END
 ELSE IF fDocType = kPictDoc THEN
 ; {Code for graphics}
END; {Adjust}

{----------------------------------------}
PROCEDURE TScrollBar.ReDraw (vRect: Rect);
 VAR
 vDelta: Integer;
 vDocRect: Rect;

BEGIN
 HideControl(fCtlHdl);
 WITH vRect, oDocument DO
 CASE GetCRefCon(fCtlHdl) OF
 kHBar: 
 BEGIN
 MoveControl(fCtlHdl, left, top);
 SizeControl(fCtlHdl, right, bottom);
 SetCtlMax(fCtlHdl, ScrollMax(kHBar, 0));
 vDelta:= GetCtlMax(fCtlHdl) + fDocRect.left - fViewRect.left;
 IF vDelta < 0 THEN
 BEGIN
 vDocRect:= fDocRect;
 OffsetRect(vDocRect, -vDelta, 0);
 fDocRect:= vDocRect;
 END;
 END;

 kVBar: 
 BEGIN
 MoveControl(fCtlHdl, left, top);
 SizeControl(fCtlHdl, right, bottom);
 SetCtlMax(fCtlHdl, ScrollMax(kVBar, 0));
 vDelta:= GetCtlMax(fCtlHdl) + fDocRect.top - fViewRect.top;
 IF vDelta < 0 THEN
 BEGIN
 vDocRect:= fDocRect;
 OffsetRect(vDocRect, 0, -vDelta);
 fDocRect:= vDocRect;
 END;
 END;
 END;
 ShowControl(fCtlHdl);
END; {ReDraw}

{----------------------------------------}
PROCEDURE TScrollBar.Activate;
BEGIN
 IF BitAnd(gEvent.modifiers, activeFlag) <> 0 THEN
 HiliteControl(fCtlHdl, 0)
 ELSE
 HiliteControl(fCtlHdl, 255);
END; {Activate}

END.  {unit StdScroll}
{****************************************}
Listing:  AboutBox.p

UNIT AboutBox;
{****************************************}
INTERFACE

 USES
 MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, MacPrint, GlobalStuff;

 PROCEDURE DoAbout(vAboutID: Integer);

{****************************************}
IMPLEMENTATION

PROCEDURE DoAbout(vAboutID: Integer);
BEGIN
 ParamText('Christian Stratowa', '', '', '');
 CenterMyDialog('ALRT', vAboutID);
 IF Alert(vAboutID, NIL) = OK THEN
END; {DoAbout}

END. {unit AboutBox}
{****************************************}
Listing:  ObjectShell.p

PROGRAM ObjectShell;
{****************************************}
 USES
 MemTypes, QuickDraw, OSIntf, ToolIntf, PackIntf, MacPrint, ObjIntf, 
GlobalStuff, StdWindows, StdMenus, MyMenus;

{========================================}
PROCEDURE Escape;
BEGIN
 ExitToShell
END; {Escape}

{========================================}
PROCEDURE InitToolbox (vMasters: Integer);
 VAR
 i: Integer;

BEGIN
 MaxApplZone;
 FOR i:= 1 TO vMasters DO
 MoreMasters;
 InitGraf(@thePort);
 InitFonts;
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs(@Escape);
 InitCursor;
 FlushEvents(everyEvent, 0);
END; {InitToolbox}

{----------------------------------------}
PROCEDURE InitGlobals;
 TYPE
 tShortPointer = ^Integer;

 VAR
 vShortPtr: tShortPointer;

BEGIN
 IF BitTst(Ptr(ROM85), 0) THEN
 gMBarHeight:= 20
 ELSE
 BEGIN
 vShortPtr:= tShortPointer(MBarHeight);
 gMBarHeight:= vShortPtr^;
 END;

 IF BitTst(Ptr(ROM85), 0) THEN
 gMFEvent:= FALSE
 ELSE
 gMFEvent:= (NGetTrapAddress($60, ToolTrap) <> 
 NGetTrapAddress($9F, ToolTrap));
 gSleep:= 10;
 gMouseRgn:= NewRgn;
 gMouseRgn:= NIL;

 gWatch:= GetCursor(watchCursor)^^;
 gCross:= GetCursor(crossCursor)^^;
 gIBeam:= GetCursor(iBeamCursor)^^;

 gWCount:= 0;
 gCloseFlag:= FALSE;
 gDone:= FALSE;
END;  {InitGlobals}

{----------------------------------------}
PROCEDURE InitRects;
BEGIN
 gMinWidth:= 108;
 gMinHeight:= 72;
 WITH ScreenBits.Bounds DO
 BEGIN
 SetRect(gDeskTopRect, left, top+gMBarHeight, right, bottom);
 gDragRect:= gDeskTopRect;
 InsetRect(gDragRect, kScreenMargin, kScreenMargin);
 SetRect(gGrowRect, gMinWidth, gMinHeight, right-kScreenMargin, bottom-kScreenMargin);
 END;
END;  {InitRects}

{----------------------------------------}
PROCEDURE InitPrint;
BEGIN
 gPrintHdl:= THPrint(NewHandle(SizeOf(TPrint)));
 PrOpen;
 IF OSError(PrError) THEN
 gPrintHdl:= NIL;
 PrintDefault(gPrintHdl);
 IF OSError(PrError) THEN
 gPrintHdl:= NIL;
 PrClose;
 IF OSError(PrError) THEN
 gPrintHdl:= NIL;
END;  {InitPrint}

{----------------------------------------}
PROCEDURE CheckFinder;
 VAR
 vMessage: Integer;
 vDocMax: Integer;
 vIndex: Integer;
 vInfo: AppFile;

BEGIN
 CountAppFiles(vMessage, vDocMax);
 IF vDocMax > 0 THEN
 IF vMessage = appOpen THEN
 FOR vIndex:= 1 TO vDocMax DO
 BEGIN
        IF FrontWindow <> NIL THEN
        BEGIN
 gEvent.message:= GetWRefCon(oWindow.fWPtr);
        BitClr(@gEvent.modifiers, 15);
        oWindow.Activate;
        END;

 GetAppFiles(vIndex, vInfo);
 WITH vInfo DO
 BEGIN
 DoNew(kWindowKind, fType);
 oWindow.DoOpen(fName, vRefNum);
 END;
 ClrAppFiles(vIndex);
 END;
END; {CheckFinder}

{========================================}
PROCEDURE DoApplTasks;
BEGIN
 IF IsAppWindow(FrontWindow) THEN
   BEGIN
 oWindow:= TWindow(GetWRefCon(FrontWindow));
 oWindow.ApplTask;
 END;

 IF gCloseFlag THEN
 BEGIN
 SetMyMenuBar;
 gCloseFlag:= FALSE;
 END;
END; {DoApplTasks}

{========================================}
PROCEDURE DoMenuClick(vMenuCode: LongInt);
 VAR
 vMenu: Integer;
 vItem: Integer;

BEGIN
 vMenu:= HiWord(vMenuCode);
 vItem:= LoWord(vMenuCode);
 CASE vMenu OF
 kAppleID..kEditID: 
 ClickInStdMenus(vMenu, vItem);
 OTHERWISE
 ClickInMyMenus(vMenu, vItem);
 END;
 HiliteMenu(0);
END; {DoMenuClick}

{----------------------------------------}
PROCEDURE DoWindowClick(vPart: Integer;
 vWPtr: WindowPtr);
BEGIN
 oWindow:= TWindow(GetWRefCon(vWPtr));
 oWindow.ClickInWindow(vPart);
END;    {DoWindowClick}

{----------------------------------------}
PROCEDURE DoMouseDown;
 VAR
 vPart: Integer;
 vWhichWindow: WindowPtr;

BEGIN
 WITH gEvent DO
 BEGIN
 vPart:= FindWindow(where, vWhichWindow);
 CASE vPart OF
 inDesk: 
 SysBeep(1);
 inMenuBar: 
 BEGIN
 SetStdMenuItems;
 SetMyMenuItems;
 DoMenuClick(MenuSelect(where));
 END;
 inSysWindow: 
 SystemClick(gEvent, vWhichWindow);
 OTHERWISE
 DoWindowClick(vPart, vWhichWindow);
 END;
 END;
END; {DoMouseDown}

{========================================}
PROCEDURE DoKeyPress (vChar: CHAR);
BEGIN
 oWindow.KeyPress(vChar);
END;  {DoKeyPress}

{----------------------------------------}
PROCEDURE DoKeyDown;
 VAR
 vCharCode: CHAR;

BEGIN
 WITH gEvent DO
 BEGIN
 vCharCode:= CHR(BitAnd(message,charCodeMask));
 IF BitAnd(modifiers, CmdKey) = CmdKey THEN
 BEGIN
 SetStdMenuItems;
 SetMyMenuItems;
 DoMenuClick(MenuKey(vCharCode));
 END
 ELSE
 DoKeyPress(vCharCode);
 END;
END; {DoKeyDown}

{========================================}
PROCEDURE DoUpdate;
 VAR
 vWhichWindow: WindowPtr;

BEGIN
 vWhichWindow:= WindowPtr(gEvent.message);
 oWindow:= TWindow(GetWRefCon(vWhichWindow));
 oWindow.Update;
END; {DoUpdate}

{========================================}
PROCEDURE DoActivate;
 VAR
 vWhichWindow: WindowPtr;

BEGIN
 vWhichWindow:= WindowPtr(gEvent.message);
 oWindow:= TWindow(GetWRefCon(vWhichWindow));
 oWindow.fWPtr:= vWhichWindow;
 oWindow.Activate;

 SetMyMenuBar;
END; {DoActivate}

{========================================}
 PROCEDURE DoDiskEvent;
 VAR
 vPt: Point;
 vErr: OSErr;

 BEGIN
 IF HiWrd(gEvent.message) <> noErr THEN
 BEGIN
 CenterDialogBox(290, 100, vPt);
 vErr:= DIBadMount(vPt, gEvent.message);
 IF vErr IN [1, 2, noErr, ioErr, badMDBErr, noMacDskErr] THEN
 ELSE IF OSError(vErr) THEN
 END;
 END; {DoDiskEvent}

{========================================}
PROCEDURE DoMFEvent;
 VAR
 vWhichWindow: WindowPtr;

BEGIN
 IF Odd(gEvent.message) THEN
 BitSet(@gEvent.modifiers, 15)
 ELSE
 BitClr(@gEvent.modifiers, 15);

 IF IsAppWindow(FrontWindow) THEN
 BEGIN
 oWindow:= TWindow(GetWRefCon(FrontWindow));
 oWindow.fWPtr:= FrontWindow;
 oWindow.Activate;
 END;
END; {DoMFEvent}

{========================================}
PROCEDURE ShutDown;
BEGIN
 DisposHandle(Handle(gPrintHdl));
 DisposeRgn(gMouseRgn);
 DisposeStdMenus;
 DisposeMyMenus;
END; {ShutDown}

{========================================}
{$I-}
BEGIN {main}
 InitToolbox(kMasters);
 InitGlobals;
 InitRects;
 InitPrint;

 InitStdMenus;
 InitMyMenus;
 DrawMenuBar;

 CheckFinder;

 REPEAT
 DoApplTasks;
 IF gMFEvent THEN
 gNextEvent:= WaitNextEvent(everyEvent, gEvent, gSleep, gMouseRgn)
 ELSE
 BEGIN
 SystemTask;
 gNextEvent:= GetNextEvent(everyEvent, gEvent);
 END;
 IF gNextEvent THEN
 CASE gEvent.what OF
 NullEvent: 
 ;
 MouseDown: 
 DoMouseDown;
 KeyDown: 
 DoKeyDown;
 AutoKey: 
 DoKeyDown;
 UpdateEvt: 
 DoUpdate;
 ActivateEvt: 
 DoActivate;
 DiskEvt: 
 DoDiskEvent;
 NetworkEvt: 
 ;
 DriverEvt: 
 ;
 App1Evt: 
 ;
 App2Evt: 
 ;
 App3Evt: 
 ;
 App4Evt: 
 DoMFEvent;
 OTHERWISE
 END;
 UNTIL gDone;

 ShutDown;
END. {main}
{****************************************}

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
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 »

Price Scanner via MacPrices.net

Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
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

Jobs Board

Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.