TweetFollow Us on Twitter

Text Editor
Volume Number:3
Issue Number:1
Column Tag:Intermediate Mac'ing

The Generic Multi-Window Text Editor

By David E. Smith, Editor & Publisher

Nostalgia

This is a column in nostalgia. A throwback to the good old days of 1986 when Mac programs were done by hand with event loops, menu bars and custom designed scroll routines. Back when men were men and programmers who knew how to write a multi-window text editor were looked up to as heros of the Mac programming cult. Yes, back in the dark ages before MacApp made all of what you are about to read obsolete. So take a ride with us into the stone age of Mac programming and see how it used to be when programmers struggled to code up a multi-window application from scratch.

Programming Puberty

Before MacApp, it was taken for granted that to reach programming puberty, you had to write a multi-window editor. The reason for this is such a program involves nearly all of the Mac user interface. Multiple windows, update events, scroll bars, text edit, cut and paste, and so on. A number of these generic editor shells have been published in both shareware, books and magazine articles. Bob Denny wrote one of the first such articles for MacTutor in May 1985 with a discussion of text edit and scrolling windows that remains a classic on the subject. Several previous articles in that series delt with editing using the C language. All of those articles are available in the Best of MacTutor, Volume 1 book.

The Chernicoff book, Macintosh Revealed, Volume 2, provided an entire book on the subject of the multi-window text editor in Pascal. This is a very complete description of such a program and covers most of the material in Inside Macintosh, vol. 1-3. Another book is the very good series by Dan Weston, The Complete Book of Macintosh Assembly Language Programming, Volumes 1 and 2. The first volume covers the generic text editor in assembly, and volume 2, just released, goes farther in explaining new programming details of using the switcher, HFS, clipboard and other topics generally discussed in Inside Macintosh Volume 4. Most of the material in volume 2 of Dan Weston's book has never been published and represents a very important book regardless of what language you program in. Be sure to buy this book without delay. Scott, Foreman and Company are the publishers.

Finally, one of the most complete multi-window text editors is the source listing supplied with LightSpeed Pascal from Think Technologies. This programming example is very complete and will provide an excellent Pascal reference. It is generally based on the Chernicoff example but expands on it in many areas.

Which brings us to this rendition of the generic multi-window text edit example. Not wanting to miss out on my own opportunity at programming manhood, I decided to write my own text editor using all the references cited above as guides. The result is the subject of this month's column. Although it took three months, and I think is a very good start, I was a bit taken back when Harvey Alcabes of Apple Computer showed off MacApp and demonstrated the simplest "do nothing" MacApp application. It was an advanced version of my program! Since this may become a lost art, I present my version of the generic text editor for those who wish to bash heads with the toolbox before moving on to MacApp.

What Our Example Does

As an application, our program is very complete. It opens four windows for text editing. Scrolling is supported vertically. All the window operations are supported including grow window, close box, and zoom box. When either the grow box or zoom box is selected, the text is wrapped to the new window size so there is no need for horizontal scrolling. A neat addition would be to include an option in the format menu where the user could select between the two modes of having the text wrap when the window changed size, or having the window scroll to view the text. The first mode is ideal for paragraph writing, like the text you are reading now. The second mode is better suited for program text or tables where placement of the text remains fixed at tab stops. Of course, Text Edit doesn't support tabs so this mode would require more work. The November 1986 issue of MacTutor presented a possible solution for extending Text Edit to handle tabs.

Figure 2. The custom size dialog box.

Text presented in any of the four windows may be selected and changed to all the quickdraw / text edit possibilities. A font menu gives access to all the fonts in your system file. A size menu allows size changes from 6 point to 72 point with a dialog box for custom font sizes up to the quickdraw limit of 127. A style menu gives all the quickdraw styles available to text without any custom programming including condensed and extended text. Finally, the mode menu allows the three quickdraw recommended modes for drawing text. After trying the different modes, I concluded that this is probably useless. But at least all the quickdraw text capability is supported in this text edit example.

Text Edit Limitations

As you know, text edit is a set of ROM routines which implement a simple text edit capability based on the ability of quickdraw. Only one font, style, size and mode is allowed for all the text in a given text edit record. Text Edit is also limited to 32K bytes of text and in fact the limit is even less depending on the font and size chosen. A more limiting factor is the destination rectangle that encloses the text. The coordinates of the rectangle are limited to integer values, but these can be quickly exceeded when a large font size, a small window, and many lines of text are combined at one time. This may explain why few programs allow complete freedom in text size selection. After playing with the size menu, I discovered that when my window was made narrow, and the size increased, the destination rectange limits were being exceeded in my routine to re-wrap the text to the new window. The product of the number of text lines calculated by Text Edit and the number of pixels per line, a function of the font size, quickly becomes greater than an integer value, making proper scrolling impossible. TEScroll generates an error since it is limited to an integer offset for the scrolling distance. In our program, when this happens, it beeps and the displayed text is scrolled to 32K, the max, which may not result in a correct window display. Reducing the font size or enlarging the window will correct the display.

In figure 1, we see a screen shot of our program showing four windows open, each with a different font, size, and style. The edit menu supports cut, copy and paste to the Text Edit scrap. Since this scrap is a private scrap known only to text edit, I copy the TEScrap to the deskscrap whenever a copy or cut operation is performed. Likewise, when a paste is done, it is taken from the deskscrap. Thus the deskscrap is used at all times so that the private text edit scrap and the clipboard scrap are always the same. This allows an easy way to cut and paste between our windows and desk accessories or other applications without the problem of trying to figure out when to convert the clipboard.

Figure 2 shows how our custom font size dialog works. The current size is displayed from a parameter variable in low memory and an edit item is provided so that a new value may be inserted.

Our edit menu includes two useful functions shown in figure 3. These are a show clipboard, and a select all function. The select all highlights all the text in the text edit record and copies it into the deskscrap so that a subsequent paste operation will paste the selected text. This seemed the most natural to me and eliminates the extra step of choosing copy from the edit menu. For simplicity, the clipboard does not show the text in the selected font, nor does it scroll. You may wish to turn the clipboard window into another text type window so that it shows the font and style of the selected text.

The search and format menus do nothing. We have already made a suggestion for the format menu, to select between word wrapping or horizontal scrolling. The search menu can be implemented by using the munger trap call to search for arbitrary strings in the text edit record. The font, style, and mode menus are fully implemented to the limit of quickdraw's built-in text commands. The transfer menu is not implemented but is ready to set up a standard file dialog box for launching another application. How this is done was covered in an earlier issue of MacTutor by Chris Yerga, available in the Best of book. The edit menu is fully operational except for UNDO. A future article on how to implement undo is waiting publication in MacTutor. The apple menu shows a simple about box dialog with an icon and the desk accessories are available. The file menu has a new and close command, which open and close up to four new windows. A single global variable, MaxWindows, determines the number available. This method was chosen so that all the window data structures could be set up low in the heap, since the window record is not re-locatable. The normal file and printing operations for opening and saving the file, and printing the file are not implemented. There is only so much one can do in a single article on a topic which others have written entire books about. Both of these subjects are well treated in Dan Weston's new volume 2 on assembly programming, previously mentioned.

LightSpeed Pascal

Figure 4 shows the files created by the LightSpeed Pascal system. The four text files shown can be read directly by Edit if you are using another Pascal system. As explained in last month's article by Tom Scheiderich, there is very little difference in running this program in TML or LS Pascal. Only the segmentation is different. The program code is contained in a short "MyWrite Main" file. This includes the init code, the event loop and the first level of event subroutines. Two other files complete the program. These are the "myWriteStuff" unit and the "scrollstuff" unit. Finally, the file "Editor Globals" contain the global constant and variable declarations. David Wilson, the Apple Programming guru, recommends that the init code be placed in the first segment so that it is always locked in memory. Apparently trying to place the init code in another segment that may be unlocked or purged can cause problems with the quickdraw global assignments. That is why I included the init stuff in my main program rather than in another unit as some people do. Resources are handled in the normal way. An edit file called "MyWrite.R" contains the RMaker format resources. These must be assembled into a resource file named "MyWrite.RSRC". The actual program is contained as a project in "MyWrite Project", shown hi-lited in figure 4, and in a stand alone file created with the "build" command. The switcher test case file was used to test the switcher event to see that the clipboard worked properly from switcher. The ease with which you can get into and out of a development task by just clicking on the project file really makes working with LS Pascal a snap. And the observe window, which allows examining local variables makes debugging fast and easy. I love it!

Figure 4. Edit program files

Segments

Figure 5 shows the implied link order for our program. The globals and main files are linked together in segment 1 along with all the various Pascal system files. ROM85 is the library of new 128K ROM calls, included just so I wouldn't have to think about it. I really don't know if I used any new calls or not. Most of the code is contained in "myWrite Stuff", the main chunk of segment 2. At the end of the event loop, an "unloadSeg" trap call is made to unload these two units, making them re-locatable. This type of segmentation by unit boundaries is unique to LS Pascal and you may wish to modify this if you are using another linker.

MyWrite Program Code

We begin our discussion of this program with the main segment, shown in figures 6, 7 and 8. The main program calls three routines to init everything, set up the menu bar, and perform the event loop, as illustrated in figure 6. The events our program responds to are shown in figure 7. These are a mouse down event, a keypress, and the update and activate events for windows. This represents the minimum response for a typical Mac application. We have included a fifth event, the switcher event, number 15, to detect when the switcher suspends and activates us. This was taken from Dan Weston's volume 2 book, which explains in great detail how the switcher works. Since we use the system scrap at all times, we really don't need this event since no clipboard conversion is required.

Figure 6. The main program

Figure 7.The Event Loop

Figure 8. The Mouse Down Event

All the action takes place in the mouse down event, where we perform the menu bar functions. Figure 8 shows the various places the mouse can be and the implied action we must take. When a click happens in the content region of a window, we have to find out if that is a scroll event, since the scroll bars are part of the content region of a window. So scrolling is done as part of a content region mouse down event. Grow and Zoom events are also handled. These events change the window size, and in our application, cause all the text to be re-formatted to wrap to the new view rectangle. This proved to be the hardest part of the program, getting grow window to work right. The little grow icon is apparently a lost child and it took quite a while to figure out how the window is grown and updated properly under all conditions.

Data Structures

Most of the published text edit examples use a custom data type called a window control block or some such thing to manage all the data structures associated with the window. Since I didn't want to confuse the toolbox data structures, which I was trying to learn, with the custom ones, I did not use this approach. Instead, I set up five arrays shown below:

myWindows: array[1..MaxWindows] of Windowptr;
GoodbyList: array[1..MaxWindows] of Integer;
myVControls: array[1..MaxWindows] of ControlHandle;
myHControls: array[1..MaxWindows] of ControlHandle;
myText: array[1..MaxWindows] of TEHandle;

The window array holds the window pointers to the window records. The text array holds the handles to the text edit records. The vertical and horizontal control arrays hold the handles to the scroller controls. Finally, the GoodbyList holds the status of each window, whether it is available or being used. The proper association of each control or text edit record to the proper window is done by the array index. The currently active index is contained in the global variable "currentWindow". MaxWindows is set at 4 but this can be changed in the global variables. From my background, this approach was the most logical to me and eliminated the need to create a custom variable type. However, now that I've written the program and understand how the Mac works, I can see that it would be cleaner to combine all these arrays into a new data structure that would tie the various components to a "window object". This approach would also lead into a MacApp conversion more easily. The trick is you can't appreciate this until after you've written such a program and understand more clearly how all the pieces fit together.

Main Program Notes

The first thing we do in our main program is init everything. Notice that we set up a bomb routine with our InitDialogs trap so that if a system error is generated, we get a resume box that will return us to our program where we can exit to the finder. Our bomb routine is called crash, and simply calls the toolbox routine ExitToShell. Scott Knaster, in his book How to Write Macintosh Software, goes into great length on how to write these saver routines. However there is some controversy on just how much such a crash routine should do. Trying to write to the disk to save a file after a crash could be bad news if the system is so fouled up that a hard disk is destroyed in the process. So I think it is better to assume the worse and just exit rather than try and be nice to the user only to find out you bombed his hard disk! Incidently, this is a very good book, which is not described by its title. It really is a book on how to debug Mac programs and is a must for everyone. The book is being distributed free if you join APDA, the official Apple developer's Association.

Rectangles are very important. Our program uses several global rectanges for the window stuff. These are:

screen screen display rectangle

DragArea window drag rectangle

GrowArea window grow rectangle

DefaultWindow new window size default

ZoomRect zoom area rectangle

ClipBdRect clipboard window rectangle

ViewRect vew rectangle for text edit

DestRect destination rectangle for text edit

VCRect vertical scroller area

HCRect horizontal scroller area

GrowRect grow box region

All of these are set up in our init routine and are calculated from the system global ScreenBits.Bounds, so that they all change appropriately if a large screen is used. I tested the program on a large screen Mac and it worked perfectly. The Zoom window zoomed to the size of the large screen because of how these rectangles are defined relative to ScreenBits.Bounds.

Another important point is the order in which things are done during init. The window must be defined first, followed by the controls and then text edit. Text Edit requires the current port be set properly to the window whoose text edit record is being defined. That is how the assocation is made between the text edit record and the window record. So you must define the window first, and then do a SetPort to that window before calling TENew to create the text edit record.

The problem of getting the menus to show the right font and sizes is handled by calling UpdateMenus as shown in the SetUpMenus routine. This in turn calls a routine for each menu which examines the current port settings and changes the menu appearance appropriately. Thus whenever a new window is activated, a call to UpdateMenus then changes the font and size menus to reflect the current font, size, style and mode for that window. You could also try to dim or hi-lite menu items as appropriate, but I have not done that here. If a menu item is selected when not appropriate, it beeps and nothing happens. This was done to help figure out potential bug bombs. Having done that, you can go back and select or unselect menu items as necessary.

The doMouse routine calls each mouse event routine for grow, content and so forth. I attempted to make the grow routine universal so I could call it with ZoomWindow as well. When the window changes size, the text is re-calculated to fit the new port rectangle. In this respect, Zoom Window is really a grow window function. So the Zoom event does the standard ZoomWindow and then calls the doGrow event routine to update the window.

The doKeyDowns routine checks for command keys and passes them back to the doMenuBar routine so that command keys are handled properly. Also the length of text in the text edit record for that window is checked so we don't type beyond 32K of text. This check is also made in the paste routine as well.

Update Events

The final routines in our main program are the update and activate events. Trying to figure out how this is done properly for scrolling windows was a real challenge. I think I have the best sequence in my Update and Activate event routines, but if someone has a better approach, I'd like to hear about it. When the BeginUpdate trap is called, the port rectangle is changed to the update region. An EraseRect on the PortRect for that window will then clear out the update region. I then draw my controls and call TEUpdate for the visible region of the window. The current port is not changed. This seems to be the best approach for the update event. The activate and de-activate events do a show control and a hide control in addition to doing a DrawGrowIcon. This seemed to be the best way to get the windows re-drawn properly under all conditions. If the clipboard window is being activated, we set our currentWindow global to 0 to indicate the active window is not one of our four text edit windows. The desk scrap is read into the text edit private scrap and the text edit record for the clipboard is updated. The clipboard text points to the text edit scrap so that whatever is in the text edit scrap will be displayed in the clipboard window.

MyWrite Stuff Unit

