

|
Volume Number: | 2 | |
Issue Number: | 8 | |
Column Tag: | Pascal Procedures |
Menu Definition Routines
By Darryl Lovato, TML Systems, Inc.
Introduction
Hello, as promised, we will cover Menu Definition Routines this month. What is a Menu Definition Routine? It is a procedure that defines a particular type of menu. The routine controls the way menus are drawn, and how items within it are selected. In general, Menu Definition routines define a given menu's appearance and behavior. The standard type of Macintosh menu is pre-defined for you and it is stored in the system file. You may however, want to define your own type of menu. The pattern selection menu in MacDraw is a good example of a non-standard menu.
The menu definition procedure I decided to use for an example doesn't create a non-standard menu but it shows exactly how the standard menu definition works! The standard definition routine was written in 68000 Assembler, by Andy Hertzsfield (my Hero) but ours will be written in TML Pascal (with the exception of one routine, which couldn't be written with Pascal, so I wrote it in 68000 Assembler). There isn't any noticeable speed difference between the standard Menu Def and our imitation.
An example of a non-standard Menu
The Pascal definition and general outline of a Menu definition procedure follows:
{1} procedure MyMenu(message : integer; theMenu : MenuHandle; var menuRect : Rect; hitPt : Point; var whichItem : Integer); procedure DoDrawMessage(aMenu : MenuHandle; aRect : Rect); begin ...{draw the menu in menurect} end; procedure DoChooseMessage(aMenu : MenuHandle; aRect : Rect; aPt : Point; var aItem : Integer); begin ...{see what item aPt is in and select it} end; procedure DoSizeMessage(aMenu : MenuHandle); begin ...{calculate the width and height of aMenu} end; begin case message of mDrawMsg : DoDrawMessage(theMenu, menuRect); mChooseMsg : DoCooseMessage(theMenu, menuRect, hitPt, whichItem); mSizeMsg : DoSizeMessage(theMenu); end; end;
The Parameters
The message parameter tells type of operation to perform. It can be one of the following values:
mDrawMsg - Draw the menu.
mChooseMsg - Choose a item.
mSizeMsg - Calculate the menu's height & width.
The 'theMenu' parameter is a MenuHandle to the menu we are to perform the action on.
The 'menuRect' is the rectangle in which the menu is in. It is in global coordinates.
The 'hitPt' parameter is the point (again, in global coordinates) that the mouse was pressed. It is only valid during a 'mChooseMsg'.
The 'whichItem' parameter holds the item number that is currently selected. You pass the result of your hit test back to the menu manager in this parameter.
The DoSizeMessage Procedure
When we are passed the mSizeMsg message, we need to calculate the width and height of the menu passed in theMenu parameter. The results are returned in the menu records Width and Height fields respectively. For a menu which has only one size, MacDraw's pattern selection menu, for example, this is real easy- just assign the width and height fields their constant values. For your example, which has variable widths and heights, it is a little harder.
The way we do this is have a variable that holds the cumulative vertical distance and another which holds the greatest horizontal distance. We loop through each item, first getting all its attributes (icon, text, cmd key...). Then we add the current items height to the cumulative height variable (36 if it has a icon, 16 otherwise). We must also find the items width; this is done by checking for icons, cmd keys, the items string width, etc. If this items width is greater than all others we have encountered so far, then we update the greatest width variable. When we have done this for all items in the menu, we return the results in the menu record fields Width and Height.
The DoDrawMessage Procedure
The DoDrawMessage is called when we receive a mDrawMsg from the menu manager. In response to this, we draw the menu items in theRect. When we are called, the current port will be the WindowMgrPort and the boarder around the menu will already be drawn. Again, we just go through a loop, looking at every item. If the item has an icon, we draw it and bump the location of where the text will go down half the size of the icon. We draw the markChar, if any. Then we draw the items text, and finally, the items keyboard equivalent, if any.
The DoChooseMessage Procedure
When we receive a mChooseMsg we call the DoChooseMessage and pass it the following parameters. The 'aPt' parameter is the mouse location that we need to check for. The 'whichItem' parameter is the item number of the last item that was choosen from this menu (initially 0). First we check to see if the point is in the menus rect, if not, we return 0. Otherwise, we see if the point is in an enabled item, if not, we return 0. If it was in an enabled item, we need to unhilite the old item (unless they are the same), hilite the new item (again, unless they are the same), and return the item it was in.
Getting the Menu Manager to Use our Def
Now that we have our definition routine defined, how do we get the Menu Manager to use our definition routine instead of the standard one (even thought they act the same)? One method of doing this follows:
1. Get the handle to the menu by calling NewMenu or GetNewMenu.
2. Put a new handle in the menuProc field of the menu record.
3. Make that point to the address of our MenuDefRoutine.
myMenuHdl := GetNewMenu(...); myMenuHdl^^.menuProc := NewHandle(0); myMenuHdl^^.menuProc^ := Ptr(@TheMenuProc);
Other Points of Interest
The assembly routine we use in this example simply returns the keyboard equivalent of a menu item. Since there is no standard trap to do this, and it can't be done with Pascal, no- wait a minuite...it can be done with Pascal. But it is a lot easier with assembly, so there! Anyway, it simply walks through the menu record until it finds 'theItem', and it gets its keyboard equivalent and returns it. It's Pascal declaration is :
procedure GetItemKey(theMenu : MenuHandle; theItem : Integer; var theChar : Char); external;
This is only one type of menu def, the number of possible menu definition routines that can be made are as big as your imagination, and, of course, available memory. For example, in addition to the MacDraw type MDEF I have done for the TML Examples Disks, I have done a Hierarchical Menu Definition routine. The way it works is this: to define a sub-menu in a menu, you simply add the string '|XXX' to the item you want to have the sub-menu. The XXX is the resource id of the menu you want to be the sub menu of the item. So, when I am supposed to draw the menu or calculate the menu's size, I strip out the '|XXX' string. Where it gets fancy is when you get the choose message, and you move onto an item that has a submenu ('|XXX' in its string). When this happens, I call a Pascal version of Mike Shuster's PopUpSelect routine [published in MacTutor December 1985 issue and available as a back issue for $4] for the menu with the res ID XXX. If the same conditions occur in the PopUpSelect routine, which calls the Definition Routine, I call it again. Can you say RECURSION ? I knew you could.
A good use for this type of Menu Def is a Character Menu, its items are Font, Size, and Style. When you move the mouse over one of these items, a list of fonts, a list of available sizes, or a list of styles appear in the sub menu. The advantage of this is that it only took one place in the menu bar to implement 3 separate menus.
Next Month...
Next month we will cover the wonderful world of Window Definition Routines. We will go through an example that shows how the standard window definition routine works. I would suggest reading the Window Manager section of Inside Macintosh, especially the section on "Defining Your Own Windows". If you have any questions/suggestions or other form of abuse, drop me a line in care of MacTutor. Or better yet, buy the TML Examples Disks, there is 3 MDEF Examples in them. Its been real...
Pascal Source Code {################################################ # #RegMDEF.Pas # # (Regular Menu Definition Routine Example) # ( just like apples, except in Pascal ) # #This program was written by Darryl Lovato. #Copyright (c) 1986 by TML Systems. # #################################################} program RegMDEF; {------------------------- Compiler Directives ---------------------------} {$B+ } { Tell the linker to set the bundle bit} {$T APPL RMDF } { type to Appl and creator to RMDF} {$I MemTypes.ipas} { Include the Memory Declarations} {$I QuickDraw.ipas}{ Include QuickDraw Declarations} {$I OSIntf.ipas} { Include the OS Declarations} {$I ToolIntf.ipas} { Include the Toolbox Declarations} {$L RegMDEFRsrc} { Tell the linker to link the resources} {$U RegMDEFGlue} { link our assembly code in too...} {--------------------------- Global Constants ----------------------------} const appleMenu = 300; { Resource ID of the Apple menu} fileMenu = 301;{ Resource ID of the File menu} editMenu = 302;{ Resource ID of the Edit menu} beginMenu = 300; { Res ID of first menu in menu bar} endMenu = 302; { Res ID of last menu in menu bar} RegMenu = 500; { Res ID of our regular menu} {--------------------------- Global Variables ------------------------------} var { The menus in menu bar} myMenus : array[beginMenu..endMenu] of MenuHandle; Finished : Boolean; { Set to true when were done} screenPort : GrafPtr; { the window mngr port} MyRegMenu : MenuHandle; { my regular menu handle} {--------------------- Assembly Procedures ----------------------------} procedure GetItemKey(theMenu : MenuHandle; theItem : Integer; var theChar : Char); external; {------------------- ChkOnOffItem procedure --------------------------} procedure ChkOnOffItem(MenuHdl:MenuHandle; item, first, last:Integer); var i: integer; begin for i := first to last do begin if item = i then CheckItem(MenuHdl, i, true) {check it on in menu} else CheckItem(MenuHdl, i, false); {check it off in menu} end; end; {----------------------- MenuDef Procedure -----------------------------} procedure MyMenuDef(message : Integer; { what do we do?} theMenu : MenuHandle; { what menu ?} var menuRect : Rect;{ in what rect?} hitPt : Point; { where's mouse?} var whichItem : Integer);{ what item is it?} {--------------------- semi-global constants ----------------------------} const MBarHeight = 20; {----------------------- DimRect procedure ------------------------------} procedure DimRect(theRect : Rect); begin PenPat(gray); PenMode(patBic); PaintRect(theRect); PenNormal; end; {--------------------- GetItemsRect Function ---------------------------} function GetItemsRect(myMenu : MenuHandle; myRect : Rect; theItem : Integer) : Rect; var Index : Integer; currentRect : Rect; itemIcon : Byte; begin currentRect.bottom := myRect.top; { initialize current rect} currentRect.left := myRect.left; currentRect.right := myRect.right; for index := 1 to theItem do begin GetItemIcon(myMenu,index,itemIcon); currentRect.top := currentRect.bottom; { update rect } if itemIcon <> 0 then currentRect.bottom := currentRect.top + 36 else currentRect.bottom := currentRect.top + 16; end; GetItemsRect := currentRect; { return result} end; {--------------- DoDrawMessage Procedure -----------------------} procedure DoDrawMessage(myMenu : MenuHandle; myRect : Rect); const MBarHeight = 20; var currentItem : Integer; currentRect : Rect; itemString : str255; itemIcon : Byte; itemMark : Char; itemStyle : Style; itemKey : Char; thePoint : Point; theIcon : Handle; iconRect : Rect; NewVert : Integer; begin currentRect.bottom := myRect.top; { initialize current rect} currentRect.left := myRect.left; currentRect.right := myRect.right; for currentItem := 1 to CountMItems(myMenu) do{ all items} begin GetItem(myMenu,currentItem,itemString);{ get info } GetItemIcon(myMenu,currentItem,itemIcon); GetItemMark(myMenu,currentItem,itemMark); GetItemStyle(myMenu,currentItem,itemStyle); GetItemKey(myMenu,currentItem,itemKey); currentRect.top := currentRect.bottom; { update rect } if itemIcon <> 0 then currentRect.bottom := currentRect.top + 36 else currentRect.bottom := currentRect.top + 16; if itemString = '-' then { special case '-' item} begin PenPat(Gray); moveTo(currentRect.left,currentRect.top + 8); Line(currentRect.right,0); PenPat(Black); end else { draw the other item stuff} begin {get baseline} NewVert := ((currentRect.bottom - currentRect.top) DIV 2); NewVert := currentRect.top + 4 + NewVert; MoveTo(currentRect.left + 2,newVert); if itemMark <> Chr(0) then DrawChar(itemMark); if itemIcon <> 0 then { draw the icon} begin iconRect.top := currentRect.top + 2; iconRect.bottom := iconRect.top + 32; iconRect.left := currentRect.left + 13; iconRect.right := iconRect.left + 32; theIcon := GetIcon(256 + itemIcon); PlotIcon(iconRect,theIcon); GetPen(thePoint); MoveTo(currentRect.left + 47,thePoint.v); end else { otherwise, just move over a bit} begin GetPen(thePoint); MoveTo(currentRect.left + 13,thePoint.v); end; TextFace(itemStyle); DrawString(itemString); TextFace([]); if itemKey <> Chr(0) then { draw key equiv} begin GetPen(thePoint); MoveTo(currentRect.right - 24,thePoint.v); DrawChar(Chr($11)); { draw cmd char symbol} DrawChar(itemKey); { and the cmd key} end; if (BitAnd(myMenu^^.enableFlags,1) = 0) then{disabled!} DimRect(currentRect); if (BitAnd(BitShift( myMenu^^.enableFlags,-currentItem),1) = 0) then DimRect(currentRect); end; { of if itemString = '-' then..else..} end; end; { of DoDrawMessage} {------------- DoChooseMessage Procedure ---------------------} function DoChooseMessage(myMenu : MenuHandle; myRect : Rect; myPoint : Point; oldItem : Integer) : Integer; var theItem : Integer; ItemsRect : Rect; begin if PtInRect(myPoint,myRect) then begin theItem := 1; repeat ItemsRect := GetItemsRect(myMenu, myRect, theItem); theItem := theItem + 1; until PtInRect(myPoint,itemsRect); theItem := theItem - 1; { undo last increment} if (BitAnd(myMenu^^.enableFlags,1) = 0) or (BitAnd(BitShift( myMenu^^.enableFlags,-theItem),1) = 0) then begin theItem := 0; end; if theItem <> oldItem then {de-select old, select new} begin if oldItem <> 0 then { deselect old} InvertRect(GetItemsRect( myMenu, myRect,oldItem)); if theItem <> 0 then InvertRect(GetItemsRect(myMenu, myRect,theItem)); end; DoChooseMessage := theItem; { return result} end else { it was not in our menu} begin if oldItem <> 0 then { we need to de-select old item} InvertRect(GetItemsRect(myMenu, myRect,oldItem)); DoChooseMessage := 0; { return result} end; end; {----------------- DoSizeMessage Procedure -----------------------} procedure DoSizeMessage(var myMenu : MenuHandle); var MaxWidth : integer; { keep track of the maximum width} TotalHeight : integer;{ keep track of the total height} currentItem : integer;{ the menu item we are looking at} itemString : Str255;{ text of the curren menu item} itemIcon : Byte; { resource id of the menu items icon} itemMark : char; { the items mark} itemStyle : Style; { the items character style} itemKey : Char;{ the keyboard equiv} tempWidth : Integer;{ the current items width} begin MaxWidth := 0; { initailize width} TotalHeight := 0; { initialize height} for currentItem := 1 to CountMItems(myMenu) do begin GetItem(myMenu,currentItem,itemString); { get text} GetItemIcon(myMenu,currentItem,itemIcon); {get icon} GetItemMark(myMenu,currentItem,itemMark); { char} GetItemStyle(myMenu,currentItem,itemStyle); { style} GetItemKey(myMenu,currentItem,itemKey); { get key} tempWidth := 13; { indent a bit} if itemIcon <> 0 then tempWidth := tempWidth + 35; { make room for icon} TextFace(itemStyle); { set to items style} tempWidth := tempWidth + StringWidth(itemString) + 4; TextFace([]); {return to normal} if itemKey <> Chr(0) then tempWidth := tempWidth + 30; if tempWidth > MaxWidth then MaxWidth := tempWidth; if itemKey <> chr(0) then tempWidth := tempWidth + 20; if itemIcon <> 0 then TotalHeight := totalHeight + 36 { add lots of space} else TotalHeight := totalHeight + 16; { add enough for text} end; with myMenu^^ do begin menuWidth := MaxWidth; { save result in menu record} menuHeight := TotalHeight; { ditto...} end; end; {----------- Case on message and call procedure ------------------} begin case message of mSizeMsg : begin DoSizeMessage(theMenu); end; mDrawMsg : begin DoDrawMessage(theMenu,menuRect); end; mChooseMsg : begin whichItem := DoChooseMessage( theMenu,menuRect,hitPt,whichItem); end; end; end; {---------------- process the menu selection ------------------------} procedure ProcessMenu(CodeWord : LongInt); var menuNum : Integer; { Res ID of the menu Selected} itemNum : Integer;{ The item number selected} nameHolder : str255; { the name of the desk acc.} dummy : Integer; { just a dummy} AboutRecord : DialogRecord;{ the actual object} AboutDlog : DialogPtr; { a pointer to my dialog} begin menuNum := HiWord(CodeWord); { get the menu number} itemNum := LoWord(CodeWord); { get the item number} if itemNum > 0 then { ok to handle the menu?} begin case MenuNum of appleMenu : begin case ItemNum of 1: begin AboutDlog := GetNewDialog( 3000,@AboutRecord,Pointer(-1)); ModalDialog(nil,dummy); CloseDialog(AboutDlog); end; 2:begin end; otherwise begin GetItem(myMenus[appleMenu], ItemNum,NameHolder); dummy := OpenDeskAcc(NameHolder); end; end; end; fileMenu : begin Finished := true; end; editMenu : begin if not SystemEdit(ItemNum - 1) then begin {we dont support any other editing} end; end; RegMenu : begin if ItemNum <> 0 then begin if itemNum > 3 then ChkOnOffItem(MyRegMenu, ItemNum, 4, 9); end; end; end; { of case menuNum of} end; { of if CodeWord...} HiliteMenu(0); end;{ of process menu} {-------------------------- Main Event loop -------------------------------} procedure MainEventLoop; type trickType = packed record{ to get around pascal's typing } case boolean of true : (I : LongInt); false : (chr3, chr2, chr1, chr0 : Char); end; var Event : EventRecord;{ Filled by Get next event} windowLoc : integer;{ the mouse location} mouseLoc : point; { the area it was in } theWindow : WindowPtr;{ Dummy, have no windows} trickVar : trickType; { because of pascal's typing} CharCode : Char; { for command keys} begin repeat{ do this until we selected quit} SystemTask;{ Take care of desk accessories} if GetNextEvent(everyEvent,Event) then begin case event.what of { case out on the event type} mouseDown : { we had a mouse-down event } begin mouseLoc := Event.where;{ wheres the mouse} windowLoc := FindWindow(mouseLoc, theWindow); case windowLoc of { now case on the location} inMenuBar : ProcessMenu(MenuSelect(MouseLoc)); inSysWindow: SystemClick(Event,theWindow); {In desk acc} end; end; keyDown,AutoKey : { we had the user hit a key} begin trickVar.I := Event.Message; { fill the longWord } CharCode := trickVar.chr0;{ and pull off low-byte} if BitAnd(Event.modifiers,CmdKey) = CmdKey then ProcessMenu(MenuKey(CharCode)); end; end; { of case event.what...} end; { end of if Get Next event} until(Finished); { end of repeat statement} end;{ of main event loop} {----------------- SetUp Everything -------------------------------} procedure SetUpThings; type ProcHdl = ^ProcPtr; var index : integer; { used in a for loop } begin for index := beginMenu to endMenu do begin myMenus[index] := GetMenu(index);{ Get next menu} end; AddResMenu(myMenus[appleMenu],'DRVR'); for index := beginMenu to endMenu do InsertMenu(myMenus[index],0);{ Insert the menu } { #*#*#*#*#* here is the non-standard menu*#*#*#*#*#*#*# } MyRegMenu := GetMenu(500); { make a new Menu} MyRegMenu^^.menuProc := NewHandle(0); MyRegMenu^^.menuProc^ := Ptr(@MyMenuDef); Insertmenu(MyRegMenu,0);{ and add it to the menu list} CalcMenuSize(MyRegMenu);{ and calculate its size} DrawMenuBar; { Now draw the menu bar } ChkOnOffItem(MyRegMenu, 4, 4, 9);{check item in menu} end; {-------------------- Initialize Everything ----------------------------} procedure InitThings; begin InitGraf(@thePort); { create grafPort for screen} MoreMasters; { create bunch of master Ptr's} MoreMasters; { wont need to worry about} MoreMasters; { heap fragmentation later!} MaxApplZone; { make sure we have lots!} InitFonts; { Startup the Font manager} InitWindows; { Startup the Window manager} InitMenus; { Startup the Menu manager} TEInit; { initialize text edit} InitDialogs(nil); { initialize dialogs } InitCursor;{ make the cursor an arrow} end; {--------------------- Main Program Seg ------------------------------} begin InitThings; SetUpThings; Finished := false; MainEventLoop; end.
Assembly Source Code ;############################################### ;# ;# procedure GetItemKey(theMenu : MenuHandle; ;# theItem : Integer; ;# var theChar : Char); ;# ;#------------------------------------------------------------------------------ ;# ;# This assembly proc returns the current command key ;# which is assigned to theItem. ;# ;################################################ ;--------------------------- Equates ----------------------------------- ; menuinfo data structure menuID equ 0 ; unique id for menu bar [word] menuWidth equ 2 ; menu Width [word] menuHeightequ 4 ; menu Height [word] menuDefHandle equ 6 ; menu definition proc [handle] menuEnableequ $A; enable flgs, one bit/item [long] menuDataequ $E ; menu item string [string] menuBlkSize equ $E; menu block plus dataString ; menu string data structure itemIconequ 0 ; icon byte itemCmd equ 1 ; command key byte itemMarkequ 2 ; checkmark character byte itemStyle equ 3 ; style byte .Trap _CountMItems $A950 ; trap word ;--------------------------- XREF's ------------------------------------ xdef GetItemKey ; so the linker will find us ;------------------------ Entrance ----------------------------------- GetItemKey link A6,#0 ; create new stack frame movem.l A0-A4/D0-D3,-(SP) ; save registers ;---------------------- Pop Parameters ------------------------------- clr.l D2; make sure it is empty clr.l D1; ditto... clr.l D0; ditto... clr.w -(SP) ; make room for result move.l 14(A6),-(SP); push MenuHandle _CountMItems ; how many are in this menu? move.w (SP)+,D3 ; pop result move.l 14(A6),A4; fetch the menu handle move.w 12(A6),D1; fetch the item number movea.l8(A6),A3 ; fetch ptr to the char Ptr(word) cmp.w D1,D3 ; num of Menu Items < theItem? blt BadItem ; yep, exit ;-------------------- Find the Cmd Key -------------------------------- move.l (A4),A4 ; get menu ptr lea menuData(A4),A4; now A4 points to menu title move.b (A4),D2 ; get length of menu title add.b #1,D2 ; skip length byte ; (A4,D2) now points to the first menu items title (length byte) nextItem sub.b #1,D1 ; decrement count beq GotItem ; if 0, return its cmd char move.b (A4,D2),D0 ; get length of the title add D0,D2 ; and skip it add #5,D2 ; skipe item attrib& length byte bra nextItem ; and look at the next item ;------------------------ Got the Item ---------------------------------- GotItem move.b (A4,D2),D0 ; get length of title add D0,D2 ; and skip it add #1,D2 ; skip length byte, too... clr.w D0; make sure its empty move.b itemCmd(A4,D2),D0 ; get the key equivalent move.w D0,(A3) ; and return it bra Exit; and exit ;------------------- No Item Found --------------------------------- BadItem clr.w (A3); return no cmd char and exit ;-------------------- Clean Up & Exit -------------------------------- Exit movem.l(SP)+,A0-A4/D0-D3 ; restore registers unlk A6; restore stack frame movea.l(SP)+,A0 ; save return address adda.l #10,SP ; clean up stack jmp (A0); ... and return
Link File For TML Pascal !PAS$Xfer /Globals -4 RegMDEF Pascal System:PAS$Library OSTraps ToolTraps Pascal System:RegMDEFGlue /Resources Pascal System:RegMDEFRsrc /Bundle /Type 'APPL' 'RMDF' $
Resource File For RMaker * * Resource listing: "RegMDEFRsrc". * RegMDEFRsrc TYPE RMDF=STR ,0 Window Definition Procedure TYPE BNDL ,128 RMDF 0 ICN# 0 128 1 129 FREF 0 128 Type DLOG ,3000 (4) New Dialog 56 74 312 444 Visible GoAway 1 0 3000 Type DITL ,3000 8 * 1 BtnItem Enabled 200 239 237 324 Okay * 2 StatText Disabled 16 83 34 284 MacLanguage Series™ Pascal * 3 StatText Disabled 34 69 50 293 Advanced Programming Examples * 4 StatText Disabled 61 52 77 307 - Standard Menu Definition Example - * 5 StatText Disabled 90 6 139 368 Written by Darryl Lovato.++ \0DSpecial thanks to Robert Ulrich.++ \0DCopyright © 1986 by TML Systems. ++ All rights reserved. * 6 StatText Disabled 147 7 180 364 Complete source code for this example ++ (and others) is available from: * 7 StatText Disabled 183 60 250 198 TML Systems, Inc. ++ \0DP.O. Box 361626 ++ \0DMelbourne, Fl 32936\0D(305) 242-1873 * 8 IconItem Enabled 14 21 46 53 3000 Type MENU ,500 (4) Junk Icon Item^1 (Disabled Item (- keyboard Equiv Item/K Shadowed Item<S Outlined Item<O Underlined Item<U Italic Item<I Bold Item<B ,300 (4) \14 About Regular MDEF... (- ,301 (4) File Quit/Q ,302 (4) Edit Undo/Z (- Cut/X Copy/C Paste/V Clear Type ICON = GNRL ,257 (4) FF7FFFFF 816AAAAB E7555555 247EAAAB 24015555 247CAAAB 24827F55 250101AB 25391C55 2527633F E7208080 812088E7 FF271465 01392255 F9011455 E482888D E47C80A5 9A0063A1 99FE1D31 E6678129 E66679E7 99999C00 99999BFF E6666667 E6666667 99999999 99999999 E6666667 E6666667 99999999 99999999 FFFFFFFF ,3000 (36) 3FFFFFFC 40000002 81C1FFC1 82210041 FE3F007F 92290049 91C9FFC9 90080009 90081C09 90C82209 9127E3F9 9F302201 91281C01 90C80001 90081F01 90086081 93C18041 94223020 9C2FC810 94247F0F 93C23007 90010007 90008007 90006007 93C01FE7 9420001F 9C3FE007 84200000 83C00000 80000001 40000002 3FFFFFFC Type ICN# = GNRL ,128 2 00000000 7FFFFFFE 47E00002 47E00002 7FFFFFFE 54001556 6DC01AAA 55DF9556 6DC01AAA 54001556 6DC01AAA 555ED556 6DC01AAA 54001556 6EAABAAA 54001556 6FFFFAAA 56073556 6FFFFAAA 54001556 6DEEDAAA 54001556 6DF81AAA 54001556 6FFFFAAA 55555556 6AAAAAAA 55555556 6AAAAAAA 55555556 7FFFFFFE 00000000 * FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF

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