TearOff TCL
Volume Number: 9
Issue Number: 2
Column Tag: TCL Workshop
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 orsource 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 key shortcut selections
of the various grid elements within your palette. I don’t have any key
shortcuts, but I allow the user to use 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 kTearMenu 1000
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, e specially 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)
{