The heart of our program is contained in the MyWrite Stuff unit. This includes all the second level subroutines for the menu bar functions, updating each of the menus, the about box, new window, close window, drag, grow and content routines. The only routines remaining are the scrolling routines, which are handled in a seperate Scroll Stuff unit.

The fonts, size, style and mode menus are all somewhat similar. When the user selects a new font, the grafport's font is changed, the font menu is updated to show the selected font (UpdateFonts), and the text is bashed to re-wrap it to be displayed in the text edit window. The Size menu works the same way. The grafport's font size is changed, the size menu updated (UpdateSize) and the text bashed to wrap the new text display in the window. The size menu includes a custom size that displays a dialog box from our resource file. The current font size is read from the grafport (thePort^.txSize) and placed in one of four low memory variables with the trap call ParamText. Then when the dialog box is displayed, the ParamText item is displayed in the dialog at the location specified in the resource file. In this way programs can pass information to a dialog box display. I was only able to get this to work for a static text item, and not for the edit text item. Anybody know why? The style menu also includes formatting of the display left, center and right. If the user selects a new format, the text edit record is updated and the text bashed after updating the menu (UpdateJust and UpdateStyle). Dealing with style items is really tricky and seems to take up a lot more programming space than if Apple had just assigned integer values to the various style selections. I found it particularly confusing trying to figure out how to tell if a given style was included in the currently selected text face. The doStyle routine shows how it works.

The important routine of course, is BashText. This routine reads all the text information from the grafport (font, face, mode and size), gets the font ascent and height from the FontInfo record, and resets the text edit record to match the newly set values of the current grafport, which were set by each of the "do" menu routines for size, font, style and so on. Once the text edit record is up to date, then the text line starts are re-calculated (TECalText), the view rectange is marked as invalid, and the scrollers updated. Then when the update event is executed, the new text edit parameters will be reflected in the window display and the scrollers will be adjusted so that the selection point is still visible.

Fig 9.

Content and Grow Events

The hardest part of the program was dealing with a content event or a grow window event. For a content event, we must find out which window the event occours in and then find out if the click is in the "real" content area or in the scrollers, which are treated by the window manager as part of the content region. If the scroller area was clicked, we call doScrollers. Otherwise, if the click is in the view rectangle, we call TEClick, which handles a shift key down selection and hi-lites the selected text for us. The grow window routine must check for a zoom event from which to get the new window size or take the new size from the GrowWindow trap call. Once the new window size is determined, we reset all our rectangles (VCRect, HCRect, GrowRect) and call SizeWindow to change the PortRect to the new size. Setting the rectangles before the SizeWindow gives the pre-size dimensions so we can Invalidate the grow icon region and erase it. Getting the grow icon area to update properly was the hardest part of this routine. The order in which the SizeWindow and updating of the grow icon region are done affects the appearance on the screen. I found the order shown in this routine seems to paint the screen in the fastest manner.

After calling SizeWindow, the PortRect is changed. So we reset our rectangles, hide and move the controls to the new positions, re-size the controls to the new dimension, and then show the controls. Then the control rectangles are marked as valid so they won't be re-drawn during the next update event, since we have already manually updated them. This order seems to produce the most pleasing display. Finally, we reset the ViewRect and DestRect and bash the text to re-wrap it to the new screen size.

Scroll Stuff Unit

The last unit is the scrolling routines. This stuff is magic. It is a combination of Bob Denny's early C routines, Chernicoff's Macintosh Revealed approach, and LS Pascal's text editor example. The heart of this unit is the MoveText routine. This routine simply scrolls the text to match the currently set control value. All the other scroll routines concentrate on setting the control values to their new values, then they call this routine to actually move the text by scrolling. The problem is that TEScroll only allows an integer offset to scroll up or down. That offset is given in pixels, so we multiply the number of lines (the scroller control value) by the line height and substract the result of any previous scroll to get the offset needed to scroll to the current control value. If the number of lines is large and the line height is large (large size), this value exceeds 32K even though the text buffer may be much less than 32K. As a result, TEScroll doesn't work and I don't know how else to move the text without it.

Other bugs in Text Edit are also tested for in this routine. When calling TEScroll, you must make sure the scrolling offset is not zero. Also, the number of lines in the text buffer is not correct if a carriage return is the last character. So a special function called LinesInText is used to calculate the correct number of lines of text. Both of these bugs were documented in a recent Tech Note.

The actual scroll routines are called from the doScroller procedure and simply set the control value to the new value and call MoveText. For the up and down button, the TrackControl trap calls back to the scroll_up and scroll_down routines continuously as long as the button is pressed. For a page scroll (clicking in the scroll area), the number of lines in a page is calculated from the current lineHeight, and the control set accordingly. If the user scrolls with the mouse to the window edge, an autoscroll function is performed which calls either scroll_up or scroll_down continuously so that text can be selected beyond the window boundary. The autoscroll function routine is set up in the init procedure by calling SetClickLoop to associate this routine with the given text edit record.

The AdjustScrollBar routine checks to see if the number of lines of text is greater than can be displayed in the view rectangle of the window. If it is then the max control is set so that the last page of text is visible. If not, then the max control is set to zero, or in otherwords, no controls are necessary.

The ScrollChar routine moves the control to a given line in the text that contains the character position passed to it. The routine checks all the line starts in the text to find the line which contains that character position in the buffer. Then the control is set so that line position will show up in the middle of the screen when the text is bashed.

The final scroll routine is checkInsertion. This routine is the front end to ScrollChar and AdjustScrollBar. It calls ScrollChar and MoveText to do the actual scrolling. Its purpose is to scroll the text so that the hi-lited selection is visible.

What is Needed

While this program does a very good job of showing off text edit, it also shows off the limitations of text edit that make it unsuitable for the basis of a text editor. What we need now is a set of routines with text edit's front end (so our program won't have to be re-written) but without the limitations of text size, font, and 32K limits. So our next step must be to re-write text edit without these limitations. A hard task, but needed to solve the problems our example program has uncovered.

An ideal text edit library should provide for unlimited scrolling to any point in the text buffer, regardless of the font or size chosen. If the text length is below the maximum allowed, then that text should be displayable regardless of the font, size or style of the text. As we have seen, this is not the case with text edit.

The real problem is that you would like to select fonts arbitrarily throughout your text. This is difficult because quickdraw doesn't keep track of font information. It only sets the current font and style and then draws to that specification. So the problem is to construct a record that allows quickdraw to reproduce the display. Conventional text editors always used two bytes for each character. One byte for the character code and one byte for the display information. Then the font, size and style can be reconstructed directly from the text buffer. Text Edit saves only the character itself in the text buffer, nothing about how that character is to be displayed. Another approach is the Solo Approach used in the new page layout program from Mac America, previously called Spud. This approach placed control codes directly in the text buffer at the point when the text changes font, size or style. This might be a little more efficient than including a style byte for every character, but harder to implement. A third approach would be to keep the text buffer for characters of text only as Text Edit does, and to add a second buffer that contains the format information. This might allow an easy way to "add on" this ability to text edit without having to re-write the whole thing. In the coming months, we welcome ideas on how these approaches might be used to expand Text Edit to a normal MacWrite type of editor.


PROGRAM MyWrite;
{ This program is a TextEdit Demo }
{ written by David E. Smith for MacTutor}
{$I-}
USES
 ROM85, EditorGlobals, MyWriteStuff, Scrollstuff;

PROCEDURE crash;
 BEGIN
 ExitToShell;
 END;

