TweetFollow Us on Twitter

Search and Replace
Volume Number:5
Issue Number:5
Column Tag:Pascal Procedures

Related Info: Dialog Manager TextEdit Memory Manager

Modeless Search and Replace

By Mark McBride, Oxford, CA

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

Introduction

While working on development of a utility to speed integration of toolbox calls in MacFortran source code, I found myself looking for a search and replace routine. Perusal of Macintosh Revealed (Vol. 2) quickly led to the toolbox routine Munger (rhymes with plunger). A search of back issues of MacTutor also revealed the lack of a general discussion of Munger and doing find/replace (not even the generic multi-window text editor from the January 1987 issue). Thus, I decided to write a general purpose find/replace unit from a modeless dialog environment.

Basic Program Design

The find/replace unit and the accompanying example program are written in LS Pascal. Conversion to TML, Turbo, or MPW should be relatively straightforward. The find/replace unit is designed as a standalone unit that requires very little modification to the user’s program to add its capabilities. The modifications required include changing the main event loop to handle modeless dialog events, adding a new menu, and inserting calls to the find/replace unit in your program. When calling the find/replace routines the user passes the current event and a handle for the editText to be searched. The program contains a simple text edit window, a modeless Find dialog, and a modeless Replace Dialog. The program supports desk accessories; cut, copy, and paste within the program (but not with desk accessories); and should be screen and machine size independent. The program requires a 512Ke or later machine. If run on an old 512k machine, the program alerts the user and exits back to the Finder.

Simple Text Editor

The demonstration program uses the ROM85 library enhancements for the textedit window which allowed me to write a barebones editor sufficient for demonstration purposes. The text window can be resized, zoomed, and automatically scrolled. However, there are no scroll bars, thus movement to text outside the visible region must done with the arrow keys. Cut, copy, paste, and clear work properly. The text window contents cannot be loaded from disk, nor saved to disk. Upon startup (Figure 1), the text window contains Lincoln’s Gettysburg Address retrieved from the resource fork.

Figure 1.

Find/Replace Unit

The Find dialog allows the user to enter text characters and does an exact find. The search process can be initiated by clicking the ‘Find’ button or pressing the return key. The find routine works from the current selection point and does not wrap past the end of the textedit record. If found, the current selection is set to the string found and highlighted. If not found, the machine beeps. The find routine does not support wildcards nor non-printing characters (e.g., control characters).

The Search dialog allows the user to enter both the search and replace strings. Initially, the ‘Search’ button is highlighted as the default button. Once the search string has been found (and selected, the search routine is just the find routine), the ‘Replace’ button is highlighted. If the ‘Replace’ button is selected then the occurrence of the search string previously found is replaced. The replace string can be null but the find string must contain at least one character. When the replace operation is successfully completed, the ‘Search’ button returns to the default button. Although I have not included a ‘replace all’ button (which could easily be integrated), the user can continually press the return key searching and replacing. Dialog cut, copy, and paste from the text edit scrap is supported within both the Find and Replace dialogs.

Handling Modeless Dialogs

To properly handle the modeless find and replace dialogs four major aspects of modeless dialog event handling must be observed carefully: main event loop changes, window events, dialog events, and command keys. Each element is relatively straightforward, but the combination of the four can lead to some confusion and unnecessary coding.

Main Event Loop

When a program contains modeless dialogs, the toolbox routine IsDialogEvent should be called after calling GetNextEvent. Inside Mac (page I-416) warns that the program should call IsDialogEvent even if GetNextEvent returns false. When IsDialogEvent returns true the program should drop into its routine to handle events for the modeless dialog. Thus, the program should call GetNextEvent, then IsDialogEvent, and then start processing on the basis of the booleans returned by the two routines. If the program does a

{1}

if GetNextEvent(myEvent) then
 begin
 if IsDialogEvent(MyEvent) then
   begin
     Do_Modeless(MyEvent);
...

structure then null dialog events will not be passed to its modeless dialog routines. The alternative used by the program in the listing does the following:

{2}

EventFlag:=GetNextEvent(MyEvent);
if IsDialogEvent(MyEvent)then
  Do_Modeless(MyEvent)
else if EventFlag then
  begin
...

According to Inside Mac, IsDialogEvent returns true for:

• activate or update events for a dialog window

• mouse-down events in the content region of an active dialog window

• any other event when a dialog window is active.

However, the last statement is misleading.

Window Events

The structure region of a window is composed of two parts: the content region and the window frame (see Inside Mac, pages I-271,272). In the standard document window, the window frame contains the drag, go-away, and zoom regions (if zoom was desired). These regions do not overlap with the content region in the standard document window, although they could in a custom window definition. Thus, if your modeless dialog uses a standard document window (or a noGrowDocument proc) then you must be careful in handling events. A mouse-down event in the window frame of a modeless dialog will return false for IsDialogEvent. The main event loop must handle mouse-downs in the drag, go-away, and zoom boxes of a modeless dialog when using standard window types. This does not pose a particular problem since handling multiple windows for growing, dragging, and zooming is a standard Mac programming task.

Dialog Handler

When IsDialogEvent returns true, the user should pass control to a routine that handles the modeless dialog event. This routine should call the function

DialogSelect(theEvent,whichDialog,ItemHit):boolean.

DialogSelect will return a result of true with the dialog pointer in whichDialog and the item number in ItemHit when there is

• a mouse-down, key-down, or auto-key event in an enabled editText item. DialogSelect will take appropriate action within the editText item.

• a mouse-down event, then mouse released within an enabled control item. DialogSelect will call TrackControl while the mouse button is down.

• a mouse-down event in any other enabled item.

DialogSelect returns a result of false with the dialog pointer, whichDialog, undefined and ItemHit undefined when there is

• an activate or update event for the dialog window. DialogSelect will handle the redrawing of all items in the window (but will not handle redrawing of the default button).

• a mouse-down, key-down, or auto-key event in a disabled editText item.

• a key-down or auto-key event when no editText item exists.

• a mouse-down event in a control when the mouse is released outside the control.

• a mouse-down event in any other disabled item.

Thus, your modeless routine calls DialogSelect, letting the Dialog Manager handle actions while the user mouses around in the dialog. If the user indicates some action within an enabled dialog item, then the modeless routine takes appropriate action. If DialogSelect returns false then the modeless routine should do nothing. Two exceptions to handling returns of false from DialogSelect exist. First, if the modeless routine needs to take special actions for modeless update or activate events (e.g., redrawing the default button, showing selected text) then the routine must check for these types of events after DialogSelect returns false.

Second, DialogSelect does not check for the command key when handling key-down events. If some key-down events are special cases or if the command key equivalents for menu events (e.g., cut, copy, paste) are to be handled while the modeless dialog is the active window, then the modeless routine must check for key-down events prior to calling DialogSelect. The find/replace unit in the listing checks for edit menu command key equivalents, find/replace menu command key equivalents, and the return key as the default button.

Find/Replace Routines

Implementing a find/replace unit can take many alternative forms. Inside Mac does not specify user-interface guidelines for find/replace actions leading to many different solutions existing. The implementation given in the listing uses a single modeless dialog and follows a simple logic within three routines: Find, Search, and Replace. These routines provide the setup to access the toolbox routine Munger. Munger provides the core find and replace actions on the specified text. Switching between find and replace options alters the appearance and action of the modeless dialog.

Logic Used

When in the Find mode, the user can enter a text string (Figure 2). When the user clicks the find button (or presses the ‘return’ key) the editText record is searched forward from the current selection. If located, the find string is highlighted in the editText record. If the string is not in the current visible region, a call to TESelView scrolls the string into view (Figure 3). If the string is not found, then the machine beeps.

Figure 2.

Figure 3.

Figure 4.

When in the replace mode, the user must enter a find string while the replace string is optional (Figure 4). When the user clicks the find button (or presses ‘return’), starting at the current selection, the find routine tries to locate and highlight the find string. If found, the search routine changes the default button to ‘Replace’ (Figure 5). When the user clicks the replace button (or presses ‘return’) the find text is replaced with the replace text, leaving the current selection after the replacement text, and re-establishing the ‘Search’ button as the default button (Figure 6).

Figure 5.

Figure 6.

How It Works: Munger

The find, search, and replace routines serve to setup the proper information to send to and retrieve from the toolbox function Munger:

{3}

function Munger(h:handle; offset:longint; ptr1: ptr; len1: longint;
                          ptr2: ptr; len2: longint): longint.

The handle, h, specifies a handle to destination text generated as a relocatable block allocated by the memory manager. Offset refers to the starting location of the operation in the destination text and must be less than the length of the destination text. Ptr1, len1 refers to the target text and ptr2, len2 refers to the replacement text. The function value returned is negative if the operation failed and positive if the specified operation was successful. The behavior of Munger depends on the values passed. The six possible cases are:

Case 1: ptr1<>nil, len1>0; ptr2<>nil, len2>0: the destination text is searched from the offset specified to the end of the text, replacing the first occurrence of the target string found with the replacement text. The function returns the offset of the first byte past the replaced text.

Case 2: ptr1=nil, len1>0; ptr2<>nil, len2>0: the destination text from the specified offset to offset + len1 is replaced by the replacement text. The function returns the offset of the first byte past the replaced text.

Case 3: ptr1=nil, len1<0; ptr2<>nil, len2>0: the destination text from the specified offset to the end of the destination text is replaced by the replacement text. The function returns the offset of the first byte past the replaced text.

Case 4: len1=0; ptr2<>nil, len2>0: the replacement text is inserted into the destination text at the specified offset. The function returns the offset of the first byte past the replaced text.

Case 5: ptr1<>nil, len1>0; ptr2=nil: the destination text is searched for the target string. Munger returns the offset at the beginning of the found string.

Case 6: ptr1<>nil, len1>0; ptr2<>nil, len2=0: the target string is deleted from the destination text instead of replaced. The function returns the offset at the byte where the text was deleted.

The find routine uses Case 5 to perform the search. The find routine retrieves the find string from the dialog, checking for a target string of length greater than zero. The replace pointer is set to nil. If the current selection is not an insertion point, the offset is set to the end of the current selection for the find dialog. The target length is retrieved and the replace length is set to zero. Finally, Munger is called. If the target string is found, the current selection is set to the target and scrolled into view.

The search routine locates the target string using the find routine. If the find was successful, then the default button is set to the replace button and enabled. Once the target string is located, a user action to replace call the replace routine. The replace routine uses Case 1 for positive length replacement text and Case 6 for empty replacement text. The replace routine retrieves the target and replace text from the dialog edit fields, sets the offset to the current selection start of the destination text, and finally calls Munger. If Munger returns a positive value, the first occurrence of the target text was replaced. The editText is then recalibrated, the selection reset, and an invalrect call used to force an update of the editText window. Finally, the default button is set back to the Search button. If Munger returns a negative value, the replace failed and the machine beeps.

Concluding Remarks

The find/replace unit presented is simple in design, takes very little additional memory (approximately 5k), easily integrated into your application, and easily modified to suit your tastes. Most all the programming work is setting up the call to Munger and interpreting the results. The routine could also be changed to a modal dialog quickly. However, the modeless dialog environment seems more natural for a find/replace capability.

Listing:  EnvironIntf.p
{  File: EnvironsIntf.p}
{ Copyright Apple Computer, Inc. 1987}
{ All Rights Reserved}

UNIT EnvironsIntf;

INTERFACE
 CONST
 envMac = -1;    {returned by glue, if any}
 envXL = -2;{returned by glue, if any}
 envMachUnknown = 0;
 env512KE = 1;
 envMacPlus = 2;
 envSE = 3;
 envMacII = 4;
 envCPUUnknown = 0;{ CPU types }
 env68000 = 1;
 env68010 = 2;
 env68020 = 3;
 envUnknownKbd = 0;{ Keyboard types }
 envMacKbd = 1;
 envMacAndPad = 2;
 envMacPlusKbd = 3;
 envAExtendKbd = 4;
 envStandADBKbd = 5;
 { Errors }
 envNotPresent = -5500; { returned by glue. Official stuff}
 envBadSel = -5501;{ Selector non-positive }
 envSelTooBig = -5502;{ Selector bigger than call can take}

 TYPE
 SysEnvRec = RECORD
 environsVersion : INTEGER;
 machineType : INTEGER;
 systemVersion : INTEGER;
 processor : INTEGER;
 hasFPU : BOOLEAN;
 hasColorQD : BOOLEAN;
 keyBoardType : INTEGER;
 atDrvrVersNum : INTEGER;
 sysVRefNum : INTEGER;
 END;

 FUNCTION SysEnvirons (versionRequested : INTEGER;
 VAR theWorld : SysEnvRec) : OSErr;

IMPLEMENTATION
 FUNCTION SysEnvirons;
 External;
END.
Listing Globals.Pas:
UNIT SearchGlobs;

{File name:Globals.Pas}
{Function: Global Variables and Constants for Search Program}
{History: 7/02/88 MEM Initial Variables Added.   }
{                       }

INTERFACE

 USES
 ROM85;
 CONST
{window constants}
 minWidth = 80;
 minHeight = 80;
 mBarHeightGlob = $BAA;
 sBarWidth = 16;
 TxtMargin = 4;
 TxtWindID = 2;
 AboutID = 20;
 AlertID = 6;
 I_OK = 1;
 SpeechID = 100;
{Menu Resource ID’s}
 AppleID = 201;
 FileID = 202;
 EditID = 203;
 SearchID = 204;
{Menu Choice Constants}
 I_About = 1;
 I_Quit = 1;
 I_Undo = 1;
 I_Cut = 3;
 I_Copy = 4;
 I_Paste = 5;
 I_Clear = 6;
 I_Find = 1;
 I_Replace = 2;
 I_Show_Text = 4;
{Global Variables}
 VAR
{misc stuff}
 doneFlag : boolean;
 mBarHeight : integer;
{menu stuff}
 AppleMenu : MenuHandle;
 FileMenu : MenuHandle;
 EditMenu : MenuHandle;
 SearchMenu : MenuHandle;
{rectangles}
 DragRect : Rect;
 GrowRect : Rect;
 ScreenRect : Rect;
 TempRect : Rect;
{window stuff}
 MyWindow : WindowPtr;
{text edit stuff}
 hte : TEHandle;
{String stuff}
 theString : Str255;

IMPLEMENTATION
END.
Listing: Inits.Pas
UNIT Inits;
{File name:Init.Pas}
{Function: Initialization Routines for Search Program}
{History: 7/02/88 MEM All routines Added.   }

INTERFACE

 USES ROM85, EnvironsIntf, SearchGlobs;

 PROCEDURE InitMac;
 PROCEDURE CheckEnvirons;
 PROCEDURE InitTxtWind;
 PROCEDURE InitRects;
 PROCEDURE InitMenus;

IMPLEMENTATION
 PROCEDURE Crash;
 BEGIN
 ExitToShell;
 END;

 PROCEDURE InitMac;
 BEGIN
 MoreMasters;
 MoreMasters;
 MoreMasters;
 initCursor;
 FlushEvents(everyEvent, 0);
 doneFlag := false;
 END;

 PROCEDURE CheckEnvirons;
 VAR
 MyWorld : SysEnvRec;
 Version : integer;
 Err1, Err2 : OSErr;
 BEGIN
 Version := 1;
 Err1 := SysEnvirons(Version, MyWorld);
 IF (MyWorld.machineType < 1) THEN
 BEGIN
 ParamText(‘Program Requires Mac 512Ke or newer machine’, ‘’, ‘’, ‘’);
 Err2 := NoteAlert(AlertID, NIL);
 ExitToShell;
 END;
 END;

 PROCEDURE InitRects;
 VAR
 MemPtr : ^Integer;
 BEGIN
 MemPtr := pointer(MBarHeightGlob);
 mBarHeight := MemPtr^;
 screenrect := ScreenBits.Bounds;
 SetRect(DragRect, ScreenRect.left + 4, Screenrect.top + mBarHeight + 
4, Screenrect.right - 4, Screenrect.bottom - 4);
 SetRect(GrowRect, ScreenRect.left + minWidth, Screenrect.top + minHeight, 
Screenrect.right - 8, Screenrect.bottom - 8);
 END;

 PROCEDURE InitTxtWind;
 VAR
 ItemHdl : Handle;
 BEGIN
 MyWindow := GetNewWindow(TxtWindID, NIL, Pointer(-1));
 SelectWindow(MyWindow);
 SetPort(MyWindow);
 WITH MyWindow^.portRect DO
 SetRect(TempRect, 0, 0, right - sBarWidth - 1, bottom - sBarWidth - 
1);
 InsetRect(TempRect, TxtMargin, TxtMargin);
 hte := TENew(TempRect, TempRect);
 HLock(Handle(hte));
 hte^^.txFont := geneva;
 hte^^.fontAscent := 12;
 hte^^.lineHeight := 12 + 3 + 1; {ascent + descent + leading}
 HUnLock(Handle(hte));
 ItemHdl := GetResource(‘TEXT’, SpeechID);
 IF ItemHdl = NIL THEN
 BEGIN
 theString := ‘Search and Replace Demo program.  Use find and replace 
to change text in this window.’;
 TESetText(Pointer(Ord4(@theString) + 1), length(theString), hte);
 END
 ELSE
 BEGIN
 DetachResource(ItemHdl);
 SetHandleSize(hte^^.hText, SizeResource(ItemHdl));
 hte^^.hText := ItemHdl;
 hte^^.teLength := SizeResource(ItemHdl);
 ReleaseResource(ItemHdl);
 END;
 TESetSelect(0, 0, hte);
 TECalText(hte);
 TEAutoView(true, hte);
 TESelView(hte);
 TextFont(applFont);
 END;

 PROCEDURE InitMenus;
 VAR
 tempMenu : MenuHandle;
 BEGIN
 ClearMenuBar;
 tempMenu := GetMenu(AppleID);
 AddResMenu(tempMenu, ‘DRVR’);
 InsertMenu(tempMenu, 0);
 AppleMenu := tempMenu;
 tempMenu := GetMenu(FileID);
 InsertMenu(tempMenu, 0);
 FileMenu := tempMenu;
 tempMenu := GetMenu(EditID);
 InsertMenu(tempMenu, 0);
 EditMenu := tempMenu;
 tempMenu := GetMenu(SearchID);
 InsertMenu(tempMenu, 0);
 SearchMenu := tempMenu;
 DrawMenuBar;
 DisableItem(EditMenu, I_Undo);
 END;

END.
Listing: Utils.Pas

UNIT Utils;
{File name:Utils.Pas}
{Function: General Utility Routines for Search Program}
{History: 7/02/88 MEM Alert Error Routine Added.   }
{                       }

INTERFACE

 USES ROM85, SearchGlobs, Find_Dialog;

 PROCEDURE Do_About;
 PROCEDURE Do_Menu (theMenu, theItem : integer);
 PROCEDURE FrameDItem (dLog : DialogPtr; iNum : integer);
 PROCEDURE Update_Txt;
 PROCEDURE Act_Txt (theEvent : EventRecord);
 PROCEDURE FixText;
 PROCEDURE Grow_Txt (hSize, vSize : integer);
 PROCEDURE Do_Quit;

IMPLEMENTATION
 PROCEDURE Do_About;
 VAR
 oldPort : GrafPtr;
 dlg : DialogPtr;
 itemHit : integer;
 BEGIN
 GetPort(OldPort);
 dlg := GetNewDialog(AboutID, NIL, pointer(-1));
 FrameDItem(dlg, I_OK);
 ModalDialog(NIL, itemHit);
 DisposDialog(dlg);
 SetPort(OldPort);
 END;

 PROCEDURE Do_Menu;
 VAR
 DaNum : integer;
 Bool : boolean;
 DAName : Str255;
 oldPort : GrafPtr;
 BEGIN
 CASE theMenu OF
 AppleID : 
 BEGIN
 CASE theItem OF
 I_About : 
 BEGIN
 Do_About;
 END;
 OTHERWISE
 BEGIN
 GetPort(oldPort);
 GetItem(AppleMenu, theItem, DAName);
 DaNum := OpenDeskAcc(DAName);
 SetPort(oldPort);
 END;
 END;
 END;
 FIleID : 
 BEGIN
 CASE theItem OF
 I_Quit : 
 doneFlag := TRUE;
 OTHERWISE
 BEGIN
 END;
 END;
 END;
 EditID : 
 BEGIN
 IF NOT SystemEdit(theItem - 1) THEN
 BEGIN
 CASE theItem OF
 I_Cut : 
 TECut(hte);
 I_Copy : 
 TECopy(hte);
 I_Paste : 
 TEPaste(hte);
 I_Clear : 
 TEDelete(hte);
 OTHERWISE
 BEGIN
 END;
 END;
 END;
 END;
 SearchID : 
 BEGIN
 CASE theItem OF
 I_Find : 
 Open_Find(I_Find);
 I_Replace : 
 Open_Find(I_Replace);
 I_Show_Text : 
 BEGIN
 ShowWindow(MyWindow);
 SelectWindow(MyWindow);
 END;
 OTHERWISE
 BEGIN
 END;
 END;
 END;
 OTHERWISE
 BEGIN
 END;
 END;
 HiliteMenu(0);
 END;

 PROCEDURE FrameDItem;
 VAR
 iBox : Rect;
 iType : integer;
 iHandle : Handle;
 oldPenState : PenState;
 oldPort : GrafPtr;
 BEGIN
 GetPort(oldPort);
 SetPort(dlog);
 GetPenState(oldPenState);
 GetDItem(dLog, iNum, iType, iHandle, iBox);
 InsetRect(iBox, -4, -4);
 PenSize(3, 3);
 FrameRoundRect(iBox, 16, 16);
 SetPenState(oldPenState);
 SetPort(oldPort)
 END;

 PROCEDURE UpDate_Txt;
 VAR
 oldPort : GrafPtr;
 BEGIN
 GetPort(oldPort);
 SetPort(MyWindow);
 BeginUpDate(MyWindow);
 TEUpdate(hte^^.viewRect, hte);
 DrawGrowIcon(MyWindow);
 EndUpDate(MyWindow);
 SetPort(oldPort);
 END;

 PROCEDURE Act_Txt;
 BEGIN
 SetPort(MyWindow);
 IF Odd(theEvent.modifiers) THEN
 BEGIN
 TEActivate(hte);
 DrawGrowIcon(MyWindow);
 DisableItem(EditMenu, I_Undo);
 END
 ELSE
 BEGIN
 TEDeactivate(hte);
 DrawGrowIcon(MyWindow);
 EnableItem(EditMenu, I_Undo);
 END;
 END;

 PROCEDURE FixText;
 BEGIN
 HLock(Handle(hte));
 WITH hte^^ DO
 BEGIN
 viewRect := MyWindow^.portRect;
 WITH viewRect DO
 BEGIN
 right := right - sBarWidth - 1;
 bottom := bottom - sBarWidth - 1;
 bottom := (bottom DIV lineHeight) * lineHeight
 END;
 destRect := viewRect;
 InsetRect(destRect, TxtMargin, TxtMargin);
 TECalText(hte);
 TESelView(hte);
 END;
 HUnlock(Handle(hte));
 END;

 PROCEDURE Grow_Txt;
 BEGIN
 InvalRect(MyWindow^.portRect);
 EraseRect(MyWindow^.portRect);
 SizeWindow(MyWindow, hSize, vSize, true);
 InvalRect(MyWindow^.portRect);
 FixText;
 END;

 PROCEDURE Do_Quit;
 BEGIN
 TEDispose(hte);
 DisposeWindow(MyWindow);
 ExitToShell;
 END;
END.                                  {End of unit}
Listing:  Find.Pas
UNIT Find_Dialog;
{File name:Find.Pas   }
{Function: Handle a modeless dialog}
{History: 7/03/88 MEM - Made the Find dialog functional    }
INTERFACE

 USES ROM85;

 PROCEDURE Init_Find;
 PROCEDURE Act_Find;
 PROCEDURE Open_Find (Action : integer);
 PROCEDURE Do_Find (theEvent : EventRecord; hte : TEHandle);
 PROCEDURE Close_Find;

