Writer/Smart Tags

From Apache OpenOffice Wiki
< Writer
Revision as of 14:41, 30 January 2007 by Jlechner (Talk | contribs)

Jump to: navigation, search

Introduction

Smart Tags were introduced in Microsoft Office XP and allow to recognize words that have a special meaning to the user and furthermore allow to execute certain actions for a recognized word. Experimental support for smart tags is included in the OpenOffice.org 2.2 release. In order to use this functionality, external UNO components (so-called smart tag libraries) have to be installed. This document explains how to write own smart tag libraries and how to integrate them into your installation of OpenOffice.org. The necessary interfaces for smart tag libraries currently have the status "unpublished" because they are still work in progress. So be aware that you may have to modify the code of your components if you want to use them with the next release of OpenOffice.org. Smart tag extensions (aka packages) must set the version dependency to 2.2.

Basic functionality of Smart Tags

Basically smart tags UNO components consist of two parts:

  • A recognizer
  • An action library.

If a recognizer component is installed in openoffice its recognize() method will be invoked by Openoffice.org Writer for every word found in the opened document. The recognize method then decides if the word has some specific meaning and returns true or false. Recognized words will be underlined violet in Openoffice.org Writer. If the control key is pressed and the mouse is moved over a underlined word the mouse cursor changes to a hand. When you click on the word with the left mouse button (still holding the control key) a context menu is opened. This is where action libraries come into play. An action library provides actions for recognized words. The captions of available actions are displayed in the context menu. When you click on one entry in the context menu the invokeAction() method of the action library component is called. This method subsequently executes the appropriate action (a simple action might be opening some URL in the browser).

Here is a sceenshot of an example smart tag component which recognizes document property keywords like author, subject, title etc. There is only one action defined. This action inserts the value of the recognized document property into the text document.

Stag screenshot1.gif

The Services

For supporting external smart tag components two new service definitions have been added to offapi:

  • com.sun.star.smarttags.SmartTagRecognizer
  • com.sun.star.smarttags.SmartTagAction

The SmartTagRecognizer service exports the interface XSmartTagRecognizer. Accordingly the service SmartTagAction exports the interface XSmartTagAction.

The Interfaces

Recognizer and action components are collections of smart tags. This means a smart tag component can contain recognizers or actions for different topics, e.g. a smart tag for recognizing email addresses and another one for recognizing dates and times can be packed into one single recognizer component (accordingly actions for both the email and the date smart tag can be combined in one action component).

Next sections explain the interfaces of recognizer and action components in detail.

The Recognizer Interface

For a look at the recognizer interface definition go to this page.

This interface is comprised of 7 methods which have to be implemented by a recognizer component.


string getName()
Arguments None.
Return Value Retuns the name of the Regonizer component.


string getDescription()
Arguments None.
Return Value Returns a textual description of the recognizer component.


long getSmartTagCount()
Arguments None.
Return Value The return value indicates how many smart tags are included in the recognizer component.


string getSmartTagName()
Arguments sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
Return Value Returns the name of one specific smart tag in this recognizer. This name is needed to associate the recognizer part of a smart tag with the action part. Thus there mustn't be two recognizer components installed that include a recognizer withthe same name.


string getSmartTagCaption()
Arguments sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
XController xController: The reference to the controller of the current document.
Return Value Returns the caption of one specific smart tag in this recognizer. The caption will be display by Openoffice.org Writer in the context menu.


boolean Recognize()
Arguments OUString aWord: Word that shall be checked by smarttag.
sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
XController xController: The reference to the controller of the current document.
Return Value Returns true if the passed word has a special meaning to the smart tag. Otherwise false is returned.


void initialize()
Description This method is used for initializing the recognizer component and is currently needed by Writer to use xml based smart tags (see example below). For your own smart tag components you can implement this method with an empty body.
Arguments sequence<any> aArguments: Initialization arguments
Return Value None.


The Action Library Interface

For a look at the action library interface definition go to this page.


string getName()
Arguments None.
Return Value Returns the name of the action library component.


string getDescription()
Arguments None.
Return Value Returns a textual description of the action library component.


long getSmartTagCount()
Arguments None.
Return Value The return value indicates how many smart tags are included in the action library component.


string getSmartTagName()
Arguments sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
Return Value Returns the name of one specific smart tag in this action component. This name is needed to associate the actions of a smart tag with the corresponding recognizer.


string getSmartTagCaption()
Arguments sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
XController xController: The reference to the controller of the current document.
Return Value Returns the caption of one specific smart tag in this action component. The caption will be display by Openoffice.org Writer in the context menu.


