TweetFollow Us on Twitter

TearOff TCL
Volume Number:9
Issue Number:2
Column Tag:TCL Workshop

Related Info: Menu Manager Window Manager

TCL Knick Knacks

The compiler knows!!

By John A. Love, III, MacTutor Regular Contributing Author

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

About the author

John is a member of the Washington Apple Pi Users’ Group from the greater Washington D.C. metropolitan area and can be reached on America Online{John Love} and on GEnie {J.LOVE7}.

This series

The purpose of this continuing series is to laboriously trace through the source code of Symantec’s THINK Class Library (TCL) that serves as the Object Oriented Programming (OOP) arm for their Pascal and C compilers to provide the answers to the age-old question, “Who does what to whom?”.

Forrest Tanaka and Sandy Mossberg wake up, this one’s for you!!!

This article ...

In the first of my “TCL Knick-Knacks” series which appeared in last August’s issue, I promised an article that addressed Tear-Off Menus (TOM), and since a promise is a promise ...

Remember that great article by Don Melton and Mike Ritter that appeared in MacTutor way, way back in April, 1988? Truly a rosy masterpiece that addressed “Tear-Off Menus & Palettes”. However, I recall all the frustration that I encountered in understanding the thorny “Why all this blasted overhead just to handle a window, even a menu-type window?” Well the folks at Symantec have successfully removed the absolute necessity of having to understand all the thorny details.

In my particular implementation, a Tear-Off Menu is both a floating window and a floating palette with each pane in this palette functionally equivalent to an individual Menu item. First, I will stipulate all a programmer must do to implement my variety of Tear-Off Menus using the TCL and then I will amplify on each step.

All this code is included on MacTutor’s disk from last August’s issue. It is also included in my “Feature Flick” package which addresses QuickTime™ and introduces my new class = CQuickTime. You can find the latter on America Online.

First, what to do ...

1) include a 'WDEF' or Window Definition Procedure in the resource file.

2) include a 'MDEF' or Menu Definition Procedure in the resource file.

3) create a 'MENU' for your Tear-Off Menu with the same ID as the 'MDEF' and then place this ID in your 'MBAR' resource.

4) create a 'WIND' resource whose ID also equals that of the 'MDEF'. For the sake of consistency, I make the IDs of the 'MDEF', 'MENU' and 'WIND' all match. Using this approach, it ’s much easier to keep things straight when you have more than one Tear-Off Menu .

5) sub-class TCL’s CGridSelector. CGridSelector is a CSelector is a CPanorama is a CPane and is functionally a palette or array of grid elements wherein each Menu item of your Tear-Off Menu is a distinct pane or grid element of this CGridSelector.

/* 1 */
struct  CCustomSelector : CGridSelector
{
void    DrawItem (short theItem, Rect *theBox);
// Inherited from CSelector:
void    DoClick (Point hitPt, short modifierKeys, long when);
};

At a minimum, override CGridSelector’s DrawItem for drawing. Optionally, override DoClick which CGridSelector inherits from CSelector for selection. Drawing occurs when you pull down the menu from the main menubar or in response to updates after your menu has been torn-off. DoClick comes into play when you click on one of the grid items or panes of your already torn-off menu.

6) sub-class TCL’s CTearOffMenu. CTearOffMenu is a CDirector. As I “panefully” described last August, “a director is a bureaucrat that supervises a window”. So, instead of a CDocument supervising our window, our CTearOffMenu does the supervising.

/* 2 */
struct CTearMenuDir : CTearOffMenu 
{
 void   ITearMenuDir (CApplication *aSupervisor);
 void   DoCommand (long theCommand);
 void   CloseWind (CWindow *theWindow);
};

At a minimum, override CTearOffMenu’s ITearOffMenu (I call it ITearMenuDir). Within ITearMenuDir, call ITearOffMenu, inherited from the superclass, to create your floating window. Then create and initialize your CGridSelector. The new CGridSelector serves as the main CPane of the CTearOffMenu; as a matter of fact, the created CGridSelector is stuffed into CTearOffMenu::itsPane. IGridSelector sets up the Menu Command Number Base for the array of grid items or Menu items; given this base #, the Command Number for each grid/Menu item is separated from its predecessor by one. You also pass to IGridSelector the number of rows & colums in this array or matrix of Menu items. For a conventional Menu there is obviously only one column, however, for a graphic Palette such as the Pattern Menu in MacPaint™, there are multiple columns.

After you call IGridSelector and do some more housekeeping, create and initialize a CSelectorMDEF object. ISelectorMDEF calls IPaneMDEF which calls IMenuDefProc to fill in your 'MDEF' resource stub. Given this filling in, when the Menu Manager calls _MenuSelect, for example, your 'MDEF' is called to magically draw the Menu and choose a particular Menu item.

You pass the above-mentioned CGridSelector object (= CPane) to ISelectorMDEF which, in turn, passes the same object into IPaneMDEF for storage in CPaneMDEF::itsPane. As a direct result, CTearOffMenu::itsPane = CPaneMDEF::itsPane = the new CGridSelector. This is a key point, so hold this thought

I do not use DoCommand, inherited from CTearOffMenu’s superclass = CDirector, in my simple example. I refer you to the “Art Class” example provided by Symantec on their TCL disk(s) wherein DoCommand is overidden.

I’ll reveal later why I override CTearOffMenu::CloseWindow.

7) Within your CMovieApp:

/* 3 */

struct CMovieApp : CApplication
{
 // One instance variable:
 CTearMenuDir  *itsTearMenu;

 // So we have floating windows:
 void   MakeDesktop (void);

 // etc.
 void   SetUpMenus (void);

 void   SwitchToDA (void); 
 void   SwitchFromDA (void); 

 void   DoCommand (long theCommand);
 void   DoKeyDown (char theChar, Byte keyCode, EventRecord *macEvent);

}; /* CMovieApp */

a) place a reference to your CTearOffMenu in an instance variable, said reference being created within CMovieApp::SetUpMenus. This is optional; however, as shown later, I need this reference in my CMovieApp::DoKeyDown.

b) override CApplication::MakeDesktop in order to place a new(CFWDesktop) into the global = gDesktop. In this manner, the desktop will support floating windows properly.

