ToolbarsAndUserLevel

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


 * Copyright Eric Bachard November 2009
 * '''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)

Introduction
Below, you'll find a technical description of the Toolbars depending on the User Level feature implementation. It's question there of : the C++ source code, the graphical layout - on the User Interface side, of the UNO Layout manager, of toolbars management in the sfx2 workwin, and some other technical points like read the User Level using the config manager. We suppose that the reader has the minimal knowledge to read and understand the content of what is exposed in this page.

The pre-requisites to read this article have still to be defined, but if ever the reader is not familiar with some points, we invite him to consult the OpenOffice.org wiki to complete his knowlege, and thus, to read this article in the best possible conditions.

Goal
The feature is coupled to the Choose the User Level in the preferences, and aims to have one different toolbar per level, in :


 * Writer;
 * Calc;
 * Draw;
 * Impress;
 * Math;
 * Chart.

For every application ( Writer, Calc, Draw, Math, Impress, Chart), the same toolbars will be used. The idea is to use :


 * either standardbar ( Expert User Level).
 * or average ( Average User Level ).
 * or beginner ( Beginner User Level ).

Rule : only one toolbar of the previous one, is supposed to be active in same time.

Specifications
The need has been described at the wiki page Ideas and Suggestions (french link, sorry).

Expected behavior : when changing the level in the prefs ( click below to enlarge ), the main toolbar changes.



Writer (as example)
As proposed, the idea is to have one different bar per user level.

The proposal does only concern Writer, but the feature will be extended to other applications ( waiting for teachers suggestions).

Beginner Level
For children 8 and 9 years :



Average Level
For 10 and 11 years children :



Expert Level
Over 11 years children :



In the Code
Work in progress

-> The changes concerning OOo4Kids are #ifdef OOo4Kids ... #endif protected

Concerned modules
In fact the feature has two sides, who must be considered there :


 * display one frame and retrieve the right toolbar, and ask the layout manager to display it correctly : sfx2, and the SfxWorkWindow are concerned
 * when changing the current user level : the layout manager, svx, and the preferences dialog box ( optgdlg.cxx ) are concerned (refresh e.g. )

Last : keep the possibility to add other toolbars (needs to be confirmed).

This is NOT the final solution : sc, sfx2, svx, svx use the same code to check the User Level in the Common.xcu file, and some code cleanup and factorization will be mandatory soon. Probably, tools or configmgr are the right place, to implement a class allowing to read in the Common.xcu, but this needs some love and investigations. Probably after 1.0

Module svx
The following parts must be modified in the optgdlg.cxx file (located in svx/source/cui) :


 * define static OUStrings, as helpers in the code.

static const OUString sULConfigSrvc( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationProvider" ) ); static const OUString sULAccessSrvc( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationUpdateAccess" ) ); static const OUString sULaNode( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Office.Common/Misc" ) ); static const OUString sULPropertyName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UserLevel" ) ) );

Other strings are usefull :

/* adapt the toolbars to the level */

static const OUString aLayoutManagerPropName( RTL_CONSTASCII_USTRINGPARAM( "LayoutManager" ) );

// The alignment bar // static const OUString sAlignmentToolbarTypeName( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/alignmentbar" ) ); // The standard toolbar static const OUString sStandardbarToolbarTypeName( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/standardbar" ) ); // The beginner toolbar (every application has its own) static const OUString sBeginnerbarToolbarTypeName( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/beginner" ) ); // The average toolbar (every application has its own) static const OUString sAveragebarToolbarTypeName( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/average" ) );


 * define setToolbarState : instantiate a Layout manager, then add ( requestElement(theToolbar) ) or remove a toolbar (destroyElement(theToolbar) )

static short setToolbarState( const OUString sSetAToolbarTypeName, bool isTrue ) {   try {       // avoids a crash if ever the user opens the prefs while the start center is the current view if ( ! (SfxViewFrame::Current) ) return -1; Reference< XPropertySet > xPropSet( SfxViewFrame::Current->GetFrame->GetFrameInterface, UNO_QUERY ); Reference< XLayoutManager > xLayoutManager;

if ( xPropSet.is ) {           Any aValue = xPropSet->getPropertyValue( aLayoutManagerPropName ); aValue >>= xLayoutManager; }

if ( !xLayoutManager.is ) return -1; else {           xLayoutManager->lock;

if ( isTrue ) xLayoutManager->requestElement( sSetAToolbarTypeName ); else xLayoutManager->destroyElement( sSetAToolbarTypeName );

// should refresh, but does not when showElement is used xLayoutManager->unlock; }   }    catch( Exception& ) {   }    return 0; }


 * initialize the toolbar state in the OfaMiscTabPage constructor : needs to retrieve the User Level in the Common.cxu.

Note: the toolbar are initialized in same time as the RadioButton.

// initialize the Radio button (User Level) switch ( getUserLevel ) {       case USER_LEVEL_BEGINNER: {	   fprintf(stdout, " %s : User level beginner \n", __func__);
 * 1) ifdef DEBUG
 * 1) endif

setToolbarState( sStandardbarToolbarTypeName, false); setToolbarState( sBeginnerbarToolbarTypeName, true ); setToolbarState( sAveragebarToolbarTypeName, false); aUserLevelBeginnerRB.SetState( TRUE ); }           break;

case USER_LEVEL_AVERAGE: {           fprintf(stdout, " %s : User level average \n", __func__);
 * 1) ifdef DEBUG
 * 1) endif

setToolbarState( sStandardbarToolbarTypeName, false); setToolbarState( sBeginnerbarToolbarTypeName, false ); setToolbarState( sAveragebarToolbarTypeName, true); aUserLevelAverageRB.SetState( TRUE ); }           break;

case USER_LEVEL_EXPERT: {           fprintf(stdout, " %s : User level expert \n", __func__); aUserLevelExpertRB.SetState( TRUE );
 * 1) ifdef DEBUG
 * 1) endif

setToolbarState( sStandardbarToolbarTypeName, true ); setToolbarState( sBeginnerbarToolbarTypeName, false ); setToolbarState( sAveragebarToolbarTypeName, false);

}           break; default: break; }
 * 1) endif // OOo4Kids


 * set the right toolbar when the User Level is modified (RadioButton) in the prefs : done in IMPL_LINK( OfaMiscTabPage, UserLevelCheckHdl_Impl, RadioButton*, pButton )

Note: to be sure,only one is set, while the two other are reset, for every user level.

IMPL_LINK( OfaMiscTabPage, UserLevelCheckHdl_Impl, RadioButton*, pButton ) {	(void) pButton; if ( aUserLevelExpertRB.IsChecked ) {       setUserLevel( USER_LEVEL_EXPERT );
 * 1) ifdef OOo4Kids

setToolbarState( sStandardbarToolbarTypeName, true ); setToolbarState( sBeginnerbarToolbarTypeName, false ); setToolbarState( sAveragebarToolbarTypeName, false); }   else if ( aUserLevelAverageRB.IsChecked ) {       setUserLevel( USER_LEVEL_AVERAGE );

setToolbarState( sStandardbarToolbarTypeName, false ); setToolbarState( sBeginnerbarToolbarTypeName, false ); setToolbarState( sAveragebarToolbarTypeName, true); }

else {       setUserLevel( USER_LEVEL_BEGINNER );

setToolbarState( sStandardbarToolbarTypeName, false ); setToolbarState( sBeginnerbarToolbarTypeName, true ); setToolbarState( sAveragebarToolbarTypeName, false); }   return 0; }

Module sfx2
Everything has been done in sfx2/source/appl/workwin.cxx. The role of the SfxWorkWindow is extremely important: for every new document, the toolbars to be drawn will be checked there. Other case: when closing the preference dialog box ( Modal dialog box)

What has been done:


 * define static OUStrings as helpers.

// The standard toolbar static const OUString sStandardbarToolbarTypeName( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/standardbar" ) ); // The beginner toolbar (every application has its own) static const OUString sBeginnerbarToolbarTypeName( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/beginner" ) ); // The average toolbar (every application has its own) static const OUString sAveragebarToolbarTypeName( RTL_CONSTASCII_USTRINGPARAM( "private:resource/toolbar/average" ) ); // Generic constants static const OUString sULConfigSrvc( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationProvider" ) ); static const OUString sULAccessSrvc( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.configuration.ConfigurationUpdateAccess" ) );


 * define getUserLevel to read the User Level in Common.xcu.

static short getUserLevel {   short dUserLevel = 1; // default is beginner try {	// get service provider Reference< XMultiServiceFactory > xSMgr( vcl::unohelper::GetMultiServiceFactory ); // create configuration hierachical access name if( xSMgr.is ) {           try {               Reference< XMultiServiceFactory > xConfigProvider(                    Reference< XMultiServiceFactory >( xSMgr->createInstance( sULConfigSrvc ), UNO_QUERY )                   ); if( xConfigProvider.is ) {                   Sequence< Any > aArgs(1); PropertyValue aVal; aVal.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "nodepath" ) ); aVal.Value <<= OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Office.Common/Misc" ) ); aArgs.getArray[0] <<= aVal; Reference< XNameAccess > xConfigAccess(                       Reference< XNameAccess >( xConfigProvider->createInstanceWithArguments( sULAccessSrvc, aArgs ), UNO_QUERY )                       ); if( xConfigAccess.is ) {                       try {                           short bValue = 1; Any aAny = xConfigAccess->getByName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UserLevel" ) ) ); if( aAny >>= bValue ) dUserLevel = bValue; }                       catch( NoSuchElementException& ) {                       }                        catch( WrappedTargetException& ) {                       }                    }                }            }            catch( Exception& ) {           }        }    }    catch( WrappedTargetException& ) {   }    return dUserLevel; }


 * add new ids, and names in the pToolBarResToName[] who will be used there.

Note: random values ... found no information in the code.

static const ResIdToResName pToolBarResToName[] = {   { 558,      "fullscreenbar"        }, { 560,     "standardbar",         },

...

{ 20001,   "beginner"      },      //writer { 20002,   "average"       },      //writer
 * 1) ifdef OOo4Kids
 * 1) endif

...


 * Read the User Level in the Common.xcu, and set the toolbars in SfxFrameWorkWin_Impl::UpdateObjectBars_Impl

Note: unlock the LayoutManager, triggers the refresh, but when the User Level is modified in the prefs, the refresh is triggered only when the dialog box (basically a modal window) is closed.

....

void SfxWorkWindow::UpdateObjectBars_Impl {

short int nUserLevel = getUserLevel; rtl::OUString aTbxId( m_aTbxTypeName ); aTbxId += GetResourceURLFromResId( aObjBarList[n].nId );
 * 1) ifdef OOo4Kids

switch ( nUserLevel ) {           case USER_LEVEL_BEGINNER: {               fprintf(stdout, " %s : User level beginner \n", __func__); fprintf(stdout, "nId = %d \n", nId ); fprintf(stdout, "aTbxId = %s \n", dbg_dump( aTbxId) ); //xLayoutManager->showElement( sBeginnerbarToolbarTypeName ); xLayoutManager->requestElement( sBeginnerbarToolbarTypeName ); xLayoutManager->destroyElement( sAveragebarToolbarTypeName ); xLayoutManager->destroyElement( sStandardbarToolbarTypeName ); }               break;
 * 1) ifdef DEBUG
 * 1) endif

case USER_LEVEL_AVERAGE: {               fprintf(stdout, " %s : User level average \n", __func__); fprintf(stdout, " nId = %d \n", nId ); //xLayoutManager->showElement( sAveragebarToolbarTypeName ); xLayoutManager->requestElement( sAveragebarToolbarTypeName ); xLayoutManager->destroyElement( sBeginnerbarToolbarTypeName ); xLayoutManager->destroyElement( sStandardbarToolbarTypeName ); }               break;
 * 1) ifdef DEBUG
 * 1) endif

case USER_LEVEL_EXPERT: {               fprintf(stdout, " %s : User level expert \n", __func__); fprintf(stdout, " nId = %d \n", nId ); //xLayoutManager->showElement( sStandardbarToolbarTypeName ); xLayoutManager->requestElement( sStandardbarToolbarTypeName ); xLayoutManager->destroyElement( sBeginnerbarToolbarTypeName ); xLayoutManager->destroyElement( sAveragebarToolbarTypeName ); }               break;
 * 1) ifdef DEBUG
 * 1) endif

default: break; }
 * 1) endif // OOo4Kids

...

}