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

Latest Forum Discussions

See All

Aether Gazer unveils Chapter 16 of its m...
After a bit of maintenance, Aether Gazer has released Chapter 16 of its main storyline, titled Night Parade of the Beasts. This big update brings a new character, a special outfit, some special limited-time events, and, of course, an engaging... | Read more »
Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »

Price Scanner via MacPrices.net

New today at Apple: Series 9 Watches availabl...
Apple is now offering Certified Refurbished Apple Watch Series 9 models on their online store for up to $80 off MSRP, starting at $339. Each Watch includes Apple’s standard one-year warranty, a new... Read more
The latest Apple iPhone deals from wireless c...
We’ve updated our iPhone Price Tracker with the latest carrier deals on Apple’s iPhone 15 family of smartphones as well as previous models including the iPhone 14, 13, 12, 11, and SE. Use our price... Read more
Boost Mobile will sell you an iPhone 11 for $...
Boost Mobile, an MVNO using AT&T and T-Mobile’s networks, is offering an iPhone 11 for $149.99 when purchased with their $40 Unlimited service plan (12GB of premium data). No trade-in is required... Read more
Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more
Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more

Jobs Board

DMR Technician - *Apple* /iOS Systems - Haml...
…relevant point-of-need technology self-help aids are available as appropriate. ** Apple Systems Administration** **:** Develops solutions for supporting, deploying, Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
IT Systems Engineer ( *Apple* Platforms) - S...
IT Systems Engineer ( Apple Platforms) at SpaceX Hawthorne, CA SpaceX was founded under the belief that a future where humanity is out exploring the stars is Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.