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