long getActionCount()
Arguments sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
XController xController: The reference to the controller of the current document.
Return Value Returns the number of available actions of one specific smart tag in this action library.


string getActionCaption()
Arguments sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
sal_Int32 nActionIndex: Index of the wanted action. Value needs to be between 0 and the number of actions available for the specified smart tag (nSmartTagIndex).
XController xController: The reference to the controller of the current document.
Return Value This function returns the caption of an action of one specific smart tag in this action library.


void invokeAction()
Arguments sal_Int32 nSmartTagIndex: Index of the wanted smart tag. Value needs to be between 0 and the number of smart tags available (exclusively).
sal_Int32 nActionIndex: Index of the wanted action. Value needs to be between 0 and the number of actions available for the specified smart tag (nSmartTagIndex).
string aWord: Word that was recognized as actionable.
XController xController: The reference to the controller of the current document.
Return Value None.


void initialize()
Description This method is used for initializing the action library component and is currently needed by Writer to use xml based smart tags (see example below). For your own smart tag components you can implement this method with an empty body.
Arguments sequence<any> aArguments: Initialization arguments
Return Value None.


Example implementations

Document Properties Smart Tag

Recognizer

As described above the recognizer matches document property keywords like title, author, description etc. The matching of scanned words of the current document is done by the Recognize() method as you can see below. This recognizer component contains just one smart tag type. Thus the getSmartTagCount() method returns 1. The name of this recognizer ("example_smarttag") is returned by the method getSmartTagName(). Of course this component needs to suport the SmartTagRecognizer service. Therefore the getSupportedServiceNames() method has to return a sequence containing the appropriate service name ("com.sun.star.smarttags.SmartTagRecognizer"). Each UNO component needs an unique implementation name. With different implementation names multiple recognizer components can be installed on your system in parallel. The implementation name of this example is "org.openoffice.comp.smarttags.recognizer.example".


[cpp] // Listing: Class definition of ExampleRecognizer

class ExampleRecognizer : public ::cppu::WeakImplHelper2<

 com::sun::star::smarttags::XSmartTagRecognizer, XServiceInfo> {

private:

 OUString aName;
 OUString aDescription;
 Reference< XMultiServiceFactory > xServiceManager;

public:

 ExampleRecognizer(Reference< XMultiServiceFactory > _xServiceManager);
 ~ExampleRecognizer();
 // XSmartTagRecognizer
 virtual inline OUString SAL_CALL getName() throw (RuntimeException) { return aName; }
 virtual OUString SAL_CALL getDescription() throw (RuntimeException) { return aDescription; }
 virtual sal_Int32 SAL_CALL getSmartTagCount() throw (RuntimeException) { return 1; }  
 virtual OUString SAL_CALL getSmartTagName (sal_Int32 nSmartTagIndex) throw (RuntimeException); 
 virtual OUString SAL_CALL getSmartTagCaption( sal_Int32 nSmartTagIndex,

const Reference< XController >& xController ) throw (RuntimeException);

 virtual sal_Bool SAL_CALL Recognize(const OUString& aWord, sal_Int32 nSmartTagIndex,

const Reference< XController >& xController) throw (RuntimeException);

 virtual void SAL_CALL initialize( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& aArguments ) 
            throw (RuntimeException);
 ////////////////////////////////////////////////////////////
 // Service specific part
 ////////////////////////////////////////////////////////////
 // XServiceInfo
 virtual OUString SAL_CALL getImplementationName() throw(RuntimeException);
 virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) 
   throw(RuntimeException);
 virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() 
   throw(RuntimeException);
 static inline OUString getImplementationName_Static() throw();
 static Sequence< OUString > getSupportedServiceNames_Static() throw();

};


[cpp] // Listing: Method implementation of ExampleRecognizer

  1. define C2U(cChar) OUString::createFromAscii(cChar)
  1. define IMPLNAME "org.openoffice.comp.smarttags.recognizer.example"
  2. define SN_SMARTTAG_RECOGNIZER "com.sun.star.smarttags.SmartTagRecognizer"
  3. define SMARTTAG_NAME C2U("example_smarttag")

ExampleRecognizer::ExampleRecognizer(Reference< XMultiServiceFactory > _xServiceManager) {

 xServiceManager = _xServiceManager;

}

OUString SAL_CALL ExampleRecognizer::getSmartTagName( sal_Int32 nSmartTagIndex) throw (RuntimeException) {

 if (nSmartTagIndex == 0)
   return SMARTTAG_NAME;
 else
   return C2U("");

}

