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}
{****************************************}

 
AAPL
$101.06
Apple Inc.
+0.10
MSFT
$47.06
Microsoft Corpora
-0.46
GOOG
$587.37
Google Inc.
-8.71

MacTech Search:
Community Search:

Software Updates via MacUpdate

Monosnap 2.2.2 - Versatile screenshot ut...
Monosnap allows you to save screenshots easily, conveniently, and quickly, sharing them with friends and colleagues at once. It's the ideal choice for anyone who is looking for a smart and fast... Read more
Tunnelblick 3.4beta36 - GUI for OpenVPN...
Tunnelblick is a free, open source graphic user interface for OpenVPN on OS X. It provides easy control of OpenVPN client and/or server connections. It comes as a ready-to-use application with all... Read more
SoftRAID 5.0.4 - High-quality RAID manag...
SoftRAID allows you to create and manage disk arrays to increase performance and reliability. SoftRAID's intuitive interface and powerful feature set makes this utility a must have for any Mac OS X... Read more
Audio Hijack Pro 2.11.3 - Record and enh...
Audio Hijack Pro drastically changes the way you use audio on your computer, giving you the freedom to listen to audio when you want and how you want. Record and enhance any audio with Audio Hijack... Read more
Airfoil 4.8.9 - Send audio from any app...
Airfoil allows you to send any audio to AirPort Express units, Apple TVs, and even other Macs and PCs, all in sync! It's your audio - everywhere. With Airfoil you can take audio from any... Read more
WhatRoute 1.13.0 - Geographically trace...
WhatRoute is designed to find the names of all the routers an IP packet passes through on its way from your Mac to a destination host. It also measures the round-trip time from your Mac to the... Read more
Chromium 37.0.2062.122 - Fast and stable...
Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all Internet users to experience the web. FreeSMUG-Free OpenSource Mac User Group build is... Read more
Attachment Tamer 3.1.14b9 - Take control...
Attachment Tamer gives you control over attachment handling in Apple Mail. It fixes the most annoying Apple Mail flaws, ensures compatibility with other email software, and allows you to set up how... Read more
Duplicate Annihilator 5.0 - Find and del...
Duplicate Annihilator takes on the time-consuming task of comparing the images in your iPhoto library using effective algorithms to make sure that no duplicate escapes. Duplicate Annihilator detects... Read more
jAlbum Pro 12.2 - Organize your digital...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code!... Read more

Latest Forum Discussions

See All

This Week at 148Apps: September 15-19, 2...
Expert App Reviewers   So little time and so very many apps. What’s a poor iPhone/iPad lover to do? Fortunately, 148Apps is here to give you the rundown on the latest and greatest releases. And we even have a tremendous back catalog of reviews; just... | Read more »
Kitty Powers’ Matchmaker – Tips, Tricks,...
Hey There, Kittens: | Read more »
Goblin Sword Review
Goblin Sword Review By Andrew Fisher on September 22nd, 2014 Our Rating: :: RETRO GOODNESSUniversal App - Designed for iPhone and iPad Fun visuals, good music, engaging level design, and lots of content make Goblin Sword an... | Read more »
Major New Update for CSR Racing Adds Fer...
Major New Update for CSR Racing Adds Ferrari and Multiplaye​r Posted by Jessica Fisher on September 22nd, 2014 [ permalink ] | Read more »
Veditor Review
Veditor Review By Jennifer Allen on September 22nd, 2014 Our Rating: :: PIMP YOUR VIDEOUniversal App - Designed for iPhone and iPad Want to add stickers and music to your videos? Veditor can do that easily.   | Read more »
1849′s Nevada Silver DLC is Still Search...
A few months ago, I took a look at 1849 from SomaSim. This Gold Rush-themed city builder for iPad had a fair bit going for it, but lacked in a few crucial areas to make it a true stand-out on the App Store. SomaSim has since added in a sandbox mode... | Read more »
Fruit Ninja Will be Reborn With a Massiv...
Fruit Ninja Will be Reborn With a Massive Update and Origins Animation Series Posted by Jessica Fisher on September 22nd, 2014 [ permalink ] Halfbrick Studios is rebuilding | Read more »
Daniel Tiger’s Grr-ific Feelings Review
Daniel Tiger’s Grr-ific Feelings Review By Amy Solomon on September 22nd, 2014 Our Rating: iPad Only App - Designed for the iPad Daniel Tiger’s Grr-ific Feelings includes activities that allow young children explore different... | Read more »
CloudMagic Updated for iOS 8 – Adds Inte...
CloudMagic Updated for iOS 8 – Adds Interactive Notifications, Share Extension, and More Posted by Jessica Fisher on September 22nd, 2014 [ | Read more »
Starbase Annex (Games)
Starbase Annex 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: "it’s really very clever... a little bit of Hearthstone and a dash of Eclipse" - PocketTactics.com From the creator of Starbase... | Read more »

Price Scanner via MacPrices.net

Apple restocks some refurbished 2014 MacBook...
The Apple Store has restocked some Apple Certified Refurbished 2014 MacBook Airs, with prices starting at $769. An Apple one-year warranty is included with each MacBook, and shipping is free. These... Read more
13-inch 128GB MacBook Air on sale for $949, s...
B&H Photo has the new 2014 13″ 1.4GHz/128GB MacBook Air on sale for $949.99 including free shipping plus NY tax only. Their price is $50 off MSRP. B&H will also include free copies of... Read more
Apple offering free $25 iTunes Gift Card with...
The Apple Store is offering a free $25 iTunes Gift Card with the purchase of a $99 Apple TV for a limited time. Shipping is free. Read more
Apple refurbished iPod touch available for up...
The Apple Store has Apple Certified Refurbished 5th generation iPod touches available starting at $149. Apple’s one-year warranty is included with each model, and shipping is free. Most colors are... Read more
iFixIt Tears Down iPhone 6; Awards Respectabl...
iFixit notes that even the smaller 4.7″ iPhone 6 is a giant among iPhones; so big that Apple couldn’t fit it into the familiar iPhone form factor. In a welcome reversal of a recent trend to more or... Read more
Phone 6 Guide – Tips Book For Both iPhone 6...
iOS Guides has announced its latest eBook: iPhone 6 Guide. Brought to you by the expert team at iOS Guides, and written by best-selling technology author Tom Rudderham, iPhone 6 Guide is packed with... Read more
How to Upgrade iPhone iPad to iOS 8 without D...
PhoneClean, a iPhone cleaner utility offered by iMobie Inc., reveals a solution for upgrading iPhone and iPad to iOS 8 without deleting photos, apps, the new U2 album or anything. Thanks to more than... Read more
Inpaint 6 – Photo Retouching Tool Gets Faster...
TeoreX has announced Inpaint 6, a simple retouching tool for end users that helps remove scratches, watermarks, and timestamps as well as more complex objects like strangers, unwanted elements and... Read more
Worldwide PC Monitor Market Sees Growth in To...
Worldwide PC monitor shipments totaled 32.5 million units in the second quarter of 2014 (2Q14), a year-over-year decline of -2.9%, according to the International Data Corporation (IDC) Worldwide... Read more
Updated Price Trackers
We’ve updated our Mac Price Trackers with the latest information on prices, bundles, and availability on systems from Apple’s authorized internet/catalog resellers: - 15″ MacBook Pros - 13″ MacBook... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of 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
Position Opening at *Apple* - Apple (United...
**Job Summary** At the Apple Store, you connect business professionals and entrepreneurs with the tools they need in order to put Apple solutions to work in their Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.