c) override CApplication::SetUpMenus. I will address this in detail later.

d) override CApplications’s SwitchToDA and SwitchFromDA to disable and enable, respectively, your Tear-Off Menu(s). These paired Switch routines are typically the place for disabling and enabling whole Menus only, whereas your UpdateMenus method(s) can disable/enable either whole Menus or individual Menu items:

/* 4 */

 void CMovieApp::SwitchToDA (void)
 {
 gBartender-> DisableMenu(kTearMenu);

 inherited::SwitchToDA();
 } /* SwitchToDA */

 void CMovieApp::SwitchFromDA (void)
 {
 gBartender->EnableMenu(kTearMenu);

 inherited::SwitchFromDA();
 } /* SwitchFromDA */

e) override CApplication::DoCommand to respond to the selection of a discrete pane or Menu item of your CGridSelector palette. This response occurs when you either pull down your Menu to select one of the items as if it were an ordinary Menu or tear it off and subsequently click on the content portion of the torn-off Menu’s floating window.

f) override CApplication::DoKeyDown to response to <CMD> key shortcut selections of the various grid elements within your palette. I don’t have any <CMD> key shortcuts, but I allow the user to use <CMD-Tab> to alternately show or hide the torn-off Menu {HyperCard™, anyone?}.

Next, the details ...

Above, I grouped all the resource stuff together and ditto for the Tear-Off classes and CMovieApp modifications. For the purposes of introduction, these groupings serve well. However, when it now comes time to “follow the bouncing ball”, I am going to be bouncing from group-to-group, mixing it up so-to-speak. I believe this is mandatory in order to logically follow all the interactions as well as the reasons behind them.

The window

Here’s a portion of my “movies.r” that SARez™ chews on:

/* 5 */

// Special WDEF for Floating Windows:
include "Windoid";
#define kWindoid 104

// Apple reserves 0 -> 127:
#define kTearMenu1000

resource'WIND' (kTearMenu, "1st Floating Tearoff Menu",
 purgeable)
{
{40,  40, 110, 112},
// = 16*kWindoid + noGrowDocProc:
1668,
invisible,
goAway,
0x0,
"",
/* I define SystemSevenOrLater
** (found in "Types.r") = true
** in my "movies.r" file:    */
noAutoCenter
};

Note in the above 'WIND' resource description that the defProc field implements the arithmetic mandated by the Window Manager, namely, 16*the ID of the 'WDEF' resource + the window variant. In this manner, the Window Manager uses my 'WDEF' for drawing the window. Take a long stare at the shape and appearance of MacPaint’s tear-off Palette, especially that of its title bar. That is the 'WDEF' that is the sole occupant of the TCL file = “Windoid” that Symantec passes along with their TCL package, together with its source code, natch.

When the TCL initializes your Tear-Off Menu object (my ITearMenuDir calls TCL’s ITearOffMenu), the ID of your 'WIND' resource is passed to inherited::ITearOffMenu as a parameter. The latter calls:

/* 6 */

// CDirector::itsWindow
itsWindow = new (CWindow);
itsWindow->IWindow(WINDid,
 TRUE,  /* floating */
 gDesktop,
 this /* supervisor */);

So a window is created and we’re off and running. Remember, this = the object that was sent the message = ITearOffMenu. Therefore, the supervisor of the floating window becomes the CDirector = my CTearMenuDir, a sub-class of CTearOffMenu. As you will see shortly, this initialization takes place below within CMovieApp::SetUpMenus but cool it.

Next, we’ve got to make the TCL handle our Tear-Off Menu properly, and since the latter is a floating window:

/* 7 */

void  CMovieApp::MakeDesktop (void)
{
 gDesktop = new (CFWDesktop);
 ((CFWDesktop*)gDesktop)-> IFWDesktop(this);
}/* MakeDesktop */

Therefore, the window’s enclosure = gDesktop passed to IWindow above is a CFWDesktop, so everything’s right with the world. Speaking of floating windows, there is a popular, but erroneous, conception that all floating windows must disappear when your application is suspended. Apple’s Human Interface Guidelines stipulate that this be true for Tear-Off Menus; they make no such statement for all floating windows, leaving the programmer the option to do his/her own thing for other floating windows. The mandatory disappearance of floating Menus is logical since MultiFinder™ has switched out your Menus a floating Menu for a non-existent Menu ??? get a life!!! The TCL implements the Human Interface Guidelines precisely within CTearOffMenu’s Suspend and Resume methods.

The Menu

When the Menu Manager is called, e.g., _MenuSelect, the Menu’s 'MDEF' is called via something akin to:

;8

 clr.l    -(sp)
 push.w   #ID
 _GetMenu
 move.l   (sp)+, a0  ; = MenuHandle
 move.l   (a0), a1
 ; menuDefProc is filled in by
 ; _GetMenu after it loads the
 ; 'MENU' resource.  This field
 ; then contains a Handle to my
 ; 'MDEF' because I place the
 ; latter's ID in the 'MENU':
 push.w   theMessage
 push.l   a0
 ; push additional parms here
 ; depending on the Message:
 move.l   menuDefProc(a1),a1
 move.l   (a1),a1
 jmp      (a1)

The TCL implements a similar methodology. Before I give all the gory details of this methodology, first let me present what the 'MDEF' and 'MENU' resources look like:

/* 9 */

// more on this buzzard later:
typedef struct   GenericMDEFRec {
short   JMPinstruction;
VoidFuncdefProc;
CMenuDefProc*itsMenuDefProc;
}GenericMDEFRec,
 *GenericMDEFPtr,
 **GenericMDEFHand;

#define kMDEF  kTearMenu

/* Only this stub appears
** in your .rsrc file:    */
data  'MDEF'(kMDEF, "1st Floating Tearoff Menu", purgeable)
{
$"4EF9" // “jmp”
$"00000000" // “defProc”
$"00000000" // “itsMenuDefProc”
};

The above jmp (a1) effectively jumps to the address containing 4EF900000000, thus in turn executing another jump to the address contained in the next four bytes represented here by zeros. We’ll see below that after ITearOffMenu creates your floating window it calls CMenuDefProc::IMenuDefProc and that these zeros will be filled in by IMenuDefProc with the effective address of TCL’s GenericMDEF. The latter is a generic Menu Definition Procedure which uses Object C. It receives a mDrawMsg to draw the Menu, a mChooseMsg to handle selection from the Menu, a mSizeMsg to determine dimensions of the Menu and a mPopupMsg to specify placement of a popup Menu.

/* 10 */

resource'MENU' (kMDEF, "1st Floating Tearoff Menu",
 purgeable)
{
kMDEF,
// By my convention, MDEF ID = Menu ID:
kMDEF,
0x7FFFFFFF,
enabled,
"Tear off this hummer !!!",
{
/* Item(s) drawn “on-the-fly” by
** DrawMenu as discussed below.  */
}
};

#define mMovie   20

resource 'MBAR' (MBARapp, "MBAR", purgeable)
{
{/* array MenuArray: 5 elements */
 /* [1] */
 mApple,
 /* [2] */
 mFile,
 /* [3] */
 mEdit,
 /* [4] */
 mMovie,
 /* [5] */
 kTearMenu
}
};

void  CMovieApp::SetUpMenus (void)
{
 CTearMenuDir  *cryBaby   = nil;

 /* Superclass takes care of 
 ** adding menus specified in
 ** a MBAR id = 1 resource:   */
 inherited::SetUpMenus();

 /* Director for a tear-off Menu:
 ** ( I’ll explain later on. )*/
 cryBaby = new (CTearMenuDir);
 // Supervisor = your app:
 cryBaby->ITearMenuDir(this);
 itsTearMenu = cryBaby;

}/* SetUpMenus */

Remember that your CMovieApp::SetUpMenus calls inherited::SetUpMenus so that your 'MBAR' resource gets loaded into memory to create the main Menubar, including your Tear-Off Menu. Internal to this loading process, _GetMenu is called and thus the menuDefProc field of the tear-off 'MENU' resource gets filled in with a Handle to my 'MDEF'. _GetMenu is able to accomplish this because the 'MENU' resource contains the ID of the 'MDEF' that should be used. I will explain later my initialization routine = ITearMenuDir. In the meantime, however, the latter eventually calls ITearOffMenu to create your floating window as well as IMenuDefProc to fill the defProc field of your 'MDEF' stub with the effective address of TCL’s GenericMDEF. It looks like we’re almost in business. Before I blow town, i.e., exit my ITearMenuDir, I move the blasted window off-screen so it’s not seen. In short, I keep it handy for later when I tear-off the Menu.

/* 11 */

void  CMenuDefProc::IMenuDefProc (short MDEFid)
{
 GenericMDEFHand theMDEF;

 theMDEF = (GenericMDEFHand) GetResource('MDEF', MDEFid);
 FailNILRes(theMDEF);
 (**theMDEF).defProc = (VoidFunc) GenericMDEF;
 // last 4 bytes of stub:
 (**theMDEF).itsMenuDefProc = this;
 FlushCache();
}

IMenuDefProc also fills in the last four bytes of your 'MDEF' resource stub with the object that was sent the message to begin with and that object is a descendant of CMenuDefProc. I’ll talk about which descendant later but in the meantime, recall when I stated above that thanks to _GetMenu and IMenuDefProc the Menu Manager will jump to the address of TCL’s GenericMDEF. The TCL depicts the latter as:

/* 12 */

pascal void GenericMDEF (short theMessage, register 
 MenuHandle macMenu, Rect *menuRect, Point hitPt,
 short *whichItem)
{
 GenericMDEFHand theMDEF;
 CMenuDefProc  *theMenuDefProc;

 theMDEF = (GenericMDEFHand) (**macMenu).menuProc;
 /* itsMenuDefProc = “this”
 ** in IMenuDefProc: */
 theMenuDefProc = (**theMDEF).itsMenuDefProc;

 switch (theMessage)
 {
 case mDrawMsg:
 theMenuDefProc-> DrawMenu(macMenu, menuRect);
 break;

 // etc.
 }
}

Remember that the object stored in the itsMenuDefProc field of your 'MDEF' stub is the object that was sent the message = IMenuDefProc and said object is our mystical descendant of CMenuDefProc. So in short, for example, when _MenuSelect sends a mDrawMsg to our 'MDEF', we end up calling CMenuDefProc::DrawMenu(...), or really the DrawMenu method of its descendant since the latter must override the parent’s DrawMenu because the parent’s DrawMenu is empty. “Simple, ain’t it ?!*!?”

/* 13 */

class CMenuDefProc : public CObject
{
public:

void  IMenuDefProc(short MDEFid);

virtual voidDrawMenu(MenuHandle macMenu, Rect *menuRect);
virtual voidChooseItem(MenuHandle macMenu, Rect *menuRect, Point hitPt, 
short *whichItem);
virtual voidSizeMenu(MenuHandle macMenu);
virtual voidPlacePopup(MenuHandle macMenu, Rect *menuRect, Point hitPt, 
short *whichItem);
};

and in similiar fashion for the other messages.

Let’s review the bidding

Okay, now I think it’s time to reveal what ITearMenuDir looks like:

/* 14 */

void  CTearMenuDir::ITearMenuDir (CApplication *aSupervisor)
{
 CCustomSelector *myMenu = NULL;
 CSelectorMDEF   *myMDEF = NULL;
 BooleansavedAlloc;
 PicHandlemyPic;
 Rect   pictRect;

First, call ITearOffMenu to create a floating window with ID = kTearMenu and set itsWindow from CTearOffMenu's superclass = CDirector. CMovieApp::SetUpMenus passed this as the Tear-Off Menu's Supervisor. this is referenced within a CMovieApp method, so this must be my application. Therefore, my app supervises my Tear-Off Menu.

ITearOffMenu, in the process of creating our floating window, passes this as the window’s supervisor . This this is the object that was sent our message = ITearOffMenu, which I called cryBaby within my SetUpMenus method (go back a couple of pages to confirm this). So, the this passed as the floating window’s supervisor is our Tear-Off Menu object = CTearMenuDir. As a direct result, our app supervises our Tear-Off Menu which, in turn, supervises our floating window.

“A director is a bureaucrat that supervises a window.” The above is consistent with this because CTearMenuDir descends from CTearOffMenu which descends from CDirector. Down a little bit we will make the supervisor of the CGridSelector (is a CSelector is a CPanorama is a CPane is a sub-view of our floating window) our application. Normally, the supervisor of a CPane is a document.

I mentioned way, way back within this article’s introduction that a CGridSelector is functionally a palette or array of grid elements wherein each Menu item of our Tear-Off Menu is a distinct pane or grid element of this CGridSelector. The fact that I have only one grid element or pane in my very simple array becomes relevant only to the discussion of Command Numbers which I’m intentionally postponing until further on.

/* 15 */

inherited::ITearOffMenu(aSupervisor, kTearMenu);

// Get size of grid element:
savedAlloc = SetAllocation(kAllocCanFail);
myPic = GetPicture(kMAC);
SetAllocation(savedAlloc);
FailNILRes(myPic);
pictRect = (**myPic).picFrame;
// Zero origin:
OffsetRect(&pictRect, -pictRect.left, -pictRect.top);
// Do NOT need after getting frame:
ReleaseResource(myPic);

IGridSelector calls ISelector which calls IPanorama which calls IPane to do all the requisite initialization of instance variables, including those of CGridSelector. For example:

• how many rows and columns are there in my grid array? (since I have only one item, my values are 1, 1)

• the width and height of each grid element (yes I’m implying that all grid elements have the identical size)

These next two instance variables are inherited from CSelector:

• when I pull down the Tear-Off Menu what item is initially highlighted? (the first item has a number = 1)

• the Command Base for my grid array of Menu items (I elected this to be equal to the ID of my TOM)

/* 16 */
myMenu = new (CCustomSelector);
myMenu->IGridSelector
 (
 itsWindow, /* From superclass of CTearOffMenu */
 aSupervisor,  /* My application */
 0, 0,
 sizELASTIC, sizELASTIC,
 0,/* Initial item to select = none in
 ** this case since items are numbered
 ** beginning at 1 */
 kTearMenu, /* Command Base */
 1, 1,  /* # of rows & columns */
 pictRect.right + 2, /* box width */
 pictRect.bottom + 2 /* & height */
 );

aSupervisor is successively passed up the inheritance chain to IBureaucrat and stored in CBureaucrat::itsSupervisor to handle the Commands that our CGridSelector (= = CView = CBureaucrat) cannot handle. I will discuss this key point in detail shortly.

IGridSelector sets CGridSelector::gridOn = true. Since I have only 1 element in my simple example, I don’t need a grid of orthogonal lines whose primary purpose in life is to visually differentiate adjacent multiple grid elements:

/* 17 */

myMenu->SetGridOn(false);
myMenu->FitToEnclosure(true, true);
myMenu->Activate();

itsPane = CTearOffMenu::itsPane = a CPane. My CCustomSelector = a CGridSelector = a CSelector = a CPanorama = a CPane. Therefore, both are CPanes and can be equated. More on this later:

/* 18 */

itsPane = myMenu;

Reset the minimum & maximum sizes of a window. TCL has these preset = 100 & GrayRgn's rgnBBox, respectively:

/* 19 */

SetRect(&itsWindow->sizeRect,
 pictRect.left  - 1,
 pictRect.top   - 1,
 pictRect.right   + 3,
 pictRect.bottom + 3);

Change the size of all the window's sub-view(s), the latter being now just my CGridSelector = myMenu in my simplistic example. Leave a blank 1 pixel border around the picture so it does NOT touch the window frame:

/* 20 */

itsWindow->ChangeSize(pictRect.right + 2, 
 pictRect.bottom + 2);

Leave same blank space in setting margins. This margin ALSO includes the regular window frame. So we have a 1 pixel blank space, a 1 pixel frame AND a 10 pixel drag bar on top:

/* 21 */

SetRect(&margins, 2, 12, 2, 2);

Now a real toughie

Go back to the segment of my introduction wherein I talked about ITearOffMenu, IGridSelector and ISelectorMDEF. The bottom line there was that CTearOffMenu::itsPane = CPaneMDEF::itsPane = my new CGridSelector. The first and third pieces are equated above (itsPane = myMenu). When we pass my new CGridSelector to ISelectorMDEF below, the same CGridSelector is passed to IPaneMDEF which then equates the second and third pieces.

/* 22 */

myMDEF = new (CSelectorMDEF);
myMDEF->ISelectorMDEF(kTearMenu, myMenu, this);

What I deliberately postponed telling you ’til now is that also passed to ISelectorMDEF is this which is the object that was sent the original message = ITearMenuDir which is my cryBaby addressed in my CMovieApp::SetUpMenus. cryBaby is a CTearMenuDir is a CTearOffMenu is a CDirector. ISelectorMDEF not only passes on my CGridSelector object ( = myMenu) to IPaneMDEF for stuffing into CPaneMDEF::itsPane, but it also passes on cryBaby. IPaneMDEF then stuffs cryBaby into CPaneMDEF::itsTearOffMenu. This last stuffing is used by the TCL when I discuss later on tearing off your Menu so hold this thought as well.

AHHHHH-HAH!!!

Remember back a few pages when I said that a descendant of CMenuDefProc gets sent a DrawMenu message when _MenuSelect sends a mDrawMsg to TCL’s GenericMDEF. Well the descendant is CSelectorMDEF and it’s this descendant object that IMenuDefProc, called by my ITearMenuDir within CMovieApp::SetUpMenus, stuffs into the itsMenuDefProc field of my 'MDEF' resource stub.

Dig into the TCL source code and observe that CSelectorMDEF does not have a DrawMenu method; however, its superclass = CPaneMDEF does. So when TCL’s GenericMDEF calls DrawMenu, CPaneMDEF::DrawMenu gets called. Ditto for SizeMenu. If responding to a mChooseMsg CSelectorMDEF::ChooseItem gets called; in short, CSelectorMDEF does not have to travel up to its superclass to find a ChooseItem method. Finally, if responding to a mPopupMsg, we need to travel all the way up to CMenuDefProc to access CMenuDefProc::PlacePopup since none of the sub-classes of CMenuDefProc have a PlacePopup method.

The DrawMenu routine of CPaneMDEF calls itsPane->Draw where itsPane is an instance variable of CPaneMDEF. Note from previous discussions that IPaneMDEF stuffs my CGridSelector object {which is a CSelector is a CPanorama which descends from CPane} into the itsPane instance variable belonging to CPaneMDEF.

So, when CPaneMDEF::DrawMenu is eventually called by TCL’s GenericMDEF, CPaneMDEF::itsPane->Draw gets called. Since itsPane is a CGridSelector object, take a gander at CGridSelector::Draw. The latter sweeps through all the rows and columns of your CGridSelector matrix of elements, or really array of Menu items, and calls CGridSelector::DrawItem for each element or Menu item.

Let’s blow town:

/* 23 */

 // Keep handy, but out of the way:
 itsWindow->MoveOffScreen();
 itsWindow->Select();
}/* ITearMenuDir */

