Framework/Article/Tool Panels Internals

From Apache OpenOffice Wiki
< Framework
Revision as of 07:13, 2 May 2012 by Ldb (Talk | contribs)

Jump to: navigation, search

OpenOffice.org Tool Panels Internals

User interface controllers in the mixed sfx2/uno environment

How user interface controllers work in a pure UNO based environment?

EnvironmentPureUnoUserInterfaceController.png

How user interface controllers work in our mixed sfx2/UNO based environment?

EnvironmentUserInterfaceController.png


Let's see what happens in more detail, especially when we switch from one layer to another one.

  1. A shell wants to invalidate a slot and calls SfxShell::Invalidate( SlotID ).
  2. The bindings updates the SfxStateCache for the provided slot ID normally through a update timer or directly.
  3. The SfxStateCache calls StateChanged with the new state on a SfxDispatchController_Impl object.
  4. The SfxDispatchController_Impl now maps the sfx2 state information based on SfxPoolItem to UNO information and sends them to UNO based toolbar controller.
...
 
::cppu::OInterfaceContainerHelper* pContnr = pDispatch->GetListeners().getContainer( aDispatchURL.Complete );
if ( bNotify && pContnr )
{
  ::com::sun::star::uno::Any aState;
  if ( ( eState >= SFX_ITEM_AVAILABLE ) && pState && !IsInvalidItem( pState ) && !pState->ISA(SfxVoidItem) )
  {
    // Retrieve metric from pool to have correct sub ID when calling QueryValue
    USHORT nSubId( 0 );
    SfxMapUnit eMapUnit( SFX_MAPUNIT_100TH_MM );
 
    // retrieve the core metric
    // it's enough to check the objectshell, the only shell that does not 
    // use the pool of the document is SfxViewFrame, but it hasn't any metric parameters
    if ( pSlotServ && pDispatcher )
    {
      SfxShell* pShell = pDispatcher->GetShell( pSlotServ->GetShellLevel() );
      DBG_ASSERT( pShell, "Can't get core metric without shell!" );
      if ( pShell )
        eMapUnit = GetCoreMetric( pShell->GetPool(), nSID );
    }
 
    if ( eMapUnit == SFX_MAPUNIT_TWIP )
      nSubId |= CONVERT_TWIPS;
    pState->QueryValue( aState, (BYTE)nSubId );
  }
  else if ( eState == SFX_ITEM_DONTCARE )
  {
    // Use special uno struct to transport don't care state
    ::com::sun::star::frame::status::ItemStatus aItemStatus;
    aItemStatus.State = ::com::sun::star::frame::status::ItemState::dont_care;
    aState = makeAny( aItemStatus );
  }
 
  ::com::sun::star::frame::FeatureStateEvent aEvent;
  aEvent.FeatureURL = aDispatchURL;
  aEvent.Source = (::com::sun::star::frame::XDispatch*) pDispatch;
  aEvent.IsEnabled = eState != SFX_ITEM_DISABLED;
  aEvent.Requery = sal_False;
  aEvent.State = aState;
 
  ::cppu::OInterfaceIteratorHelper aIt( *pContnr );
  while( aIt.hasMoreElements() )
  {
    try
    {
      ((::com::sun::star::frame::XStatusListener *)aIt.next())->statusChanged( aEvent );
    }
    catch( ::com::sun::star::uno::RuntimeException& )
    {
      aIt.remove();
    }
  }
}
 
...

How does QueryValue work and convert an item to UNO com::sun::star::uno::any?


You can find the base class of any pool item in svtools/inc/poolitem.hxx

class SVL_DLLPUBLIC SfxPoolItem
{
friend class SfxItemPool;
friend class SfxItemDesruptor_Impl;
friend class SfxItemPoolCache;
friend class SfxItemSet;
friend class SfxVoidItem;
 
 ULONG nRefCount; // Referenzzaehler
 USHORT nWhich;
 USHORT nKind;
 
private:
 inline void SetRefCount( ULONG n );
 inline void SetKind( USHORT n );
public:
 inline ULONG AddRef( ULONG n = 1 ) const;
private:
 inline ULONG ReleaseRef( ULONG n = 1 ) const;
 SVL_DLLPRIVATE long Delete_Impl(void*);
 
protected:
 SfxPoolItem( USHORT nWhich = 0 );
 SfxPoolItem( const SfxPoolItem& );
 
public:
 TYPEINFO();
 
 virtual ~SfxPoolItem();
 void SetWhich( USHORT nId ) 
 { DBG_CHKTHIS(SfxPoolItem, 0); nWhich = nId; }
 USHORT Which() const { DBG_CHKTHIS(SfxPoolItem, 0); return nWhich; }
 virtual int operator==( const SfxPoolItem& ) const = 0;
 int operator!=( const SfxPoolItem& rItem ) const
 { return !(*this == rItem); }
 virtual int Compare( const SfxPoolItem &rWith ) const;
 virtual int Compare( const SfxPoolItem &rWith, const IntlWrapper& rIntlWrapper ) const;
 
 virtual SfxItemPresentation GetPresentation( SfxItemPresentation ePresentation,
 SfxMapUnit eCoreMetric,
 SfxMapUnit ePresentationMetric,
 XubString &rText,
 const IntlWrapper * pIntlWrapper = 0 ) const;
 
 virtual USHORT GetVersion( USHORT nFileFormatVersion ) const;
 virtual int ScaleMetrics( long lMult, long lDiv );
 virtual int HasMetrics() const;
 
 virtual BOOL QueryValue( com::sun::star::uno::Any& rVal, BYTE nMemberId = 0 ) const;
 virtual BOOL PutValue( const com::sun::star::uno::Any& rVal, BYTE nMemberId = 0 );
 
 virtual SfxPoolItem* Create( SvStream &, USHORT nItemVersion ) const;
 virtual SvStream& Store( SvStream &, USHORT nItemVersion ) const;
 virtual SfxPoolItem* Clone( SfxItemPool *pPool = 0 ) const = 0;
 
 ULONG GetRefCount() const { return nRefCount; }
 inline USHORT GetKind() const { return nKind; }
 
 static bool readByteString(SvStream & rStream, UniString & rString);
 static void writeByteString(SvStream & rStream,UniString const & rString);
 static bool readUnicodeString(SvStream & rStream, UniString & rString, bool bUnicode);
 static void writeUnicodeString(SvStream & rStream, UniString const & rString);
 
private:
 SfxPoolItem& operator=( const SfxPoolItem& ); // n.i.!!
};

A example how to implement QueryValue can be found in the implementation of the SvxULSpaceItem (located in svx/source/items/frmitems.cxx).

sal_Bool SvxULSpaceItem::QueryValue( uno::Any& rVal, BYTE nMemberId ) const
{
  sal_Bool bConvert = 0!=(nMemberId&CONVERT_TWIPS);
  nMemberId &= ~CONVERT_TWIPS;
  switch( nMemberId )
  {
    // jetzt alles signed
    case 0:
    {
      ::com::sun::star::frame::status::UpperLowerMarginScale aUpperLowerMarginScale;
      aUpperLowerMarginScale.Upper = (sal_Int32)(bConvert ? TWIP_TO_MM100(nUpper) : nUpper);
      aUpperLowerMarginScale.Lower = (sal_Int32)(bConvert ? TWIP_TO_MM100(nLower) : nPropUpper);
      aUpperLowerMarginScale.ScaleUpper = (sal_Int16)nPropUpper;
      aUpperLowerMarginScale.ScaleLower = (sal_Int16)nPropLower;
      rVal <<= aUpperLowerMarginScale;
      break;
    }
    case MID_UP_MARGIN: rVal <<= (sal_Int32)(bConvert ? TWIP_TO_MM100(nUpper) : nUpper); break;
    case MID_LO_MARGIN: rVal <<= (sal_Int32)(bConvert ? TWIP_TO_MM100(nLower) : nLower); break;
    case MID_UP_REL_MARGIN: rVal <<= (sal_Int16) nPropUpper; break;
    case MID_LO_REL_MARGIN: rVal <<= (sal_Int16) nPropLower; break;
  }
  return sal_True;
}
  1. The UNO based toolbar controller now has to convert the state information (com::sun::star::uno::Any) again to provide it in a format that the sfx2 based toolbar controller implementation knows (SfxPoolItem).
