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

Caolán McNamara (via logerrit) logerrit at kemper.freedesktop.org
Wed Mar 3 09:09:16 UTC 2021


 extensions/inc/bitmaps.hlst                   |    2 
 extensions/source/update/ui/updatecheckui.cxx |  390 +-------------------------
 include/vcl/bubblewindow.hxx                  |   58 +++
 vcl/inc/bitmaps.hlst                          |    3 
 vcl/source/window/bubblewindow.cxx            |  347 ++++++++++++++++++++++-
 5 files changed, 423 insertions(+), 377 deletions(-)

New commits:
commit d2cd1fad73b97e62443a215c84b7b5d5ee74a818
Author:     Caolán McNamara <caolanm at redhat.com>
AuthorDate: Mon Mar 1 21:01:05 2021 +0000
Commit:     Caolán McNamara <caolanm at redhat.com>
CommitDate: Wed Mar 3 10:08:33 2021 +0100

    split and move MenuBar BubbleWindowManager to vcl
    
    Change-Id: I1d0f4b7d56845a77e979199917c5d9c849b76c8e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111798
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolanm at redhat.com>

diff --git a/extensions/inc/bitmaps.hlst b/extensions/inc/bitmaps.hlst
index 60a388e0c7b4..c07bc6ec16d1 100644
--- a/extensions/inc/bitmaps.hlst
+++ b/extensions/inc/bitmaps.hlst
@@ -38,8 +38,6 @@
 #define RID_EXTBMP_SCROLLBAR                "res/sx10768.png"
 #define RID_EXTBMP_SPINBUTTON               "res/sx10769.png"
 #define RID_EXTBMP_NAVIGATIONBAR            "res/sx10607.png"
-#define RID_UPDATE_AVAILABLE_16             "extensions/res/update/ui/onlineupdate_16.png"
-#define RID_UPDATE_AVAILABLE_26             "extensions/res/update/ui/onlineupdate_26.png"
 #define RID_SCANNER_HANDLE                  "extensions/res/scanner/handle.png"
 #define BMP_TABLE                           "res/sx03188.png"
 #define BMP_QUERY                           "res/sx03202.png"
diff --git a/extensions/source/update/ui/updatecheckui.cxx b/extensions/source/update/ui/updatecheckui.cxx
index c73db90d4a4f..b9582b465410 100644
--- a/extensions/source/update/ui/updatecheckui.cxx
+++ b/extensions/source/update/ui/updatecheckui.cxx
@@ -31,12 +31,8 @@
 #include <comphelper/processfactory.hxx>
 #include <unotools/resmgr.hxx>
 #include <vcl/image.hxx>
-#include <vcl/window.hxx>
 #include <vcl/bubblewindow.hxx>
 #include <vcl/timer.hxx>
-#include <vcl/idle.hxx>
-#include <vcl/lineinfo.hxx>
-#include <vcl/menu.hxx>
 #include <vcl/outdev.hxx>
 #include <vcl/weld.hxx>
 #include <vcl/settings.hxx>
@@ -59,62 +55,22 @@ using namespace ::com::sun::star;
 namespace
 {
 
-Image GetMenuBarIcon( MenuBar const * pMBar )
-{
-    OUString sResID;
-    vcl::Window *pMBarWin = pMBar->GetWindow();
-    sal_uInt32 nMBarHeight = 20;
-
-    if ( pMBarWin )
-        nMBarHeight = pMBarWin->GetOutputSizePixel().getHeight();
-
-    if (nMBarHeight >= 35)
-        sResID = RID_UPDATE_AVAILABLE_26;
-    else
-        sResID = RID_UPDATE_AVAILABLE_16;
-
-    return Image(StockImage::Yes, sResID);
-}
-
 class UpdateCheckUI : public ::cppu::WeakImplHelper
                         < lang::XServiceInfo, document::XDocumentEventListener, beans::XPropertySet >
 {
     uno::Reference< uno::XComponentContext > m_xContext;
     uno::Reference< task::XJob > mrJob;
-    OUString       maBubbleTitle;
-    OUString       maBubbleText;
     OUString       maBubbleImageURL;
-    Image               maBubbleImage;
-    VclPtr<BubbleWindow> mpBubbleWin;
-    VclPtr<SystemWindow> mpIconSysWin;
-    VclPtr<MenuBar>     mpIconMBar;
+    MenuBarUpdateIconManager maBubbleManager;
     std::locale         maSfxLocale;
-    Idle                maWaitIdle;
-    Timer               maTimeoutTimer;
-    Link<VclWindowEvent&,void> maWindowEventHdl;
-    Link<VclSimpleEvent&,void> maApplicationEventHdl;
-    bool                mbShowBubble;
-    bool                mbShowMenuIcon;
-    bool                mbBubbleChanged;
-    sal_uInt16              mnIconID;
 
 private:
-                    DECL_LINK(ClickHdl, MenuBar::MenuBarButtonCallbackArg&, bool);
-                    DECL_LINK(HighlightHdl, MenuBar::MenuBarButtonCallbackArg&, bool);
-                    DECL_LINK(WaitTimeOutHdl, Timer *, void);
-                    DECL_LINK(TimeOutHdl, Timer *, void);
-                    DECL_LINK(UserEventHdl, void *, void);
-                    DECL_LINK(WindowEventHdl, VclWindowEvent&, void);
-                    DECL_LINK(ApplicationEventHdl, VclSimpleEvent&, void);
-
-    VclPtr<BubbleWindow> GetBubbleWindow();
-    void            RemoveBubbleWindow( bool bRemoveIcon );
-    void            AddMenuBarIcon( SystemWindow* pSysWin, bool bAddEventHdl );
+                    DECL_LINK(ClickHdl, LinkParamNone*, void);
+
     Image           GetBubbleImage( OUString const &rURL );
 
 public:
     explicit        UpdateCheckUI(const uno::Reference<uno::XComponentContext>&);
-    virtual        ~UpdateCheckUI() override;
 
     // XServiceInfo
     virtual OUString SAL_CALL getImplementationName() override;
@@ -139,36 +95,18 @@ public:
                                                        const uno::Reference< beans::XVetoableChangeListener > & aListener) override;
 };
 