OUString SAL_CALL ExampleRecognizer::getSmartTagCaption( sal_Int32 nSmartTagIndex, const Reference< XController >& xController) throw (RuntimeException) {

 return C2U("Document Properties");

}

sal_Bool SAL_CALL ExampleRecognizer::Recognize(const OUString& aWord, sal_Int32 nSmartTagIndex, const Reference< XController >& xController) throw (RuntimeException) {

 if (nSmartTagIndex !=  0) return sal_False;
 if (aWord.equalsIgnoreAsciiCase(C2U("Title"))) return sal_True;
 if (aWord.equalsIgnoreAsciiCase(C2U("Author"))) return sal_True;
 if (aWord.equalsIgnoreAsciiCase(C2U("Keywords"))) return sal_True;
 if (aWord.equalsIgnoreAsciiCase(C2U("Description"))) return sal_True;
 return sal_False;

}

void ExampleRecognizer::initialize( const Sequence< Any >& aArguments ) throw (RuntimeException) {

 // empty

}

/////////////////////////////////////////////////////////////////////////// // Service specific part ///////////////////////////////////////////////////////////////////////////

OUString SAL_CALL ExampleRecognizer::getImplementationName() throw(RuntimeException) {

 return getImplementationName_Static();

}

inline OUString ExampleRecognizer::getImplementationName_Static() throw() {

 return C2U(IMPLNAME);

}

sal_Bool SAL_CALL ExampleRecognizer::supportsService( const OUString& ServiceName )

 throw(RuntimeException)

{

 Sequence< OUString > aSNL = getSupportedServiceNames();
 const OUString * pArray = aSNL.getConstArray();
 for( INT32 i = 0; i < aSNL.getLength(); i++ )
   if( pArray[i] == ServiceName )
     return TRUE;
 return FALSE;

}

Sequence< OUString > SAL_CALL ExampleRecognizer::getSupportedServiceNames()

 throw(RuntimeException)

{

 return getSupportedServiceNames_Static();

}

Sequence< OUString > ExampleRecognizer::getSupportedServiceNames_Static()

 throw()

{

 Sequence< OUString > aSNS( 1 );
 aSNS.getArray()[0] = C2U( SN_SMARTTAG_RECOGNIZER );
 return aSNS;

}

extern "C" sal_Bool SAL_CALL component_writeInfo(void * /*pServiceManager*/, registry::XRegistryKey * pRegistryKey ) {

 try {
   String aImpl( '/' );
   aImpl += ExampleRecognizer::getImplementationName_Static().getStr();
   aImpl.AppendAscii( "/UNO/SERVICES" );
   Reference< registry::XRegistryKey > xNewKey =
     pRegistryKey->createKey( aImpl );
   Sequence< OUString > aServices =
     ExampleRecognizer::getSupportedServiceNames_Static();
   for( INT32 i = 0; i < aServices.getLength(); i++ )
     xNewKey->createKey( aServices.getConstArray()[i] );
   
   return sal_True;
 }
 catch(Exception &) {
   return sal_False;
 }

}

Reference< XInterface > SAL_CALL ExampleRecognizer_create( const Reference< XMultiServiceFactory > & xMgr ) {

 Reference<XInterface> ref (static_cast<XSmartTagRecognizer*>(new ExampleRecognizer(xMgr)));
 return ref;

}

extern "C" void * SAL_CALL component_getFactory(

   const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )

{

   void * pRet = 0;
   // which implementation is required?
   if (pServiceManager && ( !ExampleRecognizer::getImplementationName_Static().compareToAscii( pImplName ) ) )
   {
       Reference< XSingleServiceFactory > xFactory(
          cppu::createSingleFactory( // helper function from cppuhelper lib
          reinterpret_cast< XMultiServiceFactory * >( pServiceManager ),
          ExampleRecognizer::getImplementationName_Static(),
          ExampleRecognizer_create,
          ExampleRecognizer::getSupportedServiceNames_Static() ) );
       if (xFactory.is())
       {
          xFactory->acquire();
          pRet = xFactory.get();
       }
   }
   return pRet;

}


Action Library

Implementing an action library is quite similar to implementing the recognizer. Of course you have to support the action service instead of the recongizer service. Thus the appropriate service name is "com.sun.star.smarttags.SmartTagAction". The implementation name of the action component has to be different too. In this expample the name "org.openoffice.comp.smarttags.action.example" was chosen. But the name of the smart tag this components provides actions for has to be the same name used in the recognizer component (i.e. "example_smarttag"). Since this component just provides actions for one smart tag type the getSmartTagCount() method returns 1. And for this single smart tag the component just provides one action, as you can see in the invokeAction() method. This action retrieves the value of document property which was recognized and inserts it just behind the recognized word.

[cpp] // Listing: Class definition of ExampleActionlib

class ExampleActionlib : public ::cppu::WeakImplHelper2<

 com::sun::star::smarttags::XSmartTagAction, XServiceInfo> {

private:

 OUString aName;
 OUString aDescription;
 Reference< XMultiServiceFactory > xServiceManager;

public:

 ExampleActionlib(Reference< XMultiServiceFactory > _xServiceManager);
 ~ExampleActionlib();
 
 // XSmartTagAction
 virtual inline OUString SAL_CALL getName() throw (RuntimeException) { return aName; }
 virtual OUString SAL_CALL getDescription() throw (RuntimeException) { return aDescription; }
 virtual sal_Int32 SAL_CALL getSmartTagCount() throw (RuntimeException) { return 1; }  
 virtual OUString SAL_CALL getSmartTagName( sal_Int32 nSmartTagIndex ) throw (RuntimeException);
 virtual OUString SAL_CALL getSmartTagCaption( sal_Int32 nSmartTagIndex,

const Reference< XController >& xController ) throw (RuntimeException);

 virtual sal_Int32 SAL_CALL getActionCount( sal_Int32 nSmartTagIndex,

const Reference< XController >& xController ) throw (RuntimeException);

 virtual OUString SAL_CALL getActionCaption( sal_Int32 nSmartTagIndex, sal_Int32 nActionIndex,

const Reference< XController >& xController ) throw (RuntimeException);

 virtual void SAL_CALL invokeAction (sal_Int32 nSmartTagIndex, sal_Int32 nActionIndex, 

const Reference< XTextRange >& xRange, const Reference< XController >& xController ) throw (RuntimeException);

 virtual void SAL_CALL initialize( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& aArguments ) 
            throw (RuntimeException);
 ////////////////////////////////////////////////////////////
 // Service specific part
 ////////////////////////////////////////////////////////////
 // XServiceInfo
 virtual OUString SAL_CALL getImplementationName() throw(RuntimeException);
 virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) 
   throw(RuntimeException);
 virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() 
   throw(RuntimeException);
 static inline OUString getImplementationName_Static() throw();
 static Sequence< OUString > getSupportedServiceNames_Static() throw();

};


[cpp] // Listing: Method implementation of ExampleActionlib

  1. define C2U(cChar) OUString::createFromAscii(cChar)
  1. define IMPLNAME "org.openoffice.comp.smarttags.action.example"
  2. define SN_SMARTTAG_ACTION "com.sun.star.smarttags.SmartTagAction"
  3. define SMARTTAG_NAME C2U("example_smarttag")

ExampleActionlib::ExampleActionlib(Reference< XMultiServiceFactory > _xServiceManager) {

 xServiceManager = _xServiceManager;

}

OUString SAL_CALL ExampleActionlib::getSmartTagName( sal_Int32 nSmartTagIndex) throw (RuntimeException) {

 if (nSmartTagIndex == 0)
   return SMARTTAG_NAME;
 else
   return C2U("");

}

OUString SAL_CALL ExampleActionlib::getSmartTagCaption( sal_Int32 nSmartTagIndex, const Reference< XController >& xController) throw (RuntimeException) {

 return C2U("Document Properties");

}

sal_Int32 SAL_CALL ExampleActionlib::getActionCount(sal_Int32 nSmartTagIndex, const Reference< XController >& xController) throw (RuntimeException) {

 if (nSmartTagIndex == 0) 
   return 1;
 else
   return 0;

}

OUString SAL_CALL ExampleActionlib::getActionCaption(sal_Int32 nSmartTagIndex, sal_Int32 nActionIndex, const Reference< XController >& xController) throw (RuntimeException) {

 if (nSmartTagIndex == 0) {
   switch (nActionIndex) {
   case 0:
     return C2U("Insert document property value");
     break;
   default:
     break;
   }
 }
 return C2U("");

}

void SAL_CALL ExampleActionlib::invokeAction(sal_Int32 nSmartTagIndex, sal_Int32 nActionIndex, const Reference< XTextRange >& xRange, const Reference< XController >& xController) throw (RuntimeException) {

 if (!xController.is()) return;
 Reference<XModel> xModel = xController->getModel();
 if (!xModel.is()) return;
 Reference<XTextDocument> xTextDocument (xModel, UNO_QUERY);
 if (!xTextDocument.is()) return;
 if (nSmartTagIndex == 0) {
   if (nActionIndex == 0) {
     Reference<XDocumentInfoSupplier> xDocInfoSupplier (xTextDocument, UNO_QUERY);
     if(!xDocInfoSupplier.is()) return;
     Reference<XDocumentInfo> xDocInfo = xDocInfoSupplier->getDocumentInfo();
     if(!xDocInfo.is()) return;
     Reference<XPropertySet> xProps (xDocInfo, UNO_QUERY);
     if (!xProps.is()) return;
     Any a = xProps->getPropertyValue(xRange->getString());
     OUString s;
     a >>= s;
     if (s) {

Reference<XTextCursor> xCursor = xRange->getText()->createTextCursorByRange(xRange->getEnd()); // check if colon follows word xCursor->goRight(1, sal_True); if (xCursor->getString().indexOf(C2U(":")) != -1) { xTextDocument->getText()->insertString(xCursor->getEnd(), C2U(" ") + s, sal_False); } else { xTextDocument->getText()->insertString(xRange->getEnd(), C2U(": ") + s, sal_False); }

     }   
   }   
 }

}

void ExampleActionlib::initialize( const Sequence< Any >& aArguments ) throw (RuntimeException) {

 // empty

}

/////////////////////////////////////////////////////////////////////////// // Service specific part ///////////////////////////////////////////////////////////////////////////

OUString SAL_CALL ExampleActionlib::getImplementationName() throw(RuntimeException) {

 return getImplementationName_Static();

}

inline OUString ExampleActionlib::getImplementationName_Static() throw() {

 return C2U(IMPLNAME);

}

sal_Bool SAL_CALL ExampleActionlib::supportsService( const OUString& ServiceName )

 throw(RuntimeException)

{

 Sequence< OUString > aSNL = getSupportedServiceNames();
 const OUString * pArray = aSNL.getConstArray();
 for( INT32 i = 0; i < aSNL.getLength(); i++ )
   if( pArray[i] == ServiceName )
     return TRUE;
 return FALSE;

}

Sequence< OUString > SAL_CALL ExampleActionlib::getSupportedServiceNames()

 throw(RuntimeException)

{

 return getSupportedServiceNames_Static();

}

Sequence< OUString > ExampleActionlib::getSupportedServiceNames_Static()

 throw()

{

 Sequence< OUString > aSNS( 1 );
 aSNS.getArray()[0] = C2U( SN_SMARTTAG_ACTION );
 return aSNS;

}

extern "C" sal_Bool SAL_CALL component_writeInfo(void * /*pServiceManager*/, registry::XRegistryKey * pRegistryKey ) {

 try {
   String aImpl( '/' );
   aImpl += ExampleActionlib::getImplementationName_Static().getStr();
   aImpl.AppendAscii( "/UNO/SERVICES" );
   Reference< registry::XRegistryKey > xNewKey =
     pRegistryKey->createKey( aImpl );
   Sequence< OUString > aServices =
     ExampleActionlib::getSupportedServiceNames_Static();
   for( INT32 i = 0; i < aServices.getLength(); i++ )
     xNewKey->createKey( aServices.getConstArray()[i] );
   
   return sal_True;
 }
 catch(Exception &) {
   return sal_False;
 }

}

Reference< XInterface > SAL_CALL ExampleActionlib_create( const Reference< XMultiServiceFactory > & xMgr ) {

 Reference<XInterface> ref (static_cast<XSmartTagAction*>(new ExampleActionlib(xMgr)));
 return ref;

}

extern "C" void * SAL_CALL component_getFactory(

   const sal_Char * pImplName, void * pServiceManager, void * pRegistryKey )

{

   void * pRet = 0;
   // which implementation is required?
   if (pServiceManager && ( !ExampleActionlib::getImplementationName_Static().compareToAscii( pImplName ) ) )
   {
       Reference< XSingleServiceFactory > xFactory(
          cppu::createSingleFactory( // helper function from cppuhelper lib
          reinterpret_cast< XMultiServiceFactory * >( pServiceManager ),
          ExampleActionlib::getImplementationName_Static(),
          ExampleActionlib_create,
          ExampleActionlib::getSupportedServiceNames_Static() ) );
       if (xFactory.is())
       {
          xFactory->acquire();
          pRet = xFactory.get();
       }
   }
   return pRet;

}


Xml based Smart Tags

In this section recognizer and action components for xml based smart tags are presented. In some cases you just want to match a list of words or regular expressions and open some URL associated with these words.... TODO!


See also

Personal tools