IMPLEMENTATION
 CONST
 FindID = 4;
 I_Find = 1;
 I_Replace = 2;
 I_Cancel = 3;
 I_Find_Label = 4;
 I_Find_Text = 5;
 I_Replace_Label = 6;
 I_Replace_Text = 7;
 Active = 0;
 InActive = 255;
 AlertID = 6;
 VAR
 MyDialog : DialogPtr;
 ExitDialog : Boolean;
 tempRect : Rect;
 DType : Integer;
 Index : Integer;
 DItem : Handle;
 CItem, CTempItem : controlhandle;
 itemHit : Integer;
 temp : Integer;
 Bolden : integer;
 Opened : integer;
 theString : Str255;

 PROCEDURE FrameItem (inum : integer);
 VAR
 iBox : Rect;
 iType : integer;
 iHandle : Handle;
 oldPenState : PenState;
 oldPort : GrafPtr;
 BEGIN
 GetPort(oldPort);
 SetPort(MyDialog);
 GetPenState(oldPenState);
 GetDItem(MyDialog, inum, iType, iHandle, iBox);
 InsetRect(iBox, -4, -4);
 PenSize(3, 3);
 FrameRoundRect(iBox, 16, 16);
 SetPenState(oldPenState);
 SetPort(oldPort)
 END;

 PROCEDURE DeFrameItem (inum : integer);
 VAR
 iBox : Rect;
 iType : integer;
 iHandle : Handle;
 oldPenState : PenState;
 oldPort : GrafPtr;
 BEGIN
 GetPort(oldPort);
 SetPort(MyDialog);
 GetPenState(oldPenState);
 GetDItem(MyDialog, inum, iType, iHandle, iBox);
 InsetRect(iBox, -4, -4);
 PenSize(3, 3);
 PenMode(PatBic);
 FrameRoundRect(iBox, 16, 16);
 SetPenState(oldPenState);
 SetPort(oldPort)
 END;

 PROCEDURE Init_Find;
 BEGIN
 MyDialog := NIL;
 END;

 PROCEDURE Act_Find;
 BEGIN
 IF (MyDialog <> NIL) THEN
 ShowWindow(MyDialog);
 END;

 PROCEDURE Open_Find;
 VAR
 ThisEditText : TEHandle;
 TheDialogPtr : DialogPeek;
 BEGIN
 IF (MyDialog = NIL) THEN
 BEGIN
 MyDialog := GetNewDialog(FindID, NIL, Pointer(-1));
 ShowWindow(MyDialog);
 SelectWindow(MyDialog);
 SetPort(MyDialog);
 TheDialogPtr := DialogPeek(MyDialog);
 ThisEditText := TheDialogPtr^.textH;
 HLock(Handle(ThisEditText));
 ThisEditText^^.txSize := 12;
 TextSize(12);
 ThisEditText^^.txFont := systemFont;
 TextFont(systemFont);
 ThisEditText^^.txFont := 0;
 ThisEditText^^.fontAscent := 12;
 ThisEditText^^.lineHeight := 12 + 3 + 1;
 HUnLock(Handle(ThisEditText));
 GetDItem(MyDialog, I_Replace_Text, DType, DItem, tempRect);
 SetIText(DItem, ‘’);
 SelIText(MyDialog, I_Replace_Text, 0, 0);
 GetDItem(MyDialog, I_Find_Text, DType, DItem, tempRect);
 SetIText(DItem, ‘’);
 SelIText(MyDialog, I_Find_Text, 0, 0);
 Opened := action;
 Bolden := I_Find;
 END
 ELSE
 BEGIN
 Opened := action;
 SelectWindow(MyDialog);
 END;
 IF (action = I_FInd) THEN
 BEGIN
 DeFrameItem(I_Replace);
 HideDItem(MyDialog, I_Replace);
 HideDItem(MyDialog, I_Replace_Label);
 HideDItem(MyDialog, I_Replace_Text);
 GetDItem(MyDialog, I_Find, DType, DItem, tempRect);
 GetIText(DItem, theString);
 SetCTitle(ControlHandle(DItem), ‘Find’);
 SetWTitle(MyDialog, ‘Find’);
 Bolden := I_Find;
 END
 ELSE
 BEGIN
 ShowDItem(MyDialog, I_Replace);
 SHowDItem(MyDialog, I_Replace_Label);
 SHowDItem(MyDialog, I_Replace_Text);
 GetDItem(MyDialog, I_Replace, DType, DItem, tempRect);
 HiLiteControl(ControlHandle(DItem), InActive);
 GetDItem(MyDialog, I_Find, DType, DItem, tempRect);
 SetCTitle(ControlHandle(DItem), ‘Search’);
 SetWTitle(MyDialog, ‘Replace’);
 END;
 DrawDialog(MyDialog);
 FrameItem(Bolden);
 ShowWindow(MyDialog);
 SelectWindow(MyDialog);
 END;

 PROCEDURE Close_Find;
 BEGIN
 IF (MyDialog <> NIL) THEN
 BEGIN
 DisposDialog(MyDialog);
 Opened := 0;
 MyDialog := NIL
 END;
 END;

 FUNCTION Find (hte : TEHandle) : boolean;
 VAR
 theFindString : Str255;
 StartAt, EndAt, targetlen, replacelen, FoundAt : longint;
 destText : Handle;
 targetPtr, replacePtr : Ptr;
 BEGIN
 Find := false;
 GetDItem(MyDialog, I_Find_Text, Dtype, DItem, tempRect);
 GetIText(DItem, theFindString);
 IF (theFindString = ‘’) THEN
 BEGIN
 ParamText(‘You have not specified anything to Find’, ‘’, ‘’, ‘’);
 temp := NoteAlert(AlertID, NIL);
 END
 ELSE
 BEGIN
 destText := Handle(hte^^.hText);
 targetPtr := pointer(Ord(@theFindString) + 1);
 replacePtr := NIL;
 IF (hte^^.selStart < hte^^.selEnd) THEN
 IF (opened = I_Find) THEN
 StartAt := hte^^.selEnd
 ELSE
 StartAt := hte^^.selStart
 ELSE
 StartAt := hte^^.selStart;
 targetlen := length(theFindString);
 replacelen := 0;
 FoundAt := Munger(destText, StartAt, targetPtr, targetlen, replacePtr, 
replacelen);
 IF (FoundAt >= 0) THEN
 BEGIN
 TESetSelect(FoundAt, FoundAt + targetlen, hte);
 TESelView(hte);
 TEActivate(hte);
 Find := true;
 END
 ELSE
 SysBeep(1);
 END;
 END;

 PROCEDURE Replace (hte : TEHandle);
 VAR
 theFindString, theReplString : Str255;
 StartAt, EndAt, targetlen, replacelen, FoundAt : longint;
 destText : Handle;
 targetPtr, replacePtr : Ptr;
 OldPort : GrafPtr;
 BEGIN
 GetDItem(MyDialog, I_Find_Text, Dtype, DItem, tempRect);
 GetIText(DItem, theFindString);
 GetDItem(MyDialog, I_Replace_Text, Dtype, DItem, tempRect);
 GetIText(DItem, theReplString);
 destText := Handle(hte^^.hText);
 targetPtr := pointer(Ord(@theFindString) + 1);
 targetlen := length(theFindString);
 replacePtr := pointer(Ord(@theReplString) + 1);
 replacelen := length(theReplString);
 StartAt := hte^^.selStart;
 FoundAt := Munger(destText, StartAt, targetPtr, targetlen, replacePtr, 
replacelen);
 IF (FoundAt >= 0) THEN
 BEGIN
 TECalText(hte);
 TESetSelect(FoundAt, FoundAt, hte);
 TESelView(hte);
 GetPort(OldPort);
 SetPort(hte^^.inPort);
 InvalRect(hte^^.viewRect);
 SetPort(OldPort);
 TEActivate(hte);
 GetDItem(MyDialog, I_Replace, DType, DItem, tempRect);
 HiLiteControl(ControlHandle(DItem), Inactive);
 DeFrameItem(I_Replace);
 FrameItem(I_Find);
 Bolden := I_Find;
 END
 ELSE
 BEGIN
 Sysbeep(1);
 GetDItem(MyDialog, I_Replace, DType, DItem, tempRect);
 HiLiteControl(ControlHandle(DItem), Inactive);
 IF (bolden = I_Replace) THEN
 DeFrameItem(I_Replace);
 FrameItem(I_Find);
 Bolden := I_Find;
 END;
 END;

 PROCEDURE Search (hte : TEHandle);
 VAR
 theReplString : Str255;
 success : boolean;
 BEGIN
 GetDItem(MyDialog, I_Replace_Text, Dtype, DItem, tempRect);
 GetIText(DItem, theReplString);
 success := Find(hte);
 IF success THEN
 BEGIN
 GetDItem(MyDialog, I_Replace, DType, DItem, tempRect);
 HiLiteControl(ControlHandle(DItem), Active);
 DeFrameItem(I_Find);
 FrameItem(I_Replace);
 Bolden := I_Replace;
 END;
 END;

 PROCEDURE Do_Find;
 CONST
 ReturnKey = 13;
 LABEL
 1;
 VAR
 Index : integer;
 myPt : Point;
 ExitDialog : boolean;
 CmdDown : boolean;
 success : boolean;
 chCode : integer;
 MyCmdKey : char;
 whichDialog : DialogPtr;
 BEGIN
 ExitDialog := false;
 IF (MyDialog <> NIL) THEN
 BEGIN
 IF (theEvent.what = KeyDown) THEN
 BEGIN
 CmdDown := odd(theEvent.modifiers DIV CmdKey);
 chCode := BitAnd(theEvent.message, CharCodeMask);
 MyCmdKey := CHR(chCode);
 IF CmdDown THEN
 BEGIN
 IF ((MyCmdKey = ‘x’) OR (MyCmdKey = ‘X’)) THEN
 DlgCut(MyDialog)
 ELSE IF ((MyCmdKey = ‘c’) OR (MyCmdKey = ‘C’)) THEN
 DlgCopy(MyDialog)
 ELSE IF ((MyCmdKey = ‘v’) OR (MyCmdKey = ‘V’)) THEN
 DlgPaste(MyDialog)
 ELSE IF ((MyCmdKey = ‘f’) OR (MyCmdKey = ‘F’)) THEN
 Open_Find(I_Find)
 ELSE IF ((MyCmdKey = ‘r’) OR (MyCmdKey = ‘R’)) THEN
 Open_Find(I_Replace);
 GOTO 1
 END
 ELSE IF (chCode = ReturnKey) THEN
 BEGIN
 IF (bolden = I_Find) THEN
 IF (Opened = I_Find) THEN
 success := Find(hte)
 ELSE
 Search(hte)
 ELSE
 Replace(hte);
 GOTO 1;
 END
 END;
 IF DialogSelect(theEvent, whichDialog, itemHit) THEN
 BEGIN
 GetDItem(whichDialog, itemHit, DType, DItem, tempRect);
 CItem := Pointer(DItem);
 IF (ItemHit = I_Cancel) THEN
 ExitDialog := true;
 IF (ItemHit = I_Find) THEN
 IF (Opened = I_Find) THEN
 success := Find(hte)
 ELSE
 Search(hte);
 IF (ItemHit = I_Replace) THEN
 Replace(hte);
 END
 ELSE IF (theEvent.what = UpDateEvt) THEN
 FrameItem(Bolden)
 ELSE IF (theEvent.what = ActivateEvt) AND (Opened = I_Find) AND Odd(theEvent.modifiers) 
