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 = ControlState::ENABLED;
71 0 : if ( i_nItemFlags & ITEM_STATE_FOCUSED ) nState |= ControlState::FOCUSED | ControlState::PRESSED;
72 0 : if ( i_nItemFlags & ITEM_STATE_HOVERED ) nState |= ControlState::ROLLOVER;
73 0 : if ( i_nItemFlags & ITEM_STATE_ACTIVE ) nState |= ControlState::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 : explicit 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 : explicit 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 : explicit 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 : ControlState::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(Rectangle(i_rContentArea.Left() + TAB_TABOFFSET_X,
300 : i_rContentArea.Right() - TAB_TABOFFSET_X,
301 : i_rContentArea.Top() + TAB_TABOFFSET_Y,
302 : i_rContentArea.Bottom() - TAB_TABOFFSET_Y));
303 :
304 : Rectangle aBoundingRegion, aContentRegion;
305 : bool bNativeOK = getTargetDevice().GetNativeControlRegion(
306 : CTRL_TAB_ITEM, PART_ENTIRE_CONTROL,
307 : i_rContentArea, nState,
308 : tiValue, OUString(),
309 : aBoundingRegion, aContentRegion
310 : );
311 : (void)bNativeOK;
312 : OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::calculateDecorations: GetNativeControlRegion not implemented for CTRL_TAB_ITEM?!" );
313 :
314 : return aBoundingRegion;
315 : }
316 :
317 :
318 : void NWFTabItemRenderer::preRenderItem( const Rectangle& i_rContentRect, const ItemFlags i_nItemFlags ) const
319 : {
320 : const ControlState nState = lcl_ItemToControlState( i_nItemFlags );
321 :
322 : TabitemValue tiValue(Rectangle(i_rContentRect.Left() + TAB_TABOFFSET_X,
323 : i_rContentRect.Right() - TAB_TABOFFSET_X,
324 : i_rContentRect.Top() + TAB_TABOFFSET_Y,
325 : i_rContentRect.Bottom() - TAB_TABOFFSET_Y));
326 :
327 : if ( i_nItemFlags & ITEM_POSITION_FIRST )
328 : tiValue.mnAlignment |= TabitemFlags::FirstInGroup;
329 : if ( i_nItemFlags & ITEM_POSITION_LAST )
330 : tiValue.mnAlignment |= TabitemFlags::LastInGroup;
331 :
332 :
333 : bool bNativeOK = getTargetDevice().DrawNativeControl( CTRL_TAB_ITEM, PART_ENTIRE_CONTROL, i_rContentRect, nState, tiValue, OUString() );
334 : (void)bNativeOK;
335 : OSL_ENSURE( bNativeOK, "NWFTabItemRenderer::preRenderItem: inconsistent NWF implementation!" );
336 : // IsNativeControlSupported returned true, previously, otherwise we would not be here ...
337 : }
338 :
339 :
340 : void NWFTabItemRenderer::postRenderItem( vcl::Window& i_rActualWindow, const Rectangle& i_rItemRect, const ItemFlags i_nItemFlags ) const
341 : {
342 : (void)i_rActualWindow;
343 : (void)i_rItemRect;
344 : (void)i_nItemFlags;
345 : }
346 : #endif
347 :
348 : //= PanelTabBar_Impl
349 :
350 : class PanelTabBar_Impl : public IToolPanelDeckListener
351 : {
352 : public:
353 : PanelTabBar_Impl(PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck,
354 : const TabAlignment i_eAlignment, const TabItemContent i_eItemContent);
355 :
356 0 : virtual ~PanelTabBar_Impl()
357 0 : {
358 0 : m_rPanelDeck.RemoveListener(*this);
359 0 : }
360 :
361 : // IToolPanelDeckListener
362 0 : virtual void PanelInserted(const PToolPanel& i_pPanel, const size_t i_nPosition) SAL_OVERRIDE
363 : {
364 : (void) i_pPanel;
365 : (void) i_nPosition;
366 0 : m_bItemsDirty = true;
367 0 : m_rTabBar.Invalidate();
368 :
369 0 : Relayout();
370 0 : }
371 :
372 0 : virtual void PanelRemoved( const size_t i_nPosition ) SAL_OVERRIDE
373 : {
374 0 : m_bItemsDirty = true;
375 0 : m_rTabBar.Invalidate();
376 :
377 0 : if ( i_nPosition < m_nScrollPosition )
378 0 : --m_nScrollPosition;
379 :
380 0 : Relayout();
381 0 : }
382 :
383 : virtual void ActivePanelChanged(const boost::optional<size_t>& i_rOldActive,
384 : const boost::optional<size_t>& i_rNewActive) SAL_OVERRIDE;
385 : virtual void LayouterChanged(const PDeckLayouter& i_rNewLayouter) SAL_OVERRIDE;
386 : virtual void Dying() SAL_OVERRIDE;
387 :
388 0 : void UpdateScrollButtons()
389 : {
390 0 : m_aScrollBack->Enable(m_nScrollPosition > 0);
391 0 : m_aScrollForward->Enable(m_nScrollPosition < m_aItems.size() - 1);
392 0 : }
393 :
394 : void Relayout();
395 : void EnsureItemsCache();
396 : boost::optional<size_t> FindItemForPoint( const Point& i_rPoint ) const;
397 : void DrawItem(vcl::RenderContext& rRenderContext, const size_t i_nItemIndex, const Rectangle& i_rBoundaries) const;
398 : void InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags = 0 ) const;
399 : void CopyFromRenderDevice(vcl::RenderContext& rRenderContext, const Rectangle& i_rLogicalRect) const;
400 : Rectangle GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const;
401 : Rectangle GetItemScreenRect( const size_t i_nItemPos ) const;
402 :
403 : void FocusItem( const ::boost::optional< size_t >& i_rItemPos );
404 :
405 0 : inline bool IsVertical() const
406 : {
407 0 : return ( ( m_eTabAlignment == TABS_LEFT )
408 0 : || ( m_eTabAlignment == TABS_RIGHT )
409 0 : );
410 : }
411 :
412 : protected:
413 : DECL_LINK( OnScroll, const PushButton* );
414 :
415 : void impl_calcItemRects();
416 : Size impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const;
417 : void impl_renderItemContent(vcl::RenderContext& rRenderContext, const PToolPanel& i_pPanel,
418 : const Rectangle& i_rContentArea, const TabItemContent i_eItemContent) const;
419 : ItemFlags impl_getItemFlags( const size_t i_nItemIndex ) const;
420 :
421 : public:
422 : PanelTabBar& m_rTabBar;
423 : TabBarGeometry m_aGeometry;
424 : NormalizedArea m_aNormalizer;
425 : TabAlignment m_eTabAlignment;
426 : IToolPanelDeck& m_rPanelDeck;
427 :
428 : ScopedVclPtr<VirtualDevice> m_aRenderDevice;
429 : PTabBarRenderer m_pRenderer;
430 :
431 : boost::optional<size_t> m_aHoveredItem;
432 : boost::optional<size_t> m_aFocusedItem;
433 : bool m_bMouseButtonDown;
434 :
435 : ItemDescriptors m_aItems;
436 : bool m_bItemsDirty;
437 :
438 : VclPtr<PushButton> m_aScrollBack;
439 : VclPtr<PushButton> m_aScrollForward;
440 :
441 : size_t m_nScrollPosition;
442 : };
443 :
444 :
445 : //= helper
446 :
447 : namespace
448 : {
449 :
450 : #if OSL_DEBUG_LEVEL > 0
451 : static void lcl_checkConsistency( const PanelTabBar_Impl& i_rImpl )
452 : {
453 : if ( !i_rImpl.m_bItemsDirty )
454 : {
455 : if ( i_rImpl.m_rPanelDeck.GetPanelCount() != i_rImpl.m_aItems.size() )
456 : {
457 : OSL_FAIL( "lcl_checkConsistency: inconsistent array sizes!" );
458 : return;
459 : }
460 : for ( size_t i = 0; i < i_rImpl.m_rPanelDeck.GetPanelCount(); ++i )
461 : {
462 : if ( i_rImpl.m_rPanelDeck.GetPanel( i ).get() != i_rImpl.m_aItems[i].pPanel.get() )
463 : {
464 : OSL_FAIL( "lcl_checkConsistency: array elements are inconsistent!" );
465 : return;
466 : }
467 : }
468 : }
469 : }
470 :
471 : #define DBG_CHECK( data ) \
472 : lcl_checkConsistency( data );
473 : #else
474 : #define DBG_CHECK( data ) \
475 : (void)data;
476 : #endif
477 :
478 :
479 : class ClipItemRegion
480 : {
481 : public:
482 0 : explicit ClipItemRegion( const PanelTabBar_Impl& i_rImpl )
483 0 : :m_rDevice( i_rImpl.m_rTabBar )
484 : {
485 0 : m_rDevice.Push( PushFlags::CLIPREGION );
486 : m_rDevice.SetClipRegion(vcl::Region(
487 : i_rImpl.m_aNormalizer.getTransformed(
488 0 : i_rImpl.m_aGeometry.getItemsRect(),
489 0 : i_rImpl.m_eTabAlignment )));
490 0 : }
491 :
492 0 : ~ClipItemRegion()
493 : {
494 0 : m_rDevice.Pop();
495 0 : }
496 :
497 : private:
498 : OutputDevice& m_rDevice;
499 : };
500 : }
501 :
502 :
503 : //= PanelTabBar_Impl - implementation
504 :
505 :
506 0 : PanelTabBar_Impl::PanelTabBar_Impl( PanelTabBar& i_rTabBar, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent )
507 : :m_rTabBar( i_rTabBar )
508 : ,m_aGeometry( i_eItemContent )
509 : ,m_aNormalizer()
510 : ,m_eTabAlignment( i_eAlignment )
511 : ,m_rPanelDeck( i_rPanelDeck )
512 : ,m_aRenderDevice( VclPtr<VirtualDevice>::Create(i_rTabBar) )
513 : ,m_pRenderer()
514 : ,m_aHoveredItem()
515 : ,m_aFocusedItem()
516 : ,m_bMouseButtonDown( false )
517 : ,m_aItems()
518 : ,m_bItemsDirty( true )
519 : ,m_aScrollBack( VclPtr<PushButton>::Create(&i_rTabBar, WB_BEVELBUTTON) )
520 : ,m_aScrollForward( VclPtr<PushButton>::Create(&i_rTabBar, WB_BEVELBUTTON) )
521 0 : ,m_nScrollPosition( 0 )
522 : {
523 : #ifdef WNT
524 : if (m_aRenderDevice->IsNativeControlSupported(CTRL_TAB_ITEM, PART_ENTIRE_CONTROL))
525 : // this mode requires the NWF framework to be able to render those items onto a virtual
526 : // device. For some frameworks (some GTK themes, in particular), this is known to fail.
527 : // So, be on the safe side for the moment.
528 : m_pRenderer.reset(new NWFTabItemRenderer(*m_aRenderDevice.get()));
529 : else
530 : #endif
531 0 : if (m_aRenderDevice->IsNativeControlSupported(CTRL_TOOLBAR, PART_BUTTON))
532 0 : m_pRenderer.reset(new NWFToolboxItemRenderer(*m_aRenderDevice.get()));
533 : else
534 0 : m_pRenderer.reset(new VCLItemRenderer(*m_aRenderDevice.get()));
535 :
536 0 : m_aRenderDevice->SetLineColor();
537 :
538 0 : m_rPanelDeck.AddListener( *this );
539 :
540 0 : m_aScrollBack->SetSymbol( IsVertical() ? SymbolType::ARROW_UP : SymbolType::ARROW_LEFT );
541 0 : m_aScrollBack->Show();
542 0 : m_aScrollBack->SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) );
543 0 : m_aScrollBack->SetAccessibleDescription( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_FWD ).toString() );
544 0 : m_aScrollBack->SetAccessibleName( m_aScrollBack->GetAccessibleDescription() );
545 :
546 0 : m_aScrollForward->SetSymbol( IsVertical() ? SymbolType::ARROW_DOWN : SymbolType::ARROW_RIGHT );
547 0 : m_aScrollForward->Show();
548 0 : m_aScrollForward->SetClickHdl( LINK( this, PanelTabBar_Impl, OnScroll ) );
549 0 : m_aScrollForward->SetAccessibleDescription( SvtResId( STR_SVT_TOOL_PANEL_BUTTON_BACK ).toString() );
550 0 : m_aScrollForward->SetAccessibleName( m_aScrollForward->GetAccessibleDescription() );
551 0 : }
552 :
553 :
554 0 : void PanelTabBar_Impl::impl_calcItemRects()
555 : {
556 0 : m_aItems.resize(0);
557 :
558 0 : Point aCompletePos( m_aGeometry.getFirstItemPosition() );
559 0 : Point aIconOnlyPos( aCompletePos );
560 0 : Point aTextOnlyPos( aCompletePos );
561 :
562 0 : for ( size_t i = 0;
563 0 : i < m_rPanelDeck.GetPanelCount();
564 : ++i
565 : )
566 : {
567 0 : PToolPanel pPanel( m_rPanelDeck.GetPanel( i ) );
568 :
569 0 : ItemDescriptor aItem;
570 0 : aItem.pPanel = pPanel;
571 :
572 0 : const Size aCompleteSize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_AND_TEXT ) );
573 0 : const Size aIconOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_IMAGE_ONLY ) );
574 0 : const Size aTextOnlySize( impl_calculateItemContentSize( pPanel, TABITEM_TEXT_ONLY ) );
575 :
576 : // TODO: have one method calculating all sizes?
577 :
578 : // remember the three areas
579 0 : aItem.aCompleteArea = Rectangle( aCompletePos, aCompleteSize );
580 0 : aItem.aIconOnlyArea = Rectangle( aIconOnlyPos, aIconOnlySize );
581 0 : aItem.aTextOnlyArea = Rectangle( aTextOnlyPos, aTextOnlySize );
582 :
583 0 : m_aItems.push_back( aItem );
584 :
585 0 : aCompletePos = aItem.aCompleteArea.TopRight();
586 0 : aIconOnlyPos = aItem.aIconOnlyArea.TopRight();
587 0 : aTextOnlyPos = aItem.aTextOnlyArea.TopRight();
588 0 : }
589 :
590 0 : m_bItemsDirty = false;
591 0 : }
592 :
593 :
594 0 : Size PanelTabBar_Impl::impl_calculateItemContentSize( const PToolPanel& i_pPanel, const TabItemContent i_eItemContent ) const
595 : {
596 : // calculate the size needed for the content
597 : OSL_ENSURE( i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_calculateItemContentSize: illegal TabItemContent value!" );
598 :
599 0 : const Image aImage( i_pPanel->GetImage() );
600 0 : const bool bUseImage = !!aImage && ( i_eItemContent != TABITEM_TEXT_ONLY );
601 :
602 0 : const OUString sItemText( i_pPanel->GetDisplayName() );
603 0 : const bool bUseText = ( !sItemText.isEmpty() ) && ( i_eItemContent != TABITEM_IMAGE_ONLY );
604 :
605 0 : Size aItemContentSize;
606 0 : if ( bUseImage )
607 : {
608 0 : aItemContentSize = aImage.GetSizePixel();
609 : }
610 :
611 0 : if ( bUseText )
612 : {
613 0 : if ( bUseImage )
614 0 : aItemContentSize.Width() += ITEM_ICON_TEXT_DISTANCE;
615 :
616 : // add space for text
617 0 : const Size aTextSize( m_rTabBar.GetCtrlTextWidth( sItemText ), m_rTabBar.GetTextHeight() );
618 0 : aItemContentSize.Width() += aTextSize.Width();
619 0 : aItemContentSize.Height() = ::std::max( aItemContentSize.Height(), aTextSize.Height() );
620 :
621 0 : aItemContentSize.Width() += 2 * ITEM_TEXT_FLOW_SPACE;
622 : }
623 :
624 0 : if ( !bUseImage && !bUseText )
625 : {
626 : // have a minimal size - this is pure heuristics, but if it doesn't suit your needs, then give your panels
627 : // a name and or image! :)
628 0 : aItemContentSize = Size( 16, 16 );
629 : }
630 :
631 0 : aItemContentSize.Width() += 2 * ITEM_OUTER_SPACE;
632 0 : aItemContentSize.Height() += 2 * ITEM_OUTER_SPACE;
633 :
634 0 : return aItemContentSize;
635 : }
636 :
637 :
638 0 : void PanelTabBar_Impl::impl_renderItemContent(vcl::RenderContext& rRenderContext, const PToolPanel& i_pPanel, const Rectangle& i_rContentArea, const TabItemContent i_eItemContent) const
639 : {
640 : OSL_ENSURE(i_eItemContent != TABITEM_AUTO, "PanelTabBar_Impl::impl_renderItemContent: illegal TabItemContent value!");
641 :
642 0 : Rectangle aRenderArea(i_rContentArea);
643 0 : if (IsVertical())
644 : {
645 0 : aRenderArea.Top() += ITEM_OUTER_SPACE;
646 : }
647 : else
648 : {
649 0 : aRenderArea.Left() += ITEM_OUTER_SPACE;
650 : }
651 :
652 : // draw the image
653 0 : const Image aItemImage(i_pPanel->GetImage());
654 0 : const Size aImageSize(aItemImage.GetSizePixel());
655 0 : const bool bUseImage = !!aItemImage && (i_eItemContent != TABITEM_TEXT_ONLY);
656 :
657 0 : if (bUseImage)
658 : {
659 0 : Point aImagePos;
660 0 : if (IsVertical())
661 : {
662 0 : aImagePos.X() = aRenderArea.Left() + (aRenderArea.GetWidth() - aImageSize.Width()) / 2;
663 0 : aImagePos.Y() = aRenderArea.Top();
664 : }
665 : else
666 : {
667 0 : aImagePos.X() = aRenderArea.Left();
668 0 : aImagePos.Y() = aRenderArea.Top() + (aRenderArea.GetHeight() - aImageSize.Height()) / 2;
669 : }
670 0 : rRenderContext.DrawImage(aImagePos, aItemImage);
671 : }
672 :
673 0 : const OUString sItemText(i_pPanel->GetDisplayName());
674 0 : const bool bUseText = (!sItemText.isEmpty()) && (i_eItemContent != TABITEM_IMAGE_ONLY);
675 :
676 0 : if (bUseText)
677 : {
678 0 : if (IsVertical())
679 : {
680 0 : if (bUseImage)
681 0 : aRenderArea.Top() += aImageSize.Height() + ITEM_ICON_TEXT_DISTANCE;
682 0 : aRenderArea.Top() += ITEM_TEXT_FLOW_SPACE;
683 : }
684 : else
685 : {
686 0 : if (bUseImage)
687 0 : aRenderArea.Left() += aImageSize.Width() + ITEM_ICON_TEXT_DISTANCE;
688 0 : aRenderArea.Left() += ITEM_TEXT_FLOW_SPACE;
689 : }
690 :
691 : // draw the text
692 0 : const Size aTextSize(m_rTabBar.GetCtrlTextWidth(sItemText), rRenderContext.GetTextHeight());
693 0 : Point aTextPos(aRenderArea.TopLeft());
694 0 : if (IsVertical())
695 : {
696 0 : rRenderContext.Push(PushFlags::FONT);
697 :
698 0 : vcl::Font aFont(rRenderContext.GetFont());
699 0 : aFont.SetOrientation(2700);
700 0 : aFont.SetVertical(true);
701 0 : rRenderContext.SetFont(aFont);
702 :
703 0 : aTextPos.X() += aTextSize.Height();
704 0 : aTextPos.X() += (aRenderArea.GetWidth() - aTextSize.Height()) / 2;
705 : }
706 : else
707 : {
708 0 : aTextPos.Y() += (aRenderArea.GetHeight() - aTextSize.Height()) / 2;
709 : }
710 :
711 0 : rRenderContext.DrawText(aTextPos, sItemText);
712 :
713 0 : if (IsVertical())
714 : {
715 0 : rRenderContext.Pop();
716 : }
717 0 : }
718 0 : }
719 :
720 0 : void PanelTabBar_Impl::CopyFromRenderDevice(vcl::RenderContext& rRenderContext, const Rectangle& i_rLogicalRect) const
721 : {
722 0 : BitmapEx aBitmap(m_aRenderDevice->GetBitmapEx(i_rLogicalRect.TopLeft(),
723 0 : Size(i_rLogicalRect.GetSize().Width(),
724 0 : i_rLogicalRect.GetSize().Height())));
725 0 : if (IsVertical())
726 : {
727 0 : aBitmap.Rotate(2700, COL_BLACK);
728 0 : if (m_eTabAlignment == TABS_LEFT)
729 0 : aBitmap.Mirror(BmpMirrorFlags::Horizontal);
730 : }
731 0 : else if (m_eTabAlignment == TABS_BOTTOM)
732 : {
733 0 : aBitmap.Mirror(BmpMirrorFlags::Vertical);
734 : }
735 :
736 0 : const Rectangle aActualRect(m_aNormalizer.getTransformed(i_rLogicalRect, m_eTabAlignment));
737 0 : rRenderContext.DrawBitmapEx(aActualRect.TopLeft(), aBitmap);
738 0 : }
739 :
740 :
741 0 : void PanelTabBar_Impl::InvalidateItem( const size_t i_nItemIndex, const ItemFlags i_nAdditionalItemFlags ) const
742 : {
743 0 : const ItemDescriptor& rItem( m_aItems[ i_nItemIndex ] );
744 0 : const ItemFlags nItemFlags( impl_getItemFlags( i_nItemIndex ) | i_nAdditionalItemFlags );
745 :
746 0 : const Rectangle aNormalizedContent( GetActualLogicalItemRect( rItem.GetCurrentRect() ) );
747 0 : const Rectangle aNormalizedBounds( m_pRenderer->calculateDecorations( aNormalizedContent, nItemFlags ) );
748 :
749 0 : const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment );
750 0 : m_rTabBar.Invalidate( aActualBounds );
751 0 : }
752 :
753 :
754 0 : ItemFlags PanelTabBar_Impl::impl_getItemFlags( const size_t i_nItemIndex ) const
755 : {
756 0 : ItemFlags nItemFlags( ITEM_STATE_NORMAL );
757 0 : if ( m_aHoveredItem == i_nItemIndex )
758 : {
759 0 : nItemFlags |= ITEM_STATE_HOVERED;
760 0 : if ( m_bMouseButtonDown )
761 0 : nItemFlags |= ITEM_STATE_ACTIVE;
762 : }
763 :
764 0 : if ( m_rPanelDeck.GetActivePanel() == i_nItemIndex )
765 0 : nItemFlags |= ITEM_STATE_ACTIVE;
766 :
767 0 : if ( m_aFocusedItem == i_nItemIndex )
768 0 : nItemFlags |= ITEM_STATE_FOCUSED;
769 :
770 0 : if ( 0 == i_nItemIndex )
771 0 : nItemFlags |= ITEM_POSITION_FIRST;
772 :
773 0 : if ( m_rPanelDeck.GetPanelCount() - 1 == i_nItemIndex )
774 0 : nItemFlags |= ITEM_POSITION_LAST;
775 :
776 0 : return nItemFlags;
777 : }
778 :
779 :
780 0 : void PanelTabBar_Impl::DrawItem(vcl::RenderContext& rRenderContext, const size_t i_nItemIndex, const Rectangle& i_rBoundaries) const
781 : {
782 0 : const ItemDescriptor& rItem(m_aItems[i_nItemIndex]);
783 0 : const ItemFlags nItemFlags(impl_getItemFlags(i_nItemIndex));
784 :
785 : // the normalized bounding and content rect
786 0 : const Rectangle aNormalizedContent(GetActualLogicalItemRect(rItem.GetCurrentRect()));
787 0 : const Rectangle aNormalizedBounds(m_pRenderer->calculateDecorations(aNormalizedContent, nItemFlags));
788 :
789 : // check whether the item actually overlaps with the painting area
790 0 : if (!i_rBoundaries.IsEmpty())
791 : {
792 0 : const Rectangle aItemRect(GetActualLogicalItemRect(rItem.GetCurrentRect()));
793 0 : if (!aItemRect.IsOver(i_rBoundaries))
794 0 : return;
795 : }
796 :
797 0 : m_rTabBar.SetUpdateMode(false);
798 :
799 : // the aligned bounding and content rect
800 0 : const Rectangle aActualBounds = m_aNormalizer.getTransformed( aNormalizedBounds, m_eTabAlignment );
801 0 : const Rectangle aActualContent = m_aNormalizer.getTransformed( aNormalizedContent, m_eTabAlignment );
802 :
803 : // render item "background" layer
804 0 : m_pRenderer->preRenderItem(aNormalizedContent, nItemFlags);
805 :
806 : // copy from the virtual device to ourself
807 0 : CopyFromRenderDevice(rRenderContext, aNormalizedBounds);
808 :
809 : // render the actual item content
810 0 : impl_renderItemContent(rRenderContext, rItem.pPanel, aActualContent, rItem.eContent);
811 :
812 : // render item "foreground" layer
813 0 : m_pRenderer->postRenderItem(m_rTabBar, aActualBounds, nItemFlags);
814 :
815 0 : m_rTabBar.SetUpdateMode(true);
816 : }
817 :
818 :
819 0 : void PanelTabBar_Impl::EnsureItemsCache()
820 : {
821 0 : if ( !m_bItemsDirty )
822 : {
823 : DBG_CHECK( *this );
824 0 : return;
825 : }
826 0 : impl_calcItemRects();
827 : SAL_WARN_IF( m_bItemsDirty , "svtools", "PanelTabBar_Impl::EnsureItemsCache: cache still dirty!" );
828 : DBG_CHECK( *this );
829 : }
830 :
831 :
832 0 : void PanelTabBar_Impl::Relayout()
833 : {
834 0 : EnsureItemsCache();
835 :
836 0 : const Size aOutputSize( m_rTabBar.GetOutputSizePixel() );
837 0 : m_aNormalizer = NormalizedArea( Rectangle( Point(), aOutputSize ), IsVertical() );
838 0 : const Size aLogicalOutputSize( m_aNormalizer.getReferenceSize() );
839 :
840 : // forward actual output size to our render device
841 0 : m_aRenderDevice->SetOutputSizePixel( aLogicalOutputSize );
842 :
843 : // re-calculate the size of the scroll buttons and of the items
844 0 : m_aGeometry.relayout( aLogicalOutputSize, m_aItems );
845 :
846 0 : if ( m_aGeometry.getButtonBackRect().IsEmpty() )
847 : {
848 0 : m_aScrollBack->Hide();
849 : }
850 : else
851 : {
852 0 : const Rectangle aButtonBack( m_aNormalizer.getTransformed( m_aGeometry.getButtonBackRect(), m_eTabAlignment ) );
853 0 : m_aScrollBack->SetPosSizePixel( aButtonBack.TopLeft(), aButtonBack.GetSize() );
854 0 : m_aScrollBack->Show();
855 : }
856 :
857 0 : if ( m_aGeometry.getButtonForwardRect().IsEmpty() )
858 : {
859 0 : m_aScrollForward->Hide();
860 : }
861 : else
862 : {
863 0 : const Rectangle aButtonForward( m_aNormalizer.getTransformed( m_aGeometry.getButtonForwardRect(), m_eTabAlignment ) );
864 0 : m_aScrollForward->SetPosSizePixel( aButtonForward.TopLeft(), aButtonForward.GetSize() );
865 0 : m_aScrollForward->Show();
866 : }
867 :
868 0 : UpdateScrollButtons();
869 0 : }
870 :
871 :
872 0 : ::boost::optional< size_t > PanelTabBar_Impl::FindItemForPoint( const Point& i_rPoint ) const
873 : {
874 0 : Point aPoint( IsVertical() ? i_rPoint.Y() : i_rPoint.X(), IsVertical() ? i_rPoint.X() : i_rPoint.Y() );
875 :
876 0 : if ( !m_aGeometry.getItemsRect().IsInside( aPoint ) )
877 0 : return ::boost::optional< size_t >();
878 :
879 0 : size_t i=0;
880 0 : for ( ItemDescriptors::const_iterator item = m_aItems.begin();
881 0 : item != m_aItems.end();
882 : ++item, ++i
883 : )
884 : {
885 0 : Rectangle aItemRect( GetActualLogicalItemRect( item->GetCurrentRect() ) );
886 0 : if ( aItemRect.IsInside( aPoint ) )
887 : {
888 0 : return ::boost::optional< size_t >( i );
889 : }
890 : }
891 0 : return ::boost::optional< size_t >();
892 : }
893 :
894 :
895 0 : Rectangle PanelTabBar_Impl::GetItemScreenRect( const size_t i_nItemPos ) const
896 : {
897 0 : ENSURE_OR_RETURN( i_nItemPos < m_aItems.size(), "PanelTabBar_Impl::GetItemScreenRect: invalid item pos!", Rectangle() );
898 0 : const ItemDescriptor& rItem( m_aItems[ i_nItemPos ] );
899 : const Rectangle aItemRect( m_aNormalizer.getTransformed(
900 0 : GetActualLogicalItemRect( rItem.GetCurrentRect() ),
901 0 : m_eTabAlignment ) );
902 :
903 0 : const Rectangle aTabBarRect( m_rTabBar.GetWindowExtentsRelative( NULL ) );
904 : return Rectangle(
905 0 : Point( aTabBarRect.Left() + aItemRect.Left(), aTabBarRect.Top() + aItemRect.Top() ),
906 : aItemRect.GetSize()
907 0 : );
908 : }
909 :
910 :
911 0 : void PanelTabBar_Impl::FocusItem( const ::boost::optional< size_t >& i_rItemPos )
912 : {
913 : // reset old focus item
914 0 : if ( !!m_aFocusedItem )
915 0 : InvalidateItem( *m_aFocusedItem );
916 0 : m_aFocusedItem.reset();
917 :
918 : // mark the active icon as focused
919 0 : if ( !!i_rItemPos )
920 : {
921 0 : m_aFocusedItem = i_rItemPos;
922 0 : InvalidateItem( *m_aFocusedItem );
923 : }
924 0 : }
925 :
926 :
927 0 : IMPL_LINK( PanelTabBar_Impl, OnScroll, const PushButton*, i_pButton )
928 : {
929 0 : if ( i_pButton == m_aScrollBack.get() )
930 : {
931 : OSL_ENSURE( m_nScrollPosition > 0, "PanelTabBar_Impl::OnScroll: inconsistency!" );
932 0 : --m_nScrollPosition;
933 0 : m_rTabBar.Invalidate();
934 : }
935 0 : else if ( i_pButton == m_aScrollForward.get() )
936 : {
937 : OSL_ENSURE( m_nScrollPosition < m_aItems.size() - 1, "PanelTabBar_Impl::OnScroll: inconsistency!" );
938 0 : ++m_nScrollPosition;
939 0 : m_rTabBar.Invalidate();
940 : }
941 :
942 0 : UpdateScrollButtons();
943 :
944 0 : return 0L;
945 : }
946 :
947 :
948 0 : Rectangle PanelTabBar_Impl::GetActualLogicalItemRect( const Rectangle& i_rLogicalItemRect ) const
949 : {
950 : // care for the offset imposed by our geometry, i.e. whether or not we have scroll buttons
951 0 : Rectangle aItemRect( i_rLogicalItemRect );
952 0 : aItemRect.Move( m_aGeometry.getItemsRect().Left() - m_aGeometry.getButtonBackRect().Left(), 0 );
953 :
954 : // care for the current scroll position
955 : OSL_ENSURE( m_nScrollPosition < m_aItems.size(), "GetActualLogicalItemRect: invalid scroll position!" );
956 0 : if ( ( m_nScrollPosition > 0 ) && ( m_nScrollPosition < m_aItems.size() ) )
957 : {
958 0 : long nOffsetX = m_aItems[ m_nScrollPosition ].GetCurrentRect().Left() - m_aItems[ 0 ].GetCurrentRect().Left();
959 0 : long nOffsetY = m_aItems[ m_nScrollPosition ].GetCurrentRect().Top() - m_aItems[ 0 ].GetCurrentRect().Top();
960 0 : aItemRect.Move( -nOffsetX, -nOffsetY );
961 : }
962 :
963 0 : return aItemRect;
964 : }
965 :
966 :
967 : //= PanelTabBar_Impl
968 :
969 :
970 0 : void PanelTabBar_Impl::ActivePanelChanged( const ::boost::optional< size_t >& i_rOldActive, const ::boost::optional< size_t >& i_rNewActive )
971 : {
972 0 : EnsureItemsCache();
973 :
974 0 : if ( !!i_rOldActive )
975 0 : InvalidateItem( *i_rOldActive, ITEM_STATE_ACTIVE );
976 0 : if ( !!i_rNewActive )
977 0 : InvalidateItem( *i_rNewActive );
978 0 : }
979 :
980 :
981 0 : void PanelTabBar_Impl::LayouterChanged( const PDeckLayouter& i_rNewLayouter )
982 : {
983 : // not interested in
984 : (void)i_rNewLayouter;
985 0 : }
986 :
987 :
988 0 : void PanelTabBar_Impl::Dying()
989 : {
990 : // not interested in - the notifier is a member of this instance here, so we're dying ourself at the moment
991 0 : }
992 :
993 :
994 : //= PanelTabBar
995 :
996 :
997 0 : PanelTabBar::PanelTabBar( vcl::Window& i_rParentWindow, IToolPanelDeck& i_rPanelDeck, const TabAlignment i_eAlignment, const TabItemContent i_eItemContent )
998 : :Control( &i_rParentWindow, 0 )
999 0 : ,m_pImpl( new PanelTabBar_Impl( *this, i_rPanelDeck, i_eAlignment, i_eItemContent ) )
1000 : {
1001 0 : DBG_CHECK( *m_pImpl );
1002 0 : }
1003 :
1004 0 : PanelTabBar::~PanelTabBar()
1005 : {
1006 0 : disposeOnce();
1007 0 : }
1008 :
1009 0 : void PanelTabBar::dispose()
1010 : {
1011 0 : Control::dispose();
1012 0 : }
1013 :
1014 0 : TabItemContent PanelTabBar::GetTabItemContent() const
1015 : {
1016 0 : return m_pImpl->m_aGeometry.getItemContent();
1017 : }
1018 :
1019 :
1020 0 : void PanelTabBar::SetTabItemContent( const TabItemContent& i_eItemContent )
1021 : {
1022 0 : m_pImpl->m_aGeometry.setItemContent( i_eItemContent );
1023 0 : m_pImpl->Relayout();
1024 0 : Invalidate();
1025 0 : }
1026 :
1027 :
1028 0 : IToolPanelDeck& PanelTabBar::GetPanelDeck() const
1029 : {
1030 0 : DBG_CHECK( *m_pImpl );
1031 0 : return m_pImpl->m_rPanelDeck;
1032 : }
1033 :
1034 :
1035 0 : Size PanelTabBar::GetOptimalSize() const
1036 : {
1037 0 : m_pImpl->EnsureItemsCache();
1038 0 : Size aOptimalSize(m_pImpl->m_aGeometry.getOptimalSize(m_pImpl->m_aItems));
1039 0 : if ( m_pImpl->IsVertical() )
1040 0 : ::std::swap( aOptimalSize.Width(), aOptimalSize.Height() );
1041 0 : return aOptimalSize;
1042 : }
1043 :
1044 :
1045 0 : void PanelTabBar::Resize()
1046 : {
1047 0 : Control::Resize();
1048 0 : m_pImpl->Relayout();
1049 0 : }
1050 :
1051 :
1052 0 : void PanelTabBar::Paint(vcl::RenderContext& rRenderContext, const Rectangle& i_rRect)
1053 : {
1054 0 : m_pImpl->EnsureItemsCache();
1055 :
1056 : // background
1057 0 : const Rectangle aNormalizedPaintArea(m_pImpl->m_aNormalizer.getNormalized(i_rRect, m_pImpl->m_eTabAlignment));
1058 0 : m_pImpl->m_aRenderDevice->Push(PushFlags::CLIPREGION);
1059 0 : m_pImpl->m_aRenderDevice->SetClipRegion(vcl::Region(aNormalizedPaintArea));
1060 0 : m_pImpl->m_pRenderer->renderBackground();
1061 0 : m_pImpl->m_aRenderDevice->Pop();
1062 0 : m_pImpl->CopyFromRenderDevice(rRenderContext, aNormalizedPaintArea);
1063 :
1064 : // ensure the items really paint into their own playground only
1065 0 : ClipItemRegion aClipItems(*m_pImpl);
1066 :
1067 0 : const Rectangle aLogicalPaintRect(m_pImpl->m_aNormalizer.getNormalized(i_rRect, m_pImpl->m_eTabAlignment));
1068 :
1069 0 : const boost::optional<size_t> aActivePanel(m_pImpl->m_rPanelDeck.GetActivePanel());
1070 0 : const boost::optional<size_t> aHoveredPanel(m_pImpl->m_aHoveredItem);
1071 :
1072 : // items:
1073 : // 1. paint all non-active, non-hovered items
1074 0 : size_t i = 0;
1075 0 : ItemDescriptors::const_iterator item;
1076 0 : for (item = m_pImpl->m_aItems.begin(); item != m_pImpl->m_aItems.end(); ++item, ++i)
1077 : {
1078 0 : if (i == aActivePanel)
1079 0 : continue;
1080 :
1081 0 : if (aHoveredPanel == i)
1082 0 : continue;
1083 :
1084 0 : m_pImpl->DrawItem(rRenderContext, i, aLogicalPaintRect);
1085 : }
1086 :
1087 : // 2. paint the item which is hovered, /without/ the mouse button pressed down
1088 0 : if (!!aHoveredPanel && !m_pImpl->m_bMouseButtonDown)
1089 0 : m_pImpl->DrawItem(rRenderContext, *aHoveredPanel, aLogicalPaintRect);
1090 :
1091 : // 3. paint the active item
1092 0 : if (!!aActivePanel)
1093 0 : m_pImpl->DrawItem(rRenderContext, *aActivePanel, aLogicalPaintRect);
1094 :
1095 : // 4. paint the item which is hovered, /with/ the mouse button pressed down
1096 0 : if (!!aHoveredPanel && m_pImpl->m_bMouseButtonDown)
1097 0 : m_pImpl->DrawItem(rRenderContext, *aHoveredPanel, aLogicalPaintRect);
1098 0 : }
1099 :
1100 :
1101 0 : void PanelTabBar::MouseMove( const MouseEvent& i_rMouseEvent )
1102 : {
1103 0 : m_pImpl->EnsureItemsCache();
1104 :
1105 0 : boost::optional< size_t > aOldItem(m_pImpl->m_aHoveredItem);
1106 0 : boost::optional< size_t > aNewItem(m_pImpl->FindItemForPoint(i_rMouseEvent.GetPosPixel()));
1107 :
1108 0 : if (i_rMouseEvent.IsLeaveWindow())
1109 0 : aNewItem = boost::optional<size_t>();
1110 :
1111 : bool const bChanged(
1112 0 : ( !aOldItem && aNewItem )
1113 0 : || ( aOldItem && !aNewItem )
1114 0 : || ( aOldItem && aNewItem && aOldItem != aNewItem ) );
1115 :
1116 0 : if (bChanged)
1117 : {
1118 0 : if (aOldItem)
1119 0 : m_pImpl->InvalidateItem( *aOldItem );
1120 :
1121 0 : m_pImpl->m_aHoveredItem = aNewItem;
1122 :
1123 0 : if (aNewItem)
1124 0 : m_pImpl->InvalidateItem( *aNewItem );
1125 0 : }
1126 0 : }
1127 :
1128 :
1129 0 : void PanelTabBar::MouseButtonDown( const MouseEvent& i_rMouseEvent )
1130 : {
1131 0 : Control::MouseButtonDown( i_rMouseEvent );
1132 :
1133 0 : if ( !i_rMouseEvent.IsLeft() )
1134 0 : return;
1135 :
1136 0 : m_pImpl->EnsureItemsCache();
1137 :
1138 0 : ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) );
1139 0 : if ( !aHitItem )
1140 0 : return;
1141 :
1142 0 : CaptureMouse();
1143 0 : m_pImpl->m_bMouseButtonDown = true;
1144 :
1145 0 : m_pImpl->InvalidateItem( *aHitItem );
1146 : }
1147 :
1148 :
1149 0 : void PanelTabBar::MouseButtonUp( const MouseEvent& i_rMouseEvent )
1150 : {
1151 0 : Control::MouseButtonUp( i_rMouseEvent );
1152 :
1153 0 : if ( m_pImpl->m_bMouseButtonDown )
1154 : {
1155 0 : ::boost::optional< size_t > aHitItem( m_pImpl->FindItemForPoint( i_rMouseEvent.GetPosPixel() ) );
1156 0 : if ( !!aHitItem )
1157 : {
1158 : // re-draw that item now that we're not in mouse-down mode anymore
1159 0 : m_pImpl->InvalidateItem( *aHitItem );
1160 : // activate the respective panel
1161 0 : m_pImpl->m_rPanelDeck.ActivatePanel( *aHitItem );
1162 : }
1163 :
1164 : OSL_ENSURE( IsMouseCaptured(), "PanelTabBar::MouseButtonUp: inconsistency!" );
1165 0 : if ( IsMouseCaptured() )
1166 0 : ReleaseMouse();
1167 0 : m_pImpl->m_bMouseButtonDown = false;
1168 : }
1169 0 : }
1170 :
1171 :
1172 0 : void PanelTabBar::RequestHelp( const HelpEvent& i_rHelpEvent )
1173 : {
1174 0 : m_pImpl->EnsureItemsCache();
1175 :
1176 0 : ::boost::optional< size_t > aHelpItem( m_pImpl->FindItemForPoint( ScreenToOutputPixel( i_rHelpEvent.GetMousePosPixel() ) ) );
1177 0 : if ( !aHelpItem )
1178 0 : return;
1179 :
1180 0 : const ItemDescriptor& rItem( m_pImpl->m_aItems[ *aHelpItem ] );
1181 0 : if ( rItem.eContent != TABITEM_IMAGE_ONLY )
1182 : // if the text is displayed for the item, we do not need to show it as tooltip
1183 0 : return;
1184 :
1185 0 : const OUString sItemText( rItem.pPanel->GetDisplayName() );
1186 0 : if ( i_rHelpEvent.GetMode() == HelpEventMode::BALLOON )
1187 0 : Help::ShowBalloon( this, OutputToScreenPixel( rItem.GetCurrentRect().Center() ), rItem.GetCurrentRect(), sItemText );
1188 : else
1189 0 : Help::ShowQuickHelp( this, rItem.GetCurrentRect(), sItemText );
1190 : }
1191 :
1192 :
1193 0 : void PanelTabBar::GetFocus()
1194 : {
1195 0 : Control::GetFocus();
1196 0 : if ( !m_pImpl->m_aFocusedItem )
1197 0 : m_pImpl->FocusItem( m_pImpl->m_rPanelDeck.GetActivePanel() );
1198 0 : }
1199 :
1200 :
1201 0 : void PanelTabBar::LoseFocus()
1202 : {
1203 0 : Control::LoseFocus();
1204 :
1205 0 : if ( m_pImpl )
1206 : {
1207 0 : if ( !!m_pImpl->m_aFocusedItem )
1208 0 : m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1209 :
1210 0 : m_pImpl->m_aFocusedItem.reset();
1211 : }
1212 0 : }
1213 :
1214 :
1215 : class KeyInputHandler
1216 : {
1217 : public:
1218 0 : KeyInputHandler( Control& i_rControl, const KeyEvent& i_rKeyEvent )
1219 : :m_rControl( i_rControl )
1220 : ,m_rKeyEvent( i_rKeyEvent )
1221 0 : ,m_bHandled( false )
1222 : {
1223 0 : }
1224 :
1225 0 : ~KeyInputHandler()
1226 : {
1227 0 : if ( !m_bHandled )
1228 0 : m_rControl.Control::KeyInput( m_rKeyEvent );
1229 0 : }
1230 :
1231 0 : void setHandled()
1232 : {
1233 0 : m_bHandled = true;
1234 0 : }
1235 :
1236 : private:
1237 : Control& m_rControl;
1238 : const KeyEvent& m_rKeyEvent;
1239 : bool m_bHandled;
1240 : };
1241 :
1242 :
1243 0 : void PanelTabBar::KeyInput( const KeyEvent& i_rKeyEvent )
1244 : {
1245 0 : KeyInputHandler aKeyInputHandler( *this, i_rKeyEvent );
1246 :
1247 0 : const vcl::KeyCode& rKeyCode( i_rKeyEvent.GetKeyCode() );
1248 0 : if ( rKeyCode.GetModifier() != 0 )
1249 : // only interested in mere key presses
1250 0 : return;
1251 :
1252 : // if there are less than 2 panels, we cannot travel them ...
1253 0 : const size_t nPanelCount( m_pImpl->m_rPanelDeck.GetPanelCount() );
1254 0 : if ( nPanelCount < 2 )
1255 0 : return;
1256 :
1257 : OSL_PRECOND( !!m_pImpl->m_aFocusedItem, "PanelTabBar::KeyInput: we should have a focused item here!" );
1258 : // if we get KeyInput events, we should have the focus. In this case, m_aFocusedItem should not be empty,
1259 : // except if there are no panels, but then we bail out of this method here earlier ...
1260 :
1261 0 : bool bFocusNext = false;
1262 0 : bool bFocusPrev = false;
1263 :
1264 0 : switch ( rKeyCode.GetCode() )
1265 : {
1266 0 : case KEY_UP: bFocusPrev = true; break;
1267 0 : case KEY_DOWN: bFocusNext = true; break;
1268 : case KEY_LEFT:
1269 0 : if ( IsRTLEnabled() )
1270 0 : bFocusNext = true;
1271 : else
1272 0 : bFocusPrev = true;
1273 0 : break;
1274 : case KEY_RIGHT:
1275 0 : if ( IsRTLEnabled() )
1276 0 : bFocusPrev = true;
1277 : else
1278 0 : bFocusNext = true;
1279 0 : break;
1280 : case KEY_RETURN:
1281 0 : m_pImpl->m_rPanelDeck.ActivatePanel( *m_pImpl->m_aFocusedItem );
1282 0 : break;
1283 : }
1284 :
1285 0 : if ( !bFocusNext && !bFocusPrev )
1286 0 : return;
1287 :
1288 0 : m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1289 0 : if ( bFocusNext )
1290 : {
1291 0 : m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + 1 ) % nPanelCount );
1292 : }
1293 : else
1294 : {
1295 0 : m_pImpl->m_aFocusedItem.reset( ( *m_pImpl->m_aFocusedItem + nPanelCount - 1 ) % nPanelCount );
1296 : }
1297 0 : m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1298 :
1299 : // don't delegate to base class
1300 0 : aKeyInputHandler.setHandled();
1301 : }
1302 :
1303 :
1304 0 : void PanelTabBar::DataChanged( const DataChangedEvent& i_rDataChanedEvent )
1305 : {
1306 0 : Control::DataChanged( i_rDataChanedEvent );
1307 :
1308 0 : if ( ( i_rDataChanedEvent.GetType() == DataChangedEventType::SETTINGS )
1309 0 : && ( i_rDataChanedEvent.GetFlags() & AllSettingsFlags::STYLE )
1310 : )
1311 : {
1312 0 : Invalidate();
1313 : }
1314 0 : }
1315 :
1316 :
1317 0 : bool PanelTabBar::IsVertical() const
1318 : {
1319 0 : return m_pImpl->IsVertical();
1320 : }
1321 :
1322 :
1323 0 : PushButton& PanelTabBar::GetScrollButton( const bool i_bForward )
1324 : {
1325 0 : return i_bForward ? *m_pImpl->m_aScrollForward.get() : *m_pImpl->m_aScrollBack.get();
1326 : }
1327 :
1328 :
1329 0 : ::boost::optional< size_t > PanelTabBar::GetFocusedPanelItem() const
1330 : {
1331 0 : return m_pImpl->m_aFocusedItem;
1332 : }
1333 :
1334 :
1335 0 : void PanelTabBar::FocusPanelItem( const size_t i_nItemPos )
1336 : {
1337 0 : ENSURE_OR_RETURN_VOID( i_nItemPos < m_pImpl->m_rPanelDeck.GetPanelCount(), "PanelTabBar::FocusPanelItem: illegal item pos!" );
1338 :
1339 0 : if ( !HasChildPathFocus() )
1340 0 : GrabFocus();
1341 :
1342 0 : m_pImpl->FocusItem( i_nItemPos );
1343 : SAL_WARN_IF( !m_pImpl->m_aFocusedItem, "svtools", "PanelTabBar::FocusPanelItem: have the focus, but no focused item?" );
1344 0 : if ( !!m_pImpl->m_aFocusedItem )
1345 0 : m_pImpl->InvalidateItem( *m_pImpl->m_aFocusedItem );
1346 0 : m_pImpl->m_aFocusedItem.reset( i_nItemPos );
1347 : }
1348 :
1349 :
1350 0 : Rectangle PanelTabBar::GetItemScreenRect( const size_t i_nItemPos ) const
1351 : {
1352 0 : return m_pImpl->GetItemScreenRect( i_nItemPos );
1353 : }
1354 :
1355 :
1356 0 : Reference< XWindowPeer > PanelTabBar::GetComponentInterface( bool i_bCreate )
1357 : {
1358 0 : Reference< XWindowPeer > xWindowPeer( Control::GetComponentInterface( false ) );
1359 0 : if ( !xWindowPeer.is() && i_bCreate )
1360 : {
1361 0 : xWindowPeer.set( new PanelTabBarPeer( *this ) );
1362 0 : SetComponentInterface( xWindowPeer );
1363 : }
1364 0 : return xWindowPeer;
1365 : }
1366 :
1367 :
1368 : } // namespace svt
1369 :
1370 :
1371 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|