LicenseDialog/de
License Dialog
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
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.
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
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)
How things work
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 :
<node oor:name=".uno:ShowLicense" oor:op="replace"> <prop oor:name="Label" oor:type="xs:string"> <value xml:lang="en-US">L~icense ...</value> <value xml:lang="fr">L~icence ...</value> </prop> </node>
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
#define RID_DEFAULTLICENSE (RID_SFX_START+20)
// constants used to define the dialog box content
#define LICENSE_BTN_OK 6
#define LICENSE_FTXT_VERSION 6
#define LICENSE_FTXT_COPYRIGHT 7
#define LICENSE_STR_DEVELOPER_ARY 6
#define LICENSE_STR_FRENCH_COPYRIGHT 7
#define LICENSE_STR_ACCEL 8
- in sfx2/inc/sfx2/sfxsids.hrc:
// added SID_LICENSE constant // default-ids for application #define SID_QUITAPP (SID_SFX_START + 300) #define SID_ABOUT (SID_SFX_START + 301) #define SID_SETUPPRINTER (SID_SFX_START + 302) #define SID_EXITANDRETURN (SID_SFX_START + 303) #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 :
- HUNK1
Add the constant in vcl/inc/vcl/cmdevt.hxx
+#if defined( OOo4Kids ) +#define SHOWDIALOG_ID_LICENCE 3 +#endif
- 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 ";
};
- 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_AUTOCHECK ((MenuItemBits)0x0004) #define MIB_ABOUT ((MenuItemBits)0x0008) #define MIB_HELP ((MenuItemBits)0x0010) +#define MIB_SHOWLICENSE ((MenuItemBits)0x0016) #define MIB_POPUPSELECT ((MenuItemBits)0x0020) // not in rsc/vclsrc.hxx because only a prelimitary solution #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_ABOUT 10204
#define SV_STDTEXT_PREFERENCES 10205
#define SV_MAC_SCREENNNAME 10206
+#define SV_STDTEXT_LICENSE 10207
#define SV_STDTEXT_LAST SV_MAC_SCREENNNAME
#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 :
- define ITEM_HELP_ABOUT \
Identifier = SID_ABOUT ; \
Command = ".uno:About" ; \
HelpID = SID_ABOUT ; \
Text [ en-US ] = "A~bout %PRODUCTNAME..." ; \
3) desktop
desktop/source/app/app.cxx
- HUNK 1
361 struct AboutBoxVersion
362 : public rtl::Static< String, AboutBoxVersion > {};
- 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();
- 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
External links
- The extremely important article from Mathias Bauer : Implementation of the Dispatch API in sfx2
To do
- The background image
- Put a mockup here.


