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

Luminar 1.2.1 - Powerful, adaptive, conf...
Luminar is the new full-featured image editor that adapts to the way you edit photos. Over 300 essential tools to fix, edit, and enhance your photos with comfort. The future of photo editing is here... Read more
1Password 6.8.1 - Powerful password mana...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
EtreCheck 3.4.4 - For troubleshooting yo...
EtreCheck is an app that displays the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support Communities to... Read more
GarageSale 7.0.8 - Create outstanding eB...
GarageSale is a slick, full-featured client application for the eBay online auction system. Create and manage your auctions with ease. With GarageSale, you can create, edit, track, and manage... Read more
Backblaze 5.0.0.116 - 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
Parallels Desktop 13.0.0 - Run Windows a...
Parallels allows you to run Windows and Mac applications side by side. Choose your view to make Windows invisible while still using its applications, or keep the familiar Windows background and... Read more
Mellel 4.0.0 - The word processor for sc...
Mellel is the leading word processor for OS X and has been widely considered the industry standard for long form documents since its inception. Mellel focuses on writers and scholars for technical... Read more
Adobe Muse CC 2017 2017.1.0 - Design and...
Muse CC 2017 is available as part of Adobe Creative Cloud for as little as $14.99/month (or $9.99/month if you're a previous Muse customer). Adobe Muse 2017 enables designers to create websites as... Read more
WhatsApp 0.2.5862 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
WhatsApp 0.2.5862 - Desktop client for W...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more

The best deals on the App Store this wee...
The summer is drawing quickly to a close, but luckily there's a game for every season. It's an excellent week for some bargain shopping if that's what you're after. There are some big names and indie darlings in this week's roundup. It's a great... | Read more »
KORG iMono/Poly (Music)
KORG iMono/Poly 1.0.0 Device: iOS Universal Category: Music Price: $19.99, Version: 1.0.0 (iTunes) Description: *** Special Sale for a limited time to celebrate the debut of KORG iMono/Poly (33% OFF) until Sep 30! *** Reviving a... | Read more »
Super Phantom Cat 2 beginner's guid...
Super Phantom Cat 2 presents a whole new world of fun platforming challenges and perplexing puzzles. It's a well-designed platformer with a bright, neon aesthetic that brings the genre up to date. [Read more] | Read more »
Shadow Fight 2 Special Edition (Games)
Shadow Fight 2 Special Edition 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: ** New story chapter! **** No Ads! **** No energy! ** The best fighting series on mobile has returned and... | Read more »
4 RPGs like Final Fantasy XV that deserv...
Square Enix announced another Final Fantasy XV spin-off today - Final Fantasy XV Pocket Edition. This mobile, episodic version of the hit RPG gives the game a chibi-fied makeover. The first episode will be free, followed by 9 more premium episodes... | Read more »
Guild sieges and soul gems in latest upd...
Webzen’s MU Origin hit app stores last year, giving fans of fantasy hack-n-slash MMOs like Diablo a new fix to fixate on. This latest update introduces a competitive guild battle, a fresh dungeon challenge, a mini-game and some elemental gems to... | Read more »
Little Red Lie (Games)
Little Red Lie 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: ARE YOU MORE AFRAID OF POVERTY THAN DEATH? Little Red Lie is a narrative-focused, interactive fiction experience that reduces... | Read more »
You can now apply to be Clash of Clans...
Earlier this month, word got out that the Builder, the trusty handiman who tirelessly built every single building inevery singleClash of Clansbase had called it quits. Sick of seeing his work destroyed endless, the Builder has set out for our world... | Read more »
Meshi Quest beginner's guide - how...
Meshi Quest is Square Enix's newest free-to-play release, and it's a real charmer. You start off as the head of a sushi restaurant, upgrading your food and equipment as you serve visitors heaping helpings of your delicious meals. As you progress,... | Read more »
BUST-A-MOVE JOURNEY (Games)
BUST-A-MOVE JOURNEY 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: BUST-A-MOVE Features:- Shoot bubbles and match 3 or more bubbles of the same color to make them pop!- Complete your... | Read more »

Price Scanner via MacPrices.net

Low Cost Subscription Graphics App Alternativ...
I’m not a fan of the subscription software model, I don’t use any subscription apps. Used to be that you paid your license fee and the app was yours to use indefinitely, or until one opted for a paid... Read more
Clearance 2016 13-inch MacBook Airs, Apple re...
Apple has Certified Refurbished 2016 13″ MacBook Airs available starting at $809. An Apple one-year warranty is included with each MacBook, and shipping is free: – 13″ 1.6GHz/8GB/128GB MacBook Air: $... Read more
2017 13-inch MacBook Airs on sale for $100 of...
B&H Photo new 2017 13″ MacBook Airs on sale today for $100 off MSRP, starting at $899: – 13″ 1.8GHz/128GB MacBook Air (MQD32LL/A): $899, $100 off MSRP – 13″ 1.8GHz/256GB MacBook Air (MQD42LL/A... Read more
Sale! 13-inch 2.3GHz MacBook Pros for $100 of...
B&H Photo has 13″ 2.3GHz MacBook Pros in stock today and on sale for $100 off MSRP including free shipping plus NY & NJ sales tax only: – 13-inch 2.3GHz/128GB Space Gray MacBook Pro (MPXQ2LL... Read more
2016 MacBook Pros, Apple refurbished, availab...
Apple has Certified Refurbished 2016 15″ and 13″ MacBook Pros available starting at $1189. An Apple one-year warranty is included with each model, and shipping is free: – 15″ 2.7GHz Touch Bar Space... Read more
Apple offers Certified Refurbished iPhone 6s...
Apple has Certified Refurbished unlocked iPhone 6s’s and 6s Plus’s available starting at $449. An Apple one-year warranty is included with each phone, and shipping is free: – 16GB iPhone 6s: $449, $... Read more
Apple offers Certified Refurbished Pencils fo...
Apple has Certified Refurbished Apple Pencils available for $85 including free shipping. Their price is $14 off MSRP, and it’s the lowest price available for a Pencil. Read more
2016 15-inch 2.6GHz Touch Bar MacBook Pro ava...
B&H Photo has clearance 2016 15″ 2.6GHz MacBook Pros in stock today and on sale for $500 off original MSRP. Shipping is free, and B&H charges NY & NJ sales tax only: – 15″ 2.6GHz Touch... Read more
21-inch 2.3GHz iMac on sale for $999, save $1...
Amazon has the new 2017 21″ 2.3GHz iMac (MMQA2LL/A) in stock and on sale for $999.99 including free shipping. Their price is $100 off MSRP, and it’s the lowest price available for this model. Read more
Free Instant Translator 2.0 App For iOS Relea...
Mobile application development company, Neoappz has announced the release and immediate availability of Instant Translator 2.0 for iOS devices. Instant Translator is a user-friendly application which... 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
Development Operations and Site Reliability E...
Development Operations and Site Reliability Engineer, Apple Payment Gateway Job Number: 57572631 Santa Clara Valley, California, United States Posted: Jul. 27, 2017 Read more
Frameworks Engineering Manager, *Apple* Wat...
Frameworks Engineering Manager, Apple Watch Job Number: 41632321 Santa Clara Valley, California, United States Posted: Jun. 15, 2017 Weekly Hours: 40.00 Job Summary Read more
Development Operations and Site Reliability E...
Development Operations and Site Reliability Engineer, Apple Payment Gateway Job Number: 57572631 Santa Clara Valley, California, United States Posted: Jul. 27, 2017 Read more
Frameworks Engineering Manager, *Apple* Wat...
Frameworks Engineering Manager, Apple Watch Job Number: 41632321 Santa Clara Valley, California, United States Posted: Jun. 15, 2017 Weekly Hours: 40.00 Job Summary Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.