Disable Commands

From Apache OpenOffice Wiki
Jump to: navigation, search



In Apache OpenOffice, there may be situations where functions should be disabled to prevent users from changing or destroying documents inadvertently. Apache OpenOffice maintains a list of disabled commands that can be maintained by users and developers through the configuration API.

A command request can be created by any object, but in most cases, user interface objects create these requests. Consider, for instance, a toolbox where different functions acting on the office component are presented as buttons. Once a button is clicked, the desired functionality should be executed. If the code assigned to the button is provided with a suitable command URL, the dispatch framework can handle the user action by creating the request and finding a component that can handle it.

The dispatch framework works with the design pattern chain of responsibility: everything a component needs to know if it wants to execute a request is the last link in a chain of objects capable of executing requests. If this object gets the request, it checks whether it can handle it or otherwise passes it to the next chain member until the request is executed or the end of the chain is reached. The disable commands implementation is the first chain member and can therefore work as a wall for all disabled commands. They are not be sent to the next chain member, and disappear.

The illustration below shows how the disable commands feature affects the normal command application flow.

DisableCommands ApplicationFlow.png


Documentation caution.png Since the disable commands implementation is the first part in the dispatch chain, there is no way to circumvent it. The disabled command must be removed from the list, otherwise it remains disabled.

Configuration

The disable commands feature uses the configuration branch org.openoffice.Office.Commands to read which commands should be disabled. The following schema applies:

  <?xml version='1.0' encoding='UTF-8'?>
  <oor:component-schema oor:name="Commands" 
                        oor:package="org.openoffice.Office" 
                        xml:lang="en-US" 
                        xmlns:oor="http://openoffice.org/2001/registry" 
                        xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <templates>
          <group oor:name="CommandType">
              <prop oor:name="Command" oor:type="xs:string"/>
          </group>
      </templates>
      <component>
          <group oor:name="Execute">
              <set oor:name="Disabled" oor:node-type="CommandType"/>
          </group>
      </component>
  </oor:component-schema>

The configuration schema for disabled commands is very simple. The org.openoffice.Office.Commands branch has a group called Execute. This group has only one set called Disabled. The Disabled set supports nodes of the type CommandType. The following table describes the supported properties of CommandType.

Properties of the CommandType group
oor:component-data string. It must be unique inside the Disabled set, but has no additional meaning for the implementation of the disable commands feature. Use a consecutive numbering scheme; even numbers are allowed.
Command string. This is the command name with the preceding protocol. That means the command URL .uno:Open (which shows the File – Open dialog) must be written as Open.

The valid commands can be found in the document Index of Command Names in the Documentation section of the framework project on the Apache OpenOffice web page. The Apache OpenOffice SDK also includes the latest list of command names. Additional information regarding Command names is available in: OpenOffice.org 3.x Commands

The example below shows a configuration file that disables the commands for File – Open, Edit – Select All, Help – About Apache OpenOffice and File – Exit.

  <?xml version="1.0" encoding="UTF-8" ?>
  <oor:component-data oor:name="Commands" 
                      oor:package="org.openoffice.Office" 
                      xmlns:oor="http://openoffice.org/2001/registry" 
                      xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <node oor:name="Execute">
          <node oor:name="Disabled">
              <node oor:name="com.mycompany.a-chosen-unique-name.m1" oor:op="replace">
                  <prop oor:name="Command">
                      <value>Open</value>
                  </prop>
              </node>
              <node oor:name="com.mycompany.a-chosen-unique-name.m2" oor:op="replace">
                  <prop oor:name="Command">
                      <value>SelectAll</value>
                  </prop>
              </node>
              <node oor:name="com.mycompany.a-chosen-unique-name.m3" oor:op="replace">
                  <prop oor:name="Command">
                      <value>About</value>
                  </prop>
              </node>
              <node oor:name="com.mycompany.a-chosen-unique-name.m4" oor:op="replace">
                  <prop oor:name="Command">
                      <value>Quit</value>
                  </prop>
              </node>
          </node>
      </node>
  </oor:component-data>

A few lines on the node name 'com.mycompany.a-chosen-unique-name.m1' in the xml example above.

The node name is chosen in order to be unique in the 'Disable' data set, and is given as a suggestion.

You can use whatever name you like, it has no additional meaning for the implementation of the disable commands feature.

A good idea would be to use a consecutive numbering at the name end, the name must be unique.

Disabling Commands at Runtime

The following code example first removes all commands that were defined in the user layer of the configuration branch org.openoffice.Office.Commands as having a defined starting point. Then it checks if it can get dispatch objects for some pre-defined commands.Then the example disables these commands and tries to get dispatch objects for them again. At the end, the code removes the disabled commands again, otherwise Apache OpenOffice would not be fully usable any longer.
  import com.sun.star.bridge.XUnoUrlResolver;
  import com.sun.star.uno.UnoRuntime;
  import com.sun.star.uno.XComponentContext;
  import com.sun.star.lang.XMultiComponentFactory;
  import com.sun.star.beans.XPropertySet;
  import com.sun.star.beans.PropertyValue;
  import com.sun.star.lang.XMultiServiceFactory;
  import com.sun.star.lang.XSingleServiceFactory;
  import com.sun.star.util.XURLTransformer;
  import com.sun.star.frame.XDesktop;
  
  import com.sun.star.beans.UnknownPropertyException;
  
  /*
   *  Provides example code how to enable/disable
   *  commands.
   */
  public class DisableCommandsTest extends java.lang.Object {
  
      /*
       *  A list of command names
       */
      final static private String[] aCommandURLTestSet =
      {
          "Open",
          "About",
          "SelectAll",
          "Quit",
      };
  
      private static XComponentContext xRemoteContext = null;
      private static XMultiComponentFactory xRemoteServiceManager = null;
      private static XURLTransformer xTransformer = null;
      private static XMultiServiceFactory xConfigProvider = null;
      
      /*
       *  @param args the command line arguments
       */
      public static void main(String[] args) {
      
          try {
              // connect
              XComponentContext xLocalContext =
              com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);
              XMultiComponentFactory xLocalServiceManager = xLocalContext.getServiceManager();
              Object urlResolver = xLocalServiceManager.createInstanceWithContext(
                  "com.sun.star.bridge.UnoUrlResolver", xLocalContext);
              XUnoUrlResolver xUnoUrlResolver = (XUnoUrlResolver) UnoRuntime.queryInterface( 
                  XUnoUrlResolver.class, urlResolver );
              Object initialObject = xUnoUrlResolver.resolve( 
              "uno:socket,host=localhost,port=2083;urp;StarOffice.ServiceManager");
              XPropertySet xPropertySet = (XPropertySet)UnoRuntime.queryInterface(
                  XPropertySet.class, initialObject);
              Object context = xPropertySet.getPropertyValue("DefaultContext");
              xRemoteContext = (XComponentContext)UnoRuntime.queryInterface(
                  XComponentContext.class, context);
              xRemoteServiceManager = xRemoteContext.getServiceManager();
              Object transformer = xRemoteServiceManager.createInstanceWithContext(
                  "com.sun.star.util.URLTransformer", xRemoteContext);
              xTransformer = (com.sun.star.util.XURLTransformer)UnoRuntime.queryInterface(
                  com.sun.star.util.XURLTransformer.class, transformer);
      
              Object configProvider = xRemoteServiceManager.createInstanceWithContext(
                  "com.sun.star.configuration.ConfigurationProvider", xRemoteContext);
              xConfigProvider = (com.sun.star.lang.XMultiServiceFactory)UnoRuntime.queryInterface(
                  com.sun.star.lang.XMultiServiceFactory.class, configProvider);
    
              // First we need a defined starting point. So we have to remove
              // all commands from the disabled set!
              enableCommands();
  
              // Check if the commands are usable
              testCommands(false);
          
              // Disable the commands
              disableCommands();
          
              // Now the commands should not be usable anymore
              testCommands(true);
  
              // Remove disable commands to make Office usable again
              enableCommands();
          }
          catch (java.lang.Exception e){
              e.printStackTrace();
          } 
          finally {
              System.exit(0);
          }
      }
  
      /**
       *  Test the commands that we enabled/disabled
       */
      private static void testCommands(boolean bDisabledCmds) throws com.sun.star.uno.Exception {
          // We need the desktop to get access to the current frame
          Object desktop = xRemoteServiceManager.createInstanceWithContext(
              "com.sun.star.frame.Desktop", xRemoteContext );
          com.sun.star.frame.XDesktop xDesktop = (com.sun.star.frame.XDesktop)UnoRuntime.queryInterface(
              com.sun.star.frame.XDesktop.class, desktop);
          com.sun.star.frame.XFrame xFrame = xDesktop.getCurrentFrame();
          com.sun.star.frame.XDispatchProvider xDispatchProvider = null;
          if (xFrame != null) {
              // We have a frame. Now we need access to the dispatch provider.
              xDispatchProvider = (com.sun.star.frame.XDispatchProvider)UnoRuntime.queryInterface( 
                  com.sun.star.frame.XDispatchProvider.class, xFrame );
              if (xDispatchProvider != null) {
                  // As we have the dispatch provider we can now check if we get a dispatch
                  // object or not.
                  for (int n = 0; n < aCommandURLTestSet.length; n++) {
                      // Prepare the URL
                      com.sun.star.util.URL[] aURL = new com.sun.star.util.URL[1];
                      aURL[0] = new com.sun.star.util.URL();
                      com.sun.star.frame.XDispatch xDispatch = null;
    
                      aURL[0].Complete = ".uno:" + aCommandURLTestSet[n];
                      xTransformer.parseSmart(aURL, ".uno:");
    
                      // Try to get a dispatch object for our URL
                      xDispatch = xDispatchProvider.queryDispatch(aURL[0], "", 0);
    
                      if (xDispatch != null) {
                          if (bDisabledCmds)
                              System.out.println("Something is wrong, I got dispatch object for " 
                                  + aURL[0].Complete);
                          else
                              System.out.println("Ok, dispatch object for " + aURL[0].Complete);
                      }
                      else {
                          if (!bDisabledCmds)
                              System.out.println("Something is wrong, I cannot get dispatch   object for " 
                                  + aURL[0].Complete);
                          else
                              System.out.println("Ok, no dispatch object for " + aURL[0].Complete);
                      }
                      resetURL(aURL[0]);
                  }
              }
              else
                  System.out.println("Couldn't get XDispatchProvider from Frame!");
          }
          else
              System.out.println("Couldn't get current Frame from Desktop!");
      }
    
      /**
       *  Ensure that there are no disabled commands in the user layer. The
       *  implementation removes all commands from the disabled set!
       */
      private static void enableCommands() {
          // Set the root path for our configuration access
          com.sun.star.beans.PropertyValue[] lParams = new com.sun.star.beans.PropertyValue[1];
  
          lParams[0] = new com.sun.star.beans.PropertyValue();
          lParams[0].Name = "nodepath";
          lParams[0].Value = "/org.openoffice.Office.Commands/Execute/Disabled";
      
          try {
              // Create configuration update access to have write access to the configuration
              Object xAccess = xConfigProvider.createInstanceWithArguments( 
"com.sun.star.configuration.ConfigurationUpdateAccess", lParams);
  
              com.sun.star.container.XNameAccess xNameAccess = (com.sun.star.container.XNameAccess)
              UnoRuntime.queryInterface(com.sun.star.container.XNameAccess.class, xAccess);
              if (xNameAccess != null) {
                  // We need the XNameContainer interface to remove the nodes by name
                  com.sun.star.container.XNameContainer xNameContainer =
                      (com.sun.star.container.XNameContainer)
                  UnoRuntime.queryInterface(com.sun.star.container.XNameContainer.class, xAccess);
    
                  // Retrieves the names of all Disabled nodes
                  String[] aCommandsSeq = xNameAccess.getElementNames();
                  for (int n = 0; n < aCommandsSeq.length; n++) {
                      try {
                          // remove the node
                          xNameContainer.removeByName( aCommandsSeq[n]);
                      }
                      catch (com.sun.star.lang.WrappedTargetException e) {
                      }
                      catch (com.sun.star.container.NoSuchElementException e) {
                      }
                  }
              } 
    
              // Commit our changes
              com.sun.star.util.XChangesBatch xFlush =
              (com.sun.star.util.XChangesBatch)UnoRuntime.queryInterface(
                  com.sun.star.util.XChangesBatch.class, xAccess);
              xFlush.commitChanges();
          }    
          catch (com.sun.star.uno.Exception e) {
              System.out.println("Exception detected!");
              System.out.println(e);
          }
      }
    
      /**
       *  Disable all commands defined in the aCommandURLTestSet array
       */
      private static void disableCommands() {
      // Set the root path for our configuration access
      com.sun.star.beans.PropertyValue[] lParams = new com.sun.star.beans.PropertyValue[1];
      lParams[0] = new com.sun.star.beans.PropertyValue();
      lParams[0].Name = "nodepath";
      lParams[0].Value = "/org.openoffice.Office.Commands/Execute/Disabled";
  
      try {
          // Create configuration update access to have write access to the configuration
          Object xAccess = xConfigProvider.createInstanceWithArguments( 
          "com.sun.star.configuration.ConfigurationUpdateAccess", lParams);
    
          com.sun.star.lang.XSingleServiceFactory xSetElementFactory = 
          (com.sun.star.lang.XSingleServiceFactory)UnoRuntime.queryInterface(
              com.sun.star.lang.XSingleServiceFactory.class, xAccess);
          
          com.sun.star.container.XNameContainer xNameContainer =
          (com.sun.star.container.XNameContainer)UnoRuntime.queryInterface(
              com.sun.star.container.XNameContainer.class, xAccess );
  
          if (xSetElementFactory != null && xNameContainer != null) {
              Object[] aArgs = { };
    
              for (int i = 0; i < aCommandURLTestSet.length; i++) {
                  // Create the nodes with the XSingleServiceFactory of the configuration
                      Object xNewElement = xSetElementFactory.createInstanceWithArguments( aArgs );
                      if (xNewElement != null) {
                          // We have a new node. To set the properties of the node we need
                          // the XPropertySet interface.
                          com.sun.star.beans.XPropertySet xPropertySet = 
                          (com.sun.star.beans.XPropertySet)UnoRuntime.queryInterface( com.sun.star.beans.XPropertySet.class,
                              xNewElement );
  
                          if (xPropertySet != null) {
                              // Create a unique node name.
                              String aCmdNodeName = "Command-";
                              aCmdNodeName += i;
    
                              // Insert the node into the Disabled set
                              xPropertySet.setPropertyValue("Command", aCommandURLTestSet[i]);
                              xNameContainer.insertByName(aCmdNodeName, xNewElement);
                          }
                      }
                  }
    
                  // Commit our changes
                  com.sun.star.util.XChangesBatch xFlush = (com.sun.star.util.XChangesBatch)
                  UnoRuntime.queryInterface(com.sun.star.util.XChangesBatch.class, xAccess);
                  xFlush.commitChanges();
              } 
          }
          catch (com.sun.star.uno.Exception e) {
              System.out.println("Exception detected!");
              System.out.println(e);
          }
      } 
      
      /**
       *  reset URL so it can be reused
       *
       *  @param aURL
       *  the URL that should be reseted
       */
      private static void resetURL(com.sun.star.util.URL aURL) {
          aURL.Protocol = "";
          aURL.User = "";
          aURL.Password = "";
          aURL.Server = "";
          aURL.Port = 0;
          aURL.Path = "";
          aURL.Name = "";
          aURL.Arguments = "";
          aURL.Mark = "";
          aURL.Main = "";
          aURL.Complete = "";
      }
  }
Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages