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