PROCEDURE InitThings;
 VAR
 i, j : integer;
 WRect : Rect;
 windtype : integer;
 Visible : boolean;
 GoAway : boolean;
 RefVal : LongInt;
 title : str255;
 stri : str255;
 longi : longInt;
 BEGIN
 MaxApplZone;
 MoreMasters;
 MoreMasters;
 MoreMasters;
 InitGraf(@thePort);
 InitFonts;
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs(@crash);
 InitCursor;
 FlushEvents(everyEvent, 0);
 Finished := false;

 CR := Chr(13);
 BS := Chr(8);
 { init my window stuff }
 screen := ScreenBits.Bounds; {current screen device}
 SetRect(DragArea, Screen.left + 4, Screen.top + MenuBarHeight + 4, Screen.right 
- 4, Screen.bottom - 4);
 SetRect(GrowArea, Screen.left + MinWidth, Screen.top + MinHeight, Screen.right 
- 8, Screen.bottom - 8);
 SetRect(DefaultWindow, Screen.left + 30, Screen.top + 50, Screen.right 
- 75, Screen.bottom - 75);
 SetRect(ZoomRect, Screen.left + 4, Screen.top + 24, Screen.right - 4, 
Screen.bottom - 4);
 SetRect(ClipBdRect, Screen.left + 100, Screen.top + 150, Screen.right 
- 100, Screen.bottom - 60);

 RefVal := 0;
 title := 'Clipboard';
 ClipBdWindow := NewWindow(@ClipBdStorage, ClipBdRect, title, False, 
rDocProc, pointer(-1), true, RefVal);
 SetPort(ClipBdWindow);
 TextFont(systemFont);
 TextSize(12);
 TextFace([]); {plain}
 TextMode(1);  {Or}
 WITH ClipBdWindow^.portRect DO
 BEGIN
 SetRect(ViewRect, left + 4, top + 4, right - (SBarWidth - 1), bottom 
- (SBarWidth - 1));
 DestRect := ViewRect;
 END;
 ClipBdText := TENew(DestRect, ViewRect);
 IF TEFromScrap <> noErr THEN
 BEGIN  {Desk Scrap to TEScrap}
 END;
 ClipBdText^^.hText := TEScrapHandle; {TEto Clip}
 ClipBdText^^.teLength := TEGetScrapLen;
 TECalText(ClipBdText);

 Visible := false;
 windtype := documentProc + ZoomBox;
 GoAway := true;
 FOR i := 1 TO MaxWindows DO
 BEGIN
 RefVal := i;
 j := (i - 1) * 18;
 longi := longInt(i);
 NumToString(longi, stri);
 title := concat('Untitled ', 'Window ', stri);
 SetRect(WRect, DefaultWindow.left + j, DefaultWindow.top + j, DefaultWindow.right 
+ J, DefaultWindow.bottom + J);
 myWindows[i] := NewWindow(NIL, WRect, title, Visible, windtype, pointer(-1), 
GoAway, RefVal);
 SetPort(myWindows[i]);
 TextFont(systemFont);
 TextSize(12);
 TextFace([]); {plain}
 TextMode(1);  {Or}
 WITH myWindows[i]^.portRect DO
 BEGIN
 SetRect(ViewRect, left + 4, top + 4, right - (SBarWidth - 1), bottom 
- (SBarWidth - 1));
 DestRect := ViewRect;
 SetRect(VCRect, right - (SBarWidth - 1), top - 1, right + 1, bottom 
- (SBarWidth - 2));
 title := '';
 myVControls[i] := NewControl(myWindows[i], VCRect, title, Visible, 0, 
0, 0, ScrollBarProc, i);
 ValidRect(VCRect);
 SetRect(HCRect, left - 1, bottom - (SBarWidth - 1), right - (SBarWidth 
- 2), bottom + 1);
 myHControls[i] := NewControl(myWindows[i], HCRect, title, Visible, 0, 
0, 0, ScrollBarProc, i);
 ValidRect(HCRect);
 END;
 myText[i] := TENew(DestRect, ViewRect);
 SetClikLoop(@AutoScroll, myText[i]);
 CurrentWindow := i; {active window}
 goodbyList[i] := 0; {window is busy}
 ShowWindow(myWindows[i]);
 SelectWindow(myWindows[i]);
 END; {of i}
 END;

 PROCEDURE SetUpMenus;
 VAR
 i : integer;
 BEGIN
 FOR i := AppleMenu TO LastMenu DO
 BEGIN
 myMenus[i] := GetMenu(i);
 InsertMenu(myMenus[i], 0);
 END; {of i}
 AddResMenu(myMenus[AppleMenu], 'DRVR');
 AddResMenu(myMenus[FontMenu], 'FONT');
 DrawMenuBar;
 UpdateMenus;
 END; {of proc}

 PROCEDURE doMouse (myEvent : EventRecord);
 VAR
 whereIsIt : integer;
 whichWindow : WindowPtr;
 localPt, globalPt : Point;
 oldPort : GrafPtr;
 BEGIN
 globalPt := myEvent.where;
 localPt := globalPt;{global coord of mouse}
 GlobalToLocal(localPt);  {local coord of mouse}
 whereIsIt := FindWindow(globalPt, whichWindow);
 CASE whereIsIt OF
 inDesk : {0}
 BEGIN
 END;
 inMenuBar :     {1}
 doMenuBar(MenuSelect(globalPt));
 inSysWindow :   {2}
 SystemClick(myEvent, whichWindow);
 inContent :     {3}
 doContent(myEvent, whichWindow);
 inDrag : {4}
 doDrag(whichWindow, globalPt);
 inGrow : {5}
 doGrow(whichWindow, globalPt, False);
 inGoAway : {6}
 IF TrackGoAway(whichWindow, globalPt) THEN
 doCloseWindow(whichWindow);
 inZoomIn, InZoomOut :  {7, 8}
 BEGIN
 IF TrackBox(whichWindow, globalPt, whereIsIt) 
 THEN
 BEGIN
 GetPort(OldPort);
 SetPort(whichWindow); {safety device}
 ZoomWindow(whichWindow, whereIsIt, True);
 doGrow(whichWindow, globalPt, True);
 SetPort(OldPort);
 END;
 END;
 OTHERWISE
 BEGIN
 END;
 END; {of whereIsIt}
 END;

 PROCEDURE doKeyDowns (myEvent : EventRecord);
 VAR
 ch : char;
 charCode : longInt;
 keyCode : longInt;
 BEGIN
 charCode := BitAnd(myEvent.Message, charCodeMask);  
 keyCode := BitAnd(myEvent.Message, keyCodeMask);
 ch := Chr(charCode);  {get keyboard char}
 IF BitAnd(myEvent.Modifiers, CmdKey) = CmdKey THEN
 doMenuBar(MenuKey(ch))  { do menu command key}
 ELSE
 BEGIN  { do keystroke }
 IF currentWindow = 0 THEN
 Sysbeep(5)  {no typing in clipboard }
 ELSE IF myWindows[currentWindow] <> FrontWindow THEN
 Sysbeep(5)  {error}
 ELSE IF (myText[currentWindow]^^.teLength = MaxInt) AND (ch <> BS) THEN
 SysBeep(5)
 ELSE
 BEGIN {type in active text window}
 TEKey(ch, myText[currentWindow]);
 AdjustScrollBar(currentWindow);
 CheckInsertion(currentWindow);
 END;  { of TEKey stuff}
 END;  { of else key stroke }
 END;  { of proc}

 PROCEDURE doUpdates (myEvent : EventRecord);
 VAR
 i : integer;
 UpdateWindow, TempPort : WindowPtr;
 BEGIN
 UpdateWindow := WindowPtr(myEvent.message);
 IF UpdateWindow = ClipBdWindow THEN
 BEGIN
 GetPort(TempPort); {save port}
 SetPort(ClipBdWindow);
 BeginUpDate(ClipBdWindow);
 EraseRect(ClipBdWindow^.visRgn^^.rgnBBox);
 TEUpdate(ClipBdWindow^.visRgn^^.rgnBBox, ClipBdText);
 EndUpDate(ClipBdWindow);
 SetPort(TempPort);{restore port}
 END;
 FOR i := 1 TO MaxWindows DO
 BEGIN
 IF UpdateWindow = myWindows[i] THEN
 BEGIN
 GetPort(TempPort); {save port}
 SetPort(myWindows[i]);
 BeginUpDate(myWindows[i]);
 EraseRect(myWindows[i]^.portRect); 
 { clear update region}
 DrawControls(myWindows[i]);
 DrawGrowIcon(myWindows[i]);
 TEUpdate(myWindows[i]^.visRgn^^.rgnBBox, myText[i]);
 EndUpDate(myWindows[i]);
 SetPort(TempPort);
 END; {of if then}
 END; {of i loop}
 END; {of proc}

 PROCEDURE doActivates (myEvent : EventRecord);
 VAR
 i : integer;
 TargetWindow : WindowPtr;
 BEGIN
 TargetWindow := WindowPtr(myEvent.message);
 IF Odd(myEvent.modifiers) THEN
 BEGIN {activate}
 FOR i := 1 TO MaxWindows DO
 BEGIN
 IF TargetWindow = myWindows[i] THEN
 BEGIN
 SetPort(TargetWindow);
 UpdateMenus;
 DrawGrowIcon(myWindows[i]);
 TEActivate(myText[i]);
 Showcontrol(myVControls[i]);
 ShowControl(myHControls[i]);
 currentWindow := i;
 END { of my window activation}
 ELSE IF TargetWindow = ClipBdWindow 
 THEN
 BEGIN
 IF TEFromScrap <> noErr THEN
 BEGIN  {read desk scrap}
 END;
 currentWindow := 0;
 ClipBdText^^.teLength := TEGetScrapLen;
 TECalText(ClipBdText);
 InvalRect(ClipBdText^^.viewRect);
 END;
 END; { of i loop}
 END  { of activate loop}
 ELSE
 BEGIN {deactivate}
 FOR i := 1 TO MaxWindows DO
 BEGIN
 IF TargetWindow = myWindows[i] THEN
 BEGIN
 DrawGrowIcon(myWindows[i]);
 TEDeactivate(myText[i]);
 HideControl(myVControls[i]);
 HideControl(myHControls[i]);
 END; { of my window activation}
 END; { of i loop}
 END; {of deactivate loop}
 END; {of proc}

 PROCEDURE doSwitcher (myEvent : EventRecord);
 VAR
 SwitchInfo : LongInt;
 bit0 : LongInt;
 bit1 : LongInt;
 BEGIN  {we use the desk scrap so no need}
 SwitchInfo := myEvent.message;
 bit0 := 31; {convert 68000 to toolbox}
 bit1 := 30;
 IF BitTst(@SwitchInfo, bit0) = TRUE THEN
 BEGIN  {resume}
 IF BitTst(@SwitchInfo, bit1) = TRUE THEN
 BEGIN  {convert clipboard}
 END;
 END
 ELSE
 BEGIN  {suspend}
 IF BitTst(@SwitchInfo, bit1) = TRUE THEN
 BEGIN  {convert clipboard}
 END;
 END;
 END;

 PROCEDURE MainEventLoop;
 CONST
 SwitcherEvt = 15;
 VAR
 Event : EventRecord;
 DoIt : Boolean;
 BEGIN
 REPEAT
 SystemTask;
 IF currentWindow <> 0 THEN
 TEIdle(myText[currentWindow]);
 DoIt := GetNextEvent(EveryEvent, Event);
 IF DoIt THEN
 CASE Event.what OF
 mouseDown : 
 doMouse(Event);
 KeyDown, Autokey : 
 doKeyDowns(Event);
 updateEvt : 
 doUpdates(Event);
 activateEvt : 
 doActivates(Event);
 SwitcherEvt : 
 doSwitcher(Event);{event 15}
 OTHERWISE
 BEGIN
 END;
 END; {of event case}
 unLoadSeg(@doScrollers); {ScrollStuff unit}
 unLoadSeg(@doGrow); {myWrite Stuff unit }
 UNTIL Finished; {end program}
 IF TEGetScrapLen > 0 THEN
 BEGIN
 dummy := ZeroScrap;
 dummy := TEToScrap;
 END;
 END;

