[Libreoffice-commits] core.git: framework/inc framework/source include/vcl vcl/inc vcl/source

Maxim Monastirsky momonasmon at gmail.com
Sun Feb 12 21:56:55 UTC 2017


 framework/inc/uielement/toolbarmanager.hxx            |   11 
 framework/source/uielement/addonstoolbarmanager.cxx   |    1 
 framework/source/uielement/popuptoolbarcontroller.cxx |   10 
 framework/source/uielement/subtoolbarcontroller.cxx   |    1 
 framework/source/uielement/toolbarmanager.cxx         |  217 +++++++++---------
 include/vcl/toolbox.hxx                               |   10 
 vcl/inc/toolbox.h                                     |    1 
 vcl/source/window/floatwin.cxx                        |   14 -
 vcl/source/window/toolbox.cxx                         |   20 +
 vcl/source/window/toolbox2.cxx                        |   48 +--
 10 files changed, 185 insertions(+), 148 deletions(-)

New commits:
commit 81d4fbc0daa54889ccb09e6a3fadff9c70d99448
Author: Maxim Monastirsky <momonasmon at gmail.com>
Date:   Sun Feb 12 22:14:48 2017 +0200

    tdf#42029 Use a floating toolbar to show clipped items
    
    Change-Id: I6b366f115258ef8497807163179d3e08ab3d5e6f
    Reviewed-on: https://gerrit.libreoffice.org/34180
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Maxim Monastirsky <momonasmon at gmail.com>

diff --git a/framework/inc/uielement/toolbarmanager.hxx b/framework/inc/uielement/toolbarmanager.hxx
index 739d22f..9876d6b 100644
--- a/framework/inc/uielement/toolbarmanager.hxx
+++ b/framework/inc/uielement/toolbarmanager.hxx
@@ -94,6 +94,7 @@ class ToolBarManager : public ToolbarManager_Base
         void CheckAndUpdateImages();
         virtual void RefreshImages();
         void FillToolbar( const css::uno::Reference< css::container::XIndexAccess >& rToolBarData );
+        void FillOverflowToolbar( ToolBox* pParent );
         void notifyRegisteredControllers( const OUString& aUIElementName, const OUString& aCommand );
         void Destroy();
 
@@ -113,8 +114,6 @@ class ToolBarManager : public ToolbarManager_Base
         };
 
     protected:
-        DECL_LINK( Command, CommandEvent const *, void );
-        PopupMenu * GetToolBarCustomMenu(ToolBox* pToolBar);
         DECL_LINK(Click, ToolBox *, void);
         DECL_LINK(DropdownClick, ToolBox *, void);
         DECL_LINK(DoubleClick, ToolBox *, void);
@@ -124,13 +123,16 @@ class ToolBarManager : public ToolbarManager_Base
         DECL_LINK( MiscOptionsChanged, LinkParamNone*, void );
 
         DECL_LINK( MenuButton, ToolBox *, void );
+        DECL_LINK( MenuPreExecute, ToolBox *, void );
         DECL_LINK( MenuSelect, Menu *, bool );
-        void MenuDeactivated();
         DECL_LINK(AsyncUpdateControllersHdl, Timer *, void);
+        DECL_LINK( OverflowEventListener, VclWindowEvent&, void );
         DECL_STATIC_LINK( ToolBarManager, ExecuteHdl_Impl, void*, void );
 
         virtual bool MenuItemAllowed( sal_uInt16 ) const;
 
+        void AddCustomizeMenuItems(ToolBox* pToolBar);
+        void InitImageManager();
         void RemoveControllers();
         void CreateControllers();
         void UpdateControllers();
@@ -138,7 +140,6 @@ class ToolBarManager : public ToolbarManager_Base
         void UpdateController( const css::uno::Reference< css::frame::XToolbarController >& xController);
         //end
         void AddFrameActionListener();
-        void ImplClearPopupMenu( ToolBox *pToolBar );
         void RequestImages();
         ToolBoxItemBits ConvertStyleToToolboxItemBits( sal_Int32 nStyle );
         css::uno::Reference< css::frame::XModel > GetModelFromFrame() const;
@@ -178,6 +179,8 @@ class ToolBarManager : public ToolbarManager_Base
         SubToolBarToSubToolBarControllerMap                          m_aSubToolBarControllerMap;
         Timer                                                        m_aAsyncUpdateControllersTimer;
         OUString                                                     m_sIconTheme;
+
+        rtl::Reference< ToolBarManager >                             m_aOverflowManager;
 };
 
 }
diff --git a/framework/source/uielement/addonstoolbarmanager.cxx b/framework/source/uielement/addonstoolbarmanager.cxx
index 9458d2f..4f8a4f6 100644
--- a/framework/source/uielement/addonstoolbarmanager.cxx
+++ b/framework/source/uielement/addonstoolbarmanager.cxx
@@ -82,7 +82,6 @@ AddonsToolBarManager::AddonsToolBarManager( const Reference< XComponentContext >
     m_pToolBar->SetSelectHdl( LINK( this, AddonsToolBarManager, Select) );
     m_pToolBar->SetClickHdl( LINK( this, AddonsToolBarManager, Click ) );
     m_pToolBar->SetDoubleClickHdl( LINK( this, AddonsToolBarManager, DoubleClick ) );
-    m_pToolBar->SetCommandHdl( LINK( this, AddonsToolBarManager, Command ) );
     m_pToolBar->SetStateChangedHdl( LINK( this, AddonsToolBarManager, StateChanged ) );
     m_pToolBar->SetDataChangedHdl( LINK( this, AddonsToolBarManager, DataChanged ) );
 }
diff --git a/framework/source/uielement/popuptoolbarcontroller.cxx b/framework/source/uielement/popuptoolbarcontroller.cxx
index b04c831..34b3a39 100644
--- a/framework/source/uielement/popuptoolbarcontroller.cxx
+++ b/framework/source/uielement/popuptoolbarcontroller.cxx
@@ -200,7 +200,13 @@ PopupMenuToolbarController::createPopupWindow()
 
     pToolBox->SetItemDown( m_nToolBoxId, true );
     WindowAlign eAlign( pToolBox->GetAlign() );
-    sal_uInt16 nId = m_xPopupMenu->execute(
+
+    // If the parent ToolBox is in popup mode (e.g. sub toolbar, overflow popup),
+    // its ToolBarManager can be disposed along with our controller, destroying
+    // m_xPopupMenu, while the latter still in execute. This should be fixed at a
+    // different level, for now just hold it here so it won't crash.
+    css::uno::Reference< css::awt::XPopupMenu > xPopupMenu ( m_xPopupMenu );
+    sal_uInt16 nId = xPopupMenu->execute(
         css::uno::Reference< css::awt::XWindowPeer >( getParent(), css::uno::UNO_QUERY ),
         VCLUnoHelper::ConvertToAWTRect( pToolBox->GetItemRect( m_nToolBoxId ) ),
         ( eAlign == WindowAlign::Top || eAlign == WindowAlign::Bottom ) ?
@@ -209,7 +215,7 @@ PopupMenuToolbarController::createPopupWindow()
     pToolBox->SetItemDown( m_nToolBoxId, false );
 
     if ( nId )
-        functionExecuted( m_xPopupMenu->getCommand( nId ) );
+        functionExecuted( xPopupMenu->getCommand( nId ) );
 
     return xRet;
 }
diff --git a/framework/source/uielement/subtoolbarcontroller.cxx b/framework/source/uielement/subtoolbarcontroller.cxx
index de8dffd..aa5450f 100644
--- a/framework/source/uielement/subtoolbarcontroller.cxx
+++ b/framework/source/uielement/subtoolbarcontroller.cxx
@@ -245,6 +245,7 @@ css::uno::Reference< css::awt::XWindow > SubToolBarController::createPopupWindow
                     // calc and set size for popup mode
                     Size aSize = pToolBar->CalcPopupWindowSizePixel();
                     pToolBar->SetSizePixel( aSize );
+                    pToolBar->SetMenuType( ToolBoxMenuType::NONE );
                     // open subtoolbox in popup mode
                     vcl::Window::GetDockingManager()->StartPopupMode( pToolBox, pToolBar );
                 }
diff --git a/framework/source/uielement/toolbarmanager.cxx b/framework/source/uielement/toolbarmanager.cxx
index 7331d24..c7af7cf 100644
--- a/framework/source/uielement/toolbarmanager.cxx
+++ b/framework/source/uielement/toolbarmanager.cxx
@@ -180,9 +180,10 @@ ToolBarManager::ToolBarManager( const Reference< XComponentContext >& rxContext,
     if ( !aCmdOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, "CreateDialog"))
          nMenuType |= ToolBoxMenuType::Customize;
 
