TweetFollow Us on Twitter

MIDI User Interface
Volume Number:5
Issue Number:5
Column Tag:MIDI Connections

Related Info: List Manager Dialog Manager

MIDI User Interface

By Kirk Austin, San Rafael, CA

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

MIDI and the User Interface

This is an article that takes a look at the Macintosh’s Dialog Box routines, and how they can be used effectively. The PopUp menus and ArrowEditText controls described here are an attempt at getting the greatest use out of limited space in a Dialog Box. Techniques like this are important in programs that make use of MIDI like sequencers and patch librarians, for example. Extensive use of dialogs can be found in these type of programs.

The significance of the user interface

This article is going to take a little bit of a different slant from the previous MIDI articles because I think it is important to talk about the human interface considerations for a bit. It seems that the thing that separates good Macintosh programs from average ones is the way that the user interface is implemented. A program has to look and feel like a Macintosh program should. The best way to accomplish this is to try to follow the Apple guidelines for doing things, and only using alternate techniques when absolutely nothing else will work. Probably the first book that aspiring Macintosh programmers should read is Human Interfaced Guidelines published by Addison Wesley.

Dialogs in MIDI programs

Most of the things that take place in a MIDI program happen in Dialog Boxes. These structures have been made very simple to use by Apple, and they can lead the user through a great deal of setup information that would otherwise be difficult to deal with. Getting the most out of a particular dialog box can be a challenge, though. There is limited space and a lot of things to be accomplished, so what is the best way to communicate with the user while making the most efficient use of space? In addressing this problem, I’ve found the pop-up menu idea to be very useful. It takes up very little space in a dialog box until it is selected. At that point it can display a large range of choices. Now, this can also be done with the list manager by displaying a scrolling list of choices, but that would take up more room than the pop-up menu solution. Another technique that I have made use of is what I call an Edit Text Arrow Control. I use this for the input of numbers. It can be used like a standard Edit Text Item, but, in addition, there is an up/down arrow control associated with the item that allows the user to select a number with the mouse instead of having to use the keyboard. In order to provide an example of these two techniques I have written a sample application that lets the user change the MIDI program number on a synthesizer. We’ll get to the application in a short time, but first we should talk about the Pop-up Menus and the Arrow Edit Text Control.

The RefCon field

Fortunately for us, Apple gave us the RefCon field of a window record. This is a longint that we can use however we want. I use this field to give each Dialog a unique number that can be used to calculate a number for the User Items in a Dialog. Both the Pop-Up Menus and the ArrowEditTextCtl use this number to identify themselves.

Figure 1. Example Dialog box

PopUp Menus

These routines are based on some samples distributed by Apple, and produce the standard Pop-Up Menus as described in the User Interface Guidelines. The way I have implemented them is closely related with the items in the dialog box that I created with the Resource Editor. This is because the PopUp menu is made up of two dialog items, a static text item and a user item. My routines expect the UserItem item Number to be one greater then the staticText item number. This works out to be a logical way to create the PopUp menu location from the Resource Editor. Just create the StaticText first, then create the UserItem. In addition to the items in the dialog box you have to define the menu as a resource with the Resource Editor. The important thing here is that the menu resource number has to be the UserItem item number plus the value in the Dialog’s RefCon field (whew). I set the dialog’s RefCon field to 200 in my program, and if I were going to use additional dialog’s I could assign RefCon values of 300, 400, 500, etc. to them. If these numbering relationships are kept straight then it is pretty easy to create and use PopUp Menus. There are only two routines to use for all of the PopUp Menus in your application:

{1}

procedure DrawPopUp(TheDialog : DialogPtr; theItem : integer);
function PUMenuAction(theDialog : DialogPtr; PopUpItem : integer) : integer;

The DrawPopUp procedure is called by PUMenuAction and also by the Dialog Manager after you install it as your UserItem drawing proc. Otherwise, you never actually call this routine yourself. PUMenuAction is what you call when there’s a mousedown in the UserItem that is associated with the particular PopUp menu. PUMenuAction returns the menu item number as its result.

The ArrowEditTextCtl

EditText items in dialog boxes are great! They are pretty easy to use, and provide a standard way for the user to enter information into the Mac. The only thing about them that bothers me sometimes is that they require the user to type in the information, thereby taking the hand off of the mouse. I wanted a way to change text in an EditText item that would work from the mouse, so I came up with the ArrowEditTextCtl. The ArrowEditTextCtl is a regular EditText item with a frame drawn around it. Along the right side of the frame are the up and down arrows that increment or decrement the number in the EditText item. The ArrowEditTextCtl is a structure that also requires two dialog items defined for it. There is an EditText box and a UserItem. The EditText item number must be one less that the UserItem item number. Some care must be taken when creating the EditText and UserItem’s. First create your EditText box (which must be 16 pixels high), then create the UserItem to be the same coordinates except -3 top, -3 left, +3 bottom, and +13 right. Once you do that the control will draw itself properly. There are two routines that are used to implement the ArrowEditTextCtl’s in a program:

{2}

procedure DrawArrowETCtl(TheDialog : DialogPtr: theItem : integer);
 
function ArrowCtlAction(TheDialog : DialogPtr; theItem : integer; limitLo 
: integer; limitHi : integer) : integer;

All you have to do with the DrawArrowETCtl procedure is install it as the UserItem’s drawing proc. Then you call ArrowCtlAction in response to a mousedown in the UserItem. The way it is written this routine will return the integer that is selected by either using the up and down arrows, or typing it in the EditText box.

Using the MIDI library

