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.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Dash 3.0.2 - Instant search and offline...
Dash is an API Documentation Browser and Code Snippet Manager. Dash helps you store snippets of code, as well as instantly search and browse documentation for almost any API you might use (for a full... Read more
FontExplorer X Pro 5.0 - Font management...
FontExplorer X Pro is optimized for professional use; it's the solution that gives you the power you need to manage all your fonts. Now you can more easily manage, activate and organize your... Read more
Typinator 6.6 - Speedy and reliable text...
Typinator turbo-charges your typing productivity. Type a little. Typinator does the rest. We've all faced projects that require repetitive typing tasks. With Typinator, you can store commonly used... Read more
Arq 4.12.1 - Online backup to Google Dri...
Arq is super-easy online backup for the Mac. Back up to your own Google Drive storage (15GB free storage), your own Amazon Glacier ($.01/GB per month storage) or S3, or any SFTP server. Arq backs up... Read more
Gutenprint 5.2.11-pre1 - Quality drivers...
Gutenprint is a suite of printer drivers that may be used with most common UNIX print spooling systems, including CUPS, lpr, LPRng, or others. Gutenprint currently supports over 2000 printer models.... Read more
Dropbox 3.6.8 - Cloud backup and synchro...
Dropbox is an application that creates a special Finder folder that automatically syncs online and between your computers. It allows you to both backup files and keep them up-to-date between systems... Read more
Evernote 6.0.15 - Create searchable note...
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
MacUpdate Desktop 6.0.7 - Search and ins...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
Freeway Pro 7.0.4 - Drag-and-drop Web de...
Freeway Pro lets you design and build sophisticated responsive websites code free. With its user-oriented drag-and-drop interface, Freeway Pro helps you piece together the website of your dreams. Add... Read more
VueScan 9.5.18 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more

This Week at 148Apps:June 22-26, 2015
June's Summer Journey Continues With 148Apps How do you know what apps are worth your time and money? Just look to the review team at 148Apps. We sort through the chaos and find the apps you're looking for. The ones we love become Editor’s Choice,... | Read more »
LEGO® Minifigures Online (Games)
LEGO® Minifigures Online 1.0.1 Device: iOS iPhone Category: Games Price: $4.99, Version: 1.0.1 (iTunes) Description: | Read more »
World of Tanks Blitz celebrates its firs...
Today marks the first anniversary of the launch of World of Tanks Blitz, the mobile version of the PC tank battler, World of Tanks. World of Tanks Blitz launched on iOS and Android on June 26th last year and to celebrate, Wargaming is giving all... | Read more »
Heroes and Castles 2 Has its Own Standal...
Heroes and Castles 2 is a third-person castle defense game from the same team behind Block Fortress and Bug Heroes. It's cool, it's fun, and now it has its very own free version. [Read more] | Read more »
Formula Cartoon All-Stars Lets You Race...
Ever want to pit your favorite characters from shows like Steven Universe, Adventure Time, and Regular Show against each other in a not quite death race? Well once upon a time you could, but Formula All Stars Touch N' Go doesn't exist anymore. Hope... | Read more »
Retype - Typography Photo Editor (Photo...
Retype - Typography Photo Editor 1.0 Device: iOS Universal Category: Photography Price: $2.99, Version: 1.0 (iTunes) Description: Retype is built out of passion for great typography and it's all about adding text to photo with style... | Read more »
Hungry Shark Evolution Celebrates Shark...
Shark Week is almost here, as is Independence Day, so naturally Hungry Shark Evolution is going to get in on the action. Yes, even the fireworks. [Read more] | Read more »
The New Trivia Crack Will Feature a Musi...
It's official: iHeartMedia (you may know them from iHeartRadio) will be in charge of providing music-related questions for Trivia Crack's upcoming sequel. Also Trivia Crack is getting a sequel. [Read more] | Read more »
Toca Life: City (Education)
Toca Life: City 1.0 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0 (iTunes) Description: Welcome to Toca Life: City, a metropolis filled with everyday fun! Customize characters, explore exciting locations and... | Read more »
Ironkill's Big New Update is Big an...
The other popular robot fighting game on the App Store, Ironkill, has received a pretty substantial update today. We're talking new bots, new rewards, graphical tweaks; the works. [Read more] | Read more »

Price Scanner via MacPrices.net

MacBook Airs on sale for up to $75 off MSRP
Save up to $75 on the purchase of a new 2015 13″ or 11″ 1.6GHz MacBook Air at the following resellers. Shipping is free with each model: 11" 128GB MSRP $899 11" 256GB... Read more
Apple’s Education discount saves up to $300 o...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
Save up to $600 with Apple refurbished Mac Pr...
The Apple Store has Apple Certified Refurbished Mac Pros available 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
Mac Pros on sale for up to $260 off MSRP
B&H Photo has Mac Pros on sale for up to $260 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2799, $200 off MSRP - 3.5GHz 6-core Mac Pro: $3719.99... Read more
Save up to $400 on 2014 15-inch Retina MacBoo...
B&H Photo has previous-generation 2014 15″ Retina MacBook Pros on sale for up to $400 off original MSRP. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro... Read more
15-inch Retina MacBook Pros on sale for up to...
B&H Photo has new 2015 15″ Retina MacBook Pros on sale for up to $125 off MSRP including free shipping plus NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1899.99 $100 off - 15″ 2.5GHz... Read more
College Student Deals: Additional $100 off Ma...
Take an additional $100 off all MacBooks and iMacs at Best Buy Online with their College Students Deals Savings, valid through July 11, 2015. Anyone with a valid .EDU email address can take advantage... Read more
Apple refurbished Time Capsules available for...
The Apple Store has certified refurbished Time Capsules available for $100 off MSRP. Apple’s one-year warranty is included with each Time Capsule, and shipping is free: - 2TB Time Capsule: $199, $100... Read more
Newsweek Launches iPhone App
The venerable weekly news magazine Newsweek, owned by IBT Media, has announced the launch of its first iPhone app. The new app is available through Apple’s App Store and will allow consumers to read... Read more
New Initiative Covering 80 Million Homes Will...
Today, Internet service providers, equipment suppliers and retail equipment manufacturers joined the Consumer Electronics Association (CEA) and the National Cable & Telecommunications Association... Read more

Jobs Board

Architect / Senior Software Engineer, *Apple...
**Job Summary** Apple Pay is already changing our pay-habit in a deeper and fundamental level. We are looking for a software engineer with a passion for large scale inte Read more
Project Manager, *Apple* Retail New Store O...
**Job Summary** An Apple Retail New Store Openings & Remodels Project Manager is responsible for successfully managing the openings, remodels, and small works of Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
Program Manger, WW *Apple* Direct Fulfillme...
**Job Summary** We are seeking a business analyst to work within our Worldwide Apple Direct Fulfillment Operations team. This role will work closely with related program Read more
Hardware Design Validation Engineer - *Apple...
**Job Summary** The Apple Watch team is looking for a Hardware Design Validation Engineer. This person will be part of the Apple Watch hardware team with Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.