LicenseDialog/fr

À propos du code proposé dans cette page : tous les changements par rapport au code original d'OpenOffice.org ayant servi de base, et fournis dans cette page, sont :


 * '''sous Licence LGPL V3 ( et vous pouvez-le redistribuer et / ou le modifier selon les termes de la licence GNU Lesser General Public License telle que publiée par la Free Software Foundation)
 * Copyright Eric Bachard 12th august 2009

Introduction
Ci-dessous, vous trouverez une description technique de l'implémentation de la fonctionnalité : faire afficher la boîte de dialogue associée à l'entrée de menu ou le bouton "Licence..." . Il est ainsi question de code source (C++, objective C), mais aussi de la structure et de l'organisation interne d'OpenOffice.org (framework), et nous supposons que le lecteur possède les bases nécessaires pour lire et comprendre le contenu de ce qui est exposé.

Les pré-requis pour lire cet article sont encore à définir, mais si certaines notions ne vous sont pas familières, nous vous invitons à consulter le wiki d'OpenOffice.org pour compléter vos connaissances, afin de pouvoir lire cet article dans les meilleures conditions possibles.

État de l'Implémentation

 * Partie Mac OS X (Aqua)
 * Écrire le .xcu
 * Création de la boîte de dialogue
 * Description de la Classe ShowLicenseDialog
 * Connexion avec vcl
 * Changements dans svx
 * Connexion avec sfx2
 * Connexion avec framework
 * Connexion avec unotools
 * Deboguage
 * Documentation

Specifications

 * Ajouter une entrée dans toutes les applications, qui permet de connaître la licence de OOo4Kids.


 * Comportement attendu :
 * un clic sur l'entrée de menu ouvre une fenêtre modale, qui explique la licence utilisée par OOo4Kdids, et donne un lien pointant sur la licence complète.
 * clicquer sur OK ferme la fenêtre

Réalisation
Cette entrée sera disponible par défaut :

- dans le menu Application (menu Cocoa, à gauche), en dessous de l'entrée "Préférences" pour la version Mac OS X d'OOo4Kids

- dans le menu Aide pour les autres systèmes d'exploitation (Linux et Windows).

Moyens utilisés

 * Nouvelle entrée ajoutée dans un menu

Le clic sur l'item va générer un événement, que l'application va recevoir, et transfomer en une commande UNO ( .uno:ShowLicense). Cette commande sera diffusée, et la fenêtre modale correspondant à l'affichage de la licence sera ouverte. Un appui sur Ok (ou esc ) permettra à l'utilisateur de fermer cette fenêtre.

Autres possibilités :


 * Le fichier de configuration GenericCommands.xcu possède une entrée .uno:ShowLicense ( la commande correspondante sera dispatchée )

Et donc, l'utilisateur pourra '''soit ajouter une nouvelle entrée de menu "Licence..." dans un menu de son choix, soit ajouter un nouveau bouton "Licence...".'''

Ceci pourra se faire grâce au menu Outils -> Personnaliser...

Cette entrée sera disponible par défaut :

- dans le menu Application (menu Cocoa, à gauche), en dessous de l'entrée "Préférences" pour les version Mac OS X

- dans le menu Aide pour les autres systèmes d'exploitation.

1) En partant de vcl
Cette partie décrit uniquement l'implémentation de la version Mac OS X Aqua d'OOo4Kids

Sous Mac OS X, version Aqua :

La chaîne "License ..." (en-US) est créee dans vcl/source/src/stdtext.src (j'ai aussi ajouté la chaîne en fr, et donc, en fonctionnement, "Licence..." sera affiché avec la version fr, ce qui signifie que le menu correspondant sera automatiquemenet localisé)

String SV_STDTEXT_LICENSE {                                                                                                                                          Text [ en-US ] = "License ..."; Text [ fr ] = "Licence ..."; };

'''Au moment de la compilation, les chaînes ajoutées seront ajoutées dans le binaire vclfr.res (version fr) ou vcl.res (en-US, pardefaut), puis délivrées dansle solver, packagées, ... etc'''

L'action sélectionner le menu principal Aqua " File -> License... "

=> aura pour effet d'envoyer un message interne (Cocoa) de type "SHOW_DIALOG" et contenant, parmi d'autres informations, la chaîne "LICENSE"

Dans le code source, see vcl/aqua/source/window/salmenu.cxx

else if( nDialog == SHOWDIALOG_ID_LICENSE ) aDialog = String( RTL_CONSTASCII_USTRINGPARAM( "LICENSE" ) ); const ApplicationEvent* pAppEvent = new ApplicationEvent( String,                                                                                                                              ApplicationAddress,                                                                                                                   ByteString( "SHOWDIALOG" ),                                                                                                             aDialog ); AquaSalInstance::aAppEventList.push_back( pAppEvent );

2) ...dans la partie "framework"
Étape suivante  : comme il s'agit d'un événement système, l'Application (voir le point suivant dans desktop), n'est pas capable d'intercepter l'événement, et pour contourner cette insuffisance, un écouteur d'événéments (eventListener) est démaré dans le framework, afin que celui-ci intercepte tous les événement hard codés de type SHOWDIALOG. Ceci est précisément réalisé dans framework/source/dispatch/windowcommanddispatch.c

xx

Tout d'abord, un écouteur d'événements est démarré ( void WindowCommandDispatch::impl_startListening ) dans le constructeur de la classe WindowCommandDispatch.

Ensuite, on crée une chaîne de caractères, de type OUString : const ::rtl::OUString WindowCommandDispatch::COMMAND_LICENSEBOX   = ::rtl::OUString::createFromAscii(".uno:ShowLicense"); Et une fois que l'événement est intercepté, on utlise un callback, afin de diffuser la commande, de la façon suivante : IMPL_LINK(WindowCommandDispatch, impl_notifyCommand, void*, pParam) Qui fait ... :   switch (nCommand) {                                                                                                            ...                                    case SHOWDIALOG_ID_LICENSE : sCommand = WindowCommandDispatch::COMMAND_LICENSEBOX; en terminant avec : impl_dispatchCommand(sCommand);

Étape suivante-> la commande est interceptée, est exécutée dans sfx2

Suggestion : 

=> voir la description complète de l'interface dans : framework/source/inc/dispatch/windowcommanddispatch.hxx /** @short internal helper to bind e.g. MAC-Menu events to our internal dispatch API. @descr On e.g. MAC platform system menus are merged together with some fix entries as                                    e.g. "Preferences" or "About". These menu entries trigger hard coded commands. Here we map these commands to the right URLs and dispatch them.

3) de desktop vers ... unotools ?)
TODO : est-ce que l'API serait déjà capable d'utiliser ce qui est implémenté ?

4) officefg
Que se passe-t-il dans officecfg ?


 * Un nouvelle entrée de menu est ajoutée, et porte le nom : .uno:ShowLicense. Si cette commande correspond avec un numéro de Slot existant vraiment, alors l'entrée de menu devient active, sinon, elle reste grisée.

Les changements sont faits dans GenericCommand.xcu,en respectant la syntaxe .xml. Le noeud suivant a été créé : .uno:ShowLicense :   L~icense ... L~icence ...

Ainsi, dans Outils -> Personnaliser, l'utilisateur aura la possibilité d'ajouter soit une nouvelle entrée de menu de type "Licence...", soit un nouveau bouton de type "Licence..." dans la barred 'outils de son choix.

L'exemple ci-dessous, décrit le cas de l'addition d'un nouvel item "Licence ..." dans un menu :



5) ce qui se passe dans sfx2

 * '''Un slot, vu comme une nouvelle commande, devient actif si sa déclaration est correcte, et si elle existe vraiment (sinon l'entrée de menu correspondante est grisée). Le rôle de sfx2 est de diffuser rediriger un événement tel que l'entrée de menu, losque celle-ci est "cliquée".
 * La détection de tous les slots actifs/inactifs, se fait au lancement d'OpenOffice.org (détection des services .etc).
 * Tout événement, vu comme l'émission de la constante SID_LICENSE sera diffusée et envoyée au framework. Elle sera alors vue comme une commande UNO de type :  .uno:ShowLicense, traitée dans appserv.cxx (dans framework/source/app/appserv.cxx )

Attention : dans ce qui suit, il y a peut être des valeurs erronnées. Dans ce cas, merci de me contacter en m'expliquant ce qui n'est pas correct, et doit être modifié.

Par exemple, j'ai un peu défini certaines valeurs au pif ;-)

Définition des nouvelles constantes
'''Comment choisir les bonnes valeurs ? '''

=> Il semble qu'il n'y ait pas de règles exact (juste faire attention à ne pas avoir de recouvrement des ID ressources)


 * Dans sfx2/inc/sfx2/sfx.hrc:

Added :

// ressource ID used in appserv.cxx


 * 1) define RID_DEFAULTLICENSE                                     (RID_SFX_START+20)

// constants used to define the dialog box content
 * 1) define LICENSE_BTN_OK                       6
 * 1) define LICENSE_FTXT_VERSION                 6
 * 2) define LICENSE_FTXT_COPYRIGHT               7
 * 1) define LICENSE_STR_DEVELOPER_ARY            6
 * 2) define LICENSE_STR_FRENCH_COPYRIGHT         7
 * 3) define LICENSE_STR_ACCEL                    8


 * in sfx2/inc/sfx2/sfxsids.hrc:

// ajouté la constante SID_LICENSE

// default-ids for application
 * 1) define SID_QUITAPP                        (SID_SFX_START + 300)
 * 2) define SID_ABOUT                          (SID_SFX_START + 301)
 * 3) define SID_SETUPPRINTER                   (SID_SFX_START + 302)
 * 4) define SID_EXITANDRETURN                  (SID_SFX_START + 303)
 * 5) define SID_LICENSE                        (SID_SFX_START + 304)

Définition du nouveau slot
Tout d'abord, ajouter l'item dans sfx2/sdi/sfx.sdi:SfxVoidItem ShowLicense SID_LICENSE

Merci de bien noter la syntaxe utilisée : SfxVoidItem ShowLicense SID_LICENSE


 * Le type est SfxVoidItem
 * Le nom de la méthode est ShowLicense
 * La constante associée est SID_LICENSE

