diff options
Diffstat (limited to 'svtools/source/toolpanel/paneltabbar.cxx')
-rwxr-xr-x | svtools/source/toolpanel/paneltabbar.cxx | 1356 |
1 files changed, 1356 insertions, 0 deletions
diff --git a/svtools/source/toolpanel/paneltabbar.cxx b/svtools/source/toolpanel/paneltabbar.cxx new file mode 100755 index 000000000000..38aee00c8f55 --- /dev/null +++ b/svtools/source/toolpanel/paneltabbar.cxx @@ -0,0 +1,1356 @@ +/************************************************************************* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * +************************************************************************/ + +#include "precompiled_svtools.hxx" + +#include "svtools/toolpanel/paneltabbar.hxx" +#include "svtools/toolpanel/toolpaneldeck.hxx" +#include "svtools/svtdata.hxx" +#include "svtools/svtools.hrc" + +#include "tabitemdescriptor.hxx" +#include "paneltabbarpeer.hxx" +#include "tabbargeometry.hxx" + +#include <vcl/button.hxx> +#include <vcl/help.hxx> +#include <vcl/virdev.hxx> +#include <tools/diagnose_ex.h> + +#include <boost/optional.hpp> +#include <vector> + +// space around an item +#define ITEM_OUTER_SPACE 2 * 3 +// spacing before and after an item's text +#define ITEM_TEXT_FLOW_SPACE 5 +// space between item icon and icon text +#define ITEM_ICON_TEXT_DISTANCE 4 + +//........................................................................ +namespace svt +{ +//........................................................................ + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::awt::XWindowPeer; + + typedef sal_uInt16 ItemFlags; + + #define ITEM_STATE_NORMAL 0x00 + #define ITEM_STATE_ACTIVE 0x01 + #define ITEM_STATE_HOVERED 0x02 + #define ITEM_STATE_FOCUSED 0x04 + #define ITEM_POSITION_FIRST 0x08 + #define ITEM_POSITION_LAST 0x10 + + //================================================================================================================== + //= helper + //================================================================================================================== + namespace + { + ControlState lcl_ItemToControlState( const ItemFlags i_nItemFlags ) + { + ControlState nState = CTRL_STATE_ENABLED; + if ( i_nItemFlags & ITEM_STATE_FOCUSED ) nState |= CTRL_STATE_FOCUSED | CTRL_STATE_PRESSED; + if ( i_nItemFlags & ITEM_STATE_HOVERED ) nState |= CTRL_STATE_ROLLOVER; + if ( i_nItemFlags & ITEM_STATE_ACTIVE ) nState |= CTRL_STATE_SELECTED; + return nState; + } + } + + //================================================================================================================== + //= ITabBarRenderer + //================================================================================================================== + class SAL_NO_VTABLE ITabBarRenderer + { + public: + /** fills the background of our target device + */ + virtual void renderBackground() const = 0; + virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const = 0; + virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const = 0; + virtual void postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const = 0; + + // TODO: postRenderItem takes the "real" window, i.e. effectively the tab bar. This is because + // DrawSelectionBackground needs to be applied after everything else is painted, and is available at the Window + // class, but not at the OutputDevice. This makes the API somewhat weird, as we're now mixing operations on the + // target device, done in a normalized geometry, with operations on the window, done in a transformed geometry. + // So, we should get rid of postRenderItem completely. + }; + typedef ::boost::shared_ptr< ITabBarRenderer > PTabBarRenderer; + + //================================================================================================================== + //= VCLItemRenderer - declaration + //================================================================================================================== + class VCLItemRenderer : public ITabBarRenderer + { + public: + VCLItemRenderer( OutputDevice& i_rTargetDevice ) + :m_rTargetDevice( i_rTargetDevice ) + { + } + + // ITabBarRenderer + virtual void renderBackground() const; + virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const; + virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const; + virtual void postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const; + + protected: + OutputDevice& getTargetDevice() const { return m_rTargetDevice; } + + private: + OutputDevice& m_rTargetDevice; + }; + + //================================================================================================================== + //= VCLItemRenderer - implementation + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + void VCLItemRenderer::renderBackground() const + { + getTargetDevice().DrawRect( Rectangle( Point(), getTargetDevice().GetOutputSizePixel() ) ); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle VCLItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const + { + (void)i_nItemFlags; + // no decorations at all + return i_rContentArea; + } + + //------------------------------------------------------------------------------------------------------------------ + void VCLItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const + { + (void)i_rContentRect; + (void)i_nItemFlags; + } + + //------------------------------------------------------------------------------------------------------------------ + void VCLItemRenderer::postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const + { + const bool bActive = ( ( i_nItemFlags & ITEM_STATE_ACTIVE ) != 0 ); + const bool bHovered = ( ( i_nItemFlags & ITEM_STATE_HOVERED ) != 0 ); + const bool bFocused = ( ( i_nItemFlags & ITEM_STATE_FOCUSED ) != 0 ); + if ( bActive || bHovered || bFocused ) + { + Rectangle aSelectionRect( i_rItemRect ); + aSelectionRect.Left() += ITEM_OUTER_SPACE / 2; + aSelectionRect.Top() += ITEM_OUTER_SPACE / 2; + aSelectionRect.Right() -= ITEM_OUTER_SPACE / 2; + aSelectionRect.Bottom() -= ITEM_OUTER_SPACE / 2; + i_rActualWindow.DrawSelectionBackground( + aSelectionRect, + ( bHovered || bFocused ) ? ( bActive ? 1 : 2 ) : 0 /* hilight */, + bActive /* check */, + TRUE /* border */, + FALSE /* ext border only */, + 0 /* corner radius */, + NULL, + NULL + ); + } + } + + //================================================================================================================== + //= NWFToolboxItemRenderer - declaration + //================================================================================================================== + class NWFToolboxItemRenderer : public ITabBarRenderer + { + public: + NWFToolboxItemRenderer( OutputDevice& i_rTargetDevice ) + :m_rTargetDevice( i_rTargetDevice ) + { + } + + // ITabBarRenderer + virtual void renderBackground() const; + virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const; + virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const; + virtual void postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const; + + protected: + OutputDevice& getTargetDevice() const { return m_rTargetDevice; } + + private: + OutputDevice& m_rTargetDevice; + }; + + //================================================================================================================== + //= NWFToolboxItemRenderer - implementation + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + void NWFToolboxItemRenderer::renderBackground() const + { + getTargetDevice().DrawRect( Rectangle( Point(), getTargetDevice().GetOutputSizePixel() ) ); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle NWFToolboxItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const + { + // don't ask GetNativeControlRegion, this will not deliver proper results in all cases. + // Instead, simply assume that both the content and the bounding region are the same. +// const ControlState nState( lcl_ItemToControlState( i_nItemFlags ); +// const ImplControlValue aControlValue; +// bool bNativeOK = m_rTargetWindow.GetNativeControlRegion( +// CTRL_TOOLBAR, PART_BUTTON, +// i_rContentArea, nState, +// aControlValue, ::rtl::OUString(), +// aBoundingRegion, aContentRegion +// ); + (void)i_nItemFlags; + return Rectangle( + Point( i_rContentArea.Left() - 1, i_rContentArea.Top() - 1 ), + Size( i_rContentArea.GetWidth() + 2, i_rContentArea.GetHeight() + 2 ) + ); + } + + //------------------------------------------------------------------------------------------------------------------ + void NWFToolboxItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const + { + const ControlState nState = lcl_ItemToControlState( i_nItemFlags ); + + ImplControlValue aControlValue; + aControlValue.setTristateVal( ( i_nItemFlags & ITEM_STATE_ACTIVE ) ? BUTTONVALUE_ON : BUTTONVALUE_OFF ); + + bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TOOLBAR, PART_BUTTON, i_rContentRect, nState, aControlValue, rtl::OUString() ); + (void)bNativeOK; + OSL_ENSURE( bNativeOK, "NWFToolboxItemRenderer::preRenderItem: inconsistent NWF implementation!" ); + // IsNativeControlSupported returned true, previously, otherwise we would not be here ... + } + + //------------------------------------------------------------------------------------------------------------------ + void NWFToolboxItemRenderer::postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const + { + (void)i_rActualWindow; + (void)i_rItemRect; + (void)i_nItemFlags; + } + + //================================================================================================================== + //= NWFTabItemRenderer - declaration + //================================================================================================================== + class NWFTabItemRenderer : public ITabBarRenderer + { + public: + NWFTabItemRenderer( OutputDevice& i_rTargetDevice ) + :m_rTargetDevice( i_rTargetDevice ) + { + } + + // ITabBarRenderer + virtual void renderBackground() const; + virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const; + virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const; + virtual void postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const; + + protected: + OutputDevice& getTargetDevice() const { return m_rTargetDevice; } + + private: + OutputDevice& m_rTargetDevice; + }; + + //================================================================================================================== + //= NWFTabItemRenderer - implementation + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + void NWFTabItemRenderer::renderBackground() const + { + Rectangle aBackground( Point(), getTargetDevice().GetOutputSizePixel() ); + getTargetDevice().DrawRect( aBackground ); + + aBackground.Top() = aBackground.Bottom(); + getTargetDevice().DrawNativeControl( CTRL_TAB_PANE, PART_ENTIRE_CONTROL, aBackground, + CTRL_STATE_ENABLED, ImplControlValue(), ::rtl::OUString() ); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle NWFTabItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const + { + const ControlState nState( lcl_ItemToControlState( i_nItemFlags ) ); + + TabitemValue tiValue; + ImplControlValue aControlValue( (void*)(&tiValue) ); + + Region aBoundingRegion, aContentRegion; + bool bNativeOK = getTargetDevice().GetNativeControlRegion( + CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, + i_rContentArea, nState, + aControlValue, ::rtl::OUString(), + aBoundingRegion, aContentRegion + ); + (void)bNativeOK; + OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::calculateDecorations: GetNativeControlRegion not implemented for CTRL_TAB_ITEM?!" ); + + return aBoundingRegion.GetBoundRect(); + } + + //------------------------------------------------------------------------------------------------------------------ + void NWFTabItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const + { + const ControlState nState = lcl_ItemToControlState( i_nItemFlags ); + + TabitemValue tiValue; + if ( i_nItemFlags & ITEM_POSITION_FIRST ) + tiValue.mnAlignment |= TABITEM_FIRST_IN_GROUP; + if ( i_nItemFlags & ITEM_POSITION_LAST ) + tiValue.mnAlignment |= TABITEM_LAST_IN_GROUP; + + ImplControlValue aControlValue( (void *)(&tiValue) ); + + bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, i_rContentRect, nState, aControlValue, rtl::OUString() ); + (void)bNativeOK; + OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::preRenderItem: inconsistent NWF implementation!" ); + // IsNativeControlSupported returned true, previously, otherwise we would not be here ... + } + + //------------------------------------------------------------------------------------------------------------------ + void NWFTabItemRenderer::postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const + { + (void)i_rActualWindow; + (void)i_rItemRect; + (void)i_nItemFlags; + } + + //================================================================================================================== + //= PanelTabBar_Impl + //================================================================================================================== + class PanelTabBar_Impl : public IToolPanelDeckListener + { + public: + PanelTabBar_Impl( PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent ); + + ~PanelTabBar_Impl() + { + m_rPanelDeck.RemoveListener( *this ); + } + + // IToolPanelDeckListener + virtual void PanelInserted( const PToolPanel& i_pPanel, const size_t i_nPosition ) + { + (void)i_pPanel; + (void)i_nPosition; + m_bItemsDirty = true; + m_rTabBar.Invalidate(); + + Relayout(); + } + + virtual void PanelRemoved( const size_t i_nPosition ) + { + m_bItemsDirty = true; + m_rTabBar.Invalidate(); + + if ( i_nPosition < m_nScrollPosition ) + --m_nScrollPosition; + + Relayout(); + } + + virtual void ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive ); + virtual void LayouterChanged( const PDeckLayouter& i_rNewLayouter ); + virtual void Dying(); + + void UpdateScrollButtons() + { + m_aScrollBack.Enable( m_nScrollPosition > 0 ); + m_aScrollForward.Enable( m_nScrollPosition < m_aItems.size() - 1 ); + } + + void Relayout(); + void EnsureItemsCache(); + ::boost::optional< size_t > FindItemForPoint( const Point& i_rPoint ) const; + void DrawItem( const size_t i_nItemIndex, const Rectangle& i_rBoundaries ) const; + void InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags = 0 ) const; + void CopyFromRenderDevice( const Rectangle& i_rLogicalRect ) const; + Rectangle GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const; + Rectangle GetItemScreenRect( const size_t i_nItemPos ) const; + + void FocusItem( const ::boost::optional< size_t >& i_rItemPos ); + + inline bool IsVertical() const + { + return ( ( m_eTabAlignment == TABS_LEFT ) + || ( m_eTabAlignment == TABS_RIGHT ) + ); + } + + protected: + DECL_LINK( OnScroll, const PushButton* ); + + void impl_calcItemRects(); + Size impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const; + void impl_renderItemContent( const PToolPanel& i_pPanel, const Rectangle& i_rContentArea, const TabItemContent i_eItemContent ) const; + ItemFlags impl_getItemFlags( const size_t i_nItemIndex ) const; + + public: + PanelTabBar& m_rTabBar; + TabBarGeometry m_aGeometry; + NormalizedArea m_aNormalizer; + TabAlignment m_eTabAlignment; + IToolPanelDeck& m_rPanelDeck; + + VirtualDevice m_aRenderDevice; + PTabBarRenderer m_pRenderer; + + ::boost::optional< size_t > m_aHoveredItem; + ::boost::optional< size_t > m_aFocusedItem; + bool m_bMouseButtonDown; + + ItemDescriptors m_aItems; + bool m_bItemsDirty; + + PushButton m_aScrollBack; + PushButton m_aScrollForward; + + size_t m_nScrollPosition; + }; + + //================================================================================================================== + //= helper + //================================================================================================================== + namespace + { + //-------------------------------------------------------------------------------------------------------------- + #if OSL_DEBUG_LEVEL > 0 + static void lcl_checkConsistency( const PanelTabBar_Impl& i_rImpl ) + { + if ( !i_rImpl.m_bItemsDirty ) + { + if ( i_rImpl.m_rPanelDeck.GetPanelCount() != i_rImpl.m_aItems.size() ) + { + OSL_ENSURE( false, "lcl_checkConsistency: inconsistent array sizes!" ); + return; + } + for ( size_t i = 0; i < i_rImpl.m_rPanelDeck.GetPanelCount(); ++i ) + { + if ( i_rImpl.m_rPanelDeck.GetPanel( i ).get() != i_rImpl.m_aItems[i].pPanel.get() ) + { + OSL_ENSURE( false, "lcl_checkConsistency: array elements are inconsistent!" ); + return; + } + } + } + } + + #define DBG_CHECK( data ) \ + lcl_checkConsistency( data ); + #else + #define DBG_CHECK( data ) \ + (void)data; + #endif + + //-------------------------------------------------------------------------------------------------------------- + class ClipItemRegion + { + public: + ClipItemRegion( const PanelTabBar_Impl& i_rImpl ) + :m_rDevice( i_rImpl.m_rTabBar ) + { + m_rDevice.Push( PUSH_CLIPREGION ); + m_rDevice.SetClipRegion( i_rImpl.m_aNormalizer.getTransformed( i_rImpl.m_aGeometry.getItemsRect(), i_rImpl.m_eTabAlignment ) ); + } + + ~ClipItemRegion() + { + m_rDevice.Pop(); + } + + private: + OutputDevice& m_rDevice; + }; + } + + //================================================================================================================== + //= PanelTabBar_Impl - implementation + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + PanelTabBar_Impl::PanelTabBar_Impl( PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent ) + :m_rTabBar( i_rTabBar ) + ,m_aGeometry( i_eItemContent ) + ,m_aNormalizer() + ,m_eTabAlignment( i_eAlignment ) + ,m_rPanelDeck( i_rPanelDeck ) + ,m_aRenderDevice( i_rTabBar ) + ,m_pRenderer() + ,m_aHoveredItem() + ,m_aFocusedItem() + ,m_bMouseButtonDown( false ) + ,m_aItems() + ,m_bItemsDirty( true ) + ,m_aScrollBack( &i_rTabBar, WB_BEVELBUTTON ) + ,m_aScrollForward( &i_rTabBar, WB_BEVELBUTTON ) + ,m_nScrollPosition( 0 ) + { +#ifdef WNT + if ( m_aRenderDevice.IsNativeControlSupported( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL ) ) + // this mode requires the NWF framework to be able to render those items onto a virtual + // device. For some frameworks (some GTK themes, in particular), this is known to fail. + // So, be on the safe side for the moment. + m_pRenderer.reset( new NWFTabItemRenderer( m_aRenderDevice ) ); + else +#endif + if ( m_aRenderDevice.IsNativeControlSupported( CTRL_TOOLBAR, PART_BUTTON ) ) + m_pRenderer.reset( new NWFToolboxItemRenderer( m_aRenderDevice ) ); + else + m_pRenderer.reset( new VCLItemRenderer( m_aRenderDevice ) ); + + m_aRenderDevice.SetLineColor(); + + m_rPanelDeck.AddListener( *this ); + + m_aScrollBack.SetSymbol( IsVertical() ? SYMBOL_ARROW_UP : SYMBOL_ARROW_LEFT ); + m_aScrollBack.Show(); + m_aScrollBack.SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) ); + m_aScrollBack.SetAccessibleDescription( String( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_FWD ) ) ); + m_aScrollBack.SetAccessibleName( m_aScrollBack.GetAccessibleDescription() ); + + m_aScrollForward.SetSymbol( IsVertical() ? SYMBOL_ARROW_DOWN : SYMBOL_ARROW_RIGHT ); + m_aScrollForward.Show(); + m_aScrollForward.SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) ); + m_aScrollForward.SetAccessibleDescription( String( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_BACK ) ) ); + m_aScrollForward.SetAccessibleName( m_aScrollForward.GetAccessibleDescription() ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::impl_calcItemRects() + { + m_aItems.resize(0); + + Point aCompletePos( m_aGeometry.getFirstItemPosition() ); + Point aIconOnlyPos( aCompletePos ); + Point aTextOnlyPos( aCompletePos ); + + for ( size_t i = 0; + i < m_rPanelDeck.GetPanelCount(); + ++i + ) + { + PToolPanel pPanel( m_rPanelDeck.GetPanel( i ) ); + + ItemDescriptor aItem; + aItem.pPanel = pPanel; + + Rectangle aContentArea; + + const Size aCompleteSize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_AND_TEXT ) ); + const Size aIconOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_ONLY ) ); + const Size aTextOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_TEXT_ONLY ) ); + + // TODO: have one method calculating all sizes? + + // remember the three areas + aItem.aCompleteArea = Rectangle( aCompletePos, aCompleteSize ); + aItem.aIconOnlyArea = Rectangle( aIconOnlyPos, aIconOnlySize ); + aItem.aTextOnlyArea = Rectangle( aTextOnlyPos, aTextOnlySize ); + + m_aItems.push_back( aItem ); + + aCompletePos = aItem.aCompleteArea.TopRight(); + aIconOnlyPos = aItem.aIconOnlyArea.TopRight(); + aTextOnlyPos = aItem.aTextOnlyArea.TopRight(); + } + + m_bItemsDirty = false; + } + + //------------------------------------------------------------------------------------------------------------------ + Size PanelTabBar_Impl::impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const + { + // calculate the size needed for the content + OSL_ENSURE( i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_calculateItemContentSize: illegal TabItemContent value!" ); + + const Image aImage( i_pPanel->GetImage() ); + const bool bUseImage = !!aImage && ( i_eItemContent != TABITEM_TEXT_ONLY ); + + const ::rtl::OUString sItemText( i_pPanel->GetDisplayName() ); + const bool bUseText = ( sItemText.getLength() != 0 ) && ( i_eItemContent != TABITEM_IMAGE_ONLY ); + + Size aItemContentSize; + if ( bUseImage ) + { + aItemContentSize = aImage.GetSizePixel(); + } + + if ( bUseText ) + { + if ( bUseImage ) + aItemContentSize.Width() += ITEM_ICON_TEXT_DISTANCE; + + // add space for text + const Size aTextSize( m_rTabBar.GetCtrlTextWidth( sItemText ), m_rTabBar.GetTextHeight() ); + aItemContentSize.Width() += aTextSize.Width(); + aItemContentSize.Height() = ::std::max( aItemContentSize.Height(), aTextSize.Height() ); + + aItemContentSize.Width() += 2 * ITEM_TEXT_FLOW_SPACE; + } + + if ( !bUseImage && !bUseText ) + { + // have a minimal size - this is pure heuristics, but if it doesn't suit your needs, then give your panels + // a name and or image! :) + aItemContentSize = Size( 16, 16 ); + } + + aItemContentSize.Width() += 2 * ITEM_OUTER_SPACE; + aItemContentSize.Height() += 2 * ITEM_OUTER_SPACE; + + return aItemContentSize; + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::impl_renderItemContent( const PToolPanel& i_pPanel, const Rectangle& i_rContentArea, const TabItemContent i_eItemContent ) const + { + OSL_ENSURE( i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_renderItemContent: illegal TabItemContent value!" ); + + Rectangle aRenderArea( i_rContentArea ); + if ( IsVertical() ) + { + aRenderArea.Top() += ITEM_OUTER_SPACE; + } + else + { + aRenderArea.Left() += ITEM_OUTER_SPACE; + } + + // draw the image + const Image aItemImage( i_pPanel->GetImage() ); + const Size aImageSize( aItemImage.GetSizePixel() ); + const bool bUseImage = !!aItemImage && ( i_eItemContent != TABITEM_TEXT_ONLY ); + + if ( bUseImage ) + { + Point aImagePos; + if ( IsVertical() ) + { + aImagePos.X() = aRenderArea.Left() + ( aRenderArea.GetWidth() - aImageSize.Width() ) / 2; + aImagePos.Y() = aRenderArea.Top(); + } + else + { + aImagePos.X() = aRenderArea.Left(); + aImagePos.Y() = aRenderArea.Top() + ( aRenderArea.GetHeight() - aImageSize.Height() ) / 2; + } + m_rTabBar.DrawImage( aImagePos, aItemImage ); + } + + const ::rtl::OUString sItemText( i_pPanel->GetDisplayName() ); + const bool bUseText = ( sItemText.getLength() != 0 ) && ( i_eItemContent != TABITEM_IMAGE_ONLY ); + + if ( bUseText ) + { + if ( IsVertical() ) + { + if ( bUseImage ) + aRenderArea.Top() += aImageSize.Height() + ITEM_ICON_TEXT_DISTANCE; + aRenderArea.Top() += ITEM_TEXT_FLOW_SPACE; + } + else + { + if ( bUseImage ) + aRenderArea.Left() += aImageSize.Width() + ITEM_ICON_TEXT_DISTANCE; + aRenderArea.Left() += ITEM_TEXT_FLOW_SPACE; + } + + // draw the text + const Size aTextSize( m_rTabBar.GetCtrlTextWidth( sItemText ), m_rTabBar.GetTextHeight() ); + Point aTextPos( aRenderArea.TopLeft() ); + if ( IsVertical() ) + { + m_rTabBar.Push( PUSH_FONT ); + + Font aFont( m_rTabBar.GetFont() ); + aFont.SetOrientation( 2700 ); + aFont.SetVertical( TRUE ); + m_rTabBar.SetFont( aFont ); + + aTextPos.X() += aTextSize.Height(); + aTextPos.X() += ( aRenderArea.GetWidth() - aTextSize.Height() ) / 2; + } + else + { + aTextPos.Y() += ( aRenderArea.GetHeight() - aTextSize.Height() ) / 2; + } + + m_rTabBar.DrawText( aTextPos, sItemText ); + + if ( IsVertical() ) + { + m_rTabBar.Pop(); + } + } + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::CopyFromRenderDevice( const Rectangle& i_rLogicalRect ) const + { + BitmapEx aBitmap( m_aRenderDevice.GetBitmapEx( + i_rLogicalRect.TopLeft(), + Size( + i_rLogicalRect.GetSize().Width(), + i_rLogicalRect.GetSize().Height() + ) + ) ); + if ( IsVertical() ) + { + aBitmap.Rotate( 2700, COL_BLACK ); + if ( m_eTabAlignment == TABS_LEFT ) + aBitmap.Mirror( BMP_MIRROR_HORZ ); + } + else if ( m_eTabAlignment == TABS_BOTTOM ) + { + aBitmap.Mirror( BMP_MIRROR_VERT ); + } + + const Rectangle aActualRect( m_aNormalizer.getTransformed( i_rLogicalRect, m_eTabAlignment ) ); + m_rTabBar.DrawBitmapEx( aActualRect.TopLeft(), aBitmap ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags ) const + { + const ItemDescriptor& rItem( m_aItems[ i_nItemIndex ] ); + const ItemFlags nItemFlags( impl_getItemFlags( i_nItemIndex ) | i_nAdditionalItemFlags ); + + const Rectangle aNormalizedContent( GetActualLogicalItemRect( rItem.GetCurrentRect() ) ); + const Rectangle aNormalizedBounds( m_pRenderer->calculateDecorations( aNormalizedContent, nItemFlags ) ); + + const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment ); + m_rTabBar.Invalidate( aActualBounds ); + } + + //------------------------------------------------------------------------------------------------------------------ + ItemFlags PanelTabBar_Impl::impl_getItemFlags( const size_t i_nItemIndex ) const + { + ItemFlags nItemFlags( ITEM_STATE_NORMAL ); + if ( m_aHoveredItem == i_nItemIndex ) + { + nItemFlags |= ITEM_STATE_HOVERED; + if ( m_bMouseButtonDown ) + nItemFlags |= ITEM_STATE_ACTIVE; + } + + if ( m_rPanelDeck.GetActivePanel() == i_nItemIndex ) + nItemFlags |= ITEM_STATE_ACTIVE; + + if ( m_aFocusedItem == i_nItemIndex ) + nItemFlags |= ITEM_STATE_FOCUSED; + + if ( 0 == i_nItemIndex ) + nItemFlags |= ITEM_POSITION_FIRST; + + if ( m_rPanelDeck.GetPanelCount() - 1 == i_nItemIndex ) + nItemFlags |= ITEM_POSITION_LAST; + + return nItemFlags; + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::DrawItem( const size_t i_nItemIndex, const Rectangle& i_rBoundaries ) const + { + const ItemDescriptor& rItem( m_aItems[ i_nItemIndex ] ); + const ItemFlags nItemFlags( impl_getItemFlags( i_nItemIndex ) ); + + // the normalized bounding and content rect + const Rectangle aNormalizedContent( GetActualLogicalItemRect( rItem.GetCurrentRect() ) ); + const Rectangle aNormalizedBounds( m_pRenderer->calculateDecorations( aNormalizedContent, nItemFlags ) ); + + // check whether the item actually overlaps with the painting area + if ( !i_rBoundaries.IsEmpty() ) + { + const Rectangle aItemRect( GetActualLogicalItemRect( rItem.GetCurrentRect() ) ); + if ( !aItemRect.IsOver( i_rBoundaries ) ) + return; + } + + m_rTabBar.SetUpdateMode( FALSE ); + + // the aligned bounding and content rect + const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment ); + const Rectangle aActualContent = m_aNormalizer.getTransformed( aNormalizedContent, m_eTabAlignment ); + + // render item "background" layer + m_pRenderer->preRenderItem( aNormalizedContent, nItemFlags ); + + // copy from the virtual device to ourself + CopyFromRenderDevice( aNormalizedBounds ); + + // render the actual item content + impl_renderItemContent( rItem.pPanel, aActualContent, rItem.eContent ); + + // render item "foreground" layer + m_pRenderer->postRenderItem( m_rTabBar, aActualBounds, nItemFlags ); + + m_rTabBar.SetUpdateMode( TRUE ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::EnsureItemsCache() + { + if ( m_bItemsDirty == false ) + { + DBG_CHECK( *this ); + return; + } + impl_calcItemRects(); + OSL_POSTCOND( m_bItemsDirty == false, "PanelTabBar_Impl::EnsureItemsCache: cache still dirty!" ); + DBG_CHECK( *this ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::Relayout() + { + EnsureItemsCache(); + + const Size aOutputSize( m_rTabBar.GetOutputSizePixel() ); + m_aNormalizer = NormalizedArea( Rectangle( Point(), aOutputSize ), IsVertical() ); + const Size aLogicalOutputSize( m_aNormalizer.getReferenceSize() ); + + // forward actual output size to our render device + m_aRenderDevice.SetOutputSizePixel( aLogicalOutputSize ); + + // re-calculate the size of the scroll buttons and of the items + m_aGeometry.relayout( aLogicalOutputSize, m_aItems ); + + if ( m_aGeometry.getButtonBackRect().IsEmpty() ) + { + m_aScrollBack.Hide(); + } + else + { + const Rectangle aButtonBack( m_aNormalizer.getTransformed( m_aGeometry.getButtonBackRect(), m_eTabAlignment ) ); + m_aScrollBack.SetPosSizePixel( aButtonBack.TopLeft(), aButtonBack.GetSize() ); + m_aScrollBack.Show(); + } + + if ( m_aGeometry.getButtonForwardRect().IsEmpty() ) + { + m_aScrollForward.Hide(); + } + else + { + const Rectangle aButtonForward( m_aNormalizer.getTransformed( m_aGeometry.getButtonForwardRect(), m_eTabAlignment ) ); + m_aScrollForward.SetPosSizePixel( aButtonForward.TopLeft(), aButtonForward.GetSize() ); + m_aScrollForward.Show(); + } + + UpdateScrollButtons(); + } + + //------------------------------------------------------------------------------------------------------------------ + ::boost::optional< size_t > PanelTabBar_Impl::FindItemForPoint( const Point& i_rPoint ) const + { + Point aPoint( IsVertical() ? i_rPoint.Y() : i_rPoint.X(), IsVertical() ? i_rPoint.X() : i_rPoint.Y() ); + + if ( !m_aGeometry.getItemsRect().IsInside( aPoint ) ) + return ::boost::optional< size_t >(); + + size_t i=0; + for ( ItemDescriptors::const_iterator item = m_aItems.begin(); + item != m_aItems.end(); + ++item, ++i + ) + { + Rectangle aItemRect( GetActualLogicalItemRect( item->GetCurrentRect() ) ); + if ( aItemRect.IsInside( aPoint ) ) + { + return ::boost::optional< size_t >( i ); + } + } + return ::boost::optional< size_t >(); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle PanelTabBar_Impl::GetItemScreenRect( const size_t i_nItemPos ) const + { + ENSURE_OR_RETURN( i_nItemPos < m_aItems.size(), "PanelTabBar_Impl::GetItemScreenRect: invalid item pos!", Rectangle() ); + const ItemDescriptor& rItem( m_aItems[ i_nItemPos ] ); + const Rectangle aItemRect( m_aNormalizer.getTransformed( + GetActualLogicalItemRect( rItem.GetCurrentRect() ), + m_eTabAlignment ) ); + + const Rectangle aTabBarRect( m_rTabBar.GetWindowExtentsRelative( NULL ) ); + return Rectangle( + Point( aTabBarRect.Left() + aItemRect.Left(), aTabBarRect.Top() + aItemRect.Top() ), + aItemRect.GetSize() + ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::FocusItem( const ::boost::optional< size_t >& i_rItemPos ) + { + // reset old focus item + if ( !!m_aFocusedItem ) + InvalidateItem( *m_aFocusedItem ); + m_aFocusedItem.reset(); + + // mark the active icon as focused + if ( !!i_rItemPos ) + { + m_aFocusedItem = i_rItemPos; + InvalidateItem( *m_aFocusedItem ); + } + } + + //------------------------------------------------------------------------------------------------------------------ + IMPL_LINK( PanelTabBar_Impl, OnScroll, const PushButton*, i_pButton ) + { + if ( i_pButton == &m_aScrollBack ) + { + OSL_ENSURE( m_nScrollPosition > 0, "PanelTabBar_Impl::OnScroll: inconsistency!" ); + --m_nScrollPosition; + m_rTabBar.Invalidate(); + } + else if ( i_pButton == &m_aScrollForward ) + { + OSL_ENSURE( m_nScrollPosition < m_aItems.size() - 1, "PanelTabBar_Impl::OnScroll: inconsistency!" ); + ++m_nScrollPosition; + m_rTabBar.Invalidate(); + } + + UpdateScrollButtons(); + + return 0L; + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle PanelTabBar_Impl::GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const + { + // care for the offset imposed by our geometry, i.e. whether or not we have scroll buttons + Rectangle aItemRect( i_rLogicalItemRect ); + aItemRect.Move( m_aGeometry.getItemsRect().Left() - m_aGeometry.getButtonBackRect().Left(), 0 ); + + // care for the current scroll position + OSL_ENSURE( m_nScrollPosition < m_aItems.size(), "GetActualLogicalItemRect: invalid scroll position!" ); + if ( ( m_nScrollPosition > 0 ) && ( m_nScrollPosition < m_aItems.size() ) ) + { + long nOffsetX = m_aItems[ m_nScrollPosition ].GetCurrentRect().Left() - m_aItems[ 0 ].GetCurrentRect().Left(); + long nOffsetY = m_aItems[ m_nScrollPosition ].GetCurrentRect().Top() - m_aItems[ 0 ].GetCurrentRect().Top(); + aItemRect.Move( -nOffsetX, -nOffsetY ); + } + + return aItemRect; + } + + //================================================================================================================== + //= PanelTabBar_Impl + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive ) + { + EnsureItemsCache(); + + if ( !!i_rOldActive ) + InvalidateItem( *i_rOldActive, ITEM_STATE_ACTIVE ); + if ( !!i_rNewActive ) + InvalidateItem( *i_rNewActive ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::LayouterChanged( const PDeckLayouter& i_rNewLayouter ) + { + // not interested in + (void)i_rNewLayouter; + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar_Impl::Dying() + { + // not interested in - the notifier is a member of this instance here, so we're dying ourself at the moment + } + + //================================================================================================================== + //= PanelTabBar + //================================================================================================================== + //------------------------------------------------------------------------------------------------------------------ + PanelTabBar::PanelTabBar( Window& i_rParentWindow, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent ) + :Control( &i_rParentWindow, 0 ) + ,m_pImpl( new PanelTabBar_Impl( *this, i_rPanelDeck, i_eAlignment, i_eItemContent ) ) + { + DBG_CHECK( *m_pImpl ); + } + + //------------------------------------------------------------------------------------------------------------------ + PanelTabBar::~PanelTabBar() + { + } + + //------------------------------------------------------------------------------------------------------------------ + TabItemContent PanelTabBar::GetTabItemContent() const + { + return m_pImpl->m_aGeometry.getItemContent(); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::SetTabItemContent( const TabItemContent& i_eItemContent ) + { + m_pImpl->m_aGeometry.setItemContent( i_eItemContent ); + m_pImpl->Relayout(); + Invalidate(); + } + + //------------------------------------------------------------------------------------------------------------------ + IToolPanelDeck& PanelTabBar::GetPanelDeck() const + { + DBG_CHECK( *m_pImpl ); + return m_pImpl->m_rPanelDeck; + } + + //------------------------------------------------------------------------------------------------------------------ + Size PanelTabBar::GetOptimalSize( WindowSizeType i_eType ) const + { + m_pImpl->EnsureItemsCache(); + Size aOptimalSize( m_pImpl->m_aGeometry.getOptimalSize( m_pImpl->m_aItems, i_eType == WINDOWSIZE_MINIMUM ) ); + if ( m_pImpl->IsVertical() ) + ::std::swap( aOptimalSize.Width(), aOptimalSize.Height() ); + return aOptimalSize; + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::Resize() + { + Control::Resize(); + m_pImpl->Relayout(); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::Paint( const Rectangle& i_rRect ) + { + m_pImpl->EnsureItemsCache(); + + // background + const Rectangle aNormalizedPaintArea( m_pImpl->m_aNormalizer.getNormalized( i_rRect, m_pImpl->m_eTabAlignment ) ); + m_pImpl->m_aRenderDevice.Push( PUSH_CLIPREGION ); + m_pImpl->m_aRenderDevice.SetClipRegion( aNormalizedPaintArea ); + m_pImpl->m_pRenderer->renderBackground(); + m_pImpl->m_aRenderDevice.Pop(); + m_pImpl->CopyFromRenderDevice( aNormalizedPaintArea ); + + // ensure the items really paint into their own playground only + ClipItemRegion aClipItems( *m_pImpl ); + + const Rectangle aLogicalPaintRect( m_pImpl->m_aNormalizer.getNormalized( i_rRect, m_pImpl->m_eTabAlignment ) ); + + const ::boost::optional< size_t > aActivePanel( m_pImpl->m_rPanelDeck.GetActivePanel() ); + const ::boost::optional< size_t > aHoveredPanel( m_pImpl->m_aHoveredItem ); + + // items: + // 1. paint all non-active, non-hovered items + size_t i=0; + for ( ItemDescriptors::const_iterator item = m_pImpl->m_aItems.begin(); + item != m_pImpl->m_aItems.end(); + ++item, ++i + ) + { + if ( i == aActivePanel ) + continue; + + if ( aHoveredPanel == i ) + continue; + + m_pImpl->DrawItem( i, aLogicalPaintRect ); + } + + // 2. paint the item which is hovered, /without/ the mouse button pressed down + if ( !!aHoveredPanel && !m_pImpl->m_bMouseButtonDown ) + m_pImpl->DrawItem( *aHoveredPanel, aLogicalPaintRect ); + + // 3. paint the active item + if ( !!aActivePanel ) + m_pImpl->DrawItem( *aActivePanel, aLogicalPaintRect ); + + // 4. paint the item which is hovered, /with/ the mouse button pressed down + if ( !!aHoveredPanel && m_pImpl->m_bMouseButtonDown ) + m_pImpl->DrawItem( *aHoveredPanel, aLogicalPaintRect ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::MouseMove( const MouseEvent& i_rMouseEvent ) + { + m_pImpl->EnsureItemsCache(); + + ::boost::optional< size_t > aOldItem( m_pImpl->m_aHoveredItem ); + ::boost::optional< size_t > aNewItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) ); + + if ( i_rMouseEvent.IsLeaveWindow() ) + aNewItem.reset(); + + if ( aOldItem != aNewItem ) + { + if ( !!aOldItem ) + m_pImpl->InvalidateItem( *aOldItem ); + + m_pImpl->m_aHoveredItem = aNewItem; + + if ( !!aNewItem ) + m_pImpl->InvalidateItem( *aNewItem ); + } + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::MouseButtonDown( const MouseEvent& i_rMouseEvent ) + { + Control::MouseButtonDown( i_rMouseEvent ); + + if ( !i_rMouseEvent.IsLeft() ) + return; + + m_pImpl->EnsureItemsCache(); + + ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) ); + if ( !aHitItem ) + return; + + CaptureMouse(); + m_pImpl->m_bMouseButtonDown = true; + + m_pImpl->InvalidateItem( *aHitItem ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::MouseButtonUp( const MouseEvent& i_rMouseEvent ) + { + Control::MouseButtonUp( i_rMouseEvent ); + + if ( m_pImpl->m_bMouseButtonDown ) + { + ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) ); + if ( !!aHitItem ) + { + // re-draw that item now that we're not in mouse-down mode anymore + m_pImpl->InvalidateItem( *aHitItem ); + // activate the respective panel + m_pImpl->m_rPanelDeck.ActivatePanel( *aHitItem ); + } + + OSL_ENSURE( IsMouseCaptured(), "PanelTabBar::MouseButtonUp: inconsistency!" ); + if ( IsMouseCaptured() ) + ReleaseMouse(); + m_pImpl->m_bMouseButtonDown = false; + } + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::RequestHelp( const HelpEvent& i_rHelpEvent ) + { + m_pImpl->EnsureItemsCache(); + + ::boost::optional< size_t > aHelpItem( m_pImpl->FindItemForPoint( ScreenToOutputPixel( i_rHelpEvent.GetMousePosPixel() ) ) ); + if ( !aHelpItem ) + return; + + const ItemDescriptor& rItem( m_pImpl->m_aItems[ *aHelpItem ] ); + if ( rItem.eContent != TABITEM_IMAGE_ONLY ) + // if the text is displayed for the item, we do not need to show it as tooltip + return; + + const ::rtl::OUString sItemText( rItem.pPanel->GetDisplayName() ); + if ( i_rHelpEvent.GetMode() == HELPMODE_BALLOON ) + Help::ShowBalloon( this, OutputToScreenPixel( rItem.GetCurrentRect().Center() ), rItem.GetCurrentRect(), sItemText ); + else + Help::ShowQuickHelp( this, rItem.GetCurrentRect(), sItemText ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::GetFocus() + { + Control::GetFocus(); + if ( !m_pImpl->m_aFocusedItem ) + m_pImpl->FocusItem( m_pImpl->m_rPanelDeck.GetActivePanel() ); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::LoseFocus() + { + Control::LoseFocus(); + + if ( !!m_pImpl->m_aFocusedItem ) + { + m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem ); + } + + m_pImpl->m_aFocusedItem.reset(); + } + + //------------------------------------------------------------------------------------------------------------------ + class KeyInputHandler + { + public: + KeyInputHandler( Control& i_rControl, const KeyEvent& i_rKeyEvent ) + :m_rControl( i_rControl ) + ,m_rKeyEvent( i_rKeyEvent ) + ,m_bHandled( false ) + { + } + + ~KeyInputHandler() + { + if ( !m_bHandled ) + m_rControl.Control::KeyInput( m_rKeyEvent ); + } + + void setHandled() + { + m_bHandled = true; + } + + private: + Control& m_rControl; + const KeyEvent& m_rKeyEvent; + bool m_bHandled; + }; + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::KeyInput( const KeyEvent& i_rKeyEvent ) + { + KeyInputHandler aKeyInputHandler( *this, i_rKeyEvent ); + + const KeyCode& rKeyCode( i_rKeyEvent.GetKeyCode() ); + if ( rKeyCode.GetModifier() != 0 ) + // only interested in mere key presses + return; + + // if there are less than 2 panels, we cannot travel them ... + const size_t nPanelCount( m_pImpl->m_rPanelDeck.GetPanelCount() ); + if ( nPanelCount < 2 ) + return; + + OSL_PRECOND( !!m_pImpl->m_aFocusedItem, "PanelTabBar::KeyInput: we should have a focused item here!" ); + // if we get KeyInput events, we should have the focus. In this case, m_aFocusedItem should not be empty, + // except if there are no panels, but then we bail out of this method here earlier ... + + bool bFocusNext = false; + bool bFocusPrev = false; + + switch ( rKeyCode.GetCode() ) + { + case KEY_UP: bFocusPrev = true; break; + case KEY_DOWN: bFocusNext = true; break; + case KEY_LEFT: + if ( IsRTLEnabled() ) + bFocusNext = true; + else + bFocusPrev = true; + break; + case KEY_RIGHT: + if ( IsRTLEnabled() ) + bFocusPrev = true; + else + bFocusNext = true; + break; + case KEY_RETURN: + m_pImpl->m_rPanelDeck.ActivatePanel( *m_pImpl->m_aFocusedItem ); + break; + } + + if ( !bFocusNext && !bFocusPrev ) + return; + + m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem ); + if ( bFocusNext ) + { + m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + 1 ) % nPanelCount ); + } + else + { + m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + nPanelCount - 1 ) % nPanelCount ); + } + m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem ); + + // don't delegate to base class + aKeyInputHandler.setHandled(); + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::DataChanged( const DataChangedEvent& i_rDataChanedEvent ) + { + Control::DataChanged( i_rDataChanedEvent ); + + if ( ( i_rDataChanedEvent.GetType() == DATACHANGED_SETTINGS ) + && ( ( i_rDataChanedEvent.GetFlags() & SETTINGS_STYLE ) != 0 ) + ) + { + Invalidate(); + } + } + + //------------------------------------------------------------------------------------------------------------------ + bool PanelTabBar::IsVertical() const + { + return m_pImpl->IsVertical(); + } + + //------------------------------------------------------------------------------------------------------------------ + PushButton& PanelTabBar::GetScrollButton( const bool i_bForward ) + { + return i_bForward ? m_pImpl->m_aScrollForward : m_pImpl->m_aScrollBack; + } + + //------------------------------------------------------------------------------------------------------------------ + ::boost::optional< size_t > PanelTabBar::GetFocusedPanelItem() const + { + return m_pImpl->m_aFocusedItem; + } + + //------------------------------------------------------------------------------------------------------------------ + void PanelTabBar::FocusPanelItem( const size_t i_nItemPos ) + { + ENSURE_OR_RETURN_VOID( i_nItemPos < m_pImpl->m_rPanelDeck.GetPanelCount(), "PanelTabBar::FocusPanelItem: illegal item pos!" ); + + if ( !HasChildPathFocus() ) + GrabFocus(); + + m_pImpl->FocusItem( i_nItemPos ); + OSL_POSTCOND( !!m_pImpl->m_aFocusedItem, "PanelTabBar::FocusPanelItem: have the focus, but no focused item?" ); + if ( !!m_pImpl->m_aFocusedItem ) + m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem ); + m_pImpl->m_aFocusedItem.reset( i_nItemPos ); + } + + //------------------------------------------------------------------------------------------------------------------ + Rectangle PanelTabBar::GetItemScreenRect( const size_t i_nItemPos ) const + { + return m_pImpl->GetItemScreenRect( i_nItemPos ); + } + + //------------------------------------------------------------------------------------------------------------------ + Reference< XWindowPeer > PanelTabBar::GetComponentInterface( BOOL i_bCreate ) + { + Reference< XWindowPeer > xWindowPeer( Control::GetComponentInterface( FALSE ) ); + if ( !xWindowPeer.is() && i_bCreate ) + { + xWindowPeer.set( new PanelTabBarPeer( *this ) ); + SetComponentInterface( xWindowPeer ); + } + return xWindowPeer; + } + +//........................................................................ +} // namespace svt +//........................................................................ |