Here’s what my own DrawItem method looks like:

/* 24 */

void  CCustomSelector::DrawItem (short theItem, Rect *theBox)
{
/* Passed area rect is in QuickDraw coordinates */

 #define  kMAC   3000

 BooleansavedAlloc;
 PicHandlemyPic;
 Rect   pictRect;
 
 switch (theItem){
 
 case 1:
 savedAlloc = SetAllocation(kAllocCanFail);
 myPic = GetPicture(kMAC);
 SetAllocation(savedAlloc);
 FailNILRes(myPic);

 pictRect = (**myPic).picFrame;
 CenterRects(&pictRect, theBox);
 DrawPicture(myPic, &pictRect);

 /* Do NOT need after drawing: */
 ReleaseResource(myPic);
 break;
 
 default:
 // Empty for now:
 inherited::DrawItem(theItem, theBox); 
 break; 
 } /* only 1 item */
}/* DrawItem */

Once again, notice that I only have one pane or grid element or Menu item. Given a more complex grid of Menu items, take a gander at Figure 37-1 in Symantec’s “Object Oriented Programming Manual” for the numbering scheme associated with multiple grids.

Tear Off this hummer

Let’s talk a little bit more about CTearOffMenu:

/* 25 */

class CTearOffMenu : public CDirector
{
public:
 CPane  *itsPane;
 Point  corner;
 Rect   margins;

 void   ITearOffMenu (CApplication *aSupervisor,
 short WINDid);

 virtual void  Suspend (void);
 virtual void  Resume (void);
 virtual void  CloseWind (CWindow *theWindow);
 
 virtual void  TornOff (Point aCorner);
 virtual void  MoveToCorner (void);
 virtual WindowPtr  GetMacWindow (void);
 virtual void  SetMargins (Rect *aMargins);
 virtual void  GetMargins (Rect *theMargins);
};

I’ve already presented a flood of words about ITearOffMenu. The Suspend and Resume methods call CWindow::HideSuspend and CWindow::ShowResume to make the Tear-Off Menu disappear and re-appear, respectively, per the specifications of Apple’s Human Interface Guidelines. I’ll postpone addressing CloseWind until later (trust me!!!).

CSelectorMDEF::ChooseItem method detects a Tear-Off event by following your dragging motion. If you’ve dragged beyond the confines of the pulled-down Menu and not stayed within them as you would for a normal choose, then CPaneMDEF::TearOffMenu is called. After some moderate housekeeping, CPaneMDEF::itsTearOffMenu->TornOff is eventually called. Remember when I said a zillion words ago that IPaneMDEF stuffed my CTearOffMenu object = cryBaby into CPaneMDEF::itsTearOffMenu? Well “there she blows!”.

With the calling of TornOff, a new CTearChore is created, initialized and subsequently added to CApplication::itsUrgentChores via:

/* 26 */

gApplication-> AssignUrgentChore(newTearChore);

When ITearChore is called by AssignUrgentChore, this is passed as a parameter where this = the object that was sent the original message. The original message = TornOff, so this this = CPaneMDEF::itsTearOffMenu. ITearChore places the passed this into the instance variable = CTearChore::itsTearOffMenu.

AssignUrgentChore calls:

/* 27 */

 newTearChore->Perform();

which then calls:

/* 28 */

 newTearChore::itsTearOffMenu-> MoveToCorner();

CTearOffMenu::MoveToCorner eventually calls _MoveWindow, passing TRUE as the update parameter. An update Event occurs and guess what !!! Remember, thanks to CMovieApp::SetUpMenus, my window has been hiding in the weeds, i.e., off-screen. The “corner” the window is moved to is CTearOffMenu::corner, which is initialized to {0, 0} by ITearOffMenu. Given a tear-off as detected by ChooseItem (described above), CTearOffMenu::TornOff is called. Passed to TornOff is a single parameter = the mouse location in local Menu pane coordinates. The TCL has already setup the GrafPort so that its origin is the topLeft of the Menu. This is why when there is no tear off and the corner instance variable remains at its initialized value = {0, 0}, the Menu simply “plops down”. When there is a tear-off and TornOff is called, the passed corner changes with the mouse movement and is stored in CTearOffMenu::corner. When _MoveWindow is subsequently called, it’s passed the instance variable. As a direct result, the Tear-Off Menu follows your mouse as you drag.

Let’s start to pick up some loose pieces that we’ve only touched on so far.

The two remaining methods of your CApplication that you should override are DoCommand and DoKeyDown.

command numbers

I still need to talk about how tear-off Menus assign Command Numbers, but here is a kernel for this method so we can subsequently discuss Command Numbers in a logical manner:

/* 29 */

void  CMovieApp::DoCommand (long theCommand)
{
 GrafPtrsavePort;
 Str255 theDAName;

 if (theCommand < 0)
 {
 if (HiShort(-theCommand) == MENUapple)
 {
 /* BIG-time TCL boo-boo for not 
 ** saving & restoring Port:   */
 GetPort(&savePort);
 GetItem(GetMHandle(MENUapple), LoShort(-theCommand),
 theDAName);
 OpenDeskAcc(theDAName);
 SetPort(savePort);
 }
 else if (HiShort(-theCommand) == kTearMenu)
 {
 enum {
 OFF,
 ONApple = MENUapple
 };
 HiliteMenu(ONApple);
 DoCommand(cmdAbout);
 HiliteMenu(OFF);
 } 
 else
 inherited::DoCommand(theCommand);
 
 } /* end: theCommand < 0 */
 else /* theCommand > 0 */
 {
 switch (theCommand)
 {
 case cmdAbout:
 TRY
 {
 }
 CATCH
 {
 }
 ENDTRY;
 break; /* cmdAbout */
 
 // etc.

 default:
 
 inherited::DoCommand(theCommand); 
 } /* end: switch */
 } /* end: theCommand > 0 */
}/* DoCommand */

First, note that I have placed the test for a TOM Command Number in my application’s DoCommand method. Go back a page or so and review the fact that the supervisor of a TOM is your app (I pass this to ITearMenuDir within the CMovieApp’s SetUpMenus). Also recall that the superisor of my TOM’s main pane, my CGridSelector, is also my app. Notice that this Command Number is negative which unambiguously implies that it’s created “on-the-fly”. These are key points, so keep them safe.

Additionally see that within my app’s version of DoCommand, I only test for the high word of the Command Number, that is, the Menu ID. I am not interested in the low word = the Menu item because my CGridSelector has only one pane. If I had a Tear-Off Menu akin to MacPaint’s Pattern Menu I would have multiple grid elements whose Command Numbers were separated by one. As a direct result, I would then be concerned with the low word.

Two zillion words ago I stated that my CMovieApp::SetUpMenus called my ITearMenuDir method and that the latter called IGridSelector. Go back and review that my CGridSelector serves as my Tear-Off Menu’s main CPane when I call:

/* 30 */

 itsPane = myMenu;

IGridSelector, in turn, calls ISelector to place the passed Command Number base integer into CSelector::commandBase. For my simplistic example wherein my Tear-Off Menu has only one pane or grid element or Menu item, I simply pass the ID of my TOM.

Tear-Off the Menu ...

Assuming you tear-off the Menu then, as I’ve previously explained, CPaneMDEF’s TearOffMenu is dutifully called the latter eventually calls CTearOffMenu’s TornOff method to create a new urgent CTearChore and assign said CTearChore to gApplication by calling CApplication::AssignUrgentChore the latter calls CTearChore::Perform which, in turn, calls CTearOffMenu’s MoveToCorner the latter finally calls _MoveWindow to allow the Torn-Off Menu to follow your dragging motion.

Okay, I’m off

Given a tear-off, then we access our pane(s) or Menu item(s) by clicking on them just as if we were clicking on a pane of an ordinary, oh-hum window. CSwitchboard’s DoMouseDown effects the necessary processing by calling gDesktop->DispatchClick (remember, our gDesktop is really a CFWDesktop object). DispatchClick calls _FindWindow to determine where we clicked. Naturally, the returned partCode = inContent in which case CWindow::DispatchClick is called. The latter calls CView::DispatchClick to find out which sub-View has been hit via a call to CView::FindSubView. Given success, CView::DispatchClick calls the pertinent sub-View’s DoClick method. The pertinent sub-View is a CGridSelector object which inherits DoClick from its superclass = CSelector. The pertinent sub-View is my CGridSelector because within my ITearMenuDir I call:

/* 31 */

 myMenu->IGridSelector(...);

Because a CGridSelector (= myMenu) is a CPane, after several jumps up the inheritance chain, eventually IGridSelector calls IPaneX which calls:

/* 32 */

 itsEnclosure->AddSubView(this);

where the pane’s enclosure = our floating window and this = my CGridSelector. Eventually, the TCL calls CView::AddSubView which does exactly that add the sub-View to the CWindow’s CList instance variable = CView::itsSubViews.

I have chosen to “roll my own” DoClick method as follows:

/* 33 */

void  CCustomSelector::DoClick (Point hitPt,
 short modifierKeys, long when)
{
 short  itemHit, count;
 long   finalTicks;
 extern short  gClicks;
 
 itemHit = FindItem(hitPt);

 // Ignore all clicks beyond 2:
 if ( (itemHit != selection) &&
   (gClicks == 2) )
 {
 for (count = 2; count > 0; count--)
 {
 HiliteItem(itemHit, hiliteON);
 Delay(20, &finalTicks);
 HiliteItem(itemHit, hiliteOFF);
 Delay(20, &finalTicks);
 }
 itsSupervisor->DoCommand(-(((long)commandBase << 16 ) + 
 itemHit));
 }
}/* DoClick */

Notice that when we call IGridSelector from within ITearMenuDir, the CMovieApp is passed as the supervisor to handle the Commands that our CGridSelector ( = = CBureaucrat) cannot handle. So when DoClick calls:

/* 34 */

 itsSupervisor->DoCommand(...);

CMovieApp::DoCommand is really being called, another reason for testing for a Tear Menu Command Number within your CApplication’s DoCommand. Also, note that we negate the Command Number when we call DoCommand, thus creating the Command Number “on the fly”.

No tear-off

What about pulling down the Menu, but not tearing it off? CSelectorMDEF::ChooseItem calls CSelector’s special HiliteItem method and then stuffs the selected item number into the *whichItem passed by reference to ChooseItem by the Menu Manager just like it’s normally done. ChooseItem fills in the low memory long word global = MenuDisable and exits.

In this scenario, i.e., no tear-off, ChooseItem was originally called by _MenuSelect which returns a long word with the Menu ID in the High word and the Menu item in the low word. _MenuSelect is called from within CDesktop::DispatchClick. The latter continues by calling CBartender’s FindCmdNumber and passing the result to:

/* 35*/

 gGopher->DoCommand(cmdNbr);

FindCmdNumber, quite naturally, finds our Tear-Off Menu because we placed its ID in our 'MBAR' resource. However, since we did not place any Command Numbers in our Tear-Off 'MENU' resource, FindCmdNumber returns the negative of the Tear-Off Menu ID in the High word and the negative of the selected Menu item in the low word of the composite long word result. Just like a 'FONT' Menu or the Apple Menu, eh? So, once again, we have a negative Command Number just as we did when we executed CSelector’s DoClick method.

Whew I thought I was in trouble for a second

What’s the gGopher here?

Maybe you have just activated a oh-hum window supervised by a particular CDocument in which case the gGopher becomes that document (see my August, 1992 article). The TCL goes to yourDoc->DoCommand. If the TCL fails to find the appropriate Command Number there, the TCL calls:

/* 36 */

 inherited::DoCommand( );

Since a CDocument is a CDirector is a CDirectorOwner is a CBureaucrat, we wind our way up to CBureaucrat::DoCommand which then calls:

/* 37 */

 itsSupervisor->DoCommand( );

We are now looking at the supervisor of the CDocument and that is our app.

Okay that’s one down.

Maybe we have just torn off our Menu, so CTearOffMenu::MoveToCorner is eventually called as previously described. CWindow::Select is called after the window is moved to follow your mouse. When CWindow::Activate is subsequently called, CDirector::Activate ends up being called. The latter detects that we have a floating window, so CBureaucrat::BecomeGopher is not called. As a direct result, the gGopher stays put, remaining whatever it was as a result of our non-floating windows. So we end up going to CMovieApp::DoCommand just as we did before we tore-off our Menu.

HyperCard™ anyone

In order to fill in the details of showing/hiding my floating window via pressing <CMD-Tab>, let me present my app’s DoKeyDown method, explaining as I go:

/* 38 */

void  CMovieApp::DoKeyDown (char theChar, Byte keyCode, EventRecord *macEvent)
{
 enum {
 OFF,
 ONApple = MENUapple
 };
 long   finalTicks;
 WindowPeek tearPeek;
 Point  tlWind,
 defaultPt = {100, 100};
 Rect   desk;
 CWindow*theWindow;

 if (macEvent->modifiers & cmdKey)
 {
 if (theChar == '/')
 {
 HiliteMenu(ONApple);
 DoCommand(cmdAbout);
 HiliteMenu(OFF);
 } /* CMD-? */ 
 else if (keyCode == 0x0D)
 { /* = 'w', regardless
 ** of modifier key*/
 if (macEvent->modifiers & optionKey)
 { // = <CMD-Option> w
 theWindow = gDesktop-> GetTopWindow();
 while (theWindow != nil)
 {
 theWindow->Close();
 theWindow = gDesktop-> GetTopWindow();
 }
 }
 }

 // okay, now we get to my <CMD-Tab> stuff:
 else if (keyCode == 0x30 ||
 keyCode == 0x48)  // <Tab>
 {
 if (itsTearMenu->corner.h == 0 && 
  itsTearMenu->corner.v == 0)

Never torn-off before. The reason is that when ITearOffMenu is called by my ITearMenuDir, CTearOffMenu::corner is initialized to {0, 0} and does not change until we tear-off the Menu. Only then does CTearOffMenu::TornOff get called to create a new CTearChore. Subsequently, CTearOffMenu::MoveToCorner is called to alter the instance variable = corner. As a direct result, we must call the TornOff method directly to execute a tear-off:

/* 39 */
          
 itsTearMenu-> TornOff(defaultPt);
 
 else
 {
 // Toggle between show & hide:
 tearPeek = (WindowPeek) itsTearMenu->GetMacWindow();
 tlWind = topLeft( (**(tearPeek ->contRgn)).rgnBBox );
 ((CFWDesktop*)gDesktop)-> GetBounds(&desk);
 
 if (tlWind.v > desk.bottom && 
  tlWind.h > desk.right)

Torn-off before, BUT subsequently closed because CTearOffMenu::CloseWind calls CWindow::MoveOffScreen. Put the window back where it was BEFORE you closed it:

/* 40 */

 itsTearMenu-> TornOff(itsTearMenu->corner);
 else
 itsTearMenu-> CloseWind(itsTearMenu->itsWindow);
 }
 
 } /* CMD-Tab */
 } /* CMD-key pressed */
 else if (keyCode == KeyHelp)
 {
 HiliteMenu(ONApple);
 DoCommand(cmdAbout);
 HiliteMenu(OFF);
 } /* Help Key */
 else inherited::DoKeyDown(theChar, keyCode, macEvent);
}/* DoKeyDown */

Overidding CloseWind, inherited from CTearOffMenu’s superclass = CDirector, completes the discussion:

/* 41 */

void  CTearMenuDir::CloseWind (CWindow *theWindow)
{
 WindowPeek tearPeek;
 Point  tlWind;

If we insist on toggling the showing/hiding of the torn-off Menu via a keystroke, we canNOT save the current position of the associated window JUST in our DoKeyDown(...) method. For example, what if we move our window, close it by clicking in the goAway box and then hit <CMD-Tab> to re-show it. The window will be shown in its position prior to our move. We could do our saving within our application's Idle() method, but that consumes unnecessary time.

/* 42 */

 tearPeek = (WindowPeek) GetMacWindow();
 // corner in GLOBAL coordinates:
 tlWind = topLeft( (**(tearPeek-> contRgn)).rgnBBox );

 SetPt(&corner, tlWind.h, tlWind.v);

 inherited::CloseWind(theWindow);
}/* CloseWind */

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

OmniGraffle Pro 6.1.4 - Create diagrams,...
OmniGraffle Pro helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use... Read more
OmniGraffle 6.1.4 - Create diagrams, flo...
OmniGraffle helps you draw beautiful diagrams, family trees, flow charts, org charts, layouts, and (mathematically speaking) any other directed or non-directed graphs. We've had people use Graffle to... Read more
MegaSeg 5.9.5 - Professional MP3 DJ appl...
MegaSeg is a complete solution for pro audio/video DJ mixing, radio automation, and music scheduling with rock-solid performance and an easy-to-use design. Mix with visual waveforms and Magic... Read more
MarsEdit 3.6.8 - Quick and convenient bl...
MarsEdit is a blog editor for OS X that makes editing your blog like writing email, with spell-checking, drafts, multiple windows, and even AppleScript support. It works with with most blog services... Read more
BBEdit 11.0.3 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
Microsoft Office Preview 15.8 - Popular...
Welcome to the new and modern Microsoft Office for Mac. You will receive regular updates automatically until the official release in the second half of 2015. With the redesigned Ribbon and your... Read more
Yosemite Cache Cleaner 9.0.5 - Clear cac...
Yosemite Cache Cleaner is an award-winning general purpose tool for OS X. YCC makes system maintenance simple with an easy point-and-click interface to many OS X functions. Novice and expert users... Read more
ExpanDrive 4.3.2 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
RapidWeaver 6.0.8 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
Artlantis Studio 5.1.2.7 - 3D rendering...
Artlantis Studio is a unique and ideal tool for performing very high resolution rendering easily and in real time. The new FastRadiosity engine now lets you compute images in radiosity-even in... Read more

Bored? MyLeisure FreeTime Maximizer Will...
Bored? MyLeisure FreeTime Maximizer Will Take Care of That! Posted by Jessica Fisher on March 5th, 2015 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
New Publisher Allstar Games Heads West w...
Allstar Games has announced its first mobile title designed for western audiences, Allstar Heroes. The game will be a massive online battle arena (MOBA) that offers dozens of heroes for you to collect and pit against your opponents. As each hero has... | Read more »
RAD Boarding Review
RAD Boarding Review By Jennifer Allen on March 5th, 2015 Our Rating: :: NEARLY RADUniversal App - Designed for iPhone and iPad RAD Boarding isn’t quite one of the greats, but it has potential.   | Read more »
Presenting the International Mobile Gami...
11th Annual International Mobile Gaming Awards ceremony, hosted by actress Allison Haislip, gathered mobile game developers and publishers from around the world. They chose 13 winners out of the 93 nominations. British studio USTWO won the the Grand... | Read more »
AG Drive Review
AG Drive Review By Tre Lawrence on March 5th, 2015 Our Rating: :: FUTURISTIC STREET RACING.Universal App - Designed for iPhone and iPad Futuristic racing… interstellar style.   | Read more »
GDC 2015 – Nightmare Guardians is an Int...
GDC 2015 – Nightmare Guardians is an Interesting Hybrid of MOBA and Lane Defense Posted by Rob Rich on March 5th, 2015 [ permalink ] I have to say that lane defense (i.e. | Read more »
Overkill 3 Review
Overkill 3 Review By Tre Lawrence on March 5th, 2015 Our Rating: :: WHO'S NEXT?Universal App - Designed for iPhone and iPad Cover system gameplay in the third-person.   Developer: Craneballs Price: Free Version Reviewed: 1.1.6... | Read more »
Warner Bros. Interactive Entertainment A...
Warner Bros. has some exciting games coming down the pipe! | Read more »
GDC 2015 – Star Trek Timelines will Prob...
GDC 2015 – Star Trek Timelines will Probably Make Your Inner Trekkie Squeal With Glee Posted by Rob Rich on March 4th, 2015 [ permalink ] Any popular fictional universe has its fair share of fan fiction – where belo | Read more »
Protect Yourself from an Onslaught of Ca...
Surprise Attack Games has announced a Cat-astrophic new physics puzzler called Fort Meow! In the game, a young girl named Nia finds her grandfather’s journal which triggers an all mighty feline attack! Why do the cats want the journal? Who knows,... | Read more »

Price Scanner via MacPrices.net

Apple restocks refurbished 15-inch Retina Mac...
The Apple Store has restocked Apple Certified Refurbished 2014 15″ Retina MacBook Pros, available for up to $400 off the cost of new models. An Apple one-year warranty is included with each model,... Read more
Roundup of MacBook Air sale prices, models up...
B&H Photo has MacBook Airs on sale for up to $100 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 11″ 128GB MacBook Air: $799 100 off MSRP - 11″ 256GB MacBook Air: $999 $100... Read more
New Firstrade Mobile App Enables On-The-Go Tr...
Firstrade Securities Inc. has announced its new mobile app, which gives investors immediate access to the company’s trading platform on all mobile devices. The app was developed in-house and was... Read more
Sonnet Introduces USB 3.0 + eSATA Thunderbolt...
Sonnet has announced the launch of its new USB 3.0 + eSATA Thunderbolt Adapter for easy connectivity to USB 3.0 devices and eSATA storage, and USB 3.0 + Gigabit Ethernet Thunderbolt Adapter for easy... Read more
Apple restocks refurbished 27-inch 5K iMacs f...
The Apple Store has restocked Apple Certified Refurbished 27″ 3.5GHz 5K iMacs for $2119 including free shipping. Their price is $380 off the cost of new models, and it’s the lowest price available... Read more
Free Clean Reader Mobile App Hides Swear Word...
The new Clean Reader app, now available in the Apple App Store and Google Play, delivers the opportunity of reading any book without being exposed to profanity. By selecting how clean they want their... Read more
Kinsa Launches “Groups” App to Monitor Illnes...
Kinsa, makers of the first FDA approved app-enabled smartphone thermometer thst won the 2013 Cleveland Clinic Medical Innovation Grand Prize and recently appeared in Apple’s “Parenthood” TV... Read more
iPad: A More Positive Outlook – The ‘Book Mys...
It’s good to hear someone saying positive things about the iPad. I’ve been trying to bend my mind around how Apple’s tablet could have gone from zero to bestselling personal computing device on the... Read more
Mac Pros on sale for up to $279 off MSRP
Amazon has Mac Pros in stock and on sale for up to $279 off MSRP. Shipping is free: - 4-Core Mac Pro: $2725.87, $273 off MSRP (9%) - 6-Core Mac Pro: $3719.99, $279 off MSRP (7%) Read more
Sale! 13-inch Retina MacBook Pros for up to $...
B&H Photo has 13″ Retina MacBook Pros on sale for up to $205 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 13″ 2.6GHz/128GB Retina MacBook Pro: $1219.99 save $80 - 13″ 2.... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
Position Opening at *Apple* - Apple (United...
…Summary** As a Specialist, you help create the energy and excitement around Apple products, providing the right solutions and getting products into customers' hands. You Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
*Apple* Solutions Consultant - Retail Sales...
**Job Summary** As an Apple Solutions Consultant (ASC) you are the link between our customers and our products. Your role is to drive the Apple business in a retail Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.