-    m_pToolBar->SetCommandHdl( LINK( this, ToolBarManager, Command ) );
     m_pToolBar->SetMenuType( nMenuType );
     m_pToolBar->SetMenuButtonHdl( LINK( this, ToolBarManager, MenuButton ) );
+    m_pToolBar->SetMenuExecuteHdl( LINK( this, ToolBarManager, MenuPreExecute ) );
+    m_pToolBar->GetMenu()->SetSelectHdl( LINK( this, ToolBarManager, MenuSelect ) );
 
     // set name for testtool, the useful part is after the last '/'
     sal_Int32 idx = rResourceName.lastIndexOf('/');
@@ -243,7 +244,6 @@ void ToolBarManager::Destroy()
     m_pToolBar->SetDoubleClickHdl( Link<ToolBox *, void>() );
     m_pToolBar->SetStateChangedHdl( Link<StateChangedType const *, void>() );
     m_pToolBar->SetDataChangedHdl( Link<DataChangedEvent const *, void>() );
-    m_pToolBar->SetCommandHdl( Link<CommandEvent const *, void>() );
 
     m_pToolBar.clear();
 
@@ -493,7 +493,11 @@ void SAL_CALL ToolBarManager::dispose()
         }
         m_xModuleImageManager.clear();
 
-        ImplClearPopupMenu( m_pToolBar );
+        if ( m_aOverflowManager.is() )
+        {
+            m_aOverflowManager->dispose();
+            m_aOverflowManager.clear();
+        }
 
         // We have to destroy our toolbar instance now.
         Destroy();
@@ -919,18 +923,8 @@ ToolBoxItemBits ToolBarManager::ConvertStyleToToolboxItemBits( sal_Int32 nStyle
     return nItemBits;
 }
 
-void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContainer )
+void ToolBarManager::InitImageManager()
 {
-    OString aTbxName = OUStringToOString( m_aResourceName, RTL_TEXTENCODING_ASCII_US );
-    SAL_INFO( "fwk.uielement", "framework (cd100003) ::ToolBarManager::FillToolbar " << aTbxName.getStr() );
-
-    SolarMutexGuard g;
-
-    if ( m_bDisposed )
-        return;
-
-    sal_uInt16    nId( 1 );
-
     Reference< XModuleManager2 > xModuleManager = ModuleManager::create( m_xContext );
     if ( !m_xDocImageManager.is() )
     {
@@ -966,6 +960,19 @@ void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContaine
         m_xModuleImageManager->addConfigurationListener( Reference< XUIConfigurationListener >(
                                                             static_cast< OWeakObject* >( this ), UNO_QUERY ));
     }
+}
+
+void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContainer )
+{
+    OString aTbxName = OUStringToOString( m_aResourceName, RTL_TEXTENCODING_ASCII_US );
+    SAL_INFO( "fwk.uielement", "framework (cd100003) ::ToolBarManager::FillToolbar " << aTbxName.getStr() );
+
+    SolarMutexGuard g;
+
+    if ( m_bDisposed )
+        return;
+
+    InitImageManager();
 
     RemoveControllers();
 
@@ -974,6 +981,7 @@ void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContaine
     m_aControllerMap.clear();
     m_aCommandMap.clear();
 
+    sal_uInt16 nId( 1 );
     CommandInfo aCmdInfo;
     for ( sal_Int32 n = 0; n < rItemContainer->getCount(); n++ )
     {
@@ -1165,6 +1173,65 @@ void ToolBarManager::FillToolbar( const Reference< XIndexAccess >& rItemContaine
     }
 }
 
+void ToolBarManager::FillOverflowToolbar( ToolBox* pParent )
+{
+    CommandInfo aCmdInfo;
+    bool bInsertSeparator = false;
+    for ( sal_uInt16 i = 0; i < pParent->GetItemCount(); ++i )
+    {
+        sal_uInt16 nId = pParent->GetItemId( i );
+        if ( pParent->IsItemClipped( nId ) )
+        {
+            if ( bInsertSeparator )
+            {
+                m_pToolBar->InsertSeparator();
+                bInsertSeparator = false;
+            }
+
+            const OUString aCommandURL( pParent->GetItemCommand( nId ) );
+            m_pToolBar->InsertItem( nId, pParent->GetItemText( nId ) );
+            m_pToolBar->SetItemCommand( nId, aCommandURL );
+            m_pToolBar->SetQuickHelpText( nId, pParent->GetQuickHelpText( nId ) );
+
+            // Fill command map. It stores all our commands and from what
+            // image manager we got our image. So we can decide if we have to use an
+            // image from a notification message.
+            CommandToInfoMap::iterator pIter = m_aCommandMap.find( aCommandURL );
+            if ( pIter == m_aCommandMap.end())
+            {
+                aCmdInfo.nId = nId;
+                const CommandToInfoMap::value_type aValue( aCommandURL, aCmdInfo );
+                m_aCommandMap.insert( aValue );
+            }
+            else
+            {
+                pIter->second.aIds.push_back( nId );
+            }
+        }
+        else
+        {
+            ToolBoxItemType eType = pParent->GetItemType( i );
+            if ( m_pToolBar->GetItemCount() &&
+                ( eType == ToolBoxItemType::SEPARATOR || eType == ToolBoxItemType::BREAK ) )
+                bInsertSeparator = true;
+        }
+    }
+
+    InitImageManager();
+
+    // Request images for all toolbar items. Must be done before CreateControllers as
+    // some controllers need access to the image.
+    RequestImages();
+
+    // Create controllers after we set the images. There are controllers which needs
+    // an image at the toolbar at creation time!
+    CreateControllers();
+
+    // Notify controllers that they are now correctly initialized and can start listening
+    // toolbars that will open in popup mode will be updated immediately to avoid flickering
+    UpdateControllers();
+}
+
 void ToolBarManager::RequestImages()
 {
 
@@ -1297,39 +1364,6 @@ IMPL_LINK_NOARG(ToolBarManager, DoubleClick, ToolBox *, void)
     HandleClick(&XToolbarController::doubleClick);
 }
 
-void ToolBarManager::ImplClearPopupMenu( ToolBox *pToolBar )
-{
-    if ( m_bDisposed )
-        return;
-
-    ::PopupMenu *pMenu = pToolBar->GetMenu();
-    if (pMenu == nullptr) {
-        return;
-    }
-
-    // remove config entries from menu, so we have a clean menu to start with
-    // remove submenu first
-    pMenu->SetPopupMenu( 1, nullptr );
-
-    // remove all items that were not added by the toolbar itself
-    sal_uInt16 i;
-    for( i=0; i<pMenu->GetItemCount(); )
-    {
-        if( pMenu->GetItemId( i ) < TOOLBOX_MENUITEM_START
-            && pMenu->GetItemId( i ) != 0 ) // Don't remove separators (Id == 0)
-            pMenu->RemoveItem( i );
-        else
-            i++;
-    }
-}
-
-void ToolBarManager::MenuDeactivated()
-{
-    if (m_bDisposed)
-        return;
-    ImplClearPopupMenu(m_pToolBar);
-}
-
 Reference< XModel > ToolBarManager::GetModelFromFrame() const
 {
     Reference< XController > xController = m_xFrame->getController();
@@ -1363,14 +1397,10 @@ bool ToolBarManager::MenuItemAllowed( sal_uInt16 ) const
     return true;
 }
 
-::PopupMenu * ToolBarManager::GetToolBarCustomMenu(ToolBox* pToolBar)
+void ToolBarManager::AddCustomizeMenuItems(ToolBox* pToolBar)
 {
-    // update the list of hidden tool items first
-    pToolBar->UpdateCustomMenu();
-
     ::PopupMenu *pMenu = pToolBar->GetMenu();
-    // remove all entries before inserting new ones
-    ImplClearPopupMenu( pToolBar );
+
     // No config menu entries if command ".uno:ConfigureDialog" is not enabled
     Reference< XDispatch > xDisp;
     css::util::URL aURL;
@@ -1383,7 +1413,7 @@ bool ToolBarManager::MenuItemAllowed( sal_uInt16 ) const
             xDisp = xProv->queryDispatch( aURL, OUString(), 0 );
 
         if ( !xDisp.is() || IsPluginMode() )
-            return nullptr;
+            return;
     }
 
     // popup menu for quick customization
@@ -1485,60 +1515,57 @@ bool ToolBarManager::MenuItemAllowed( sal_uInt16 ) const
 
     if ( bHideDisabledEntries )
         pMenu->RemoveDisabledEntries();
-
-    return pMenu;
 }
 
-IMPL_LINK( ToolBarManager, Command, CommandEvent const *, pCmdEvt, void )
+IMPL_LINK( ToolBarManager, MenuButton, ToolBox*, pToolBar, void )
 {
     SolarMutexGuard g;
 
     if ( m_bDisposed )
         return;
-    if ( pCmdEvt->GetCommand() != CommandEventId::ContextMenu )
+
+    assert( !m_aOverflowManager.is() );
+
+    VclPtrInstance<ToolBox> pOverflowToolBar( pToolBar, WB_LINESPACING | WB_BORDER | WB_SCROLL );
+    pOverflowToolBar->SetOutStyle( pToolBar->GetOutStyle() );
+    m_aOverflowManager.set( new ToolBarManager( m_xContext, m_xFrame, OUString(), pOverflowToolBar ) );
+    m_aOverflowManager->FillOverflowToolbar( pToolBar );
+    pOverflowToolBar->SetMenuType( ToolBoxMenuType::NONE );
+
+    ::Size aActSize( pOverflowToolBar->GetSizePixel() );
+    ::Size aSize( pOverflowToolBar->CalcWindowSizePixel() );
+    aSize.Width() = aActSize.Width();
+    pOverflowToolBar->SetOutputSizePixel( aSize );
+
+    aSize = pOverflowToolBar->CalcPopupWindowSizePixel();
+    pOverflowToolBar->SetSizePixel( aSize );
+
+    pOverflowToolBar->EnableDocking();
+    pOverflowToolBar->AddEventListener( LINK( this, ToolBarManager, OverflowEventListener ) );
+    vcl::Window::GetDockingManager()->StartPopupMode( pToolBar, pOverflowToolBar, FloatWinPopupFlags::AllMouseButtonClose );
+}
+
+IMPL_LINK( ToolBarManager, OverflowEventListener, VclWindowEvent&, rWindowEvent, void )
+{
+    if ( rWindowEvent.GetId() != VclEventId::WindowEndPopupMode )
         return;
 
-    ::PopupMenu * pMenu = GetToolBarCustomMenu(m_pToolBar);
-    if (pMenu)
+    if ( m_aOverflowManager.is() )
     {
-        // We only want to handle events for the context menu, but not events
-        // on the toolbars overflow menu, hence we should only receive events
-        // from the toolbox menu when we are actually showing it as our context
-        // menu (the same menu retrieved with  GetMenu() is reused for both the
-        // overflow and context menus). If we set these Hdls permanently rather
-        // than just when the context menu is showing, then events are duplicated
-        // when the menu is being used as an overflow menu.
-        Menu *pManagerMenu = m_pToolBar->GetMenu();
-        pManagerMenu->SetSelectHdl( LINK( this, ToolBarManager, MenuSelect ) );
-
-        // make sure all disabled entries will be shown
-        pMenu->SetMenuFlags( pMenu->GetMenuFlags() | MenuFlags::AlwaysShowDisabledEntries );
-        ::Point aPoint( pCmdEvt->GetMousePosPixel() );
-        pMenu->Execute( m_pToolBar, aPoint );
-
-        //fdo#86820 We may have been disposed and so have a NULL m_pToolBar by
-        //executing a menu entry, e.g. inserting a chart replaces the toolbars
-        pManagerMenu = m_bDisposed ? nullptr : m_pToolBar->GetMenu();
-        if (pManagerMenu)
-        {
-            // Unlink our listeners again -- see above for why.
-            pManagerMenu->SetSelectHdl( Link<Menu*, bool>() );
-            MenuDeactivated();
-        }
+        m_aOverflowManager->dispose();
+        m_aOverflowManager.clear();
     }
 }
 
-IMPL_LINK( ToolBarManager, MenuButton, ToolBox*, pToolBar, void )
+IMPL_LINK( ToolBarManager, MenuPreExecute, ToolBox*, pToolBar, void )
 {
     SolarMutexGuard g;
 
     if ( m_bDisposed )
         return;
 
-    pToolBar->UpdateCustomMenu();
-    // remove all entries that do not come from the toolbar itself (fdo#38276)
-    ImplClearPopupMenu( pToolBar );
- }
+    AddCustomizeMenuItems( pToolBar );
+}
 
 IMPL_LINK( ToolBarManager, MenuSelect, Menu*, pMenu, bool )
 {
@@ -1700,21 +1727,9 @@ IMPL_LINK( ToolBarManager, MenuSelect, Menu*, pMenu, bool )
                         }
                     }
                 }
-                else
-                // The list of "hidden items", i.e. items which are disabled on
-                // the toolbar hence shown in the context menu for easier access,
-                // which are managed by the owning toolbar.
-                {
-                    m_pToolBar->TriggerItem( pMenu->GetCurItemId()
-                                             - TOOLBOX_MENUITEM_START );
-                }
                 break;
             }
         }
-
-        // remove all entries - deactivate is not reliable
-        // The method checks if we are already disposed and in that case does nothing!
-        ImplClearPopupMenu( m_pToolBar );
     }
 
     return true;
diff --git a/include/vcl/toolbox.hxx b/include/vcl/toolbox.hxx
index 3dbcb34..19730ec 100644
--- a/include/vcl/toolbox.hxx
+++ b/include/vcl/toolbox.hxx
@@ -160,7 +160,7 @@ private:
     Link<ToolBox *, void> maActivateHdl;
     Link<ToolBox *, void> maDeactivateHdl;
     Link<ToolBox *, void> maSelectHdl;
-    Link<CommandEvent const *, void> maCommandHandler;
+    Link<ToolBox *, void> maMenuButtonHdl;
     Link<StateChangedType const *, void> maStateChangedHandler;
     Link<DataChangedEvent const *, void> maDataChangedHandler;
     /** StatusListener. Notifies about rotated images etc */
@@ -354,6 +354,7 @@ public:
     sal_uInt16          GetItemId( const OUString& rCommand ) const;
     Rectangle           GetItemRect( sal_uInt16 nItemId ) const;
     Rectangle           GetItemPosRect( sal_uInt16 nPos ) const;
+    Rectangle           GetOverflowRect() const;
 
     /// Returns size of the bitmap / text that is inside this toolbox item.
     Size                GetItemContentSize( sal_uInt16 nItemId ) const;
@@ -402,6 +403,7 @@ public:
     /// Convenience method to hide items (via ShowItem).
     void                HideItem(sal_uInt16 nItemId) { ShowItem( nItemId, false ); }
 
+    bool                IsItemClipped( sal_uInt16 nItemId ) const;
     bool                IsItemVisible( sal_uInt16 nItemId ) const;
     bool                IsItemReallyVisible( sal_uInt16 nItemId ) const;
 
@@ -463,9 +465,9 @@ public:
     void                SetDeactivateHdl( const Link<ToolBox *, void>& rLink ) { maDeactivateHdl = rLink; }
     void                SetSelectHdl( const Link<ToolBox *, void>& rLink ) { maSelectHdl = rLink; }
     const Link<ToolBox *, void>& GetSelectHdl() const { return maSelectHdl; }
-    void                SetCommandHdl( const Link<CommandEvent const *, void>& aLink ) { maCommandHandler = aLink; }
     void                SetStateChangedHdl( const Link<StateChangedType const *, void>& aLink ) { maStateChangedHandler = aLink; }
     void                SetDataChangedHdl( const Link<DataChangedEvent const *, void>& aLink ) { maDataChangedHandler = aLink; }
+    void                SetMenuButtonHdl( const Link<ToolBox *, void>& rLink ) { maMenuButtonHdl = rLink; }
 
     // support for custom menu (eg for configuration)
     // note: this menu will also be used to display currently
@@ -479,10 +481,10 @@ public:
     bool                IsMenuEnabled() const;
     PopupMenu*          GetMenu() const;
     void                UpdateCustomMenu();
-    void                SetMenuButtonHdl( const Link<ToolBox *, void>& rLink );
+    void                SetMenuExecuteHdl( const Link<ToolBox *, void>& rLink );
 
     // open custommenu
-    void                ExecuteCustomMenu();
+    void                ExecuteCustomMenu( const Rectangle& rRect = Rectangle() );
 
     // allow Click Handler to distinguish between mouse and key input
     bool                IsKeyEvent() const { return mbIsKeyEvent; }
diff --git a/vcl/inc/toolbox.h b/vcl/inc/toolbox.h
index 7be02bf..5ea8b50 100644
--- a/vcl/inc/toolbox.h
+++ b/vcl/inc/toolbox.h
@@ -128,6 +128,7 @@ struct ImplToolBoxPrivateData
 
     // the optional custom menu
     VclPtr<PopupMenu>   mpMenu;
+    Rectangle       maMenuRect;
     ToolBoxMenuType maMenuType;
     ImplSVEvent *   mnEventId;
 
diff --git a/vcl/source/window/floatwin.cxx b/vcl/source/window/floatwin.cxx
index 3e9cd96..41e12ea 100644
--- a/vcl/source/window/floatwin.cxx
+++ b/vcl/source/window/floatwin.cxx
@@ -708,16 +708,16 @@ void FloatingWindow::StartPopupMode( const Rectangle& rRect, FloatWinPopupFlags
 
 void FloatingWindow::StartPopupMode( ToolBox* pBox, FloatWinPopupFlags nFlags )
 {
+    mpImplData->mpBox = pBox;
+
     // get selected button
     sal_uInt16 nItemId = pBox->GetDownItemId();
-    if ( !nItemId )
-        return;
 
-    mpImplData->mpBox = pBox;
-    pBox->ImplFloatControl( true, this );
+    if ( nItemId )
+        pBox->ImplFloatControl( true, this );
 
     // retrieve some data from the ToolBox
-    Rectangle aRect = pBox->GetItemRect( nItemId );
+    Rectangle aRect = nItemId ? pBox->GetItemRect( nItemId ) : pBox->GetOverflowRect();
     Point aPos;
     // convert to parent's screen coordinates
     aPos = GetParent()->OutputToScreenPixel( GetParent()->AbsoluteScreenToOutputPixel( pBox->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) ) );
@@ -793,6 +793,10 @@ void FloatingWindow::ImplEndPopupMode( FloatWinPopupEndFlags nFlags, const VclPt
     if (mpImplData && mpImplData->mpBox)
     {
         mpImplData->mpBox->ImplFloatControl( false, this );
+        // if the parent ToolBox is in popup mode, it should be closed too.
+        if ( GetDockingManager()->IsInPopupMode( mpImplData->mpBox ) )
+            nFlags |= FloatWinPopupEndFlags::CloseAll;
+
         mpImplData->mpBox = nullptr;
     }
 
diff --git a/vcl/source/window/toolbox.cxx b/vcl/source/window/toolbox.cxx
index 6cf2f6a..a6e3d56 100644
--- a/vcl/source/window/toolbox.cxx
+++ b/vcl/source/window/toolbox.cxx
@@ -3947,7 +3947,10 @@ void ToolBox::MouseButtonDown( const MouseEvent& rMEvt )
         // menu button hit ?
         if( mpData->maMenubuttonItem.maRect.IsInside( aMousePos ) && ImplHasClippedItems() )
         {
-            ExecuteCustomMenu();
+            if ( maMenuButtonHdl.IsSet() )
+                maMenuButtonHdl.Call( this );
+            else
+                ExecuteCustomMenu( mpData->maMenubuttonItem.maRect );
             return;
         }
 
@@ -4405,8 +4408,6 @@ bool ToolBox::EventNotify( NotifyEvent& rNEvt )
 
 void ToolBox::Command( const CommandEvent& rCEvt )
 {
-    maCommandHandler.Call( &rCEvt );
-
     // depict StartDrag on MouseButton/Left/Alt
     if ( (rCEvt.GetCommand() == CommandEventId::StartDrag) && rCEvt.IsMouseEvent() &&
          mbCustomize && !mbDragging && !mbDrag && !mbSelection &&
@@ -4462,6 +4463,11 @@ void ToolBox::Command( const CommandEvent& rCEvt )
             }
         }
     }
+    else if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
+    {
+        ExecuteCustomMenu( Rectangle( rCEvt.GetMousePosPixel(), rCEvt.GetMousePosPixel() ) );
+        return;
+    }
 
     DockingWindow::Command( rCEvt );
 }
@@ -5052,13 +5058,15 @@ bool ToolBox::ImplOpenItem( vcl::KeyCode aKeyCode )
       || ((nCode == KEY_UP   || nCode == KEY_DOWN)  && !IsHorizontal()) )
         return false;
 
-    if( IsMenuEnabled() && mpData->mbMenubuttonSelected )
+    if( mpData->mbMenubuttonSelected )
     {
         if( ImplCloseLastPopup( GetParent() ) )
             return bRet;
 
-        UpdateCustomMenu();
-        mpData->mnEventId = Application::PostUserEvent( LINK( this, ToolBox, ImplCallExecuteCustomMenu ), nullptr, true );
+        if ( maMenuButtonHdl.IsSet() )
+            maMenuButtonHdl.Call( this );
+        else
+            ExecuteCustomMenu( mpData->maMenubuttonItem.maRect );
     }
     else if( mnHighItemId &&  ImplGetItem( mnHighItemId ) &&
         (ImplGetItem( mnHighItemId )->mnBits & ToolBoxItemBits::DROPDOWN) )
diff --git a/vcl/source/window/toolbox2.cxx b/vcl/source/window/toolbox2.cxx
index 10cede7..ec58bbd 100644
--- a/vcl/source/window/toolbox2.cxx
+++ b/vcl/source/window/toolbox2.cxx
@@ -873,6 +873,11 @@ Rectangle ToolBox::GetItemPosRect( sal_uInt16 nPos ) const
         return Rectangle();
 }
 
+Rectangle ToolBox::GetOverflowRect() const
+{
+    return mpData->maMenubuttonItem.maRect;
+}
+
 bool ToolBox::ImplHasExternalMenubutton()
 {
     // check if the borderwindow (i.e. the decoration) provides the menu button
@@ -1317,6 +1322,16 @@ void ToolBox::ShowItem( sal_uInt16 nItemId, bool bVisible )
     }
 }
 
+bool ToolBox::IsItemClipped( sal_uInt16 nItemId ) const
+{
+    ImplToolItem* pItem = ImplGetItem( nItemId );
+
+    if ( pItem )
+        return pItem->IsClipped();
+    else
+        return false;
+}
+
 bool ToolBox::IsItemVisible( sal_uInt16 nItemId ) const
 {
     ImplToolItem* pItem = ImplGetItem( nItemId );
@@ -1564,7 +1579,7 @@ PopupMenu* ToolBox::GetMenu() const
     return mpData == nullptr ? nullptr : mpData->mpMenu;
 }
 
-void ToolBox::SetMenuButtonHdl( const Link<ToolBox *, void>& rLink )
+void ToolBox::SetMenuExecuteHdl( const Link<ToolBox *, void>& rLink )
 {
     mpData->maMenuButtonHdl = rLink;
 }
@@ -1604,19 +1619,7 @@ void ToolBox::UpdateCustomMenu()
         return;
 
     PopupMenu *pMenu = GetMenu();
-
-    sal_uInt16 i = 0;
-    // remove old entries
-    while( i < pMenu->GetItemCount() )
-    {
-        if( pMenu->GetItemId( i ) >= TOOLBOX_MENUITEM_START )
-        {
-            pMenu->RemoveItem( i );
-            i = 0;
-        }
-        else
-            i++;
-    }
+    pMenu->Clear();
 
     // add menu items: first the overflow items, then hidden items, both in the
     // order they would usually appear in the toolbar. Separators that would be
@@ -1680,13 +1683,6 @@ IMPL_LINK_NOARG(ToolBox, ImplCallExecuteCustomMenu, void*, void)
             // call button handler to allow for menu customization
             mpData->maMenuButtonHdl.Call( this );
 
-        // We specifically only register this event listener when executing our
-        // overflow menu (and remove it directly afterwards), as the same menu
-        // is reused for both the overflow menu (as managed here in ToolBox),
-        // but also by ToolBarManager for its context menu. If we leave event
-        // listeners alive beyond when the menu is showing in the desired mode
-        // then duplicate events can happen as the context menu "duplicates"
-        // items from the overflow menu, which both listeners would then act on.
         GetMenu()->AddEventListener( LINK( this, ToolBox, ImplCustomMenuListener ) );
 
         // make sure all disabled entries will be shown
@@ -1697,9 +1693,10 @@ IMPL_LINK_NOARG(ToolBox, ImplCallExecuteCustomMenu, void*, void)
         bool bBorderDel = false;
 
         VclPtr<vcl::Window> pWin = this;
-        Rectangle aMenuRect = mpData->maMenubuttonItem.maRect;
+        Rectangle aMenuRect = mpData->maMenuRect;
+        mpData->maMenuRect.SetEmpty();
         VclPtr<ImplBorderWindow> pBorderWin;
-        if( IsFloatingMode() )
+        if( aMenuRect.IsEmpty() && IsFloatingMode() )
         {
             // custom menu is placed in the decoration
             pBorderWin = dynamic_cast<ImplBorderWindow*>( GetWindow( GetWindowType::Border ) );
@@ -1732,13 +1729,14 @@ IMPL_LINK_NOARG(ToolBox, ImplCallExecuteCustomMenu, void*, void)
     }
 }
 
-void ToolBox::ExecuteCustomMenu()
+void ToolBox::ExecuteCustomMenu( const Rectangle& rRect )
 {
     if( IsMenuEnabled() )
     {
+        UpdateCustomMenu();
         // handle custom menu asynchronously
         // to avoid problems if the toolbox is closed during menu execute
-        UpdateCustomMenu();
+        mpData->maMenuRect = rRect;
         mpData->mnEventId = Application::PostUserEvent( LINK( this, ToolBox, ImplCallExecuteCustomMenu ), nullptr, true );
     }
 }


More information about the Libreoffice-commits mailing list