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

Microsoft OneNote 15.29 - Free digital n...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more
Spotify 1.0.44.100. - Stream music, crea...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
SpamSieve 2.9.27 - Robust spam filter fo...
SpamSieve is a robust spam filter for major email clients that uses powerful Bayesian spam filtering. SpamSieve understands what your spam looks like in order to block it all, but also learns what... Read more
VueScan 9.5.62 - 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
Fantastical 2.3.2 - Create calendar even...
Fantastical 2 is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event... Read more
PCalc 4.4.4 - Full-featured scientific c...
PCalc is a full-featured, scriptable scientific calculator with support for hexadecimal, octal, and binary calculations, as well as an RPN mode, programmable functions, and an extensive set of unit... Read more
Alfred 3.2.1 - Quick launcher for apps a...
Alfred is an award-winning productivity application for OS X. Alfred saves you time when you search for files online or on your Mac. Be more productive with hotkeys, keywords, and file actions at... Read more
OmniPlan 3.6 - Robust project management...
With OmniPlan, you can create logical, manageable project plans with Gantt charts, schedules, summaries, milestones, and critical paths. Break down the tasks needed to make your project a success,... Read more
Backblaze 4.2.0.990 - Online backup serv...
Backblaze is an online backup service designed from the ground-up for the Mac. With unlimited storage available for $5 per month, as well as a free 15-day trial, peace of mind is within reach with... Read more
AppDelete 4.3.1 - $7.99
AppDelete is an uninstaller that will remove not only applications but also widgets, preference panes, plugins, and screensavers along with their associated files. Without AppDelete these associated... Read more

Latest Forum Discussions

See All

Galaxy on Fire 3 and four other fantasti...
Galaxy on Fire 3 - Manticore brings the series back for another round of daring space battles. It's familiar territory for folks who are familiar with the franchise. If you've beaten the game and are looking to broaden your horizons, might we... | Read more »
The best apps for your holiday gift exch...
What's that, you say? You still haven't started your holiday shopping? Don't beat yourself up over it -- a lot of people have been putting it off, too. It's become easier and easier to procrastinate gift shopping thanks to a number of apps that... | Read more »
Toca Hair Salon 3 (Education)
Toca Hair Salon 3 1.0 Device: iOS Universal Category: Education Price: $2.99, Version: 1.0 (iTunes) Description: | Read more »
Winter comes to Darkwood as Seekers Note...
MyTona, based in the chilly Siberian city of Yakutsk, has brought a little festive fun to its hidden object game Seekers Notes: Hidden Mystery. The Christmas update introduces some new inhabitants to players, and with them a chance to win plenty of... | Read more »
Bully: Anniversary Edition (Games)
Bully: Anniversary Edition 1.03.1 Device: iOS Universal Category: Games Price: $6.99, Version: 1.03.1 (iTunes) Description: *** PLEASE NOTE: This game is officially supported on the following devices: iPhone 5 and newer, iPod Touch... | Read more »
PINE GROVE (Games)
PINE GROVE 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: A pine grove where there are no footsteps of people due to continuous missing cases. The case is still unsolved and nothing has... | Read more »
Niantic teases new Pokémon announcement...
After rumors started swirling yesterday, it turns out there is an official Pokémon GO update on its way. We’ll find out what’s in store for us and our growing Pokémon collections tomorrow during the Starbucks event, but Niantic will be revealing... | Read more »
3 reasons why Nicki Minaj: The Empire is...
Nicki Minaj is as business-savvy as she is musically talented and she’s proved that by launching her own game. Designed by Glu, purveyors of other fine celebrity games like cult favorite Kim Kardashian: Hollywood, Nicki Minaj: The Empire launched... | Read more »
Clash of Clans is getting its own animat...
Riding on its unending wave of fame and success, Clash of Clans is getting an animated web series based on its Clash-A-Rama animated shorts.As opposed to the current shorts' 60 second run time, the new and improved Clash-A-Rama will be comprised of... | Read more »
Leaks hint at Pokémon GO and Starbucks C...
Leaked images from a hub for Starbucks employees suggests that a big Pokémon GO event with the coffee giant could begin this very week. The images appeared on Reddit and hint at some exciting new things to come for Niantic's smash hit game. | Read more »

Price Scanner via MacPrices.net

Never Settle for Low Performing Wifi With iOS...
AppYogi Software has announced the release of WiFi Signal Strength Status App 1.0, the company’s new utility developed exclusively for macOS. WiFi Signal Strength Status App features a unique, single... Read more
New 2016 13-inch Touch Bar MacBook Pros in st...
B&H Photo has stock of new 2016 Apple 13″ Touch Bar MacBook Pro models, each including free shipping plus NY sales tax only: - 13″ 2.9GHz/512GB Touch Bar MacBook Pro Space Gray: $1999 - 13″ 2.... Read more
New 2016 15″ Touch Bar MacBook Pros in stock...
B&H Photo has new 2016 Apple 15″ Touch Bar MacBook Pro models in stock today including free shipping plus NY sales tax only: - 15″ 2.7GHz Touch Bar MacBook Pro Space Gray: $2799 - 15″ 2.7GHz... Read more
DietSensor App Targeting Diabetes and Obesity...
DietSensor, Inc., a developer of smart food and nutrition applications designed to fight diabetes and obesity and help improve overall fitness, has announced the launch of its DietSensor app for... Read more
Holiday 2016 13-inch 2.0GHz MacBook Pro sales...
B&H has the non-Touch Bar 13″ MacBook Pros in stock today for $50-$100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.0GHz MacBook Pro Space Gray (MLL42LL/A): $1449 $... Read more
Holiday sale: Apple TVs for $51-$40 off MSRP,...
Best Buy has dropped their price on the 64GB Apple TV to $159.99 including free shipping. That’s $40 off MSRP. 32GB Apple TVs are on sale right now for $98 on Sams Club’s online store. That’s $51 off... Read more
12-inch Retina MacBooks, Apple refurbished, n...
Apple has restocked a full line of Certified Refurbished 2016 12″ Retina MacBooks, now available for $200-$260 off MSRP. Refurbished 2015 models are available starting at $929. Apple will include a... Read more
Holiday sale: 12-inch Retina MacBook for $100...
B&H has 12″ Retina MacBooks on sale for $100 off MSRP as part of their Holiday sale. Shipping is free, and B&H charges NY sales tax only: - 12″ 1.1GHz Space Gray Retina MacBook: $1199 $100... Read more
Apple refurbished 13-inch MacBook Airs availa...
Apple has Certified Refurbished 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: - 13″ 1.6GHz/8GB/128GB MacBook Air: $849 $... Read more
Apple refurbished iMacs available for up to $...
Apple has Certified Refurbished 2015 21″ & 27″ iMacs available for up to $350 off MSRP. Apple’s one-year warranty is standard, and shipping is free. The following models are available: - 21″ 3.... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, 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
Automotive Detailer - *Apple* Used Autos -...
We are currently conductinginterviews and will be accepting applications for a part-time detailer. Apple Used Autos is a great place to work andstart a career. We Read more
*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
*Apple* Retail - Multiple Positions- Trumbul...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.