// XStatusListener 
 
void SAL_CALL SfxToolBoxControl::statusChanged( const FeatureStateEvent& rEvent ) 
throw ( ::com::sun::star::uno::RuntimeException ) 
{ 
    SfxViewFrame* pViewFrame = NULL; 
    Reference < XController > xController; 
 
    ::vos::OGuard aGuard( Application::GetSolarMutex() ); 
    if ( getFrameInterface().is() ) 
        xController = getFrameInterface()->getController(); 
 
    Reference < XDispatchProvider > xProvider( xController, UNO_QUERY ); 
    if ( xProvider.is() ) 
    { 
        Reference < XDispatch > xDisp = xProvider->queryDispatch( 
            rEvent.FeatureURL, ::rtl::OUString(), 0 ); 
        if ( xDisp.is() ) 
        { 
            Reference< XUnoTunnel > xTunnel( xDisp, UNO_QUERY ); 
            SfxOfficeDispatch* pDisp = NULL; 
            if ( xTunnel.is() ) 
            { 
                sal_Int64 nImplementation = xTunnel>getSomething 
                SfxOfficeDispatch::impl_getStaticIdentifier() ); 
                pDisp = (SfxOfficeDispatch*)(nImplementation); 
            } 
 
            if ( pDisp ) 
                pViewFrame = pDisp->GetDispatcher_Impl()->GetFrame(); 
        } 
    } 
 
    USHORT nSlotId = 0; 
    SfxSlotPool& rPool = SFX_APP()->GetSlotPool( pViewFrame ); 
    const SfxSlot* pSlot = rPool.GetUnoSlot( rEvent.FeatureURL.Path ); 
    if ( pSlot ) 
        nSlotId = pSlot->GetSlotId(); 
 
    if ( nSlotId > 0 ) 
    { 
        if ( rEvent.Requery ) 
            svt::ToolboxController::statusChanged( rEvent ); 
        else 
        { 
            SfxItemState eState = SFX_ITEM_DISABLED; 
            SfxPoolItem* pItem = NULL; 
 
            if ( rEvent.IsEnabled ) 
            { 
                eState = SFX_ITEM_AVAILABLE; 
                ::com::sun::star::uno::Type pType = rEvent.State.getValueType(); 
 
                if ( pType == ::getVoidCppuType() ) 
                { 
                    pItem = new SfxVoidItem( nSlotId ); 
                    eState = SFX_ITEM_UNKNOWN; 
                } 
                else if ( pType == ::getBooleanCppuType() ) 
                { 
                    sal_Bool bTemp ; 
                    rEvent.State >>= bTemp ; 
                    pItem = new SfxBoolItem( nSlotId, bTemp ); 
                } 
                else if ( pType == ::getCppuType((const sal_uInt16*)0) ) 
                { 
                    sal_uInt16 nTemp ; 
                    rEvent.State >>= nTemp ; 
                    pItem = new SfxUInt16Item( nSlotId, nTemp ); 
                } 
                else if ( pType == ::getCppuType((const sal_uInt32*)0) ) 
                { 
                    sal_uInt32 nTemp ; 
                    rEvent.State >>= nTemp ; 
                    pItem = new SfxUInt32Item( nSlotId, nTemp ); 
                } 
                else if ( pType == ::getCppuType((const ::rtl::OUString*)0) ) 
                { 
                    ::rtl::OUString sTemp ; 
                    rEvent.State >>= sTemp ; 
                    pItem = new SfxStringItem( nSlotId, sTemp ); 
                } 
                else if ( pType == ::getCppuType(( const 
                    ::com::sun::star::frame::status::ItemStatus* )0) ) 
                { 
                    ItemStatus aItemStatus; 
                    rEvent.State >>= aItemStatus; 
                    eState = aItemStatus.State; 
                    pItem = new SfxVoidItem( nSlotId ); 
                } 
                else if ( pType == ::getCppuType(( const 
                    ::com::sun::star::frame::status::Visibility*)0) ) 
                { 
                    Visibility aVisibilityStatus; 
                    rEvent.State >>= aVisibilityStatus; 
                    pItem = new SfxVisibilityItem( nSlotId, aVisibilityStatus.bVisible ); 
                } 
                else 
                { 
                    if ( pSlot ) 
                        pItem = pSlot->GetType()->CreateItem(); 
                    if ( pItem ) 
                    { 
                        pItem->SetWhich( nSlotId ); 
                        pItem->PutValue( rEvent.State ); 
                    } 
                    else 
                        pItem = new SfxVoidItem( nSlotId ); 
                } 
           } 
 
           StateChanged( nSlotId, eState, pItem ); 
           delete pItem; 
        } 
    } 
}

As you can see the implementation retrieves the type of the slot and creates an empty item of that type. Now it has to use PutValue to convert the UNO com::sun::star::uno::Any back to an item. Let's see how the SvxULSpaceItem implements PutValue:

sal_Bool SvxULSpaceItem::PutValue( const uno::Any& rVal, BYTE nMemberId ) 
{ 
    sal_Bool bConvert = 0!=(nMemberId&CONVERT_TWIPS); 
    nMemberId &= ~CONVERT_TWIPS; 
 
    sal_Int32 nVal; 
    switch( nMemberId ) 
    { 
        case 0: 
        { 
            ::com::sun::star::frame::status::UpperLowerMarginScale aUpperLowerMarginScale; 
            if ( !(rVal >>= aUpperLowerMarginScale )) 
                return sal_False; 
            else 
            { 
                SetUpper((sal_uInt16)(bConvert ? 
                    MM100_TO_TWIP( aUpperLowerMarginScale.Upper ) : aUpperLowerMarginScale.Upper)); 
                SetLower((sal_uInt16)(bConvert ? 
                    MM100_TO_TWIP( aUpperLowerMarginScale.Lower ) : aUpperLowerMarginScale.Lower)); 
                if( aUpperLowerMarginScale.ScaleUpper > 1 ) 
                    nPropUpper = aUpperLowerMarginScale.ScaleUpper; 
                if( aUpperLowerMarginScale.ScaleLower > 1 ) 
                    nPropUpper = aUpperLowerMarginScale.ScaleLower; 
            } 
        } 
 
        case MID_UP_MARGIN : 
            if(!(rVal >>= nVal) || nVal < 0) 
                return sal_False; 
            SetUpper((sal_uInt16)bConvert ? MM100_TO_TWIP(nVal) : nVal); 
            break; 
 
         case MID_LO_MARGIN : 
            if(!(rVal >>= nVal) || nVal < 0) 
                return sal_False; 
            SetLower((sal_uInt16)bConvert ? MM100_TO_TWIP(nVal) : nVal); 
            break; 
 
         case MID_UP_REL_MARGIN: 
         case MID_LO_REL_MARGIN: 
         { 
             sal_Int32 nRel; 
             if((rVal >>= nRel) && nRel > 1 ) 
             { 
                 if(MID_UP_REL_MARGIN == nMemberId) 
                     nPropUpper = nRel; 
                 else 
                     nPropLower = nRel; 
             } 
             else 
                 return FALSE; 
         } 
         break; 
 
         default: 
             DBG_ERROR("unknown MemberId"); 
             return sal_False; 
   } 
 
   return sal_True; 
}

We should now see some very important rules that this conversion can work correctly.


  • Never use the value 0 of the nMemberID to convert the default part of an item. This will immediately break our user interface updates.
  • If you want to add a new slot that can be used by the user interface, your MUST support Query- and PutValue. At least the default value 0 must be implemented.
  • Never use other arguments to call a slot than the ones that are declared in your SDI file. The conversion methods in sfx2 use the type description of the slot to map UNO types to items and cannot do this for unknown arguments.
Personal tools