THEN
 BEGIN
 GetDItem(MyDialog, I_Find, DType, DItem, tempRect);
 GetIText(DItem, theString);
 IF length(theString) > 0 THEN
 SelIText(MyDialog, I_Find_Text, 0, length(theString));
 END;
 END;
1 :
 IF ExitDialog THEN
 BEGIN
 Close_Find;
 END;
 END;

END.
Listing: Search.Pas
PROGRAM Searcher;
{Program name:Searcher.Pas   }
{This is the main program and event loop.  }
{History: 7/03/88 MEM Original Coding  }
{                       }
 USES
 ROM85, SearchGlobs, Inits, Find_Dialog, Utils;
 VAR
 theEvent : EventRecord;
 thecode : integer;
 theWindow : WindowPtr;
 thePeek : WindowPeek;
 OldPort : GrafPtr;
 menuResult : longint;
 newSize : longint;
 theMenu, theItem : integer;
 chCode : integer;
 ch : char;
 GlobalPt, localPt : Point;
 EventFlag : boolean;
 extend : boolean;
BEGIN
 InitMac;
 InitRects;
 InitMenus;
 InitTxtWind;
 Init_Find;
 REPEAT
 IF (hte <> NIL) THEN
 TEIdle(hte);
 SystemTask;
 EventFlag := GetNextEvent(everyEvent, theEvent);
 thecode := FindWindow(theEvent.where, theWindow);
 IF IsDialogEvent(theEvent) THEN
 Do_Find(theEvent, hte)
 ELSE IF EventFlag THEN
 BEGIN
 CASE theEvent.what OF
 MouseDown : 
 BEGIN
 IF (thecode = inMenuBar) THEN
 BEGIN
 menuResult := MenuSelect(theEvent.Where);
 theMenu := HiWord(menuResult);
 theItem := LoWord(menuResult);
 Do_Menu(theMenu, theItem);
 END;
 IF (thecode = InDrag) THEN
 DragWindow(theWindow, theEvent.where, DragRect);
 IF (thecode = inGrow) THEN
 BEGIN
 newSize := GrowWindow(theWindow, theEvent.where, GrowRect);
 IF newSize <> 0 THEN
 Grow_Txt(LoWord(newSize), HiWord(newSize))
 END;
 IF (thecode = inGoAway) THEN
 BEGIN
 IF TrackGoAway(theWindow, theEvent.where) THEN
 HideWindow(theWindow);
 END;
 IF ((thecode = inZoomIn) OR (thecode = inZoomOut)) THEN
 BEGIN
 IF TrackBox(thewindow, theEvent.where, theCode) THEN
 BEGIN
 GetPort(OldPort);
 SetPort(MyWindow);
 EraseRect(MyWindow^.portRect);
 ZoomWindow(Mywindow, thecode, TRUE);
 WITH MyWindow^.portRect DO
 Grow_Txt(right - left, bottom - top);
 SetPort(OldPort);
 END;
 END;
 IF (thecode = inContent) THEN
 BEGIN
 IF (theWindow <> FrontWindow) THEN
 SelectWindow(theWindow)
 ELSE
 BEGIN
 globalPt := theEvent.where;
 localPt := globalPt;
 GlobalToLocal(localPt);
 IF PtInRect(localPt, hte^^.viewRect) THEN
 BEGIN
 extend := (BitAnd(theEvent.modifiers, ShiftKey) <> 0);
 TEClick(localPt, extend, hte);
 END;
 END;
 END;
 IF (thecode = inSysWindow) THEN
 SystemClick(theEvent, theWindow);
 END;
 KeyDown, AutoKey : 
 BEGIN
 WITH theEvent DO
 BEGIN
 chCode := BitAnd(message, CharCodeMask);
 ch := CHR(chCode);
 IF (Odd(modifiers DIV CmdKey)) THEN
 BEGIN
 menuResult := MenuKey(ch);
 theMenu := HiWord(menuResult);
 theItem := LoWord(menuResult);
 IF (theMenu <> 0) THEN
 Do_Menu(theMenu, theItem)
 ELSE IF ((ch = ‘x’) OR (ch = ‘X’)) AND (hte <> NIL) THEN
 TECut(hte)
 ELSE IF ((ch = ‘c’) OR (ch = ‘C’)) AND (hte <> NIL) THEN
 TECopy(hte)
 ELSE IF ((ch = ‘v’) OR (ch = ‘V’)) AND (hte <> NIL) THEN
 TEPaste(hte);
 END
 ELSE IF (hte <> NIL) THEN
 TEKey(ch, hte);
 END;
 END;
 UpDateEvt : 
 BEGIN
 Update_Txt;
 END;
 ActivateEvt : 
 BEGIN
 Act_Txt(theEvent);
 END;
 OTHERWISE
 BEGIN
 END;
 END;
 END;
 UNTIL doneFlag;
 Do_Quit;
END.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Yasu 4.0.0 β - System maintenance app; p...
Yasu was 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 quickly within a... Read more
Skype 7.37.0.178 - Voice-over-internet p...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
EtreCheck 3.0.5 - For troubleshooting yo...
EtreCheck is an app that displays the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support Communities to... Read more
Amadeus Pro 2.3.1 - Multitrack sound rec...
Amadeus Pro lets you use your Mac computer for any audio-related task, such as live audio recording, digitizing tapes and records, converting between a variety of sound formats, etc. Thanks to its... Read more
NeoFinder 6.9.3 - Catalog your external...
NeoFinder (formerly CDFinder) rapidly organizes your data, either on external or internal disks, or any other volumes. It catalogs all your data, so you stay in control of your data archive or disk... Read more
WhatsApp 0.2.1880 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
Hazel 4.0.6 - Create rules for organizin...
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 familiar... Read more
Apple iBooks Author 2.5 - Create and pub...
Apple iBooks Author helps you create and publish amazing Multi-Touch books for iPad. Now anyone can create stunning iBooks textbooks, cookbooks, history books, picture books, and more for iPad. All... Read more
MYStuff Pro 2.0.26 - $39.99
MYStuff Pro is the most flexible way to create detail-rich inventories for your home or small business. Add items to MYStuff by dragging and dropping existing information, uploading new images, or... Read more
MarsEdit 3.7.8 - Quick and convenient bl...
MarsEdit is a blog editor for OS X that makes editing your blog like writing email, with spell-checking, drafts, multiple windows, and even AppleScript support. It works with with most blog services... Read more