The MIDI library is a set of routines that were described in the December issue of MacTutor, so I don’t think we need to spend any more time on them here. All the program is doing as far as MIDI is concerned is sending a program change message, which is pretty trivial, really. [There are some updates to Kirk's MIDI library from the last published version. They are not printed in the article, but they are included on the source code disk for this issue. -ed]

The MIDI Ctls Application

The application is pretty simple, really. It just gives you a menu with one item in it. When you select the menu item a dialog box appears on the screen. This is the dialog that demonstrates the use of the PopUp menu and the ArrowEditTextCtl. The dialog box is created as ‘not visible’ by the Resource Editor. This lets us do our GetNewDialog call and install all of the drawing procedures for the UserItem’s as well as set up the RefCon field of the dialog. After all of that is done we do a call to ShowWindow to make the dialog visible. This works out to be much faster than watching the dialog draw all of its items on the screen. The function MyDialogFilter is used to watch for the mouse clicks in our UserItem’s as well as check for keyboard input and making sure that a valid number gets typed into the EditText item. The rest of the program is your standard vanilla Macintosh application.

Listing:  MIDICtls

{ Kirk Austin, 4/9/88 }
{This shows how to use a dialog box in a MIDI program}

PROGRAM ShellExample;

 USES
 ROM85, ColorQuickDraw, ColorMenuMgr, LSPMIDI;

{ Global Constants }
 CONST
 Null = ‘’;

 AppleMenuID = 1;
 FileMenuID = 2;
 EditMenuID = 3;
 MIDIMenuID = 4;
 PopMenuID = 206;{Item number plus the value in the Dialog’s RefCon}

 AboutID = 200;
 MIDIDialog = 201;
 MIDIDialogRefCon = 200;
 {Items in our dialog box}
 OKOutline = 8;
 AEditText = 3;
 ArrowETCtl = 4;
 iPopPrompt = 5; {the Prompt staticText}
 iPopUp = 6;{the Pop-up userItem}
 ModemCheckBox = 9;
 PrinterCheckBox = 10;

{ Global Types }
 TYPE
 MIDIPrgData = RECORD
 MIDIChan : integer;
 MIDIProg : integer;
 ModemActive : boolean;
 PrinterActive : boolean;
 END;
 MPDPtr = ^MIDIPrgData;
 MPDHdle = ^MPDPtr;

{ Global Variables }
 VAR
 myMenus : ARRAY[AppleMenuID..MIDIMenuID] OF MenuHandle;
 Done : Boolean; { true when user selects quit}
 TextCursor : CursHandle; {handle to the text entry cursor}
 ClockCursor : CursHandle; {handle to the waiting watch cursor}
 PopMenuHdle : MenuHandle;
 TheMPDPtr : MPDPtr;
 TheMPDHdle : MPDHdle;
 TheResHdle : handle;
 TheResRefNum : integer;
 TheType : integer;
 TheHandle : Handle;
 TheRect : Rect;

 PROCEDURE ShowAbout;
 VAR
 theDlog : DialogPtr;
 oldPort : GrafPtr;
 BEGIN
 GetPort(oldPort);
 theDlog := GetNewDialog(AboutID, NIL, Pointer(-1));
 SetPort(theDlog);
 DrawDialog(theDlog);
 WHILE NOT Button DO
 SystemTask;
 DisposDialog(theDlog);
 SetPort(oldPort);
 END;

 PROCEDURE LaunchIt (mode : integer;
 VAR fName : Str255);
{The compiler has just pushed a word for the mode, and a pointer to the 
string}
 INLINE
 $204F,{movea.l a7,a0;(a0) is ptr to string, 4(a0) is mode}
 $A9F2; {_Launch}

 PROCEDURE DoXfer;
 VAR
 where : Point;
 reply : SFReply;
 vRef : integer;
 thefName : Str255;
 textType : SFTypeList;
 BEGIN
 where.h := 80;
 where.v := 55;
 textType[0] := ‘APPL’;
 SFGetFile(where, Null, NIL, 1, textType, NIL, reply);
 WITH reply DO
 IF NOT good THEN
 thefName := Null
 ELSE
 BEGIN
 thefName := fName;
 vRef := vRefNum
 END;
 IF thefName <> Null THEN
 BEGIN
 Done := true;
 IF SetVol(NIL, vRef) = noErr THEN
 LaunchIt(0, thefName)
 END
 END;

 PROCEDURE DrawOKOutline (theDialog : DialogPtr;
 theItem : INTEGER);
 VAR
 savePen : PenState;
 BEGIN
 GetPenState(savePen); {save the old pen state}
 GetDItem(TheDialog, TheItem, TheType, TheHandle, TheRect); {get the 
item’s rect}
 PenSize(3, 3); {make the pen fatter}
 InsetRect(TheRect, -4, -4);
 FrameRoundRect(TheRect, 16, 16); {draw the ring}
 SetPenState(savePen); {restore the pen state}
 END; {DrawOKOutline}

{DrawPopUp procedure was made to be as general as possible}
{The main thing to remember is that it expects PopUpMenuID to}
{be TheItem + TheRefCon of the Dialog.Also,Prompt item number}
{must be 1 less than the Pop Up Menu item number}
 PROCEDURE DrawPopUp (TheDialog : DialogPtr;
 TheItem : integer);
 CONST
 {constants for positioning the default item within its box}
 leftSlop = 13;  {leave this much space on left of title}
 rightSlop = 5;  {  this much on right}
 botSlop = 5;    {  this much below baseline}
 VAR
 TheType : integer;
 TheHandle : handle;
 r : Rect;
 TheString : Str255;
 newWid, newLen, wid : INTEGER;
 TheMenuHdle : MenuHandle;
 TheMenuItem : integer;
 i : integer;
 TheChar : char;
 TheRefCon : integer;
 MenuItemsCount : integer;
 BEGIN
 {Get the menu that is associated with this Dialog Item (TheItem + TheRefCon)}
 TheRefCon := LoWord(GetWRefCon(WindowPtr(TheDialog)));
 TheMenuHdle := MenuHandle(GetResource(‘MENU’, TheItem + TheRefCon));
 {Now, figure out which menu item is the current selection by scanning 
for a check mark}
 MenuItemsCount := CountMItems(TheMenuHdle);
 TheMenuItem := 0;
 i := 1;
 REPEAT
 GetItemMark(TheMenuHdle, i, TheChar);
 IF TheChar = char(CheckMark) THEN
 TheMenuItem := i;
 i := i + 1;
 UNTIL (TheMenuItem <> 0) OR (i = MenuItemsCount + 1);
 IF TheMenuItem = 0 THEN
 BEGIN
 SetItemMark(TheMenuHdle, 1, CHR(checkMark));
{check the first item}
 TheMenuItem := 1;
 END;
 GetItem(TheMenuHdle, TheMenuItem, TheString);
{get currently-selected item}
 GetDItem(TheDialog, TheItem, TheType, TheHandle, r);    {set up the 
rectangle}
 WITH r DO
 BEGIN
 InsetRect(r, -1, -1); {make it a little bigger}
 {Make sure title fits. Truncate it add an ellipses (“ ”)}
 {if it doesn’t (by the way, “ ” is option-semicolon)}
 wid := (right - left) - (leftSlop + rightSlop); {available string area}
 newWid := StringWidth(TheString); {get current width}
 IF newWid > wid THEN
 BEGIN {doesn’t fit - truncate it}
 newLen := LENGTH(TheString);
 {current length in characters}
 wid := wid - CharWidth(‘ ’);
 {subtract width of ellipses}

 REPEAT {until fits (or we run out of characters)}
 {drop the last character and its width}
 newWid := newWid- CharWidth(TheString[newLen]);
 newLen := PRED(newLen);
 UNTIL (newWid <= wid) OR (LENGTH(TheString) = 0);
 {add the ellipses character}
 newLen := SUCC(newLen); {one more char}
 TheString[newLen] := ‘ ’; {it’s the ellipses}
 TheString[0] := CHR(newLen); {fix the length}
 END;

 {draw the box and its drop shadow}
 FrameRect(r);
 MoveTo(right, top + 2);
 LineTo(right, bottom);
 LineTo(left + 2, bottom);
 {draw the string}
 MoveTo(left + LeftSlop, bottom - BotSlop);
 DrawString(TheString);
 END;
 END; {DrawPopUp}

 FUNCTION PUMenuAction (TheDialog : DialogPtr;
 PopUpItem : integer) : integer;
 VAR
 popLoc : Point;
 newChoice : INTEGER;
 chosen, ignoreLong : LongInt;
 TheString : Str255;
 TheItem : integer;
 TheType : integer;
 TheHandle : handle;
 PromptRect : rect;
 PopUpRect : rect;
 TheMenuHdle : MenuHandle;
 TheRefCon : integer;
 TheMenuID : integer;
 TheMenuItem : integer;
 i : integer;
 TheChar : char;
 MenuItemsCount : integer;
 BEGIN
 PUMenuAction := 0;
 {Get the menu that is associated with this Dialog Item (PopUpItem + 
TheRefCon)}
 TheRefCon := LoWord(GetWRefCon(WindowPtr(TheDialog)));
 TheMenuID := PopUpItem + TheRefCon;
 TheMenuHdle := MenuHandle(GetResource(‘MENU’, PopUpItem + TheRefCon));
 {Now, figure out which menu item is the current selection by scanning 
for a check mark}
 MenuItemsCount := CountMItems(TheMenuHdle);
 TheMenuItem := 0;
 i := 1;
 REPEAT
 GetItemMark(TheMenuHdle, i, TheChar);
 IF TheChar = char(CheckMark) THEN
 TheMenuItem := i;
 i := i + 1;
 UNTIL (TheMenuItem <> 0) OR (i = MenuItemsCount + 1);
 {Call PopUpMenuSelect and let user drag around. Note that }
 {(top,left) parameters to PopUpMenuSelect are our item’s, }
 {converted to global coordinates.}
 GetDItem(TheDialog, PopUpItem - 1, TheType, TheHandle, PromptRect);
 GetDItem(TheDialog, PopUpItem, TheType, TheHandle, PopUpRect);
 InvertRect(PromptRect); {hilight the prompt}
 InsertMenu(TheMenuHdle, -1);{insert our menu in menu list}
 PopLoc := PopUpRect.TopLeft; {copy our item’s topleft}
 LocalToGlobal(PopLoc); {convert back to global coords}
 CalcMenuSize(TheMenuHdle); {Work around Menu Mgr bug}
 WITH popLoc DO
 chosen := PopUpMenuSelect(TheMenuHdle, v, h, TheMenuItem);
 InvertRect(PromptRect); {unhilight the prompt}
 DeleteMenu(TheMenuID); {remove our menu from menu list}
 {Was something chosen?}
 IF chosen <> 0 THEN
 BEGIN {yep, something was chosen}
 newChoice := LoWord(chosen); {get chosen item number}
 IF newChoice <> TheMenuItem THEN
 BEGIN
 {the user chose an item other than the current one}
 SetItemMark(TheMenuHdle, TheMenuItem, ‘ ‘); {unmark the old choice}
 SetItemMark(TheMenuHdle, newChoice, CHR(checkMark)); {mark the new choice}
 PUMenuAction := newChoice;
 {Draw the new title}
 EraseRect(PopUpRect);
 DrawPopUp(theDialog, iPopUp);
 END; {if this choice was not the current choice}
 END; {if something was chosen}
 END;{of PUMenuAction}

{EditText number must be 1 less than ArrowUserItem number}
 PROCEDURE DrawArrowETCtl (TheDialog : DialogPtr;
 TheItem : integer);
 VAR
 theType : Integer;{ the type of dlog item   }
 theHandle : Handle; { Handle to the item    }
 theRect : Rect; { rect which encloses the item}
 Height : Integer;
 HalfHeight : integer;
 ArrowRect : rect;

 BEGIN
 GetDItem(TheDialog, TheItem, theType, theHandle, theRect); {get handle 
to control}
 FrameRect(TheRect);
 InsetRect(TheRect, 2, 2);
 TheRect.right := TheRect.right - 10;
 FrameRect(TheRect);
 GetDItem(TheDialog, TheItem, theType, theHandle, theRect); {get handle 
to control}
 Height := TheRect.bottom - TheRect.top;
 HalfHeight := Height DIV 2;
 HalfHeight := TheRect.bottom - HalfHeight;
 TheRect.left := TheRect.right - 11;

 EraseRect(TheRect);
 FrameRect(TheRect);

 MoveTo(TheRect.left, HalfHeight);{draw bold center line}
 LineTo(TheRect.right - 1, HalfHeight);
 MoveTo(TheRect.left, HalfHeight - 1);
 LineTo(TheRect.right - 1, HalfHeight - 1);

 ArrowRect.top := TheRect.top + 4; {draw up arrow}
 ArrowRect.bottom := HalfHeight - 2;
 ArrowRect.left := TheRect.left + 3;
 ArrowRect.right := TheRect.right - 3;
 FillRect(ArrowRect, black);
 MoveTo(ArrowRect.left - 1, ArrowRect.top + 1);
 LineTo(ArrowRect.right, ArrowRect.top + 1);
 MoveTo(ArrowRect.left + 1, ArrowRect.top - 1);
 LineTo(ArrowRect.right - 2, ArrowRect.top - 1);
 MoveTo(ArrowRect.left + 2, ArrowRect.top - 2);
 LineTo(ArrowRect.left + 2, ArrowRect.top - 2);

 ArrowRect.top := HalfHeight + 2;
 ArrowRect.bottom := TheRect.bottom - 4;
 ArrowRect.left := TheRect.left + 3;
 ArrowRect.right := TheRect.right - 3;
 FillRect(ArrowRect, black);
 MoveTo(ArrowRect.left - 1, ArrowRect.bottom - 2);
 LineTo(ArrowRect.right, ArrowRect.bottom - 2);
 MoveTo(ArrowRect.left + 1, ArrowRect.bottom);
 LineTo(ArrowRect.right - 2, ArrowRect.bottom);
 MoveTo(ArrowRect.left + 2, ArrowRect.bottom + 1);
 LineTo(ArrowRect.left + 2, ArrowRect.bottom + 1);
 END;

 FUNCTION ArrowCtlAction (TheDialog : DialogPtr;
 TheItem : integer;
 LimitLo : integer;
 LimitHi : integer) : integer;
 VAR
 theType : Integer;
 theHandle : Handle;
 theRect : Rect;
 TheNum : longint;
 TheString : Str255;
 Height : Integer;
 HalfHeight : integer;
 ArrowRect : rect;
 ThePoint : point;
 Inverted : boolean;
 HowLong : longint;
 TickResult : longint;
 UpArrowRect : rect;
 DnArrowRect : rect;
 TheTEHandle : handle;
 GoingUp : boolean;

 PROCEDURE BtnDelay (ticks : integer);
 VAR
 dummy : longint;
 i : integer;
 BEGIN
 i := ticks;
 IF ticks = 0 THEN
 i := 1;
 REPEAT
 Delay(1, dummy);
 i := i - 1;
 UNTIL (i = 0) OR NOT button;
 END;

 BEGIN
 GetDItem(TheDialog, TheItem - 1, theType, theHandle, theRect); {get 
handle to control}
 TheTEHandle := TheHandle;
 GetIText(TheTEHandle, TheString);
 StringToNum(TheString, TheNum);
 ArrowCtlAction := loword(TheNum);
 GetDItem(TheDialog, TheItem, theType, theHandle, theRect); {get handle 
to control}
 Height := TheRect.bottom - TheRect.top;
 HalfHeight := Height DIV 2;
 HalfHeight := TheRect.bottom - HalfHeight;
 TheRect.left := TheRect.right - 11;
 UpArrowRect := TheRect;
 UpArrowRect.bottom := HalfHeight;
 DnArrowRect := TheRect;
 DnArrowRect.top := HalfHeight;
 Inverted := false;
 HowLong := 22;
 GetMouse(ThePoint);
 IF (PtInRect(ThePoint, TheRect)) AND Stilldown THEN {we need to hilite 
an arrow}
 BEGIN
 IF PtInRect(ThePoint, UpArrowRect) THEN
 BEGIN
 ArrowRect := UpArrowRect;
 GoingUp := true;
 END
 ELSE
 BEGIN
 ArrowRect := DnArrowRect;
 GoingUp := false;
 END;
 REPEAT
 GetMouse(ThePoint);
 IF NOT PtInRect(ThePoint, ArrowRect) THEN
 BEGIN
 IF inverted THEN
 BEGIN
 TheRect := ArrowRect;
 InsetRect(TheRect, 1, 1);
 InvertRect(TheRect);
 Inverted := false;
 END;
 END
 ELSE
 BEGIN
 IF NOT Inverted THEN
 BEGIN
 TheRect := ArrowRect;
 InsetRect(TheRect, 1, 1);
 InvertRect(TheRect);
 Inverted := true;
 END;
 GetIText(TheTEHandle, TheString);
 StringToNum(TheString, TheNum);
 IF GoingUp THEN
 BEGIN
 IF TheNum <> LimitHi THEN
 BEGIN
 TheNum := TheNum + 1;
 IF TheNum > LimitHi THEN
 TheNum := LimitHi;
 ArrowCtlAction := loword(TheNum);
 NumToString(TheNum, TheString);
 SetIText(TheTEHandle, TheString);
 SelIText(TheDialog, TheItem - 1, 0, 32767);
 END;
 END
 ELSE
 BEGIN
 IF TheNum <> LimitLo THEN
 BEGIN
 TheNum := TheNum - 1;
 IF TheNum < LimitLo THEN
 TheNum := LimitLo;
 ArrowCtlAction := loword(TheNum);
 NumToString(TheNum, TheString);
 SetIText(TheTEHandle, TheString);
 SelIText(TheDialog, TheItem - 1, 0, 32767);
 END;
 END;
 BtnDelay(HowLong);
 IF HowLong > 3 THEN
 HowLong := HowLong - 2;
 END;
 UNTIL NOT StillDown;
 DrawArrowETCtl(TheDialog, TheItem);
 END;
 END; {ArrowCtlAction}

 FUNCTION MyDialogFilter (theDialog : DialogPtr;
 VAR theEvent : EventRecord;
 VAR item : integer) : Boolean;
{function called by ModalDialog for every event that occurs}
{ while in control. It is used to “filter” events so you}
{ can do things when certain events occur.   It is used to }
{ change cursor to an I beam when editing text.  The routine }
{ also handles keyboard entry; limiting text input to numbers}
{ and making return & enter the same as clicking OK button }

 CONST
 CrCode = 13; {ASCII code for RETURN}
 EnterCode = 3;  {ASCII ccode for ENTER}
 BsCode = 8; {ASCII code for Back Space}
 TabCode = 9;  {ASCII ccode for Tab}
 VAR
 mouseLocation : point; {holds coordinates of mouse loc.}
 TheHandle : Handle;  {used for dummy purpose here}
 TheType : Integer;  {used for dummy purpose here}
 TheRect : Rect; {used for dummy purpose here}
 TextBox1 : Rect;{defines area to test for cursor change}
 TheString : Str255;
 TheNum : longint;
 TheDlogRecPtr : DialogPeek;
 TheItem : integer;
 TheChoice : integer;
 TheMenuItem : integer;
 i : integer;
 TheChar : char;
 TheMenuHdle : menuHandle;
 ignorelong : longint;
 BEGIN
 MyDialogFilter := false;  {let modalDialog handle event}
 GetDItem(theDialog, AEditText, TheType, TheHandle, TextBox1); {chg cur 
in area}

 CASE theEvent.what OF
 nullEvent : {nothing happening chk if cursor to change}
 BEGIN
 GetMouse(mouseLocation);
 IF PtinRect(mouseLocation, TextBox1) THEN
 SetCursor(TextCursor^^)
 ELSE
 SetCursor(arrow);
 GetIText(TheHandle, TheString); {see if someone typed in an invalid 
number}
 StringToNum(TheString, TheNum);
 IF TheNum > 128 THEN
 BEGIN
 SysBeep(10);
 TheNum := 128;
 NumToString(TheNum, TheString);
 SetIText(TheHandle, TheString);
 SelIText(TheDialog, AEditText, 0, 32767);
 {hilite the editable text}
 END;
 IF TheNum < 1 THEN
 BEGIN
 SysBeep(10);
 TheNum := 1;
 NumToString(TheNum, TheString);
 SetIText(TheHandle, TheString);
 SelIText(TheDialog, AEditText, 0, 32767);
 {hilite the editable text}
 END;
 END;

 mouseDown : 
 BEGIN {“Click!”}
 mouseLocation := theEvent.where; 
{copy the mouse position}
 GlobalToLocal(mouseLocation); {convert to local coordinates}

 {Was the click in a user item?}
 IF (FindDItem(theDialog, mouseLocation) + 1) = 4 THEN
 BEGIN
 TheMPDPtr^.MIDIProg := ArrowCtlAction(TheDialog, 4, 1, 128);
 END; {if clicked in ArrowEditTextCtl userItem}

 IF (FindDItem(theDialog, mouseLocation) + 1) = iPopUp THEN
 BEGIN {Clicked in the pop-up box}
 TheChoice := PUMenuAction(TheDialog, iPopUp);
 IF TheChoice <> 0 THEN
 BEGIN
 MyDialogFilter := TRUE; {dialog is over}
 TheMPDPtr^.MIDIChan := TheChoice;
 item := iPopUp; {have ModalDialog return that the user changed items}
 END;
 END; {if clicked in our userItem}
 END; {mousedown case}

 keyDown, autokey :    {to follow std. procedure, chk if RETURN or ENTER 
was pressed}
 BEGIN
 IF (theEvent.message MOD 256) IN [crCode, enterCode] THEN
 BEGIN
 GetDItem(theDialog, 1, TheType, TheHandle, TheRect);
 HiLiteControl(ControlHandle(TheHandle), 1);
 {hilite the OK button}
 Delay(3, ignoreLong);
 HiliteControl(ControlHandle(TheHandle), 0);
 MyDialogFilter := true;
 Item := 1;
 END
 ELSE IF (theEvent.message MOD 256) IN [bsCode, tabCode] THEN
 BEGIN
 END
 ELSE IF (Char(theEvent.message MOD 256) >= ‘0’) AND (Char(theEvent.message 
MOD 256) <= ‘9’) THEN
 BEGIN
 TheDlogRecPtr := DialogPeek(theDialog);
 TheItem := TheDlogRecPtr^.editField + 1;
 { find out which EditText Item we are in }
 GetDItem(theDialog, TheItem, TheType, TheHandle, TheRect);
 GetIText(TheHandle, TheString);
 IF (Length(TheString) > 2) THEN
 SetIText(TheHandle, Null);
 { set it to Null if there are more than 3 characters }
 END
 ELSE
 MyDialogFilter := true;
 END;
 OTHERWISE
 ;
 END; {of the CASE statment}
 END;

 PROCEDURE SendMIDI;
 VAR
 dummy : longint;
 BEGIN
 IF TheMPDPtr^.ModemActive THEN
 BEGIN
 InitSCCA;
 TXMIDIA(TheMPDPtr^.MIDIChan + 191);
 TXMIDIA(TheMPDPtr^.MIDIProg - 1);
 Delay(1, dummy);
 ResetSCCA;
 END;
 IF TheMPDPtr^.PrinterActive THEN
 BEGIN
 InitSCCB;
 TXMIDIB(TheMPDPtr^.MIDIChan + 191);
 TXMIDIB(TheMPDPtr^.MIDIProg - 1);
 Delay(1, dummy);
 ResetSCCB;
 END;
 END; {of SendMIDI}

 PROCEDURE ProcessMenu (codeWord : Longint);{ menu selec}
 VAR
 menuNum : Integer;
 itemNum : Integer;
 NameHolder : str255;
 dummy : Integer;
 yuck : boolean;
 oldPort : GrafPtr;
 aDialog : DialogPtr;
 ItemHit : integer;
 TheItemHandle : handle;
 TheItemType : integer;  {type of the selected item}
 TheItemRect : rect;  {bounding box of the selected item}
 TheNum : longint;
 TheString : Str255;
 BEGIN
 IF codeWord <> 0 THEN  { nothing was selected}
 BEGIN
 menuNum := HiWord(codeWord);
 itemNum := LoWord(codeWord);
 CASE menuNum OF { the different menus}
 AppleMenuID : 
 BEGIN
 IF itemNum < 3 THEN
 BEGIN
 ShowAbout;
 END
 ELSE
 BEGIN
 GetItem(myMenus[AppleMenuID], itemNum, NameHolder);
 dummy := OpenDeskAcc(NameHolder);
 END;
 END;
 FileMenuID : 
 BEGIN
 CASE ItemNum OF
 1 : 
 BEGIN
 DoXfer;
 END;
 2 : 
 BEGIN
 Done := true;
 END;
 END;
 END;
 EditMenuID : 
 BEGIN
 yuck := SystemEdit(itemNum - 1);
 END;
 MIDIMenuID : 
 BEGIN
 GetPort(oldPort);
 {Get a menu}
 PopMenuHdle := GetMenu(PopMenuID); {Create a menu (its title is ignored)}
 SetItemMark(PopMenuHdle, TheMPDPtr^.MIDIChan, CHR(checkMark)); {check 
it}
 aDialog := GetNewDialog(MIDIDialog, NIL, WindowPtr(-1));
 SetPort(aDialog);
 SetWRefCon(WindowPtr(aDialog), MIDIDialogRefCon); {set the defaults}
 GetDItem(aDialog, AEditText, theType, TheHandle, TheRect);
 TheNum := TheMPDPtr^.MIDIProg;
 NumToString(TheNum, TheString);
 SetIText(TheHandle, TheString);
 GetDItem(aDialog, ModemCheckBox, theType, TheHandle, TheRect);
 IF TheMPDPtr^.ModemActive THEN
 SetCtlValue(ControlHandle(TheHandle), 1);
 GetDItem(aDialog, PrinterCheckBox, theType, TheHandle, TheRect);
 IF TheMPDPtr^.PrinterActive THEN
 SetCtlValue(ControlHandle(TheHandle), 1);
{Find out where our UserItems are, set their item handles to }
 {a pointer to the drawing procedures}
 GetDItem(aDialog, ArrowETCtl, theType, TheHandle, TheRect);
 SetDItem(aDialog, ArrowETCtl, theType, @DrawArrowETCtl, TheRect);
 GetDItem(aDialog, iPopUp, theType, TheHandle, TheRect);
 SetDItem(aDialog, iPopUp, theType, @DrawPopUp, TheRect);
 GetDItem(aDialog, OKOutline, theType, TheHandle, TheRect);
 SetDItem(aDialog, OKOutline, theType, @DrawOKOutline, TheRect);
 SelIText(aDialog, 3, 0, 32767);{hilite editable text}
 ShowWindow(WindowPtr(aDialog)); {show window}
 REPEAT
 ModalDialog(@MyDialogFilter, ItemHit);     {will process all events 
while dialog is up}
 CASE itemHit OF

 1 : 
 BEGIN {this is the Send Button item}
 SendMIDI;
 END;

 2 : 
 BEGIN {this is the Done Button item}
 DisposDialog(aDialog);
 SetPort(oldPort);
 END;

 iPopUp : 
 BEGIN {this is the PopUpMenu item}
 SysBeep(1);
 END;

 ModemCheckBox : 
 BEGIN {this is the Modem Check Box item}
 GetDItem(aDialog, ModemCheckBox, theType, TheHandle, TheRect);
 IF TheMPDPtr^.ModemActive THEN
 BEGIN
 TheMPDPtr^.ModemActive := false;
 SetCtlValue(ControlHandle(TheHandle), 0);
 END
 ELSE
 BEGIN
 TheMPDPtr^.ModemActive := true;
 SetCtlValue(ControlHandle(TheHandle), 1);
 END;
 END;

 PrinterCheckBox : 
 BEGIN {this is Printer Check Box item}
 GetDItem(aDialog, PrinterCheckBox, theType, TheHandle, TheRect);
 IF TheMPDPtr^.PrinterActive THEN
 BEGIN
 TheMPDPtr^.PrinterActive:= false;
 SetCtlValue(ControlHandle(TheHandle), 0);
 END
 ELSE
 BEGIN
 TheMPDPtr^.PrinterActive := true;
 SetCtlValue(ControlHandle(TheHandle), 1);
 END;
 END;
 OTHERWISE
 ;
 END; {case of item hit}
 UNTIL ItemHit = 2;{We’re done with Dialog}
 END;
 END;
 HiliteMenu(0);
 END;
 END;

 PROCEDURE DealWithMouseDowns (theEvent : EventRecord);
 VAR
 location : Integer;
 windowPointedTo : WindowPtr;
 mouseLoc : point;
 windowLoc : integer;
 VandH : Longint;
 Height : Integer;
 Width : Integer;
 BEGIN
 mouseLoc := theEvent.where;
 windowLoc := FindWindow(mouseLoc, windowPointedTo);
 CASE windowLoc OF
 inMenuBar : 
 BEGIN
 ProcessMenu(MenuSelect(mouseLoc));
 END;
 inSysWindow : 
 BEGIN
 SystemClick(theEvent, windowPointedTo);
 END;
 inContent : 
 BEGIN
 IF windowPointedTo <> FrontWindow THEN
 BEGIN
 SelectWindow(windowPointedTo);
 END;
 END;
 OTHERWISE
 BEGIN
 END;
 END;
 END;

 PROCEDURE DealWithKeyDowns (theEvent : EventRecord);
 TYPE
 Trick = PACKED RECORD
 CASE boolean OF
 true : (
 long : Longint
 );
 false : (
 chr3, chr2, chr1, chr0 : char
 )
 END;
 VAR
 CharCode : char;
 TrickVar : Trick;
 BEGIN
 TrickVar.long := theEvent.message;
 CharCode := TrickVar.chr0;
 IF BitAnd(theEvent.modifiers, CmdKey) = CmdKey THEN {check for a menu 
selection}
 BEGIN
 ProcessMenu(MenuKey(CharCode));
 END
 END;

 PROCEDURE DealWithActivates (theEvent : EventRecord);
 VAR
 TargetWindow : WindowPtr;
 BEGIN
 TargetWindow := WindowPtr(theEvent.message);
 IF Odd(theEvent.modifiers) THEN
 BEGIN
 SetPort(TargetWindow);
 END
 ELSE
 BEGIN

 END;
 END;

 PROCEDURE DealWithUpdates (theEvent : EventRecord);
 VAR
 UpDateWindow : WindowPtr;
 tempPort : WindowPtr;
 BEGIN
 UpDateWindow := WindowPtr(theEvent.message);
 GetPort(tempPort);
 SetPort(UpDateWindow);
 BeginUpDate(UpDateWindow);

 EndUpDate(UpDateWindow);
 SetPort(tempPort);
 END;

 PROCEDURE MainEventLoop;
 VAR
 Event : EventRecord;
 ProcessIt : boolean;
 x : byte;
 BEGIN
 REPEAT
 SystemTask;
 ProcessIt := GetNextEvent(everyEvent, Event); { get the next event in 
queue}
 IF ProcessIt THEN
 BEGIN
 CASE Event.what OF
 mouseDown : 
 DealWithMouseDowns(Event);
 AutoKey : 
 DealWithKeyDowns(Event);
 KeyDown : 
 DealWithKeyDowns(Event);
 ActivateEvt : 
 DealWithActivates(Event);
 UpdateEvt : 
 DealWithUpdates(Event);
 OTHERWISE
 BEGIN
 END;
 END;
 END;
 UNTIL Done;
 END;

 PROCEDURE MakeMenus;{ get the menus & display them}
 VAR
 index : Integer;
 BEGIN
 FOR index := AppleMenuID TO MIDIMenuID DO
 BEGIN
 myMenus[index] := GetMenu(index);
 InsertMenu(myMenus[index], 0);
 END;
 AddResMenu(myMenus[AppleMenuID], ‘DRVR’);
 DrawMenuBar;
 END;

{ Program Starts Here }
BEGIN
 Done := false;
 FlushEvents(everyEvent, 0);
   { initialize routines go here }
   {get the cursors we use and lock them down - no clutter}
 ClockCursor := GetCursor(watchCursor);
 TextCursor := GetCursor(iBeamCursor);
 HLock(Handle(ClockCursor));
 HLock(Handle(TextCursor));
 MakeMenus;
 TheResHdle := GetResource(‘MIDI’, 128);
 HLock(TheResHdle);
 TheMPDHdle := MPDHdle(TheResHdle);
 TheMPDPtr := TheMPDHdle^;
 InitCursor;
 MainEventLoop;
 ChangedResource(TheResHdle);
 TheResRefNum := HomeResFile(TheResHdle);
 UpdateResFile(TheResRefNum);
 ReleaseResource(TheResHdle);
END.
 
AAPL
$102.99
Apple Inc.
+0.52
MSFT
$44.38
Microsoft Corpora
-0.50
GOOG
$532.71
Google Inc.
+6.17

MacTech Search:
Community Search:

Software Updates via MacUpdate

Evernote 5.6.2 - Create searchable notes...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
OmniOutliner 4.1.3 - Organize your ideas...
OmniOutliner is a flexible program for creating, collecting, and organizing information. Give your creativity a kick start by using an application that's actually designed to help you think. It's... Read more
BBEdit 11.0 - Powerful text and HTML edi...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
Apple Security Update 2014-005 - For OS...
Apple Security Update is recommended for all users and improves the security of Mac OS X. For information on the security content of this update, please visit this website: http://support.apple.com/... Read more
EyeTV 3.6.6 - Watch and record TV on you...
EyeTV brings a rich TV experience to your Mac. Watch live TV on your Mac. Pause, rewind, and record whenever you want. EyeTV gives you powerful control over what you watch and how you watch it. Put... Read more
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
Command-C 1.1.5 - Clipboard sharing tool...
Command-C is a revolutionary app which makes easy to share your clipboard between iOS and OS X using your local WiFi network, even if the app is not currently opened. Copy anything (text, pictures,... 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

Latest Forum Discussions

See All

Super Glyph Quest (Games)
Super Glyph Quest 1.01 Device: iOS Universal Category: Games Price: $2.99, Version: 1.01 (iTunes) Description: Adventure is back Questers! Combine elemental glyphs together to cast powerful spells and vanquish adorable monsters in... | Read more »
Fighting Fantasy: Caverns of the Snow Wi...
Fighting Fantasy: Caverns of the Snow Witch 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: Travel to Northern Allansia’s perilous Icefinger Mountains to defeat the wicked Snow Witch in this... | Read more »
Star Warfare 2: Payback Review
Star Warfare 2: Payback Review By Blake Grundman on October 22nd, 2014 Our Rating: :: ONE-TRICK PONYUniversal App - Designed for iPhone and iPad Unfortunately, it doesn’t take long for Star Warfare 2’s free-firing fun to turn into... | Read more »
TinType by Hipstamatic (Photography)
TinType by Hipstamatic 1.0 Device: iOS iPhone Category: Photography Price: $.99, Version: 1.0 (iTunes) Description: Create hauntingly beautiful, soul capturing portraits with TinType by Hipstamatic. Inspired by daguerreotypes,... | Read more »
The Latest Update for Heroes of Dragon A...
The Latest Update for Heroes of Dragon Age Introduces Daily PvE Challenges Posted by Ellis Spice on October 22nd, 2014 [ permalink ] | Read more »
Bee Brilliant Review
Bee Brilliant Review By Jennifer Allen on October 22nd, 2014 Our Rating: :: SAMEY MATCH-3Universal App - Designed for iPhone and iPad Want even more Match-3 gaming and fancy a bee based twist? Bee Brilliant is that game. Don’t... | Read more »
A New Trailer has Been Revealed for Epic...
A New Trailer has Been Revealed for Epic of Kings Posted by Jessica Fisher on October 22nd, 2014 [ permalink ] Dead Mage Inc. has released a new, action-packed trailer for the upcoming Epic of Kings. | Read more »
Find the Line Review
Find the Line Review By Campbell Bird on October 22nd, 2014 Our Rating: :: INSLIDE THE LINESUniversal App - Designed for iPhone and iPad Tease out beautiful line drawings in this unique, free-to-play puzzle game.   | Read more »
Things Get Magical in The Sims FreePlay...
Things Get Magical in The Sims FreePlay with a New Halloween Update Posted by Ellis Spice on October 22nd, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
The Silent Age Episode 2 Review
The Silent Age Episode 2 Review By Jennifer Allen on October 22nd, 2014 Our Rating: :: ROUNDING THINGS OFF NICELYUniversal App - Designed for iPhone and iPad Rounding off a great point and click adventure comes The Silent Age... | Read more »

Price Scanner via MacPrices.net

Save with Best Buy’s College Student Deals
Take an additional $50 off all MacBooks and iMacs at Best Buy Online with their College Students Deals Savings, valid through November 1st. Anyone with a valid .EDU email address can take advantage... Read more
iPad Air 2 & iPad mini 3 Best Tablets Yet...
The new iPads turned out to be pretty much everything I’d been hoping for and more than I’d expected.”More” particularly in terms of a drinking-from-a-firehose choice of models and configurations,... Read more
Drafts 4 Reinvents iOS Productivity App
N Richland Hills, Texas based Agile Tortoise has announced the release of Drafts 4 for iPhone and iPad. Drafts is a quick capture note taking app with flexible output actions. Drafts 4 scales from... Read more
AT&T accepting preorders for new iPads fo...
AT&T Wireless is accepting preorders for the new iPad Air 2 and iPad mini 3, cellular models, for $100 off MSRP with a 2-year service agreement: - 16GB iPad Air 2 WiFi + Cellular: $529.99 - 64GB... Read more
Apple offering refurbished Mac Pros for up to...
The Apple Store is offering Apple Certified Refurbished 2013 Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more
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

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.