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