BEGIN {main program}
 InitThings;
 SetUpMenus;
 MainEventLoop;
END.
 
AAPL
$102.47
Apple Inc.
+2.71
MSFT
$44.88
Microsoft Corpora
+0.80
GOOG
$526.54
Google Inc.
+5.70

MacTech Search:
Community Search:

Software Updates via MacUpdate

RapidWeaver 6.0 - Create template-based...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
NTFS 12.0.39 - Provides full read and wr...
Paragon NTFS breaks down the barriers between Windows and OS X. Paragon NTFS effectively solves the communication problems between the Mac system and NTFS, providing full read and write access to... Read more
RestoreMeNot 2.0.3 - Disable window rest...
RestoreMeNot provides a simple way to disable the window restoration for individual applications so that you can fine-tune this behavior to suit your needs. Please note that RestoreMeNot is designed... Read more
Macgo Blu-ray Player 2.10.9.1750 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple iOS 8.1 - The latest version of Ap...
The latest version of iOS can be downloaded through iTunes. Apple iOS 8 comes with big updates to apps you use every day, like Messages and Photos. A whole new way to share content with your family.... Read more
TechTool Pro 7.0.5 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PDFKey Pro 4.0.2 - Edit and print passwo...
PDFKey Pro can unlock PDF documents protected for printing and copying when you've forgotten your password. It can now also protect your PDF files with a password to prevent unauthorized access and/... Read more
Yasu 2.9.1 - System maintenance app; per...
Yasu was originally created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks... Read more
Hazel 3.3 - Create rules for organizing...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a... Read more
Autopano Giga 3.7 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more