Note : tout ceci est évidemment inspiré de l'équivalent SID_ABOUT

//--                                                           SfxVoidItem ShowLicense SID_LICENSE [                                                                                                                                              /* flags: */ AutoUpdate = FALSE, Cachable = Cachable, FastCall = FALSE, HasCoreId = FALSE, HasDialog = TRUE, ReadOnlyDoc = TRUE, Toggle = FALSE, Container = FALSE, RecordAbsolute = FALSE, RecordPerSet; Synchron; /* config: */ AccelConfig = TRUE, MenuConfig = TRUE, StatusBarConfig = FALSE, ToolBoxConfig = TRUE, GroupId = GID_APPLICATION; ]

Et maintenant, définissons l'interface. Cela signifie, que dans appserv.cxx, l'implémentation réelle utilisera la méthode MiscExec pour lancer l'initialisation, puis l'exécution de l'affichage de la boîte de dialogue.

Si vous lisez le fichier dont le nom figure ci-dessous, vos trouverez d'autres cas possibles, ce qui vous permettra d'adapter ce qui est exposé à votre cas.

Index: sfx2/sdi/appslots.sdi

=
======================================================                                               --- sfx2/sdi/appslots.sdi       (revision 274625) +++ sfx2/sdi/appslots.sdi      (working copy) @@ -52,6 +52,10 @@                                                                                                        [                                                                                                                          ExecMethod = MiscExec_Impl ; ]                                                                                                         +       SID_LICENSE // ole(no) api(final/play/rec) +      [                                                                                                          +               ExecMethod = MiscExec_Impl ; +      ]                                                                                                                  SID_SETOPTIONS [                                                                                                                         ExecMethod = MiscExec_Impl ;

Implémentation de la méthode
Maintenant, nous sommes dans sfx2/source/appl/appserv.cxx

Nous avons d'abord besoin d'inclure le fichier d'en tête showlicence.hxx (nouveau fichier d'en-tête, dérivé de about.hxx )

Donc, comme attendu, dans void SfxApplication::MiscExec_Impl( SfxRequest& rReq ), nous ajoutons le nouveau cas SID_LICENSE dans le switch :

