1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_desktop.hxx" 30 31 #include "svtools/controldims.hrc" 32 33 #include "dp_gui.h" 34 #include "dp_gui_extlistbox.hxx" 35 #include "dp_gui_theextmgr.hxx" 36 #include "dp_gui_dialog2.hxx" 37 #include "dp_dependencies.hxx" 38 39 #include "comphelper/processfactory.hxx" 40 #include "com/sun/star/i18n/CollatorOptions.hpp" 41 #include "com/sun/star/deployment/DependencyException.hpp" 42 #include "com/sun/star/deployment/DeploymentException.hpp" 43 44 45 #define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) ) 46 47 #define USER_PACKAGE_MANAGER OUSTR("user") 48 #define SHARED_PACKAGE_MANAGER OUSTR("shared") 49 #define BUNDLED_PACKAGE_MANAGER OUSTR("bundled") 50 51 using namespace ::com::sun::star; 52 53 namespace dp_gui { 54 55 //------------------------------------------------------------------------------ 56 // struct Entry_Impl 57 //------------------------------------------------------------------------------ 58 Entry_Impl::Entry_Impl( const uno::Reference< deployment::XPackage > &xPackage, 59 const PackageState eState, const bool bReadOnly ) : 60 m_bActive( false ), 61 m_bLocked( bReadOnly ), 62 m_bHasOptions( false ), 63 m_bUser( false ), 64 m_bShared( false ), 65 m_bNew( false ), 66 m_bChecked( false ), 67 m_bMissingDeps( false ), 68 m_bHasButtons( false ), 69 m_bMissingLic( false ), 70 m_eState( eState ), 71 m_pPublisher( NULL ), 72 m_xPackage( xPackage ) 73 { 74 try 75 { 76 m_sTitle = xPackage->getDisplayName(); 77 m_sVersion = xPackage->getVersion(); 78 m_sDescription = xPackage->getDescription(); 79 m_sLicenseText = xPackage->getLicenseText(); 80 81 beans::StringPair aInfo( m_xPackage->getPublisherInfo() ); 82 m_sPublisher = aInfo.First; 83 m_sPublisherURL = aInfo.Second; 84 85 // get the icons for the package if there are any 86 uno::Reference< graphic::XGraphic > xGraphic = xPackage->getIcon( false ); 87 if ( xGraphic.is() ) 88 m_aIcon = Image( xGraphic ); 89 90 xGraphic = xPackage->getIcon( true ); 91 if ( xGraphic.is() ) 92 m_aIconHC = Image( xGraphic ); 93 else 94 m_aIconHC = m_aIcon; 95 96 if ( eState == AMBIGUOUS ) 97 m_sErrorText = DialogHelper::getResourceString( RID_STR_ERROR_UNKNOWN_STATUS ); 98 else if ( eState == NOT_REGISTERED ) 99 checkDependencies(); 100 } 101 catch (deployment::ExtensionRemovedException &) {} 102 catch (uno::RuntimeException &) {} 103 } 104 105 //------------------------------------------------------------------------------ 106 Entry_Impl::~Entry_Impl() 107 {} 108 109 //------------------------------------------------------------------------------ 110 StringCompare Entry_Impl::CompareTo( const CollatorWrapper *pCollator, const TEntry_Impl pEntry ) const 111 { 112 StringCompare eCompare = (StringCompare) pCollator->compareString( m_sTitle, pEntry->m_sTitle ); 113 if ( eCompare == COMPARE_EQUAL ) 114 { 115 eCompare = m_sVersion.CompareTo( pEntry->m_sVersion ); 116 if ( eCompare == COMPARE_EQUAL ) 117 { 118 sal_Int32 nCompare = m_xPackage->getRepositoryName().compareTo( pEntry->m_xPackage->getRepositoryName() ); 119 if ( nCompare < 0 ) 120 eCompare = COMPARE_LESS; 121 else if ( nCompare > 0 ) 122 eCompare = COMPARE_GREATER; 123 } 124 } 125 return eCompare; 126 } 127 128 //------------------------------------------------------------------------------ 129 void Entry_Impl::checkDependencies() 130 { 131 try { 132 m_xPackage->checkDependencies( uno::Reference< ucb::XCommandEnvironment >() ); 133 } 134 catch ( deployment::DeploymentException &e ) 135 { 136 deployment::DependencyException depExc; 137 if ( e.Cause >>= depExc ) 138 { 139 rtl::OUString aMissingDep( DialogHelper::getResourceString( RID_STR_ERROR_MISSING_DEPENDENCIES ) ); 140 for ( sal_Int32 i = 0; i < depExc.UnsatisfiedDependencies.getLength(); ++i ) 141 { 142 aMissingDep += OUSTR("\n"); 143 aMissingDep += dp_misc::Dependencies::getErrorText( depExc.UnsatisfiedDependencies[i]); 144 } 145 aMissingDep += OUSTR("\n"); 146 m_sErrorText = aMissingDep; 147 m_bMissingDeps = true; 148 } 149 } 150 } 151 //------------------------------------------------------------------------------ 152 // ExtensionRemovedListener 153 //------------------------------------------------------------------------------ 154 void ExtensionRemovedListener::disposing( lang::EventObject const & rEvt ) 155 throw ( uno::RuntimeException ) 156 { 157 uno::Reference< deployment::XPackage > xPackage( rEvt.Source, uno::UNO_QUERY ); 158 159 if ( xPackage.is() ) 160 { 161 m_pParent->removeEntry( xPackage ); 162 } 163 } 164 165 //------------------------------------------------------------------------------ 166 ExtensionRemovedListener::~ExtensionRemovedListener() 167 { 168 } 169 170 //------------------------------------------------------------------------------ 171 // ExtensionBox_Impl 172 //------------------------------------------------------------------------------ 173 ExtensionBox_Impl::ExtensionBox_Impl( Dialog* pParent, TheExtensionManager *pManager ) : 174 IExtensionListBox( pParent, WB_BORDER | WB_TABSTOP | WB_CHILDDLGCTRL ), 175 m_bHasScrollBar( false ), 176 m_bHasActive( false ), 177 m_bNeedsRecalc( true ), 178 m_bHasNew( false ), 179 m_bInCheckMode( false ), 180 m_bAdjustActive( false ), 181 m_bInDelete( false ), 182 m_nActive( 0 ), 183 m_nTopIndex( 0 ), 184 m_nActiveHeight( 0 ), 185 m_nExtraHeight( 2 ), 186 m_aSharedImage( DialogHelper::getResId( RID_IMG_SHARED ) ), 187 m_aSharedImageHC( DialogHelper::getResId( RID_IMG_SHARED_HC ) ), 188 m_aLockedImage( DialogHelper::getResId( RID_IMG_LOCKED ) ), 189 m_aLockedImageHC( DialogHelper::getResId( RID_IMG_LOCKED_HC ) ), 190 m_aWarningImage( DialogHelper::getResId( RID_IMG_WARNING ) ), 191 m_aWarningImageHC( DialogHelper::getResId( RID_IMG_WARNING_HC ) ), 192 m_aDefaultImage( DialogHelper::getResId( RID_IMG_EXTENSION ) ), 193 m_aDefaultImageHC( DialogHelper::getResId( RID_IMG_EXTENSION_HC ) ), 194 m_pScrollBar( NULL ), 195 m_pManager( pManager ) 196 { 197 SetHelpId( HID_EXTENSION_MANAGER_LISTBOX ); 198 199 m_pScrollBar = new ScrollBar( this, WB_VERT ); 200 m_pScrollBar->SetScrollHdl( LINK( this, ExtensionBox_Impl, ScrollHdl ) ); 201 m_pScrollBar->EnableDrag(); 202 203 SetPaintTransparent( true ); 204 SetPosPixel( Point( RSC_SP_DLG_INNERBORDER_LEFT, RSC_SP_DLG_INNERBORDER_TOP ) ); 205 long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE; 206 long nTitleHeight = 2*TOP_OFFSET + GetTextHeight(); 207 if ( nIconHeight < nTitleHeight ) 208 m_nStdHeight = nTitleHeight; 209 else 210 m_nStdHeight = nIconHeight; 211 m_nStdHeight += GetTextHeight() + TOP_OFFSET; 212 213 nIconHeight = ICON_HEIGHT + 2*TOP_OFFSET + 1; 214 if ( m_nStdHeight < nIconHeight ) 215 m_nStdHeight = nIconHeight; 216 217 m_nActiveHeight = m_nStdHeight; 218 219 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); 220 if( IsControlBackground() ) 221 SetBackground( GetControlBackground() ); 222 else 223 SetBackground( rStyleSettings.GetFieldColor() ); 224 225 m_xRemoveListener = new ExtensionRemovedListener( this ); 226 227 m_pLocale = new lang::Locale( Application::GetSettings().GetLocale() ); 228 m_pCollator = new CollatorWrapper( ::comphelper::getProcessServiceFactory() ); 229 m_pCollator->loadDefaultCollator( *m_pLocale, i18n::CollatorOptions::CollatorOptions_IGNORE_CASE ); 230 231 Show(); 232 } 233 234 //------------------------------------------------------------------------------ 235 ExtensionBox_Impl::~ExtensionBox_Impl() 236 { 237 if ( ! m_bInDelete ) 238 DeleteRemoved(); 239 240 m_bInDelete = true; 241 242 typedef std::vector< TEntry_Impl >::iterator ITER; 243 244 for ( ITER iIndex = m_vEntries.begin(); iIndex < m_vEntries.end(); ++iIndex ) 245 { 246 if ( (*iIndex)->m_pPublisher ) 247 { 248 delete (*iIndex)->m_pPublisher; 249 (*iIndex)->m_pPublisher = NULL; 250 } 251 (*iIndex)->m_xPackage->removeEventListener( uno::Reference< lang::XEventListener > ( m_xRemoveListener, uno::UNO_QUERY ) ); 252 } 253 254 m_vEntries.clear(); 255 256 delete m_pScrollBar; 257 258 m_xRemoveListener.clear(); 259 260 delete m_pLocale; 261 delete m_pCollator; 262 } 263 264 //------------------------------------------------------------------------------ 265 sal_Int32 ExtensionBox_Impl::getItemCount() const 266 { 267 return static_cast< sal_Int32 >( m_vEntries.size() ); 268 } 269 270 //------------------------------------------------------------------------------ 271 sal_Int32 ExtensionBox_Impl::getSelIndex() const 272 { 273 if ( m_bHasActive ) 274 { 275 OSL_ASSERT( m_nActive >= -1); 276 return static_cast< sal_Int32 >( m_nActive ); 277 } 278 else 279 return static_cast< sal_Int32 >( EXTENSION_LISTBOX_ENTRY_NOTFOUND ); 280 } 281 282 //------------------------------------------------------------------------------ 283 void ExtensionBox_Impl::checkIndex( sal_Int32 nIndex ) const 284 { 285 if ( nIndex < 0 ) 286 throw lang::IllegalArgumentException( OUSTR("The list index starts with 0"),0, 0 ); 287 if ( static_cast< sal_uInt32 >( nIndex ) >= m_vEntries.size()) 288 throw lang::IllegalArgumentException( OUSTR("There is no element at the provided position." 289 "The position exceeds the number of available list entries"),0, 0 ); 290 } 291 292 //------------------------------------------------------------------------------ 293 rtl::OUString ExtensionBox_Impl::getItemName( sal_Int32 nIndex ) const 294 { 295 const ::osl::MutexGuard aGuard( m_entriesMutex ); 296 checkIndex( nIndex ); 297 return m_vEntries[ nIndex ]->m_sTitle; 298 } 299 300 //------------------------------------------------------------------------------ 301 rtl::OUString ExtensionBox_Impl::getItemVersion( sal_Int32 nIndex ) const 302 { 303 const ::osl::MutexGuard aGuard( m_entriesMutex ); 304 checkIndex( nIndex ); 305 return m_vEntries[ nIndex ]->m_sVersion; 306 } 307 308 //------------------------------------------------------------------------------ 309 rtl::OUString ExtensionBox_Impl::getItemDescription( sal_Int32 nIndex ) const 310 { 311 const ::osl::MutexGuard aGuard( m_entriesMutex ); 312 checkIndex( nIndex ); 313 return m_vEntries[ nIndex ]->m_sDescription; 314 } 315 316 //------------------------------------------------------------------------------ 317 rtl::OUString ExtensionBox_Impl::getItemPublisher( sal_Int32 nIndex ) const 318 { 319 const ::osl::MutexGuard aGuard( m_entriesMutex ); 320 checkIndex( nIndex ); 321 return m_vEntries[ nIndex ]->m_sPublisher; 322 } 323 324 //------------------------------------------------------------------------------ 325 rtl::OUString ExtensionBox_Impl::getItemPublisherLink( sal_Int32 nIndex ) const 326 { 327 const ::osl::MutexGuard aGuard( m_entriesMutex ); 328 checkIndex( nIndex ); 329 return m_vEntries[ nIndex ]->m_sPublisherURL; 330 } 331 332 //------------------------------------------------------------------------------ 333 void ExtensionBox_Impl::select( sal_Int32 nIndex ) 334 { 335 const ::osl::MutexGuard aGuard( m_entriesMutex ); 336 checkIndex( nIndex ); 337 selectEntry( nIndex ); 338 } 339 340 //------------------------------------------------------------------------------ 341 void ExtensionBox_Impl::select( const rtl::OUString & sName ) 342 { 343 const ::osl::MutexGuard aGuard( m_entriesMutex ); 344 typedef ::std::vector< TEntry_Impl >::const_iterator It; 345 346 for ( It iIter = m_vEntries.begin(); iIter < m_vEntries.end(); iIter++ ) 347 { 348 if ( sName.equals( (*iIter)->m_sTitle ) ) 349 { 350 long nPos = iIter - m_vEntries.begin(); 351 selectEntry( nPos ); 352 break; 353 } 354 } 355 } 356 357 //------------------------------------------------------------------------------ 358 //------------------------------------------------------------------------------ 359 // Title + description 360 void ExtensionBox_Impl::CalcActiveHeight( const long nPos ) 361 { 362 const ::osl::MutexGuard aGuard( m_entriesMutex ); 363 364 // get title height 365 long aTextHeight; 366 long nIconHeight = 2*TOP_OFFSET + SMALL_ICON_SIZE; 367 long nTitleHeight = 2*TOP_OFFSET + GetTextHeight(); 368 if ( nIconHeight < nTitleHeight ) 369 aTextHeight = nTitleHeight; 370 else 371 aTextHeight = nIconHeight; 372 373 // calc description height 374 Size aSize = GetOutputSizePixel(); 375 if ( m_bHasScrollBar ) 376 aSize.Width() -= m_pScrollBar->GetSizePixel().Width(); 377 378 aSize.Width() -= ICON_OFFSET; 379 aSize.Height() = 10000; 380 381 rtl::OUString aText( m_vEntries[ nPos ]->m_sErrorText ); 382 if ( aText.getLength() ) 383 aText += OUSTR("\n"); 384 aText += m_vEntries[ nPos ]->m_sDescription; 385 386 Rectangle aRect = GetTextRect( Rectangle( Point(), aSize ), aText, 387 TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 388 aTextHeight += aRect.GetHeight(); 389 390 if ( aTextHeight < m_nStdHeight ) 391 aTextHeight = m_nStdHeight; 392 393 if ( m_vEntries[ nPos ]->m_bHasButtons ) 394 m_nActiveHeight = aTextHeight + m_nExtraHeight; 395 else 396 m_nActiveHeight = aTextHeight + 2; 397 } 398 399 //------------------------------------------------------------------------------ 400 const Size ExtensionBox_Impl::GetMinOutputSizePixel() const 401 { 402 return Size( 200, 80 ); 403 } 404 405 //------------------------------------------------------------------------------ 406 Rectangle ExtensionBox_Impl::GetEntryRect( const long nPos ) const 407 { 408 const ::osl::MutexGuard aGuard( m_entriesMutex ); 409 410 Size aSize( GetOutputSizePixel() ); 411 412 if ( m_bHasScrollBar ) 413 aSize.Width() -= m_pScrollBar->GetSizePixel().Width(); 414 415 if ( m_vEntries[ nPos ]->m_bActive ) 416 aSize.Height() = m_nActiveHeight; 417 else 418 aSize.Height() = m_nStdHeight; 419 420 Point aPos( 0, -m_nTopIndex + nPos * m_nStdHeight ); 421 if ( m_bHasActive && ( nPos < m_nActive ) ) 422 aPos.Y() += m_nActiveHeight - m_nStdHeight; 423 424 return Rectangle( aPos, aSize ); 425 } 426 427 //------------------------------------------------------------------------------ 428 void ExtensionBox_Impl::DeleteRemoved() 429 { 430 const ::osl::MutexGuard aGuard( m_entriesMutex ); 431 432 m_bInDelete = true; 433 434 if ( ! m_vRemovedEntries.empty() ) 435 { 436 typedef std::vector< TEntry_Impl >::iterator ITER; 437 438 for ( ITER iIndex = m_vRemovedEntries.begin(); iIndex < m_vRemovedEntries.end(); ++iIndex ) 439 { 440 if ( (*iIndex)->m_pPublisher ) 441 { 442 delete (*iIndex)->m_pPublisher; 443 (*iIndex)->m_pPublisher = NULL; 444 } 445 } 446 447 m_vRemovedEntries.clear(); 448 } 449 450 m_bInDelete = false; 451 } 452 453 //------------------------------------------------------------------------------ 454 //This function may be called with nPos < 0 455 void ExtensionBox_Impl::selectEntry( const long nPos ) 456 { 457 //ToDo whe should not use the guard at such a big scope here. 458 //Currently it is used to gard m_vEntries and m_nActive. m_nActive will be 459 //modified in this function. 460 //It would be probably best to always use a copy of m_vEntries 461 //and some other state variables from ExtensionBox_Impl for 462 //the whole painting operation. See issue i86993 463 ::osl::ClearableMutexGuard guard(m_entriesMutex); 464 465 if ( m_bInCheckMode ) 466 return; 467 468 if ( m_bHasActive ) 469 { 470 if ( nPos == m_nActive ) 471 return; 472 473 m_bHasActive = false; 474 m_vEntries[ m_nActive ]->m_bActive = false; 475 } 476 477 if ( ( nPos >= 0 ) && ( nPos < (long) m_vEntries.size() ) ) 478 { 479 m_bHasActive = true; 480 m_nActive = nPos; 481 m_vEntries[ nPos ]->m_bActive = true; 482 483 if ( IsReallyVisible() ) 484 { 485 m_bAdjustActive = true; 486 } 487 } 488 489 if ( IsReallyVisible() ) 490 { 491 m_bNeedsRecalc = true; 492 Invalidate(); 493 } 494 495 guard.clear(); 496 } 497 498 // ----------------------------------------------------------------------- 499 void ExtensionBox_Impl::DrawRow( const Rectangle& rRect, const TEntry_Impl pEntry ) 500 { 501 const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); 502 503 if ( pEntry->m_bActive ) 504 SetTextColor( rStyleSettings.GetHighlightTextColor() ); 505 else if ( ( pEntry->m_eState != REGISTERED ) && ( pEntry->m_eState != NOT_AVAILABLE ) ) 506 SetTextColor( rStyleSettings.GetDisableColor() ); 507 else if ( IsControlForeground() ) 508 SetTextColor( GetControlForeground() ); 509 else 510 SetTextColor( rStyleSettings.GetFieldTextColor() ); 511 512 if ( pEntry->m_bActive ) 513 { 514 SetLineColor(); 515 SetFillColor( rStyleSettings.GetHighlightColor() ); 516 DrawRect( rRect ); 517 } 518 else 519 { 520 if( IsControlBackground() ) 521 SetBackground( GetControlBackground() ); 522 else 523 SetBackground( rStyleSettings.GetFieldColor() ); 524 525 SetTextFillColor(); 526 Erase( rRect ); 527 } 528 529 // Draw extension icon 530 Point aPos( rRect.TopLeft() ); 531 aPos += Point( TOP_OFFSET, TOP_OFFSET ); 532 Image aImage; 533 if ( ! pEntry->m_aIcon ) 534 aImage = isHCMode() ? m_aDefaultImageHC : m_aDefaultImage; 535 else 536 aImage = isHCMode() ? pEntry->m_aIconHC : pEntry->m_aIcon; 537 Size aImageSize = aImage.GetSizePixel(); 538 if ( ( aImageSize.Width() <= ICON_WIDTH ) && ( aImageSize.Height() <= ICON_HEIGHT ) ) 539 DrawImage( Point( aPos.X()+((ICON_WIDTH-aImageSize.Width())/2), aPos.Y()+((ICON_HEIGHT-aImageSize.Height())/2) ), aImage ); 540 else 541 DrawImage( aPos, Size( ICON_WIDTH, ICON_HEIGHT ), aImage ); 542 543 // Setup fonts 544 Font aStdFont( GetFont() ); 545 Font aBoldFont( aStdFont ); 546 aBoldFont.SetWeight( WEIGHT_BOLD ); 547 SetFont( aBoldFont ); 548 long aTextHeight = GetTextHeight(); 549 550 // Init publisher link here 551 if ( !pEntry->m_pPublisher && pEntry->m_sPublisher.Len() ) 552 { 553 pEntry->m_pPublisher = new svt::FixedHyperlink( this ); 554 pEntry->m_pPublisher->SetBackground(); 555 pEntry->m_pPublisher->SetPaintTransparent( true ); 556 pEntry->m_pPublisher->SetURL( pEntry->m_sPublisherURL ); 557 pEntry->m_pPublisher->SetDescription( pEntry->m_sPublisher ); 558 Size aSize = FixedText::CalcMinimumTextSize( pEntry->m_pPublisher ); 559 pEntry->m_pPublisher->SetSizePixel( aSize ); 560 561 if ( m_aClickHdl.IsSet() ) 562 pEntry->m_pPublisher->SetClickHdl( m_aClickHdl ); 563 } 564 565 // Get max title width 566 long nMaxTitleWidth = rRect.GetWidth() - ICON_OFFSET; 567 nMaxTitleWidth -= ( 2 * SMALL_ICON_SIZE ) + ( 4 * SPACE_BETWEEN ); 568 if ( pEntry->m_pPublisher ) 569 { 570 nMaxTitleWidth -= pEntry->m_pPublisher->GetSizePixel().Width() + (2*SPACE_BETWEEN); 571 } 572 573 long aVersionWidth = GetTextWidth( pEntry->m_sVersion ); 574 long aTitleWidth = GetTextWidth( pEntry->m_sTitle ) + (aTextHeight / 3); 575 576 aPos = rRect.TopLeft() + Point( ICON_OFFSET, TOP_OFFSET ); 577 578 if ( aTitleWidth > nMaxTitleWidth - aVersionWidth ) 579 { 580 aTitleWidth = nMaxTitleWidth - aVersionWidth - (aTextHeight / 3); 581 String aShortTitle = GetEllipsisString( pEntry->m_sTitle, aTitleWidth ); 582 DrawText( aPos, aShortTitle ); 583 aTitleWidth += (aTextHeight / 3); 584 } 585 else 586 DrawText( aPos, pEntry->m_sTitle ); 587 588 SetFont( aStdFont ); 589 DrawText( Point( aPos.X() + aTitleWidth, aPos.Y() ), pEntry->m_sVersion ); 590 591 long nIconHeight = TOP_OFFSET + SMALL_ICON_SIZE; 592 long nTitleHeight = TOP_OFFSET + GetTextHeight(); 593 if ( nIconHeight < nTitleHeight ) 594 aTextHeight = nTitleHeight; 595 else 596 aTextHeight = nIconHeight; 597 598 // draw description 599 String sDescription; 600 if ( pEntry->m_sErrorText.Len() ) 601 { 602 if ( pEntry->m_bActive ) 603 sDescription = pEntry->m_sErrorText + OUSTR("\n") + pEntry->m_sDescription; 604 else 605 sDescription = pEntry->m_sErrorText; 606 } 607 else 608 sDescription = pEntry->m_sDescription; 609 610 aPos.Y() += aTextHeight; 611 if ( pEntry->m_bActive ) 612 { 613 long nExtraHeight = 0; 614 615 if ( pEntry->m_bHasButtons ) 616 nExtraHeight = m_nExtraHeight; 617 618 DrawText( Rectangle( aPos.X(), aPos.Y(), rRect.Right(), rRect.Bottom() - nExtraHeight ), 619 sDescription, TEXT_DRAW_MULTILINE | TEXT_DRAW_WORDBREAK ); 620 } 621 else 622 { 623 const long nWidth = GetTextWidth( sDescription ); 624 if ( nWidth > rRect.GetWidth() - aPos.X() ) 625 sDescription = GetEllipsisString( sDescription, rRect.GetWidth() - aPos.X() ); 626 DrawText( aPos, sDescription ); 627 } 628 629 // Draw publisher link 630 if ( pEntry->m_pPublisher ) 631 { 632 pEntry->m_pPublisher->Show(); 633 aPos = rRect.TopLeft() + Point( ICON_OFFSET + nMaxTitleWidth + (2*SPACE_BETWEEN), TOP_OFFSET ); 634 pEntry->m_pPublisher->SetPosPixel( aPos ); 635 } 636 637 // Draw status icons 638 if ( !pEntry->m_bUser ) 639 { 640 aPos = rRect.TopRight() + Point( -(RIGHT_ICON_OFFSET + SMALL_ICON_SIZE), TOP_OFFSET ); 641 if ( pEntry->m_bLocked ) 642 DrawImage( aPos, Size( SMALL_ICON_SIZE, SMALL_ICON_SIZE ), isHCMode() ? m_aLockedImageHC : m_aLockedImage ); 643 else 644 DrawImage( aPos, Size( SMALL_ICON_SIZE, SMALL_ICON_SIZE ), isHCMode() ? m_aSharedImageHC : m_aSharedImage ); 645 } 646 if ( ( pEntry->m_eState == AMBIGUOUS ) || pEntry->m_bMissingDeps || pEntry->m_bMissingLic ) 647 { 648 aPos = rRect.TopRight() + Point( -(RIGHT_ICON_OFFSET + SPACE_BETWEEN + 2*SMALL_ICON_SIZE), TOP_OFFSET ); 649 DrawImage( aPos, Size( SMALL_ICON_SIZE, SMALL_ICON_SIZE ), isHCMode() ? m_aWarningImageHC : m_aWarningImage ); 650 } 651 652 SetLineColor( Color( COL_LIGHTGRAY ) ); 653 DrawLine( rRect.BottomLeft(), rRect.BottomRight() ); 654 } 655 656 // ----------------------------------------------------------------------- 657 void ExtensionBox_Impl::RecalcAll() 658 { 659 if ( m_bHasActive ) 660 CalcActiveHeight( m_nActive ); 661 662 SetupScrollBar(); 663 664 if ( m_bHasActive ) 665 { 666 Rectangle aEntryRect = GetEntryRect( m_nActive ); 667 668 if ( m_bAdjustActive ) 669 { 670 m_bAdjustActive = false; 671 672 // If the top of the selected entry isn't visible, make it visible 673 if ( aEntryRect.Top() < 0 ) 674 { 675 m_nTopIndex += aEntryRect.Top(); 676 aEntryRect.Move( 0, -aEntryRect.Top() ); 677 } 678 679 // If the bottom of the selected entry isn't visible, make it visible even if now the top 680 // isn't visible any longer ( the buttons are more important ) 681 Size aOutputSize = GetOutputSizePixel(); 682 if ( aEntryRect.Bottom() > aOutputSize.Height() ) 683 { 684 m_nTopIndex += ( aEntryRect.Bottom() - aOutputSize.Height() ); 685 aEntryRect.Move( 0, -( aEntryRect.Bottom() - aOutputSize.Height() ) ); 686 } 687 688 // If there is unused space below the last entry but all entries don't fit into the box, 689 // move the content down to use the whole space 690 const long nTotalHeight = GetTotalHeight(); 691 if ( m_bHasScrollBar && ( aOutputSize.Height() + m_nTopIndex > nTotalHeight ) ) 692 { 693 long nOffset = m_nTopIndex; 694 m_nTopIndex = nTotalHeight - aOutputSize.Height(); 695 nOffset -= m_nTopIndex; 696 aEntryRect.Move( 0, nOffset ); 697 } 698 699 if ( m_bHasScrollBar ) 700 m_pScrollBar->SetThumbPos( m_nTopIndex ); 701 } 702 } 703 704 m_bNeedsRecalc = false; 705 } 706 707 // ----------------------------------------------------------------------- 708 bool ExtensionBox_Impl::HandleTabKey( bool ) 709 { 710 return false; 711 } 712 713 // ----------------------------------------------------------------------- 714 bool ExtensionBox_Impl::HandleCursorKey( sal_uInt16 nKeyCode ) 715 { 716 if ( m_vEntries.empty() ) 717 return true; 718 719 long nSelect = 0; 720 721 if ( m_bHasActive ) 722 { 723 long nPageSize = GetOutputSizePixel().Height() / m_nStdHeight; 724 if ( nPageSize < 2 ) 725 nPageSize = 2; 726 727 if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_RIGHT ) ) 728 nSelect = m_nActive + 1; 729 else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_LEFT ) ) 730 nSelect = m_nActive - 1; 731 else if ( nKeyCode == KEY_HOME ) 732 nSelect = 0; 733 else if ( nKeyCode == KEY_END ) 734 nSelect = m_vEntries.size() - 1; 735 else if ( nKeyCode == KEY_PAGEUP ) 736 nSelect = m_nActive - nPageSize + 1; 737 else if ( nKeyCode == KEY_PAGEDOWN ) 738 nSelect = m_nActive + nPageSize - 1; 739 } 740 else // when there is no selected entry, we will select the first or the last. 741 { 742 if ( ( nKeyCode == KEY_DOWN ) || ( nKeyCode == KEY_PAGEDOWN ) || ( nKeyCode == KEY_HOME ) ) 743 nSelect = 0; 744 else if ( ( nKeyCode == KEY_UP ) || ( nKeyCode == KEY_PAGEUP ) || ( nKeyCode == KEY_END ) ) 745 nSelect = m_vEntries.size() - 1; 746 } 747 748 if ( nSelect < 0 ) 749 nSelect = 0; 750 if ( nSelect >= (long) m_vEntries.size() ) 751 nSelect = m_vEntries.size() - 1; 752 753 selectEntry( nSelect ); 754 755 return true; 756 } 757 758 // ----------------------------------------------------------------------- 759 void ExtensionBox_Impl::Paint( const Rectangle &/*rPaintRect*/ ) 760 { 761 if ( !m_bInDelete ) 762 DeleteRemoved(); 763 764 if ( m_bNeedsRecalc ) 765 RecalcAll(); 766 767 Point aStart( 0, -m_nTopIndex ); 768 Size aSize( GetOutputSizePixel() ); 769 770 if ( m_bHasScrollBar ) 771 aSize.Width() -= m_pScrollBar->GetSizePixel().Width(); 772 773 const ::osl::MutexGuard aGuard( m_entriesMutex ); 774 775 typedef std::vector< TEntry_Impl >::iterator ITER; 776 for ( ITER iIndex = m_vEntries.begin(); iIndex < m_vEntries.end(); ++iIndex ) 777 { 778 aSize.Height() = (*iIndex)->m_bActive ? m_nActiveHeight : m_nStdHeight; 779 Rectangle aEntryRect( aStart, aSize ); 780 DrawRow( aEntryRect, *iIndex ); 781 aStart.Y() += aSize.Height(); 782 } 783 } 784 785 // ----------------------------------------------------------------------- 786 long ExtensionBox_Impl::GetTotalHeight() const 787 { 788 long nHeight = m_vEntries.size() * m_nStdHeight; 789 790 if ( m_bHasActive ) 791 { 792 nHeight += m_nActiveHeight - m_nStdHeight; 793 } 794 795 return nHeight; 796 } 797 798 // ----------------------------------------------------------------------- 799 void ExtensionBox_Impl::SetupScrollBar() 800 { 801 const Size aSize = GetOutputSizePixel(); 802 const long nScrBarSize = GetSettings().GetStyleSettings().GetScrollBarSize(); 803 const long nTotalHeight = GetTotalHeight(); 804 const bool bNeedsScrollBar = ( nTotalHeight > aSize.Height() ); 805 806 if ( bNeedsScrollBar ) 807 { 808 if ( m_nTopIndex + aSize.Height() > nTotalHeight ) 809 m_nTopIndex = nTotalHeight - aSize.Height(); 810 811 m_pScrollBar->SetPosSizePixel( Point( aSize.Width() - nScrBarSize, 0 ), 812 Size( nScrBarSize, aSize.Height() ) ); 813 m_pScrollBar->SetRangeMax( nTotalHeight ); 814 m_pScrollBar->SetVisibleSize( aSize.Height() ); 815 m_pScrollBar->SetPageSize( ( aSize.Height() * 4 ) / 5 ); 816 m_pScrollBar->SetLineSize( m_nStdHeight ); 817 m_pScrollBar->SetThumbPos( m_nTopIndex ); 818 819 if ( !m_bHasScrollBar ) 820 m_pScrollBar->Show(); 821 } 822 else if ( m_bHasScrollBar ) 823 { 824 m_pScrollBar->Hide(); 825 m_nTopIndex = 0; 826 } 827 828 m_bHasScrollBar = bNeedsScrollBar; 829 } 830 831 // ----------------------------------------------------------------------- 832 void ExtensionBox_Impl::Resize() 833 { 834 RecalcAll(); 835 } 836 837 //------------------------------------------------------------------------------ 838 long ExtensionBox_Impl::PointToPos( const Point& rPos ) 839 { 840 long nPos = ( rPos.Y() + m_nTopIndex ) / m_nStdHeight; 841 842 if ( m_bHasActive && ( nPos > m_nActive ) ) 843 { 844 if ( rPos.Y() + m_nTopIndex <= m_nActive*m_nStdHeight + m_nActiveHeight ) 845 nPos = m_nActive; 846 else 847 nPos = ( rPos.Y() + m_nTopIndex - (m_nActiveHeight - m_nStdHeight) ) / m_nStdHeight; 848 } 849 850 return nPos; 851 } 852 853 //------------------------------------------------------------------------------ 854 void ExtensionBox_Impl::MouseButtonDown( const MouseEvent& rMEvt ) 855 { 856 long nPos = PointToPos( rMEvt.GetPosPixel() ); 857 858 if ( rMEvt.IsLeft() ) 859 { 860 if ( rMEvt.IsMod1() && m_bHasActive ) 861 selectEntry( m_vEntries.size() ); // Selecting an not existing entry will deselect the current one 862 else 863 selectEntry( nPos ); 864 } 865 } 866 867 //------------------------------------------------------------------------------ 868 long ExtensionBox_Impl::Notify( NotifyEvent& rNEvt ) 869 { 870 if ( !m_bInDelete ) 871 DeleteRemoved(); 872 873 bool bHandled = false; 874 875 if ( rNEvt.GetType() == EVENT_KEYINPUT ) 876 { 877 const KeyEvent* pKEvt = rNEvt.GetKeyEvent(); 878 KeyCode aKeyCode = pKEvt->GetKeyCode(); 879 sal_uInt16 nKeyCode = aKeyCode.GetCode(); 880 881 if ( nKeyCode == KEY_TAB ) 882 bHandled = HandleTabKey( aKeyCode.IsShift() ); 883 else if ( aKeyCode.GetGroup() == KEYGROUP_CURSOR ) 884 bHandled = HandleCursorKey( nKeyCode ); 885 } 886 887 if ( rNEvt.GetType() == EVENT_COMMAND ) 888 { 889 if ( m_bHasScrollBar && 890 ( rNEvt.GetCommandEvent()->GetCommand() == COMMAND_WHEEL ) ) 891 { 892 const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData(); 893 if ( pData->GetMode() == COMMAND_WHEEL_SCROLL ) 894 { 895 long nThumbPos = m_pScrollBar->GetThumbPos(); 896 if ( pData->GetDelta() < 0 ) 897 m_pScrollBar->DoScroll( nThumbPos + m_nStdHeight ); 898 else 899 m_pScrollBar->DoScroll( nThumbPos - m_nStdHeight ); 900 bHandled = true; 901 } 902 } 903 } 904 905 if ( !bHandled ) 906 return Control::Notify( rNEvt ); 907 else 908 return true; 909 } 910 911 //------------------------------------------------------------------------------ 912 bool ExtensionBox_Impl::FindEntryPos( const TEntry_Impl pEntry, const long nStart, 913 const long nEnd, long &nPos ) 914 { 915 nPos = nStart; 916 if ( nStart > nEnd ) 917 return false; 918 919 StringCompare eCompare; 920 921 if ( nStart == nEnd ) 922 { 923 eCompare = pEntry->CompareTo( m_pCollator, m_vEntries[ nStart ] ); 924 if ( eCompare == COMPARE_LESS ) 925 return false; 926 else if ( eCompare == COMPARE_EQUAL ) 927 { 928 //Workaround. See i86963. 929 if (pEntry->m_xPackage != m_vEntries[nStart]->m_xPackage) 930 return false; 931 932 if ( m_bInCheckMode ) 933 m_vEntries[ nStart ]->m_bChecked = true; 934 return true; 935 } 936 else 937 { 938 nPos = nStart + 1; 939 return false; 940 } 941 } 942 943 const long nMid = nStart + ( ( nEnd - nStart ) / 2 ); 944 eCompare = pEntry->CompareTo( m_pCollator, m_vEntries[ nMid ] ); 945 946 if ( eCompare == COMPARE_LESS ) 947 return FindEntryPos( pEntry, nStart, nMid-1, nPos ); 948 else if ( eCompare == COMPARE_GREATER ) 949 return FindEntryPos( pEntry, nMid+1, nEnd, nPos ); 950 else 951 { 952 //Workaround.See i86963. 953 if (pEntry->m_xPackage != m_vEntries[nMid]->m_xPackage) 954 return false; 955 956 if ( m_bInCheckMode ) 957 m_vEntries[ nMid ]->m_bChecked = true; 958 nPos = nMid; 959 return true; 960 } 961 } 962 963 //------------------------------------------------------------------------------ 964 long ExtensionBox_Impl::addEntry( const uno::Reference< deployment::XPackage > &xPackage, 965 bool bLicenseMissing ) 966 { 967 long nPos = 0; 968 PackageState eState = m_pManager->getPackageState( xPackage ); 969 bool bLocked = m_pManager->isReadOnly( xPackage ); 970 971 TEntry_Impl pEntry( new Entry_Impl( xPackage, eState, bLocked ) ); 972 973 // Don't add empty entries 974 if ( ! pEntry->m_sTitle.Len() ) 975 return 0; 976 977 xPackage->addEventListener( uno::Reference< lang::XEventListener > ( m_xRemoveListener, uno::UNO_QUERY ) ); 978 979 ::osl::ClearableMutexGuard guard(m_entriesMutex); 980 if ( m_vEntries.empty() ) 981 { 982 m_vEntries.push_back( pEntry ); 983 } 984 else 985 { 986 if ( !FindEntryPos( pEntry, 0, m_vEntries.size()-1, nPos ) ) 987 { 988 m_vEntries.insert( m_vEntries.begin()+nPos, pEntry ); 989 } 990 else if ( !m_bInCheckMode ) 991 { 992 OSL_ENSURE( 0, "ExtensionBox_Impl::addEntry(): Will not add duplicate entries" ); 993 } 994 } 995 996 pEntry->m_bHasOptions = m_pManager->supportsOptions( xPackage ); 997 pEntry->m_bUser = xPackage->getRepositoryName().equals( USER_PACKAGE_MANAGER ); 998 pEntry->m_bShared = xPackage->getRepositoryName().equals( SHARED_PACKAGE_MANAGER ); 999 pEntry->m_bNew = m_bInCheckMode; 1000 pEntry->m_bMissingLic = bLicenseMissing; 1001 1002 if ( bLicenseMissing ) 1003 pEntry->m_sErrorText = DialogHelper::getResourceString( RID_STR_ERROR_MISSING_LICENSE ); 1004 1005 //access to m_nActive must be guarded 1006 if ( !m_bInCheckMode && m_bHasActive && ( m_nActive >= nPos ) ) 1007 m_nActive += 1; 1008 1009 guard.clear(); 1010 1011 if ( IsReallyVisible() ) 1012 Invalidate(); 1013 1014 m_bNeedsRecalc = true; 1015 1016 return nPos; 1017 } 1018 1019 //------------------------------------------------------------------------------ 1020 void ExtensionBox_Impl::updateEntry( const uno::Reference< deployment::XPackage > &xPackage ) 1021 { 1022 typedef std::vector< TEntry_Impl >::iterator ITER; 1023 for ( ITER iIndex = m_vEntries.begin(); iIndex < m_vEntries.end(); ++iIndex ) 1024 { 1025 if ( (*iIndex)->m_xPackage == xPackage ) 1026 { 1027 PackageState eState = m_pManager->getPackageState( xPackage ); 1028 (*iIndex)->m_bHasOptions = m_pManager->supportsOptions( xPackage ); 1029 (*iIndex)->m_eState = eState; 1030 (*iIndex)->m_sTitle = xPackage->getDisplayName(); 1031 (*iIndex)->m_sVersion = xPackage->getVersion(); 1032 (*iIndex)->m_sDescription = xPackage->getDescription(); 1033 1034 if ( eState == REGISTERED ) 1035 (*iIndex)->m_bMissingLic = false; 1036 1037 if ( eState == AMBIGUOUS ) 1038 (*iIndex)->m_sErrorText = DialogHelper::getResourceString( RID_STR_ERROR_UNKNOWN_STATUS ); 1039 else if ( ! (*iIndex)->m_bMissingLic ) 1040 (*iIndex)->m_sErrorText = String(); 1041 1042 if ( IsReallyVisible() ) 1043 Invalidate(); 1044 break; 1045 } 1046 } 1047 } 1048 1049 //------------------------------------------------------------------------------ 1050 void ExtensionBox_Impl::removeEntry( const uno::Reference< deployment::XPackage > &xPackage ) 1051 { 1052 if ( ! m_bInDelete ) 1053 { 1054 ::osl::ClearableMutexGuard aGuard( m_entriesMutex ); 1055 1056 typedef std::vector< TEntry_Impl >::iterator ITER; 1057 1058 for ( ITER iIndex = m_vEntries.begin(); iIndex < m_vEntries.end(); ++iIndex ) 1059 { 1060 if ( (*iIndex)->m_xPackage == xPackage ) 1061 { 1062 long nPos = iIndex - m_vEntries.begin(); 1063 1064 // Entries mustn't removed here, because they contain a hyperlink control 1065 // which can only be deleted when the thread has the solar mutex. Therefor 1066 // the entry will be moved into the m_vRemovedEntries list which will be 1067 // cleared on the next paint event 1068 m_vRemovedEntries.push_back( *iIndex ); 1069 m_vEntries.erase( iIndex ); 1070 1071 m_bNeedsRecalc = true; 1072 1073 if ( IsReallyVisible() ) 1074 Invalidate(); 1075 1076 if ( m_bHasActive ) 1077 { 1078 if ( nPos < m_nActive ) 1079 m_nActive -= 1; 1080 else if ( ( nPos == m_nActive ) && 1081 ( nPos == (long) m_vEntries.size() ) ) 1082 m_nActive -= 1; 1083 1084 m_bHasActive = false; 1085 //clear before calling out of this method 1086 aGuard.clear(); 1087 selectEntry( m_nActive ); 1088 } 1089 break; 1090 } 1091 } 1092 } 1093 } 1094 1095 //------------------------------------------------------------------------------ 1096 void ExtensionBox_Impl::RemoveUnlocked() 1097 { 1098 bool bAllRemoved = false; 1099 1100 while ( ! bAllRemoved ) 1101 { 1102 bAllRemoved = true; 1103 1104 ::osl::ClearableMutexGuard aGuard( m_entriesMutex ); 1105 1106 typedef std::vector< TEntry_Impl >::iterator ITER; 1107 1108 for ( ITER iIndex = m_vEntries.begin(); iIndex < m_vEntries.end(); ++iIndex ) 1109 { 1110 if ( !(*iIndex)->m_bLocked ) 1111 { 1112 bAllRemoved = false; 1113 uno::Reference< deployment::XPackage> xPackage = (*iIndex)->m_xPackage; 1114 aGuard.clear(); 1115 removeEntry( xPackage ); 1116 break; 1117 } 1118 } 1119 } 1120 } 1121 1122 //------------------------------------------------------------------------------ 1123 void ExtensionBox_Impl::prepareChecking() 1124 { 1125 m_bInCheckMode = true; 1126 typedef std::vector< TEntry_Impl >::iterator ITER; 1127 for ( ITER iIndex = m_vEntries.begin(); iIndex < m_vEntries.end(); ++iIndex ) 1128 { 1129 (*iIndex)->m_bChecked = false; 1130 (*iIndex)->m_bNew = false; 1131 } 1132 } 1133 1134 //------------------------------------------------------------------------------ 1135 void ExtensionBox_Impl::checkEntries() 1136 { 1137 long nNewPos = -1; 1138 long nPos = 0; 1139 bool bNeedsUpdate = false; 1140 1141 ::osl::ClearableMutexGuard guard(m_entriesMutex); 1142 typedef std::vector< TEntry_Impl >::iterator ITER; 1143 ITER iIndex = m_vEntries.begin(); 1144 while ( iIndex < m_vEntries.end() ) 1145 { 1146 if ( (*iIndex)->m_bChecked == false ) 1147 { 1148 (*iIndex)->m_bChecked = true; 1149 bNeedsUpdate = true; 1150 nPos = iIndex-m_vEntries.begin(); 1151 if ( (*iIndex)->m_bNew ) 1152 { // add entry to list and correct active pos 1153 if ( nNewPos == - 1) 1154 nNewPos = nPos; 1155 if ( nPos <= m_nActive ) 1156 m_nActive += 1; 1157 iIndex++; 1158 } 1159 else 1160 { // remove entry from list 1161 if ( nPos < m_nActive ) 1162 m_nActive -= 1; 1163 else if ( ( nPos == m_nActive ) && ( nPos == (long) m_vEntries.size() - 1 ) ) 1164 m_nActive -= 1; 1165 m_vRemovedEntries.push_back( *iIndex ); 1166 m_vEntries.erase( iIndex ); 1167 iIndex = m_vEntries.begin() + nPos; 1168 } 1169 } 1170 else 1171 iIndex++; 1172 } 1173 guard.clear(); 1174 1175 m_bInCheckMode = false; 1176 1177 if ( nNewPos != - 1) 1178 selectEntry( nNewPos ); 1179 1180 if ( bNeedsUpdate ) 1181 { 1182 m_bNeedsRecalc = true; 1183 if ( IsReallyVisible() ) 1184 Invalidate(); 1185 } 1186 } 1187 //------------------------------------------------------------------------------ 1188 bool ExtensionBox_Impl::isHCMode() 1189 { 1190 return (bool)GetSettings().GetStyleSettings().GetHighContrastMode(); 1191 } 1192 1193 //------------------------------------------------------------------------------ 1194 void ExtensionBox_Impl::SetScrollHdl( const Link& rLink ) 1195 { 1196 if ( m_pScrollBar ) 1197 m_pScrollBar->SetScrollHdl( rLink ); 1198 } 1199 1200 // ----------------------------------------------------------------------- 1201 void ExtensionBox_Impl::DoScroll( long nDelta ) 1202 { 1203 m_nTopIndex += nDelta; 1204 Point aNewSBPt( m_pScrollBar->GetPosPixel() ); 1205 1206 Rectangle aScrRect( Point(), GetOutputSizePixel() ); 1207 aScrRect.Right() -= m_pScrollBar->GetSizePixel().Width(); 1208 Scroll( 0, -nDelta, aScrRect ); 1209 1210 m_pScrollBar->SetPosPixel( aNewSBPt ); 1211 } 1212 1213 // ----------------------------------------------------------------------- 1214 IMPL_LINK( ExtensionBox_Impl, ScrollHdl, ScrollBar*, pScrBar ) 1215 { 1216 DoScroll( pScrBar->GetDelta() ); 1217 1218 return 1; 1219 } 1220 1221 } //namespace dp_gui 1222