Latest Forum Discussions

See All

Fiete – A Day on a Farm Review
Fiete – A Day on a Farm Review By Amy Solomon on October 21st, 2014 Our Rating: :: A MEMORABLE EXPERIENCEUniversal App - Designed for iPhone and iPad Fiete – A day on a farm in an interactive app for young children full of... | Read more »
Tilt to Live: Gauntlet’s Revenge is Almo...
Tilt to Live: Gauntlet’s Revenge is Almost Here Posted by Jessica Fisher on October 21st, 2014 [ permalink ] One Man Left has announced the official release date of Tilt to Live: Gauntlet’s Re | Read more »
Sago Mini Monsters Celebrates Halloween...
Sago Mini Monsters Celebrates Halloween with Fun Costumes and Special Treats. Posted by Jessica Fisher on October 21st, 2014 [ permal | Read more »
Inferno 2 Review
Inferno 2 Review By Andrew Fisher on October 21st, 2014 Our Rating: :: TWIN STICK GOODNESSUniversal App - Designed for iPhone and iPad With tight controls and awesome, stark visuals, Inferno 2 is loads of fun.   | Read more »
Clips Review
Clips Review By Jennifer Allen on October 21st, 2014 Our Rating: :: CONVENIENT PASTINGUniversal App - Designed for iPhone and iPad Making copying and pasting more powerful than usual, Clips is a great way to move stuff around.   | Read more »
MonSense Review
MonSense Review By Jennifer Allen on October 21st, 2014 Our Rating: :: ORGANIZED FINANCESiPhone App - Designed for the iPhone, compatible with the iPad Organize your finances with the quick and easy to use, MonSense.   | Read more »
This Week at 148Apps: October 13-17, 201...
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 »
Angry Birds Transformers Review
Angry Birds Transformers Review By Jennifer Allen on October 20th, 2014 Our Rating: :: TRANSFORMED BIRDSUniversal App - Designed for iPhone and iPad Transformed in a way you wouldn’t expect, Angry Birds Transformers is a quite... | Read more »
GAMEVIL Announces the Upcoming Launch of...
GAMEVIL Announces the Upcoming Launch of Mark of the Dragon Posted by Jessica Fisher on October 20th, 2014 [ permalink ] Mark of the Dragon, by GAMEVIL, put | Read more »
Interview With the Angry Birds Transform...
Angry Birds Transformers recently transformed and rolled out worldwide. This run-and-gun title is a hit with young Transformers fans, but the ample references to classic Transformers fandom has also earned it a place in the hearts of long-time... | Read more »

Price Scanner via MacPrices.net

Select MacBook Airs $100 off MSRP, free shipp...
B&H Photo has 2014 a couple of MacBook Airs on sale for $100 off MSRP. Shipping is free, and B&H charges NY sales tax only. They also include free copies of Parallels Desktop and LoJack for... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
Strong iPhone, Mac And App Store Sales Drive...
Apple on Monday announced financial results for its fiscal 2014 fourth quarter ended September 27, 2014. The Company posted quarterly revenue of $42.1 billion and quarterly net profit of $8.5 billion... Read more
Apple Posts How-To For OS X Recovery
OS X 10.7 Lion and later include OS X Recovery. This feature includes all of the tools you need to reinstall OS X, repair your disk, and even restore from a Time Machine backup. OS X Recovery... Read more
Mac OS X Versions (Builds) Supported By Vario...
Apple Support has posted a handy resource explaining which Mac OS X versions (builds) originally shipped with or are available for your computer via retail discs, downloads, or Software Update. Apple... Read more
Deals on 2011 13-inch MacBook Airs, from $649
Daily Steals has the Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB) available for $699 with a 90 day warranty. The Mid-2011 13″ 1.7GHz i5 MacBook Air (4GB/128GB SSD) is available for $649 at Other... Read more
2013 15-inch 2.0GHz Retina MacBook Pro availa...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Updated iPad Prices
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers, including the new iPad Air 2 and the... Read more
Apple Pay Available to Millions of Visa Cardh...
Visa Inc. brings secure, convenient payments to iPad Air 2 and iPad mini 3as well as iPhone 6 and 6 Plus. Starting October 20th, eligible Visa cardholders in the U.S. will be able to use Apple Pay,... Read more
Textkraft Pocket – the missing TextEdit for i...
infovole GmbH has announced the release and immediate availability of Textkraft Pocket 1.0, a professional text editor and note taking app for Apple’s iPhone. In March 2014 rumors were all about... Read more

Jobs Board

Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager / Business Analyst, WW *Appl...
…a senior project manager / business analyst to work within our Worldwide Apple Fulfillment Operations and the Business Process Re-engineering team. This role will work Read more
*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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.