+      case SID_LICENSE: +      {                                                                                                                 +                                                                                                                         +           String sBuildId( String::CreateFromAscii( "This text will explain the license OOo4Kids will use

Ce qui suit va contenir "License ..."

+                                                                                                                        +           rtl::OUString aLicenseContent( DEFINE_CONST_OUSTRING( "SHOW_LICENSE" ) );

Retrouver l'ID ressource :

+           // search for the resource of the license box +           ResId aDialogResId( RID_DEFAULTLICENSE, *pAppData_Impl->pLabelResMgr ); +           ResMgr* pResMgr = pAppData_Impl->pLabelResMgr; +           if( ! pResMgr->IsAvailable( aDialogResId.SetRT( RSC_MODALDIALOG ) ) ) +               pResMgr = GetOffResManager_Impl; +

... vérifier que c'est ok

+           aDialogResId.SetResMgr( pResMgr ); +           if ( !pResMgr->IsAvailable( aDialogResId ) ) +           {                                                                                                            +                DBG_ERRORFILE( "No RID_DEFAULTLICENSE in label-resource-dll" ); +           }                                                                                                            +

Une fois que tout est ok, alors afficher la boîte de dialogue (modale) :

+           // then show the license box +           ShowLicenseDialog* pDlg = new ShowLicenseDialog( 0, aDialogResId, sBuildId ); pDlg->Execute; delete pDlg; bDone = TRUE; +          break; +      }

6) La boîte de dialogue modale "ShowLicense"
TODO : décrire ..

Soit la classe : ShowLicenseDialog::ShowLicenseDialog( Window* pParent, const ResId& rId, const String& rVerStr ). Cette classe est un clone de la classe AboutDialog (voir about.hxx dans sfx2/inc ). Les ressources pour l'affichage sont gérées dans svx ( à suivre )

Justification: nous utilisons une classe, copiée sur la classe AboutDialog, pour contruire l'affichage de la licence. Les intérêts sont :

- classe modale - nous réutilisons le défilement pour faire apparaître le nom des contributeurs à OOo4Kids avec le contrôle : CTRL+SDT ( CMD+SDT sous Mac OS X) - nous affichons un .png (et non un .bmp ), car nous réutilisons le loader .png.

7) note about what happens in desktop
FIXME : why do we never hit that ?

In desktop, Desktop::HandleAppEvent should normaly catch the event, in app.cxx:2830 ( see desktop/source/app/app.cxx ), and if the catched string does contain "LICENSE". But for a reason I still ignore, we never hit this method ?? If ever we can reach the place, w<hat should happen is described below :

First, the string ".uno:ShowLicense", means the command label, will be created :

In the code :

app.cxx:2837, in void Desktop::HandleAppEvent( const ApplicationEvent& rAppEvent ) :

else if( rAppEvent.GetData.EqualsAscii( "LICENSE" ) ) aCommand.Complete = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( ".uno:ShowLicense" ) );

Then, once completed, the command is dispatched :

if( aCommand.Complete.getLength ) {                                                                                                                                          xParser->parseStrict(aCommand); css::uno::Reference< css::frame::XDispatch > xDispatch = xDesktop->queryDispatch(aCommand, rtl::OUString, 0); if (xDispatch.is) xDispatch->dispatch(aCommand, css::uno::Sequence< css::beans::PropertyValue >); }

Mac OS X (Aqua) part


Changements dans le code :

1) vcl :

--

Ajout de la constante dans vcl/inc/vcl/cmdevt.hxx
 * 1) HUNK1

+#if defined( OOo4Kids ) +#define SHOWDIALOG_ID_LICENCE         3 +#endif --


 * 1) HUNK 2

Ajout de la nouvelle entrée de menu Cocoa, en tant que chaîne de caractères, dans vcl/source/src/stdtxt.src

String SV_STDTEXT_LICENSE {                                                                                                                          Text [ en-US ] = "License "; Text [ fr ] = "Licence "; };                                                                                                                     --


 * 1) HUNK 3

Ajout de la nouvelle constante dans vcl/inc/vcl/menu.hxx :

Index: vcl/inc/vcl/menu.hxx

=
======================================================                                                    --- vcl/inc/vcl/menu.hxx        (revision 274625) +++ vcl/inc/vcl/menu.hxx       (working copy) @@ -90,6 +90,7 @@                                                                                                       +#define MIB_SHOWLICENSE                                ((MenuItemBits)0x0016) // not in rsc/vclsrc.hxx because only a prelimitary solution --
 * 1) define MIB_AUTOCHECK                 ((MenuItemBits)0x0004)
 * 2) define MIB_ABOUT                             ((MenuItemBits)0x0008)
 * 3) define MIB_HELP                              ((MenuItemBits)0x0010)
 * 1) define MIB_POPUPSELECT               ((MenuItemBits)0x0020)
 * 1) define MIB_NOSELECT              ((MenuItemBits)0x0040)

Ajout de la nouvelle constante dans vcl/inc/vcl/svids.hrc : Index: vcl/inc/vcl/svids.hrc

=
======================================================                                                    --- vcl/inc/vcl/svids.hrc       (revision 274625) +++ vcl/inc/vcl/svids.hrc      (working copy) @@ -108,6 +108,7 @@                                                                                                     +#define SV_STDTEXT_LICENSE                 10207
 * 1) define SV_STDTEXT_ABOUT                   10204
 * 2) define SV_STDTEXT_PREFERENCES             10205
 * 3) define SV_MAC_SCREENNNAME                 10206
 * 1) define SV_STDTEXT_LAST                                       SV_MAC_SCREENNNAME
 * 1) define SV_ACCESSERROR_FIRST                          SV_ACCESSERROR_WRONG_VERSION

--

Add the new entry in the Aqua menu ( vcl/aqua/source/window/salmenu.cxx ) @@ -146,11 +153,25 @@                                                                                                                           [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 3]; }                                                                                                 +                    // insert license entry +                   String aLicense( ResId( SV_STDTEXT_LICENSE, *pMgr ) ); +                   pString = CreateNSString( aLicense ); +                   pNewItem = [pAppMenu insertItemWithTitle: pString +                                        action: @selector(showLicense:) +                                        keyEquivalent: @"," +                                        atIndex: 4]; +                   if (pString) +                       [pString release]; +                   if( pNewItem ) +                   {                                                                                                  +                        [pNewItem setTarget: pMainMenuSelector]; +                       [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 5]; +                   }

+ définition de la méthode qui recevra et les événements :

Interface :

-(void)showLicense: (id)sender;

Implementation : -(void)showLicense: (id) sender {                                                                                                                         [self showDialog: SHOWDIALOG_ID_LICENSE]; }

IMPORTANT : ne pas oublier d'ajouter +2 à tous les indexes !! (sinon, les autres entrées de menu vont se superposer - effet Fun garanti- :-) )

--

2) svx:

dans svx/inc/globlmn_tmpl.hrc

--

-> ajouter l'item .uno:ShowLicence

As example :

Identifier = SID_ABOUT ; \ Command = ".uno:About" ; \ HelpID = SID_ABOUT ; \ Text [ en-US ] = "A~bout %PRODUCTNAME..." ; \
 * 1) define ITEM_HELP_ABOUT \

3) desktop

desktop/source/app/app.cxx

--


 * 1) HUNK 1

361    struct AboutBoxVersion 362    	: public rtl::Static< String, AboutBoxVersion > {};

--


 * 1) HUNK 2

+ see ReplaceStringHookProc( UniString& rStr )

=>

void ReplaceStringHookProc( UniString& rStr ) 374 {   375     static int nAll = 0, nPro = 0; 376    377 	nAll++; 378    if ( rStr.SearchAscii( "%PRODUCT" ) != STRING_NOTFOUND ) 379    {    380         String &rBrandName = BrandName::get; 381        String &rVersion = Version::get; 382        String &rAboutBoxVersion = AboutBoxVersion::get;

--
 * 1) HUNK 3

+ see : void Desktop::HandleAppEvent(

5) unotools

unotools/inc/unotools/configmgr.hxx#ABOUTBOXPRODUCTVERSION

See line around 90 :    enum ConfigProperty

And :

//direct readonly access to some special configuration elements 108            static com::sun::star::uno::Any GetDirectConfigProperty(ConfigProperty eProp); 109    110             sal_Bool        IsLocalConfigProvider; 111            com::sun::star::uno::Reference< com::sun::star::container::XHierarchicalNameAccess> 112                GetHierarchyAccess(const rtl::OUString& rFullPath); 113            com::sun::star::uno::Any GetLocalProperty(const rtl::OUString& rProperty); 114            void PutLocalProperty(const rtl::OUString&, const com::sun::star::uno::Any& rValue); 115


 * The extremely important article from Mathias Bauer : Implementation of the Dispatch API in sfx2


 * The background image
 * Put a mockup here.