Calc/Add-In/CompleteAddIn

From Apache OpenOffice Wiki
< Calc‎ | Add-In
Revision as of 17:44, 3 May 2006 by SergeMoutou (Talk | contribs)

Jump to: navigation, search

An other C++ add-in example

In this article under construction, a complete and simple add-in in C++ is presented.

Introduction

We start from an example of SDK slightly modified : <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent

This example contains two files but I only use one file (and one service). It is related to a scribtable component. An addin is slighty more complicated but we can use this example corresponding makefife to compile and install our addin.

IDL File

Here is the corresponding IDL file :

// IDL
#include <com/sun/star/uno/XInterface.idl>
#include <com/sun/star/lang/XInitialization.idl>
#include <com/sun/star/lang/XServiceName.idl>
#include <com/sun/star/lang/XLocalizable.idl>
#include <com/sun/star/sheet/XAddIn.idl>
module my_module
{
  interface XSomething : com::sun::star::uno::XInterface
{ // our four methods
  string methodOne( [in] string val );
  string methodTwo( [in] string val );
  long methodThree( [in] sequence< sequence< long > > aValList );
  sequence< sequence< long > > methodFour( [in] sequence< sequence< long > > aValList );
};
service MyService2
{
interface XSomething;
interface com::sun::star::lang::XInitialization;
interface com::sun::star::lang::XServiceName;
interface com::sun::star::sheet::XAddIn;
};
};

Four methods named methodOne, methodTwo, methodThree and methodFour can be seen in this IDL file and then will be implemented.

Implementing in C++ the corresponding four Methods

We first give the C++ code of the four methods presented in the previous IDL file.

The two first Methods

The two first methods are similar :

// C++
OUString MyService2Impl::methodOne( OUString const & str )
throw (RuntimeException)
{
  return OUString( RTL_CONSTASCII_USTRINGPARAM(
    "called methodOne() of MyService2 implementation: ") ) + m_arg + str;
}

OUString MyService2Impl::methodTwo( OUString const & str )throw (RuntimeException)
{
  return OUString( RTL_CONSTASCII_USTRINGPARAM(
    "called methodTwo() of MyService2 implementation: ") ) + m_arg + str;
}

They only take a string (from a OOoCalc Cell) and add a message and put all the message+string in the result cell.

The third Method

The third method is more complicated : it returns a value calculed from a cell range (the sum).

// C++
sal_Int32 MyService2Impl::methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
throw (RuntimeException)
{ sal_Int32 n1, n2;
  sal_Int32 nE1 = aValList.getLength();
  sal_Int32 nE2;
  sal_Int32 temp=0;
  for( n1 = 0 ; n1 < nE1 ; n1++ )
  {
    const Sequence< sal_Int32 > rList = aValList[ n1 ];
    nE2 = rList.getLength();
    const sal_Int32* pList = rList.getConstArray();
    for( n2 = 0 ; n2 < nE2 ; n2++ )
    {
      temp += pList[ n2 ];
    }
  }
  return temp;
}

The fourth Method

The goal of the fourth method is to show how we can implement a matrix function : starting from a cell range and obtaining a cell range.

//C++
//It's a matrix operation should be called like : {=METHODFOUR(A1:B4)}
Sequence< Sequence< sal_Int32 > > MyService2Impl::methodFour(const Sequence< Sequence< sal_Int32 > > &aValList )
throw (RuntimeException)
{ sal_Int32 n1, n2;
  sal_Int32 nE1 = aValList.getLength();
  sal_Int32 nE2;
  Sequence< Sequence< sal_Int32 > > temp = aValList;
  for( n1 = 0 ; n1 < nE1 ; n1++ )
  {
    Sequence< sal_Int32 > rList = temp[ n1 ];
    nE2 = rList.getLength();
    for( n2 = 0 ; n2 < nE2 ; n2++ )
    {
      rList[ n2 ] += 4;
    }
    temp[n1]=rList;
  }
  return temp;
}

What is done by this example is not great : only add four to every cells of the cell range and put the result in an other cell range.

The code is not complete with this four methods. If you want to create a component you have to add more code, if you want to make a scriptable component you still add more code and if you want to create an AddIn you have to add more.

The XAddIn Interface

Every add-in have to implement the XAddIn interface. As usual what you have to do is described by an IDL file :

// IDL
module com { module sun { module star { module sheet {
interface XAddIn: com::sun::star::lang::XLocalizable
{
  string getProgrammaticFuntionName( [in] string aDisplayName );
  string getDisplayFunctionName( [in] string aProgrammaticName );
  string getFunctionDescription( [in] string aProgrammaticName );
  string getDisplayArgumentName(
  [in] string aProgrammaticFunctionName,
  [in] long nArgument );
  string getArgumentDescription(
  [in] string aProgrammaticFunctionName,
  [in] long nArgument );
  string getProgrammaticCategoryName( [in] string aProgrammaticFunctionName );
  string getDisplayCategoryName( [in] string aProgrammaticFunctionName );
};
}; }; }; };

The corresponding C++ code is given now without explanation (for the moment)

// C++
// XAddIn
OUString SAL_CALL MyService2Impl::getProgrammaticFuntionName( const OUString& aDisplayName ) throw( uno::RuntimeException )
{
// not used by calc
// (but should be implemented for other uses of the AddIn service)
  return OUString();
}

OUString SAL_CALL MyService2Impl::getDisplayFunctionName( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{ // a nested if implementation would be better
  OUString aProgName, aRet;
  aProgName = aProgrammaticName;
  if (aProgName.equalsAscii("methodOne")) aRet = OUString::createFromAscii("method1");
  if (aProgName.equalsAscii("methodTwo")) aRet = OUString::createFromAscii("method2");
  if (aProgName.equalsAscii("methodThree")) aRet = OUString::createFromAscii("method3");
  if (aProgName.equalsAscii("methodFour")) aRet = OUString::createFromAscii("method4");
  return aRet;
}

OUString SAL_CALL MyService2Impl::getFunctionDescription( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{ // a nested if implementation would be better
  OUString aRet;
  if (aProgrammaticName.equalsAscii("methodOne"))
    aRet = OUString::createFromAscii("methodOne() : 1st try");
  if (aProgrammaticName.equalsAscii("methodTwo"))
    aRet = OUString::createFromAscii("methodTwo() : 1st try");
  if (aProgrammaticName.equalsAscii("methodThree"))
    aRet = OUString::createFromAscii("methodThree() : 1st try");
  if (aProgrammaticName.equalsAscii("methodFour")) 
    aRet = OUString::createFromAscii("methodFour() : 1st try");
  return aRet;
}

OUString SAL_CALL MyService2Impl::getDisplayArgumentName(
const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{ // (requis) is added in French and probably (required) in English (see the snapshot below)
  OUString aRet;
  if (aProgrammaticName.equalsAscii("methodOne")||aProgrammaticName.equalsAscii("methodTwo"))
    aRet = OUString::createFromAscii("a string");
  if (aProgrammaticName.equalsAscii("methodThree")||aProgrammaticName.equalsAscii("methodFour"))
    aRet = OUString::createFromAscii("a Cell Range");
  return aRet;
}

OUString SAL_CALL MyService2Impl::getArgumentDescription(
const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{
  OUString aRet;
  if (aProgrammaticName.equalsAscii("methodOne")||aProgrammaticName.equalsAscii("methodTwo"))
    aRet = OUString::createFromAscii("method1/2:a string or a cell with a string is required");
  if (aProgrammaticName.equalsAscii("methodThree")||aProgrammaticName.equalsAscii("methodFour"))
    aRet = OUString::createFromAscii("method3/4:a cell range is required");
  return aRet;
}

OUString SAL_CALL MyService2Impl::getProgrammaticCategoryName(const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
  OUString aRet( RTL_CONSTASCII_USTRINGPARAM("Add-In"));
  return aRet;
}

OUString SAL_CALL MyService2Impl::getDisplayCategoryName(const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
  return getProgrammaticCategoryName( aProgrammaticName );
}

To understand what is this code related to, we provide a snapshot : read it carefully to see where the code works and provides the strings to print out in the autopilot frame :

Addin2.png

I have used the method one as you can see over the autopilot, and I am preparing the use of the method two. What you can read in the autopilot comes directly from the code except the French word "requis" which is probably translated by "required" in an English OpenOffice version.

The XServiceName Interface

Again the corresponding IDL file indicates what we have to do :

// IDL
module com { module sun { module star { module lang {
interface XServiceName: com::sun::star::uno::XInterface
{
  string getServiceName();
};
}; }; }; };

This interface is simple : one method :

//C++
// XServiceName
OUString SAL_CALL MyService2Impl::getServiceName() throw( uno::RuntimeException )
{
// name of specific AddIn service
  return OUString::createFromAscii( "my_module.MyService2" );
}

The XServiceInfo Interface

This Interface may be avoided. You have to implement it only if you want that your add-in is also scriptable : you can call it from OOoBasic for instance. It's time to present the corresponding IDL file :

// IDL
module com {  module sun {  module star {  module lang {
interface XServiceInfo: com::sun::star::uno::XInterface
{
	string getImplementationName();
	boolean supportsService( [in] string ServiceName );
	sequence<string> getSupportedServiceNames();
};
}; }; }; }; 

Here is the corresponding C++ code :

// C++
// XServiceInfo implementation
OUString MyService2Impl::getImplementationName()
throw (RuntimeException)
{
// unique implementation name
  return OUString( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_impl.MyService2") );
}

sal_Bool MyService2Impl::supportsService( OUString const & serviceName )
throw (RuntimeException)
{
// this object only supports one service, so the test is simple
  if (serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("my_module.MyService2") ))
    return true;
  if (serviceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("com.sun.star.sheet.AddIn") ))
    return true;
  else return false;
}

Sequence< OUString > MyService2Impl::getSupportedServiceNames()
throw (RuntimeException)
{
  return getSupportedServiceNames_MyService2Impl();
}

Reference< XInterface > SAL_CALL create_MyService2Impl(Reference< XComponentContext > const & xContext)
SAL_THROW( () )
{
  return static_cast< lang::XTypeProvider * >( new MyService2Impl() );
}

To complete the code we have to add :

// C++
/* shared lib exports implemented without helpers in service_impl1.cxx */
namespace my_sc_impl
{
static struct ::cppu::ImplementationEntry s_component_entries [] =
{
    {
        create_MyService2Impl, getImplementationName_MyService2Impl,
        getSupportedServiceNames_MyService2Impl, ::cppu::createSingleComponentFactory,
        0, 0
    },
    { 0, 0, 0, 0, 0, 0 }
};
}

extern "C"
{
void SAL_CALL component_getImplementationEnvironment(
    sal_Char const ** ppEnvTypeName, uno_Environment ** ppEnv )
{
    *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
}
sal_Bool SAL_CALL component_writeInfo(
    lang::XMultiServiceFactory * xMgr, registry::XRegistryKey * xRegistry )
{
    return ::cppu::component_writeInfoHelper(
        xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
void * SAL_CALL component_getFactory(
    sal_Char const * implName, lang::XMultiServiceFactory * xMgr,
    registry::XRegistryKey * xRegistry )
{
    return ::cppu::component_getFactoryHelper(
        implName, xMgr, xRegistry, ::my_sc_impl::s_component_entries );
}
}

See also

This article is under construction and if you want to go further, for the moment have a look to the following references :

Personal tools