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 */

 
AAPL
$116.47
Apple Inc.
+0.16
MSFT
$47.98
Microsoft Corpora
-0.72
GOOG
$537.50
Google Inc.
+2.67

MacTech Search:
Community Search:

Software Updates via MacUpdate

Cobook 3.0.7 - Intelligent address book....
Cobook Contacts is an intuitive, engaging address book. Solve the problem of contact management with Cobook Contacts and its simple interface and powerful syncing and integration possibilities.... Read more
StatsBar 1.9 - Monitor system processes...
StatsBar gives you a comprehensive and detailed analysis of the following areas of your Mac: CPU usage Memory usage Disk usage Network and bandwidth usage Battery power and health (MacBooks only)... Read more
Cyberduck 4.6 - FTP and SFTP browser. (F...
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more
Maya 2015 - Professional 3D modeling and...
Maya is an award-winning software and powerful, integrated 3D modeling, animation, visual effects, and rendering solution. Because Maya is based on an open architecture, all your work can be scripted... Read more
Evernote 6.0.1 - Create searchable notes...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
calibre 2.11 - Complete e-library manage...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more
Herald 5.0.1 - Notification plugin for M...
Note: Versions 2.1.3 (for OS X 10.7), 3.0.6 (for OS X 10.8), and 4.0.8 (for OS X 10.9) are no longer supported by the developer. Herald is a notification plugin for Mail.app, Apple's Mac OS X email... Read more
Firetask 3.7 - Innovative task managemen...
Firetask uniquely combines the advantages of classical priority-and-due-date-based task management with GTD. Stay focused and on top of your commitments - Firetask's "Today" view shows all relevant... Read more
TechTool Pro 7.0.6 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PhotoDesk 3.0.1 - Instagram client for p...
PhotoDesk lets you view, like, comment, and download Instagram pictures/videos! (NO Uploads! / Image Posting! Instagram forbids that! AND you *need* an *existing* Instagram account). But you can do... Read more

Latest Forum Discussions

See All

Ubisoft Gives Everyone Two New Ways to E...
Ubisoft Gives Everyone Two New Ways to Earn In-Game Stuff for Far Cry 4 Posted by Jessica Fisher on November 21st, 2014 [ permalink ] | Read more »
Golfinity – Tips, Tricks, Strategies, an...
Dig this: Would you like to know what we thought of being an infinite golfer? Check out our Golfinity review! Golfinity offers unlimited ways to test your skills at golf. Here are a few ways to make sure your score doesn’t get too high and your... | Read more »
Dark Hearts, The Sequel to Haunting Meli...
Dark Hearts, The Sequel to Haunting Melissa, is Available Now Posted by Jessica Fisher on November 21st, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Meowza! Toyze Brings Talking Tom to Life...
Meowza! | Read more »
Square Enix Announces New Tactical RPG f...
Square Enix Announces New Tactical RPG for Mobile, Heavenstrike Rivals. Posted by Jessica Fisher on November 21st, 2014 [ permalink ] With their epic stories and gorgeous graphics, | Read more »
Quest for Revenge (Games)
Quest for Revenge 1.0.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.0 (iTunes) Description: The great Kingdom of the west has fallen. The gods ignore the prayers of the desperate. A dark warlord has extinguished... | Read more »
Threadz is a New Writing Adventure for Y...
Threadz is a New Writing Adventure for You and Your Friends Posted by Jessica Fisher on November 21st, 2014 [ permalink ] In the tradition of round-robin storytelling, | Read more »
SteelSeries Stratus XL Hardware Review
Made by: SteelSeries Price: $59.99 Hardware/iOS Integration Rating: 4 out of 5 stars Usability Rating: 4.5 out of 5 stars Reuse Value Rating: 4.25 out of 5 stars Build Quality Rating: 4.5 out of 5 stars Overall Rating: 4.31 out of 5 stars | Read more »
ACDSee (Photography)
ACDSee 1.0.0 Device: iOS iPhone Category: Photography Price: $1.99, Version: 1.0.0 (iTunes) Description: Capture, perfect, and share your photos with ACDSee. The ACDSee iPhone app combines an innovative camera, a powerful photo... | Read more »
ProTube for YouTube (Entertainment)
ProTube for YouTube 2.0.2 Device: iOS Universal Category: Entertainment Price: $1.99, Version: 2.0.2 (iTunes) Description: ProTube is the ultimate, fully featured YouTube app. With it's highly polished design, ProTube offers ad-free... | Read more »

Price Scanner via MacPrices.net

27-inch 3.5GHz 5K iMac in stock today and on...
 B&H Photo has the new 27″ 3.5GHz 5K iMac in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP, and it’s the lowest price available... Read more
21-inch 1.4GHz iMac on sale for $979, save $1...
B&H Photo has the new 21″ 1.4GHz iMac on sale for $979.99 including free shipping plus NY sales tax only. Their price is $120 off MSRP. B&H will also include free copies of Parallels Desktop... Read more
13-inch 1.4GHz/256GB MacBook Air on sale for...
B&H Photo has lowered their price on the 13″ 1.4GHz/256GB MacBook Air to $1059.99 including free shipping plus NY sales tax only. Their price is $140 off MSRP, and it’s the lowest price for this... Read more
Save up to $400 with Apple refurbished 2014 1...
The Apple Store has restocked Apple Certified Refurbished 2014 15″ Retina MacBook Pros for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and shipping... Read more
New 13-inch 1.4GHz MacBook Air on sale for $8...
 Adorama has the 2014 13″ 1.4GHz/128GB MacBook Air on sale for $899.99 including free shipping plus NY & NJ tax only. Their price is $100 off MSRP. B&H Photo has the 13″ 1.4GHz/128GB MacBook... Read more
Apple Expected to Reverse Nine-Month Tablet S...
Apple and Samsung combined accounted for 62 percent of the nearly 36 million branded tablets shipped in 3Q 2014, according to early vendor shipment share estimates from market intelligence firm ABI... Read more
Stratos: 30 Percent of US Smartphone Owners t...
Stratos, Inc., creator of the Bluetooth Connected Card Platform, has announced results from its 2014 Holiday Mobile Payments Survey. The consumer survey found that nearly one out of three (30 percent... Read more
2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has lowered their price on the new 1.4GHz Mac mini to $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new... Read more
Check Apple prices on any device with the iTr...
MacPrices is proud to offer readers a free iOS app (iPhones, iPads, & iPod touch) and Android app (Google Play and Amazon App Store) called iTracx, which allows you to glance at today’s lowest... Read more
64GB iPod touch on sale for $249, save $50
Best Buy has the 64GB iPod touch on sale for $249 on their online store for a limited time. Their price is $50 off MSRP. Choose free shipping or free local store pickup (if available). Sale price for... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
*Apple* Store Leader Program - College Gradu...
Job Description: Job Summary As an Apple Store Leader Program agent, you can continue your education as you major in the art of leadership at the Apple Store. You'll Read more
*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
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.