OWLNext    7.0
Borland's Object Windows Library for the modern age
Loading...
Searching...
No Matches
Step 7: Programming the user interface

The next set of adaptations provide standard OLE user interface features such as menu merging and drag and drop.

The following topics discuss programming the user interface.

The following topics discuss programming the user interface.

  • Handling OLE-Related Messages and Events
  • Supporting Menu Merging
  • Updating the Edit Menu
  • Assigning a Tool Bar ID

Handling OLE-Related Messages and Events

ObjectComponents notifies your application's windows of OLE-related events by sending the WM_OCEVENT message. The ObjectWindows OLE classes provide default handlers for the various WM_OCEVENT event notifications. Furthermore, the ObjectWindows classes also process a few standard Windows messages to add additional features of the standard OLE user interface. For example, if a user double-clicks within the client area of your container window, a handler in TOleWindow checks whether the click occurred over an embedded object and, if so, activates the object. Similarly, the TOleWindow::EvPaint method causes each embedded object to draw itself. The following table lists the methods implemented by the client window (TOleWindow) and frame window(TOleFrame, TOleMDIFrame) classes. If you override these handlers in your derived class you must invoke the base class version.

MethodMessageClassDescription
EvSizeWM_SIZEFrameNotifies embedded servers of the size change.
EvTimerWM_TIMERFrameInvokes IdleAction so that DLL servers can carry out command enabling.
EvActivateAppWM_ACTIVATEAPPFrameNotifies embedded servers about being activated.
EvLButtonDownWM_LBUTTONDOWNClientDeactivates any in-place active object.
EvRButtonDownWM_RBUTTONDOWNClientDisplays pop-up verb menu if cursor is on an embedded object.
EvLButtonDblClkWM_LBUTTONDBLCLKClientActivates any embedded object under the cursor.
EvMouseMoveWM_MOUSEMOVEClientAllows user to move or resize an embedded object.
EvLButtonUpWM_LBUTTONUPClientInforms the selected object of position or size changes.
EvSizeWM_SIZEClientInforms TOcView object that window has changed size.
EvMdiActivateWM_MDIACTIVATEClientInforms TOcView object that window has changed size.
EvMouseActivateWM_MOUSEACTIVATEClientForwards the message to the top-level parent window and returns the code to activate the client window.
EvSetFocusWM_SETFOCUSClientNotifies any in-place server of focus change.
EvSetCursorWM_SETCURSORClientChanges cursor shape if within an embedded object.
EvDropFilesWM_DROPFILESClientEmbeds dropped file(s).
EvPaintWM_PAINTClientCauses embedded objects to paint.
EvCommandWM_COMMANDClientProcesses command IDs of verbs.
EvCommandEnableWM_COMMANDENABLEClientProcesses command IDs of verbs.

In some cases, you might need to know what action the base class handler took before you decide what to do in your overriding handler. This is particularly true for mouse-related messages. If the base class handled a double-click action, for example, the user intended the action to activate an object and you probably don't want your code to reinterpret the double-click as a different command. The code that follows shows how to coordinate with a base class handler. These three procedures let the user draw on the surface of the client window with the mouse.

void
TMyClient::EvLButtonDown(uint modKeys, TPoint& pt)
{
if (!Drawing) {
SetCapture()
// additional GDI calls to display drawing
}
}
void
TMyClient::EvMouseMove(uint modKeys, TPoint& pt)
{
if (Drawing) {
// additional GDI calls to display drawing
}
}
void
TMyClient::EvLButtonUp(uint modKeys, TPoint& pt)
{
if (Drawing) {
Drawing = false;
ReleaseCapture();
}
}

As an OLE container, however, the client window may contain embedded objects. Mouse events performed on these objects should not result in any drawing operation. This code shows the handlers updated to allow and check for OLE related processing. Boldface type highlights the changes.

void
TMyClient::EvLButtonDown(uint modKeys, TPoint& pt)
{
TOleWindow::EvLButtonDown(modKeys, pt);
if (!Drawing && !SelectEmbedded()) {
SetCapture()
// additional GDI calls to display drawing
}
}
void
TMyClient::EvMouseMove(uint modKeys,
TPoint& pt)
{
TOleWindow::EvMouseMove(modKeys, pt);
if (Drawing && !SelectEmbedded()) {
// additional GDI calls to display drawing
}
}
void
TMyClient::EvLButtonUp(uint modKeys, TPoint& pt)
{
if (Drawing && !SelectEmbedded()) {
Drawing = false;
ReleaseCapture();
}
TOleWindow::EvLButtonUp(modKeys, pt);
}

The SelectEmbedded method is inherited from TOleWindow. It returns true if an embedded object is currently being moved. The client window calls it to determine whether a mouse message has already been processed by the OLE base class.

Typically, your derived class must call the base class handlers before processing any event or message. The EvLButtonUp handler, however, calls the base class last. Doing so allows the handler to rely on SelectEmbedded which is likely to be reset after TOleWindow processes the mouse-up message.

Supporting Menu Merging

The menu bar of an OLE container with an active object is composed of individual pieces from the normal menus of both the container and server. The container contributes pop-up menus dealing with the application frame or with documents. The server, on the other hand, provides the Edit menu, the Help menu, and any menus that let the user manipulate the activated object.

OLE divides the top-level menus of a menu bar into six groups. Each group is a set of contiguous top-level drop-down menus. Each group is made up of zero or more pop-up menus. The menu groups are named File, Edit, Container, Object, Window, and Help. The group names are for convenience only. They suggest a common organization of related commands, but you can group the commands any way you like.

When operating on its own, a container or server provides the menus for all of the six groups. During an in-place edit session, however, the container retains control of the File, Container and Window groups while the server is responsible for the Edit, Object, and Help groups.

The TMenuDescr class automatically handles all menu negotiations between the server and the container. You simply identify the various menu groups within your menu resource, and ObjectWindows displays the right ones at the right times.

To indicate where groups begin and end in your menu resource, insert SEPARATOR menu items between them. Remember to mark all six groups even if some of them are empty. The TMenuDescr class scans for the separators when loading a menu from a resource. It removes the separators found between top-level entries and builds a structure which stores the number of pop-up menus assigned to each menu group. This information allows ObjectWindows to merge the server's menu into your container's menu bar.

The following menu resource script, taken from the ObjectWindows Tutorial, illustrates defining a simple application menu before it is divided into groups.

{
pop-up "&File"
{
MENUITEM "Save &As", CM_FILESAVEAS
}
pop-up "&Tools"
{
MENUITEM "Pen &Size", CM_PENSIZE
MENUITEM "Pen &Color", CM_PENCOLOR
}
pop-up "&Help"
{
MENUITEM "&About", CM_ABOUT
}
}

The File menu entry belongs to the OLE File menu group. The Tools menu allows the user to edit the application's document, so it belongs to the Edit group. This application does not contain any menus belonging to the Object, Container, or Window group. And finally, the Help menu belongs to the Help group. The following code is a modified version of the same menu resource with SEPARATOR dividers inserted to indicate where one group stops and the next begins.

{
pop-up "&File"
{
MENUITEM "Save &As", CM_FILESAVEAS
}
MENUITEM SEPARATOR // end of File group, beginning of Edit group
pop-up "&Tools"
{
MENUITEM "Pen &Size", CM_PENSIZE
MENUITEM "Pen &Color", CM_PENCOLOR
}
MENUITEM SEPARATOR // end of Edit group, beginning of Container group
MENUITEM SEPARATOR // end of Container group, beginning of Object group
MENUITEM SEPARATOR // end of Object group, beginning of Window group
MENUITEM SEPARATOR // end of Window group, beginning of Help group
pop-up "&Help"
{
MENUITEM "&About", CM_ABOUT
}
}

Insert separators in your application's menu to indicate the various menu groups. Then modify your code to use the SetMenuDescr method when assigning your frame window's menu. This example shows the menu assignment before and after adding menu merging.

Before:

// original menu assignment
void
TScribbleApp::InitMainWindow()
{
TDecoratedFrame* frame;
// Initialize frame and decorations etc. etc.
// Assign frame's menu
frame->AssignMenu("COMMANDS");
}

After including group indicators in the menu:

void
TScribbleApp::InitMainWindow()
{
TOleFrame* frame;
// Initialize frame and decorations etc. etc.
// Assign frame's menu
frame->SetMenuDescr(TMenuDescr("COMMANDS"));
}

Instead of using separators to show which drop-down menus belong to each group, you can use the TMenuDescr constructor whose parameters accept a count for each group. For more details, see the description of the TMenuDescr constructors in the ObjectWindows Reference Guide.

Updating the Edit menu

An OLE container places OLE commands on its Edit menu. The TOleWindow class has default implementations for all of them. It invokes standard dialogs boxes where necessary and processes the user's response. All you have to do is add the commands to the Edit menu of your frame window. It's not necessary to support all six commands, but every container should support at least CM_EDITINSERTOBJECT, to let the user add new objects to the current document, and CM_EDITOBJECT, to let the user choose verbs for the currently selected object.

ObjectWindows defines standard identifiers for the OLE Edit menu commands in ocf/oleview.rh. Update your resource file to include the header file and use the standard identifiers to put OLE commands on the Edit menu.

#include <owl/oleview.rh>
#include <owl/edit.rh>
{
// File menu goes here
pop-up "&Edit"
{
MENUITEM "&Undo\aCtrl+Z", CM_EDITUNDO
MENUITEM "&Cut\aCtrl+X", CM_EDITCUT
MENUITEM "C&opy\aCtrl+C", CM_EDITCOPY
MENUITEM "&Paste\aCtrl+V", CM_EDITPASTE
MENUITEM "Paste &Special...", CM_EDITPASTESPECIAL
MENUITEM "Paste &Link", CM_EDITPASTELINK
MENUITEM "&Delete\aDel", CM_EDITDELETE
MENUITEM "&Insert Object...", CM_EDITINSERTOBJECT
MENUITEM "&Links...", CM_EDITLINKS
MENUITEM "&Show Objects", CM_EDITSHOWOBJECTS
}
// other menus go here
}

Assigning a toolbar ID

If your OLE container has a tool bar, assign it the predefined identifier IDW_TOOLBAR. ObjectWindows must be able to find the container's tool bar if a server needs to display its own tool bar in the container's window. If ObjectWindows can identify the old tool bar, it temporarily replaces it with the new one taken from the server. For ObjectWindows to identify the container's tool bar, the container must use the IDW_TOOLBAR as its window ID.

TControlBar* cb = new TControlBar(parent);
cb->Attr.Id = IDW_TOOLBAR;

The TOleFrame::EvAppBorderSpaceSet method uses the IDW_TOOLBAR for its default implementation. A container can provide its own implementation to handle more complex situations, such as merging with multiple tool bars.