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

Adobe Creative Cloud 2.2.0.129 - Access...
Adobe Creative Cloud costs $49.99/month (or less if you're a previous Creative Suite customer). Creative Suite 6 is still available for purchase (without a monthly plan) if you prefer. Introducing... Read more
Tower 2.2.3 - Version control with Git m...
Tower is a powerful Git client for OS X that makes using Git easy and more efficient. Users benefit from its elegant and comprehensive interface and a feature set that lets them enjoy the full power... Read more
Apple Java 2015-001 - For OS X 10.7, 10....
Apple Java for OS X 2015-001 installs the legacy Java 6 runtime for OS X 10.11 El Capitan, OS X 10.10 Yosemite, OS X 10.9 Mavericks, OS X 10.8 Mountain Lion, and OS X 10.7 Lion. This package is... Read more
Adobe Muse CC 2015 2015.0.1 - Design and...
Muse CC 2015 is available as part of Adobe Creative Cloud for as little as $14.99/month (or $9.99/month if you're a previous Muse customer). Muse CS6 is still available for purchase (without a... Read more
Adobe Illustrator CC 2015 19.1.0 - Profe...
Illustrator CC 2015 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous Illustrator customer). Illustrator CS6 is still available for... Read more
Corel Painter 14.1.0.1105 - Digital art...
Corel Painter helps you create astonishing art in a variety of media. Paint with vivid oil paints, fluid water colors, and earthy charcoals. Corel Painter flawlessly recreates the tones and textures... Read more
Pacifist 3.5.4 - Install individual file...
Pacifist opens up .pkg installer packages, .dmg disk images, .zip, .tar. tar.gz, .tar.bz2, .pax, and .xar archives and more, and lets you extract or install individual files out of them. This is... Read more
Merlin Project 3.1.0.40305 - Project man...
Merlin Project is for those of you who are responsible for complex projects. Simple lists of tasks won't suffice. Good planning raises questions about the dependencies of activities on each other,... Read more
DM1 2.0 - Advanced drum machine. (Commer...
DM1 is an advanced Drum Machine. It turns your computer into a fun and creative beat making machine. Easy and fast to use, loaded with 86 superb electronic drum kits and beautiful hyper-realistic... Read more
Posterino 3.2.1 - Create posters, collag...
Posterino offers enhanced customization and flexibility including a variety of new, stylish templates featuring grids of identical or odd-sized image boxes. You can customize the size and shape of... Read more

Mazes of Karradash (Games)
Mazes of Karradash 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: The city of Karradash is under attack: the monsters of the Shadow Realms are emerging from the depths.No adventurer is... | Read more »
Battle Golf is the Newest Game from the...
Wrassling was a pretty weird - and equally great - little wressling game. Now the developers, Folmer Kelly and Colin Lane, have turned their attention to a different sport: golfing. This is gonna be weird. [Read more] | Read more »
Qbert Rebooted has the App Store Going...
The weird little orange... whatever... is back, mostly thanks to that movie which shall remain nameless (you know the one). But anyway it's been "rebooted" and now you can play the fancy-looking Qbert Rebooted on iOS devices. [Read more] | Read more »
Giant Monsters Run Amok in The Sandbox...
So The Sandbox has just hit version number 1.99987 (seriously), and it's added a lot more stuff. Just like every other update, really. [Read more] | Read more »
Fish Pond Park (Games)
Fish Pond Park 1.0.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.0 (iTunes) Description: Nurture an idyllic slice of tourist's heaven into the top nature spot of the nation, furnishing it with a variety of... | Read more »
Look after Baby Buddy on your Apple Watc...
Parigami Gold is the new premium version of the match three puzzler that includes Apple Watch support and all new content. You won't simply be sliding tiles around on your wrist, the Apple Watch companion app is an all new mini-game in itself. You'... | Read more »
Swallow all of your opponents as the big...
Eat all of the opposition and become the largest ball in Battle of Balls now available in the App Store and Google Play. Battle of Balls pits you against other opponents in real time and challenges you to eat more balls and grow larger than all of... | Read more »
PAC-MAN Championship Edition DX (Games)
PAC-MAN Championship Edition DX 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: It’s Your World. EAT IT! Get ready for more ghost chain gobbling and frantic action in PAC-MAN® CE-DX! The... | Read more »
incurve (Games)
incurve 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Get ready for 2 different gravities Goal is to hit as many white dots on your way up.When you're touching the screen, the dots have a... | Read more »
Crossy Road has its Own Merch Store Now....
Do you like Crossy Road? I mean do you really like Crossy Road? Well then you're in luck! Hipster Whale has opened up a Crossy Road store, so you can show off your fandom via official T-shirts. [Read more] | Read more »

Price Scanner via MacPrices.net

Apple restocks refurbished Mac minis for up t...
The Apple Store has restocked Apple Certified Refurbished 2014 Mac minis, with models available starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: - 1.4GHz... Read more
13-inch 2.5GHz MacBook Pro on sale for $899,...
Best Buy has the 13″ 2.5GHz MacBook Pro available for $899.99 on their online store. Choose free shipping or free instant local store pickup (if available). Their price is $200 off MSRP. Price is... Read more
21-inch 2.9GHz iMac on sale for $1299, save $...
Best Buy has the 21″ 2.9GHz iMac on sale today for $1299.99 on their online store. Choose free shipping or free local store pickup (if available). Their price is $200 off MSRP, and it’s the lowest... Read more
Free Image Sizer 1.3 for iOS Offers Photo Edi...
Xi’An, China based G-Power has announced the release of Image Sizer 1.3 for the iPhone, iPad, and iPod touch, an important update to their free photo editing app. Image Sizer’s collection of easy to... Read more
Sale! 13″ 1.6GHz/128GB MacBook Air for $899,...
B&H Photo has the 13″ 1.6GHz/128GB MacBook Air on sale for $899 including free shipping plus NY tax only. Their price is $100 off MSRP, and it’s the lowest price available for this model. Read more
13-inch Retina MacBook Pros on sale for $100...
Best Buy has 13-inch Retina MacBook Pros on sale for $100 off MSRP on their online store. Choose free shipping or free local store pickup (if available). Prices are for online orders only, in-store... Read more
Will BMW’s i3 Electric Vehicle Be The Automo...
The German-language business journal Manager Magazin’s Michael Freitag reports that Apple and the German performance/luxury automaker Bayerishe Motoren Werke (BMW) are back at far-reaching... Read more
Sale! $250 off 15-inch Retina MacBook Pro, $2...
B&H Photo has lowered their price for the 15″ 2.2GHz Retina MacBook Pro to $1749, or $250 off MSRP. Shipping is free, and B&H charges NY sales tax only. They have the 27″ 3.3GHz 5K iMac on... Read more
Global Smartphone Market Posts 11.6% Year-Ove...
According to the latest preliminary data released from the International Data Corporation (IDC) Worldwide Quarterly Mobile Phone Tracker, smartphone vendors shipped a total of 337.2 million units... Read more
15-inch and 13-inch Retina MacBook Pros on sa...
B&H Photo has 15″ & 13″ Retina MacBook Pros on sale for up to $180 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1819 save $180 - 15″ 2.... Read more

Jobs Board

*Apple* Customer Experience (ACE) Leader - A...
…management to deliver on business objectives Training partner store staff on Apple products, services, and merchandising guidelines Coaching partner store staff on Read more
Project Manager - *Apple* Pay Security - Ap...
**Job Summary** The Apple Pay Security team is seeking a highly organized, results-driven Project Manager to drive the development of Apple Pay Security. If you are Read more
*Apple* TV Product Design Internship (Spring...
…the mechanical design effort associated with creating world-class products with the Apple TV PD Group. Responsibilities will include working closely with manufacturing, Read more
*Apple* Watch SW Application Project Manager...
**Job Summary** The Apple Watch software team is looking for an Application Engineering Project Manager to work on new projects for Apple . The successful candidate Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.