1 /*************************************************************************
2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
3  *
4  * Copyright 2000, 2010 Oracle and/or its affiliates.
5  *
6  * OpenOffice.org - a multi-platform office productivity suite
7  *
8  * This file is part of OpenOffice.org.
9  *
10  * OpenOffice.org is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU Lesser General Public License version 3
12  * only, as published by the Free Software Foundation.
13  *
14  * OpenOffice.org is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License version 3 for more details
18  * (a copy is included in the LICENSE file that accompanied this code).
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * version 3 along with OpenOffice.org.  If not, see
22  * <http://www.openoffice.org/license.html>
23  * for a copy of the LGPLv3 License.
24  *
25 ************************************************************************/
26 
27 #include "precompiled_svtools.hxx"
28 
29 #include "svtools/toolpanel/paneltabbar.hxx"
30 #include "svtools/toolpanel/toolpaneldeck.hxx"
31 #include "svtools/svtdata.hxx"
32 #include "svtools/svtools.hrc"
33 
34 #include "tabitemdescriptor.hxx"
35 #include "paneltabbarpeer.hxx"
36 #include "tabbargeometry.hxx"
37 
38 #include <vcl/button.hxx>
39 #include <vcl/help.hxx>
40 #include <vcl/virdev.hxx>
41 #include <tools/diagnose_ex.h>
42 
43 #include <boost/optional.hpp>
44 #include <vector>
45 
46 // space around an item
47 #define ITEM_OUTER_SPACE        2 * 3
48 // spacing before and after an item's text
49 #define ITEM_TEXT_FLOW_SPACE    5
50 // space between item icon and icon text
51 #define ITEM_ICON_TEXT_DISTANCE 4
52 
53 //........................................................................
54 namespace svt
55 {
56 //........................................................................
57 
58     using ::com::sun::star::uno::Reference;
59     using ::com::sun::star::awt::XWindowPeer;
60 
61     typedef sal_uInt16  ItemFlags;
62 
63     #define ITEM_STATE_NORMAL   0x00
64     #define ITEM_STATE_ACTIVE   0x01
65     #define ITEM_STATE_HOVERED  0x02
66     #define ITEM_STATE_FOCUSED  0x04
67     #define ITEM_POSITION_FIRST 0x08
68     #define ITEM_POSITION_LAST  0x10
69 
70 	//==================================================================================================================
71 	//= helper
72 	//==================================================================================================================
73     namespace
74     {
75         ControlState lcl_ItemToControlState( const ItemFlags i_nItemFlags )
76         {
77             ControlState nState = CTRL_STATE_ENABLED;
78             if ( i_nItemFlags & ITEM_STATE_FOCUSED )    nState |= CTRL_STATE_FOCUSED | CTRL_STATE_PRESSED;
79             if ( i_nItemFlags & ITEM_STATE_HOVERED )    nState |= CTRL_STATE_ROLLOVER;
80             if ( i_nItemFlags & ITEM_STATE_ACTIVE )     nState |= CTRL_STATE_SELECTED;
81             return nState;
82         }
83     }
84 
85 	//==================================================================================================================
86 	//= ITabBarRenderer
87 	//==================================================================================================================
88     class SAL_NO_VTABLE ITabBarRenderer
89     {
90     public:
91         /** fills the background of our target device
92         */
93         virtual void        renderBackground() const = 0;
94         virtual Rectangle   calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const = 0;
95         virtual void        preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const = 0;
96         virtual void        postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const = 0;
97 
98         // TODO: postRenderItem takes the "real" window, i.e. effectively the tab bar. This is because
99         // DrawSelectionBackground needs to be applied after everything else is painted, and is available at the Window
100         // class, but not at the OutputDevice. This makes the API somewhat weird, as we're now mixing operations on the
101         // target device, done in a normalized geometry, with operations on the window, done in a transformed geometry.
102         // So, we should get rid of postRenderItem completely.
103     };
104     typedef ::boost::shared_ptr< ITabBarRenderer >  PTabBarRenderer;
105 
106 	//==================================================================================================================
107 	//= VCLItemRenderer - declaration
108 	//==================================================================================================================
109     class VCLItemRenderer : public ITabBarRenderer
110     {
111     public:
112         VCLItemRenderer( OutputDevice& i_rTargetDevice )
113             :m_rTargetDevice( i_rTargetDevice )
114         {
115         }
116 
117         // ITabBarRenderer
118         virtual void        renderBackground() const;
119         virtual Rectangle   calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const;
120         virtual void        preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const;
121         virtual void        postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const;
122 
123     protected:
124         OutputDevice&   getTargetDevice() const { return m_rTargetDevice; }
125 
126     private:
127         OutputDevice&   m_rTargetDevice;
128     };
129 
130 	//==================================================================================================================
131 	//= VCLItemRenderer - implementation
132 	//==================================================================================================================
133 	//------------------------------------------------------------------------------------------------------------------
134     void VCLItemRenderer::renderBackground() const
135     {
136         getTargetDevice().DrawRect( Rectangle( Point(), getTargetDevice().GetOutputSizePixel() ) );
137     }
138 
139 	//------------------------------------------------------------------------------------------------------------------
140     Rectangle VCLItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const
141     {
142         (void)i_nItemFlags;
143         // no decorations at all
144         return i_rContentArea;
145     }
146 
147 	//------------------------------------------------------------------------------------------------------------------
148     void VCLItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const
149     {
150         (void)i_rContentRect;
151         (void)i_nItemFlags;
152     }
153 
154 	//------------------------------------------------------------------------------------------------------------------
155     void VCLItemRenderer::postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const
156     {
157         const bool bActive = ( ( i_nItemFlags & ITEM_STATE_ACTIVE ) != 0 );
158         const bool bHovered = ( ( i_nItemFlags & ITEM_STATE_HOVERED ) != 0 );
159         const bool bFocused = ( ( i_nItemFlags & ITEM_STATE_FOCUSED ) != 0 );
160         if ( bActive || bHovered || bFocused )
161         {
162             Rectangle aSelectionRect( i_rItemRect );
163             aSelectionRect.Left() += ITEM_OUTER_SPACE / 2;
164             aSelectionRect.Top() += ITEM_OUTER_SPACE / 2;
165             aSelectionRect.Right() -= ITEM_OUTER_SPACE / 2;
166             aSelectionRect.Bottom() -= ITEM_OUTER_SPACE / 2;
167             i_rActualWindow.DrawSelectionBackground(
168                 aSelectionRect,
169                 ( bHovered || bFocused ) ? ( bActive ? 1 : 2 ) : 0 /* hilight */,
170                 bActive /* check */,
171                 sal_True /* border */,
172                 sal_False /* ext border only */,
173                 0 /* corner radius */,
174                 NULL,
175                 NULL
176             );
177         }
178     }
179 
180 	//==================================================================================================================
181 	//= NWFToolboxItemRenderer - declaration
182 	//==================================================================================================================
183     class NWFToolboxItemRenderer : public ITabBarRenderer
184     {
185     public:
186         NWFToolboxItemRenderer( OutputDevice& i_rTargetDevice )
187             :m_rTargetDevice( i_rTargetDevice )
188         {
189         }
190 
191         // ITabBarRenderer
192         virtual void        renderBackground() const;
193         virtual Rectangle   calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const;
194         virtual void        preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const;
195         virtual void        postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const;
196 
197     protected:
198         OutputDevice&   getTargetDevice() const { return m_rTargetDevice; }
199 
200     private:
201         OutputDevice&   m_rTargetDevice;
202     };
203 
204 	//==================================================================================================================
205 	//= NWFToolboxItemRenderer - implementation
206 	//==================================================================================================================
207 	//------------------------------------------------------------------------------------------------------------------
208     void NWFToolboxItemRenderer::renderBackground() const
209     {
210         getTargetDevice().DrawRect( Rectangle( Point(), getTargetDevice().GetOutputSizePixel() ) );
211     }
212 
213 	//------------------------------------------------------------------------------------------------------------------
214     Rectangle NWFToolboxItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const
215     {
216         // don't ask GetNativeControlRegion, this will not deliver proper results in all cases.
217         // Instead, simply assume that both the content and the bounding region are the same.
218 //        const ControlState nState( lcl_ItemToControlState( i_nItemFlags );
219 //        const ImplControlValue aControlValue;
220 //        bool bNativeOK = m_rTargetWindow.GetNativeControlRegion(
221 //            CTRL_TOOLBAR, PART_BUTTON,
222 //            i_rContentArea, nState,
223 //            aControlValue, ::rtl::OUString(),
224 //            aBoundingRegion, aContentRegion
225 //        );
226         (void)i_nItemFlags;
227         return Rectangle(
228             Point( i_rContentArea.Left() - 1, i_rContentArea.Top() - 1 ),
229             Size( i_rContentArea.GetWidth() + 2, i_rContentArea.GetHeight() + 2 )
230         );
231     }
232 
233 	//------------------------------------------------------------------------------------------------------------------
234     void NWFToolboxItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const
235     {
236         const ControlState nState = lcl_ItemToControlState( i_nItemFlags );
237 
238         ImplControlValue aControlValue;
239         aControlValue.setTristateVal( ( i_nItemFlags & ITEM_STATE_ACTIVE ) ? BUTTONVALUE_ON : BUTTONVALUE_OFF );
240 
241         bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TOOLBAR, PART_BUTTON, i_rContentRect, nState, aControlValue, rtl::OUString() );
242         (void)bNativeOK;
243         OSL_ENSURE( bNativeOK, "NWFToolboxItemRenderer::preRenderItem: inconsistent NWF implementation!" );
244             // IsNativeControlSupported returned true, previously, otherwise we would not be here ...
245     }
246 
247 	//------------------------------------------------------------------------------------------------------------------
248     void NWFToolboxItemRenderer::postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const
249     {
250         (void)i_rActualWindow;
251         (void)i_rItemRect;
252         (void)i_nItemFlags;
253     }
254 
255 	//==================================================================================================================
256 	//= NWFTabItemRenderer - declaration
257 	//==================================================================================================================
258     class NWFTabItemRenderer : public ITabBarRenderer
259     {
260     public:
261         NWFTabItemRenderer( OutputDevice& i_rTargetDevice )
262             :m_rTargetDevice( i_rTargetDevice )
263         {
264         }
265 
266         // ITabBarRenderer
267         virtual void        renderBackground() const;
268         virtual Rectangle   calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const;
269         virtual void        preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const;
270         virtual void        postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const;
271 
272     protected:
273         OutputDevice&   getTargetDevice() const { return m_rTargetDevice; }
274 
275     private:
276         OutputDevice&   m_rTargetDevice;
277     };
278 
279 	//==================================================================================================================
280 	//= NWFTabItemRenderer - implementation
281 	//==================================================================================================================
282 	//------------------------------------------------------------------------------------------------------------------
283     void NWFTabItemRenderer::renderBackground() const
284     {
285         Rectangle aBackground( Point(), getTargetDevice().GetOutputSizePixel() );
286         getTargetDevice().DrawRect( aBackground );
287 
288         aBackground.Top() = aBackground.Bottom();
289         getTargetDevice().DrawNativeControl( CTRL_TAB_PANE, PART_ENTIRE_CONTROL, aBackground,
290             CTRL_STATE_ENABLED, ImplControlValue(), ::rtl::OUString() );
291     }
292 
293 	//------------------------------------------------------------------------------------------------------------------
294     Rectangle NWFTabItemRenderer::calculateDecorations( const Rectangle& i_rContentArea, const ItemFlags i_nItemFlags ) const
295     {
296         const ControlState nState( lcl_ItemToControlState( i_nItemFlags ) );
297 
298         TabitemValue tiValue;
299 
300         Rectangle aBoundingRegion, aContentRegion;
301         bool bNativeOK = getTargetDevice().GetNativeControlRegion(
302             CTRL_TAB_ITEM, PART_ENTIRE_CONTROL,
303             i_rContentArea, nState,
304             tiValue, ::rtl::OUString(),
305             aBoundingRegion, aContentRegion
306         );
307         (void)bNativeOK;
308         OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::calculateDecorations: GetNativeControlRegion not implemented for CTRL_TAB_ITEM?!" );
309 
310         return aBoundingRegion;
311     }
312 
313 	//------------------------------------------------------------------------------------------------------------------
314     void NWFTabItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const
315     {
316         const ControlState nState = lcl_ItemToControlState( i_nItemFlags );
317 
318         TabitemValue tiValue;
319         if ( i_nItemFlags & ITEM_POSITION_FIRST )
320             tiValue.mnAlignment |= TABITEM_FIRST_IN_GROUP;
321         if ( i_nItemFlags & ITEM_POSITION_LAST )
322             tiValue.mnAlignment |= TABITEM_LAST_IN_GROUP;
323 
324 
325         bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, i_rContentRect, nState, tiValue, rtl::OUString() );
326         (void)bNativeOK;
327         OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::preRenderItem: inconsistent NWF implementation!" );
328             // IsNativeControlSupported returned true, previously, otherwise we would not be here ...
329     }
330 
331 	//------------------------------------------------------------------------------------------------------------------
332     void NWFTabItemRenderer::postRenderItem( Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const
333     {
334         (void)i_rActualWindow;
335         (void)i_rItemRect;
336         (void)i_nItemFlags;
337     }
338 
339     //==================================================================================================================
340 	//= PanelTabBar_Impl
341 	//==================================================================================================================
342     class PanelTabBar_Impl : public IToolPanelDeckListener
343     {
344     public:
345         PanelTabBar_Impl( PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent );
346 
347         ~PanelTabBar_Impl()
348         {
349             m_rPanelDeck.RemoveListener( *this );
350         }
351 
352         // IToolPanelDeckListener
353         virtual void PanelInserted( const PToolPanel& i_pPanel, const size_t i_nPosition )
354         {
355             (void)i_pPanel;
356             (void)i_nPosition;
357             m_bItemsDirty = true;
358             m_rTabBar.Invalidate();
359 
360             Relayout();
361         }
362 
363         virtual void PanelRemoved( const size_t i_nPosition )
364         {
365             m_bItemsDirty = true;
366             m_rTabBar.Invalidate();
367 
368             if ( i_nPosition < m_nScrollPosition )
369                 --m_nScrollPosition;
370 
371             Relayout();
372         }
373 
374         virtual void ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive );
375         virtual void LayouterChanged( const PDeckLayouter& i_rNewLayouter );
376         virtual void Dying();
377 
378         void    UpdateScrollButtons()
379         {
380             m_aScrollBack.Enable( m_nScrollPosition > 0 );
381             m_aScrollForward.Enable( m_nScrollPosition < m_aItems.size() - 1 );
382         }
383 
384         void                        Relayout();
385         void                        EnsureItemsCache();
386         ::boost::optional< size_t > FindItemForPoint( const Point& i_rPoint ) const;
387         void                        DrawItem( const size_t i_nItemIndex, const Rectangle& i_rBoundaries ) const;
388         void                        InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags = 0 ) const;
389         void                        CopyFromRenderDevice( const Rectangle& i_rLogicalRect ) const;
390         Rectangle                   GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const;
391         Rectangle                   GetItemScreenRect( const size_t i_nItemPos ) const;
392 
393         void                        FocusItem( const ::boost::optional< size_t >& i_rItemPos );
394 
395         inline bool                 IsVertical() const
396         {
397             return  (   ( m_eTabAlignment == TABS_LEFT )
398                     ||  ( m_eTabAlignment == TABS_RIGHT )
399                     );
400         }
401 
402     protected:
403         DECL_LINK( OnScroll, const PushButton* );
404 
405         void        impl_calcItemRects();
406         Size        impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const;
407         void        impl_renderItemContent( const PToolPanel& i_pPanel, const Rectangle& i_rContentArea, const TabItemContent i_eItemContent ) const;
408         ItemFlags   impl_getItemFlags( const size_t i_nItemIndex ) const;
409 
410     public:
411         PanelTabBar&                m_rTabBar;
412         TabBarGeometry              m_aGeometry;
413         NormalizedArea              m_aNormalizer;
414         TabAlignment                m_eTabAlignment;
415         IToolPanelDeck&             m_rPanelDeck;
416 
417         VirtualDevice               m_aRenderDevice;
418         PTabBarRenderer             m_pRenderer;
419 
420         ::boost::optional< size_t > m_aHoveredItem;
421         ::boost::optional< size_t > m_aFocusedItem;
422         bool                        m_bMouseButtonDown;
423 
424         ItemDescriptors             m_aItems;
425         bool                        m_bItemsDirty;
426 
427         PushButton                  m_aScrollBack;
428         PushButton                  m_aScrollForward;
429 
430         size_t                      m_nScrollPosition;
431     };
432 
433     //==================================================================================================================
434 	//= helper
435 	//==================================================================================================================
436     namespace
437     {
438 	    //--------------------------------------------------------------------------------------------------------------
439     #if OSL_DEBUG_LEVEL > 0
440         static void lcl_checkConsistency( const PanelTabBar_Impl& i_rImpl )
441         {
442             if ( !i_rImpl.m_bItemsDirty )
443             {
444                 if ( i_rImpl.m_rPanelDeck.GetPanelCount() != i_rImpl.m_aItems.size() )
445                 {
446                     OSL_ENSURE( false, "lcl_checkConsistency: inconsistent array sizes!" );
447                     return;
448                 }
449                 for ( size_t i = 0; i < i_rImpl.m_rPanelDeck.GetPanelCount(); ++i )
450                 {
451                     if ( i_rImpl.m_rPanelDeck.GetPanel( i ).get() != i_rImpl.m_aItems[i].pPanel.get() )
452                     {
453                         OSL_ENSURE( false, "lcl_checkConsistency: array elements are inconsistent!" );
454                         return;
455                     }
456                 }
457             }
458         }
459 
460         #define DBG_CHECK( data ) \
461             lcl_checkConsistency( data );
462     #else
463         #define DBG_CHECK( data ) \
464             (void)data;
465     #endif
466 
467         //--------------------------------------------------------------------------------------------------------------
468         class ClipItemRegion
469         {
470         public:
471             ClipItemRegion( const PanelTabBar_Impl& i_rImpl )
472                 :m_rDevice( i_rImpl.m_rTabBar )
473             {
474                 m_rDevice.Push( PUSH_CLIPREGION );
475                 m_rDevice.SetClipRegion( i_rImpl.m_aNormalizer.getTransformed( i_rImpl.m_aGeometry.getItemsRect(), i_rImpl.m_eTabAlignment ) );
476             }
477 
478             ~ClipItemRegion()
479             {
480                 m_rDevice.Pop();
481             }
482 
483         private:
484             OutputDevice&   m_rDevice;
485         };
486     }
487 
488 	//==================================================================================================================
489 	//= PanelTabBar_Impl - implementation
490 	//==================================================================================================================
491 	//------------------------------------------------------------------------------------------------------------------
492     PanelTabBar_Impl::PanelTabBar_Impl( PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent )
493         :m_rTabBar( i_rTabBar )
494         ,m_aGeometry( i_eItemContent )
495         ,m_aNormalizer()
496         ,m_eTabAlignment( i_eAlignment )
497         ,m_rPanelDeck( i_rPanelDeck )
498         ,m_aRenderDevice( i_rTabBar )
499         ,m_pRenderer()
500         ,m_aHoveredItem()
501         ,m_aFocusedItem()
502         ,m_bMouseButtonDown( false )
503         ,m_aItems()
504         ,m_bItemsDirty( true )
505         ,m_aScrollBack( &i_rTabBar, WB_BEVELBUTTON )
506         ,m_aScrollForward( &i_rTabBar, WB_BEVELBUTTON )
507         ,m_nScrollPosition( 0 )
508     {
509 #ifdef WNT
510         if ( m_aRenderDevice.IsNativeControlSupported( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL ) )
511             // this mode requires the NWF framework to be able to render those items onto a virtual
512             // device. For some frameworks (some GTK themes, in particular), this is known to fail.
513             // So, be on the safe side for the moment.
514             m_pRenderer.reset( new NWFTabItemRenderer( m_aRenderDevice ) );
515         else
516 #endif
517         if ( m_aRenderDevice.IsNativeControlSupported( CTRL_TOOLBAR, PART_BUTTON ) )
518             m_pRenderer.reset( new NWFToolboxItemRenderer( m_aRenderDevice ) );
519         else
520             m_pRenderer.reset( new VCLItemRenderer( m_aRenderDevice ) );
521 
522         m_aRenderDevice.SetLineColor();
523 
524         m_rPanelDeck.AddListener( *this );
525 
526         m_aScrollBack.SetSymbol( IsVertical() ? SYMBOL_ARROW_UP : SYMBOL_ARROW_LEFT );
527         m_aScrollBack.Show();
528         m_aScrollBack.SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) );
529         m_aScrollBack.SetAccessibleDescription( String( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_FWD ) ) );
530         m_aScrollBack.SetAccessibleName( m_aScrollBack.GetAccessibleDescription() );
531 
532         m_aScrollForward.SetSymbol( IsVertical() ? SYMBOL_ARROW_DOWN : SYMBOL_ARROW_RIGHT );
533         m_aScrollForward.Show();
534         m_aScrollForward.SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) );
535         m_aScrollForward.SetAccessibleDescription( String( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_BACK ) ) );
536         m_aScrollForward.SetAccessibleName( m_aScrollForward.GetAccessibleDescription() );
537     }
538 
539     //------------------------------------------------------------------------------------------------------------------
540     void PanelTabBar_Impl::impl_calcItemRects()
541     {
542         m_aItems.resize(0);
543 
544         Point aCompletePos( m_aGeometry.getFirstItemPosition() );
545         Point aIconOnlyPos( aCompletePos );
546         Point aTextOnlyPos( aCompletePos );
547 
548         for (   size_t i = 0;
549                 i < m_rPanelDeck.GetPanelCount();
550                 ++i
551             )
552         {
553             PToolPanel pPanel( m_rPanelDeck.GetPanel( i ) );
554 
555             ItemDescriptor aItem;
556             aItem.pPanel = pPanel;
557 
558             Rectangle aContentArea;
559 
560             const Size aCompleteSize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_AND_TEXT ) );
561             const Size aIconOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_ONLY ) );
562             const Size aTextOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_TEXT_ONLY ) );
563 
564             // TODO: have one method calculating all sizes?
565 
566             // remember the three areas
567             aItem.aCompleteArea = Rectangle( aCompletePos, aCompleteSize );
568             aItem.aIconOnlyArea = Rectangle( aIconOnlyPos, aIconOnlySize );
569             aItem.aTextOnlyArea = Rectangle( aTextOnlyPos, aTextOnlySize );
570 
571             m_aItems.push_back( aItem );
572 
573             aCompletePos = aItem.aCompleteArea.TopRight();
574             aIconOnlyPos = aItem.aIconOnlyArea.TopRight();
575             aTextOnlyPos = aItem.aTextOnlyArea.TopRight();
576         }
577 
578         m_bItemsDirty = false;
579     }
580 
581     //------------------------------------------------------------------------------------------------------------------
582     Size PanelTabBar_Impl::impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const
583     {
584         // calculate the size needed for the content
585         OSL_ENSURE( i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_calculateItemContentSize: illegal TabItemContent value!" );
586 
587         const Image aImage( i_pPanel->GetImage() );
588         const bool bUseImage = !!aImage && ( i_eItemContent != TABITEM_TEXT_ONLY );
589 
590         const ::rtl::OUString sItemText( i_pPanel->GetDisplayName() );
591         const bool bUseText = ( sItemText.getLength() != 0 ) && ( i_eItemContent != TABITEM_IMAGE_ONLY );
592 
593         Size aItemContentSize;
594         if ( bUseImage )
595         {
596             aItemContentSize = aImage.GetSizePixel();
597         }
598 
599         if ( bUseText )
600         {
601             if ( bUseImage )
602                 aItemContentSize.Width() += ITEM_ICON_TEXT_DISTANCE;
603 
604             // add space for text
605             const Size aTextSize( m_rTabBar.GetCtrlTextWidth( sItemText ), m_rTabBar.GetTextHeight() );
606             aItemContentSize.Width() += aTextSize.Width();
607             aItemContentSize.Height() = ::std::max( aItemContentSize.Height(), aTextSize.Height() );
608 
609             aItemContentSize.Width() += 2 * ITEM_TEXT_FLOW_SPACE;
610         }
611 
612         if ( !bUseImage && !bUseText )
613         {
614             // have a minimal size - this is pure heuristics, but if it doesn't suit your needs, then give your panels
615             // a name and or image! :)
616             aItemContentSize = Size( 16, 16 );
617         }
618 
619         aItemContentSize.Width() += 2 * ITEM_OUTER_SPACE;
620         aItemContentSize.Height() += 2 * ITEM_OUTER_SPACE;
621 
622         return aItemContentSize;
623     }
624 
625     //------------------------------------------------------------------------------------------------------------------
626     void PanelTabBar_Impl::impl_renderItemContent( const PToolPanel& i_pPanel, const Rectangle& i_rContentArea, const TabItemContent i_eItemContent ) const
627     {
628         OSL_ENSURE( i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_renderItemContent: illegal TabItemContent value!" );
629 
630         Rectangle aRenderArea( i_rContentArea );
631         if ( IsVertical() )
632         {
633             aRenderArea.Top() += ITEM_OUTER_SPACE;
634         }
635         else
636         {
637             aRenderArea.Left() += ITEM_OUTER_SPACE;
638         }
639 
640         // draw the image
641         const Image aItemImage( i_pPanel->GetImage() );
642         const Size aImageSize( aItemImage.GetSizePixel() );
643         const bool bUseImage = !!aItemImage && ( i_eItemContent != TABITEM_TEXT_ONLY );
644 
645         if ( bUseImage )
646         {
647             Point aImagePos;
648             if ( IsVertical() )
649             {
650                 aImagePos.X() = aRenderArea.Left() + ( aRenderArea.GetWidth() - aImageSize.Width() ) / 2;
651                 aImagePos.Y() = aRenderArea.Top();
652             }
653             else
654             {
655                 aImagePos.X() = aRenderArea.Left();
656                 aImagePos.Y() = aRenderArea.Top() + ( aRenderArea.GetHeight() - aImageSize.Height() ) / 2;
657             }
658             m_rTabBar.DrawImage( aImagePos, aItemImage );
659         }
660 
661         const ::rtl::OUString sItemText( i_pPanel->GetDisplayName() );
662         const bool bUseText = ( sItemText.getLength() != 0 ) && ( i_eItemContent != TABITEM_IMAGE_ONLY );
663 
664         if ( bUseText )
665         {
666             if ( IsVertical() )
667             {
668                 if ( bUseImage )
669                     aRenderArea.Top() += aImageSize.Height() + ITEM_ICON_TEXT_DISTANCE;
670                 aRenderArea.Top() += ITEM_TEXT_FLOW_SPACE;
671             }
672             else
673             {
674                 if ( bUseImage )
675                     aRenderArea.Left() += aImageSize.Width() + ITEM_ICON_TEXT_DISTANCE;
676                 aRenderArea.Left() += ITEM_TEXT_FLOW_SPACE;
677             }
678 
679             // draw the text
680             const Size aTextSize( m_rTabBar.GetCtrlTextWidth( sItemText ), m_rTabBar.GetTextHeight() );
681             Point aTextPos( aRenderArea.TopLeft() );
682             if ( IsVertical() )
683             {
684                 m_rTabBar.Push( PUSH_FONT );
685 
686                 Font aFont( m_rTabBar.GetFont() );
687                 aFont.SetOrientation( 2700 );
688                 aFont.SetVertical( sal_True );
689                 m_rTabBar.SetFont( aFont );
690 
691                 aTextPos.X() += aTextSize.Height();
692                 aTextPos.X() += ( aRenderArea.GetWidth() - aTextSize.Height() ) / 2;
693             }
694             else
695             {
696                 aTextPos.Y() += ( aRenderArea.GetHeight() - aTextSize.Height() ) / 2;
697             }
698 
699             m_rTabBar.DrawText( aTextPos, sItemText );
700 
701             if ( IsVertical() )
702             {
703                 m_rTabBar.Pop();
704             }
705         }
706     }
707 
708     //------------------------------------------------------------------------------------------------------------------
709     void PanelTabBar_Impl::CopyFromRenderDevice( const Rectangle& i_rLogicalRect ) const
710     {
711         BitmapEx aBitmap( m_aRenderDevice.GetBitmapEx(
712             i_rLogicalRect.TopLeft(),
713             Size(
714                 i_rLogicalRect.GetSize().Width(),
715                 i_rLogicalRect.GetSize().Height()
716             )
717         ) );
718         if ( IsVertical() )
719         {
720             aBitmap.Rotate( 2700, COL_BLACK );
721             if ( m_eTabAlignment == TABS_LEFT )
722                 aBitmap.Mirror( BMP_MIRROR_HORZ );
723         }
724         else if ( m_eTabAlignment == TABS_BOTTOM )
725         {
726             aBitmap.Mirror( BMP_MIRROR_VERT );
727         }
728 
729         const Rectangle aActualRect( m_aNormalizer.getTransformed( i_rLogicalRect, m_eTabAlignment ) );
730         m_rTabBar.DrawBitmapEx( aActualRect.TopLeft(), aBitmap );
731     }
732 
733     //------------------------------------------------------------------------------------------------------------------
734     void PanelTabBar_Impl::InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags ) const
735     {
736         const ItemDescriptor& rItem( m_aItems[ i_nItemIndex ] );
737         const ItemFlags nItemFlags( impl_getItemFlags( i_nItemIndex ) | i_nAdditionalItemFlags );
738 
739         const Rectangle aNormalizedContent( GetActualLogicalItemRect( rItem.GetCurrentRect() ) );
740         const Rectangle aNormalizedBounds( m_pRenderer->calculateDecorations( aNormalizedContent, nItemFlags ) );
741 
742         const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment );
743         m_rTabBar.Invalidate( aActualBounds );
744     }
745 
746     //------------------------------------------------------------------------------------------------------------------
747     ItemFlags PanelTabBar_Impl::impl_getItemFlags( const size_t i_nItemIndex ) const
748     {
749         ItemFlags nItemFlags( ITEM_STATE_NORMAL );
750         if ( m_aHoveredItem == i_nItemIndex )
751         {
752             nItemFlags |= ITEM_STATE_HOVERED;
753             if ( m_bMouseButtonDown )
754                 nItemFlags |= ITEM_STATE_ACTIVE;
755         }
756 
757         if ( m_rPanelDeck.GetActivePanel() == i_nItemIndex )
758             nItemFlags |= ITEM_STATE_ACTIVE;
759 
760         if ( m_aFocusedItem == i_nItemIndex )
761             nItemFlags |= ITEM_STATE_FOCUSED;
762 
763         if ( 0 == i_nItemIndex )
764             nItemFlags |= ITEM_POSITION_FIRST;
765 
766         if ( m_rPanelDeck.GetPanelCount() - 1 == i_nItemIndex )
767             nItemFlags |= ITEM_POSITION_LAST;
768 
769         return nItemFlags;
770     }
771 
772     //------------------------------------------------------------------------------------------------------------------
773     void PanelTabBar_Impl::DrawItem( const size_t i_nItemIndex, const Rectangle& i_rBoundaries ) const
774     {
775         const ItemDescriptor& rItem( m_aItems[ i_nItemIndex ] );
776         const ItemFlags nItemFlags( impl_getItemFlags( i_nItemIndex ) );
777 
778         // the normalized bounding and content rect
779         const Rectangle aNormalizedContent( GetActualLogicalItemRect( rItem.GetCurrentRect() ) );
780         const Rectangle aNormalizedBounds( m_pRenderer->calculateDecorations( aNormalizedContent, nItemFlags ) );
781 
782         // check whether the item actually overlaps with the painting area
783         if ( !i_rBoundaries.IsEmpty() )
784         {
785             const Rectangle aItemRect( GetActualLogicalItemRect( rItem.GetCurrentRect() ) );
786             if ( !aItemRect.IsOver( i_rBoundaries ) )
787                 return;
788         }
789 
790         m_rTabBar.SetUpdateMode( sal_False );
791 
792         // the aligned bounding and content rect
793         const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment );
794         const Rectangle aActualContent = m_aNormalizer.getTransformed( aNormalizedContent, m_eTabAlignment );
795 
796         // render item "background" layer
797         m_pRenderer->preRenderItem( aNormalizedContent, nItemFlags );
798 
799         // copy from the virtual device to ourself
800         CopyFromRenderDevice( aNormalizedBounds );
801 
802         // render the actual item content
803         impl_renderItemContent( rItem.pPanel, aActualContent, rItem.eContent );
804 
805         // render item "foreground" layer
806         m_pRenderer->postRenderItem( m_rTabBar, aActualBounds, nItemFlags );
807 
808         m_rTabBar.SetUpdateMode( sal_True );
809     }
810 
811     //------------------------------------------------------------------------------------------------------------------
812     void PanelTabBar_Impl::EnsureItemsCache()
813     {
814         if ( m_bItemsDirty == false )
815         {
816             DBG_CHECK( *this );
817             return;
818         }
819         impl_calcItemRects();
820         OSL_POSTCOND( m_bItemsDirty == false, "PanelTabBar_Impl::EnsureItemsCache: cache still dirty!" );
821         DBG_CHECK( *this );
822     }
823 
824     //------------------------------------------------------------------------------------------------------------------
825     void PanelTabBar_Impl::Relayout()
826     {
827         EnsureItemsCache();
828 
829         const Size aOutputSize( m_rTabBar.GetOutputSizePixel() );
830         m_aNormalizer = NormalizedArea( Rectangle( Point(), aOutputSize ), IsVertical() );
831         const Size aLogicalOutputSize( m_aNormalizer.getReferenceSize() );
832 
833         // forward actual output size to our render device
834         m_aRenderDevice.SetOutputSizePixel( aLogicalOutputSize );
835 
836         // re-calculate the size of the scroll buttons and of the items
837         m_aGeometry.relayout( aLogicalOutputSize, m_aItems );
838 
839         if ( m_aGeometry.getButtonBackRect().IsEmpty() )
840         {
841             m_aScrollBack.Hide();
842         }
843         else
844         {
845             const Rectangle aButtonBack( m_aNormalizer.getTransformed( m_aGeometry.getButtonBackRect(), m_eTabAlignment ) );
846             m_aScrollBack.SetPosSizePixel( aButtonBack.TopLeft(), aButtonBack.GetSize() );
847             m_aScrollBack.Show();
848         }
849 
850         if ( m_aGeometry.getButtonForwardRect().IsEmpty() )
851         {
852             m_aScrollForward.Hide();
853         }
854         else
855         {
856             const Rectangle aButtonForward( m_aNormalizer.getTransformed( m_aGeometry.getButtonForwardRect(), m_eTabAlignment ) );
857             m_aScrollForward.SetPosSizePixel( aButtonForward.TopLeft(), aButtonForward.GetSize() );
858             m_aScrollForward.Show();
859         }
860 
861         UpdateScrollButtons();
862     }
863 
864     //------------------------------------------------------------------------------------------------------------------
865     ::boost::optional< size_t > PanelTabBar_Impl::FindItemForPoint( const Point& i_rPoint ) const
866     {
867         Point aPoint( IsVertical() ? i_rPoint.Y() : i_rPoint.X(), IsVertical() ? i_rPoint.X() : i_rPoint.Y() );
868 
869         if ( !m_aGeometry.getItemsRect().IsInside( aPoint ) )
870             return ::boost::optional< size_t >();
871 
872         size_t i=0;
873         for (   ItemDescriptors::const_iterator item = m_aItems.begin();
874                 item != m_aItems.end();
875                 ++item, ++i
876             )
877         {
878             Rectangle aItemRect( GetActualLogicalItemRect( item->GetCurrentRect() ) );
879             if ( aItemRect.IsInside( aPoint ) )
880             {
881                 return ::boost::optional< size_t >( i );
882             }
883         }
884         return ::boost::optional< size_t >();
885     }
886 
887     //------------------------------------------------------------------------------------------------------------------
888     Rectangle PanelTabBar_Impl::GetItemScreenRect( const size_t i_nItemPos ) const
889     {
890         ENSURE_OR_RETURN( i_nItemPos < m_aItems.size(), "PanelTabBar_Impl::GetItemScreenRect: invalid item pos!", Rectangle() );
891         const ItemDescriptor& rItem( m_aItems[ i_nItemPos ] );
892         const Rectangle aItemRect( m_aNormalizer.getTransformed(
893             GetActualLogicalItemRect( rItem.GetCurrentRect() ),
894             m_eTabAlignment ) );
895 
896 		const Rectangle aTabBarRect( m_rTabBar.GetWindowExtentsRelative( NULL ) );
897         return Rectangle(
898             Point( aTabBarRect.Left() + aItemRect.Left(), aTabBarRect.Top() + aItemRect.Top() ),
899             aItemRect.GetSize()
900         );
901     }
902 
903     //------------------------------------------------------------------------------------------------------------------
904     void PanelTabBar_Impl::FocusItem( const ::boost::optional< size_t >& i_rItemPos )
905     {
906         // reset old focus item
907         if ( !!m_aFocusedItem )
908             InvalidateItem( *m_aFocusedItem );
909         m_aFocusedItem.reset();
910 
911         // mark the active icon as focused
912         if ( !!i_rItemPos )
913         {
914             m_aFocusedItem = i_rItemPos;
915             InvalidateItem( *m_aFocusedItem );
916         }
917     }
918 
919     //------------------------------------------------------------------------------------------------------------------
920     IMPL_LINK( PanelTabBar_Impl, OnScroll, const PushButton*, i_pButton )
921     {
922         if ( i_pButton == &m_aScrollBack )
923         {
924             OSL_ENSURE( m_nScrollPosition > 0, "PanelTabBar_Impl::OnScroll: inconsistency!" );
925             --m_nScrollPosition;
926             m_rTabBar.Invalidate();
927         }
928         else if ( i_pButton == &m_aScrollForward )
929         {
930             OSL_ENSURE( m_nScrollPosition < m_aItems.size() - 1, "PanelTabBar_Impl::OnScroll: inconsistency!" );
931             ++m_nScrollPosition;
932             m_rTabBar.Invalidate();
933         }
934 
935         UpdateScrollButtons();
936 
937         return 0L;
938     }
939 
940     //------------------------------------------------------------------------------------------------------------------
941     Rectangle PanelTabBar_Impl::GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const
942     {
943         // care for the offset imposed by our geometry, i.e. whether or not we have scroll buttons
944         Rectangle aItemRect( i_rLogicalItemRect );
945         aItemRect.Move( m_aGeometry.getItemsRect().Left() - m_aGeometry.getButtonBackRect().Left(), 0 );
946 
947         // care for the current scroll position
948         OSL_ENSURE( m_nScrollPosition < m_aItems.size(), "GetActualLogicalItemRect: invalid scroll position!" );
949         if ( ( m_nScrollPosition > 0 ) && ( m_nScrollPosition < m_aItems.size() ) )
950         {
951             long nOffsetX = m_aItems[ m_nScrollPosition ].GetCurrentRect().Left() - m_aItems[ 0 ].GetCurrentRect().Left();
952             long nOffsetY = m_aItems[ m_nScrollPosition ].GetCurrentRect().Top() - m_aItems[ 0 ].GetCurrentRect().Top();
953             aItemRect.Move( -nOffsetX, -nOffsetY );
954         }
955 
956         return aItemRect;
957     }
958 
959 	//==================================================================================================================
960 	//= PanelTabBar_Impl
961 	//==================================================================================================================
962 	//------------------------------------------------------------------------------------------------------------------
963     void PanelTabBar_Impl::ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive )
964     {
965         EnsureItemsCache();
966 
967         if ( !!i_rOldActive )
968             InvalidateItem( *i_rOldActive, ITEM_STATE_ACTIVE );
969         if ( !!i_rNewActive )
970             InvalidateItem( *i_rNewActive );
971     }
972 
973 	//------------------------------------------------------------------------------------------------------------------
974     void PanelTabBar_Impl::LayouterChanged( const PDeckLayouter& i_rNewLayouter )
975     {
976         // not interested in
977         (void)i_rNewLayouter;
978     }
979 
980 	//------------------------------------------------------------------------------------------------------------------
981     void PanelTabBar_Impl::Dying()
982     {
983         // not interested in - the notifier is a member of this instance here, so we're dying ourself at the moment
984     }
985 
986 	//==================================================================================================================
987 	//= PanelTabBar
988 	//==================================================================================================================
989 	//------------------------------------------------------------------------------------------------------------------
990     PanelTabBar::PanelTabBar( Window& i_rParentWindow, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent )
991         :Control( &i_rParentWindow, 0 )
992         ,m_pImpl( new PanelTabBar_Impl( *this, i_rPanelDeck, i_eAlignment, i_eItemContent ) )
993     {
994         DBG_CHECK( *m_pImpl );
995     }
996 
997 	//------------------------------------------------------------------------------------------------------------------
998     PanelTabBar::~PanelTabBar()
999     {
1000     }
1001 
1002 	//------------------------------------------------------------------------------------------------------------------
1003     TabItemContent PanelTabBar::GetTabItemContent() const
1004     {
1005         return m_pImpl->m_aGeometry.getItemContent();
1006     }
1007 
1008 	//------------------------------------------------------------------------------------------------------------------
1009     void PanelTabBar::SetTabItemContent( const TabItemContent& i_eItemContent )
1010     {
1011         m_pImpl->m_aGeometry.setItemContent( i_eItemContent );
1012         m_pImpl->Relayout();
1013         Invalidate();
1014     }
1015 
1016 	//------------------------------------------------------------------------------------------------------------------
1017     IToolPanelDeck& PanelTabBar::GetPanelDeck() const
1018     {
1019         DBG_CHECK( *m_pImpl );
1020         return m_pImpl->m_rPanelDeck;
1021     }
1022 
1023 	//------------------------------------------------------------------------------------------------------------------
1024     Size PanelTabBar::GetOptimalSize( WindowSizeType i_eType ) const
1025     {
1026         m_pImpl->EnsureItemsCache();
1027         Size aOptimalSize( m_pImpl->m_aGeometry.getOptimalSize( m_pImpl->m_aItems, i_eType == WINDOWSIZE_MINIMUM ) );
1028         if ( m_pImpl->IsVertical() )
1029             ::std::swap( aOptimalSize.Width(), aOptimalSize.Height() );
1030         return aOptimalSize;
1031     }
1032 
1033 	//------------------------------------------------------------------------------------------------------------------
1034     void PanelTabBar::Resize()
1035     {
1036         Control::Resize();
1037         m_pImpl->Relayout();
1038     }
1039 
1040 	//------------------------------------------------------------------------------------------------------------------
1041     void PanelTabBar::Paint( const Rectangle& i_rRect )
1042     {
1043         m_pImpl->EnsureItemsCache();
1044 
1045         // background
1046         const Rectangle aNormalizedPaintArea( m_pImpl->m_aNormalizer.getNormalized( i_rRect, m_pImpl->m_eTabAlignment ) );
1047         m_pImpl->m_aRenderDevice.Push( PUSH_CLIPREGION );
1048         m_pImpl->m_aRenderDevice.SetClipRegion( aNormalizedPaintArea );
1049         m_pImpl->m_pRenderer->renderBackground();
1050         m_pImpl->m_aRenderDevice.Pop();
1051         m_pImpl->CopyFromRenderDevice( aNormalizedPaintArea );
1052 
1053         // ensure the items really paint into their own playground only
1054         ClipItemRegion aClipItems( *m_pImpl );
1055 
1056         const Rectangle aLogicalPaintRect( m_pImpl->m_aNormalizer.getNormalized( i_rRect, m_pImpl->m_eTabAlignment ) );
1057 
1058         const ::boost::optional< size_t > aActivePanel( m_pImpl->m_rPanelDeck.GetActivePanel() );
1059         const ::boost::optional< size_t > aHoveredPanel( m_pImpl->m_aHoveredItem );
1060 
1061         // items:
1062         // 1. paint all non-active, non-hovered items
1063         size_t i=0;
1064         for (   ItemDescriptors::const_iterator item = m_pImpl->m_aItems.begin();
1065                 item != m_pImpl->m_aItems.end();
1066                 ++item, ++i
1067             )
1068         {
1069             if ( i == aActivePanel )
1070                 continue;
1071 
1072             if ( aHoveredPanel == i )
1073                 continue;
1074 
1075             m_pImpl->DrawItem( i, aLogicalPaintRect );
1076         }
1077 
1078         // 2. paint the item which is hovered, /without/ the mouse button pressed down
1079         if ( !!aHoveredPanel && !m_pImpl->m_bMouseButtonDown )
1080             m_pImpl->DrawItem( *aHoveredPanel, aLogicalPaintRect );
1081 
1082         // 3. paint the active item
1083         if ( !!aActivePanel )
1084             m_pImpl->DrawItem( *aActivePanel, aLogicalPaintRect );
1085 
1086         // 4. paint the item which is hovered, /with/ the mouse button pressed down
1087         if ( !!aHoveredPanel && m_pImpl->m_bMouseButtonDown )
1088             m_pImpl->DrawItem( *aHoveredPanel, aLogicalPaintRect );
1089     }
1090 
1091 	//------------------------------------------------------------------------------------------------------------------
1092     void PanelTabBar::MouseMove( const MouseEvent& i_rMouseEvent )
1093     {
1094         m_pImpl->EnsureItemsCache();
1095 
1096         ::boost::optional< size_t > aOldItem( m_pImpl->m_aHoveredItem );
1097         ::boost::optional< size_t > aNewItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) );
1098 
1099         if  ( i_rMouseEvent.IsLeaveWindow() )
1100             aNewItem.reset();
1101 
1102         if ( aOldItem != aNewItem )
1103         {
1104             if ( !!aOldItem )
1105                 m_pImpl->InvalidateItem( *aOldItem );
1106 
1107             m_pImpl->m_aHoveredItem = aNewItem;
1108 
1109             if ( !!aNewItem )
1110                 m_pImpl->InvalidateItem( *aNewItem );
1111         }
1112     }
1113 
1114 	//------------------------------------------------------------------------------------------------------------------
1115     void PanelTabBar::MouseButtonDown( const MouseEvent& i_rMouseEvent )
1116     {
1117         Control::MouseButtonDown( i_rMouseEvent );
1118 
1119         if ( !i_rMouseEvent.IsLeft() )
1120             return;
1121 
1122         m_pImpl->EnsureItemsCache();
1123 
1124         ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) );
1125         if ( !aHitItem )
1126             return;
1127 
1128         CaptureMouse();
1129         m_pImpl->m_bMouseButtonDown = true;
1130 
1131         m_pImpl->InvalidateItem( *aHitItem );
1132     }
1133 
1134 	//------------------------------------------------------------------------------------------------------------------
1135     void PanelTabBar::MouseButtonUp( const MouseEvent& i_rMouseEvent )
1136     {
1137         Control::MouseButtonUp( i_rMouseEvent );
1138 
1139         if ( m_pImpl->m_bMouseButtonDown )
1140         {
1141             ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) );
1142             if ( !!aHitItem )
1143             {
1144                 // re-draw that item now that we're not in mouse-down mode anymore
1145                 m_pImpl->InvalidateItem( *aHitItem );
1146                 // activate the respective panel
1147                 m_pImpl->m_rPanelDeck.ActivatePanel( *aHitItem );
1148             }
1149 
1150             OSL_ENSURE( IsMouseCaptured(), "PanelTabBar::MouseButtonUp: inconsistency!" );
1151             if ( IsMouseCaptured() )
1152                 ReleaseMouse();
1153             m_pImpl->m_bMouseButtonDown = false;
1154         }
1155     }
1156 
1157 	//------------------------------------------------------------------------------------------------------------------
1158     void PanelTabBar::RequestHelp( const HelpEvent& i_rHelpEvent )
1159     {
1160         m_pImpl->EnsureItemsCache();
1161 
1162         ::boost::optional< size_t > aHelpItem( m_pImpl->FindItemForPoint( ScreenToOutputPixel( i_rHelpEvent.GetMousePosPixel() ) ) );
1163         if ( !aHelpItem )
1164             return;
1165 
1166         const ItemDescriptor& rItem( m_pImpl->m_aItems[ *aHelpItem ] );
1167         if ( rItem.eContent != TABITEM_IMAGE_ONLY )
1168             // if the text is displayed for the item, we do not need to show it as tooltip
1169             return;
1170 
1171         const ::rtl::OUString sItemText( rItem.pPanel->GetDisplayName() );
1172 		if ( i_rHelpEvent.GetMode() == HELPMODE_BALLOON )
1173 			Help::ShowBalloon( this, OutputToScreenPixel( rItem.GetCurrentRect().Center() ), rItem.GetCurrentRect(), sItemText );
1174 		else
1175 			Help::ShowQuickHelp( this, rItem.GetCurrentRect(), sItemText );
1176     }
1177 
1178 	//------------------------------------------------------------------------------------------------------------------
1179     void PanelTabBar::GetFocus()
1180     {
1181         Control::GetFocus();
1182         if ( !m_pImpl->m_aFocusedItem )
1183             m_pImpl->FocusItem( m_pImpl->m_rPanelDeck.GetActivePanel() );
1184     }
1185 
1186 	//------------------------------------------------------------------------------------------------------------------
1187     void PanelTabBar::LoseFocus()
1188     {
1189         Control::LoseFocus();
1190 
1191         if ( !!m_pImpl->m_aFocusedItem )
1192         {
1193             m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1194         }
1195 
1196         m_pImpl->m_aFocusedItem.reset();
1197     }
1198 
1199 	//------------------------------------------------------------------------------------------------------------------
1200     class KeyInputHandler
1201     {
1202     public:
1203         KeyInputHandler( Control& i_rControl, const KeyEvent& i_rKeyEvent )
1204             :m_rControl( i_rControl )
1205             ,m_rKeyEvent( i_rKeyEvent )
1206             ,m_bHandled( false )
1207         {
1208         }
1209 
1210         ~KeyInputHandler()
1211         {
1212             if ( !m_bHandled )
1213                 m_rControl.Control::KeyInput( m_rKeyEvent );
1214         }
1215 
1216         void   setHandled()
1217         {
1218             m_bHandled = true;
1219         }
1220 
1221     private:
1222         Control&        m_rControl;
1223         const KeyEvent& m_rKeyEvent;
1224         bool            m_bHandled;
1225     };
1226 
1227 	//------------------------------------------------------------------------------------------------------------------
1228     void PanelTabBar::KeyInput( const KeyEvent& i_rKeyEvent )
1229     {
1230         KeyInputHandler aKeyInputHandler( *this, i_rKeyEvent );
1231 
1232         const KeyCode& rKeyCode( i_rKeyEvent.GetKeyCode() );
1233         if ( rKeyCode.GetModifier() != 0 )
1234             // only interested in mere key presses
1235             return;
1236 
1237         // if there are less than 2 panels, we cannot travel them ...
1238         const size_t nPanelCount( m_pImpl->m_rPanelDeck.GetPanelCount() );
1239         if ( nPanelCount < 2 )
1240             return;
1241 
1242         OSL_PRECOND( !!m_pImpl->m_aFocusedItem, "PanelTabBar::KeyInput: we should have a focused item here!" );
1243             // if we get KeyInput events, we should have the focus. In this case, m_aFocusedItem should not be empty,
1244             // except if there are no panels, but then we bail out of this method here earlier ...
1245 
1246         bool bFocusNext = false;
1247         bool bFocusPrev = false;
1248 
1249         switch ( rKeyCode.GetCode() )
1250         {
1251         case KEY_UP:    bFocusPrev = true; break;
1252         case KEY_DOWN:  bFocusNext = true; break;
1253         case KEY_LEFT:
1254             if ( IsRTLEnabled() )
1255                 bFocusNext = true;
1256             else
1257                 bFocusPrev = true;
1258             break;
1259         case KEY_RIGHT:
1260             if ( IsRTLEnabled() )
1261                 bFocusPrev = true;
1262             else
1263                 bFocusNext = true;
1264             break;
1265         case KEY_RETURN:
1266             m_pImpl->m_rPanelDeck.ActivatePanel( *m_pImpl->m_aFocusedItem );
1267             break;
1268         }
1269 
1270         if ( !bFocusNext && !bFocusPrev )
1271             return;
1272 
1273         m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1274         if ( bFocusNext )
1275         {
1276             m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + 1 ) % nPanelCount );
1277         }
1278         else
1279         {
1280             m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + nPanelCount - 1 ) % nPanelCount );
1281         }
1282         m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1283 
1284         // don't delegate to base class
1285         aKeyInputHandler.setHandled();
1286     }
1287 
1288 	//------------------------------------------------------------------------------------------------------------------
1289     void PanelTabBar::DataChanged( const DataChangedEvent& i_rDataChanedEvent )
1290     {
1291         Control::DataChanged( i_rDataChanedEvent );
1292 
1293         if  (   ( i_rDataChanedEvent.GetType() == DATACHANGED_SETTINGS )
1294             &&  ( ( i_rDataChanedEvent.GetFlags() & SETTINGS_STYLE ) != 0 )
1295             )
1296         {
1297             Invalidate();
1298         }
1299     }
1300 
1301     //------------------------------------------------------------------------------------------------------------------
1302     bool PanelTabBar::IsVertical() const
1303     {
1304         return m_pImpl->IsVertical();
1305     }
1306 
1307     //------------------------------------------------------------------------------------------------------------------
1308     PushButton& PanelTabBar::GetScrollButton( const bool i_bForward )
1309     {
1310         return i_bForward ? m_pImpl->m_aScrollForward : m_pImpl->m_aScrollBack;
1311     }
1312 
1313     //------------------------------------------------------------------------------------------------------------------
1314     ::boost::optional< size_t > PanelTabBar::GetFocusedPanelItem() const
1315     {
1316         return m_pImpl->m_aFocusedItem;
1317     }
1318 
1319     //------------------------------------------------------------------------------------------------------------------
1320     void PanelTabBar::FocusPanelItem( const size_t i_nItemPos )
1321     {
1322         ENSURE_OR_RETURN_VOID( i_nItemPos < m_pImpl->m_rPanelDeck.GetPanelCount(), "PanelTabBar::FocusPanelItem: illegal item pos!" );
1323 
1324         if ( !HasChildPathFocus() )
1325             GrabFocus();
1326 
1327         m_pImpl->FocusItem( i_nItemPos );
1328         OSL_POSTCOND( !!m_pImpl->m_aFocusedItem, "PanelTabBar::FocusPanelItem: have the focus, but no focused item?" );
1329         if ( !!m_pImpl->m_aFocusedItem )
1330             m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1331         m_pImpl->m_aFocusedItem.reset( i_nItemPos );
1332     }
1333 
1334     //------------------------------------------------------------------------------------------------------------------
1335     Rectangle PanelTabBar::GetItemScreenRect( const size_t i_nItemPos ) const
1336     {
1337         return m_pImpl->GetItemScreenRect( i_nItemPos );
1338     }
1339 
1340     //------------------------------------------------------------------------------------------------------------------
1341     Reference< XWindowPeer > PanelTabBar::GetComponentInterface( sal_Bool i_bCreate )
1342     {
1343         Reference< XWindowPeer > xWindowPeer( Control::GetComponentInterface( sal_False ) );
1344         if ( !xWindowPeer.is() && i_bCreate )
1345         {
1346             xWindowPeer.set( new PanelTabBarPeer( *this ) );
1347             SetComponentInterface( xWindowPeer );
1348         }
1349         return xWindowPeer;
1350     }
1351 
1352 //........................................................................
1353 } // namespace svt
1354 //........................................................................
1355