| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the 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 */, |
| sal_True /* border */, |
| sal_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; |
| |
| Rectangle aBoundingRegion, aContentRegion; |
| bool bNativeOK = getTargetDevice().GetNativeControlRegion( |
| CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, |
| i_rContentArea, nState, |
| tiValue, ::rtl::OUString(), |
| aBoundingRegion, aContentRegion |
| ); |
| (void)bNativeOK; |
| OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::calculateDecorations: GetNativeControlRegion not implemented for CTRL_TAB_ITEM?!" ); |
| |
| return aBoundingRegion; |
| } |
| |
| //------------------------------------------------------------------------------------------------------------------ |
| 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; |
| |
| |
| bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, i_rContentRect, nState, tiValue, 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( sal_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( sal_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( sal_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( sal_Bool i_bCreate ) |
| { |
| Reference< XWindowPeer > xWindowPeer( Control::GetComponentInterface( sal_False ) ); |
| if ( !xWindowPeer.is() && i_bCreate ) |
| { |
| xWindowPeer.set( new PanelTabBarPeer( *this ) ); |
| SetComponentInterface( xWindowPeer ); |
| } |
| return xWindowPeer; |
| } |
| |
| //........................................................................ |
| } // namespace svt |
| //........................................................................ |