

|
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.

- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine