FR/Documentation/Construire des Addins

From Apache OpenOffice Wiki
< FR‎ | Documentation
Revision as of 17:08, 10 July 2008 by SergeMoutou (Talk | contribs)

Jump to: navigation, search

Notre propos est d'étendre OpenOffice.org Calc avec du code C++. De telles extensions sont appelées Add-ins, comme on a déjà eu l'occasion de le souligner, et sont simples à implanter. Encore une fois les aides (helpers) seront de la partie et nous aiderons à simplifier le code.

Introduction

Partons de la documentation officielle Voir : http://api.openoffice.org/docs/common/ref/com/sun/star/sheet/AddIn.html et http://sc.openoffice.org/addin_howto.html Chaque fonction d'un AddIn peut prendre des paramètres ayant les types suivants :

  • long pour les valeurs entières.
  • double pour les valeurs réelles.
  • string pour les chaînes de caractères.
  • long[][] pour les tableaux deux dimensions d'entiers.
  • double[][] pour es tableaux deux dimensions de réels.
  • string[][] pour les tableaux deux dimensions de chaînes de caractères.
  • any[][] pour les tableaux deux dimensions de contenus divers et non définis. Chaque any contiendra un double ou *une chaîne de caractères (string), dependant des données.
  • any

Chaque fonction AddIn doit retourner elle aussi des types définis dans la liste suivante :

  • long
  • double
  • string
  • long[][]
  • double[][]
  • string[][]
  • any[][]
  • XVolatileResult
  • any

Les services nécessaires

Ce que nous avons à faire et encore une fois défini par des services et interfaces. Ce type d'investigation a déjà souvent été rencontré dans ce document : naviguer dans les fichiers IDL pour glaner le plus d'informations possibles. Les services que nous aurons à examinés sont trouvés à partir du fichier IDL Addin.idl :

//Listing 1 Service AddIn (fichier IDL)
// IDL
module com {  module sun {  module star {  module sheet {
service AddIn
{
	interface com::sun::star::lang::XServiceName;
	interface com::sun::star::sheet::XAddIn;
	[optional] interface com::sun::star::sheet::XCompatibilityNames;
};
}; }; }; };

On peut voir les trois interfaces (deux ne sont pas optionnelles). Commençons par regarder la première  :

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

L'implantation C++ correspondante devrait être (basée sur es sources d'OpenOffice <OOo_1.1.3_src>/sch/source/addin/sampleaddin.cxx) :

//Listing 3 Implémentation de l'interface XServiceName in C++
// C++
// XServiceName
OUString SAL_CALL SampleAddIn::getServiceName() throw( uno::RuntimeException )
{
	return OUString::createFromAscii( "com.sun.star.chart.SampleAddIn" );
}

où nous avons simplement changé le nom “com.sun.star.chart.SampleAddIn“ avec notre propre nom de service. L'interface XAddin est probablement la plus importante et donnée maintenant  :

//Listing 4 Interface XAddin
// 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 );
 
};
}; }; }; };

Une implémentation possible de ces méthodes pourrait être  :

//Listing 5 C++ Implementation of XAddin Interface 
// C++
// XAddIn
 
OUString SAL_CALL ScaDateAddIn::getProgrammaticFuntionName( const OUString& aDisplayName ) throw( uno::RuntimeException )
{
    //  not used by calc
    //  (but should be implemented for other uses of the AddIn service)
    return OUString();
}
 
/*** This method returns the name for a given function, which should be displayed for the user, e.g. in a formula. 
***/
OUString SAL_CALL ScaDateAddIn::getDisplayFunctionName( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    OUString aRet;
	.....
    return aRet;
}
 
/*** The strings which this method returns are displayed in the AutoPilot for functions to give a short description for the entire function.
***/
OUString SAL_CALL ScaDateAddIn::getFunctionDescription( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    OUString aRet;
	....
    return aRet;
}
 
/*** For each parameter you will find a name which occurs in the parameter list. These strings are requested parameter wise.
One special parameter should be mentioned here: The "constREFXPS&" ("com::sun::star::beans::XPropertySet" in the IDL file) in some functions is not visible in the UI. It's a "hidden" parameter for which no request is generated, but it must be considered in the count of parameters, when informations for the other parameters are requested.
***/
 
OUString SAL_CALL ScaDateAddIn::getDisplayArgumentName(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{
    OUString aRet;
	....
    return aRet;
}
 
/*** Unlike the previous method, a description of a parameter is returned, which is also used in the AutoPilot for functions. 
***/
OUString SAL_CALL ScaDateAddIn::getArgumentDescription(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{
    OUString aRet;
	....
    return aRet;
}
 
/*** Each function in OpenOffice.org Calc can be assigned to a category. This is also used in AutoPilot for functions to make the listing of functions clearer. So this method returns the internal name for the category, which the function should be assigned to.
***/
OUString SAL_CALL ScaDateAddIn::getProgrammaticCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    OUString aRet;
    ....
    return aRet;
}
 
/*** Same as getProgrammaticCategoryName but this name of the category will be displayed in the UI. Of course this makes only sense, if it's a user defined one. For the build in categories the names of course are fix. In the current implementation no language dependent strings are returned. This should be done, when Calc gets support for user defined categories.
***/
OUString SAL_CALL ScaDateAddIn::getDisplayCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    return getProgrammaticCategoryName( aProgrammaticName );
}

La dernière interface est :

// Listing 6 XCompatibilityNames Interface (IDL File)
// IDL
module com {  module sun {  module star {  module sheet {
interface XCompatibilityNames: com::sun::star::uno::XInterface
{
	sequence< com::sun::star::sheet::LocalizedName >
		getCompatibilityNames( [in] string aProgrammaticName );
};
}; }; }; };

qui pourrait être implémentée (basée sur les sources OpenOffice.org1.1) : OOo_1.1.3_src/scaddins/source/datefunc/datefunc.cxx

//Listing 7  Implémentation en C++ de l'interface XCompatibilityNames
// C++
// XCompatibilityNames
 
/*** This method is very important in the context with the im- and export of Excel files. For each function a list is returned, where every element holds a name and a accompanying language. With this it is possible to parse a number of language variants of the add-in for the import. One remark: Excel is only able to load such add-in functions, which are in the same language as the installed add-in. In this case, OpenOffice.org Calc is a little bit more advanced than Excel... :-) In the opposite direction, when exporting to Excel, the installed OpenOffice.org language is chosen to select a name.
The main goal for the implementation of the Analysis add-in was to get a better compatibility to Excel. For this reason the "CompatibilityNames" are chosen in a way, that they are identically to those which are uses by Excel. These don't need to match the "DisplayFunctionNames", but in most cases they do. Exceptions are the cases, where the Excel namings colliding with already build in functions of Calc. In this cases, a simple "_ADD" is appended to the name.
***/
uno::Sequence< sheet::LocalizedName > SAL_CALL ScaDateAddIn::getCompatibilityNames(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException )
{
    ....
 
    uno::Sequence< sheet::LocalizedName > aRet( nCount );
    sheet::LocalizedName* pArray = aRet.getArray();
 
    ....
 
    return aRet;
}

Vous ne pouvez pas écrire un addin sans les fameuses functions C déjà mentionnée dans le chapitre précédent sur les composants.

Mon premier Add-In

J'ai réalisé mon premier Add-in en :

  • partant de l'exemple du composant (voir section 13.5)
  • ajoutant deux interfaces lang::XServiceName et XAddIn et les fichiers correspondants dans le makefile, les fichiers IDL en ajoutant les directives d'inclusion correspondantes dans le code C++
  • implementant ces interfaces avec un minimum de lignes de code. A noter que lorsque vous ajoutez l'interface Xaddin il vous faut implanter automatiquement l'interface XInternationalize sans l'ajouter dans l'aide (helper).

L'add-in fonctionne dès que le service com.sun.star.sheet.AddIn était disponible (voir sal_Bool MyService2Impl::supportsService( OUString const & serviceName) dans le Listing 1).

On choisit de partir d'un exemple du SDK concernant un composant (anoter qu'il n'y a pas d'exemple de addin C++ dans le SDK) : <OpenOffice.org1.1_SDK>/examples/DevelopersGuide/Components/CppComponent

Je vais podifier légèrement cet exemple en modifiant le fichier makefile pour tenir compte qu'un seul fichier nous sera nécessaire. Ce fichier makefile compilera le addin et l'intallera.

Commençons par donner notre cahier des charges sous la forme de fichier IDL.

Le fichier IDL

Voici notre fichier IDL de départ :

//Listing 8 My first Add-In 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
{
	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 MyService1
//{
//    interface XSomething;
//};
service MyService2
{
    interface XSomething;
    interface com::sun::star::lang::XInitialization;
    interface com::sun::star::lang::XServiceName;
    interface com::sun::star::sheet::XAddIn;
};
 
};

On peut distinguer quatre méthodes methodOne, methodTwo, methodThree and methodFour. Les quatre méthodes peuvent être utilisées avec OOoCalc et aussi avec OOoBasic, ce dernier parce que mon code est tiré d'un exemple de composant.

Nous donnons le code le la classe automatiquement générée avec l'outil cppumaker.

class SAL_NO_VTABLE XSomething : public ::com::sun::star::uno::XInterface
{
public:
 
    // Methods
    virtual ::rtl::OUString SAL_CALL methodOne( const ::rtl::OUString& val ) throw
                     (::com::sun::star::uno::RuntimeException) = 0; 
    virtual ::rtl::OUString SAL_CALL methodTwo( const ::rtl::OUString& val ) throw 
                     (::com::sun::star::uno::RuntimeException) = 0;
    virtual sal_Int32 SAL_CALL methodThree( const ::com::sun::star::uno::Sequence< 
                     ::com::sun::star::uno::Sequence< sal_Int32 > >& aValList ) throw
                           (::com::sun::star::uno::RuntimeException) = 0;
    virtual ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Sequence< 
                      sal_Int32 > > SAL_CALL methodFour( const ::com::sun::star::uno::Sequence<
                           ::com::sun::star::uno::Sequence< sal_Int32 > >& aValList ) throw 
                               (::com::sun::star::uno::RuntimeException) = 0;
};

Implantation en C++ des fonctions membres

Nous donnons le code C++ des quatres fonctions membres présentées dans le fichier IDL.

Les deux premières fonctions membres

Les deux premières sont assez similaires :

// 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;
}

Elles prennent seulement une chaîne de caractères (d'une celleule OOoCalc) et ajoute un message en mettant le résultat total dans une cellule résultat.

La troisième fonction membre

La troisième fonction membre est un peu plus compliqué : elle retourne une valeur calculée à partir d'une zone de cellules (la somme en l'occurence).

// 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;
}

La quatrième fonction membre

Le but de la quatrième fonction membre est de montrer comment on peut implanter une fonction matrice : partir d'une zone de cellules et en obtenir une autre.

//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;
}

Ce qui peut être fait avec cet exemple n'est pas grandiose : seulement ajouter 4 à toutes les cellules de la zone de cellules et mettre le résultat dans une zone de cellules.

Toutes les méthodes sont reliées à une classe C++ nommée MyService2Impl. Donnons maintenant la définition de la classe.

La classe C++

On utilise un helper pour implémenter notre classe et nous ne devons donc pas imlémenter toutes les interfaces : pas besoin d'implémenter XInterface, XTypeProvider, XWeak par exemple. Nous donnons donc le code C++ correspondant.

//C++
class MyService2Impl : public ::cppu::WeakImplHelper5<
      ::my_module::XSomething, lang::XServiceInfo, lang::XInitialization, lang::XServiceName,
	XAddIn>
{
    OUString m_arg;
public:
    // no need to implement XInterface, XTypeProvider, XWeak
 
    // XInitialization will be called upon createInstanceWithArguments[AndContext]()
    virtual void SAL_CALL initialize( Sequence< Any > const & args )
        throw (Exception);
    // XSomething
    virtual OUString SAL_CALL methodOne( OUString const & str )
        throw (RuntimeException);
    virtual OUString SAL_CALL methodTwo( OUString const & str )
        throw (RuntimeException);
    virtual sal_Int32 SAL_CALL methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
        throw (RuntimeException);
    virtual Sequence< Sequence< sal_Int32 > > SAL_CALL methodFour(
				const Sequence< Sequence< sal_Int32 > > &aValList )
	throw (RuntimeException);
 
    // XServiceName
    virtual OUString SAL_CALL getServiceName() throw( uno::RuntimeException );
    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName()
        throw (RuntimeException);
    virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName )
        throw (RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames()
        throw (RuntimeException);
    // XAddIn
    virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) 
                throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) 
                throw( uno::RuntimeException );
    virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) 
                throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayArgumentName(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getArgumentDescription(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getProgrammaticCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    // XLocalizable
    virtual void SAL_CALL setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException );
    virtual lang::Locale SAL_CALL getLocale() throw( uno::RuntimeException );
};

Le code est loin d'être complet avec les quatres méthodes comme on peut le voir dans la déclaration de classe.. Seulement votre propre interface XSomething est implémentée (see above). Vous pouvez vous demander comment il est possible de treouver toutes les méthodes de votre classe. Comme vous allez l'apprendre maintenant une enquête minutieuse à l'aide des fichiers IDL est nécessaire. Le nombre d'interface que vous avez à implémenter dépend de votre but final. Si vous voulez implémenter un composant il vous faudra implémenter l'interface XInterface ainsi que la votre, si vous voulez faire un composant visible d'un autre langage de programmation (scriptable component) il faut ajouter les interfaces : XTypeprovider, XServiceInfo and XWeak) et si vous voulez créer un AddIn (scriptable) ajouter encore (XAddin, XServiceName). C'est expliqué dans le Developper's Guide. On commence par l'interface typique : XAddin.

L'interface XAddIn

Avant de comprendre comment les choses marchent, demandons-nous ce que sont les paramètres OUString& aProgrammaticName que l'on trouve dans toutes les méthodes de l'interface XAddin (voir Listing 4). C'est seulement le nom des méthodes données dans le fichier IDL : par exemple le sous-programme getDisplayFunctionName( const OUString& aProgrammaticName ) sera appelé par l'extérieur avec comme valeur soit « methodOne » ou soit « methodTwo ». Comme programmeur vous devez répondre à tous les appels et donc tester les valeurs de cet argument.

Le code C++ correspondant est donné sans explication pour le 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 );
}

Pour comprendre à quoi est relié ce code nous vous proposons la copie d'écran suivante, regardez-la en détail pour voir les parties du codes qui sont concernées par l'autopilot :

Addin2.png

J'y ai utilisé methodOne comme vous pouvez le voir dans l'autopilot, et je suis prêt à utiliser la méthode deux. Ce qui apparaît dans l'autopilot vient directement du code C++ excepté le mot français "requis" qui est probablement traduit par "required" dans une version anglo-saxonne d'OpenOffice.


L'interface XServiceName

Commençons encore et toujours par son fichier IDL :

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

Cette interface est simple : une méthode :

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

L'interface XServiceInfo

L'implémentation de cette interface n'est pas obligatoire. Vous devez l'implanter si vous voulez que votre add-in soit aussi scriptable : appelable depuis OOoBasic par exemple. Voila donc le fichier IDL correspondant :

// 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();
};
}; }; }; };

Et voila le code C++ implémentant l'interface XServiceInfo :

// 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() );
}


L'interface XInitialization

Voila directement son code C++.

// C++
// XInitialization implemention
void MyService2Impl::initialize( Sequence< Any > const & args )
    throw (Exception)
{
    if (1 != args.getLength())
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("give a string instanciating this component!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
    if (! (args[ 0 ] >>= m_arg))
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("no string given as argument!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
}


L'interface XLocalizable

Cette interface est particulière : on l'a laissée dans la définition de la classe en donnant maintenant son code, mais puisque son code ne fait rien en fait, on ne l'ajoute pas comme sixième interface quand on utilise l'aide (helper).

// C++
// XLocalizable
void SAL_CALL MyService2Impl::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException )
{
//    aFuncLoc = eLocale;
//    InitData();     // change of locale invalidates resources!
}
 
lang::Locale SAL_CALL MyService2Impl::getLocale() throw( uno::RuntimeException )
{
//    return aFuncLoc;
}


Le code complet

Il est maintenant temps de donner le code C++ complet :

//Listing 9 My first Add-In
//C++
#include <cppuhelper/implbase5.hxx> // "5" implementing five interfaces
#include <cppuhelper/factory.hxx>
#include <cppuhelper/implementationentry.hxx>
 
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/lang/XServiceName.hpp>
#include <com/sun/star/lang/IllegalArgumentException.hpp>
#include <com/sun/star/sheet/XAddIn.hpp>
#include <com/sun/star/lang/XLocalizable.hpp>
#include <my_module/XSomething.hpp>
 
 
using namespace ::rtl; // for OUString
using namespace ::com::sun::star; // for odk interfaces
using namespace ::com::sun::star::uno; // for basic types
using namespace ::com::sun::star::sheet;
 
 
namespace my_sc_impl
{
 
static Sequence< OUString > getSupportedServiceNames_MyService2Impl()
{
	static Sequence < OUString > *pNames = 0;
	if( ! pNames )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( !pNames )
		{
			static Sequence< OUString > seqNames(2);
			seqNames.getArray()[0] = OUString(RTL_CONSTASCII_USTRINGPARAM("my_module.MyService2"));
                        seqNames.getArray()[1] = OUString(RTL_CONSTASCII_USTRINGPARAM("com.sun.star.sheet.AddIn"));
			pNames = &seqNames;
		}
	}
	return *pNames;
}
 
static OUString getImplementationName_MyService2Impl()
{
	static OUString *pImplName = 0;
	if( ! pImplName )
	{
//		MutexGuard guard( Mutex::getGlobalMutex() );
		if( ! pImplName )
		{
			static OUString implName( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_implementation.MyService2") );
			pImplName = &implName;
		}
	}
	return *pImplName;
}
 
class MyService2Impl : public ::cppu::WeakImplHelper5<
      ::my_module::XSomething, lang::XServiceInfo, lang::XInitialization, lang::XServiceName,
	/*lang::XLocalizable, */XAddIn>
{
    OUString m_arg;
public:
    // focus on three given interfaces,
    // no need to implement XInterface, XTypeProvider, XWeak
 
    // XInitialization will be called upon createInstanceWithArguments[AndContext]()
    virtual void SAL_CALL initialize( Sequence< Any > const & args )
        throw (Exception);
    // XSomething
    virtual OUString SAL_CALL methodOne( OUString const & str )
        throw (RuntimeException);
    // **********************ADDED
    virtual OUString SAL_CALL methodTwo( OUString const & str )
        throw (RuntimeException);
    virtual sal_Int32 SAL_CALL methodThree(const Sequence< Sequence< sal_Int32 > > &aValList )
        throw (RuntimeException);
    virtual Sequence< Sequence< sal_Int32 > > SAL_CALL methodFour(
				const Sequence< Sequence< sal_Int32 > > &aValList )
	throw (RuntimeException);
    // ********************** END ADDED
    // XServiceName
    virtual OUString SAL_CALL getServiceName() throw( uno::RuntimeException );
    // XServiceInfo
    virtual OUString SAL_CALL getImplementationName()
        throw (RuntimeException);
    virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName )
        throw (RuntimeException);
    virtual Sequence< OUString > SAL_CALL getSupportedServiceNames()
        throw (RuntimeException);
    // XAddIn
    virtual OUString SAL_CALL getProgrammaticFuntionName( const OUString& aDisplayName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayFunctionName( const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getFunctionDescription( const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayArgumentName(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getArgumentDescription(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getProgrammaticCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    virtual OUString SAL_CALL getDisplayCategoryName(
        const OUString& aProgrammaticName ) throw( uno::RuntimeException );
    // XLocalizable
    virtual void SAL_CALL MyService2Impl::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException );
    virtual lang::Locale SAL_CALL MyService2Impl::getLocale() throw( uno::RuntimeException );
};
 
// XInitialization implemention
void MyService2Impl::initialize( Sequence< Any > const & args )
    throw (Exception)
{
    if (1 != args.getLength())
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("give a string instanciating this component!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
    if (! (args[ 0 ] >>= m_arg))
    {
        throw lang::IllegalArgumentException(
            OUString( RTL_CONSTASCII_USTRINGPARAM("no string given as argument!") ),
            (::cppu::OWeakObject *)this, // resolve to XInterface reference
            0 ); // argument pos
    }
}
 
// XServiceName
OUString SAL_CALL MyService2Impl::getServiceName() throw( uno::RuntimeException )
{
    // name of specific AddIn service
    return OUString::createFromAscii( "my_module.MyService2" );
}
// XSomething implementation
OUString MyService2Impl::methodOne( OUString const & str )
    throw (RuntimeException)
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM(
        "called methodOne() of MyService2 implementation: ") ) + m_arg + str;
}
// **********************ADDED 
 
OUString MyService2Impl::methodTwo( OUString const & str )
    throw (RuntimeException)
{
    return OUString( RTL_CONSTASCII_USTRINGPARAM(
        "called methodTwo() of MyService2 implementation: ") ) + m_arg + str;
}
 
 
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;
}
 
//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;
}
 
// ********************** END ADDED 
 
// 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 )
{
    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 )
{
    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");
    return aRet;
}
 
OUString SAL_CALL MyService2Impl::getDisplayArgumentName(
        const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
{   
    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 );
}
 
// 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
    // modified *********
    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() );
}
 
// XLocalizable
void SAL_CALL MyService2Impl::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException )
{
//    aFuncLoc = eLocale;
//    InitData();     // change of locale invalidates resources!
}
 
lang::Locale SAL_CALL MyService2Impl::getLocale() throw( uno::RuntimeException )
{
//    return aFuncLoc;
}
 
 
}
 
/* 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 );
}
}

Nous savons maintenant comment les choses fonctionnent et particulièrement comment gérer les séquences de séquences comme paramètre ou comme valeur retournée. Même si l'exemple ci-dessus est donné avec un type long, il est facile de le transposer avec un double.

Utilitaires

Dans le but de fournir des utilitaires pour interfacer Openoffice.org avec d'autres librairies externes, nous allons fournir dans cette section des utilitaires permettant de transformer les variables classiques en variables UNO.

Transformer une séquence de séquence en un tableau

Si vous utilisez une librairie externe, comme par exemple la librairie GSL (GNU Scientific Library) qui est une librairie écrite en C, vous vous apercevez qu'en général les données sont sous forme de tableaux de réels (en format double). Prenez le temps d'examiner le Listing 182 où vous voyez une méthode imaginaire appelée « foo ». Le point important est que l'on doit utiliser une séquence de séquence comme paramètre de la méthode alors que l'on dispose de tableaux à deux dimensions.

//Listing 10 Head of Method foo : Sequence of Sequence into Array
//C+
// Be careful it's not the complete function !!!!
Sequence< Sequence< double > > MyService2Impl::foo(
                      const Sequence< Sequence< double > > &aValList )
                                                     throw (RuntimeException)
{ 	sal_Int32		nE1 = aValList.getLength();
	sal_Int32		nE2;
 
	Sequence< double >	rList = aValList[ 0 ];
	nE2 = rList.getLength();
	double table[nE1][nE2];
	for( sal_Int32 n1 = 0 ; n1 < nE1; n1++ )
	{
		for (sal_Int32 n2=0;n2<nE2;n2++)
			table[n1][n2]=aValList[n1][n2];
	}
// we have a 2D array now

A la fin de ce programme vous avez un tableau à deux dimensions nommé « table » et nous montrons ainsi comment le construire. (J'ai essayé une autre méthode utilisant un getArray() mais sans succès)

Transformer un tableau en une séquence de séquence

Imaginons que votre calcul produise un tableau à une dimension nommé « z » et que vous désiriez construire une séquence de séquence avant de la retourner. Voici un chemin que vous pouvez suivre :

//Listing 11 Construire une séquence de séquence en partant d'un tableau « z »
//C+
...
// here a line is constructed of size (nE2-1)*2
	Sequence< double >	solList1D(z,(nE2-1)*2);
	Sequence< Sequence< double > > solList2D(1);
	solList2D[0]=solList1D;
	return solList2D;

Un problème similaire rencontré plus tard doit être résolu différemment  : je pars d'un tableau à une dimension, mais ce tableau contient une partie réelle et une partie imaginaire « lesThe n-1 rarines sont retournées dans un tableau de nombre complexes z de longueur 2(n-1), alternant les parties réelles et les parties imaginaires » Je veux montrer le résultat d'un tel calcul dans Calc mais avec deux colonnes (une pour la partie réelle, une pour la partie imaginaire) avec autant de lignes que nécessaire. Voici maintenant la façon d'y parvenir :

//Listing 12 Construire une séquence de séquence en partant d'un tableau « z »
//C+
...
// two rows and nE2-1 lines starting from an array "z" with (nE2-1)*2 lements
	Sequence< double >	solList1D(2);
		Sequence< Sequence< double > > solList2D(nE2-1);
		for( int j=0;j<((nE2-1)<<1);j+=2){ // (nE2-1)<<1 is faster than (nE2-1)*2
			for( int i=0; i<2 ; i++){
				solList1D[i]=z[i+j];
			}
			solList2D[j>>1]=solList1D;//j >> 1 is faster than j/2
		}
		return solList2D;

De temps en temps il nous faudra utiliser la STL (Standard Template Library) parce que la librairie que l'on décide d'interfacer est en C++. Eric Ehlers a donné un exemple en interfaçant une librairie C++ et a donc été obligé d'écrire quelques utilitaires et fournir ainsi du code intéressant.

Utilitaires d'Eric Ehlers

Le projet s'appelle QuantLibAddin - http://quantlib.org/quantlibaddin/ C'est une intégration de QuantLib, une librairie open source, à de nombreuses plates-formes dont calc. Les notes expliquant comment construire et installer QuantLibAddin pour Calc peuvent être trouvées ici : http://quantlib.org/quantlibaddin/calc.html

Sur lapage de téléchargement de QuantLib http://sourceforge.net/project/showfiles.php?group_id=12740 vous pouvez télécharger QuantLibAddin et regarder le code source de l' addin OOoCalc. Pour exécuter l'addin QuantLib vous devez construire QuantLib - http://quantlib.org, log4cxx - http://logging.apache.org/log4cxx/, ObjectHandler - http://quantlib.org/objecthandler et QuantLibAddin - http://quantlib.org/quantlibaddin/

Comme QuantLib est écrit en C++ avec la STL, des conversions de types doivent être fournies. Nous en présentons quelques unes maintenant :

//Listing 13 Uitilitaires de conversion d'Eric Ehlers
//C+
// from QuantLibAddin-0.3.10/Addins/Calc/calcutils.cpp
/*
 Copyright (C) 2004, 2005 Eric Ehlers
 
 This file is part of QuantLib, a free-software/open-source library
 for financial quantitative analysts and developers - http://quantlib.org/
 
 QuantLib is free software: you can redistribute it and/or modify it under the
 terms of the QuantLib license.  You should have received a copy of the
 license along with this program; if not, please email quantlib-dev@lists.sf.net
 The license is also available online at http://quantlib.org/html/license.html
 
 This program is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 FOR A PARTICULAR PURPOSE.  See the license for more details.
*/
 
....
 
std::vector < double >SeqSeqToVectorDouble(const Sequence< Sequence < double > >& ss) {
    std::vector < double >v;
    for (int i=0; i<ss.getLength(); i++)
        for (int j=0; j<ss[i].getLength(); j++)
            v.push_back(ss[i][j]);
    return v;
}
 
std::vector < std::vector < double > >SeqSeqToMatrixDouble(
											const Sequence< Sequence < double > >& ss) {
    std::vector < std::vector < double > >vv;
    for (int i=0; i<ss.getLength(); i++) {
        std::vector < double >v;
        for (int j=0; j<ss[i].getLength(); j++)
            v.push_back(ss[i][j]);
        vv.push_back(v);
    }
    return vv;
}
 
Sequence< Sequence< double > > VectorDoubleToSeqSeq(const std::vector < double > &v) {
    Sequence< Sequence< double > > ss(v.size());
    for (unsigned int i=0; i<v.size(); i++) {
        Sequence< double > s(1);
        s[0] = v[i];
        ss[i] = s;
    }
    return ss;
}
 
Sequence< Sequence< double > > MatrixDoubleToSeqSeq(
									const std::vector < std::vector < double > >&vv) {
    Sequence< Sequence< double > > ss(vv.size());
    for (unsigned int i=0; i<vv.size(); i++) {
        std::vector < double > v = vv[i];
        Sequence< double > s(v.size());
        for (unsigned int j=0; j<v.size(); j++)
            s[j] = v[j];
        ss[i] = s;
    }
    return ss;
}

Le fichier calcutils.cpp fournit d'autres conversions bien utiles mais non présentées ici. Prenez le temps de jeter un coup d'oeil dans les codes sources du projet.

Notez aussi qu'au chapitre 3 nous avons présenté des conversions un peu plus abstraites en utilisant les templates. Il me semble que ce type de conversion est intégré dans la nouvelle version du projet maintenant.

Interfacer la librairie GSL (GNU Scientific Library)

Notre objectif est de fournir un exemple permettant l'utilisation de routines scientifiques C dans notre tableur. Interfacer complètement la librairie est un travail assez énorme puisqu'il faudra pour chaque fonction écrire la méthode correspondante dans un fichier IDL Je ne donnera qu'un exemple restreint seulement pour montrer la voie.

Première étape

La première étape est de naturellement installer la librairie GSL. Pour cela il faut lire la documentation correspondante.

Deuxième étape

Il nous faut trouver la fonction GSL que l'on veut utiliser et écrire alors le fichier IDL correspondant. Pour cet exemple, je me suis intéressé au calcul des racines complexes d'un polynôme de degré quelconque. La fonction correspondante s'appelle « gsl_poly_complex_solve ». Le fichier IDL correspondant est présenté maintenant : il est un peu alourdi par le fait que l'on a gardé les méthodes de la version précédente de notre add-in (methodOne ...methodFour) :

//Listing 14 Mon Add-in avec la GSL : le fichier IDL
// IDL
module my_module
{
 
interface XSomething : com::sun::star::uno::XInterface
{
	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 );
	sequence< sequence< double > > poly_complex_solve( 
					[in] sequence< sequence< double > > aValList );
};
 
service MyService2
{
    interface XSomething;
    interface com::sun::star::lang::XInitialization;
    interface com::sun::star::lang::XServiceName;
    interface com::sun::star::sheet::XAddIn;
};
};

Le nom de la nouvelle méthode sera donc « poly_complex_solve ».

Troisième étape

Il faut modifier le makefile pour que l'édition de liens utilise la librairie GSL en plus des autres librairies standards :

#Listing 15 Nouveau MakeFile
....
ifeq "$(OS)" "WIN"
$(SHAREDLIB_OUT)/%.$(SHAREDLIB_EXT) : $(SLOFILES) $(OUT_COMP_GEN)/%.def
	-$(MKDIR) $(subst /,$(PS),$(@D))
	$(LINK) $(LIBRARY_LINK_FLAGS) /OUT:$@ /MAP:$(OUT_COMP_GEN)/$(subst $(SHAREDLIB_EXT),map,$(@F)) \
	/DEF:$(OUT_COMP_GEN)/$(subst $(SHAREDLIB_EXT),def,$(@F)) $(SLOFILES) \
	$(CPPUHELPERLIB) $(CPPULIB) $(SALLIB) $(STLPORTLIB) msvcrt.lib kernel32.lib
else
$(SHAREDLIB_OUT)/%.$(SHAREDLIB_EXT) : $(SLOFILES)
	-$(MKDIR) $(subst /,$(PS),$(@D))
	$(LINK) $(LIBRARY_LINK_FLAGS) $(LINK_LIBS) -o $@ $^\
	$(CPPUHELPERLIB) $(CPPULIB) $(SALLIB) $(STLPORTLIB) $(STC++LIB) -lgsl -lgslcblas -lm 
#	$(CPPUHELPERLIB) 
endif	
....

Quatrième étape

Ecrire le code et compiler. Le code donné ci-dessous fonctionne correctement si les coefficients du polynôme sont donnés sur une ligne du tableur, donc plus exactement un groupe de cellules ne faisant qu'une ligne. Dans tous les autres cas il retourne les même valeurs que celle qui sont données en entrées.

//Listing 16 Nouveau Code
// C++
....
#include <gsl/gsl_poly.h>
....
Sequence< Sequence< double > > MyService2Impl::poly_complex_solve(
		const Sequence< Sequence< double > > &aValList )throw (RuntimeException)
{ 	
	sal_Int32		nE1 = aValList.getLength();
	sal_Int32		nE2;
 
	Sequence< double >	rList = aValList[ 0 ];
	nE2 = rList.getLength();
	double table[nE1][nE2];
	for( sal_Int32 n1 = 0 ; n1 < nE1; n1++ )
	{
		for (sal_Int32 n2=0;n2<nE2;n2++) 
			table[n1][n2]=aValList[n1][n2];
	}	
// I have a 2D nE1xnE2 table here 
	if (nE1==1){ // real coefficients horizontally disposed	
		double z[(nE2-1)*2];
		gsl_poly_complex_workspace * w = gsl_poly_complex_workspace_alloc(nE2);	
		gsl_poly_complex_solve(table[0],nE2,w,z);
		gsl_poly_complex_workspace_free(w);
		Sequence< double >	solList1D(2);
		Sequence< Sequence< double > > solList2D(nE2-1);
		for( int j=0;j<((nE2-1)<<1);j+=2){
			for( int i=0; i<2 ; i++){
				solList1D[i]=z[i+j];
			}
			solList2D[j>>1]=solList1D;
		}
		return solList2D;
	} else
	if (nE2==1){ // real coefficients vertically disposed	: doesn't work at the moment
		Sequence< Sequence< double > > temp = aValList; // return the coefficients
	  	return temp;
	} else {
	// If here : error 
	  Sequence< Sequence< double > > temp = aValList; // return the coefficients
	  return temp;
	}
}

Et voici le résultat présenté dans un tableau symbolisant votre tableur. La première ligne n'étant pas essentielle elle pourra être ou non présente dans votre feuille :

Résultats coefficients horizontaux et racines verticales
x^4 x^5
-1 0 0 0 0 1
-0.81 0.59
-0.81 -0.59
0.31 0.95
0.31 -0.95
1 0


où vous voyez en haut les coefficients sur la première ligne. Ils signifie que le polynôme qui nous intéresse est -1+x^5. Les cinq racines se retrouvent ensuite en dessous (avec la partie réelle à gauche et la partie imaginaire à droite).

Retour à la page d'accueil

Page d'accueil du développement C++ à l'aide du SDK

Voir aussi

Personal tools