How to get past Vulture Island's tr...
Vulture Island is a colorful and quirky mish-mash of platforming and puzzles. It’s creative and fresh, but sometimes the game can throw a curveball at you, leaving you stuck as to how you should progress. These tips will help you explore smoothly... | Read more »
The new Clash of Kings is just for Weste...
If you’ve played the original Clash of Kings, you’ll probably recognise the city building, alliance forging and strategic battles in Clash of Kings: The West. What sets this version apart is that it’s tailor made for a Western audience and the... | Read more »
Frost - Survival card game (Games)
Frost - Survival card game 1.12.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.12.1 (iTunes) Description: *Warning: the game will work on iPhone 5C and above and iPad Pro / 4. Other devices are not supported* | Read more »
How to build and care for your team in D...
Before you hit the trail and become a dog sledding legend, there’s actually a fair bit of prep work to be done. In Dog Sled Saga, you’re not only racing, you’re also building and caring for a team of furry friends. There’s a lot to consider—... | Read more »
How to win every race in Dog Sled Saga
If I had to guess, I’d say Dog Sled Saga is the most adorable racing game on the App Store right now. It’s a dog sled racing sim full of adorable, loyal puppies. Just look at those fluffy little tails wagging. Behind that cute, pixelated facade is... | Read more »
Let the war games commence in Gunship Ba...
Buzz Lightyear famously said, “This isn’t flying, this is falling – with style!” In the case of Gunship Battle: Second War, though, this really is flying - with style! The flight simulator app from Joycity puts you in control of 20 faithfully... | Read more »
How to get a high score in Fired Up
Fired Up is Noodlecake Games’ high score chasing, firefighting adventure. You take control of a wayward firefighter who propels himself up the side of a highrise with blasts of water. Sound silly? It is. It’s also pretty difficult. You can’t... | Read more »
NBA 2K17 (Games)
NBA 2K17 1.0 Device: iOS iPhone Category: Games Price: $7.99, Version: 1.0 (iTunes) Description: Following the record-breaking launch of NBA 2K16, the NBA 2K franchise continues to stake its claim as the most authentic sports video... | Read more »
Dog Sled Saga (Games)
Dog Sled Saga 1.0.1 Device: iOS Universal Category: Games Price: $3.99, Version: 1.0.1 (iTunes) Description: A game by Dan + Lisa As a rookie musher, foster a dogsledding team whose skills will grow if they're treated right. Week by... | Read more »
60 Seconds! Atomic Adventure (Games)
60 Seconds! Atomic Adventure 1.2 Device: iOS Universal Category: Games Price: $2.99, Version: 1.2 (iTunes) Description: 60 Seconds! is a dark comedy atomic adventure of scavenge and survival. Collect supplies and rescue your family... | Read more »

Price Scanner via MacPrices.net

21-inch iMacs on sale for up to $120 off MSRP
B&H Photo has 21″ iMacs on sale for up to $120 off MSRP including free shipping plus NY sales tax only: - 21″ 3.1GHz iMac 4K: $1379 $120 off MSRP - 21″ 2.8GHz iMac: $1199.99 $100 off MSRP - 21″ 1... Read more
13-inch 2.7GHz/256GB Retina MacBook Pro on sa...
Amazon.com has the 13″ 2.7GHz/256GB Retina Apple MacBook Pro on sale for $151 off MSRP including free shipping: - 13″ 2.7GHz/256GB Retina MacBook Pro (sku MF840LL/A): $1348 $151 off MSRP Read more
Apple TVs on sale for up to $50 off MSRP
Best Buy has 32GB and 64GB Apple TVs on sale for $40-$50 off MSRP on their online store. Choose free shipping or free local store pickup (if available). Sale prices for online orders only, in-store... Read more
Apple refurbished 13-inch Retina MacBook Pros...
Apple has Certified Refurbished 13″ Retina MacBook Pros available for up to $270 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 13″ 2.7GHz... Read more
Duplicate Sweeper Free On Mac App Store For O...
To celebrate the launch of Apple’s latest macOS Sierra, Stafford, United Kingdom based Wide Angle Software has announced that its duplicate file finder software, Duplicate Sweeper, is now available... Read more
13-inch Retina MacBook Pros on sale for up to...
B&H Photo has 13″ Retina Apple MacBook Pros on sale for up to $150 off MSRP. Shipping is free, and B&H charges NY tax only: - 13″ 2.7GHz/128GB Retina MacBook Pro: $1174.99 $125 off MSRP - 13... Read more
Evidence Surfaces Pointing To New A10X Chip F...
Citing a job description for a Project Lead position at Apple’s Austin, Texas engineering labs, Motley Fool’s Ashraf Eassa deduces that development is progressing well on Apple’s next-generation in-... Read more
Check Print’R for macOS Allows Anyone to Easi...
Delaware-based Match Software has announced the release and immediate availability of Check Print’R 3.21, an important update to their easy-to-use check printing application for macOS. Check Print’R... Read more
Apple refurbished 11-inch MacBook Airs availa...
Apple has Certified Refurbished 11″ MacBook Airs (the latest models), available for up to $170 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is... Read more
Apple refurbished 15-inch Retina MacBook Pros...
Apple has Certified Refurbished 2015 15″ Retina MacBook Pros available for up to $380 off the cost of new models. An Apple one-year warranty is included with each model, and shipping is free: - 15″ 2... Read more

Jobs Board

Sr. *Apple* Mac Engineer - Net2Source Inc....
…staffing, training and technology. We have following position open with our client. Sr. Apple Mac Engineer6+ Months CTH Start date : 19th Sept Travelling Job If Read more
*Apple* Retail - Multiple Positions-Norfolk,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Restaurant Manager (Neighborhood Captain) - A...
…in every aspect of daily operation. WHY YOU'LL LIKE IT: You'll be the Big Apple . You'll solve problems. You'll get to show your ability to handle the stress and Read more
Lead *Apple* Solutions Consultant - Apple (...
# Lead Apple Solutions Consultant Job Number: 51829230 Detroit, Michigan, United States Posted: Sep. 19, 2016 Weekly Hours: 40.00 **Job Summary** The Lead ASC is an Read more
US- *Apple* Store Leader Program - Apple (Un...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.