-UpdateCheckUI::UpdateCheckUI(const uno::Reference<uno::XComponentContext>& xContext) :
-      m_xContext(xContext)
-    , mpIconMBar( nullptr )
-    , mbShowBubble( false )
-    , mbShowMenuIcon( false )
-    , mbBubbleChanged( false )
-    , mnIconID( 0 )
+UpdateCheckUI::UpdateCheckUI(const uno::Reference<uno::XComponentContext>& xContext)
+    : m_xContext(xContext)
 {
     maSfxLocale = Translate::Create("sfx");
 
-    maBubbleImage = GetBubbleImage( maBubbleImageURL );
-
-    maWaitIdle.SetPriority( TaskPriority::LOWEST );
-    maWaitIdle.SetInvokeHandler( LINK( this, UpdateCheckUI, WaitTimeOutHdl ) );
-
-    maTimeoutTimer.SetTimeout( 10000 );
-    maTimeoutTimer.SetInvokeHandler( LINK( this, UpdateCheckUI, TimeOutHdl ) );
-
     uno::Reference< document::XDocumentEventBroadcaster > xBroadcaster( frame::theGlobalEventBroadcaster::get(m_xContext) );
     xBroadcaster->addDocumentEventListener( this );
 
-    maWindowEventHdl = LINK( this, UpdateCheckUI, WindowEventHdl );
-    maApplicationEventHdl = LINK( this, UpdateCheckUI, ApplicationEventHdl );
-    Application::AddEventListener( maApplicationEventHdl );
-}
+    SolarMutexGuard aGuard;
 
-UpdateCheckUI::~UpdateCheckUI()
-{
-    Application::RemoveEventListener( maApplicationEventHdl );
-    RemoveBubbleWindow( true );
+    maBubbleManager.SetBubbleImage(GetBubbleImage(maBubbleImageURL));
+    maBubbleManager.SetClickHdl(LINK(this, UpdateCheckUI, ClickHdl));
 }
 
 OUString SAL_CALL
@@ -225,83 +163,25 @@ Image UpdateCheckUI::GetBubbleImage( OUString const &rURL )
     return aImage;
 }
 
-
-void UpdateCheckUI::AddMenuBarIcon( SystemWindow *pSysWin, bool bAddEventHdl )
-{
-    if ( ! mbShowMenuIcon )
-        return;
-
-    SolarMutexGuard aGuard;
-
-    MenuBar *pActiveMBar = pSysWin->GetMenuBar();
-    if ( ( pSysWin != mpIconSysWin ) || ( pActiveMBar != mpIconMBar ) )
-    {
-        if ( bAddEventHdl && mpIconSysWin )
-            mpIconSysWin->RemoveEventListener( maWindowEventHdl );
-
-        RemoveBubbleWindow( true );
-
-        if ( pActiveMBar )
-        {
-            OUStringBuffer aBuf;
-            if( !maBubbleTitle.isEmpty() )
-                aBuf.append( maBubbleTitle );
-            if( !maBubbleText.isEmpty() )
-            {
-                if( !maBubbleTitle.isEmpty() )
-                    aBuf.append( "\n\n" );
-                aBuf.append( maBubbleText );
-            }
-
-            Image aImage = GetMenuBarIcon( pActiveMBar );
-            mnIconID = pActiveMBar->AddMenuBarButton( aImage,
-                                    LINK( this, UpdateCheckUI, ClickHdl ),
-                                    aBuf.makeStringAndClear()
-                                    );
-            pActiveMBar->SetMenuBarButtonHighlightHdl( mnIconID,
-                                    LINK( this, UpdateCheckUI, HighlightHdl ) );
-        }
-        mpIconMBar = pActiveMBar;
-        mpIconSysWin = pSysWin;
-        if ( bAddEventHdl && mpIconSysWin )
-            mpIconSysWin->AddEventListener( maWindowEventHdl );
-    }
-
-    if ( mbShowBubble && pActiveMBar )
-    {
-        mpBubbleWin = GetBubbleWindow();
-        if ( mpBubbleWin )
-        {
-            mpBubbleWin->Show();
-            maTimeoutTimer.Start();
-        }
-        mbShowBubble = false;
-    }
-}
-
-
 void SAL_CALL UpdateCheckUI::documentEventOccured(const document::DocumentEvent& rEvent)
 {
     SolarMutexGuard aGuard;
 
     if( rEvent.EventName == "OnPrepareViewClosing" )
     {
-        RemoveBubbleWindow( true );
+        maBubbleManager.RemoveBubbleWindow(true);
     }
 }
 
-
 void SAL_CALL UpdateCheckUI::disposing(const lang::EventObject&)
 {
 }
 
-
 uno::Reference< beans::XPropertySetInfo > UpdateCheckUI::getPropertySetInfo()
 {
     return nullptr;
 }
 
-
 void UpdateCheckUI::setPropertyValue(const OUString& rPropertyName,
                                      const uno::Any& rValue)
 {
@@ -311,32 +191,23 @@ void UpdateCheckUI::setPropertyValue(const OUString& rPropertyName,
 
     if( rPropertyName == PROPERTY_TITLE ) {
         rValue >>= aString;
-        if ( aString != maBubbleTitle ) {
-            maBubbleTitle = aString;
-            mbBubbleChanged = true;
-        }
+        maBubbleManager.SetBubbleTitle(aString);
     }
     else if( rPropertyName == PROPERTY_TEXT ) {
         rValue >>= aString;
-        if ( aString != maBubbleText ) {
-            maBubbleText = aString;
-            mbBubbleChanged = true;
-        }
+        maBubbleManager.SetBubbleText(aString);
     }
     else if( rPropertyName == PROPERTY_IMAGE ) {
         rValue >>= aString;
         if ( aString != maBubbleImageURL ) {
             maBubbleImageURL = aString;
-            maBubbleImage = GetBubbleImage( maBubbleImageURL );
-            mbBubbleChanged = true;
+            maBubbleManager.SetBubbleImage(GetBubbleImage(maBubbleImageURL));
         }
     }
     else if( rPropertyName == PROPERTY_SHOW_BUBBLE ) {
-        rValue >>= mbShowBubble;
-        if ( mbShowBubble )
-            Application::PostUserEvent( LINK( this, UpdateCheckUI, UserEventHdl ) );
-        else if ( mpBubbleWin )
-            mpBubbleWin->Show( false );
+        bool bShowBubble= false;
+        rValue >>= bShowBubble;
+        maBubbleManager.SetShowBubble(bShowBubble);
     }
     else if( rPropertyName == PROPERTY_CLICK_HDL ) {
         uno::Reference< task::XJob > aJob;
@@ -348,23 +219,12 @@ void UpdateCheckUI::setPropertyValue(const OUString& rPropertyName,
     else if (rPropertyName == PROPERTY_SHOW_MENUICON ) {
         bool bShowMenuIcon = false;
         rValue >>= bShowMenuIcon;
-        if ( bShowMenuIcon != mbShowMenuIcon )
-        {
-            mbShowMenuIcon = bShowMenuIcon;
-            if ( bShowMenuIcon )
-                Application::PostUserEvent( LINK( this, UpdateCheckUI, UserEventHdl ) );
-            else
-                RemoveBubbleWindow( true );
-        }
+        maBubbleManager.SetShowMenuIcon(bShowMenuIcon);
     }
     else
         throw beans::UnknownPropertyException(rPropertyName);
-
-    if ( mbBubbleChanged && mpBubbleWin )
-        mpBubbleWin->Show( false );
 }
 
-
 uno::Any UpdateCheckUI::getPropertyValue(const OUString& rPropertyName)
 {
     SolarMutexGuard aGuard;
@@ -372,17 +232,17 @@ uno::Any UpdateCheckUI::getPropertyValue(const OUString& rPropertyName)
     uno::Any aRet;
 
     if( rPropertyName == PROPERTY_TITLE )
-        aRet <<= maBubbleTitle;
+        aRet <<= maBubbleManager.GetBubbleTitle();
     else if( rPropertyName == PROPERTY_TEXT )
-        aRet <<= maBubbleText;
+        aRet <<= maBubbleManager.GetBubbleText();
     else if( rPropertyName == PROPERTY_SHOW_BUBBLE )
-        aRet <<= mbShowBubble;
+        aRet <<= maBubbleManager.GetShowBubble();
     else if( rPropertyName == PROPERTY_IMAGE )
         aRet <<= maBubbleImageURL;
     else if( rPropertyName == PROPERTY_CLICK_HDL )
         aRet <<= mrJob;
     else if( rPropertyName == PROPERTY_SHOW_MENUICON )
-        aRet <<= mbShowMenuIcon;
+        aRet <<= maBubbleManager.GetShowMenuIcon();
     else
         throw beans::UnknownPropertyException(rPropertyName);
 
@@ -403,91 +263,22 @@ void UpdateCheckUI::removePropertyChangeListener( const OUString& /*aPropertyNam
     //no bound properties
 }
 
-
 void UpdateCheckUI::addVetoableChangeListener( const OUString& /*aPropertyName*/,
                                                const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
 {
     //no vetoable properties
 }
 
-
 void UpdateCheckUI::removeVetoableChangeListener( const OUString& /*aPropertyName*/,
                                                   const uno::Reference< beans::XVetoableChangeListener > & /*aListener*/)
 {
     //no vetoable properties
 }
 
-
-VclPtr<BubbleWindow> UpdateCheckUI::GetBubbleWindow()
-{
-    if ( !mpIconSysWin )
-        return nullptr;
-
-    tools::Rectangle aIconRect = mpIconMBar->GetMenuBarButtonRectPixel( mnIconID );
-    if( aIconRect.IsEmpty() )
-        return nullptr;
-
-    auto pBubbleWin = mpBubbleWin;
-
-    if ( !pBubbleWin ) {
-        pBubbleWin = VclPtr<BubbleWindow>::Create( mpIconSysWin, maBubbleTitle,
-                                       maBubbleText, maBubbleImage );
-        mbBubbleChanged = false;
-    }
-    else if ( mbBubbleChanged ) {
-        pBubbleWin->SetTitleAndText( maBubbleTitle, maBubbleText,
-                                     maBubbleImage );
-        mbBubbleChanged = false;
-    }
-
-    Point aWinPos = aIconRect.BottomCenter();
-
-    pBubbleWin->SetTipPosPixel( aWinPos );
-
-    return pBubbleWin;
-}
-
-
-void UpdateCheckUI::RemoveBubbleWindow( bool bRemoveIcon )
+IMPL_LINK_NOARG(UpdateCheckUI, ClickHdl, LinkParamNone*, void)
 {
     SolarMutexGuard aGuard;
 
-    maWaitIdle.Stop();
-    maTimeoutTimer.Stop();
-
-    if ( mpBubbleWin )
-    {
-        mpBubbleWin.disposeAndClear();
-    }
-
-    if ( bRemoveIcon )
-    {
-        try {
-            if ( mpIconMBar && ( mnIconID != 0 ) )
-            {
-                mpIconMBar->RemoveMenuBarButton( mnIconID );
-                mpIconMBar = nullptr;
-                mnIconID = 0;
-            }
-        }
-        catch ( ... ) {
-            mpIconMBar = nullptr;
-            mnIconID = 0;
-        }
-
-        mpIconSysWin = nullptr;
-    }
-}
-
-
-IMPL_LINK_NOARG(UpdateCheckUI, ClickHdl, MenuBar::MenuBarButtonCallbackArg&, bool)
-{
-    SolarMutexGuard aGuard;
-
-    maWaitIdle.Stop();
-    if ( mpBubbleWin )
-        mpBubbleWin->Show( false );
-
     if ( mrJob.is() )
     {
         try {
@@ -501,145 +292,6 @@ IMPL_LINK_NOARG(UpdateCheckUI, ClickHdl, MenuBar::MenuBarButtonCallbackArg&, boo
             xErrorBox->run();
         }
     }
-
-    return false;
-}
-
-
-IMPL_LINK( UpdateCheckUI, HighlightHdl, MenuBar::MenuBarButtonCallbackArg&, rData, bool )
-{
-    if ( rData.bHighlight )
-        maWaitIdle.Start();
-    else
-        RemoveBubbleWindow( false );
-
-    return false;
-}
-
-
-IMPL_LINK_NOARG(UpdateCheckUI, WaitTimeOutHdl, Timer *, void)
-{
-    SolarMutexGuard aGuard;
-
-    mpBubbleWin = GetBubbleWindow();
-
-    if ( mpBubbleWin )
-    {
-        mpBubbleWin->Show();
-    }
-}
-
-
-IMPL_LINK_NOARG(UpdateCheckUI, TimeOutHdl, Timer *, void)
-{
-    RemoveBubbleWindow( false );
-}
-
-
-IMPL_LINK_NOARG(UpdateCheckUI, UserEventHdl, void*, void)
-{
-    SolarMutexGuard aGuard;
-
-    vcl::Window *pTopWin = Application::GetFirstTopLevelWindow();
-    vcl::Window *pActiveWin = Application::GetActiveTopWindow();
-    SystemWindow *pActiveSysWin = nullptr;
-
-    vcl::Window *pBubbleWin = nullptr;
-    if ( mpBubbleWin )
-        pBubbleWin = mpBubbleWin;
-
-    if ( pActiveWin && ( pActiveWin != pBubbleWin ) && pActiveWin->IsTopWindow() )
-        pActiveSysWin = pActiveWin->GetSystemWindow();
-
-    if ( pActiveWin == pBubbleWin )
-        pActiveSysWin = nullptr;
-
-    while ( !pActiveSysWin && pTopWin )
-    {
-        if ( ( pTopWin != pBubbleWin ) && pTopWin->IsTopWindow() )
-            pActiveSysWin = pTopWin->GetSystemWindow();
-        if ( !pActiveSysWin )
-            pTopWin = Application::GetNextTopLevelWindow( pTopWin );
-    }
-
-    if ( pActiveSysWin )
-        AddMenuBarIcon( pActiveSysWin, true );
-}
-
-
-IMPL_LINK( UpdateCheckUI, WindowEventHdl, VclWindowEvent&, rEvent, void )
-{
-    VclEventId nEventID = rEvent.GetId();
-
-    if ( VclEventId::ObjectDying == nEventID )
-    {
-        SolarMutexGuard aGuard;
-        if ( mpIconSysWin == rEvent.GetWindow() )
-        {
-            mpIconSysWin->RemoveEventListener( maWindowEventHdl );
-            RemoveBubbleWindow( true );
-        }
-    }
-    else if ( VclEventId::WindowMenubarAdded == nEventID )
-    {
-        SolarMutexGuard aGuard;
-        vcl::Window *pWindow = rEvent.GetWindow();
-        if ( pWindow )
-        {
-            SystemWindow *pSysWin = pWindow->GetSystemWindow();
-            if ( pSysWin )
-            {
-                AddMenuBarIcon( pSysWin, false );
-            }
-        }
-    }
-    else if ( VclEventId::WindowMenubarRemoved == nEventID )
-    {
-        SolarMutexGuard aGuard;
-        MenuBar *pMBar = static_cast<MenuBar*>(rEvent.GetData());
-        if ( pMBar && ( pMBar == mpIconMBar ) )
-            RemoveBubbleWindow( true );
-    }
-    else if ( ( nEventID == VclEventId::WindowMove ) ||
-              ( nEventID == VclEventId::WindowResize ) )
-    {
-        SolarMutexGuard aGuard;
-        if ( ( mpIconSysWin == rEvent.GetWindow() ) &&
-             mpBubbleWin && ( mpIconMBar != nullptr ) )
-        {
-            tools::Rectangle aIconRect = mpIconMBar->GetMenuBarButtonRectPixel( mnIconID );
-            Point aWinPos = aIconRect.BottomCenter();
-            mpBubbleWin->SetTipPosPixel( aWinPos );
-            if ( mpBubbleWin->IsVisible() )
-                mpBubbleWin->Show();    // This will recalc the screen position of the bubble
-        }
-    }
-}
-
-
-IMPL_LINK( UpdateCheckUI, ApplicationEventHdl, VclSimpleEvent&, rEvent, void)
-{
-    switch (rEvent.GetId())
-    {
-        case VclEventId::WindowShow:
-        case VclEventId::WindowActivate:
-        case VclEventId::WindowGetFocus: {
-            SolarMutexGuard aGuard;
-
-            vcl::Window *pWindow = static_cast< VclWindowEvent * >(&rEvent)->GetWindow();
-            if ( pWindow && pWindow->IsTopWindow() )
-            {
-                SystemWindow *pSysWin = pWindow->GetSystemWindow();
-                MenuBar *pMBar = pSysWin ? pSysWin->GetMenuBar() : nullptr;
-                if (pMBar)
-                {
-                    AddMenuBarIcon( pSysWin, true );
-                }
-            }
-            break;
-        }
-        default: break;
-    }
 }
 
 } // anonymous namespace
diff --git a/include/vcl/bubblewindow.hxx b/include/vcl/bubblewindow.hxx
index 0d9d873437e2..ae15c051ecc1 100644
--- a/include/vcl/bubblewindow.hxx
+++ b/include/vcl/bubblewindow.hxx
@@ -21,6 +21,7 @@
 
 #include <vcl/floatwin.hxx>
 #include <vcl/image.hxx>
+#include <vcl/menu.hxx>
 
 class VCL_DLLPUBLIC BubbleWindow final : public FloatingWindow
 {
@@ -52,4 +53,61 @@ public:
                                      const Image& rImage );
 };
 
+class VCL_DLLPUBLIC MenuBarUpdateIconManager
+{
+private:
+    OUString       maBubbleTitle;
+    OUString       maBubbleText;
+    OUString       maBubbleImageURL;
+    Image               maBubbleImage;
+    VclPtr<BubbleWindow> mpBubbleWin;
+    VclPtr<SystemWindow> mpIconSysWin;
+    VclPtr<MenuBar>     mpIconMBar;
+
+    Link<VclWindowEvent&,void> maWindowEventHdl;
+    Link<VclSimpleEvent&,void> maApplicationEventHdl;
+    Link<LinkParamNone*,void> maClickHdl;
+
+    Timer               maTimeoutTimer;
+    Idle                maWaitIdle;
+
+    sal_uInt16          mnIconID;
+
+    bool                mbShowMenuIcon;
+    bool                mbShowBubble;
+    bool                mbBubbleChanged;
+
+    DECL_LINK(UserEventHdl, void *, void);
+    DECL_LINK(TimeOutHdl, Timer *, void);
+    DECL_LINK(WindowEventHdl, VclWindowEvent&, void);
+    DECL_LINK(ApplicationEventHdl, VclSimpleEvent&, void);
+    DECL_LINK(WaitTimeOutHdl, Timer *, void);
+    DECL_LINK(ClickHdl, MenuBar::MenuBarButtonCallbackArg&, bool);
+    DECL_LINK(HighlightHdl, MenuBar::MenuBarButtonCallbackArg&, bool);
+
+    VclPtr<BubbleWindow> GetBubbleWindow();
+    void SetBubbleChanged();
+
+public:
+    MenuBarUpdateIconManager();
+    ~MenuBarUpdateIconManager();
+
+    void SetShowMenuIcon(bool bShowMenuIcon);
+    void SetShowBubble(bool bShowBubble);
+    void SetBubbleImage(const Image& rImage);
+    void SetBubbleTitle(const OUString& rTitle);
+    void SetBubbleText(const OUString& rText);
+
+    void SetClickHdl(const Link<LinkParamNone*,void>& rHdl) { maClickHdl = rHdl; }
+
+    bool GetShowMenuIcon() const { return mbShowMenuIcon; }
+    bool GetShowBubble() const { return mbShowBubble; }
+    OUString GetBubbleTitle() const { return maBubbleTitle; }
+    OUString GetBubbleText() const { return maBubbleText; }
+
+    void RemoveBubbleWindow(bool bRemoveIcon);
+
+    void AddMenuBarIcon(SystemWindow *pSysWin, bool bAddEventHdl);
+};
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/bitmaps.hlst b/vcl/inc/bitmaps.hlst
index 73e05c724ca2..02e3dea0f540 100644
--- a/vcl/inc/bitmaps.hlst
+++ b/vcl/inc/bitmaps.hlst
@@ -221,6 +221,9 @@
 
 #define CHEVRON                      "sfx2/res/chevron.png"
 
+#define RID_UPDATE_AVAILABLE_16      "extensions/res/update/ui/onlineupdate_16.png"
+#define RID_UPDATE_AVAILABLE_26      "extensions/res/update/ui/onlineupdate_26.png"
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/vcl/source/window/bubblewindow.cxx b/vcl/source/window/bubblewindow.cxx
index 65e96753d08e..736b66612b0f 100644
--- a/vcl/source/window/bubblewindow.cxx
+++ b/vcl/source/window/bubblewindow.cxx
@@ -17,10 +17,13 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <rtl/ustrbuf.hxx>
 #include <vcl/bubblewindow.hxx>
 #include <vcl/lineinfo.hxx>
 #include <vcl/settings.hxx>
 #include <vcl/svapp.hxx>
+#include <unotools/resmgr.hxx>
+#include <bitmaps.hlst>
 
 #define TIP_HEIGHT             15
 #define TIP_WIDTH               7
@@ -46,8 +49,6 @@ BubbleWindow::BubbleWindow( vcl::Window* pParent, const OUString& rTitle,
 
 void BubbleWindow::Resize()
 {
-    SolarMutexGuard aGuard;
-
     FloatingWindow::Resize();
 
     Size aSize = GetSizePixel();
@@ -87,8 +88,6 @@ void BubbleWindow::SetTitleAndText( const OUString& rTitle,
 
 void BubbleWindow::Paint(vcl::RenderContext& /*rRenderContext*/, const tools::Rectangle& /*rRect*/)
 {
-    SolarMutexGuard aGuard;
-
     LineInfo aThickLine( LineStyle::Solid, 2 );
 
     DrawPolyLine( maRectPoly, aThickLine );
@@ -130,8 +129,6 @@ void BubbleWindow::MouseButtonDown( const MouseEvent& )
 
 void BubbleWindow::Show( bool bVisible )
 {
-    SolarMutexGuard aGuard;
-
     if ( !bVisible )
     {
         FloatingWindow::Show( bVisible );
@@ -216,4 +213,342 @@ void BubbleWindow::RecalcTextRects()
     maTextRect.Move( 2*BUBBLE_BORDER, BUBBLE_BORDER + TIP_HEIGHT + maTitleRect.GetHeight() + aBoldFont.GetFontHeight() * 3 / 4 );
 }
 
+MenuBarUpdateIconManager::MenuBarUpdateIconManager()
+    : mnIconID (0)
+    , mbShowMenuIcon(false)
+    , mbShowBubble(false)
+    , mbBubbleChanged( false )
+{
+    maTimeoutTimer.SetTimeout( 10000 );
+    maTimeoutTimer.SetInvokeHandler(LINK(this, MenuBarUpdateIconManager, TimeOutHdl));
+
+    maWaitIdle.SetPriority( TaskPriority::LOWEST );
+    maWaitIdle.SetInvokeHandler(LINK(this, MenuBarUpdateIconManager, WaitTimeOutHdl));
+
+    maApplicationEventHdl = LINK(this, MenuBarUpdateIconManager, ApplicationEventHdl);
+    Application::AddEventListener( maApplicationEventHdl );
+
+    maWindowEventHdl = LINK(this, MenuBarUpdateIconManager, WindowEventHdl);
+}
+
+VclPtr<BubbleWindow> MenuBarUpdateIconManager::GetBubbleWindow()
+{
+    if ( !mpIconSysWin )
+        return nullptr;
+
+    tools::Rectangle aIconRect = mpIconMBar->GetMenuBarButtonRectPixel( mnIconID );
+    if( aIconRect.IsEmpty() )
+        return nullptr;
+
+    auto pBubbleWin = mpBubbleWin;
+
+    if ( !pBubbleWin ) {
+        pBubbleWin = VclPtr<BubbleWindow>::Create( mpIconSysWin, maBubbleTitle,
+                                       maBubbleText, maBubbleImage );
+        mbBubbleChanged = false;
+    }
+    else if ( mbBubbleChanged ) {
+        pBubbleWin->SetTitleAndText( maBubbleTitle, maBubbleText,
+                                     maBubbleImage );
+        mbBubbleChanged = false;
+    }
+
+    Point aWinPos = aIconRect.BottomCenter();
+
+    pBubbleWin->SetTipPosPixel( aWinPos );
+
+    return pBubbleWin;
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, TimeOutHdl, Timer *, void)
+{
+    RemoveBubbleWindow( false );
+}
+
+IMPL_LINK(MenuBarUpdateIconManager, WindowEventHdl, VclWindowEvent&, rEvent, void)
+{
+    VclEventId nEventID = rEvent.GetId();
+
+    if ( VclEventId::ObjectDying == nEventID )
+    {
+        if ( mpIconSysWin == rEvent.GetWindow() )
+        {
+            mpIconSysWin->RemoveEventListener( maWindowEventHdl );
+            RemoveBubbleWindow( true );
+        }
+    }
+    else if ( VclEventId::WindowMenubarAdded == nEventID )
+    {
+        vcl::Window *pWindow = rEvent.GetWindow();
+        if ( pWindow )
+        {
+            SystemWindow *pSysWin = pWindow->GetSystemWindow();
+            if ( pSysWin )
+            {
+                AddMenuBarIcon( pSysWin, false );
+            }
+        }
+    }
+    else if ( VclEventId::WindowMenubarRemoved == nEventID )
+    {
+        MenuBar *pMBar = static_cast<MenuBar*>(rEvent.GetData());
+        if ( pMBar && ( pMBar == mpIconMBar ) )
+            RemoveBubbleWindow( true );
+    }
+    else if ( ( nEventID == VclEventId::WindowMove ) ||
+              ( nEventID == VclEventId::WindowResize ) )
+    {
+        if ( ( mpIconSysWin == rEvent.GetWindow() ) &&
+             mpBubbleWin && ( mpIconMBar != nullptr ) )
+        {
+            tools::Rectangle aIconRect = mpIconMBar->GetMenuBarButtonRectPixel( mnIconID );
+            Point aWinPos = aIconRect.BottomCenter();
+            mpBubbleWin->SetTipPosPixel( aWinPos );
+            if ( mpBubbleWin->IsVisible() )
+                mpBubbleWin->Show();    // This will recalc the screen position of the bubble
+        }
+    }
+}
+
+IMPL_LINK(MenuBarUpdateIconManager, ApplicationEventHdl, VclSimpleEvent&, rEvent, void)
+{
+    switch (rEvent.GetId())
+    {
+        case VclEventId::WindowShow:
+        case VclEventId::WindowActivate:
+        case VclEventId::WindowGetFocus: {
+
+            vcl::Window *pWindow = static_cast< VclWindowEvent * >(&rEvent)->GetWindow();
+            if ( pWindow && pWindow->IsTopWindow() )
+            {
+                SystemWindow *pSysWin = pWindow->GetSystemWindow();
+                MenuBar *pMBar = pSysWin ? pSysWin->GetMenuBar() : nullptr;
+                if (pMBar)
+                {
+                    AddMenuBarIcon( pSysWin, true );
+                }
+            }
+            break;
+        }
+        default: break;
+    }
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, UserEventHdl, void*, void)
+{
+    vcl::Window *pTopWin = Application::GetFirstTopLevelWindow();
+    vcl::Window *pActiveWin = Application::GetActiveTopWindow();
+    SystemWindow *pActiveSysWin = nullptr;
+
+    vcl::Window *pBubbleWin = nullptr;
+    if ( mpBubbleWin )
+        pBubbleWin = mpBubbleWin;
+
+    if ( pActiveWin && ( pActiveWin != pBubbleWin ) && pActiveWin->IsTopWindow() )
+        pActiveSysWin = pActiveWin->GetSystemWindow();
+
+    if ( pActiveWin == pBubbleWin )
+        pActiveSysWin = nullptr;
+
+    while ( !pActiveSysWin && pTopWin )
+    {
+        if ( ( pTopWin != pBubbleWin ) && pTopWin->IsTopWindow() )
+            pActiveSysWin = pTopWin->GetSystemWindow();
+        if ( !pActiveSysWin )
+            pTopWin = Application::GetNextTopLevelWindow( pTopWin );
+    }
+
+    if ( pActiveSysWin )
+        AddMenuBarIcon( pActiveSysWin, true );
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, ClickHdl, MenuBar::MenuBarButtonCallbackArg&, bool)
+{
+    maWaitIdle.Stop();
+    if ( mpBubbleWin )
+        mpBubbleWin->Show( false );
+
+    maClickHdl.Call(nullptr);
+
+    return false;
+}
+
+IMPL_LINK(MenuBarUpdateIconManager, HighlightHdl, MenuBar::MenuBarButtonCallbackArg&, rData, bool)
+{
+    if ( rData.bHighlight )
+        maWaitIdle.Start();
+    else
+        RemoveBubbleWindow(false);
+
+    return false;
+}
+
+IMPL_LINK_NOARG(MenuBarUpdateIconManager, WaitTimeOutHdl, Timer *, void)
+{
+    mpBubbleWin = GetBubbleWindow();
+
+    if ( mpBubbleWin )
+    {
+        mpBubbleWin->Show();
+    }
+}
+
+MenuBarUpdateIconManager::~MenuBarUpdateIconManager()
+{
+    Application::RemoveEventListener( maApplicationEventHdl );
+
+    RemoveBubbleWindow(true);
+}
+
+void MenuBarUpdateIconManager::SetShowMenuIcon(bool bShowMenuIcon)
+{
+    if ( bShowMenuIcon != mbShowMenuIcon )
+    {
+        mbShowMenuIcon = bShowMenuIcon;
+        if ( bShowMenuIcon )
+            Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager, UserEventHdl));
+        else
+            RemoveBubbleWindow( true );
+    }
+}
+
+void MenuBarUpdateIconManager::SetShowBubble(bool bShowBubble)
+{
+    mbShowBubble = bShowBubble;
+    if ( mbShowBubble )
+        Application::PostUserEvent(LINK(this, MenuBarUpdateIconManager, UserEventHdl));
+    else if ( mpBubbleWin )
+        mpBubbleWin->Show( false );
+}
+
+void MenuBarUpdateIconManager::SetBubbleChanged()
+{
+    mbBubbleChanged = true;
+    if (mbBubbleChanged && mpBubbleWin)
+        mpBubbleWin->Show( false );
+}
+
+void MenuBarUpdateIconManager::SetBubbleImage(const Image& rImage)
+{
+    maBubbleImage = rImage;
+    SetBubbleChanged();
+}
+
+void MenuBarUpdateIconManager::SetBubbleTitle(const OUString& rTitle)
+{
+    if (rTitle != maBubbleTitle)
+    {
+        maBubbleTitle = rTitle;
+        SetBubbleChanged();
+    }
+}
+
+void MenuBarUpdateIconManager::SetBubbleText(const OUString& rText)
+{
+    if (rText != maBubbleText)
+    {
+        maBubbleText = rText;
+        SetBubbleChanged();
+    }
+}
+
+namespace {
+Image GetMenuBarIcon( MenuBar const * pMBar )
+{
+    OUString sResID;
+    vcl::Window *pMBarWin = pMBar->GetWindow();
+    sal_uInt32 nMBarHeight = 20;
+
+    if ( pMBarWin )
+        nMBarHeight = pMBarWin->GetOutputSizePixel().getHeight();
+
+    if (nMBarHeight >= 35)
+        sResID = RID_UPDATE_AVAILABLE_26;
+    else
+        sResID = RID_UPDATE_AVAILABLE_16;
+
+    return Image(StockImage::Yes, sResID);
+}
+}
+
+void MenuBarUpdateIconManager::AddMenuBarIcon(SystemWindow *pSysWin, bool bAddEventHdl)
+{
+    if ( ! mbShowMenuIcon )
+        return;
+
+    MenuBar *pActiveMBar = pSysWin->GetMenuBar();
+    if ( ( pSysWin != mpIconSysWin ) || ( pActiveMBar != mpIconMBar ) )
+    {
+        if ( bAddEventHdl && mpIconSysWin )
+            mpIconSysWin->RemoveEventListener( maWindowEventHdl );
+
+        RemoveBubbleWindow( true );
+
+        if ( pActiveMBar )
+        {
+            OUStringBuffer aBuf;
+            if( !maBubbleTitle.isEmpty() )
+                aBuf.append( maBubbleTitle );
+            if( !maBubbleText.isEmpty() )
+            {
+                if( !maBubbleTitle.isEmpty() )
+                    aBuf.append( "\n\n" );
+                aBuf.append( maBubbleText );
+            }
+
+            Image aImage = GetMenuBarIcon( pActiveMBar );
+            mnIconID = pActiveMBar->AddMenuBarButton( aImage,
+                                    LINK( this, MenuBarUpdateIconManager, ClickHdl ),
+                                    aBuf.makeStringAndClear()
+                                    );
+            pActiveMBar->SetMenuBarButtonHighlightHdl( mnIconID,
+                                    LINK( this, MenuBarUpdateIconManager, HighlightHdl ) );
+        }
+        mpIconMBar = pActiveMBar;
+        mpIconSysWin = pSysWin;
+        if ( bAddEventHdl && mpIconSysWin )
+            mpIconSysWin->AddEventListener( maWindowEventHdl );
+    }
+
+    if ( mbShowBubble && pActiveMBar )
+    {
+        mpBubbleWin = GetBubbleWindow();
+        if ( mpBubbleWin )
+        {
+            mpBubbleWin->Show();
+            maTimeoutTimer.Start();
+        }
+        mbShowBubble = false;
+    }
+}
+
+void MenuBarUpdateIconManager::RemoveBubbleWindow( bool bRemoveIcon )
+{
+    maWaitIdle.Stop();
+    maTimeoutTimer.Stop();
+
+    if ( mpBubbleWin )
+    {
+        mpBubbleWin.disposeAndClear();
+    }
+
+    if ( bRemoveIcon )
+    {
+        try {
+            if ( mpIconMBar && ( mnIconID != 0 ) )
+            {
+                mpIconMBar->RemoveMenuBarButton( mnIconID );
+                mpIconMBar = nullptr;
+                mnIconID = 0;
+            }
+        }
+        catch ( ... ) {
+            mpIconMBar = nullptr;
+            mnIconID = 0;
+        }
+
+        mpIconSysWin = nullptr;
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list