Bump version to 5.0-14
[LibreOffice.git] / svtools / source / toolpanel / paneltabbar.cxx
blobb1f28b5ed2a47570604cd7bc5b204011f95abefe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <svtools/toolpanel/paneltabbar.hxx>
22 #include <svtools/toolpanel/toolpaneldeck.hxx>
23 #include <svtools/svtresid.hxx>
24 #include <svtools/svtools.hrc>
26 #include "tabitemdescriptor.hxx"
27 #include "paneltabbarpeer.hxx"
28 #include "tabbargeometry.hxx"
30 #include <vcl/button.hxx>
31 #include <vcl/help.hxx>
32 #include <vcl/virdev.hxx>
33 #include <vcl/settings.hxx>
34 #include <tools/diagnose_ex.h>
36 #include <boost/optional.hpp>
37 #include <vector>
39 // space around an item
40 #define ITEM_OUTER_SPACE 2 * 3
41 // spacing before and after an item's text
42 #define ITEM_TEXT_FLOW_SPACE 5
43 // space between item icon and icon text
44 #define ITEM_ICON_TEXT_DISTANCE 4
47 namespace svt
51 using ::com::sun::star::uno::Reference;
52 using ::com::sun::star::awt::XWindowPeer;
54 typedef sal_uInt16 ItemFlags;
56 #define ITEM_STATE_NORMAL 0x00
57 #define ITEM_STATE_ACTIVE 0x01
58 #define ITEM_STATE_HOVERED 0x02
59 #define ITEM_STATE_FOCUSED 0x04
60 #define ITEM_POSITION_FIRST 0x08
61 #define ITEM_POSITION_LAST 0x10
64 //= helper
66 namespace
68 ControlState lcl_ItemToControlState( const ItemFlags i_nItemFlags )
70 ControlState nState = ControlState::ENABLED;
71 if ( i_nItemFlags & ITEM_STATE_FOCUSED ) nState |= ControlState::FOCUSED | ControlState::PRESSED;
72 if ( i_nItemFlags & ITEM_STATE_HOVERED ) nState |= ControlState::ROLLOVER;
73 if ( i_nItemFlags & ITEM_STATE_ACTIVE ) nState |= ControlState::SELECTED;
74 return nState;
79 //= ITabBarRenderer
81 class SAL_NO_VTABLE ITabBarRenderer
83 public:
84 /** fills the background of our target device
86 virtual void renderBackground() const = 0;
87 virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const = 0;
88 virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const = 0;
89 virtual void postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const = 0;
91 // TODO: postRenderItem takes the "real" window, i.e. effectively the tab bar. This is because
92 // DrawSelectionBackground needs to be applied after everything else is painted, and is available at the Window
93 // class, but not at the OutputDevice. This makes the API somewhat weird, as we're now mixing operations on the
94 // target device, done in a normalized geometry, with operations on the window, done in a transformed geometry.
95 // So, we should get rid of postRenderItem completely.
97 protected:
98 ~ITabBarRenderer() {}
100 typedef ::boost::shared_ptr< ITabBarRenderer > PTabBarRenderer;
103 //= VCLItemRenderer - declaration
105 class VCLItemRenderer : public ITabBarRenderer
107 public:
108 VCLItemRenderer( OutputDevice& i_rTargetDevice )
109 :m_rTargetDevice( i_rTargetDevice )
112 virtual ~VCLItemRenderer() {}
114 // ITabBarRenderer
115 virtual void renderBackground() const SAL_OVERRIDE;
116 virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
117 virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
118 virtual void postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
120 protected:
121 OutputDevice& getTargetDevice() const { return m_rTargetDevice; }
123 private:
124 OutputDevice& m_rTargetDevice;
128 //= VCLItemRenderer - implementation
131 void VCLItemRenderer::renderBackground() const
133 getTargetDevice().DrawRect( Rectangle( Point(), getTargetDevice().GetOutputSizePixel() ) );
137 Rectangle VCLItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const
139 (void)i_nItemFlags;
140 // no decorations at all
141 return i_rContentArea;
145 void VCLItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const
147 (void)i_rContentRect;
148 (void)i_nItemFlags;
152 void VCLItemRenderer::postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const
154 const bool bActive = ( ( i_nItemFlags & ITEM_STATE_ACTIVE ) != 0 );
155 const bool bHovered = ( ( i_nItemFlags & ITEM_STATE_HOVERED ) != 0 );
156 const bool bFocused = ( ( i_nItemFlags & ITEM_STATE_FOCUSED ) != 0 );
157 if ( bActive || bHovered || bFocused )
159 Rectangle aSelectionRect( i_rItemRect );
160 aSelectionRect.Left() += ITEM_OUTER_SPACE / 2;
161 aSelectionRect.Top() += ITEM_OUTER_SPACE / 2;
162 aSelectionRect.Right() -= ITEM_OUTER_SPACE / 2;
163 aSelectionRect.Bottom() -= ITEM_OUTER_SPACE / 2;
164 i_rActualWindow.DrawSelectionBackground(
165 aSelectionRect,
166 ( bHovered || bFocused ) ? ( bActive ? 1 : 2 ) : 0 /* highlight */,
167 bActive /* check */,
168 true /* border */,
169 false /* ext border only */,
170 0 /* corner radius */,
171 NULL,
172 NULL
178 //= NWFToolboxItemRenderer - declaration
180 class NWFToolboxItemRenderer : public ITabBarRenderer
182 public:
183 NWFToolboxItemRenderer( OutputDevice& i_rTargetDevice )
184 :m_rTargetDevice( i_rTargetDevice )
187 virtual ~NWFToolboxItemRenderer() {}
189 // ITabBarRenderer
190 virtual void renderBackground() const SAL_OVERRIDE;
191 virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
192 virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
193 virtual void postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
195 protected:
196 OutputDevice& getTargetDevice() const { return m_rTargetDevice; }
198 private:
199 OutputDevice& m_rTargetDevice;
203 //= NWFToolboxItemRenderer - implementation
206 void NWFToolboxItemRenderer::renderBackground() const
208 getTargetDevice().DrawRect( Rectangle( Point(), getTargetDevice().GetOutputSizePixel() ) );
212 Rectangle NWFToolboxItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const
214 // don't ask GetNativeControlRegion, this will not deliver proper results in all cases.
215 // Instead, simply assume that both the content and the bounding region are the same.
216 // const ControlState nState( lcl_ItemToControlState( i_nItemFlags );
217 // const ImplControlValue aControlValue;
218 // bool bNativeOK = m_rTargetWindow.GetNativeControlRegion(
219 // CTRL_TOOLBAR, PART_BUTTON,
220 // i_rContentArea, nState,
221 // aControlValue, OUString(),
222 // aBoundingRegion, aContentRegion
223 // );
224 (void)i_nItemFlags;
225 return Rectangle(
226 Point( i_rContentArea.Left() - 1, i_rContentArea.Top() - 1 ),
227 Size( i_rContentArea.GetWidth() + 2, i_rContentArea.GetHeight() + 2 )
232 void NWFToolboxItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const
234 const ControlState nState = lcl_ItemToControlState( i_nItemFlags );
236 ImplControlValue aControlValue;
237 aControlValue.setTristateVal( ( i_nItemFlags & ITEM_STATE_ACTIVE ) ? BUTTONVALUE_ON : BUTTONVALUE_OFF );
239 bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TOOLBAR, PART_BUTTON, i_rContentRect, nState, aControlValue, OUString() );
240 (void)bNativeOK;
241 OSL_ENSURE( bNativeOK, "NWFToolboxItemRenderer::preRenderItem: inconsistent NWF implementation!" );
242 // IsNativeControlSupported returned true, previously, otherwise we would not be here ...
246 void NWFToolboxItemRenderer::postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const
248 (void)i_rActualWindow;
249 (void)i_rItemRect;
250 (void)i_nItemFlags;
254 #if defined WNT
255 //= NWFTabItemRenderer - declaration
257 class NWFTabItemRenderer : public ITabBarRenderer
259 public:
260 NWFTabItemRenderer( OutputDevice& i_rTargetDevice )
261 :m_rTargetDevice( i_rTargetDevice )
265 virtual ~NWFTabItemRenderer() {}
267 // ITabBarRenderer
268 virtual void renderBackground() const SAL_OVERRIDE;
269 virtual Rectangle calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
270 virtual void preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
271 virtual void postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const SAL_OVERRIDE;
273 protected:
274 OutputDevice& getTargetDevice() const { return m_rTargetDevice; }
276 private:
277 OutputDevice& m_rTargetDevice;
281 //= NWFTabItemRenderer - implementation
284 void NWFTabItemRenderer::renderBackground() const
286 Rectangle aBackground( Point(), getTargetDevice().GetOutputSizePixel() );
287 getTargetDevice().DrawRect( aBackground );
289 aBackground.Top() = aBackground.Bottom();
290 getTargetDevice().DrawNativeControl( CTRL_TAB_PANE, PART_ENTIRE_CONTROL, aBackground,
291 ControlState::ENABLED, ImplControlValue(), OUString() );
295 Rectangle NWFTabItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const
297 const ControlState nState( lcl_ItemToControlState( i_nItemFlags ) );
299 TabitemValue tiValue(Rectangle(i_rContentArea.Left() + TAB_TABOFFSET_X,
300 i_rContentArea.Right() - TAB_TABOFFSET_X,
301 i_rContentArea.Top() + TAB_TABOFFSET_Y,
302 i_rContentArea.Bottom() - TAB_TABOFFSET_Y));
304 Rectangle aBoundingRegion, aContentRegion;
305 bool bNativeOK = getTargetDevice().GetNativeControlRegion(
306 CTRL_TAB_ITEM, PART_ENTIRE_CONTROL,
307 i_rContentArea, nState,
308 tiValue, OUString(),
309 aBoundingRegion, aContentRegion
311 (void)bNativeOK;
312 OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::calculateDecorations: GetNativeControlRegion not implemented for CTRL_TAB_ITEM?!" );
314 return aBoundingRegion;
318 void NWFTabItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const
320 const ControlState nState = lcl_ItemToControlState( i_nItemFlags );
322 TabitemValue tiValue(Rectangle(i_rContentRect.Left() + TAB_TABOFFSET_X,
323 i_rContentRect.Right() - TAB_TABOFFSET_X,
324 i_rContentRect.Top() + TAB_TABOFFSET_Y,
325 i_rContentRect.Bottom() - TAB_TABOFFSET_Y));
327 if ( i_nItemFlags & ITEM_POSITION_FIRST )
328 tiValue.mnAlignment |= TABITEM_FIRST_IN_GROUP;
329 if ( i_nItemFlags & ITEM_POSITION_LAST )
330 tiValue.mnAlignment |= TABITEM_LAST_IN_GROUP;
333 bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, i_rContentRect, nState, tiValue, OUString() );
334 (void)bNativeOK;
335 OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::preRenderItem: inconsistent NWF implementation!" );
336 // IsNativeControlSupported returned true, previously, otherwise we would not be here ...
340 void NWFTabItemRenderer::postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const
342 (void)i_rActualWindow;
343 (void)i_rItemRect;
344 (void)i_nItemFlags;
346 #endif
348 //= PanelTabBar_Impl
350 class PanelTabBar_Impl : public IToolPanelDeckListener
352 public:
353 PanelTabBar_Impl(PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck,
354 const TabAlignment i_eAlignment, const TabItemContent i_eItemContent);
356 virtual ~PanelTabBar_Impl()
358 m_rPanelDeck.RemoveListener(*this);
361 // IToolPanelDeckListener
362 virtual void PanelInserted(const PToolPanel& i_pPanel, const size_t i_nPosition) SAL_OVERRIDE
364 (void) i_pPanel;
365 (void) i_nPosition;
366 m_bItemsDirty = true;
367 m_rTabBar.Invalidate();
369 Relayout();
372 virtual void PanelRemoved( const size_t i_nPosition ) SAL_OVERRIDE
374 m_bItemsDirty = true;
375 m_rTabBar.Invalidate();
377 if ( i_nPosition < m_nScrollPosition )
378 --m_nScrollPosition;
380 Relayout();
383 virtual void ActivePanelChanged(const boost::optional<size_t>& i_rOldActive,
384 const boost::optional<size_t>& i_rNewActive) SAL_OVERRIDE;
385 virtual void LayouterChanged(const PDeckLayouter& i_rNewLayouter) SAL_OVERRIDE;
386 virtual void Dying() SAL_OVERRIDE;
388 void UpdateScrollButtons()
390 m_aScrollBack->Enable(m_nScrollPosition > 0);
391 m_aScrollForward->Enable(m_nScrollPosition < m_aItems.size() - 1);
394 void Relayout();
395 void EnsureItemsCache();
396 boost::optional<size_t> FindItemForPoint( const Point& i_rPoint ) const;
397 void DrawItem(vcl::RenderContext& rRenderContext, const size_t i_nItemIndex, const Rectangle& i_rBoundaries) const;
398 void InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags = 0 ) const;
399 void CopyFromRenderDevice(vcl::RenderContext& rRenderContext, const Rectangle& i_rLogicalRect) const;
400 Rectangle GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const;
401 Rectangle GetItemScreenRect( const size_t i_nItemPos ) const;
403 void FocusItem( const ::boost::optional< size_t >& i_rItemPos );
405 inline bool IsVertical() const
407 return ( ( m_eTabAlignment == TABS_LEFT )
408 || ( m_eTabAlignment == TABS_RIGHT )
412 protected:
413 DECL_LINK( OnScroll, const PushButton* );
415 void impl_calcItemRects();
416 Size impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const;
417 void impl_renderItemContent(vcl::RenderContext& rRenderContext, const PToolPanel& i_pPanel,
418 const Rectangle& i_rContentArea, const TabItemContent i_eItemContent) const;
419 ItemFlags impl_getItemFlags( const size_t i_nItemIndex ) const;
421 public:
422 PanelTabBar& m_rTabBar;
423 TabBarGeometry m_aGeometry;
424 NormalizedArea m_aNormalizer;
425 TabAlignment m_eTabAlignment;
426 IToolPanelDeck& m_rPanelDeck;
428 ScopedVclPtr<VirtualDevice> m_aRenderDevice;
429 PTabBarRenderer m_pRenderer;
431 boost::optional<size_t> m_aHoveredItem;
432 boost::optional<size_t> m_aFocusedItem;
433 bool m_bMouseButtonDown;
435 ItemDescriptors m_aItems;
436 bool m_bItemsDirty;
438 VclPtr<PushButton> m_aScrollBack;
439 VclPtr<PushButton> m_aScrollForward;
441 size_t m_nScrollPosition;
445 //= helper
447 namespace
450 #if OSL_DEBUG_LEVEL > 0
451 static void lcl_checkConsistency( const PanelTabBar_Impl& i_rImpl )
453 if ( !i_rImpl.m_bItemsDirty )
455 if ( i_rImpl.m_rPanelDeck.GetPanelCount() != i_rImpl.m_aItems.size() )
457 OSL_FAIL( "lcl_checkConsistency: inconsistent array sizes!" );
458 return;
460 for ( size_t i = 0; i < i_rImpl.m_rPanelDeck.GetPanelCount(); ++i )
462 if ( i_rImpl.m_rPanelDeck.GetPanel( i ).get() != i_rImpl.m_aItems[i].pPanel.get() )
464 OSL_FAIL( "lcl_checkConsistency: array elements are inconsistent!" );
465 return;
471 #define DBG_CHECK( data ) \
472 lcl_checkConsistency( data );
473 #else
474 #define DBG_CHECK( data ) \
475 (void)data;
476 #endif
479 class ClipItemRegion
481 public:
482 ClipItemRegion( const PanelTabBar_Impl& i_rImpl )
483 :m_rDevice( i_rImpl.m_rTabBar )
485 m_rDevice.Push( PushFlags::CLIPREGION );
486 m_rDevice.SetClipRegion(vcl::Region(
487 i_rImpl.m_aNormalizer.getTransformed(
488 i_rImpl.m_aGeometry.getItemsRect(),
489 i_rImpl.m_eTabAlignment )));
492 ~ClipItemRegion()
494 m_rDevice.Pop();
497 private:
498 OutputDevice& m_rDevice;
503 //= PanelTabBar_Impl - implementation
506 PanelTabBar_Impl::PanelTabBar_Impl( PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent )
507 :m_rTabBar( i_rTabBar )
508 ,m_aGeometry( i_eItemContent )
509 ,m_aNormalizer()
510 ,m_eTabAlignment( i_eAlignment )
511 ,m_rPanelDeck( i_rPanelDeck )
512 ,m_aRenderDevice( VclPtr<VirtualDevice>::Create(i_rTabBar) )
513 ,m_pRenderer()
514 ,m_aHoveredItem()
515 ,m_aFocusedItem()
516 ,m_bMouseButtonDown( false )
517 ,m_aItems()
518 ,m_bItemsDirty( true )
519 ,m_aScrollBack( VclPtr<PushButton>::Create(&i_rTabBar, WB_BEVELBUTTON) )
520 ,m_aScrollForward( VclPtr<PushButton>::Create(&i_rTabBar, WB_BEVELBUTTON) )
521 ,m_nScrollPosition( 0 )
523 #ifdef WNT
524 if (m_aRenderDevice->IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL))
525 // this mode requires the NWF framework to be able to render those items onto a virtual
526 // device. For some frameworks (some GTK themes, in particular), this is known to fail.
527 // So, be on the safe side for the moment.
528 m_pRenderer.reset(new NWFTabItemRenderer(*m_aRenderDevice.get()));
529 else
530 #endif
531 if (m_aRenderDevice->IsNativeControlSupported(CTRL_TOOLBAR, PART_BUTTON))
532 m_pRenderer.reset(new NWFToolboxItemRenderer(*m_aRenderDevice.get()));
533 else
534 m_pRenderer.reset(new VCLItemRenderer(*m_aRenderDevice.get()));
536 m_aRenderDevice->SetLineColor();
538 m_rPanelDeck.AddListener( *this );
540 m_aScrollBack->SetSymbol( IsVertical() ? SymbolType::ARROW_UP : SymbolType::ARROW_LEFT );
541 m_aScrollBack->Show();
542 m_aScrollBack->SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) );
543 m_aScrollBack->SetAccessibleDescription( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_FWD ).toString() );
544 m_aScrollBack->SetAccessibleName( m_aScrollBack->GetAccessibleDescription() );
546 m_aScrollForward->SetSymbol( IsVertical() ? SymbolType::ARROW_DOWN : SymbolType::ARROW_RIGHT );
547 m_aScrollForward->Show();
548 m_aScrollForward->SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) );
549 m_aScrollForward->SetAccessibleDescription( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_BACK ).toString() );
550 m_aScrollForward->SetAccessibleName( m_aScrollForward->GetAccessibleDescription() );
554 void PanelTabBar_Impl::impl_calcItemRects()
556 m_aItems.resize(0);
558 Point aCompletePos( m_aGeometry.getFirstItemPosition() );
559 Point aIconOnlyPos( aCompletePos );
560 Point aTextOnlyPos( aCompletePos );
562 for ( size_t i = 0;
563 i < m_rPanelDeck.GetPanelCount();
567 PToolPanel pPanel( m_rPanelDeck.GetPanel( i ) );
569 ItemDescriptor aItem;
570 aItem.pPanel = pPanel;
572 const Size aCompleteSize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_AND_TEXT ) );
573 const Size aIconOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_ONLY ) );
574 const Size aTextOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_TEXT_ONLY ) );
576 // TODO: have one method calculating all sizes?
578 // remember the three areas
579 aItem.aCompleteArea = Rectangle( aCompletePos, aCompleteSize );
580 aItem.aIconOnlyArea = Rectangle( aIconOnlyPos, aIconOnlySize );
581 aItem.aTextOnlyArea = Rectangle( aTextOnlyPos, aTextOnlySize );
583 m_aItems.push_back( aItem );
585 aCompletePos = aItem.aCompleteArea.TopRight();
586 aIconOnlyPos = aItem.aIconOnlyArea.TopRight();
587 aTextOnlyPos = aItem.aTextOnlyArea.TopRight();
590 m_bItemsDirty = false;
594 Size PanelTabBar_Impl::impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const
596 // calculate the size needed for the content
597 OSL_ENSURE( i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_calculateItemContentSize: illegal TabItemContent value!" );
599 const Image aImage( i_pPanel->GetImage() );
600 const bool bUseImage = !!aImage && ( i_eItemContent != TABITEM_TEXT_ONLY );
602 const OUString sItemText( i_pPanel->GetDisplayName() );
603 const bool bUseText = ( !sItemText.isEmpty() ) && ( i_eItemContent != TABITEM_IMAGE_ONLY );
605 Size aItemContentSize;
606 if ( bUseImage )
608 aItemContentSize = aImage.GetSizePixel();
611 if ( bUseText )
613 if ( bUseImage )
614 aItemContentSize.Width() += ITEM_ICON_TEXT_DISTANCE;
616 // add space for text
617 const Size aTextSize( m_rTabBar.GetCtrlTextWidth( sItemText ), m_rTabBar.GetTextHeight() );
618 aItemContentSize.Width() += aTextSize.Width();
619 aItemContentSize.Height() = ::std::max( aItemContentSize.Height(), aTextSize.Height() );
621 aItemContentSize.Width() += 2 * ITEM_TEXT_FLOW_SPACE;
624 if ( !bUseImage && !bUseText )
626 // have a minimal size - this is pure heuristics, but if it doesn't suit your needs, then give your panels
627 // a name and or image! :)
628 aItemContentSize = Size( 16, 16 );
631 aItemContentSize.Width() += 2 * ITEM_OUTER_SPACE;
632 aItemContentSize.Height() += 2 * ITEM_OUTER_SPACE;
634 return aItemContentSize;
638 void PanelTabBar_Impl::impl_renderItemContent(vcl::RenderContext& rRenderContext, const PToolPanel& i_pPanel, const Rectangle& i_rContentArea, const TabItemContent i_eItemContent) const
640 OSL_ENSURE(i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_renderItemContent: illegal TabItemContent value!");
642 Rectangle aRenderArea(i_rContentArea);
643 if (IsVertical())
645 aRenderArea.Top() += ITEM_OUTER_SPACE;
647 else
649 aRenderArea.Left() += ITEM_OUTER_SPACE;
652 // draw the image
653 const Image aItemImage(i_pPanel->GetImage());
654 const Size aImageSize(aItemImage.GetSizePixel());
655 const bool bUseImage = !!aItemImage && (i_eItemContent != TABITEM_TEXT_ONLY);
657 if (bUseImage)
659 Point aImagePos;
660 if (IsVertical())
662 aImagePos.X() = aRenderArea.Left() + (aRenderArea.GetWidth() - aImageSize.Width()) / 2;
663 aImagePos.Y() = aRenderArea.Top();
665 else
667 aImagePos.X() = aRenderArea.Left();
668 aImagePos.Y() = aRenderArea.Top() + (aRenderArea.GetHeight() - aImageSize.Height()) / 2;
670 rRenderContext.DrawImage(aImagePos, aItemImage);
673 const OUString sItemText(i_pPanel->GetDisplayName());
674 const bool bUseText = (!sItemText.isEmpty()) && (i_eItemContent != TABITEM_IMAGE_ONLY);
676 if (bUseText)
678 if (IsVertical())
680 if (bUseImage)
681 aRenderArea.Top() += aImageSize.Height() + ITEM_ICON_TEXT_DISTANCE;
682 aRenderArea.Top() += ITEM_TEXT_FLOW_SPACE;
684 else
686 if (bUseImage)
687 aRenderArea.Left() += aImageSize.Width() + ITEM_ICON_TEXT_DISTANCE;
688 aRenderArea.Left() += ITEM_TEXT_FLOW_SPACE;
691 // draw the text
692 const Size aTextSize(m_rTabBar.GetCtrlTextWidth(sItemText), rRenderContext.GetTextHeight());
693 Point aTextPos(aRenderArea.TopLeft());
694 if (IsVertical())
696 rRenderContext.Push(PushFlags::FONT);
698 vcl::Font aFont(rRenderContext.GetFont());
699 aFont.SetOrientation(2700);
700 aFont.SetVertical(true);
701 rRenderContext.SetFont(aFont);
703 aTextPos.X() += aTextSize.Height();
704 aTextPos.X() += (aRenderArea.GetWidth() - aTextSize.Height()) / 2;
706 else
708 aTextPos.Y() += (aRenderArea.GetHeight() - aTextSize.Height()) / 2;
711 rRenderContext.DrawText(aTextPos, sItemText);
713 if (IsVertical())
715 rRenderContext.Pop();
720 void PanelTabBar_Impl::CopyFromRenderDevice(vcl::RenderContext& rRenderContext, const Rectangle& i_rLogicalRect) const
722 BitmapEx aBitmap(m_aRenderDevice->GetBitmapEx(i_rLogicalRect.TopLeft(),
723 Size(i_rLogicalRect.GetSize().Width(),
724 i_rLogicalRect.GetSize().Height())));
725 if (IsVertical())
727 aBitmap.Rotate(2700, COL_BLACK);
728 if (m_eTabAlignment == TABS_LEFT)
729 aBitmap.Mirror(BmpMirrorFlags::Horizontal);
731 else if (m_eTabAlignment == TABS_BOTTOM)
733 aBitmap.Mirror(BmpMirrorFlags::Vertical);
736 const Rectangle aActualRect(m_aNormalizer.getTransformed(i_rLogicalRect, m_eTabAlignment));
737 rRenderContext.DrawBitmapEx(aActualRect.TopLeft(), aBitmap);
741 void PanelTabBar_Impl::InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags ) const
743 const ItemDescriptor& rItem( m_aItems[ i_nItemIndex ] );
744 const ItemFlags nItemFlags( impl_getItemFlags( i_nItemIndex ) | i_nAdditionalItemFlags );
746 const Rectangle aNormalizedContent( GetActualLogicalItemRect( rItem.GetCurrentRect() ) );
747 const Rectangle aNormalizedBounds( m_pRenderer->calculateDecorations( aNormalizedContent, nItemFlags ) );
749 const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment );
750 m_rTabBar.Invalidate( aActualBounds );
754 ItemFlags PanelTabBar_Impl::impl_getItemFlags( const size_t i_nItemIndex ) const
756 ItemFlags nItemFlags( ITEM_STATE_NORMAL );
757 if ( m_aHoveredItem == i_nItemIndex )
759 nItemFlags |= ITEM_STATE_HOVERED;
760 if ( m_bMouseButtonDown )
761 nItemFlags |= ITEM_STATE_ACTIVE;
764 if ( m_rPanelDeck.GetActivePanel() == i_nItemIndex )
765 nItemFlags |= ITEM_STATE_ACTIVE;
767 if ( m_aFocusedItem == i_nItemIndex )
768 nItemFlags |= ITEM_STATE_FOCUSED;
770 if ( 0 == i_nItemIndex )
771 nItemFlags |= ITEM_POSITION_FIRST;
773 if ( m_rPanelDeck.GetPanelCount() - 1 == i_nItemIndex )
774 nItemFlags |= ITEM_POSITION_LAST;
776 return nItemFlags;
780 void PanelTabBar_Impl::DrawItem(vcl::RenderContext& rRenderContext, const size_t i_nItemIndex, const Rectangle& i_rBoundaries) const
782 const ItemDescriptor& rItem(m_aItems[i_nItemIndex]);
783 const ItemFlags nItemFlags(impl_getItemFlags(i_nItemIndex));
785 // the normalized bounding and content rect
786 const Rectangle aNormalizedContent(GetActualLogicalItemRect(rItem.GetCurrentRect()));
787 const Rectangle aNormalizedBounds(m_pRenderer->calculateDecorations(aNormalizedContent, nItemFlags));
789 // check whether the item actually overlaps with the painting area
790 if (!i_rBoundaries.IsEmpty())
792 const Rectangle aItemRect(GetActualLogicalItemRect(rItem.GetCurrentRect()));
793 if (!aItemRect.IsOver(i_rBoundaries))
794 return;
797 m_rTabBar.SetUpdateMode(false);
799 // the aligned bounding and content rect
800 const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment );
801 const Rectangle aActualContent = m_aNormalizer.getTransformed( aNormalizedContent, m_eTabAlignment );
803 // render item "background" layer
804 m_pRenderer->preRenderItem(aNormalizedContent, nItemFlags);
806 // copy from the virtual device to ourself
807 CopyFromRenderDevice(rRenderContext, aNormalizedBounds);
809 // render the actual item content
810 impl_renderItemContent(rRenderContext, rItem.pPanel, aActualContent, rItem.eContent);
812 // render item "foreground" layer
813 m_pRenderer->postRenderItem(m_rTabBar, aActualBounds, nItemFlags);
815 m_rTabBar.SetUpdateMode(true);
819 void PanelTabBar_Impl::EnsureItemsCache()
821 if ( !m_bItemsDirty )
823 DBG_CHECK( *this );
824 return;
826 impl_calcItemRects();
827 SAL_WARN_IF( m_bItemsDirty , "svtools", "PanelTabBar_Impl::EnsureItemsCache: cache still dirty!" );
828 DBG_CHECK( *this );
832 void PanelTabBar_Impl::Relayout()
834 EnsureItemsCache();
836 const Size aOutputSize( m_rTabBar.GetOutputSizePixel() );
837 m_aNormalizer = NormalizedArea( Rectangle( Point(), aOutputSize ), IsVertical() );
838 const Size aLogicalOutputSize( m_aNormalizer.getReferenceSize() );
840 // forward actual output size to our render device
841 m_aRenderDevice->SetOutputSizePixel( aLogicalOutputSize );
843 // re-calculate the size of the scroll buttons and of the items
844 m_aGeometry.relayout( aLogicalOutputSize, m_aItems );
846 if ( m_aGeometry.getButtonBackRect().IsEmpty() )
848 m_aScrollBack->Hide();
850 else
852 const Rectangle aButtonBack( m_aNormalizer.getTransformed( m_aGeometry.getButtonBackRect(), m_eTabAlignment ) );
853 m_aScrollBack->SetPosSizePixel( aButtonBack.TopLeft(), aButtonBack.GetSize() );
854 m_aScrollBack->Show();
857 if ( m_aGeometry.getButtonForwardRect().IsEmpty() )
859 m_aScrollForward->Hide();
861 else
863 const Rectangle aButtonForward( m_aNormalizer.getTransformed( m_aGeometry.getButtonForwardRect(), m_eTabAlignment ) );
864 m_aScrollForward->SetPosSizePixel( aButtonForward.TopLeft(), aButtonForward.GetSize() );
865 m_aScrollForward->Show();
868 UpdateScrollButtons();
872 ::boost::optional< size_t > PanelTabBar_Impl::FindItemForPoint( const Point& i_rPoint ) const
874 Point aPoint( IsVertical() ? i_rPoint.Y() : i_rPoint.X(), IsVertical() ? i_rPoint.X() : i_rPoint.Y() );
876 if ( !m_aGeometry.getItemsRect().IsInside( aPoint ) )
877 return ::boost::optional< size_t >();
879 size_t i=0;
880 for ( ItemDescriptors::const_iterator item = m_aItems.begin();
881 item != m_aItems.end();
882 ++item, ++i
885 Rectangle aItemRect( GetActualLogicalItemRect( item->GetCurrentRect() ) );
886 if ( aItemRect.IsInside( aPoint ) )
888 return ::boost::optional< size_t >( i );
891 return ::boost::optional< size_t >();
895 Rectangle PanelTabBar_Impl::GetItemScreenRect( const size_t i_nItemPos ) const
897 ENSURE_OR_RETURN( i_nItemPos < m_aItems.size(), "PanelTabBar_Impl::GetItemScreenRect: invalid item pos!", Rectangle() );
898 const ItemDescriptor& rItem( m_aItems[ i_nItemPos ] );
899 const Rectangle aItemRect( m_aNormalizer.getTransformed(
900 GetActualLogicalItemRect( rItem.GetCurrentRect() ),
901 m_eTabAlignment ) );
903 const Rectangle aTabBarRect( m_rTabBar.GetWindowExtentsRelative( NULL ) );
904 return Rectangle(
905 Point( aTabBarRect.Left() + aItemRect.Left(), aTabBarRect.Top() + aItemRect.Top() ),
906 aItemRect.GetSize()
911 void PanelTabBar_Impl::FocusItem( const ::boost::optional< size_t >& i_rItemPos )
913 // reset old focus item
914 if ( !!m_aFocusedItem )
915 InvalidateItem( *m_aFocusedItem );
916 m_aFocusedItem.reset();
918 // mark the active icon as focused
919 if ( !!i_rItemPos )
921 m_aFocusedItem = i_rItemPos;
922 InvalidateItem( *m_aFocusedItem );
927 IMPL_LINK( PanelTabBar_Impl, OnScroll, const PushButton*, i_pButton )
929 if ( i_pButton == m_aScrollBack.get() )
931 OSL_ENSURE( m_nScrollPosition > 0, "PanelTabBar_Impl::OnScroll: inconsistency!" );
932 --m_nScrollPosition;
933 m_rTabBar.Invalidate();
935 else if ( i_pButton == m_aScrollForward.get() )
937 OSL_ENSURE( m_nScrollPosition < m_aItems.size() - 1, "PanelTabBar_Impl::OnScroll: inconsistency!" );
938 ++m_nScrollPosition;
939 m_rTabBar.Invalidate();
942 UpdateScrollButtons();
944 return 0L;
948 Rectangle PanelTabBar_Impl::GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const
950 // care for the offset imposed by our geometry, i.e. whether or not we have scroll buttons
951 Rectangle aItemRect( i_rLogicalItemRect );
952 aItemRect.Move( m_aGeometry.getItemsRect().Left() - m_aGeometry.getButtonBackRect().Left(), 0 );
954 // care for the current scroll position
955 OSL_ENSURE( m_nScrollPosition < m_aItems.size(), "GetActualLogicalItemRect: invalid scroll position!" );
956 if ( ( m_nScrollPosition > 0 ) && ( m_nScrollPosition < m_aItems.size() ) )
958 long nOffsetX = m_aItems[ m_nScrollPosition ].GetCurrentRect().Left() - m_aItems[ 0 ].GetCurrentRect().Left();
959 long nOffsetY = m_aItems[ m_nScrollPosition ].GetCurrentRect().Top() - m_aItems[ 0 ].GetCurrentRect().Top();
960 aItemRect.Move( -nOffsetX, -nOffsetY );
963 return aItemRect;
967 //= PanelTabBar_Impl
970 void PanelTabBar_Impl::ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive )
972 EnsureItemsCache();
974 if ( !!i_rOldActive )
975 InvalidateItem( *i_rOldActive, ITEM_STATE_ACTIVE );
976 if ( !!i_rNewActive )
977 InvalidateItem( *i_rNewActive );
981 void PanelTabBar_Impl::LayouterChanged( const PDeckLayouter& i_rNewLayouter )
983 // not interested in
984 (void)i_rNewLayouter;
988 void PanelTabBar_Impl::Dying()
990 // not interested in - the notifier is a member of this instance here, so we're dying ourself at the moment
994 //= PanelTabBar
997 PanelTabBar::PanelTabBar( vcl::Window& i_rParentWindow, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent )
998 :Control( &i_rParentWindow, 0 )
999 ,m_pImpl( new PanelTabBar_Impl( *this, i_rPanelDeck, i_eAlignment, i_eItemContent ) )
1001 DBG_CHECK( *m_pImpl );
1004 PanelTabBar::~PanelTabBar()
1006 disposeOnce();
1009 void PanelTabBar::dispose()
1011 Control::dispose();
1014 TabItemContent PanelTabBar::GetTabItemContent() const
1016 return m_pImpl->m_aGeometry.getItemContent();
1020 void PanelTabBar::SetTabItemContent( const TabItemContent& i_eItemContent )
1022 m_pImpl->m_aGeometry.setItemContent( i_eItemContent );
1023 m_pImpl->Relayout();
1024 Invalidate();
1028 IToolPanelDeck& PanelTabBar::GetPanelDeck() const
1030 DBG_CHECK( *m_pImpl );
1031 return m_pImpl->m_rPanelDeck;
1035 Size PanelTabBar::GetOptimalSize() const
1037 m_pImpl->EnsureItemsCache();
1038 Size aOptimalSize(m_pImpl->m_aGeometry.getOptimalSize(m_pImpl->m_aItems));
1039 if ( m_pImpl->IsVertical() )
1040 ::std::swap( aOptimalSize.Width(), aOptimalSize.Height() );
1041 return aOptimalSize;
1045 void PanelTabBar::Resize()
1047 Control::Resize();
1048 m_pImpl->Relayout();
1052 void PanelTabBar::Paint(vcl::RenderContext& rRenderContext, const Rectangle& i_rRect)
1054 m_pImpl->EnsureItemsCache();
1056 // background
1057 const Rectangle aNormalizedPaintArea(m_pImpl->m_aNormalizer.getNormalized(i_rRect, m_pImpl->m_eTabAlignment));
1058 m_pImpl->m_aRenderDevice->Push(PushFlags::CLIPREGION);
1059 m_pImpl->m_aRenderDevice->SetClipRegion(vcl::Region(aNormalizedPaintArea));
1060 m_pImpl->m_pRenderer->renderBackground();
1061 m_pImpl->m_aRenderDevice->Pop();
1062 m_pImpl->CopyFromRenderDevice(rRenderContext, aNormalizedPaintArea);
1064 // ensure the items really paint into their own playground only
1065 ClipItemRegion aClipItems(*m_pImpl);
1067 const Rectangle aLogicalPaintRect(m_pImpl->m_aNormalizer.getNormalized(i_rRect, m_pImpl->m_eTabAlignment));
1069 const boost::optional<size_t> aActivePanel(m_pImpl->m_rPanelDeck.GetActivePanel());
1070 const boost::optional<size_t> aHoveredPanel(m_pImpl->m_aHoveredItem);
1072 // items:
1073 // 1. paint all non-active, non-hovered items
1074 size_t i = 0;
1075 ItemDescriptors::const_iterator item;
1076 for (item = m_pImpl->m_aItems.begin(); item != m_pImpl->m_aItems.end(); ++item, ++i)
1078 if (i == aActivePanel)
1079 continue;
1081 if (aHoveredPanel == i)
1082 continue;
1084 m_pImpl->DrawItem(rRenderContext, i, aLogicalPaintRect);
1087 // 2. paint the item which is hovered, /without/ the mouse button pressed down
1088 if (!!aHoveredPanel && !m_pImpl->m_bMouseButtonDown)
1089 m_pImpl->DrawItem(rRenderContext, *aHoveredPanel, aLogicalPaintRect);
1091 // 3. paint the active item
1092 if (!!aActivePanel)
1093 m_pImpl->DrawItem(rRenderContext, *aActivePanel, aLogicalPaintRect);
1095 // 4. paint the item which is hovered, /with/ the mouse button pressed down
1096 if (!!aHoveredPanel && m_pImpl->m_bMouseButtonDown)
1097 m_pImpl->DrawItem(rRenderContext, *aHoveredPanel, aLogicalPaintRect);
1101 void PanelTabBar::MouseMove( const MouseEvent& i_rMouseEvent )
1103 m_pImpl->EnsureItemsCache();
1105 boost::optional< size_t > aOldItem(m_pImpl->m_aHoveredItem);
1106 boost::optional< size_t > aNewItem(m_pImpl->FindItemForPoint(i_rMouseEvent.GetPosPixel()));
1108 if (i_rMouseEvent.IsLeaveWindow())
1109 aNewItem = boost::optional<size_t>();
1111 bool const bChanged(
1112 ( !aOldItem && aNewItem )
1113 || ( aOldItem && !aNewItem )
1114 || ( aOldItem && aNewItem && aOldItem != aNewItem ) );
1116 if (bChanged)
1118 if (aOldItem)
1119 m_pImpl->InvalidateItem( *aOldItem );
1121 m_pImpl->m_aHoveredItem = aNewItem;
1123 if (aNewItem)
1124 m_pImpl->InvalidateItem( *aNewItem );
1129 void PanelTabBar::MouseButtonDown( const MouseEvent& i_rMouseEvent )
1131 Control::MouseButtonDown( i_rMouseEvent );
1133 if ( !i_rMouseEvent.IsLeft() )
1134 return;
1136 m_pImpl->EnsureItemsCache();
1138 ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) );
1139 if ( !aHitItem )
1140 return;
1142 CaptureMouse();
1143 m_pImpl->m_bMouseButtonDown = true;
1145 m_pImpl->InvalidateItem( *aHitItem );
1149 void PanelTabBar::MouseButtonUp( const MouseEvent& i_rMouseEvent )
1151 Control::MouseButtonUp( i_rMouseEvent );
1153 if ( m_pImpl->m_bMouseButtonDown )
1155 ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) );
1156 if ( !!aHitItem )
1158 // re-draw that item now that we're not in mouse-down mode anymore
1159 m_pImpl->InvalidateItem( *aHitItem );
1160 // activate the respective panel
1161 m_pImpl->m_rPanelDeck.ActivatePanel( *aHitItem );
1164 OSL_ENSURE( IsMouseCaptured(), "PanelTabBar::MouseButtonUp: inconsistency!" );
1165 if ( IsMouseCaptured() )
1166 ReleaseMouse();
1167 m_pImpl->m_bMouseButtonDown = false;
1172 void PanelTabBar::RequestHelp( const HelpEvent& i_rHelpEvent )
1174 m_pImpl->EnsureItemsCache();
1176 ::boost::optional< size_t > aHelpItem( m_pImpl->FindItemForPoint( ScreenToOutputPixel( i_rHelpEvent.GetMousePosPixel() ) ) );
1177 if ( !aHelpItem )
1178 return;
1180 const ItemDescriptor& rItem( m_pImpl->m_aItems[ *aHelpItem ] );
1181 if ( rItem.eContent != TABITEM_IMAGE_ONLY )
1182 // if the text is displayed for the item, we do not need to show it as tooltip
1183 return;
1185 const OUString sItemText( rItem.pPanel->GetDisplayName() );
1186 if ( i_rHelpEvent.GetMode() == HelpEventMode::BALLOON )
1187 Help::ShowBalloon( this, OutputToScreenPixel( rItem.GetCurrentRect().Center() ), rItem.GetCurrentRect(), sItemText );
1188 else
1189 Help::ShowQuickHelp( this, rItem.GetCurrentRect(), sItemText );
1193 void PanelTabBar::GetFocus()
1195 Control::GetFocus();
1196 if ( !m_pImpl->m_aFocusedItem )
1197 m_pImpl->FocusItem( m_pImpl->m_rPanelDeck.GetActivePanel() );
1201 void PanelTabBar::LoseFocus()
1203 Control::LoseFocus();
1205 if ( m_pImpl )
1207 if ( !!m_pImpl->m_aFocusedItem )
1208 m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1210 m_pImpl->m_aFocusedItem.reset();
1215 class KeyInputHandler
1217 public:
1218 KeyInputHandler( Control& i_rControl, const KeyEvent& i_rKeyEvent )
1219 :m_rControl( i_rControl )
1220 ,m_rKeyEvent( i_rKeyEvent )
1221 ,m_bHandled( false )
1225 ~KeyInputHandler()
1227 if ( !m_bHandled )
1228 m_rControl.Control::KeyInput( m_rKeyEvent );
1231 void setHandled()
1233 m_bHandled = true;
1236 private:
1237 Control& m_rControl;
1238 const KeyEvent& m_rKeyEvent;
1239 bool m_bHandled;
1243 void PanelTabBar::KeyInput( const KeyEvent& i_rKeyEvent )
1245 KeyInputHandler aKeyInputHandler( *this, i_rKeyEvent );
1247 const vcl::KeyCode& rKeyCode( i_rKeyEvent.GetKeyCode() );
1248 if ( rKeyCode.GetModifier() != 0 )
1249 // only interested in mere key presses
1250 return;
1252 // if there are less than 2 panels, we cannot travel them ...
1253 const size_t nPanelCount( m_pImpl->m_rPanelDeck.GetPanelCount() );
1254 if ( nPanelCount < 2 )
1255 return;
1257 OSL_PRECOND( !!m_pImpl->m_aFocusedItem, "PanelTabBar::KeyInput: we should have a focused item here!" );
1258 // if we get KeyInput events, we should have the focus. In this case, m_aFocusedItem should not be empty,
1259 // except if there are no panels, but then we bail out of this method here earlier ...
1261 bool bFocusNext = false;
1262 bool bFocusPrev = false;
1264 switch ( rKeyCode.GetCode() )
1266 case KEY_UP: bFocusPrev = true; break;
1267 case KEY_DOWN: bFocusNext = true; break;
1268 case KEY_LEFT:
1269 if ( IsRTLEnabled() )
1270 bFocusNext = true;
1271 else
1272 bFocusPrev = true;
1273 break;
1274 case KEY_RIGHT:
1275 if ( IsRTLEnabled() )
1276 bFocusPrev = true;
1277 else
1278 bFocusNext = true;
1279 break;
1280 case KEY_RETURN:
1281 m_pImpl->m_rPanelDeck.ActivatePanel( *m_pImpl->m_aFocusedItem );
1282 break;
1285 if ( !bFocusNext && !bFocusPrev )
1286 return;
1288 m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1289 if ( bFocusNext )
1291 m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + 1 ) % nPanelCount );
1293 else
1295 m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + nPanelCount - 1 ) % nPanelCount );
1297 m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1299 // don't delegate to base class
1300 aKeyInputHandler.setHandled();
1304 void PanelTabBar::DataChanged( const DataChangedEvent& i_rDataChanedEvent )
1306 Control::DataChanged( i_rDataChanedEvent );
1308 if ( ( i_rDataChanedEvent.GetType() == DataChangedEventType::SETTINGS )
1309 && ( i_rDataChanedEvent.GetFlags() & AllSettingsFlags::STYLE )
1312 Invalidate();
1317 bool PanelTabBar::IsVertical() const
1319 return m_pImpl->IsVertical();
1323 PushButton& PanelTabBar::GetScrollButton( const bool i_bForward )
1325 return i_bForward ? *m_pImpl->m_aScrollForward.get() : *m_pImpl->m_aScrollBack.get();
1329 ::boost::optional< size_t > PanelTabBar::GetFocusedPanelItem() const
1331 return m_pImpl->m_aFocusedItem;
1335 void PanelTabBar::FocusPanelItem( const size_t i_nItemPos )
1337 ENSURE_OR_RETURN_VOID( i_nItemPos < m_pImpl->m_rPanelDeck.GetPanelCount(), "PanelTabBar::FocusPanelItem: illegal item pos!" );
1339 if ( !HasChildPathFocus() )
1340 GrabFocus();
1342 m_pImpl->FocusItem( i_nItemPos );
1343 SAL_WARN_IF( !m_pImpl->m_aFocusedItem, "svtools", "PanelTabBar::FocusPanelItem: have the focus, but no focused item?" );
1344 if ( !!m_pImpl->m_aFocusedItem )
1345 m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1346 m_pImpl->m_aFocusedItem.reset( i_nItemPos );
1350 Rectangle PanelTabBar::GetItemScreenRect( const size_t i_nItemPos ) const
1352 return m_pImpl->GetItemScreenRect( i_nItemPos );
1356 Reference< XWindowPeer > PanelTabBar::GetComponentInterface( bool i_bCreate )
1358 Reference< XWindowPeer > xWindowPeer( Control::GetComponentInterface( false ) );
1359 if ( !xWindowPeer.is() && i_bCreate )
1361 xWindowPeer.set( new PanelTabBarPeer( *this ) );
1362 SetComponentInterface( xWindowPeer );
1364 return xWindowPeer;
1368 } // namespace svt
1371 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */