1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <tools/diagnose_ex.h> 21 #include <sal/log.hxx> 22 23 #include <comphelper/lok.hxx> 24 #include <vcl/dialoghelper.hxx> 25 #include <vcl/svapp.hxx> 26 #include <vcl/mnemonic.hxx> 27 #include <vcl/image.hxx> 28 #include <vcl/event.hxx> 29 #include <vcl/help.hxx> 30 #include <vcl/toolkit/floatwin.hxx> 31 #include <vcl/decoview.hxx> 32 #include <vcl/menu.hxx> 33 #include <vcl/taskpanelist.hxx> 34 #include <vcl/settings.hxx> 35 #include <vcl/commandinfoprovider.hxx> 36 37 #include <salinst.hxx> 38 #include <svdata.hxx> 39 #include <strings.hrc> 40 #include <window.h> 41 #include <salmenu.hxx> 42 #include <salframe.hxx> 43 44 #include "menubarwindow.hxx" 45 #include "menufloatingwindow.hxx" 46 #include "menuitemlist.hxx" 47 48 #include <com/sun/star/uno/Reference.h> 49 #include <com/sun/star/lang/XComponent.hpp> 50 #include <com/sun/star/accessibility/XAccessible.hpp> 51 #include <vcl/toolkit/unowrap.hxx> 52 #include <rtl/ustrbuf.hxx> 53 54 #include <configsettings.hxx> 55 56 #include <map> 57 #include <string_view> 58 #include <vector> 59 60 namespace vcl 61 { 62 63 struct MenuLayoutData : public ControlLayoutData 64 { 65 std::vector< sal_uInt16 > m_aLineItemIds; 66 std::map< sal_uInt16, tools::Rectangle > m_aVisibleItemBoundRects; 67 }; 68 69 } 70 71 using namespace vcl; 72 73 #define EXTRAITEMHEIGHT 4 74 #define SPACE_AROUND_TITLE 4 75 76 static bool ImplAccelDisabled() 77 { 78 // display of accelerator strings may be suppressed via configuration 79 static int nAccelDisabled = -1; 80 81 if( nAccelDisabled == -1 ) 82 { 83 OUString aStr = 84 vcl::SettingsConfigItem::get()-> 85 getValue( "Menu", "SuppressAccelerators" ); 86 nAccelDisabled = aStr.equalsIgnoreAsciiCase("true") ? 1 : 0; 87 } 88 return nAccelDisabled == 1; 89 } 90 91 static void ImplSetMenuItemData( MenuItemData* pData ) 92 { 93 // convert data 94 if ( !pData->aImage ) 95 pData->eType = MenuItemType::STRING; 96 else if ( pData->aText.isEmpty() ) 97 pData->eType = MenuItemType::IMAGE; 98 else 99 pData->eType = MenuItemType::STRINGIMAGE; 100 } 101 102 namespace { 103 104 void ImplClosePopupToolBox( const VclPtr<vcl::Window>& pWin ) 105 { 106 if ( pWin->GetType() == WindowType::TOOLBOX && ImplGetDockingManager()->IsInPopupMode( pWin ) ) 107 { 108 SystemWindow* pFloatingWindow = ImplGetDockingManager()->GetFloatingWindow(pWin); 109 if (pFloatingWindow) 110 static_cast<FloatingWindow*>(pFloatingWindow)->EndPopupMode( FloatWinPopupEndFlags::CloseAll ); 111 } 112 } 113 114 // TODO: Move to common code with the same function in toolbox 115 // Draw the ">>" - more indicator at the coordinates 116 void lclDrawMoreIndicator(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) 117 { 118 rRenderContext.Push(PushFlags::FILLCOLOR | PushFlags::LINECOLOR); 119 rRenderContext.SetLineColor(); 120 121 if (rRenderContext.GetSettings().GetStyleSettings().GetFaceColor().IsDark()) 122 rRenderContext.SetFillColor(COL_WHITE); 123 else 124 rRenderContext.SetFillColor(COL_BLACK); 125 float fScaleFactor = rRenderContext.GetDPIScaleFactor(); 126 127 int linewidth = 1 * fScaleFactor; 128 int space = 4 * fScaleFactor; 129 130 tools::Long width = 8 * fScaleFactor; 131 tools::Long height = 5 * fScaleFactor; 132 133 //Keep odd b/c drawing code works better 134 if ( height % 2 == 0 ) 135 height--; 136 137 tools::Long heightOrig = height; 138 139 tools::Long x = rRect.Left() + (rRect.getWidth() - width)/2 + 1; 140 tools::Long y = rRect.Top() + (rRect.getHeight() - height)/2 + 1; 141 while( height >= 1) 142 { 143 rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) ); 144 x += space; 145 rRenderContext.DrawRect( tools::Rectangle( x, y, x + linewidth, y ) ); 146 x -= space; 147 y++; 148 if( height <= heightOrig / 2 + 1) x--; 149 else x++; 150 height--; 151 } 152 rRenderContext.Pop(); 153 } 154 155 } // end anonymous namespace 156 157 158 Menu::Menu() 159 : mpFirstDel(nullptr), 160 pItemList(new MenuItemList), 161 pStartedFrom(nullptr), 162 pWindow(nullptr), 163 nTitleHeight(0), 164 nEventId(nullptr), 165 mnHighlightedItemPos(ITEMPOS_INVALID), 166 nMenuFlags(MenuFlags::NONE), 167 nSelectedId(0), 168 nImgOrChkPos(0), 169 nTextPos(0), 170 bCanceled(false), 171 bInCallback(false), 172 bKilled(false) 173 { 174 } 175 176 Menu::~Menu() 177 { 178 disposeOnce(); 179 } 180 181 void Menu::dispose() 182 { 183 ImplCallEventListeners( VclEventId::ObjectDying, ITEMPOS_INVALID ); 184 185 // at the window free the reference to the accessible component 186 // and make sure the MenuFloatingWindow knows about our destruction 187 if ( pWindow ) 188 { 189 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get()); 190 if( pFloat->pMenu.get() == this ) 191 pFloat->pMenu.clear(); 192 pWindow->SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() ); 193 } 194 195 // dispose accessible components 196 if ( mxAccessible.is() ) 197 { 198 css::uno::Reference< css::lang::XComponent> xComponent( mxAccessible, css::uno::UNO_QUERY ); 199 if ( xComponent.is() ) 200 xComponent->dispose(); 201 } 202 203 if ( nEventId ) 204 Application::RemoveUserEvent( nEventId ); 205 206 // Notify deletion of this menu 207 ImplMenuDelData* pDelData = mpFirstDel; 208 while ( pDelData ) 209 { 210 pDelData->mpMenu = nullptr; 211 pDelData = pDelData->mpNext; 212 } 213 214 bKilled = true; 215 216 // tdf#140225 when clearing pItemList, keep SalMenu in sync with 217 // their removal during menu teardown 218 for (size_t n = pItemList->size(); n;) 219 { 220 --n; 221 if (mpSalMenu) 222 mpSalMenu->RemoveItem(n); 223 pItemList->Remove(n); 224 } 225 226 assert(!pItemList->size()); 227 228 mpLayoutData.reset(); 229 230 // Native-support: destroy SalMenu 231 mpSalMenu.reset(); 232 233 pStartedFrom.clear(); 234 pWindow.clear(); 235 VclReferenceBase::dispose(); 236 } 237 238 void Menu::CreateAutoMnemonics() 239 { 240 MnemonicGenerator aMnemonicGenerator; 241 size_t n; 242 for ( n = 0; n < pItemList->size(); n++ ) 243 { 244 MenuItemData* pData = pItemList->GetDataFromPos( n ); 245 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) ) 246 aMnemonicGenerator.RegisterMnemonic( pData->aText ); 247 } 248 for ( n = 0; n < pItemList->size(); n++ ) 249 { 250 MenuItemData* pData = pItemList->GetDataFromPos( n ); 251 if ( ! (pData->nBits & MenuItemBits::NOSELECT ) ) 252 pData->aText = aMnemonicGenerator.CreateMnemonic( pData->aText ); 253 } 254 } 255 256 void Menu::Activate() 257 { 258 bInCallback = true; 259 260 ImplMenuDelData aDelData( this ); 261 262 ImplCallEventListeners( VclEventId::MenuActivate, ITEMPOS_INVALID ); 263 264 if( !aDelData.isDeleted() ) 265 { 266 if ( !aActivateHdl.Call( this ) ) 267 { 268 if( !aDelData.isDeleted() ) 269 { 270 Menu* pStartMenu = ImplGetStartMenu(); 271 if ( pStartMenu && ( pStartMenu != this ) ) 272 { 273 pStartMenu->bInCallback = true; 274 // MT 11/01: Call EventListener here? I don't know... 275 pStartMenu->aActivateHdl.Call( this ); 276 pStartMenu->bInCallback = false; 277 } 278 } 279 } 280 bInCallback = false; 281 } 282 283 if (!aDelData.isDeleted() && !(nMenuFlags & MenuFlags::NoAutoMnemonics)) 284 CreateAutoMnemonics(); 285 } 286 287 void Menu::Deactivate() 288 { 289 for ( size_t n = pItemList->size(); n; ) 290 { 291 MenuItemData* pData = pItemList->GetDataFromPos( --n ); 292 if ( pData->bIsTemporary ) 293 { 294 if ( ImplGetSalMenu() ) 295 ImplGetSalMenu()->RemoveItem( n ); 296 297 pItemList->Remove( n ); 298 } 299 } 300 301 bInCallback = true; 302 303 ImplMenuDelData aDelData( this ); 304 305 Menu* pStartMenu = ImplGetStartMenu(); 306 ImplCallEventListeners( VclEventId::MenuDeactivate, ITEMPOS_INVALID ); 307 308 if( !aDelData.isDeleted() ) 309 { 310 if ( !aDeactivateHdl.Call( this ) ) 311 { 312 if( !aDelData.isDeleted() ) 313 { 314 if ( pStartMenu && ( pStartMenu != this ) ) 315 { 316 pStartMenu->bInCallback = true; 317 pStartMenu->aDeactivateHdl.Call( this ); 318 pStartMenu->bInCallback = false; 319 } 320 } 321 } 322 } 323 324 if( !aDelData.isDeleted() ) 325 { 326 bInCallback = false; 327 } 328 } 329 330 void Menu::ImplSelect() 331 { 332 MenuItemData* pData = GetItemList()->GetData( nSelectedId ); 333 if ( pData && (pData->nBits & MenuItemBits::AUTOCHECK) ) 334 { 335 bool bChecked = IsItemChecked( nSelectedId ); 336 if ( pData->nBits & MenuItemBits::RADIOCHECK ) 337 { 338 if ( !bChecked ) 339 CheckItem( nSelectedId ); 340 } 341 else 342 CheckItem( nSelectedId, !bChecked ); 343 } 344 345 // call select 346 ImplSVData* pSVData = ImplGetSVData(); 347 pSVData->maAppData.mpActivePopupMenu = nullptr; // if new execute in select() 348 nEventId = Application::PostUserEvent( LINK( this, Menu, ImplCallSelect ) ); 349 } 350 351 void Menu::Select() 352 { 353 ImplMenuDelData aDelData( this ); 354 355 ImplCallEventListeners( VclEventId::MenuSelect, GetItemPos( GetCurItemId() ) ); 356 if (aDelData.isDeleted()) 357 return; 358 if (aSelectHdl.Call(this)) 359 return; 360 if (aDelData.isDeleted()) 361 return; 362 Menu* pStartMenu = ImplGetStartMenu(); 363 if (!pStartMenu || (pStartMenu == this)) 364 return; 365 pStartMenu->nSelectedId = nSelectedId; 366 pStartMenu->sSelectedIdent = sSelectedIdent; 367 pStartMenu->aSelectHdl.Call( this ); 368 } 369 370 #if defined(MACOSX) 371 void Menu::ImplSelectWithStart( Menu* pSMenu ) 372 { 373 auto pOldStartedFrom = pStartedFrom; 374 pStartedFrom = pSMenu; 375 auto pOldStartedStarted = pOldStartedFrom ? pOldStartedFrom->pStartedFrom : VclPtr<Menu>(); 376 Select(); 377 if( pOldStartedFrom ) 378 pOldStartedFrom->pStartedFrom = pOldStartedStarted; 379 pStartedFrom = pOldStartedFrom; 380 } 381 #endif 382 383 void Menu::ImplCallEventListeners( VclEventId nEvent, sal_uInt16 nPos ) 384 { 385 ImplMenuDelData aDelData( this ); 386 387 VclMenuEvent aEvent( this, nEvent, nPos ); 388 389 // This is needed by atk accessibility bridge 390 if ( nEvent == VclEventId::MenuHighlight ) 391 { 392 Application::ImplCallEventListeners( aEvent ); 393 } 394 395 if ( !aDelData.isDeleted() ) 396 { 397 // Copy the list, because this can be destroyed when calling a Link... 398 std::list<Link<VclMenuEvent&,void>> aCopy( maEventListeners ); 399 for ( const auto& rLink : aCopy ) 400 { 401 if( std::find(maEventListeners.begin(), maEventListeners.end(), rLink) != maEventListeners.end() ) 402 rLink.Call( aEvent ); 403 } 404 } 405 } 406 407 void Menu::AddEventListener( const Link<VclMenuEvent&,void>& rEventListener ) 408 { 409 maEventListeners.push_back( rEventListener ); 410 } 411 412 void Menu::RemoveEventListener( const Link<VclMenuEvent&,void>& rEventListener ) 413 { 414 maEventListeners.remove( rEventListener ); 415 } 416 417 MenuItemData* Menu::NbcInsertItem(sal_uInt16 nId, MenuItemBits nBits, 418 const OUString& rStr, Menu* pMenu, 419 size_t nPos, const OString &rIdent) 420 { 421 // put Item in MenuItemList 422 MenuItemData* pData = pItemList->Insert(nId, MenuItemType::STRING, 423 nBits, rStr, pMenu, nPos, rIdent); 424 425 // update native menu 426 if (ImplGetSalMenu() && pData->pSalMenuItem) 427 ImplGetSalMenu()->InsertItem(pData->pSalMenuItem.get(), nPos); 428 429 return pData; 430 } 431 432 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, MenuItemBits nItemBits, 433 const OString &rIdent, sal_uInt16 nPos) 434 { 435 SAL_WARN_IF( !nItemId, "vcl", "Menu::InsertItem(): ItemId == 0" ); 436 SAL_WARN_IF( GetItemPos( nItemId ) != MENU_ITEM_NOTFOUND, "vcl", 437 "Menu::InsertItem(): ItemId already exists" ); 438 439 // if Position > ItemCount, append 440 if ( nPos >= pItemList->size() ) 441 nPos = MENU_APPEND; 442 443 // put Item in MenuItemList 444 NbcInsertItem(nItemId, nItemBits, rStr, this, nPos, rIdent); 445 446 vcl::Window* pWin = ImplGetWindow(); 447 mpLayoutData.reset(); 448 if ( pWin ) 449 { 450 ImplCalcSize( pWin ); 451 if ( pWin->IsVisible() ) 452 pWin->Invalidate(); 453 } 454 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos ); 455 } 456 457 void Menu::InsertItem(sal_uInt16 nItemId, const Image& rImage, 458 MenuItemBits nItemBits, const OString &rIdent, sal_uInt16 nPos) 459 { 460 InsertItem(nItemId, OUString(), nItemBits, rIdent, nPos); 461 SetItemImage( nItemId, rImage ); 462 } 463 464 void Menu::InsertItem(sal_uInt16 nItemId, const OUString& rStr, 465 const Image& rImage, MenuItemBits nItemBits, 466 const OString &rIdent, sal_uInt16 nPos) 467 { 468 InsertItem(nItemId, rStr, nItemBits, rIdent, nPos); 469 SetItemImage( nItemId, rImage ); 470 } 471 472 void Menu::InsertSeparator(const OString &rIdent, sal_uInt16 nPos) 473 { 474 // do nothing if it's a menu bar 475 if (IsMenuBar()) 476 return; 477 478 // if position > ItemCount, append 479 if ( nPos >= pItemList->size() ) 480 nPos = MENU_APPEND; 481 482 // put separator in item list 483 pItemList->InsertSeparator(rIdent, nPos); 484 485 // update native menu 486 size_t itemPos = ( nPos != MENU_APPEND ) ? nPos : pItemList->size() - 1; 487 MenuItemData *pData = pItemList->GetDataFromPos( itemPos ); 488 if( ImplGetSalMenu() && pData && pData->pSalMenuItem ) 489 ImplGetSalMenu()->InsertItem( pData->pSalMenuItem.get(), nPos ); 490 491 mpLayoutData.reset(); 492 493 ImplCallEventListeners( VclEventId::MenuInsertItem, nPos ); 494 } 495 496 void Menu::RemoveItem( sal_uInt16 nPos ) 497 { 498 bool bRemove = false; 499 500 if ( nPos < GetItemCount() ) 501 { 502 // update native menu 503 if( ImplGetSalMenu() ) 504 ImplGetSalMenu()->RemoveItem( nPos ); 505 506 pItemList->Remove( nPos ); 507 bRemove = true; 508 } 509 510 vcl::Window* pWin = ImplGetWindow(); 511 if ( pWin ) 512 { 513 ImplCalcSize( pWin ); 514 if ( pWin->IsVisible() ) 515 pWin->Invalidate(); 516 } 517 mpLayoutData.reset(); 518 519 if ( bRemove ) 520 ImplCallEventListeners( VclEventId::MenuRemoveItem, nPos ); 521 } 522 523 static void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos ) 524 { 525 MenuItemType eType = rMenu.GetItemType( nPos ); 526 527 if ( eType == MenuItemType::DONTKNOW ) 528 return; 529 530 if ( eType == MenuItemType::SEPARATOR ) 531 pThis->InsertSeparator( OString(), nNewPos ); 532 else 533 { 534 sal_uInt16 nId = rMenu.GetItemId( nPos ); 535 536 SAL_WARN_IF( pThis->GetItemPos( nId ) != MENU_ITEM_NOTFOUND, "vcl", 537 "Menu::CopyItem(): ItemId already exists" ); 538 539 MenuItemData* pData = rMenu.GetItemList()->GetData( nId ); 540 541 if (!pData) 542 return; 543 544 if ( eType == MenuItemType::STRINGIMAGE ) 545 pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, pData->sIdent, nNewPos ); 546 else if ( eType == MenuItemType::STRING ) 547 pThis->InsertItem( nId, pData->aText, pData->nBits, pData->sIdent, nNewPos ); 548 else 549 pThis->InsertItem( nId, pData->aImage, pData->nBits, pData->sIdent, nNewPos ); 550 551 if ( rMenu.IsItemChecked( nId ) ) 552 pThis->CheckItem( nId ); 553 if ( !rMenu.IsItemEnabled( nId ) ) 554 pThis->EnableItem( nId, false ); 555 pThis->SetHelpId( nId, pData->aHelpId ); 556 pThis->SetHelpText( nId, pData->aHelpText ); 557 pThis->SetAccelKey( nId, pData->aAccelKey ); 558 pThis->SetItemCommand( nId, pData->aCommandStr ); 559 pThis->SetHelpCommand( nId, pData->aHelpCommandStr ); 560 561 PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId ); 562 if ( pSubMenu ) 563 { 564 // create auto-copy 565 VclPtr<PopupMenu> pNewMenu = VclPtr<PopupMenu>::Create( *pSubMenu ); 566 pThis->SetPopupMenu( nId, pNewMenu ); 567 } 568 } 569 } 570 571 void Menu::Clear() 572 { 573 for ( sal_uInt16 i = GetItemCount(); i; i-- ) 574 RemoveItem( 0 ); 575 } 576 577 sal_uInt16 Menu::GetItemCount() const 578 { 579 return static_cast<sal_uInt16>(pItemList->size()); 580 } 581 582 sal_uInt16 Menu::ImplGetVisibleItemCount() const 583 { 584 sal_uInt16 nItems = 0; 585 for ( size_t n = pItemList->size(); n; ) 586 { 587 if ( ImplIsVisible( --n ) ) 588 nItems++; 589 } 590 return nItems; 591 } 592 593 sal_uInt16 Menu::ImplGetFirstVisible() const 594 { 595 for ( size_t n = 0; n < pItemList->size(); n++ ) 596 { 597 if ( ImplIsVisible( n ) ) 598 return n; 599 } 600 return ITEMPOS_INVALID; 601 } 602 603 sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const 604 { 605 for ( size_t n = nPos; n; ) 606 { 607 if (ImplIsVisible(--n)) 608 return n; 609 } 610 return ITEMPOS_INVALID; 611 } 612 613 sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const 614 { 615 for ( size_t n = nPos+1; n < pItemList->size(); n++ ) 616 { 617 if ( ImplIsVisible( n ) ) 618 return n; 619 } 620 return ITEMPOS_INVALID; 621 } 622 623 sal_uInt16 Menu::GetItemId(sal_uInt16 nPos) const 624 { 625 MenuItemData* pData = pItemList->GetDataFromPos( nPos ); 626 627 if ( pData ) 628 return pData->nId; 629 else 630 return 0; 631 } 632 633 sal_uInt16 Menu::GetItemId(std::string_view rIdent) const 634 { 635 for (size_t n = 0; n < pItemList->size(); ++n) 636 { 637 MenuItemData* pData = pItemList->GetDataFromPos(n); 638 if (pData && pData->sIdent == rIdent) 639 return pData->nId; 640 } 641 return MENU_ITEM_NOTFOUND; 642 } 643 644 sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const 645 { 646 size_t nPos; 647 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 648 649 if ( pData ) 650 return static_cast<sal_uInt16>(nPos); 651 else 652 return MENU_ITEM_NOTFOUND; 653 } 654 655 MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const 656 { 657 MenuItemData* pData = pItemList->GetDataFromPos( nPos ); 658 659 if ( pData ) 660 return pData->eType; 661 else 662 return MenuItemType::DONTKNOW; 663 } 664 665 OString Menu::GetItemIdent(sal_uInt16 nId) const 666 { 667 const MenuItemData* pData = pItemList->GetData(nId); 668 return pData ? pData->sIdent : OString(); 669 } 670 671 void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits ) 672 { 673 size_t nPos; 674 MenuItemData* pData = pItemList->GetData(nItemId, nPos); 675 676 if (pData && (pData->nBits != nBits)) 677 { 678 pData->nBits = nBits; 679 680 // update native menu 681 if (ImplGetSalMenu()) 682 ImplGetSalMenu()->SetItemBits(nPos, nBits); 683 } 684 } 685 686 MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const 687 { 688 MenuItemBits nBits = MenuItemBits::NONE; 689 MenuItemData* pData = pItemList->GetData( nItemId ); 690 if ( pData ) 691 nBits = pData->nBits; 692 return nBits; 693 } 694 695 void Menu::SetUserValue(sal_uInt16 nItemId, void* nUserValue, MenuUserDataReleaseFunction aFunc) 696 { 697 MenuItemData* pData = pItemList->GetData(nItemId); 698 if (pData) 699 { 700 if (pData->aUserValueReleaseFunc) 701 pData->aUserValueReleaseFunc(pData->nUserValue); 702 pData->aUserValueReleaseFunc = aFunc; 703 pData->nUserValue = nUserValue; 704 } 705 } 706 707 void* Menu::GetUserValue( sal_uInt16 nItemId ) const 708 { 709 MenuItemData* pData = pItemList->GetData( nItemId ); 710 return pData ? pData->nUserValue : nullptr; 711 } 712 713 void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu ) 714 { 715 size_t nPos; 716 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 717 718 // Item does not exist -> return NULL 719 if ( !pData ) 720 return; 721 722 // same menu, nothing to do 723 if ( static_cast<PopupMenu*>(pData->pSubMenu.get()) == pMenu ) 724 return; 725 726 // remove old menu 727 auto oldSubMenu = pData->pSubMenu; 728 729 // data exchange 730 pData->pSubMenu = pMenu; 731 732 // #112023# Make sure pStartedFrom does not point to invalid (old) data 733 if ( pData->pSubMenu ) 734 pData->pSubMenu->pStartedFrom = nullptr; 735 736 // set native submenu 737 if( ImplGetSalMenu() && pData->pSalMenuItem ) 738 { 739 if( pMenu ) 740 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), pMenu->ImplGetSalMenu(), nPos ); 741 else 742 ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem.get(), nullptr, nPos ); 743 } 744 745 oldSubMenu.disposeAndClear(); 746 747 ImplCallEventListeners( VclEventId::MenuSubmenuChanged, nPos ); 748 } 749 750 PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const 751 { 752 MenuItemData* pData = pItemList->GetData( nItemId ); 753 754 if ( pData ) 755 return static_cast<PopupMenu*>(pData->pSubMenu.get()); 756 else 757 return nullptr; 758 } 759 760 void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode ) 761 { 762 size_t nPos; 763 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 764 765 if ( !pData ) 766 return; 767 768 if ( pData->aAccelKey == rKeyCode ) 769 return; 770 771 pData->aAccelKey = rKeyCode; 772 773 // update native menu 774 if( ImplGetSalMenu() && pData->pSalMenuItem ) 775 ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem.get(), rKeyCode, rKeyCode.GetName() ); 776 } 777 778 KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const 779 { 780 MenuItemData* pData = pItemList->GetData( nItemId ); 781 782 if ( pData ) 783 return pData->aAccelKey; 784 else 785 return KeyCode(); 786 } 787 788 KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const 789 { 790 KeyEvent aRet; 791 MenuItemData* pData = pItemList->GetData( nItemId ); 792 if( pData ) 793 { 794 sal_Int32 nPos = pData->aText.indexOf( '~' ); 795 if( nPos != -1 && nPos < pData->aText.getLength()-1 ) 796 { 797 sal_uInt16 nCode = 0; 798 sal_Unicode cAccel = pData->aText[nPos+1]; 799 if( cAccel >= 'a' && cAccel <= 'z' ) 800 nCode = KEY_A + (cAccel-'a'); 801 else if( cAccel >= 'A' && cAccel <= 'Z' ) 802 nCode = KEY_A + (cAccel-'A'); 803 else if( cAccel >= '0' && cAccel <= '9' ) 804 nCode = KEY_0 + (cAccel-'0'); 805 806 aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) ); 807 } 808 809 } 810 return aRet; 811 } 812 813 void Menu::CheckItem( sal_uInt16 nItemId, bool bCheck ) 814 { 815 size_t nPos; 816 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 817 818 if ( !pData || pData->bChecked == bCheck ) 819 return; 820 821 // if radio-check, then uncheck previous 822 if ( bCheck && (pData->nBits & MenuItemBits::AUTOCHECK) && 823 (pData->nBits & MenuItemBits::RADIOCHECK) ) 824 { 825 MenuItemData* pGroupData; 826 sal_uInt16 nGroupPos; 827 sal_uInt16 nItemCount = GetItemCount(); 828 bool bFound = false; 829 830 nGroupPos = nPos; 831 while ( nGroupPos ) 832 { 833 pGroupData = pItemList->GetDataFromPos( nGroupPos-1 ); 834 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK ) 835 { 836 if ( IsItemChecked( pGroupData->nId ) ) 837 { 838 CheckItem( pGroupData->nId, false ); 839 bFound = true; 840 break; 841 } 842 } 843 else 844 break; 845 nGroupPos--; 846 } 847 848 if ( !bFound ) 849 { 850 nGroupPos = nPos+1; 851 while ( nGroupPos < nItemCount ) 852 { 853 pGroupData = pItemList->GetDataFromPos( nGroupPos ); 854 if ( pGroupData->nBits & MenuItemBits::RADIOCHECK ) 855 { 856 if ( IsItemChecked( pGroupData->nId ) ) 857 { 858 CheckItem( pGroupData->nId, false ); 859 break; 860 } 861 } 862 else 863 break; 864 nGroupPos++; 865 } 866 } 867 } 868 869 pData->bChecked = bCheck; 870 871 // update native menu 872 if( ImplGetSalMenu() ) 873 ImplGetSalMenu()->CheckItem( nPos, bCheck ); 874 875 ImplCallEventListeners( bCheck ? VclEventId::MenuItemChecked : VclEventId::MenuItemUnchecked, nPos ); 876 } 877 878 void Menu::CheckItem( std::string_view rIdent , bool bCheck ) 879 { 880 CheckItem( GetItemId( rIdent ), bCheck ); 881 } 882 883 bool Menu::IsItemChecked( sal_uInt16 nItemId ) const 884 { 885 size_t nPos; 886 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 887 888 if ( !pData ) 889 return false; 890 891 return pData->bChecked; 892 } 893 894 void Menu::EnableItem( sal_uInt16 nItemId, bool bEnable ) 895 { 896 size_t nPos; 897 MenuItemData* pItemData = pItemList->GetData( nItemId, nPos ); 898 899 if ( !(pItemData && ( pItemData->bEnabled != bEnable )) ) 900 return; 901 902 pItemData->bEnabled = bEnable; 903 904 vcl::Window* pWin = ImplGetWindow(); 905 if ( pWin && pWin->IsVisible() ) 906 { 907 SAL_WARN_IF(!IsMenuBar(), "vcl", "Menu::EnableItem - Popup visible!" ); 908 tools::Long nX = 0; 909 size_t nCount = pItemList->size(); 910 for ( size_t n = 0; n < nCount; n++ ) 911 { 912 MenuItemData* pData = pItemList->GetDataFromPos( n ); 913 if ( n == nPos ) 914 { 915 pWin->Invalidate( tools::Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) ); 916 break; 917 } 918 nX += pData->aSz.Width(); 919 } 920 } 921 // update native menu 922 if( ImplGetSalMenu() ) 923 ImplGetSalMenu()->EnableItem( nPos, bEnable ); 924 925 ImplCallEventListeners( bEnable ? VclEventId::MenuEnable : VclEventId::MenuDisable, nPos ); 926 } 927 928 bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const 929 { 930 size_t nPos; 931 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 932 933 if ( !pData ) 934 return false; 935 936 return pData->bEnabled; 937 } 938 939 void Menu::ShowItem( sal_uInt16 nItemId, bool bVisible ) 940 { 941 size_t nPos; 942 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 943 944 SAL_WARN_IF(IsMenuBar() && !bVisible , "vcl", "Menu::ShowItem - ignored for menu bar entries!"); 945 if (IsMenuBar() || !pData || (pData->bVisible == bVisible)) 946 return; 947 948 vcl::Window* pWin = ImplGetWindow(); 949 if ( pWin && pWin->IsVisible() ) 950 { 951 SAL_WARN( "vcl", "Menu::ShowItem - ignored for visible popups!" ); 952 return; 953 } 954 pData->bVisible = bVisible; 955 956 // update native menu 957 if( ImplGetSalMenu() ) 958 ImplGetSalMenu()->ShowItem( nPos, bVisible ); 959 } 960 961 void Menu::SetItemText( sal_uInt16 nItemId, const OUString& rStr ) 962 { 963 size_t nPos; 964 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 965 966 if ( !pData ) 967 return; 968 969 if ( rStr == pData->aText ) 970 return; 971 972 pData->aText = rStr; 973 // Clear layout for aText. 974 pData->aTextGlyphs.Invalidate(); 975 ImplSetMenuItemData( pData ); 976 // update native menu 977 if( ImplGetSalMenu() && pData->pSalMenuItem ) 978 ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem.get(), rStr ); 979 980 vcl::Window* pWin = ImplGetWindow(); 981 mpLayoutData.reset(); 982 if (pWin && IsMenuBar()) 983 { 984 ImplCalcSize( pWin ); 985 if ( pWin->IsVisible() ) 986 pWin->Invalidate(); 987 } 988 989 ImplCallEventListeners( VclEventId::MenuItemTextChanged, nPos ); 990 } 991 992 OUString Menu::GetItemText( sal_uInt16 nItemId ) const 993 { 994 size_t nPos; 995 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 996 997 if ( pData ) 998 return pData->aText; 999 1000 return OUString(); 1001 } 1002 1003 void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage ) 1004 { 1005 size_t nPos; 1006 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 1007 1008 if ( !pData ) 1009 return; 1010 1011 pData->aImage = rImage; 1012 ImplSetMenuItemData( pData ); 1013 1014 // update native menu 1015 if( ImplGetSalMenu() && pData->pSalMenuItem ) 1016 ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem.get(), rImage ); 1017 } 1018 1019 Image Menu::GetItemImage( sal_uInt16 nItemId ) const 1020 { 1021 MenuItemData* pData = pItemList->GetData( nItemId ); 1022 1023 if ( pData ) 1024 return pData->aImage; 1025 else 1026 return Image(); 1027 } 1028 1029 void Menu::SetItemCommand( sal_uInt16 nItemId, const OUString& rCommand ) 1030 { 1031 size_t nPos; 1032 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 1033 1034 if ( pData ) 1035 pData->aCommandStr = rCommand; 1036 } 1037 1038 OUString Menu::GetItemCommand( sal_uInt16 nItemId ) const 1039 { 1040 MenuItemData* pData = pItemList->GetData( nItemId ); 1041 1042 if (pData) 1043 return pData->aCommandStr; 1044 1045 return OUString(); 1046 } 1047 1048 void Menu::SetHelpCommand( sal_uInt16 nItemId, const OUString& rStr ) 1049 { 1050 MenuItemData* pData = pItemList->GetData( nItemId ); 1051 1052 if ( pData ) 1053 pData->aHelpCommandStr = rStr; 1054 } 1055 1056 OUString Menu::GetHelpCommand( sal_uInt16 nItemId ) const 1057 { 1058 MenuItemData* pData = pItemList->GetData( nItemId ); 1059 1060 if ( pData ) 1061 return pData->aHelpCommandStr; 1062 1063 return OUString(); 1064 } 1065 1066 void Menu::SetHelpText( sal_uInt16 nItemId, const OUString& rStr ) 1067 { 1068 MenuItemData* pData = pItemList->GetData( nItemId ); 1069 1070 if ( pData ) 1071 pData->aHelpText = rStr; 1072 } 1073 1074 OUString Menu::ImplGetHelpText( sal_uInt16 nItemId ) const 1075 { 1076 MenuItemData* pData = pItemList->GetData( nItemId ); 1077 1078 if (!pData) 1079 return OUString(); 1080 1081 if ( pData->aHelpText.isEmpty() && 1082 (( !pData->aHelpId.isEmpty() ) || ( !pData->aCommandStr.isEmpty() ))) 1083 { 1084 Help* pHelp = Application::GetHelp(); 1085 if ( pHelp ) 1086 { 1087 if (!pData->aCommandStr.isEmpty()) 1088 pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr, static_cast<weld::Widget*>(nullptr) ); 1089 if (pData->aHelpText.isEmpty() && !pData->aHelpId.isEmpty()) 1090 pData->aHelpText = pHelp->GetHelpText( OStringToOUString( pData->aHelpId, RTL_TEXTENCODING_UTF8 ), static_cast<weld::Widget*>(nullptr) ); 1091 } 1092 } 1093 1094 return pData->aHelpText; 1095 } 1096 1097 OUString Menu::GetHelpText( sal_uInt16 nItemId ) const 1098 { 1099 return ImplGetHelpText( nItemId ); 1100 } 1101 1102 void Menu::SetTipHelpText( sal_uInt16 nItemId, const OUString& rStr ) 1103 { 1104 MenuItemData* pData = pItemList->GetData( nItemId ); 1105 1106 if ( pData ) 1107 pData->aTipHelpText = rStr; 1108 } 1109 1110 OUString Menu::GetTipHelpText( sal_uInt16 nItemId ) const 1111 { 1112 MenuItemData* pData = pItemList->GetData( nItemId ); 1113 1114 if ( pData ) 1115 return pData->aTipHelpText; 1116 1117 return OUString(); 1118 } 1119 1120 void Menu::SetHelpId( sal_uInt16 nItemId, const OString& rHelpId ) 1121 { 1122 MenuItemData* pData = pItemList->GetData( nItemId ); 1123 1124 if ( pData ) 1125 pData->aHelpId = rHelpId; 1126 } 1127 1128 OString Menu::GetHelpId( sal_uInt16 nItemId ) const 1129 { 1130 OString aRet; 1131 1132 MenuItemData* pData = pItemList->GetData( nItemId ); 1133 1134 if ( pData ) 1135 { 1136 if ( !pData->aHelpId.isEmpty() ) 1137 aRet = pData->aHelpId; 1138 else 1139 aRet = OUStringToOString( pData->aCommandStr, RTL_TEXTENCODING_UTF8 ); 1140 } 1141 1142 return aRet; 1143 } 1144 1145 Menu& Menu::operator=( const Menu& rMenu ) 1146 { 1147 if(this == &rMenu) 1148 return *this; 1149 1150 // clean up 1151 Clear(); 1152 1153 // copy items 1154 sal_uInt16 nCount = rMenu.GetItemCount(); 1155 for ( sal_uInt16 i = 0; i < nCount; i++ ) 1156 ImplCopyItem( this, rMenu, i, MENU_APPEND ); 1157 1158 aActivateHdl = rMenu.aActivateHdl; 1159 aDeactivateHdl = rMenu.aDeactivateHdl; 1160 aSelectHdl = rMenu.aSelectHdl; 1161 aTitleText = rMenu.aTitleText; 1162 nTitleHeight = rMenu.nTitleHeight; 1163 1164 return *this; 1165 } 1166 1167 // Returns true if the item is completely hidden on the GUI and shouldn't 1168 // be possible to interact with 1169 bool Menu::ImplCurrentlyHiddenOnGUI(sal_uInt16 nPos) const 1170 { 1171 MenuItemData* pData = pItemList->GetDataFromPos(nPos); 1172 if (pData) 1173 { 1174 MenuItemData* pPreviousData = pItemList->GetDataFromPos( nPos - 1 ); 1175 if (pPreviousData && pPreviousData->bHiddenOnGUI) 1176 { 1177 return true; 1178 } 1179 } 1180 return false; 1181 } 1182 1183 bool Menu::ImplIsVisible( sal_uInt16 nPos ) const 1184 { 1185 bool bVisible = true; 1186 1187 MenuItemData* pData = pItemList->GetDataFromPos( nPos ); 1188 // check general visibility first 1189 if( pData && !pData->bVisible ) 1190 bVisible = false; 1191 1192 if ( bVisible && pData && pData->eType == MenuItemType::SEPARATOR ) 1193 { 1194 if( nPos == 0 ) // no separator should be shown at the very beginning 1195 bVisible = false; 1196 else 1197 { 1198 // always avoid adjacent separators 1199 size_t nCount = pItemList->size(); 1200 size_t n; 1201 MenuItemData* pNextData = nullptr; 1202 // search next visible item 1203 for( n = nPos + 1; n < nCount; n++ ) 1204 { 1205 pNextData = pItemList->GetDataFromPos( n ); 1206 if( pNextData && pNextData->bVisible ) 1207 { 1208 if( pNextData->eType == MenuItemType::SEPARATOR || ImplIsVisible(n) ) 1209 break; 1210 } 1211 } 1212 if( n == nCount ) // no next visible item 1213 bVisible = false; 1214 // check for separator 1215 if( pNextData && pNextData->bVisible && pNextData->eType == MenuItemType::SEPARATOR ) 1216 bVisible = false; 1217 1218 if( bVisible ) 1219 { 1220 for( n = nPos; n > 0; n-- ) 1221 { 1222 pNextData = pItemList->GetDataFromPos( n-1 ); 1223 if( pNextData && pNextData->bVisible ) 1224 { 1225 if( pNextData->eType != MenuItemType::SEPARATOR && ImplIsVisible(n-1) ) 1226 break; 1227 } 1228 } 1229 if( n == 0 ) // no previous visible item 1230 bVisible = false; 1231 } 1232 } 1233 } 1234 1235 // not allowed for menubar, as I do not know 1236 // whether a menu-entry will disappear or will appear 1237 if (bVisible && !IsMenuBar() && (nMenuFlags & MenuFlags::HideDisabledEntries) && 1238 !(nMenuFlags & MenuFlags::AlwaysShowDisabledEntries)) 1239 { 1240 if( !pData ) // e.g. nPos == ITEMPOS_INVALID 1241 bVisible = false; 1242 else if ( pData->eType != MenuItemType::SEPARATOR ) // separators handled above 1243 { 1244 // tdf#86850 Always display clipboard functions 1245 if ( pData->aCommandStr == ".uno:Cut" || pData->aCommandStr == ".uno:Copy" || pData->aCommandStr == ".uno:Paste" || 1246 pData->sIdent == ".uno:Cut" || pData->sIdent == ".uno:Copy" || pData->sIdent == ".uno:Paste" ) 1247 bVisible = true; 1248 else 1249 // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( true ) ); 1250 bVisible = pData->bEnabled; // do not check submenus as they might be filled at Activate(). 1251 } 1252 } 1253 1254 return bVisible; 1255 } 1256 1257 bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const 1258 { 1259 return IsMenuVisible() && ImplIsVisible( nItemPos ); 1260 } 1261 1262 bool Menu::IsMenuVisible() const 1263 { 1264 return pWindow && pWindow->IsReallyVisible(); 1265 } 1266 1267 bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const 1268 { 1269 bool bSelectable = true; 1270 1271 MenuItemData* pData = pItemList->GetDataFromPos( nPos ); 1272 // check general visibility first 1273 if ( pData && ( pData->nBits & MenuItemBits::NOSELECT ) ) 1274 bSelectable = false; 1275 1276 return bSelectable; 1277 } 1278 1279 css::uno::Reference<css::accessibility::XAccessible> Menu::GetAccessible() 1280 { 1281 // Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets 1282 // overwritten and may contain a disposed object when the initial menubar gets set again. So use the 1283 // mxAccessible member only for sub menus. 1284 if (pStartedFrom && pStartedFrom != this) 1285 { 1286 for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i ) 1287 { 1288 sal_uInt16 nItemId = pStartedFrom->GetItemId( i ); 1289 if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this ) 1290 { 1291 css::uno::Reference<css::accessibility::XAccessible> xParent = pStartedFrom->GetAccessible(); 1292 if ( xParent.is() ) 1293 { 1294 css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext( xParent->getAccessibleContext() ); 1295 if (xParentContext.is()) 1296 return xParentContext->getAccessibleChild( i ); 1297 } 1298 } 1299 } 1300 } 1301 else if ( !mxAccessible.is() ) 1302 { 1303 UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper(); 1304 if ( pWrapper ) 1305 mxAccessible = pWrapper->CreateAccessible(this, IsMenuBar()); 1306 } 1307 1308 return mxAccessible; 1309 } 1310 1311 void Menu::SetAccessible(const css::uno::Reference<css::accessibility::XAccessible>& rxAccessible ) 1312 { 1313 mxAccessible = rxAccessible; 1314 } 1315 1316 Size Menu::ImplGetNativeCheckAndRadioSize(vcl::RenderContext const & rRenderContext, tools::Long& rCheckHeight, tools::Long& rRadioHeight ) const 1317 { 1318 tools::Long nCheckWidth = 0, nRadioWidth = 0; 1319 rCheckHeight = rRadioHeight = 0; 1320 1321 if (!IsMenuBar()) 1322 { 1323 ImplControlValue aVal; 1324 tools::Rectangle aNativeBounds; 1325 tools::Rectangle aNativeContent; 1326 1327 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15))); 1328 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemCheckMark)) 1329 { 1330 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemCheckMark, 1331 aCtrlRegion, ControlState::ENABLED, aVal, 1332 aNativeBounds, aNativeContent)) 1333 { 1334 rCheckHeight = aNativeBounds.GetHeight() - 1; 1335 nCheckWidth = aNativeContent.GetWidth() - 1; 1336 } 1337 } 1338 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItemRadioMark)) 1339 { 1340 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::MenuItemRadioMark, 1341 aCtrlRegion, ControlState::ENABLED, aVal, 1342 aNativeBounds, aNativeContent)) 1343 { 1344 rRadioHeight = aNativeBounds.GetHeight() - 1; 1345 nRadioWidth = aNativeContent.GetWidth() - 1; 1346 } 1347 } 1348 } 1349 return Size(std::max(nCheckWidth, nRadioWidth), std::max(rCheckHeight, rRadioHeight)); 1350 } 1351 1352 bool Menu::ImplGetNativeSubmenuArrowSize(vcl::RenderContext const & rRenderContext, Size& rArrowSize, tools::Long& rArrowSpacing) 1353 { 1354 ImplControlValue aVal; 1355 tools::Rectangle aCtrlRegion(tools::Rectangle(Point(), Size(100, 15))); 1356 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow)) 1357 { 1358 tools::Rectangle aNativeContent; 1359 tools::Rectangle aNativeBounds; 1360 if (rRenderContext.GetNativeControlRegion(ControlType::MenuPopup, ControlPart::SubmenuArrow, 1361 aCtrlRegion, ControlState::ENABLED, 1362 aVal, aNativeBounds, aNativeContent)) 1363 { 1364 Size aSize(aNativeContent.GetWidth(), aNativeContent.GetHeight()); 1365 rArrowSize = aSize; 1366 rArrowSpacing = aNativeBounds.GetWidth() - aNativeContent.GetWidth(); 1367 return true; 1368 } 1369 } 1370 return false; 1371 } 1372 1373 void Menu::ImplAddDel( ImplMenuDelData& rDel ) 1374 { 1375 SAL_WARN_IF( rDel.mpMenu, "vcl", "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" ); 1376 if( !rDel.mpMenu ) 1377 { 1378 rDel.mpMenu = this; 1379 rDel.mpNext = mpFirstDel; 1380 mpFirstDel = &rDel; 1381 } 1382 } 1383 1384 void Menu::ImplRemoveDel( ImplMenuDelData& rDel ) 1385 { 1386 rDel.mpMenu = nullptr; 1387 if ( mpFirstDel == &rDel ) 1388 { 1389 mpFirstDel = rDel.mpNext; 1390 } 1391 else 1392 { 1393 ImplMenuDelData* pData = mpFirstDel; 1394 while ( pData && (pData->mpNext != &rDel) ) 1395 pData = pData->mpNext; 1396 1397 SAL_WARN_IF( !pData, "vcl", "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" ); 1398 if( pData ) 1399 pData->mpNext = rDel.mpNext; 1400 } 1401 } 1402 1403 Size Menu::ImplCalcSize( vcl::Window* pWin ) 1404 { 1405 // | Check/Radio/Image| Text| Accel/Popup| 1406 1407 // for symbols: nFontHeight x nFontHeight 1408 tools::Long nFontHeight = pWin->GetTextHeight(); 1409 tools::Long nExtra = nFontHeight/4; 1410 1411 tools::Long nMinMenuItemHeight = nFontHeight; 1412 tools::Long nCheckHeight = 0, nRadioHeight = 0; 1413 Size aMarkSize = ImplGetNativeCheckAndRadioSize(*pWin->GetOutDev(), nCheckHeight, nRadioHeight); 1414 if( aMarkSize.Height() > nMinMenuItemHeight ) 1415 nMinMenuItemHeight = aMarkSize.Height(); 1416 1417 tools::Long aMaxImgWidth = 0; 1418 1419 const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings(); 1420 if ( rSettings.GetUseImagesInMenus() ) 1421 { 1422 if ( 16 > nMinMenuItemHeight ) 1423 nMinMenuItemHeight = 16; 1424 for ( size_t i = pItemList->size(); i; ) 1425 { 1426 MenuItemData* pData = pItemList->GetDataFromPos( --i ); 1427 if ( ImplIsVisible( i ) 1428 && ( ( pData->eType == MenuItemType::IMAGE ) 1429 || ( pData->eType == MenuItemType::STRINGIMAGE ) 1430 ) 1431 ) 1432 { 1433 Size aImgSz = pData->aImage.GetSizePixel(); 1434 if ( aImgSz.Width() > aMaxImgWidth ) 1435 aMaxImgWidth = aImgSz.Width(); 1436 if ( aImgSz.Height() > nMinMenuItemHeight ) 1437 nMinMenuItemHeight = aImgSz.Height(); 1438 break; 1439 } 1440 } 1441 } 1442 1443 Size aSz; 1444 tools::Long nMaxWidth = 0; 1445 1446 for ( size_t n = pItemList->size(); n; ) 1447 { 1448 MenuItemData* pData = pItemList->GetDataFromPos( --n ); 1449 1450 pData->aSz.setHeight( 0 ); 1451 pData->aSz.setWidth( 0 ); 1452 1453 if ( ImplIsVisible( n ) ) 1454 { 1455 tools::Long nWidth = 0; 1456 1457 // Separator 1458 if (!IsMenuBar()&& (pData->eType == MenuItemType::SEPARATOR)) 1459 { 1460 pData->aSz.setHeight( 4 ); 1461 } 1462 1463 // Image: 1464 if (!IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE))) 1465 { 1466 tools::Long aImgHeight = pData->aImage.GetSizePixel().Height(); 1467 1468 aImgHeight += 4; // add a border for native marks 1469 if (aImgHeight > pData->aSz.Height()) 1470 pData->aSz.setHeight(aImgHeight); 1471 } 1472 1473 // Check Buttons: 1474 if (!IsMenuBar() && pData->HasCheck()) 1475 { 1476 // checks / images take the same place 1477 if( ( pData->eType != MenuItemType::IMAGE ) && ( pData->eType != MenuItemType::STRINGIMAGE ) ) 1478 { 1479 nWidth += aMarkSize.Width() + nExtra * 2; 1480 if (aMarkSize.Height() > pData->aSz.Height()) 1481 pData->aSz.setHeight(aMarkSize.Height()); 1482 } 1483 } 1484 1485 // Text: 1486 if ( (pData->eType == MenuItemType::STRING) || (pData->eType == MenuItemType::STRINGIMAGE) ) 1487 { 1488 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(pWin->GetOutDev()); 1489 tools::Long nTextWidth = pWin->GetOutDev()->GetCtrlTextWidth(pData->aText, pGlyphs); 1490 tools::Long nTextHeight = pWin->GetTextHeight() + EXTRAITEMHEIGHT; 1491 1492 if (IsMenuBar()) 1493 { 1494 if ( nTextHeight > pData->aSz.Height() ) 1495 pData->aSz.setHeight( nTextHeight ); 1496 1497 pData->aSz.setWidth( nTextWidth + 4*nExtra ); 1498 aSz.AdjustWidth(pData->aSz.Width() ); 1499 } 1500 else 1501 pData->aSz.setHeight( std::max( std::max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight ) ); 1502 1503 nWidth += nTextWidth; 1504 } 1505 1506 // Accel 1507 if (!IsMenuBar()&& pData->aAccelKey.GetCode() && !ImplAccelDisabled()) 1508 { 1509 OUString aName = pData->aAccelKey.GetName(); 1510 tools::Long nAccWidth = pWin->GetTextWidth( aName ); 1511 nAccWidth += nExtra; 1512 nWidth += nAccWidth; 1513 } 1514 1515 // SubMenu? 1516 if (!IsMenuBar() && pData->pSubMenu) 1517 { 1518 if ( nFontHeight > nWidth ) 1519 nWidth += nFontHeight; 1520 1521 pData->aSz.setHeight( std::max( std::max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight ) ); 1522 } 1523 1524 if (!IsMenuBar()) 1525 aSz.AdjustHeight(pData->aSz.Height() ); 1526 1527 if ( nWidth > nMaxWidth ) 1528 nMaxWidth = nWidth; 1529 1530 } 1531 } 1532 1533 // Additional space for title 1534 nTitleHeight = 0; 1535 if (!IsMenuBar() && aTitleText.getLength() > 0) { 1536 // Set expected font 1537 pWin->GetOutDev()->Push(PushFlags::FONT); 1538 vcl::Font aFont = pWin->GetFont(); 1539 aFont.SetWeight(WEIGHT_BOLD); 1540 pWin->SetFont(aFont); 1541 1542 // Compute text bounding box 1543 tools::Rectangle aTextBoundRect; 1544 pWin->GetOutDev()->GetTextBoundRect(aTextBoundRect, aTitleText); 1545 1546 // Vertically, one height of char + extra space for decoration 1547 nTitleHeight = aTextBoundRect.GetSize().Height() + 4 * SPACE_AROUND_TITLE ; 1548 aSz.AdjustHeight(nTitleHeight ); 1549 1550 tools::Long nWidth = aTextBoundRect.GetSize().Width() + 4 * SPACE_AROUND_TITLE; 1551 pWin->GetOutDev()->Pop(); 1552 if ( nWidth > nMaxWidth ) 1553 nMaxWidth = nWidth; 1554 } 1555 1556 if (!IsMenuBar()) 1557 { 1558 // popup menus should not be wider than half the screen 1559 // except on rather small screens 1560 // TODO: move GetScreenNumber from SystemWindow to Window ? 1561 // currently we rely on internal privileges 1562 unsigned int nDisplayScreen = pWin->ImplGetWindowImpl()->mpFrame->maGeometry.screen(); 1563 tools::Rectangle aDispRect( Application::GetScreenPosSizePixel( nDisplayScreen ) ); 1564 tools::Long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800; 1565 if( nMaxWidth > nScreenWidth/2 ) 1566 nMaxWidth = nScreenWidth/2; 1567 1568 sal_uInt16 gfxExtra = static_cast<sal_uInt16>(std::max( nExtra, tools::Long(7) )); // #107710# increase space between checkmarks/images/text 1569 nImgOrChkPos = static_cast<sal_uInt16>(nExtra); 1570 tools::Long nImgOrChkWidth = 0; 1571 if( aMarkSize.Height() > 0 ) // NWF case 1572 nImgOrChkWidth = aMarkSize.Height() + nExtra; 1573 else // non NWF case 1574 nImgOrChkWidth = nFontHeight/2 + gfxExtra; 1575 nImgOrChkWidth = std::max( nImgOrChkWidth, aMaxImgWidth + gfxExtra ); 1576 nTextPos = static_cast<sal_uInt16>(nImgOrChkPos + nImgOrChkWidth); 1577 nTextPos = nTextPos + gfxExtra; 1578 1579 aSz.setWidth( nTextPos + nMaxWidth + nExtra ); 1580 aSz.AdjustWidth(4*nExtra ); // a _little_ more ... 1581 1582 aSz.AdjustWidth(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderX ); 1583 aSz.AdjustHeight(2*ImplGetSVData()->maNWFData.mnMenuFormatBorderY ); 1584 } 1585 else 1586 { 1587 nTextPos = static_cast<sal_uInt16>(2*nExtra); 1588 aSz.setHeight( nFontHeight+6 ); 1589 1590 // get menubar height from native methods if supported 1591 if( pWindow->IsNativeControlSupported( ControlType::Menubar, ControlPart::Entire ) ) 1592 { 1593 ImplControlValue aVal; 1594 tools::Rectangle aNativeBounds; 1595 tools::Rectangle aNativeContent; 1596 Point tmp( 0, 0 ); 1597 tools::Rectangle aCtrlRegion( tmp, Size( 100, 15 ) ); 1598 if( pWindow->GetNativeControlRegion( ControlType::Menubar, 1599 ControlPart::Entire, 1600 aCtrlRegion, 1601 ControlState::ENABLED, 1602 aVal, 1603 aNativeBounds, 1604 aNativeContent ) 1605 ) 1606 { 1607 int nNativeHeight = aNativeBounds.GetHeight(); 1608 if( nNativeHeight > aSz.Height() ) 1609 aSz.setHeight( nNativeHeight ); 1610 } 1611 } 1612 1613 // account for the size of the close button, which actually is a toolbox 1614 // due to NWF this is variable 1615 tools::Long nCloseButtonHeight = static_cast<MenuBarWindow*>(pWindow.get())->MinCloseButtonSize().Height(); 1616 if (aSz.Height() < nCloseButtonHeight) 1617 aSz.setHeight( nCloseButtonHeight ); 1618 } 1619 1620 return aSz; 1621 } 1622 1623 static void ImplPaintCheckBackground(vcl::RenderContext & rRenderContext, vcl::Window const & rWindow, const tools::Rectangle& i_rRect, bool i_bHighlight) 1624 { 1625 bool bNativeOk = false; 1626 if (rRenderContext.IsNativeControlSupported(ControlType::Toolbar, ControlPart::Button)) 1627 { 1628 ImplControlValue aControlValue; 1629 aControlValue.setTristateVal(ButtonValue::On); 1630 tools::Rectangle r = i_rRect; 1631 r.AdjustBottom(1); 1632 1633 bNativeOk = rRenderContext.DrawNativeControl(ControlType::Toolbar, ControlPart::Button, 1634 r, 1635 ControlState::PRESSED | ControlState::ENABLED, 1636 aControlValue, 1637 OUString()); 1638 } 1639 1640 if (!bNativeOk) 1641 { 1642 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings(); 1643 Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() ); 1644 RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, nullptr, 2, &aColor); 1645 } 1646 } 1647 1648 static OUString getShortenedString( const OUString& i_rLong, vcl::RenderContext const & rRenderContext, tools::Long i_nMaxWidth ) 1649 { 1650 sal_Int32 nPos = -1; 1651 OUString aNonMnem(OutputDevice::GetNonMnemonicString(i_rLong, nPos)); 1652 aNonMnem = rRenderContext.GetEllipsisString( aNonMnem, i_nMaxWidth, DrawTextFlags::CenterEllipsis); 1653 // re-insert mnemonic 1654 if (nPos != -1) 1655 { 1656 if (nPos < aNonMnem.getLength() && i_rLong[nPos+1] == aNonMnem[nPos]) 1657 { 1658 OUString aTmp = OUString::Concat(aNonMnem.subView(0, nPos)) + "~" + aNonMnem.subView(nPos); 1659 aNonMnem = aTmp; 1660 } 1661 } 1662 return aNonMnem; 1663 } 1664 1665 void Menu::ImplPaintMenuTitle(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) const 1666 { 1667 // Save previous graphical settings, set new one 1668 rRenderContext.Push(PushFlags::FONT | PushFlags::FILLCOLOR); 1669 Wallpaper aOldBackground = rRenderContext.GetBackground(); 1670 1671 Color aBackgroundColor = rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor(); 1672 rRenderContext.SetBackground(Wallpaper(aBackgroundColor)); 1673 rRenderContext.SetFillColor(aBackgroundColor); 1674 vcl::Font aFont = rRenderContext.GetFont(); 1675 aFont.SetWeight(WEIGHT_BOLD); 1676 rRenderContext.SetFont(aFont); 1677 1678 // Draw background rectangle 1679 tools::Rectangle aBgRect(rRect); 1680 int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX; 1681 aBgRect.Move(SPACE_AROUND_TITLE, SPACE_AROUND_TITLE); 1682 aBgRect.setWidth(aBgRect.getWidth() - 2 * SPACE_AROUND_TITLE - 2 * nOuterSpaceX); 1683 aBgRect.setHeight(nTitleHeight - 2 * SPACE_AROUND_TITLE); 1684 rRenderContext.DrawRect(aBgRect); 1685 1686 // Draw the text centered 1687 Point aTextTopLeft(aBgRect.TopLeft()); 1688 tools::Rectangle aTextBoundRect; 1689 rRenderContext.GetTextBoundRect( aTextBoundRect, aTitleText ); 1690 aTextTopLeft.AdjustX((aBgRect.getWidth() - aTextBoundRect.GetSize().Width()) / 2 ); 1691 aTextTopLeft.AdjustY((aBgRect.GetHeight() - aTextBoundRect.GetSize().Height()) / 2 1692 - aTextBoundRect.Top() ); 1693 rRenderContext.DrawText(aTextTopLeft, aTitleText, 0, aTitleText.getLength()); 1694 1695 // Restore 1696 rRenderContext.Pop(); 1697 rRenderContext.SetBackground(aOldBackground); 1698 } 1699 1700 void Menu::ImplPaint(vcl::RenderContext& rRenderContext, Size const & rSize, 1701 sal_uInt16 nBorder, tools::Long nStartY, MenuItemData const * pThisItemOnly, 1702 bool bHighlighted, bool bLayout, bool bRollover) const 1703 { 1704 // for symbols: nFontHeight x nFontHeight 1705 tools::Long nFontHeight = rRenderContext.GetTextHeight(); 1706 tools::Long nExtra = nFontHeight / 4; 1707 1708 tools::Long nCheckHeight = 0, nRadioHeight = 0; 1709 ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight); 1710 1711 DecorationView aDecoView(&rRenderContext); 1712 const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings(); 1713 1714 Point aTopLeft, aTmpPos; 1715 1716 int nOuterSpaceX = 0; 1717 if (!IsMenuBar()) 1718 { 1719 nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX; 1720 aTopLeft.AdjustX(nOuterSpaceX ); 1721 aTopLeft.AdjustY(ImplGetSVData()->maNWFData.mnMenuFormatBorderY ); 1722 } 1723 1724 // for the computations, use size of the underlying window, not of RenderContext 1725 Size aOutSz(rSize); 1726 1727 size_t nCount = pItemList->size(); 1728 if (bLayout) 1729 mpLayoutData->m_aVisibleItemBoundRects.clear(); 1730 1731 // Paint title 1732 if (!pThisItemOnly && !IsMenuBar() && nTitleHeight > 0) 1733 ImplPaintMenuTitle(rRenderContext, tools::Rectangle(aTopLeft, aOutSz)); 1734 1735 bool bHiddenItems = false; // are any items on the GUI hidden 1736 1737 for (size_t n = 0; n < nCount; n++) 1738 { 1739 MenuItemData* pData = pItemList->GetDataFromPos( n ); 1740 if (ImplIsVisible(n) && (!pThisItemOnly || (pData == pThisItemOnly))) 1741 { 1742 if (pThisItemOnly) 1743 { 1744 if (IsMenuBar()) 1745 { 1746 if (!ImplGetSVData()->maNWFData.mbRolloverMenubar) 1747 { 1748 if (bRollover) 1749 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor()); 1750 else if (bHighlighted) 1751 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor()); 1752 } 1753 else 1754 { 1755 if (bHighlighted) 1756 rRenderContext.SetTextColor(rSettings.GetMenuBarHighlightTextColor()); 1757 else if (bRollover) 1758 rRenderContext.SetTextColor(rSettings.GetMenuBarRolloverTextColor()); 1759 } 1760 if (!bRollover && !bHighlighted) 1761 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor()); 1762 } 1763 else if (bHighlighted) 1764 rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor()); 1765 } 1766 1767 Point aPos(aTopLeft); 1768 aPos.AdjustY(nBorder ); 1769 aPos.AdjustY(nStartY ); 1770 1771 if (aPos.Y() >= 0) 1772 { 1773 tools::Long nTextOffsetY = (pData->aSz.Height() - nFontHeight) / 2; 1774 if (IsMenuBar()) 1775 nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2; 1776 DrawTextFlags nTextStyle = DrawTextFlags::NONE; 1777 DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE; 1778 DrawImageFlags nImageStyle = DrawImageFlags::NONE; 1779 1780 // submenus without items are not disabled when no items are 1781 // contained. The application itself should check for this! 1782 // Otherwise it could happen entries are disabled due to 1783 // asynchronous loading 1784 if (!pData->bEnabled || !pWindow->IsEnabled()) 1785 { 1786 nTextStyle |= DrawTextFlags::Disable; 1787 nSymbolStyle |= DrawSymbolFlags::Disable; 1788 nImageStyle |= DrawImageFlags::Disable; 1789 } 1790 1791 // Separator 1792 if (!bLayout && !IsMenuBar() && (pData->eType == MenuItemType::SEPARATOR)) 1793 { 1794 bool bNativeOk = false; 1795 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Separator)) 1796 { 1797 ControlState nState = ControlState::NONE; 1798 if (pData->bEnabled && pWindow->IsEnabled()) 1799 nState |= ControlState::ENABLED; 1800 if (bHighlighted) 1801 nState |= ControlState::SELECTED; 1802 Size aSz(pData->aSz); 1803 aSz.setWidth( aOutSz.Width() - 2*nOuterSpaceX ); 1804 tools::Rectangle aItemRect(aPos, aSz); 1805 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect); 1806 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Separator, 1807 aItemRect, nState, aVal, OUString()); 1808 } 1809 if (!bNativeOk) 1810 { 1811 aTmpPos.setY( aPos.Y() + ((pData->aSz.Height() - 2) / 2) ); 1812 aTmpPos.setX( aPos.X() + 2 + nOuterSpaceX ); 1813 rRenderContext.SetLineColor(rSettings.GetShadowColor()); 1814 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y())); 1815 aTmpPos.AdjustY( 1 ); 1816 rRenderContext.SetLineColor(rSettings.GetLightColor()); 1817 rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpaceX, aTmpPos.Y())); 1818 rRenderContext.SetLineColor(); 1819 } 1820 } 1821 1822 tools::Rectangle aOuterCheckRect(Point(aPos.X()+nImgOrChkPos, aPos.Y()), 1823 Size(pData->aSz.Height(), pData->aSz.Height())); 1824 1825 // CheckMark 1826 if (!bLayout && !IsMenuBar() && pData->HasCheck()) 1827 { 1828 // draw selection transparent marker if checked 1829 // onto that either a checkmark or the item image 1830 // will be painted 1831 // however do not do this if native checks will be painted since 1832 // the selection color too often does not fit the theme's check and/or radio 1833 1834 if( (pData->eType != MenuItemType::IMAGE) && (pData->eType != MenuItemType::STRINGIMAGE)) 1835 { 1836 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, 1837 (pData->nBits & MenuItemBits::RADIOCHECK) 1838 ? ControlPart::MenuItemCheckMark 1839 : ControlPart::MenuItemRadioMark)) 1840 { 1841 ControlPart nPart = ((pData->nBits & MenuItemBits::RADIOCHECK) 1842 ? ControlPart::MenuItemRadioMark 1843 : ControlPart::MenuItemCheckMark); 1844 1845 ControlState nState = ControlState::NONE; 1846 1847 if (pData->bChecked) 1848 nState |= ControlState::PRESSED; 1849 1850 if (pData->bEnabled && pWindow->IsEnabled()) 1851 nState |= ControlState::ENABLED; 1852 1853 if (bHighlighted) 1854 nState |= ControlState::SELECTED; 1855 1856 tools::Long nCtrlHeight = (pData->nBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight; 1857 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2 ); 1858 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2 ); 1859 1860 tools::Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight)); 1861 Size aSz(pData->aSz); 1862 aSz.setWidth( aOutSz.Width() - 2 * nOuterSpaceX ); 1863 tools::Rectangle aItemRect(aPos, aSz); 1864 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect); 1865 rRenderContext.DrawNativeControl(ControlType::MenuPopup, nPart, aCheckRect, 1866 nState, aVal, OUString()); 1867 } 1868 else if (pData->bChecked) // by default do nothing for unchecked items 1869 { 1870 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted); 1871 1872 SymbolType eSymbol; 1873 Size aSymbolSize; 1874 if (pData->nBits & MenuItemBits::RADIOCHECK) 1875 { 1876 eSymbol = SymbolType::RADIOCHECKMARK; 1877 aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2); 1878 } 1879 else 1880 { 1881 eSymbol = SymbolType::CHECKMARK; 1882 aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2); 1883 } 1884 aTmpPos.setX( aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width()) / 2 ); 1885 aTmpPos.setY( aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height()) / 2 ); 1886 tools::Rectangle aRect(aTmpPos, aSymbolSize); 1887 aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetTextColor(), nSymbolStyle); 1888 } 1889 } 1890 } 1891 1892 // Image: 1893 if (!bLayout && !IsMenuBar() && ((pData->eType == MenuItemType::IMAGE) || (pData->eType == MenuItemType::STRINGIMAGE))) 1894 { 1895 // Don't render an image for a check thing 1896 if (pData->bChecked) 1897 ImplPaintCheckBackground(rRenderContext, *pWindow, aOuterCheckRect, pThisItemOnly && bHighlighted); 1898 1899 Image aImage = pData->aImage; 1900 1901 aTmpPos = aOuterCheckRect.TopLeft(); 1902 aTmpPos.AdjustX((aOuterCheckRect.GetWidth() - aImage.GetSizePixel().Width()) / 2 ); 1903 aTmpPos.AdjustY((aOuterCheckRect.GetHeight() - aImage.GetSizePixel().Height()) / 2 ); 1904 rRenderContext.DrawImage(aTmpPos, aImage, nImageStyle); 1905 } 1906 1907 // Text: 1908 if ((pData->eType == MenuItemType::STRING ) || (pData->eType == MenuItemType::STRINGIMAGE)) 1909 { 1910 aTmpPos.setX( aPos.X() + nTextPos ); 1911 aTmpPos.setY( aPos.Y() ); 1912 aTmpPos.AdjustY(nTextOffsetY ); 1913 DrawTextFlags nStyle = nTextStyle | DrawTextFlags::Mnemonic; 1914 1915 if (pData->bIsTemporary) 1916 nStyle |= DrawTextFlags::Disable; 1917 std::vector< tools::Rectangle >* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : nullptr; 1918 OUString* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : nullptr; 1919 if (bLayout) 1920 { 1921 mpLayoutData->m_aLineIndices.push_back(mpLayoutData->m_aDisplayText.getLength()); 1922 mpLayoutData->m_aLineItemIds.push_back(pData->nId); 1923 } 1924 // #i47946# with NWF painted menus the background is transparent 1925 // since DrawCtrlText can depend on the background (e.g. for 1926 // DrawTextFlags::Disable), temporarily set a background which 1927 // hopefully matches the NWF background since it is read 1928 // from the system style settings 1929 bool bSetTmpBackground = !rRenderContext.IsBackground() 1930 && rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire); 1931 if (bSetTmpBackground) 1932 { 1933 Color aBg = IsMenuBar() ? rRenderContext.GetSettings().GetStyleSettings().GetMenuBarColor() 1934 : rRenderContext.GetSettings().GetStyleSettings().GetMenuColor(); 1935 rRenderContext.SetBackground(Wallpaper(aBg)); 1936 } 1937 // how much space is there for the text? 1938 tools::Long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpaceX; 1939 if (!IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled()) 1940 { 1941 OUString aAccText = pData->aAccelKey.GetName(); 1942 nMaxItemTextWidth -= rRenderContext.GetTextWidth(aAccText) + 3 * nExtra; 1943 } 1944 if (!IsMenuBar() && pData->pSubMenu) 1945 { 1946 nMaxItemTextWidth -= nFontHeight - nExtra; 1947 } 1948 1949 OUString aItemText(pData->aText); 1950 pData->bHiddenOnGUI = false; 1951 1952 if (IsMenuBar()) // In case of menubar if we are out of bounds we shouldn't paint the item 1953 { 1954 if (nMaxItemTextWidth < rRenderContext.GetTextWidth(aItemText)) 1955 { 1956 aItemText = ""; 1957 pData->bHiddenOnGUI = true; 1958 bHiddenItems = true; 1959 } 1960 } 1961 else 1962 { 1963 aItemText = getShortenedString(aItemText, rRenderContext, nMaxItemTextWidth); 1964 pData->bHiddenOnGUI = false; 1965 } 1966 1967 const SalLayoutGlyphs* pGlyphs = pData->GetTextGlyphs(&rRenderContext); 1968 if (aItemText != pData->aText) 1969 // Can't use pre-computed glyphs, item text was 1970 // changed. 1971 pGlyphs = nullptr; 1972 rRenderContext.DrawCtrlText(aTmpPos, aItemText, 0, aItemText.getLength(), 1973 nStyle, pVector, pDisplayText, pGlyphs); 1974 if (bSetTmpBackground) 1975 rRenderContext.SetBackground(); 1976 } 1977 1978 // Accel 1979 if (!bLayout && !IsMenuBar() && pData->aAccelKey.GetCode() && !ImplAccelDisabled()) 1980 { 1981 OUString aAccText = pData->aAccelKey.GetName(); 1982 aTmpPos.setX( aOutSz.Width() - rRenderContext.GetTextWidth(aAccText) ); 1983 aTmpPos.AdjustX( -(4 * nExtra) ); 1984 1985 aTmpPos.AdjustX( -nOuterSpaceX ); 1986 aTmpPos.setY( aPos.Y() ); 1987 aTmpPos.AdjustY(nTextOffsetY ); 1988 rRenderContext.DrawCtrlText(aTmpPos, aAccText, 0, aAccText.getLength(), nTextStyle); 1989 } 1990 1991 // SubMenu? 1992 if (!bLayout && !IsMenuBar() && pData->pSubMenu) 1993 { 1994 bool bNativeOk = false; 1995 if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::SubmenuArrow)) 1996 { 1997 ControlState nState = ControlState::NONE; 1998 Size aTmpSz(0, 0); 1999 tools::Long aSpacing = 0; 2000 2001 if (!ImplGetNativeSubmenuArrowSize(rRenderContext, aTmpSz, aSpacing)) 2002 { 2003 aTmpSz = Size(nFontHeight, nFontHeight); 2004 aSpacing = nOuterSpaceX; 2005 } 2006 2007 if (pData->bEnabled && pWindow->IsEnabled()) 2008 nState |= ControlState::ENABLED; 2009 if (bHighlighted) 2010 nState |= ControlState::SELECTED; 2011 2012 aTmpPos.setX( aOutSz.Width() - aTmpSz.Width() - aSpacing - nOuterSpaceX ); 2013 aTmpPos.setY( aPos.Y() + ( pData->aSz.Height() - aTmpSz.Height() ) / 2 ); 2014 aTmpPos.AdjustY(nExtra / 2 ); 2015 2016 tools::Rectangle aItemRect(aTmpPos, aTmpSz); 2017 MenupopupValue aVal(nTextPos - GUTTERBORDER, aItemRect); 2018 bNativeOk = rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::SubmenuArrow, 2019 aItemRect, nState, aVal, OUString()); 2020 } 2021 if (!bNativeOk) 2022 { 2023 aTmpPos.setX( aOutSz.Width() - nFontHeight + nExtra - nOuterSpaceX ); 2024 aTmpPos.setY( aPos.Y() ); 2025 aTmpPos.AdjustY(nExtra/2 ); 2026 aTmpPos.AdjustY((pData->aSz.Height() / 2) - (nFontHeight / 4) ); 2027 if (pData->nBits & MenuItemBits::POPUPSELECT) 2028 { 2029 rRenderContext.SetTextColor(rSettings.GetMenuTextColor()); 2030 Point aTmpPos2(aPos); 2031 aTmpPos2.setX( aOutSz.Width() - nFontHeight - nFontHeight/4 ); 2032 aDecoView.DrawFrame(tools::Rectangle(aTmpPos2, Size(nFontHeight + nFontHeight / 4, 2033 pData->aSz.Height())), 2034 DrawFrameStyle::Group); 2035 } 2036 aDecoView.DrawSymbol(tools::Rectangle(aTmpPos, Size(nFontHeight / 2, nFontHeight / 2)), 2037 SymbolType::SPIN_RIGHT, rRenderContext.GetTextColor(), nSymbolStyle); 2038 } 2039 } 2040 2041 if (pThisItemOnly && bHighlighted) 2042 { 2043 // This restores the normal menu or menu bar text 2044 // color for when it is no longer highlighted. 2045 if (IsMenuBar()) 2046 rRenderContext.SetTextColor(rSettings.GetMenuBarTextColor()); 2047 else 2048 rRenderContext.SetTextColor(rSettings.GetMenuTextColor()); 2049 } 2050 } 2051 if( bLayout ) 2052 { 2053 if (!IsMenuBar()) 2054 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, Size(aOutSz.Width(), pData->aSz.Height())); 2055 else 2056 mpLayoutData->m_aVisibleItemBoundRects[ n ] = tools::Rectangle(aTopLeft, pData->aSz); 2057 } 2058 } 2059 2060 if (!IsMenuBar()) 2061 aTopLeft.AdjustY(pData->aSz.Height() ); 2062 else 2063 aTopLeft.AdjustX(pData->aSz.Width() ); 2064 } 2065 2066 // draw "more" (">>") indicator if some items have been hidden as they go out of visible area 2067 if (bHiddenItems) 2068 { 2069 sal_Int32 nSize = nFontHeight; 2070 tools::Rectangle aRectangle(Point(aOutSz.Width() - nSize, (aOutSz.Height() / 2) - (nSize / 2)), Size(nSize, nSize)); 2071 lclDrawMoreIndicator(rRenderContext, aRectangle); 2072 } 2073 } 2074 2075 Menu* Menu::ImplGetStartMenu() 2076 { 2077 Menu* pStart = this; 2078 while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) ) 2079 pStart = pStart->pStartedFrom; 2080 return pStart; 2081 } 2082 2083 void Menu::ImplCallHighlight(sal_uInt16 nItem) 2084 { 2085 ImplMenuDelData aDelData( this ); 2086 2087 nSelectedId = 0; 2088 sSelectedIdent.clear(); 2089 MenuItemData* pData = pItemList->GetDataFromPos(nItem); 2090 if (pData) 2091 { 2092 nSelectedId = pData->nId; 2093 sSelectedIdent = pData->sIdent; 2094 } 2095 ImplCallEventListeners( VclEventId::MenuHighlight, GetItemPos( GetCurItemId() ) ); 2096 2097 if( !aDelData.isDeleted() ) 2098 { 2099 nSelectedId = 0; 2100 sSelectedIdent.clear(); 2101 } 2102 } 2103 2104 IMPL_LINK_NOARG(Menu, ImplCallSelect, void*, void) 2105 { 2106 nEventId = nullptr; 2107 Select(); 2108 } 2109 2110 Menu* Menu::ImplFindSelectMenu() 2111 { 2112 Menu* pSelMenu = nEventId ? this : nullptr; 2113 2114 for ( size_t n = GetItemList()->size(); n && !pSelMenu; ) 2115 { 2116 MenuItemData* pData = GetItemList()->GetDataFromPos( --n ); 2117 2118 if ( pData->pSubMenu ) 2119 pSelMenu = pData->pSubMenu->ImplFindSelectMenu(); 2120 } 2121 2122 return pSelMenu; 2123 } 2124 2125 Menu* Menu::ImplFindMenu( sal_uInt16 nItemId ) 2126 { 2127 Menu* pSelMenu = nullptr; 2128 2129 for ( size_t n = GetItemList()->size(); n && !pSelMenu; ) 2130 { 2131 MenuItemData* pData = GetItemList()->GetDataFromPos( --n ); 2132 2133 if( pData->nId == nItemId ) 2134 pSelMenu = this; 2135 else if ( pData->pSubMenu ) 2136 pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId ); 2137 } 2138 2139 return pSelMenu; 2140 } 2141 2142 void Menu::RemoveDisabledEntries( bool bRemoveEmptyPopups ) 2143 { 2144 for ( sal_uInt16 n = 0; n < GetItemCount(); n++ ) 2145 { 2146 bool bRemove = false; 2147 MenuItemData* pItem = pItemList->GetDataFromPos( n ); 2148 if ( pItem->eType == MenuItemType::SEPARATOR ) 2149 { 2150 if ( !n || ( GetItemType( n-1 ) == MenuItemType::SEPARATOR ) ) 2151 bRemove = true; 2152 } 2153 else 2154 bRemove = !pItem->bEnabled; 2155 2156 if ( pItem->pSubMenu ) 2157 { 2158 pItem->pSubMenu->RemoveDisabledEntries(); 2159 if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() ) 2160 bRemove = true; 2161 } 2162 2163 if ( bRemove ) 2164 RemoveItem( n-- ); 2165 } 2166 2167 if ( GetItemCount() ) 2168 { 2169 sal_uInt16 nLast = GetItemCount() - 1; 2170 MenuItemData* pItem = pItemList->GetDataFromPos( nLast ); 2171 if ( pItem->eType == MenuItemType::SEPARATOR ) 2172 RemoveItem( nLast ); 2173 } 2174 mpLayoutData.reset(); 2175 } 2176 2177 void Menu::UpdateNativeMenu() 2178 { 2179 if ( ImplGetSalMenu() ) 2180 ImplGetSalMenu()->Update(); 2181 } 2182 2183 void Menu::MenuBarKeyInput(const KeyEvent&) 2184 { 2185 } 2186 2187 void Menu::ImplKillLayoutData() const 2188 { 2189 mpLayoutData.reset(); 2190 } 2191 2192 void Menu::ImplFillLayoutData() const 2193 { 2194 if (!(pWindow && pWindow->IsReallyVisible())) 2195 return; 2196 2197 mpLayoutData.reset(new MenuLayoutData); 2198 if (IsMenuBar()) 2199 { 2200 ImplPaint(*pWindow->GetOutDev(), pWindow->GetOutputSizePixel(), 0, 0, nullptr, false, true); // FIXME 2201 } 2202 else 2203 { 2204 MenuFloatingWindow* pFloat = static_cast<MenuFloatingWindow*>(pWindow.get()); 2205 ImplPaint(*pWindow->GetOutDev(), pWindow->GetOutputSizePixel(), pFloat->nScrollerHeight, pFloat->ImplGetStartY(), 2206 nullptr, false, true); //FIXME 2207 } 2208 } 2209 2210 SalMenu* Menu::GetNativeMenuBar() 2211 { 2212 return mpSalMenu && mpSalMenu->HasNativeMenuBar() ? mpSalMenu.get() : nullptr; 2213 } 2214 2215 tools::Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, tools::Long nIndex ) const 2216 { 2217 tools::Long nItemIndex = -1; 2218 if( ! mpLayoutData ) 2219 ImplFillLayoutData(); 2220 if( mpLayoutData ) 2221 { 2222 for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ ) 2223 { 2224 if( mpLayoutData->m_aLineItemIds[i] == nItemID ) 2225 { 2226 nItemIndex = mpLayoutData->m_aLineIndices[i]; 2227 break; 2228 } 2229 } 2230 } 2231 return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : tools::Rectangle(); 2232 } 2233 2234 tools::Long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const 2235 { 2236 tools::Long nIndex = -1; 2237 rItemID = 0; 2238 if( ! mpLayoutData ) 2239 ImplFillLayoutData(); 2240 if( mpLayoutData ) 2241 { 2242 nIndex = mpLayoutData->GetIndexForPoint( rPoint ); 2243 for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ ) 2244 { 2245 if( mpLayoutData->m_aLineIndices[i] <= nIndex && 2246 (i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) ) 2247 { 2248 // make index relative to item 2249 nIndex -= mpLayoutData->m_aLineIndices[i]; 2250 rItemID = mpLayoutData->m_aLineItemIds[i]; 2251 break; 2252 } 2253 } 2254 } 2255 return nIndex; 2256 } 2257 2258 tools::Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const 2259 { 2260 tools::Rectangle aRet; 2261 2262 if (!mpLayoutData ) 2263 ImplFillLayoutData(); 2264 if (mpLayoutData) 2265 { 2266 std::map< sal_uInt16, tools::Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos ); 2267 if( it != mpLayoutData->m_aVisibleItemBoundRects.end() ) 2268 aRet = it->second; 2269 } 2270 return aRet; 2271 } 2272 2273 void Menu::SetAccessibleName( sal_uInt16 nItemId, const OUString& rStr ) 2274 { 2275 size_t nPos; 2276 MenuItemData* pData = pItemList->GetData( nItemId, nPos ); 2277 2278 if (pData && !rStr.equals(pData->aAccessibleName)) 2279 { 2280 pData->aAccessibleName = rStr; 2281 ImplCallEventListeners(VclEventId::MenuAccessibleNameChanged, nPos); 2282 } 2283 } 2284 2285 OUString Menu::GetAccessibleName( sal_uInt16 nItemId ) const 2286 { 2287 MenuItemData* pData = pItemList->GetData( nItemId ); 2288 2289 if ( pData ) 2290 return pData->aAccessibleName; 2291 2292 return OUString(); 2293 } 2294 2295 void Menu::SetAccessibleDescription( sal_uInt16 nItemId, const OUString& rStr ) 2296 { 2297 MenuItemData* pData = pItemList->GetData( nItemId ); 2298 2299 if ( pData ) 2300 pData->aAccessibleDescription = rStr; 2301 } 2302 2303 OUString Menu::GetAccessibleDescription( sal_uInt16 nItemId ) const 2304 { 2305 MenuItemData* pData = pItemList->GetData( nItemId ); 2306 2307 if (pData && !pData->aAccessibleDescription.isEmpty()) 2308 return pData->aAccessibleDescription; 2309 2310 return GetHelpText(nItemId); 2311 } 2312 2313 void Menu::GetSystemMenuData( SystemMenuData* pData ) const 2314 { 2315 Menu* pMenu = const_cast<Menu*>(this); 2316 if( pData && pMenu->ImplGetSalMenu() ) 2317 { 2318 pMenu->ImplGetSalMenu()->GetSystemMenuData( pData ); 2319 } 2320 } 2321 2322 bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const 2323 { 2324 bool bRet = false; 2325 2326 if( pWindow ) 2327 { 2328 if (IsMenuBar()) 2329 bRet = ( nItemPos == static_cast< MenuBarWindow * > (pWindow.get())->GetHighlightedItem() ); 2330 else 2331 bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (pWindow.get())->GetHighlightedItem() ); 2332 } 2333 2334 return bRet; 2335 } 2336 2337 void Menu::HighlightItem( sal_uInt16 nItemPos ) 2338 { 2339 if ( !pWindow ) 2340 return; 2341 2342 if (IsMenuBar()) 2343 { 2344 MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow.get() ); 2345 pMenuWin->SetAutoPopup( false ); 2346 pMenuWin->ChangeHighlightItem( nItemPos, false ); 2347 } 2348 else 2349 { 2350 static_cast< MenuFloatingWindow* >( pWindow.get() )->ChangeHighlightItem( nItemPos, false ); 2351 } 2352 } 2353 2354 MenuBarWindow* MenuBar::getMenuBarWindow() 2355 { 2356 // so far just a dynamic_cast, hopefully to be turned into something saner 2357 // at some stage 2358 MenuBarWindow *pWin = dynamic_cast<MenuBarWindow*>(pWindow.get()); 2359 //either there is no window (fdo#87663) or it is a MenuBarWindow 2360 assert(!pWindow || pWin); 2361 return pWin; 2362 } 2363 2364 MenuBar::MenuBar() 2365 : mbCloseBtnVisible(false), 2366 mbFloatBtnVisible(false), 2367 mbHideBtnVisible(false), 2368 mbDisplayable(true) 2369 { 2370 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this); 2371 } 2372 2373 MenuBar::MenuBar( const MenuBar& rMenu ) 2374 : mbCloseBtnVisible(false), 2375 mbFloatBtnVisible(false), 2376 mbHideBtnVisible(false), 2377 mbDisplayable(true) 2378 { 2379 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(true, this); 2380 *this = rMenu; 2381 } 2382 2383 MenuBar::~MenuBar() 2384 { 2385 disposeOnce(); 2386 } 2387 2388 void MenuBar::dispose() 2389 { 2390 ImplDestroy( this, true ); 2391 Menu::dispose(); 2392 } 2393 2394 void MenuBar::ClosePopup(Menu *pMenu) 2395 { 2396 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2397 if (!pMenuWin) 2398 return; 2399 pMenuWin->PopupClosed(pMenu); 2400 } 2401 2402 void MenuBar::MenuBarKeyInput(const KeyEvent& rEvent) 2403 { 2404 pWindow->KeyInput(rEvent); 2405 } 2406 2407 void MenuBar::ShowCloseButton(bool bShow) 2408 { 2409 ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible ); 2410 } 2411 2412 void MenuBar::ShowButtons( bool bClose, bool bFloat, bool bHide ) 2413 { 2414 if ((bClose != mbCloseBtnVisible) || 2415 (bFloat != mbFloatBtnVisible) || 2416 (bHide != mbHideBtnVisible)) 2417 { 2418 mbCloseBtnVisible = bClose; 2419 mbFloatBtnVisible = bFloat; 2420 mbHideBtnVisible = bHide; 2421 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2422 if (pMenuWin) 2423 pMenuWin->ShowButtons(bClose, bFloat, bHide); 2424 } 2425 } 2426 2427 void MenuBar::LayoutChanged() 2428 { 2429 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2430 if (pMenuWin) 2431 pMenuWin->LayoutChanged(); 2432 } 2433 2434 void MenuBar::SetDisplayable( bool bDisplayable ) 2435 { 2436 if( bDisplayable != mbDisplayable ) 2437 { 2438 if ( ImplGetSalMenu() ) 2439 ImplGetSalMenu()->ShowMenuBar( bDisplayable ); 2440 2441 mbDisplayable = bDisplayable; 2442 LayoutChanged(); 2443 } 2444 } 2445 2446 VclPtr<vcl::Window> MenuBar::ImplCreate(vcl::Window* pParent, vcl::Window* pWindow, MenuBar* pMenu) 2447 { 2448 // can't this be a static_cast? is there a real possibility, the pWindow is not the MenuBarWindow or nullptr? 2449 VclPtr<MenuBarWindow> pMenuBarWindow = dynamic_cast<MenuBarWindow*>(pWindow); 2450 if (!pMenuBarWindow) 2451 pMenuBarWindow = VclPtr<MenuBarWindow>::Create(pParent); 2452 2453 pMenu->pStartedFrom = nullptr; 2454 pMenu->pWindow = pMenuBarWindow; 2455 pMenuBarWindow->SetMenu(pMenu); 2456 2457 return pMenuBarWindow; 2458 } 2459 2460 void MenuBar::ImplDestroy( MenuBar* pMenu, bool bDelete ) 2461 { 2462 vcl::Window *pWindow = pMenu->ImplGetWindow(); 2463 if (pWindow && bDelete) 2464 { 2465 MenuBarWindow* pMenuWin = pMenu->getMenuBarWindow(); 2466 if (pMenuWin) 2467 pMenuWin->KillActivePopup(); 2468 pWindow->disposeOnce(); 2469 } 2470 pMenu->pWindow = nullptr; 2471 if (pMenu->mpSalMenu) { 2472 pMenu->mpSalMenu->ShowMenuBar(false); 2473 } 2474 } 2475 2476 bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent ) 2477 { 2478 // No keyboard processing when our menubar is invisible 2479 if (!IsDisplayable()) 2480 return false; 2481 2482 // No keyboard processing when system handles the menu. 2483 SalMenu *pNativeMenu = ImplGetSalMenu(); 2484 if (pNativeMenu && pNativeMenu->HasNativeMenuBar()) 2485 { 2486 // Except when the event is the F6 cycle pane event and we can put our 2487 // focus into it (i.e. the gtk3 menubar case but not the mac/unity case 2488 // where it's not part of the application window) 2489 if (!TaskPaneList::IsCycleKey(rKEvent.GetKeyCode())) 2490 return false; 2491 if (!pNativeMenu->CanGetFocus()) 2492 return false; 2493 } 2494 2495 bool bDone = false; 2496 // check for enabled, if this method is called from another window... 2497 vcl::Window* pWin = ImplGetWindow(); 2498 if (pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode()) 2499 { 2500 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2501 bDone = pMenuWin && pMenuWin->HandleKeyEvent(rKEvent, false/*bFromMenu*/); 2502 } 2503 return bDone; 2504 } 2505 2506 void MenuBar::SelectItem(sal_uInt16 nId) 2507 { 2508 if (!pWindow) 2509 return; 2510 2511 pWindow->GrabFocus(); 2512 nId = GetItemPos( nId ); 2513 2514 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2515 if (pMenuWin) 2516 { 2517 // #99705# popup the selected menu 2518 pMenuWin->SetAutoPopup( true ); 2519 if (ITEMPOS_INVALID != pMenuWin->GetHighlightedItem()) 2520 { 2521 pMenuWin->KillActivePopup(); 2522 pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, false ); 2523 } 2524 if (nId != ITEMPOS_INVALID) 2525 pMenuWin->ChangeHighlightItem( nId, false ); 2526 } 2527 } 2528 2529 // handler for native menu selection and command events 2530 bool Menu::HandleMenuActivateEvent( Menu *pMenu ) const 2531 { 2532 if( pMenu ) 2533 { 2534 ImplMenuDelData aDelData( this ); 2535 2536 pMenu->pStartedFrom = const_cast<Menu*>(this); 2537 pMenu->bInCallback = true; 2538 pMenu->Activate(); 2539 2540 if( !aDelData.isDeleted() ) 2541 pMenu->bInCallback = false; 2542 } 2543 return true; 2544 } 2545 2546 bool Menu::HandleMenuDeActivateEvent( Menu *pMenu ) const 2547 { 2548 if( pMenu ) 2549 { 2550 ImplMenuDelData aDelData( this ); 2551 2552 pMenu->pStartedFrom = const_cast<Menu*>(this); 2553 pMenu->bInCallback = true; 2554 pMenu->Deactivate(); 2555 if( !aDelData.isDeleted() ) 2556 pMenu->bInCallback = false; 2557 } 2558 return true; 2559 } 2560 2561 bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventId ) const 2562 { 2563 if( !pMenu ) 2564 pMenu = const_cast<MenuBar*>(this)->ImplFindMenu(nHighlightEventId); 2565 if( pMenu ) 2566 { 2567 ImplMenuDelData aDelData( pMenu ); 2568 2569 if( mnHighlightedItemPos != ITEMPOS_INVALID ) 2570 pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, mnHighlightedItemPos ); 2571 2572 if( !aDelData.isDeleted() ) 2573 { 2574 pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId ); 2575 pMenu->nSelectedId = nHighlightEventId; 2576 pMenu->sSelectedIdent = pMenu->GetItemIdent( nHighlightEventId ); 2577 pMenu->pStartedFrom = const_cast<MenuBar*>(this); 2578 pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos ); 2579 } 2580 return true; 2581 } 2582 else 2583 return false; 2584 } 2585 2586 bool Menu::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const 2587 { 2588 if( !pMenu ) 2589 pMenu = const_cast<Menu*>(this)->ImplFindMenu(nCommandEventId); 2590 if( pMenu ) 2591 { 2592 pMenu->nSelectedId = nCommandEventId; 2593 pMenu->sSelectedIdent = pMenu->GetItemIdent(nCommandEventId); 2594 pMenu->pStartedFrom = const_cast<Menu*>(this); 2595 pMenu->ImplSelect(); 2596 return true; 2597 } 2598 else 2599 return false; 2600 } 2601 2602 sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link<MenuBarButtonCallbackArg&,bool>& i_rLink, const OUString& i_rToolTip ) 2603 { 2604 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2605 return pMenuWin ? pMenuWin->AddMenuBarButton(i_rImage, i_rLink, i_rToolTip) : 0; 2606 } 2607 2608 void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link<MenuBarButtonCallbackArg&,bool>& rLink ) 2609 { 2610 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2611 if (!pMenuWin) 2612 return; 2613 pMenuWin->SetMenuBarButtonHighlightHdl(nId, rLink); 2614 } 2615 2616 void MenuBar::RemoveMenuBarButton( sal_uInt16 nId ) 2617 { 2618 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2619 if (!pMenuWin) 2620 return; 2621 pMenuWin->RemoveMenuBarButton(nId); 2622 } 2623 2624 tools::Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId ) 2625 { 2626 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2627 return pMenuWin ? pMenuWin->GetMenuBarButtonRectPixel(nId) : tools::Rectangle(); 2628 } 2629 2630 bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId ) 2631 { 2632 MenuBarWindow* pMenuWin = getMenuBarWindow(); 2633 return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId); 2634 } 2635 2636 int MenuBar::GetMenuBarHeight() const 2637 { 2638 MenuBar* pMenuBar = const_cast<MenuBar*>(this); 2639 const SalMenu *pNativeMenu = pMenuBar->ImplGetSalMenu(); 2640 int nMenubarHeight; 2641 if (pNativeMenu) 2642 nMenubarHeight = pNativeMenu->GetMenuBarHeight(); 2643 else 2644 { 2645 vcl::Window* pMenubarWin = GetWindow(); 2646 nMenubarHeight = pMenubarWin ? pMenubarWin->GetOutputSizePixel().Height() : 0; 2647 } 2648 return nMenubarHeight; 2649 } 2650 2651 // bool PopupMenu::bAnyPopupInExecute = false; 2652 2653 MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const { 2654 return static_cast<MenuFloatingWindow *>(Menu::ImplGetWindow()); 2655 } 2656 2657 PopupMenu::PopupMenu() 2658 { 2659 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this); 2660 } 2661 2662 PopupMenu::PopupMenu( const PopupMenu& rMenu ) 2663 { 2664 mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu(false, this); 2665 *this = rMenu; 2666 } 2667 2668 PopupMenu::~PopupMenu() 2669 { 2670 disposeOnce(); 2671 } 2672 2673 void PopupMenu::ClosePopup(Menu* pMenu) 2674 { 2675 MenuFloatingWindow* p = dynamic_cast<MenuFloatingWindow*>(ImplGetWindow()); 2676 PopupMenu *pPopup = dynamic_cast<PopupMenu*>(pMenu); 2677 if (p && pPopup) 2678 p->KillActivePopup(pPopup); 2679 } 2680 2681 namespace vcl 2682 { 2683 bool IsInPopupMenuExecute() 2684 { 2685 return PopupMenu::GetActivePopupMenu() != nullptr; 2686 } 2687 } 2688 2689 PopupMenu* PopupMenu::GetActivePopupMenu() 2690 { 2691 ImplSVData* pSVData = ImplGetSVData(); 2692 return pSVData->maAppData.mpActivePopupMenu; 2693 } 2694 2695 void PopupMenu::EndExecute() 2696 { 2697 if ( ImplGetWindow() ) 2698 ImplGetFloatingWindow()->EndExecute( 0 ); 2699 } 2700 2701 void PopupMenu::SelectItem(sal_uInt16 nId) 2702 { 2703 if ( !ImplGetWindow() ) 2704 return; 2705 2706 if( nId != ITEMPOS_INVALID ) 2707 { 2708 size_t nPos = 0; 2709 MenuItemData* pData = GetItemList()->GetData( nId, nPos ); 2710 if (pData && pData->pSubMenu) 2711 ImplGetFloatingWindow()->ChangeHighlightItem( nPos, true ); 2712 else 2713 ImplGetFloatingWindow()->EndExecute( nId ); 2714 } 2715 else 2716 { 2717 MenuFloatingWindow* pFloat = ImplGetFloatingWindow(); 2718 pFloat->GrabFocus(); 2719 2720 for( size_t nPos = 0; nPos < GetItemList()->size(); nPos++ ) 2721 { 2722 MenuItemData* pData = GetItemList()->GetDataFromPos( nPos ); 2723 if( pData->pSubMenu ) 2724 { 2725 pFloat->KillActivePopup(); 2726 } 2727 } 2728 pFloat->ChangeHighlightItem( ITEMPOS_INVALID, false ); 2729 } 2730 } 2731 2732 void PopupMenu::SetSelectedEntry( sal_uInt16 nId ) 2733 { 2734 nSelectedId = nId; 2735 sSelectedIdent = GetItemIdent(nId); 2736 } 2737 2738 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const Point& rPopupPos ) 2739 { 2740 return Execute( pExecWindow, tools::Rectangle( rPopupPos, rPopupPos ), PopupMenuFlags::ExecuteDown ); 2741 } 2742 2743 static FloatWinPopupFlags lcl_TranslateFlags(PopupMenuFlags nFlags) 2744 { 2745 FloatWinPopupFlags nPopupModeFlags = FloatWinPopupFlags::NONE; 2746 if ( nFlags & PopupMenuFlags::ExecuteDown ) 2747 nPopupModeFlags = FloatWinPopupFlags::Down; 2748 else if ( nFlags & PopupMenuFlags::ExecuteUp ) 2749 nPopupModeFlags = FloatWinPopupFlags::Up; 2750 else if ( nFlags & PopupMenuFlags::ExecuteRight ) 2751 nPopupModeFlags = FloatWinPopupFlags::Right; 2752 else 2753 nPopupModeFlags = FloatWinPopupFlags::Down; 2754 2755 if (nFlags & PopupMenuFlags::NoMouseUpClose ) // allow popup menus to stay open on mouse button up 2756 nPopupModeFlags |= FloatWinPopupFlags::NoMouseUpClose; // useful if the menu was opened on mousebutton down (eg toolbox configuration) 2757 2758 return nPopupModeFlags; 2759 } 2760 2761 sal_uInt16 PopupMenu::Execute( vcl::Window* pExecWindow, const tools::Rectangle& rRect, PopupMenuFlags nFlags ) 2762 { 2763 ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 ); 2764 return ImplExecute( pExecWindow, rRect, lcl_TranslateFlags(nFlags), nullptr, false ); 2765 } 2766 2767 void PopupMenu::ImplFlushPendingSelect() 2768 { 2769 // is there still Select? 2770 Menu* pSelect = ImplFindSelectMenu(); 2771 if (pSelect) 2772 { 2773 // Select should be called prior to leaving execute in a popup menu! 2774 Application::RemoveUserEvent( pSelect->nEventId ); 2775 pSelect->nEventId = nullptr; 2776 pSelect->Select(); 2777 } 2778 } 2779 2780 bool PopupMenu::PrepareRun(const VclPtr<vcl::Window>& pParentWin, tools::Rectangle& rRect, 2781 FloatWinPopupFlags& nPopupModeFlags, Menu* pSFrom, 2782 bool& bRealExecute, VclPtr<MenuFloatingWindow>& pWin) 2783 { 2784 bRealExecute = false; 2785 const sal_uInt16 nItemCount = GetItemCount(); 2786 if (!pSFrom && (vcl::IsInPopupMenuExecute() || !nItemCount)) 2787 return false; 2788 2789 mpLayoutData.reset(); 2790 2791 ImplSVData* pSVData = ImplGetSVData(); 2792 2793 pStartedFrom = pSFrom; 2794 nSelectedId = 0; 2795 sSelectedIdent.clear(); 2796 bCanceled = false; 2797 2798 VclPtr<vcl::Window> xFocusId; 2799 if ( !pStartedFrom ) 2800 { 2801 pSVData->mpWinData->mbNoDeactivate = true; 2802 xFocusId = Window::SaveFocus(); 2803 bRealExecute = true; 2804 } 2805 else 2806 { 2807 // assure that only one menu is open at a time 2808 if (pStartedFrom->IsMenuBar() && pSVData->mpWinData->mpFirstFloat) 2809 pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel 2810 | FloatWinPopupEndFlags::CloseAll); 2811 } 2812 2813 SAL_WARN_IF( ImplGetWindow(), "vcl", "Win?!" ); 2814 rRect.SetPos(pParentWin->OutputToScreenPixel(rRect.TopLeft())); 2815 2816 nPopupModeFlags |= FloatWinPopupFlags::NoKeyClose | FloatWinPopupFlags::AllMouseButtonClose | FloatWinPopupFlags::GrabFocus; 2817 if (bRealExecute) 2818 nPopupModeFlags |= FloatWinPopupFlags::NewLevel; 2819 2820 bInCallback = true; // set it here, if Activate overridden 2821 Activate(); 2822 bInCallback = false; 2823 2824 if (pParentWin->isDisposed()) 2825 return false; 2826 2827 if ( bCanceled || bKilled ) 2828 return false; 2829 2830 if (!nItemCount) 2831 return false; 2832 2833 // The flag MenuFlags::HideDisabledEntries is inherited. 2834 if ( pSFrom ) 2835 { 2836 if ( pSFrom->nMenuFlags & MenuFlags::HideDisabledEntries ) 2837 nMenuFlags |= MenuFlags::HideDisabledEntries; 2838 else 2839 nMenuFlags &= ~MenuFlags::HideDisabledEntries; 2840 } 2841 else 2842 // #102790# context menus shall never show disabled entries 2843 nMenuFlags |= MenuFlags::HideDisabledEntries; 2844 2845 sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount(); 2846 if ( !nVisibleEntries ) 2847 { 2848 OUString aTmpEntryText(VclResId(SV_RESID_STRING_NOSELECTIONPOSSIBLE)); 2849 2850 MenuItemData* pData = NbcInsertItem(0xFFFF, MenuItemBits::NONE, aTmpEntryText, nullptr, 0xFFFF, OString()); 2851 size_t nPos = 0; 2852 pData = pItemList->GetData( pData->nId, nPos ); 2853 assert(pData); 2854 if (pData) 2855 { 2856 pData->bIsTemporary = true; 2857 } 2858 ImplCallEventListeners(VclEventId::MenuSubmenuChanged, nPos); 2859 } 2860 2861 pWin = VclPtrInstance<MenuFloatingWindow>(this, pParentWin, WB_BORDER | WB_SYSTEMWINDOW); 2862 if (comphelper::LibreOfficeKit::isActive() && get_id() == "editviewspellmenu") 2863 { 2864 VclPtr<vcl::Window> xNotifierParent = pParentWin->GetParentWithLOKNotifier(); 2865 assert(xNotifierParent && xNotifierParent->GetLOKNotifier() && "editview menu without LOKNotifier"); 2866 pWin->SetLOKNotifier(xNotifierParent->GetLOKNotifier()); 2867 } 2868 2869 if( pSVData->maNWFData.mbFlatMenu ) 2870 pWin->SetBorderStyle( WindowBorderStyle::NOBORDER ); 2871 else 2872 pWin->SetBorderStyle( pWin->GetBorderStyle() | WindowBorderStyle::MENU ); 2873 pWindow = pWin; 2874 2875 Size aSz = ImplCalcSize( pWin ); 2876 2877 tools::Rectangle aDesktopRect(pWin->GetDesktopRectPixel()); 2878 if( Application::GetScreenCount() > 1 && Application::IsUnifiedDisplay() ) 2879 { 2880 vcl::Window* pDeskW = pWindow->GetWindow( GetWindowType::RealParent ); 2881 if( ! pDeskW ) 2882 pDeskW = pWindow; 2883 Point aDesktopTL(pDeskW->OutputToAbsoluteScreenPixel(rRect.TopLeft())); 2884 aDesktopRect = Application::GetScreenPosSizePixel( 2885 Application::GetBestScreen(tools::Rectangle(aDesktopTL, rRect.GetSize()))); 2886 } 2887 2888 tools::Long nMaxHeight = aDesktopRect.GetHeight(); 2889 2890 //rhbz#1021915. If a menu won't fit in the desired location the default 2891 //mode is to place it somewhere it will fit. e.g. above, left, right. For 2892 //some cases, e.g. menubars, it's desirable to limit the options to 2893 //above/below and force the menu to scroll if it won't fit 2894 if (nPopupModeFlags & FloatWinPopupFlags::NoHorzPlacement) 2895 { 2896 vcl::Window* pRef = pWin; 2897 if ( pRef->GetParent() ) 2898 pRef = pRef->GetParent(); 2899 2900 tools::Rectangle devRect(pRef->OutputToAbsoluteScreenPixel(rRect.TopLeft()), 2901 pRef->OutputToAbsoluteScreenPixel(rRect.BottomRight())); 2902 2903 tools::Long nHeightAbove = devRect.Top() - aDesktopRect.Top(); 2904 tools::Long nHeightBelow = aDesktopRect.Bottom() - devRect.Bottom(); 2905 nMaxHeight = std::min(nMaxHeight, std::max(nHeightAbove, nHeightBelow)); 2906 } 2907 2908 // In certain cases this might be misdetected with a height of 0, leading to menus not being displayed. 2909 // So assume that the available screen size matches at least the system requirements 2910 SAL_WARN_IF(nMaxHeight < 768, "vcl", 2911 "Available height misdetected as " << nMaxHeight 2912 << "px. Setting to 768px instead."); 2913 nMaxHeight = std::max(nMaxHeight, tools::Long(768)); 2914 2915 if (pStartedFrom && pStartedFrom->IsMenuBar()) 2916 nMaxHeight -= pParentWin->GetSizePixel().Height(); 2917 sal_Int32 nLeft, nTop, nRight, nBottom; 2918 pWindow->GetBorder( nLeft, nTop, nRight, nBottom ); 2919 nMaxHeight -= nTop+nBottom; 2920 if ( aSz.Height() > nMaxHeight ) 2921 { 2922 pWin->EnableScrollMenu( true ); 2923 sal_uInt16 nStart = ImplGetFirstVisible(); 2924 sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart ); 2925 aSz.setHeight( ImplCalcHeight( nEntries ) ); 2926 } 2927 2928 pWin->SetFocusId( xFocusId ); 2929 pWin->SetOutputSizePixel( aSz ); 2930 return true; 2931 } 2932 2933 bool PopupMenu::Run(const VclPtr<MenuFloatingWindow>& pWin, const bool bRealExecute, const bool bPreSelectFirst, 2934 const FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, const tools::Rectangle& rRect) 2935 { 2936 SalMenu* pMenu = ImplGetSalMenu(); 2937 if (pMenu && bRealExecute && pMenu->ShowNativePopupMenu(pWin, rRect, nPopupModeFlags)) 2938 return true; 2939 2940 pWin->StartPopupMode(rRect, nPopupModeFlags); 2941 if (pSFrom) 2942 { 2943 sal_uInt16 aPos; 2944 if (pSFrom->IsMenuBar()) 2945 aPos = static_cast<MenuBarWindow *>(pSFrom->pWindow.get())->GetHighlightedItem(); 2946 else 2947 aPos = static_cast<MenuFloatingWindow *>(pSFrom->pWindow.get())->GetHighlightedItem(); 2948 2949 pWin->SetPosInParent(aPos); // store position to be sent in SUBMENUDEACTIVATE 2950 pSFrom->ImplCallEventListeners(VclEventId::MenuSubmenuActivate, aPos); 2951 } 2952 2953 if ( bPreSelectFirst ) 2954 { 2955 for (size_t n = 0; n < static_cast<size_t>(GetItemCount()); n++) 2956 { 2957 MenuItemData* pData = pItemList->GetDataFromPos( n ); 2958 if ( ( pData->bEnabled 2959 || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus() 2960 ) 2961 && ( pData->eType != MenuItemType::SEPARATOR ) 2962 && ImplIsVisible( n ) 2963 && ImplIsSelectable( n ) 2964 ) 2965 { 2966 pWin->ChangeHighlightItem(n, false); 2967 break; 2968 } 2969 } 2970 } 2971 2972 if (bRealExecute) 2973 pWin->Execute(); 2974 2975 return false; 2976 } 2977 2978 void PopupMenu::FinishRun(const VclPtr<MenuFloatingWindow>& pWin, const VclPtr<vcl::Window>& pParentWin, const bool bRealExecute, const bool bIsNativeMenu) 2979 { 2980 if (!bRealExecute || pWin->isDisposed()) 2981 return; 2982 2983 if (!bIsNativeMenu) 2984 { 2985 VclPtr<vcl::Window> xFocusId = pWin->GetFocusId(); 2986 assert(xFocusId == nullptr && "Focus should already be restored by MenuFloatingWindow::End"); 2987 pWin->ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xFocusId); 2988 2989 if (nSelectedId) // then clean up .. ( otherwise done by TH ) 2990 { 2991 PopupMenu* pSub = pWin->GetActivePopup(); 2992 while ( pSub ) 2993 { 2994 pSub->ImplGetFloatingWindow()->EndPopupMode(); 2995 pSub = pSub->ImplGetFloatingWindow()->GetActivePopup(); 2996 } 2997 } 2998 } 2999 else 3000 pWin->StopExecute(); 3001 3002 pWin->doShutdown(); 3003 pWindow.disposeAndClear(); 3004 ImplClosePopupToolBox(pParentWin); 3005 ImplFlushPendingSelect(); 3006 } 3007 3008 sal_uInt16 PopupMenu::ImplExecute(const VclPtr<vcl::Window>& pParentWin, const tools::Rectangle& rRect, 3009 FloatWinPopupFlags nPopupModeFlags, Menu* pSFrom, bool bPreSelectFirst) 3010 { 3011 // tdf#126054 hold this until after function completes 3012 VclPtr<PopupMenu> xThis(this); 3013 bool bRealExecute = false; 3014 tools::Rectangle aRect(rRect); 3015 VclPtr<MenuFloatingWindow> pWin; 3016 if (!PrepareRun(pParentWin, aRect, nPopupModeFlags, pSFrom, bRealExecute, pWin)) 3017 return 0; 3018 const bool bNative = Run(pWin, bRealExecute, bPreSelectFirst, nPopupModeFlags, pSFrom, aRect); 3019 FinishRun(pWin, pParentWin, bRealExecute, bNative); 3020 return nSelectedId; 3021 } 3022 3023 sal_uInt16 PopupMenu::ImplCalcVisEntries( tools::Long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const 3024 { 3025 nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight(); 3026 3027 tools::Long nHeight = 0; 3028 size_t nEntries = pItemList->size(); 3029 sal_uInt16 nVisEntries = 0; 3030 3031 if ( pLastVisible ) 3032 *pLastVisible = 0; 3033 3034 for ( size_t n = nStartEntry; n < nEntries; n++ ) 3035 { 3036 if ( ImplIsVisible( n ) ) 3037 { 3038 MenuItemData* pData = pItemList->GetDataFromPos( n ); 3039 nHeight += pData->aSz.Height(); 3040 if ( nHeight > nMaxHeight ) 3041 break; 3042 3043 if ( pLastVisible ) 3044 *pLastVisible = n; 3045 nVisEntries++; 3046 } 3047 } 3048 return nVisEntries; 3049 } 3050 3051 tools::Long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const 3052 { 3053 tools::Long nHeight = 0; 3054 3055 sal_uInt16 nFound = 0; 3056 for ( size_t n = 0; ( nFound < nEntries ) && ( n < pItemList->size() ); n++ ) 3057 { 3058 if ( ImplIsVisible( static_cast<sal_uInt16>(n) ) ) 3059 { 3060 MenuItemData* pData = pItemList->GetDataFromPos( n ); 3061 nHeight += pData->aSz.Height(); 3062 nFound++; 3063 } 3064 } 3065 3066 nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight(); 3067 3068 return nHeight; 3069 } 3070 3071 css::uno::Reference<css::awt::XPopupMenu> PopupMenu::CreateMenuInterface() 3072 { 3073 UnoWrapperBase* pWrapper = UnoWrapperBase::GetUnoWrapper(); 3074 if ( pWrapper ) 3075 return pWrapper->CreateMenuInterface(this); 3076 return nullptr; 3077 } 3078 3079 ImplMenuDelData::ImplMenuDelData( const Menu* pMenu ) 3080 : mpNext( nullptr ) 3081 , mpMenu( nullptr ) 3082 { 3083 if( pMenu ) 3084 const_cast< Menu* >( pMenu )->ImplAddDel( *this ); 3085 } 3086 3087 ImplMenuDelData::~ImplMenuDelData() 3088 { 3089 if( mpMenu ) 3090 const_cast< Menu* >( mpMenu.get() )->ImplRemoveDel( *this ); 3091 } 3092 3093 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 3094
