Manipulations of an In-Place Object

When an object is both in-place active and UI active, various events can occur that might be processed by the object, the container, or both.

First are mouse events. When the mouse pointer is over the in-place object's editing window (when the object is both in-place active and UI active), all mouse messages go directly to that window, including WM_MOUSEMOVE, WM_SETCURSOR, and WM_LBUTTONDOWN. If the user now clicks an in-place–active object, it should become UI active. When the mouse pointer moves out of the object's window, it moves into the hatched area. If the user double-clicks here, the object should deactivate its in-place user interface and become active in a separate window—the semantics of OLEIVERB_OPEN. If you are using a hatch window, you have an easy way to detect this mouse event.

If the user moves the mouse out of the hatched area and into the container document's area, the container will now receive messages. If the user clicks on the document itself—outside any objects—this signals the container to deactivate the object by calling IOleInPlaceObject::InPlaceDeactivate or IOleInPlaceObject::UIDeactivate if the object is marked with OLEMISC_ACTIVATEWHENVISIBLE. (We'll see what happens here in a moment.) If the user happens to move the mouse to another in-place–active object and then single-clicks, that object becomes UI active. If that object is not in-place active and the user double-clicks, the container deactivates the UI-active object and activates the new one.

The second type of event is a keyboard event. According to the user interface guidelines, the UI-active object always has first crack at keystrokes. This is guaranteed by the UI-active object always having the focus. The container is responsible for calling the Windows function SetFocus to change the focus to the object window whenever the document containing that object becomes the currently active document. The container obtains the object's window handle by calling IOleInPlaceObject::GetWindow. Although focus takes care of normal keystrokes, it does not take care of accelerators. This requires some special treatment on behalf of the container and the server. The container must modify its message loop to call the current UI-active object's IOleInPlaceActiveObject::TranslateAccelerator before doing any of its own translation. This call actually applies only to in-process objects that would otherwise have no chance of handling accelerators because they have no message loop to call their own. When a local object is UI active, its own message loop is the active one, so it must call OleTranslateAccelerator after checking for accelerators itself but before calling TranslateMessage and DispatchMessage. OleTranslateAccelerator will call IOleInPlaceFrame::TranslateAccelerator if it finds that the accelerator pressed is in the container's accelerator table. This accelerator table is the other piece of information that an object receives from IOleInPlaceSite::GetWindowContext.

This brings up another point: both container and server need to load and process different accelerator tables when they're involved with an in-place–active object. The container will want to disable its accelerators for any menu command that is not currently available—that is, for anything outside the File, Container, and Window menu groups. The server must do the same so that it doesn't attempt File Save or Window Tile operations as an in-place object.

Speaking of menu commands, we already saw how OLE uses the menu descriptor to route messages from the shared menu to the container or the object. One message is WM_MENUSELECT, which causes many applications to change the text in their status lines. When an object receives such a message, it can display appropriate text in the container's status line by calling IOleInPlaceFrame::SetStatusText. Also, an object can determine whether the container even has a status line by calling SetStatusText with NULL and watching for failure. If the call fails, the object can create a status line as an in-place tool if it wants.

A status line is one form of help that is not associated with the object's Help menu. The other form of help is context-sensitive help, initiated by pressing Shift+F1 (and possibly by choosing a menu command as well). If the container detects a request for context-sensitive help, it calls IOleInPlaceActiveObject::ContextSensitiveHelp to instruct the object to enter that help mode. If the object detects the keystroke, it can call the same member function on all three of the container interfaces.

Undo is another common application feature that has some special considerations for in-place activation. For example, performing an Undo operation immediately after an in-place object has been activated should mean "deactivate," just as an Undo operation immediately after a deactivation should mean "reactivate." If an Undo is the first event that occurs after an object is activated in place, the object calls IOleInPlaceSite::DeactivateAndUndo; otherwise, it calls IOleInPlaceSite::DiscardUndoState to let the container free the memory it was holding in case DeactivateAndUndo was called. In the same way, if an in-place object is deactivated and the first thing that happens in the container is an Undo, the container calls IOleInPlaceObject::ReactivateAndUndo. If anything else happens after a deactivation, the container calls IOleObject::DoVerb(OLEIVERB_DISCARDUNDOSTATE).

Something else that can happen with in-place activation is that the position rectangle that an object is given might actually extend past the edge of the container window. In that case, the object can call IOleInPlaceSite::Scroll to have the container show more of the object. This is useful for a spreadsheet object, for example, because the user might move the cell selection outside the visible region using the arrow keys. The user might also scroll the container document directly, in which case the container calls IOleInPlaceObject::SetObjectRects to let the object know the new position. If the object allows resizing while in place, it calls IOleInPlaceSite::OnPosRectChange with the new rectangle it wants. In response, the container calls IOleInPlaceObject::SetObjectRects to let the object know its allowable position rectangle and the clipping rectangle.

If the object is too small, the user can open the object in the full server window as if in-place activation never existed. This is done by calling the object's IOleObject::DoVerb with OLEIVERB_OPEN, which always causes it to activate in another window and never to activate in place. Besides double-clicking in the hatched border, the object should also provide an Open item on its Edit menu and watch for a Ctrl+Enter accelerator. Both means are also defined in the user interface guidelines.