LicenseDialog

All the changes added to the original OpenOffice.org source code provided in this page, are :


 * '''Under LGPL License V3, ( and you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation)
 * Copyright Eric Bachard 12th august 2009

State of the Implementation

 * Mac OS X (Aqua) part
 * Write the .xcu
 * Create the dialog box
 * Description of the ShowLicenseDialog Class
 * Connect to vcl
 * Connect to svx
 * Connect to sfx2
 * Connect to framework
 * Connect to unotools
 * Debug
 * Document

Introduction
Below, you'll find a technical description of the License menu entry feature. It is question of OpenOffice.org source code (mainly C++ and Objective C), e.g. the internal source code organisation in OpenOffice.org framework, the interaction with the graphical part aka vcl, .. and so on. Thus, we suppose the reader has the mandatory bases to understand the content of what is proposed.

The pre-requisites to read this article still have to be defined, but if the reader is not used with some notions, we invite him to have a look at the OpenOffice.org wiki to complete his knowledge, and read this article in the best conditions.

Specifications

 * Add an entry in all applications menus, allowing to check the OOo4Kids license.




 * Expected behavior:
 * click on the menu entry opens a modal window, explaining the OOo4Kids licence, and giving a link where to read it.
 * click OK closes the window

Way used

 * A new entry will be added in some menu

The menu will send an event, the application will receive, and turn into a uno command ( .uno:ShowLicense). the command will be dispatched, and the modal window, showing the text, will be open. Ok will close it

Other ways:


 * The GenericCommands.xcu will have an .uno:ShowLicense entry ( a command will be dispatched )

So either a new menu entry will be available in the list, '''or a new button "License ..." '''can be added to any toolbar, through the Options ->Customize menu.

As test, the menubar.xml of all applications will have the command available (for testing purpose)

1) starting from vcl
The description in this part is for the Mac OS X Aqua version only

On Mac OS X, Aqua version :

The string "License ..." (en-US) is created in vcl/source/src/stdtext.src (I have added the fr string, so it will appear localized in the menu)

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

'''At build time, the strings will be added in the vclfr.res (fr ) or vcl.res (en-US, default), and delivered, packaged .. and so on'''

Select the main Aqua menu : " File -> License... "

=> will send a "SHOW_DIALOG" event type, containing the string "LICENSE"

In the code:  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) ...in framework
Next step : since this is a system event, the Application (see the next point, in desktop) cannot catch the event, and the workaround is to start an eventlistener in framework, to catch all the hardcoded SHOWDIALOG events. This is exactly achieved in framework/source/dispatch/windowcommanddispatch.cxx

First a listener is launched ( void WindowCommandDispatch::impl_startListening ) in the Ctor.

Second, we create an OUString : const ::rtl::OUString WindowCommandDispatch::COMMAND_LICENSEBOX   = ::rtl::OUString::createFromAscii(".uno:ShowLicense"); Once the event is catched, we use a callback to dispatch it, using : IMPL_LINK(WindowCommandDispatch, impl_notifyCommand, void*, pParam) Who does : switch (nCommand) {                                                                                                            ...                                    case SHOWDIALOG_ID_LICENSE : sCommand = WindowCommandDispatch::COMMAND_LICENSEBOX; and finishes with : impl_dispatchCommand(sCommand);

Next step -> the command is catched and executed in sfx2

Suggestion : 

=> see the full description in : framework/source/inc/dispatch/windowcommanddispatch.cxx /** @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) from desktop to (unotools ?)
TODO : is the API already able to use it ?

4) officefg
What happens in officecfg ?


 * A new menu entry is created, with the name .uno:ShowLicense. If matching with a real slot id, the menu entry will become active, else it will stay greyed.

The changes have been made in GenericCommand.xcu, and respect .xml syntax. We added the "node" .uno:ShowLicense :   L~icense ... L~icence ...

In Tools -> Customize, the user will have the possibility to add either a new "License ..." menu entry, or a new "License ..." button in any toolbar.

As example, the image below describes the case of adding a new "License ..." menu entry :



5) what happens in sfx2

 * A slot, seen as a new command, will become active (else the menu entry will be greyed). The rôle of the sfx2, is to dispatch the menu entry when clicked
 * Any "event" seen as SID_LICENSE will be dispatched, and forwarded to framework, and will be treated as .uno:ShowLicense command in appserv.cxx

Be carefull: what follows, has maybe some wrong changes. Please contact me (and explain me what is wrong, and should be modified).

e.g. : I have randomly choosen some values ;-)

Define the new constants
'''What are the best values ? '''

=> it seems there is no exact rule (just take care there is no overlap)


 * in 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:

// added SID_LICENSE constant

// 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)

Define the new slot
First, add a new item in sfx2/sdi/sfx.sdi:SfxVoidItem ShowLicense SID_LICENSE

Please notice the syntax : SfxVoidItem ShowLicense SID_LICENSE


 * Type is SfxVoidItem
 * The name of the slot method is ShowLicense
 * the associated constant is SID_LICENSE

Note : obviously inspired from 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; ]

And now, define the interface. Means in the appserv.cxx, the real implementation will use the MiscExec method.

If you read other interfaces definitions in the same file, you'll see other possibilities.

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 ;

Implement the method
Now, we are in sfx2/source/appl/appserv.cxx

First, include the showlicence.hxx (new header file, derived from the about.hxx )

Then, as expected, in void SfxApplication::MiscExec_Impl( SfxRequest& rReq ), add the new case SID_LICENSE

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

What follows will contain "License ..."

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

Retrieve the resource ID

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

... verify that's ok

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

Once everything is ok, then display the dialog box :

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

6) The "ShowLicense" modal dialog box
TODO : describe

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


Changes in the code :

1) vcl :

--

Add the constant in vcl/inc/vcl/cmdevt.hxx
 * 1) HUNK1

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


 * 1) HUNK 2

Add the new Cocoa menu entry, as a string, in vcl/source/src/stdtxt.src

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


 * 1) HUNK 3

Add the new constant in 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)

Add a new constant in 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]; +                   }

+ define the method to receive the events :

Interface :

-(void)showLicense: (id)sender;

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

IMPORTANT : do not forget to add +2 to all indexes !! (else other menu entries will overlap :-) )

--

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.