Intercepting Context Menus

From Apache OpenOffice Wiki
Jump to: navigation, search



A context menu is displayed when an object is right clicked. Typically, a context menu has context dependent functions to manipulate the selected object, such as cut, copy and paste. Developers can intercept context menus before they are displayed to cancel the execution of a context menu, add, delete, or modify the menu by replacing context menu entries or complete sub menus. It is possible to provide new customized context menus.

Context menu interception is implemented by the observer pattern. This pattern defines a one-to-many dependency between objects, so that when an object changes state, all its dependents are notified. The implementation supports more than one interceptor. The root access point for intercepting context menus is a com.sun.star.frame.Controller object. The controller implements the interface com.sun.star.ui.XContextMenuInterception to support context menu interception.

Register and Remove an Interceptor

The com.sun.star.ui.XContextMenuInterception interface enables the developer to register and remove the interceptor code. When an interceptor is registered, it is notified whenever a context menu is about to be executed. Registering an interceptor adds it to the front of the interceptor chain, so that it is called first. The order of removals is arbitrary. It is not necessary to remove the interceptor that registered last.

Writing an Interceptor

Notification

A context menu interceptor implements the com.sun.star.ui.XContextMenuInterceptor interface. This interface has one function that is called by the responsible controller whenever a context menu is about to be executed.

  ContextMenuInterceptorAction notifyContextMenuExecute ( [in] ContextMenuExecuteEvent aEvent)

The com.sun.star.ui.ContextMenuExecuteEvent is a struct that holds all the important information for an interceptor.

Members of com.sun.star.ui.ContextMenuExecuteEvent
ExecutePosition com.sun.star.awt.Point. Contains the position the context menu will be executed.
SourceWindow com.sun.star.awt.XWindow. Contains the window where the context menu has been requested.
ActionTriggerContainer com.sun.star.container.XIndexContainer. The structure of the intercepted context menu. The member implements the com.sun.star.ui.ActionTriggerContainer service.
Selection com.sun.star.view.XSelectionSupplier. Provides the current selection inside the source window.

Querying a Menu Structure

The ActionTriggerContainer member is an indexed container of context menu entries, where each menu entry is a property set. It implements the com.sun.star.ui.ActionTriggerContainer service. The interface com.sun.star.container.XIndexContainer directly accesses the intercepted context menu structure through methods to access, insert, remove and replace menu entries.

All elements in an ActionTriggerContainer member support the com.sun.star.beans.XPropertySet interface to get and set property values. There are two different types of menu entries with different sets of properties:

Type of Menu Entry Service Name
Menu entry "com.sun.star.ui.ActionTrigger"
Separator "com.sun.star.ui.ActionTriggerSeparator"

It is essential to determine the type of each menu entry be querying it for the interface com.sun.star.lang.XServiceInfo and calling

  boolean supportsService ( [in] string ServiceName )

The following example shows a small helper class to determine the correct menu entry type.

  // A helper class to determine the menu element type
  public class MenuElement
  {
      static public boolean IsMenuEntry( com.sun.star.beans.XPropertySet xMenuElement ) {
          com.sun.star.lang.XServiceInfo xServiceInfo =
              (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface(
                  com.sun.star.lang.XServiceInfo.class, xMenuElement );
 
          return xServiceInfo.supportsService( "com.sun.star.ui.ActionTrigger" );
      }
 
      static public boolean IsMenuSeparator( com.sun.star.beans.XPropertySet xMenuElement ) { com.sun.star.lang.XServiceInfo xServiceInfo =
          (com.sun.star.lang.XServiceInfo)UnoRuntime.queryInterface(
              com.sun.star.lang.XServiceInfo.class, xMenuElement );
 
          return xServiceInfo.supportsService( "com.sun.star.ui.ActionTriggerSeparator" );
      }
  }

Figure 4.1: Determine the menu element type

The com.sun.star.ui.ActionTrigger service supported by selectable menu entries has the following properties:

Properties of com.sun.star.ui.ActionTrigger
Text string. Contains the text of the label of the menu entry.
CommandURL string. Contains the command URL that defines which function will be executed if the menu entry is selected by the user.
HelpURL string. This optional property contains a help URL that points to the help text.
Image com.sun.star.awt.XBitmap. This property contains an image that is shown left of the menu label. The use is optional so that no image is used if this member is not initialized.
SubContainer com.sun.star.container.XIndexContainer. This property contains an optional sub menu.

The com.sun.star.ui.ActionTriggerSeparator service defines only one optional property:

Property of com.sun.star.ui.ActionTriggerSeparator
SeparatorType com.sun.star.ui.ActionTriggerSeparatorType. Specifies a certain type of a separator. Currently the following types are possible:
  • const int LINE = 0
  • const int SPACE = 1
  • const int LINEBREAK = 2

Changing a Menu

It is possible to accomplish certain tasks without implementing code in a context menu interceptor, such as preventing a context menu from being activated. Normally, a context menu is changed to provide additional functions to the user.

As previously discussed, the context menu structure is queried through the ActionTriggerContainer member that is part of the com.sun.star.ui.ContextMenuExecuteEvent structure. The com.sun.star.ui.ActionTriggerContainer service has an additional interface com.sun.star.lang.XMultiServiceFactory that creates [IDL:com.sun.star.ui.ActionTriggerContainer], ActionTriggerContainer and com.sun.star.ui.ActionTriggerSeparator objects. These objects are used to extend a context menu.

The com.sun.star.lang.XMultiServiceFactory implementation of the ActionTriggerContainer implementation supports the following strings:

String Object
"com.sun.star.ui.ActionTrigger" Creates a normal menu entry.
"com.sun.star.ui.ActionTriggerContainer" Creates an empty sub menu1 .
"com.sun.star.ui.ActionTriggerSeparator" Creates an unspecified separator2 .

1 A sub menu cannot exist by itself. It has to be inserted into a com.sun.star.ui.ActionTrigger !

2 The separator has no special type. It is the responsibility of the concrete implementation to render an unspecified separator.

Finishing Interception

Every interceptor that is called directs the controller how it continues after the call returns. The enumeration com.sun.star.ui.ContextMenuInterceptorAction defines the possible return values.

Values of com.sun.star.ui.ContextMenuInterceptorAction
IGNORED Called object has ignored the call. The next registered com.sun.star.ui.XContextMenuInterceptor should be notified.
CANCELLED The context menu must not be executed. No remaining interceptor will be called.
EXECUTE_MODIFIED The context menu has been modified and should be executed without notifying the next registered com.sun.star.ui.XContextMenuInterceptor.
CONTINUE_MODIFIED The context menu was modified by the called object. The next registered com.sun.star.ui.XContextMenuInterceptor should be notified.

The following example shows a context menu interceptor that adds a a sub menu to a menu that has been intercepted at a controller, where this com.sun.star.ui.XContextMenuInterceptor has been registered. This sub menu is inserted into the context menu at the topmost position. It provides help functions to the user that are reachable through the menu Help.

import com.sun.star.ui.*;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.beans.XPropertySet;
import com.sun.star.container.XIndexContainer;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.Exception;
import com.sun.star.beans.UnknownPropertyException;
import com.sun.star.lang.IllegalArgumentException;
 
 
public class ContextMenuInterceptor implements XContextMenuInterceptor {
 
  public ContextMenuInterceptorAction notifyContextMenuExecute( 
  com.sun.star.ui.ContextMenuExecuteEvent aEvent ) throws RuntimeException {
 
    try {
      // Retrieve context menu container and query for service factory to
      // create sub menus, menu entries and separators
      com.sun.star.container.XIndexContainer xContextMenu = aEvent.ActionTriggerContainer;
      com.sun.star.lang.XMultiServiceFactory xMenuElementFactory = 
          (com.sun.star.lang.XMultiServiceFactory)UnoRuntime.queryInterface(
              com.sun.star.lang.XMultiServiceFactory.class, xContextMenu );
      if ( xMenuElementFactory != null ) {
          // create root menu entry for sub menu and sub menu
          com.sun.star.beans.XPropertySet xRootMenuEntry =
              (XPropertySet)UnoRuntime.queryInterface(
                  com.sun.star.beans.XPropertySet.class, 
                  xMenuElementFactory.createInstance ( "com.sun.star.ui.ActionTrigger" ));
 
              // create a line separator for our new help sub menu
              com.sun.star.beans.XPropertySet xSeparator = 
              (com.sun.star.beans.XPropertySet)UnoRuntime.queryInterface( 
                  com.sun.star.beans.XPropertySet.class, 
                  xMenuElementFactory.createInstance( "com.sun.star.ui.ActionTriggerSeparator" ) );
 
              Short aSeparatorType = new Short( ActionTriggerSeparatorType.LINE );
              xSeparator.setPropertyValue( "SeparatorType", (Object)aSeparatorType );
 
              // query sub menu for index container to get access
              com.sun.star.container.XIndexContainer xSubMenuContainer =
              (com.sun.star.container.XIndexContainer)UnoRuntime.queryInterface( 
                  com.sun.star.container.XIndexContainer.class,
                      xMenuElementFactory.createInstance( 
              "com.sun.star.ui.ActionTriggerContainer" ));
 
              // intialize root menu entry "Help"
              xRootMenuEntry.setPropertyValue( "Text", new String( "Help" ));
              xRootMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5410" ));
              xRootMenuEntry.setPropertyValue( "HelpURL", new String( "5410" ));
              xRootMenuEntry.setPropertyValue( "SubContainer", (Object)xSubMenuContainer );
 
              // create menu entries for the new sub menu
 
              // intialize help/content menu entry
              // entry "Content"
              XPropertySet xMenuEntry = (XPropertySet)UnoRuntime.queryInterface( 
                  XPropertySet.class, xMenuElementFactory.createInstance (
                      "com.sun.star.ui.ActionTrigger" ));
 
              xMenuEntry.setPropertyValue( "Text", new String( "Content" ));
              xMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5401" ));
              xMenuEntry.setPropertyValue( "HelpURL", new String( "5401" ));
 
              // insert menu entry to sub menu
              xSubMenuContainer.insertByIndex ( 0, (Object)xMenuEntry );
 
              // intialize help/help agent
              // entry "Help Agent"
              xMenuEntry = (com.sun.star.beans.XPropertySet)UnoRuntime.queryInterface( 
                  com.sun.star.beans.XPropertySet.class,
                      xMenuElementFactory.createInstance (
                          "com.sun.star.ui.ActionTrigger" ));
              xMenuEntry.setPropertyValue( "Text", new String( "Help Agent" ));
              xMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5962" ));
              xMenuEntry.setPropertyValue( "HelpURL", new String( "5962" ));
 
              // insert menu entry to sub menu
              xSubMenuContainer.insertByIndex( 1, (Object)xMenuEntry );
 
              // intialize help/tips
              // entry "Tips"
              xMenuEntry = (com.sun.star.beans.XPropertySet)UnoRuntime.queryInterface( 
                  com.sun.star.beans.XPropertySet.class,
                      xMenuElementFactory.createInstance( 
                          "com.sun.star.ui.ActionTrigger" ));
              xMenuEntry.setPropertyValue( "Text", new String( "Tips" ));
              xMenuEntry.setPropertyValue( "CommandURL", new String( "slot:5404" ));
              xMenuEntry.setPropertyValue( "HelpURL", new String( "5404" ));
 
              // insert menu entry to sub menu
              xSubMenuContainer.insertByIndex ( 2, (Object)xMenuEntry );
 
              // add separator into the given context menu
              xContextMenu.insertByIndex ( 0, (Object)xSeparator );
 
              // add new sub menu into the given context menu
              xContextMenu.insertByIndex ( 0, (Object)xRootMenuEntry );
 
              // The controller should execute the modified context menu and stop notifying other
              // interceptors.
              return com.sun.star.ui.ContextMenuInterceptorAction.EXECUTE_MODIFIED ;
          }
      }
      catch ( com.sun.star.beans.UnknownPropertyException ex ) {
          // do something useful
          // we used a unknown property 
      }
      catch ( com.sun.star.lang.IndexOutOfBoundsException ex ) {
          // do something useful
          // we used an invalid index for accessing a container
      }
      catch ( com.sun.star.uno.Exception ex ) {
          // something strange has happend!
      }
      catch ( java.lang.Throwable ex ) {
          // catch java exceptions - do something useful
      }
 
      return com.sun.star.ui.ContextMenuInterceptorAction.IGNORED;
  }
}
Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages