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