d9a00ace2c
Change-Id: I611a276d048121084ec842d062129e042f3baea5
1368 lines
56 KiB
C++
1368 lines
56 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*************************************************************************
|
|
* 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 "svtools/toolpanel/paneltabbar.hxx"
|
|
#include "svtools/toolpanel/toolpaneldeck.hxx"
|
|
#include "svtools/svtresid.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.
|
|
|
|
protected:
|
|
~ITabBarRenderer() {}
|
|
};
|
|
typedef ::boost::shared_ptr< ITabBarRenderer > PTabBarRenderer;
|
|
|
|
//==================================================================================================================
|
|
//= VCLItemRenderer - declaration
|
|
//==================================================================================================================
|
|
class VCLItemRenderer : public ITabBarRenderer
|
|
{
|
|
public:
|
|
VCLItemRenderer( OutputDevice& i_rTargetDevice )
|
|
:m_rTargetDevice( i_rTargetDevice )
|
|
{
|
|
}
|
|
virtual ~VCLItemRenderer() {}
|
|
|
|
// 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 )
|
|
{
|
|
}
|
|
virtual ~NWFToolboxItemRenderer() {}
|
|
|
|
// 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 )
|
|
{
|
|
}
|
|
|
|
virtual ~NWFTabItemRenderer() {}
|
|
|
|
// 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 );
|
|
|
|
virtual ~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_FAIL( "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_FAIL( "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( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_FWD ).toString() );
|
|
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( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_BACK ).toString() );
|
|
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.isEmpty() ) && ( 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.isEmpty() ) && ( 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 = ::boost::optional< size_t >();
|
|
|
|
bool const bChanged(
|
|
( !aOldItem && aNewItem )
|
|
|| ( aOldItem && !aNewItem )
|
|
|| ( aOldItem && aNewItem && aOldItem != aNewItem ) )
|
|
;
|
|
if ( bChanged )
|
|
{
|
|
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
|
|
//........................................................................
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|