Borland Online And The Cobb Group Present:


February, 1996 - Vol. 3 No. 2

ObjectWindows programming - OWL quick tips

by Kent Reisdorph

If you're not using the ObjectWindows Library (OWL) to create your Windows applications, you should be. Here are three quick tips that you can use to make sure you're getting the most out of OWL.

Enabling and disabling menu items in OWL

A good Windows application follows certain conventions. When a menu item is not available or not applicable for the current situation, it should be grayed out. This prevents the user from clicking on a menu item and wondering why nothing is happening.

In an OWL application, you can create a TMenu object for your menu and do all sorts of nifty things. But you don't often need all the power of TMenu.

The TCommandEnabler class offers a simple and elegant solution for basic menu manipulation. You need only add a response table entry, EV_COMMAND_ENABLE, for the menu item you want to manipulate and then provide a simple function to do the enabling. OWL takes care of calling the enabler function for you.

In addition to enabling and disabling menu items, the TCommandEnabler class also allows you to set a check mark on or remove it from the menu item, and to change the menu text. The most commonly used TCommandEnabler functions are Enable(), SetCheck(), and SetText().

You nearly always see a command enabler used with an application's Save command, for example. You don't want to enable the Save option if there is no data to save, or if the current data is unchanged. You'd typically use a boolean value to determine whether the file needs to be saved. (By the way, the OWL Doc/View architecture automatically handles enabling and disabling the standard commands using the technique we're describing.) The designated value will be zero if the file doesn't need saving and non-zero if it does.

Part of your response table would look like this:

EV_COMMAND(CM_FILESAVE, CmFileSave),
EV_COMMAND_ENABLE(CM_FILESAVE, FileSaveEnabler)

Note that we use the same ID in both entries. Next, you'll need to define the FileSaveEnabler() function:

void 
MyWindow::FileSaveEnabler(TCommandEnabler& ce)
{
   ce.Enable(isFileDirty);
}

That's it! Now the Save menu item will automatically be enabled when isFileDirty is True and disabled when isFileDirty is False.

To set a check mark or to change the text, you need only call the appropriate TCommandEnabler functions. You can see that command enablers are both easy to use and effective!

Who's under there?

You're probably aware that OWL provides fly-by status bar hints nearly automatically if you use the TControlBar and TStatusBar classes in your applications. OWL accomplishes this task by determining what control the cursor is over, loading a string resource with the same ID as the control, and displaying that string in the status bar.

This process works like a charm, but at times you may need to accomplish the same type of action to add some flexibility that OWL doesn't provide. In order to do this you'll need to determine what control the cursor is over. EvSetCursor() provides you with all the information you need.

The function signature of EvSetCursor() looks like this:

bool EvSetCursor(HWND hWndCursor,
  uint hitTest, uint mouseMsg)

As you can see, you get a lot of information in the EvSetCursor() function: You get the handle of the window that the cursor is over (remember, all controls are windows), the hitTest code, and the mouse message. Using this information, you can do a lot of things!

For example, let's say you want hint text for buttons in a dialog box. Assuming that you had a pointer to a static control to display the text, called Hint, you could use the following:

bool
TMainDlg::EvSetCursor(HWND hwnd, UINT, 
  UINT msg)
{
   if (msg == WM_MOUSEMOVE) {
      if (hwnd == *this) {
         Hint->SetText("");
      }
      else {
         char text[50];
         int id = ::GetDlgCtrlID(hwnd);
         HINSTANCE hInstance = 
           GetModule()->GetInstance();
         LoadString(hInstance, id, text, 
                    sizeof(text));
         Hint->SetText(text);
      }
   }
return false;
}

This code sets the text to blank if the cursor is positioned over the dialog box itself. If the cursor isn't over the dialog box, then we first get the control's ID based on its HWND. Next, we load a string resource with the same ID as the control. Finally, we display the string by setting the text of the static control.

In reality, this code would include some modifications to prevent flicker in the static control, but you get the idea. As you can see, EvSetCursor() can be useful for actions other than just setting the cursor.

Dynamic_cast

Frequently, you'll need to access members of one class from within another class. For instance, if your application has a control bar, your control bar will typically be set up at the application level, as a member of your TApplication derived class. If you try to get at the control bar using

GetApplication()->cb->Insert( . . . );

you'll get a compiler error telling you that cb is not a member of TApplication. This error appears because the GetApplication() function returns a TApplication pointer, and your control bar isn't a member of TApplication but rather a member of your class derived from TApplication.

To get at the control bar, you'll need to cast the pointer returned from GetApplication() to a pointer of your derived class. C++ provides an operator called dynamic_cast to cast from a base class to a derived class. In our example, the cast would look like this:

MyApp* app = dynamic_cast<MyApp*>(GetApplication());
if (app) app->cb->Insert( . . . );

Note that we test the pointer before we use it. If the case fails, dynamic_cast will then return NULL.

In addition to the obvious, dynamic_cast is also useful for other operations. For instance, you could use dynamic_cast to determine the type of a derived class, given the base class pointer:

Fruit* fruit = dynamic_cast<Fruit*>(ptrObject);
if (dynamic_cast<Apple*>(fruit))
   // fruit must be an Apple
   fruit->MakeApplePie();
else if (dynamic_cast<Cherry*>(fruit))
  // fruit must be a Cherry
  fruit->MakeCherryPie();

We've stepped through just one of the many uses for dynamic_cast. With this basic introduction, you can now experiment to find other ways to use it in your applications.

Return to the Borland C++ Developer's Journal index

Subscribe to the Borland C++ Developer's Journal


Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.