tdf#97926 Add UNO API for Infobar

This allows creating, updating and removing infobars from macros/extensions.

It also extends the infobar with a primary and a secondary text, so there can
be a bold summary at the beginning at the infobar with a longer text following
in normal letters.

Macro sample:
------------------------------------------------------------
Sub AddInfobar
	dim buttons(1) as new com.sun.star.beans.StringPair
	buttons(0).first = "Close doc"
	buttons(0).second = ".uno:CloseDoc"
	buttons(1).first = "Paste into doc"
	buttons(1).second = ".uno:Paste"
	ThisComponent.getCurrentController().appendInfobar("my", "Hello world", "Things happened. What now?", com.sun.star.frame.InfobarType.INFO, buttons, true)
End Sub

Sub UpdateInfobar
	ThisComponent.getCurrentController().updateInfobar("my", "WARNING","Do not read this message.", com.sun.star.frame.InfobarType.WARNING)
End Sub

Sub RemoveInfobar
	ThisComponent.getCurrentController().removeInfobar("my")
End Sub
------------------------------------------------------------

Change-Id: I5d0a223525845d23ffab17acdaa431e0eb783fec
Reviewed-on: https://gerrit.libreoffice.org/29816
Reviewed-by: Serge Krot (CIB) <Serge.Krot@cib.de>
Tested-by: Serge Krot (CIB) <Serge.Krot@cib.de>
Reviewed-by: Samuel Mehrbrodt <Samuel.Mehrbrodt@cib.de>
diff --git a/include/sfx2/classificationhelper.hxx b/include/sfx2/classificationhelper.hxx
index b372c8d..ed55fe9 100644
--- a/include/sfx2/classificationhelper.hxx
+++ b/include/sfx2/classificationhelper.hxx
@@ -73,7 +73,7 @@ public:
    OUString GetHigherClass(const OUString& first, const OUString& second);
    /// If GetImpactScale() and GetImpactLevel*() will return something meaningful.
    bool HasImpactLevel();
    InfoBarType GetImpactLevelType();
    InfobarType GetImpactLevelType();
    /// Larger value means more confidential.
    sal_Int32 GetImpactLevel();
    /// Comparing the GetImpactLevel() result is only meaningful when the impact scale is the same.
diff --git a/include/sfx2/infobar.hxx b/include/sfx2/infobar.hxx
index 4543189..a0dcdbd 100644
--- a/include/sfx2/infobar.hxx
+++ b/include/sfx2/infobar.hxx
@@ -18,11 +18,12 @@
#include <sfx2/dllapi.h>
#include <sfx2/childwin.hxx>

enum class InfoBarType {
    Info,
    Success,
    Warning,
    Danger
// These must match the values in offapi/com/sun/star/frame/InfobarType.idl
enum class InfobarType {
    INFO = 0,
    SUCCESS = 1,
    WARNING = 2,
    DANGER = 3
};

/** SfxChildWindow for positioning the InfoBar in the view.
@@ -50,27 +51,30 @@ class SFX2_DLLPUBLIC SfxInfoBarWindow final : public vcl::Window
{
    private:
        OUString const            m_sId;
        InfoBarType m_eType;
        InfobarType m_eType;
        VclPtr<FixedImage>        m_pImage;
        VclPtr<FixedText>           m_pMessage;
        VclPtr<FixedText> m_pPrimaryMessage;
        VclPtr<FixedText> m_pSecondaryMessage;
        VclPtr<Button>                m_pCloseBtn;
        std::vector< VclPtr<PushButton> >  m_aActionBtns;

        void SetForeAndBackgroundColors( InfoBarType eType );
        void SetForeAndBackgroundColors( InfobarType eType );

    public:
        SfxInfoBarWindow( vcl::Window* parent, const OUString& sId,
                          const OUString& sMessage,
                          InfoBarType infoBarType,
                          WinBits nMessageStyle);
                          const OUString& sPrimaryMessage,
                          const OUString& sSecondaryMessage,
                          InfobarType InfobarType,
                          bool bShowCloseButton, WinBits nMessageStyle);
        virtual ~SfxInfoBarWindow( ) override;
        virtual void dispose() override;

        const OUString& getId() const { return m_sId; }
        virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& ) override;
        virtual void Resize( ) override;
        void Update( const OUString& sNewMessage, InfoBarType eType );
        basegfx::BColor                m_aBackgroundColor;
        void Update(const OUString& sPrimaryMessage, const OUString& sSecondaryMessage,
                    InfobarType eType);
        basegfx::BColor m_aBackgroundColor;
        basegfx::BColor                m_aForegroundColor;

        /** Add button to Infobar.
@@ -95,9 +99,11 @@ class SfxInfoBarContainerWindow final : public vcl::Window
        virtual void dispose() override;

        VclPtr<SfxInfoBarWindow> appendInfoBar(const OUString& sId,
                                        const OUString& sMessage,
                                        InfoBarType ibType,
                                        WinBits nMessageStyle);
                                        const OUString& sPrimaryMessage,
                                        const OUString& sSecondaryMessage,
                                        InfobarType ibType,
                                        WinBits nMessageStyle,
                                        bool bShowCloseButton);
        VclPtr<SfxInfoBarWindow> getInfoBar(const OUString& sId);
        bool hasInfoBarWithID(const OUString& sId);
        void removeInfoBar(VclPtr<SfxInfoBarWindow> const & pInfoBar);
diff --git a/include/sfx2/sfxbasecontroller.hxx b/include/sfx2/sfxbasecontroller.hxx
index 4b7237f..95a082c 100644
--- a/include/sfx2/sfxbasecontroller.hxx
+++ b/include/sfx2/sfxbasecontroller.hxx
@@ -28,6 +28,7 @@
#include <com/sun/star/frame/XDispatchInformationProvider.hpp>
#include <com/sun/star/frame/XController2.hpp>
#include <com/sun/star/frame/XControllerBorder.hpp>
#include <com/sun/star/frame/XInfobarProvider.hpp>
#include <com/sun/star/frame/XTitle.hpp>
#include <com/sun/star/frame/XTitleChangeBroadcaster.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
@@ -70,6 +71,7 @@ typedef ::cppu::WeakImplHelper  <   css::frame::XController2
                                ,   css::ui::XContextMenuInterception
                                ,   css::awt::XUserInputInterception
                                ,   css::frame::XDispatchInformationProvider
                                ,   css::frame::XInfobarProvider
                                ,   css::frame::XTitle
                                ,   css::frame::XTitleChangeBroadcaster
                                ,   css::lang::XInitialization
@@ -175,6 +177,17 @@ public:
    // css::lang::XInitialization
    virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override;

    // XInfobarProvider
    virtual void SAL_CALL
    appendInfobar(const OUString& sId, const OUString& sPrimaryMessage,
                  const OUString& sSecondaryMessage, sal_Int32 aInfobarType,
                  const css::uno::Sequence<css::beans::StringPair>& actionButtons,
                  sal_Bool bShowCloseButton) override;
    virtual void SAL_CALL updateInfobar(const OUString& sId, const OUString& sPrimaryMessage,
                                        const OUString& sSecondaryMessage,
                                        sal_Int32 aInfobarType) override;
    virtual void SAL_CALL removeInfobar(const OUString& sId) override;

    // FIXME: TL needs this in sw/source/ui/uno/unotxdoc.cxx now;
    // either the _Impl name should vanish or there should be an "official" API
    SfxViewShell* GetViewShell_Impl() const;
diff --git a/include/sfx2/viewfrm.hxx b/include/sfx2/viewfrm.hxx
index 0b65d90..fe336ba 100644
--- a/include/sfx2/viewfrm.hxx
+++ b/include/sfx2/viewfrm.hxx
@@ -39,7 +39,7 @@ class Point;
class Size;
class SfxChildWindow;
class SfxInfoBarWindow;
enum class InfoBarType;
enum class InfobarType;

class SFX2_DLLPUBLIC SfxViewFrame: public SfxShell, public SfxListener
{
@@ -153,11 +153,14 @@ public:
        and position of each button will be changed: only the width will remain unchanged.
      */
    VclPtr<SfxInfoBarWindow> AppendInfoBar(const OUString& sId,
                                    const OUString& sMessage,
                                    InfoBarType aInfoBarType);
                                    const OUString& sPrimaryMessage,
                                    const OUString& sSecondaryMessage,
                                    InfobarType aInfobarType,
                                    bool bShowCloseButton=true);
    void              RemoveInfoBar(const OUString& sId);
    void              UpdateInfoBar(const OUString& sId,
                               const OUString& sMessage, InfoBarType eType);
    void              UpdateInfoBar(const OUString& sId, const OUString& sPrimaryMessage,
                                    const OUString& sSecondaryMessage,
                                    InfobarType eType);
    bool              HasInfoBarWithID(const OUString& sId);

    SAL_DLLPRIVATE void GetDocNumber_Impl();
diff --git a/offapi/UnoApi_offapi.mk b/offapi/UnoApi_offapi.mk
index 5fa55f8..81dbf3c 100644
--- a/offapi/UnoApi_offapi.mk
+++ b/offapi/UnoApi_offapi.mk
@@ -2584,6 +2584,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/frame,\
	FrameActionEvent \
	FrameSearchFlag \
	IllegalArgumentIOException \
	InfobarType \
	LayoutManagerEvents \
	TerminationVetoException \
	TitleChangedEvent \
@@ -2624,6 +2625,7 @@ $(eval $(call gb_UnoApi_add_idlfiles,offapi,com/sun/star/frame,\
	XFrames \
	XFramesSupplier \
	XGlobalEventBroadcaster \
	XInfobarProvider \
	XInterceptorInfo \
	XLayoutManager \
	XLayoutManager2 \
diff --git a/offapi/com/sun/star/frame/InfobarType.idl b/offapi/com/sun/star/frame/InfobarType.idl
new file mode 100644
index 0000000..6c410d6
--- /dev/null
+++ b/offapi/com/sun/star/frame/InfobarType.idl
@@ -0,0 +1,38 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef __com_sun_star_frame_InfobarType_idl__
#define __com_sun_star_frame_InfobarType_idl__

module com { module sun { module star { module frame {

/** Infobar types.

    @since LibreOffice 6.5
*/
constants InfobarType
{
    /** For information messages (color: light blue). */
    const long INFO = 0;

    /** For success notifications (color: light green). */
    const long SUCCESS = 1;

    /** For warning messages (color: orange). */
    const long WARNING = 2;

    /** For critical errors (color: red). */
    const long DANGER = 3;
};

}; }; }; };

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
\ No newline at end of file
diff --git a/offapi/com/sun/star/frame/XInfobarProvider.idl b/offapi/com/sun/star/frame/XInfobarProvider.idl
new file mode 100644
index 0000000..68278c5
--- /dev/null
+++ b/offapi/com/sun/star/frame/XInfobarProvider.idl
@@ -0,0 +1,104 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef __com_sun_star_frame_XInfobarProvider_idl__
#define __com_sun_star_frame_XInfobarProvider_idl__

#include <com/sun/star/beans/StringPair.idl>
#include <com/sun/star/frame/InfobarType.idl>
#include <com/sun/star/uno/XInterface.idl>

module com {  module sun {  module star {  module frame {

/** Allows to add Infobars to a frame.

    This interface can be obtained via com::sun::star::frame::XController.

    @since LibreOffice 6.5
 */
interface XInfobarProvider: uno::XInterface
{
    /** Creates and displays a new Infobar.

        @param id
            The ID by which this Infobar is recognized.
            You can remove the Infobar afterwards using this ID.

        @param primaryMessage
            The (short) primary message.
            Will appear at the start of the infobar in bold letters.
            May be empty.

        @param secondaryMessage
            The (longer) secondary message.
            Will appear in normal letters after the primaryMessage

        @param infobarType
            The type of the Infobar.
            See com::sun::star::frame::InfobarType for possible values.

        @param actionButtons
            A sequence of action buttons.
            The buttons will be added from Right to Left at the right side of the info bar.
            Each button is represented by a com::sun::star::beans::StringPair.
            StringPair::First represents the button label, while
            StringPair::Second represents the button URL which will be called on button click.
            The URL can be any URL, either external (http://libreoffice.org), or internal (.uno:Save),
            or from your extension (service:your.example.Extension?anyAction).

        @param showCloseButton
            Whether the Close (x) button is shown at the end of the Infobar.
            Set to false, when you don't want the user to close the Infobar.

        @throws com::sun::star::lang::IllegalArgumentException
            If an Infobar with the same ID already exists, or infobarType contains an invalid value.
     */
    void appendInfobar(
        [in] string id,
        [in] string primaryMessage,
        [in] string secondaryMessage,
        [in] long infobarType,
        [in] sequence<com::sun::star::beans::StringPair> actionButtons,
        [in] boolean showCloseButton)
        raises(com::sun::star::lang::IllegalArgumentException);

    /** Updates an existing Infobar.
        Use if you want to update only small parts of the Infobar.

        @see appendInfobar for parameter documentation.

        @throws com::sun::star::container::NoSuchElementException
            If no such Infobar exists (it might have been closed by the user already)
        @throws com::sun::star::lang::IllegalArgumentException
            If infobarType contains an invalid value.
     */
    void updateInfobar(
        [in] string id,
        [in] string primaryMessage,
        [in] string secondaryMessage,
        [in] long infobarType)
        raises(com::sun::star::container::NoSuchElementException);

    /** Removes an existing Infobar.

        @param id
            The ID which was used when creating this Infobar.

        @throws com::sun::star::container::NoSuchElementException
            If no such Infobar exists (it might have been closed by the user already)
     */
    void removeInfobar([in] string id) raises(com::sun::star::container::NoSuchElementException);
};


}; }; }; };

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
\ No newline at end of file
diff --git a/sc/source/ui/docshell/docsh4.cxx b/sc/source/ui/docshell/docsh4.cxx
index e1c9332..ced26c3 100644
--- a/sc/source/ui/docshell/docsh4.cxx
+++ b/sc/source/ui/docshell/docsh4.cxx
@@ -495,7 +495,7 @@ void ScDocShell::Execute( SfxRequest& rReq )
                    if (pViewFrame)
                    {
                        pViewFrame->RemoveInfoBar("enablecontent");
                        auto pInfoBar = pViewFrame->AppendInfoBar("enablecontent", ScResId(STR_RELOAD_TABLES), InfoBarType::Warning);
                        auto pInfoBar = pViewFrame->AppendInfoBar("enablecontent", "", ScResId(STR_RELOAD_TABLES), InfobarType::WARNING);
                        if (pInfoBar)
                        {
                            VclPtrInstance<PushButton> xBtn(&pViewFrame->GetWindow());
diff --git a/sfx2/source/dialog/infobar.cxx b/sfx2/source/dialog/infobar.cxx
index 84ab3d8..a1c9ef2 100644
--- a/sfx2/source/dialog/infobar.cxx
+++ b/sfx2/source/dialog/infobar.cxx
@@ -30,35 +30,36 @@ using namespace drawinglayer::processor2d;
using namespace drawinglayer::primitive2d;
using namespace drawinglayer::attribute;
using namespace basegfx;
using namespace css::frame;

namespace
{

const long INFO_BAR_BASE_HEIGHT = 40;

void GetInfoBarColors(InfoBarType ibType, BColor&  rBackgroundColor, BColor& rForegroundColor, BColor& rMessageColor)
void GetInfoBarColors(InfobarType ibType, BColor&  rBackgroundColor, BColor& rForegroundColor, BColor& rMessageColor)
{
    rMessageColor = basegfx::BColor(0.0, 0.0, 0.0);

    switch (ibType)
    {
    case InfoBarType::Info: // blue; #004785/0,71,133; #BDE5F8/189,229,248
    case InfobarType::INFO: // blue; #004785/0,71,133; #BDE5F8/189,229,248
        rBackgroundColor = basegfx::BColor(0.741, 0.898, 0.973);
        rForegroundColor = basegfx::BColor(0.0, 0.278, 0.522);
        break;
    case InfoBarType::Success: // green; #32550C/50,85,12; #DFF2BF/223,242,191
    case InfobarType::SUCCESS: // green; #32550C/50,85,12; #DFF2BF/223,242,191
        rBackgroundColor = basegfx::BColor(0.874,0.949,0.749);
        rForegroundColor = basegfx::BColor(0.196,0.333,0.047);
        break;
    case InfoBarType::Warning: // orange; #704300/112,67,0; #FEEFB3/254,239,179
    case InfobarType::WARNING: // orange; #704300/112,67,0; #FEEFB3/254,239,179
        rBackgroundColor = basegfx::BColor(0.996,0.937,0.702);
        rForegroundColor = basegfx::BColor(0.439,0.263,0.0);
        break;
    case InfoBarType::Danger: // red; #7A0006/122,0,6; #FFBABA/255,186,186
    case InfobarType::DANGER: // red; #7A0006/122,0,6; #FFBABA/255,186,186
        rBackgroundColor = basegfx::BColor(1.0,0.729,0.729);
        rForegroundColor = basegfx::BColor(0.478,0.0,0.024);
        break;
    }//switch
    }

    //remove this?
    const StyleSettings& rSettings = Application::GetSettings().GetStyleSettings();
@@ -69,26 +70,26 @@ void GetInfoBarColors(InfoBarType ibType, BColor&  rBackgroundColor, BColor& rFo
    }

}
OUString GetInfoBarIconName(InfoBarType ibType)
OUString GetInfoBarIconName(InfobarType ibType)
{

    OUString aRet;

    switch (ibType)
    {
    case InfoBarType::Info:
    case InfobarType::INFO:
       aRet = "vcl/res/infobox.svg";
       break;
    case InfoBarType::Success:
    case InfobarType::SUCCESS:
        aRet = "vcl/res/successbox.svg";
        break;
    case InfoBarType::Warning:
    case InfobarType::WARNING:
        aRet = "vcl/res/warningbox.svg";
        break;
    case InfoBarType::Danger:
    case InfobarType::DANGER:
        aRet = "vcl/res/errorbox.svg";
        break;
    }//switch
    }

    return aRet;
}
@@ -102,7 +103,7 @@ public:
    explicit SfxCloseButton(vcl::Window* pParent) : PushButton(pParent, 0)
    {
        basegfx::BColor aMessageColor;
        GetInfoBarColors(InfoBarType::Warning,m_aBackgroundColor,m_aForegroundColor,aMessageColor);
        GetInfoBarColors(InfobarType::WARNING, m_aBackgroundColor, m_aForegroundColor, aMessageColor);
    }

    virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override;
@@ -169,14 +170,17 @@ void SfxCloseButton::setForegroundColor(const basegfx::BColor& rColor)
} // anonymous namespace

SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window* pParent, const OUString& sId,
       const OUString& sMessage,
       InfoBarType ibType,
       const OUString& sPrimaryMessage,
       const OUString& sSecondaryMessage,
       InfobarType ibType,
       bool bShowCloseButton,
       WinBits nMessageStyle = WB_LEFT|WB_VCENTER) :
    Window(pParent, 0),
    m_sId(sId),
    m_eType(ibType),
    m_pImage(VclPtr<FixedImage>::Create(this, nMessageStyle)),
    m_pMessage(VclPtr<FixedText>::Create(this, nMessageStyle | WB_WORDBREAK)),
    m_pPrimaryMessage(VclPtr<FixedText>::Create(this, nMessageStyle | WB_WORDBREAK)),
    m_pSecondaryMessage(VclPtr<FixedText>::Create(this, nMessageStyle | WB_WORDBREAK)),
    m_pCloseBtn(VclPtr<SfxCloseButton>::Create(this)),
    m_aActionBtns()
{
@@ -189,11 +193,23 @@ SfxInfoBarWindow::SfxInfoBarWindow(vcl::Window* pParent, const OUString& sId,
    m_pImage->SetPaintTransparent(true);
    m_pImage->Show();

    m_pMessage->SetText(sMessage);
    m_pMessage->Show();
    vcl::Font aFont(m_pPrimaryMessage->GetControlFont());
    aFont.SetWeight(WEIGHT_BOLD);
    m_pPrimaryMessage->SetControlFont(aFont);
    if (!sPrimaryMessage.isEmpty())
    {
        m_pPrimaryMessage->SetText(sPrimaryMessage);
        m_pPrimaryMessage->Show();
    }

    m_pCloseBtn->SetClickHdl(LINK(this, SfxInfoBarWindow, CloseHandler));
    m_pCloseBtn->Show();
    m_pSecondaryMessage->SetText(sSecondaryMessage);
    m_pSecondaryMessage->Show();

    if (bShowCloseButton)
    {
        m_pCloseBtn->SetClickHdl(LINK(this, SfxInfoBarWindow, CloseHandler));
        m_pCloseBtn->Show();
    }

    EnableChildTransparentMode();

@@ -212,14 +228,15 @@ SfxInfoBarWindow::~SfxInfoBarWindow()
    disposeOnce();
}

void SfxInfoBarWindow::SetForeAndBackgroundColors(InfoBarType eType)
void SfxInfoBarWindow::SetForeAndBackgroundColors(InfobarType eType)
{
    basegfx::BColor aMessageColor;
    GetInfoBarColors(eType,m_aBackgroundColor,m_aForegroundColor,aMessageColor);

    static_cast<SfxCloseButton*>(m_pCloseBtn.get())->setBackgroundColor(m_aBackgroundColor);
    static_cast<SfxCloseButton*>(m_pCloseBtn.get())->setForegroundColor(m_aForegroundColor);
    m_pMessage->SetControlForeground(Color(aMessageColor));
    m_pPrimaryMessage->SetControlForeground(Color(aMessageColor));
    m_pSecondaryMessage->SetControlForeground(Color(aMessageColor));
}

void SfxInfoBarWindow::dispose()
@@ -228,7 +245,8 @@ void SfxInfoBarWindow::dispose()
        rxBtn.disposeAndClear();

    m_pImage.disposeAndClear();
    m_pMessage.disposeAndClear();
    m_pPrimaryMessage.disposeAndClear();
    m_pSecondaryMessage.disposeAndClear();
    m_pCloseBtn.disposeAndClear();
    m_aActionBtns.clear( );
    vcl::Window::dispose();
@@ -292,12 +310,19 @@ void SfxInfoBarWindow::Resize()
        nX -= nButtonGap;
    }

    Point aMessagePosition(32 * fScaleFactor + 10 * fScaleFactor, 10 * fScaleFactor);
    Point aPrimaryMessagePosition(32 * fScaleFactor + 10 * fScaleFactor, 10 * fScaleFactor);
    Point aSecondaryMessagePosition(aPrimaryMessagePosition);
    Size aMessageSize(nX - 35 * fScaleFactor, 20 * fScaleFactor);
    Size aActualSize = m_pMessage->CalcMinimumSize(aMessageSize.getWidth());
    long aMinimumHeight = m_pMessage->CalcMinimumSize().getHeight();
    Size aPrimaryTextSize = m_pPrimaryMessage->CalcMinimumSize(aMessageSize.getWidth());
    Size aSecondaryTextSize = m_pSecondaryMessage->CalcMinimumSize(aMessageSize.getWidth()
                                                                   - aPrimaryTextSize.getWidth());
    if (!m_pPrimaryMessage->GetText().isEmpty())
        aSecondaryMessagePosition.AdjustX(aPrimaryTextSize.getWidth() + 6 * fScaleFactor);

    long aExtraHeight = aActualSize.getHeight() - aMinimumHeight;
    long aMinimumHeight = std::max(m_pPrimaryMessage->CalcMinimumSize().getHeight(),
                                   m_pSecondaryMessage->CalcMinimumSize().getHeight());

    long aExtraHeight = aSecondaryTextSize.getHeight() - aMinimumHeight;

    // The message won't be legible and the window will get too high
    if (aMessageSize.getWidth() < 30)
@@ -305,13 +330,14 @@ void SfxInfoBarWindow::Resize()
        aExtraHeight = 0;
    }

    m_pMessage->SetPosSizePixel(aMessagePosition, aActualSize);
    m_pPrimaryMessage->SetPosSizePixel(aPrimaryMessagePosition, aPrimaryTextSize);
    m_pSecondaryMessage->SetPosSizePixel(aSecondaryMessagePosition, aSecondaryTextSize);
    m_pImage->SetPosSizePixel(Point(4, 4), Size(32 * fScaleFactor, 32 * fScaleFactor));

    SetPosSizePixel(Point(0, 0), Size(nWidth, INFO_BAR_BASE_HEIGHT * fScaleFactor + aExtraHeight * fScaleFactor));
}

void SfxInfoBarWindow::Update( const OUString &sNewMessage, InfoBarType eType )
void SfxInfoBarWindow::Update( const OUString& sPrimaryMessage, const OUString& sSecondaryMessage, InfobarType eType )
{
    if (m_eType != eType)
    {
@@ -320,7 +346,8 @@ void SfxInfoBarWindow::Update( const OUString &sNewMessage, InfoBarType eType )
        m_pImage->SetImage(Image(StockImage::Yes, GetInfoBarIconName(eType)));
    }

    m_pMessage->SetText( sNewMessage );
    m_pPrimaryMessage->SetText( sPrimaryMessage );
    m_pSecondaryMessage->SetText( sSecondaryMessage );
    Resize();
    Invalidate();
}
@@ -350,14 +377,15 @@ void SfxInfoBarContainerWindow::dispose()
    Window::dispose();
}

VclPtr<SfxInfoBarWindow> SfxInfoBarContainerWindow::appendInfoBar(const OUString& sId,
                                                           const OUString& sMessage,
                                                           InfoBarType ibType,
                                                           WinBits nMessageStyle)
VclPtr<SfxInfoBarWindow>
SfxInfoBarContainerWindow::appendInfoBar(const OUString& sId, const OUString& sPrimaryMessage,
                                         const OUString& sSecondaryMessage, InfobarType ibType,
                                         WinBits nMessageStyle, bool bShowCloseButton)
{
    Size aSize = GetSizePixel();

    auto pInfoBar = VclPtr<SfxInfoBarWindow>::Create(this, sId, sMessage, ibType, nMessageStyle);
    auto pInfoBar = VclPtr<SfxInfoBarWindow>::Create(this, sId, sPrimaryMessage, sSecondaryMessage,
                                                     ibType, nMessageStyle, bShowCloseButton);

    basegfx::BColor aBackgroundColor;
    basegfx::BColor aForegroundColor;
diff --git a/sfx2/source/doc/objserv.cxx b/sfx2/source/doc/objserv.cxx
index 512fe81..ff155a0 100644
--- a/sfx2/source/doc/objserv.cxx
+++ b/sfx2/source/doc/objserv.cxx
@@ -1298,35 +1298,35 @@ void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)
                if ( pFrame )
                {
                    SignatureState eState = GetDocumentSignatureState();
                    InfoBarType aInfoBarType(InfoBarType::Info);
                    InfobarType aInfobarType(InfobarType::INFO);
                    OUString sMessage("");

                    switch (eState)
                    {
                    case SignatureState::BROKEN:
                        sMessage = SfxResId(STR_SIGNATURE_BROKEN);
                        aInfoBarType = InfoBarType::Danger;
                        aInfobarType = InfobarType::DANGER;
                        break;
                    case SignatureState::INVALID:
                        sMessage = SfxResId(STR_SIGNATURE_INVALID);
                        // Warning only, I've tried Danger and it looked too scary
                        aInfoBarType = InfoBarType::Warning;
                        aInfobarType = InfobarType::WARNING;
                        break;
                    case SignatureState::NOTVALIDATED:
                        sMessage = SfxResId(STR_SIGNATURE_NOTVALIDATED);
                        aInfoBarType = InfoBarType::Warning;
                        aInfobarType = InfobarType::WARNING;
                        break;
                    case SignatureState::PARTIAL_OK:
                        sMessage = SfxResId(STR_SIGNATURE_PARTIAL_OK);
                        aInfoBarType = InfoBarType::Warning;
                        aInfobarType = InfobarType::WARNING;
                        break;
                    case SignatureState::OK:
                        sMessage = SfxResId(STR_SIGNATURE_OK);
                        aInfoBarType = InfoBarType::Info;
                        aInfobarType = InfobarType::INFO;
                        break;
                    case SignatureState::NOTVALIDATED_PARTIAL_OK:
                        sMessage = SfxResId(STR_SIGNATURE_NOTVALIDATED_PARTIAL_OK);
                        aInfoBarType = InfoBarType::Warning;
                        aInfobarType = InfobarType::WARNING;
                        break;
                    //FIXME SignatureState::Unknown, own message?
                    default:
@@ -1338,7 +1338,7 @@ void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)
                    {
                        if ( !sMessage.isEmpty() )
                        {
                            auto pInfoBar = pFrame->AppendInfoBar("signature", sMessage, aInfoBarType);
                            auto pInfoBar = pFrame->AppendInfoBar("signature", "", sMessage, aInfobarType);
                            if (pInfoBar == nullptr || pInfoBar->IsDisposed())
                                return;
                            VclPtrInstance<PushButton> xBtn(&(pFrame->GetWindow()));
@@ -1353,7 +1353,7 @@ void SfxObjectShell::GetState_Impl(SfxItemSet &rSet)
                        if ( eState == SignatureState::NOSIGNATURES )
                            pFrame->RemoveInfoBar("signature");
                        else
                            pFrame->UpdateInfoBar("signature", sMessage, aInfoBarType);
                            pFrame->UpdateInfoBar("signature", "", sMessage, aInfobarType);
                    }
                }

diff --git a/sfx2/source/view/classificationhelper.cxx b/sfx2/source/view/classificationhelper.cxx
index 57c6a50..66d27a0 100644
--- a/sfx2/source/view/classificationhelper.cxx
+++ b/sfx2/source/view/classificationhelper.cxx
@@ -693,11 +693,11 @@ bool SfxClassificationHelper::HasDocumentFooter()
    return it != rCategory.m_aLabels.end() && !it->second.isEmpty();
}

InfoBarType SfxClassificationHelper::GetImpactLevelType()
InfobarType SfxClassificationHelper::GetImpactLevelType()
{
    InfoBarType aRet;
    InfobarType aRet;

    aRet = InfoBarType::Warning;
    aRet = InfobarType::WARNING;

    auto itCategory = m_pImpl->m_aCategory.find(SfxClassificationPolicyType::IntellectualProperty);
    if (itCategory == m_pImpl->m_aCategory.end())
@@ -718,22 +718,22 @@ InfoBarType SfxClassificationHelper::GetImpactLevelType()
    if (aScale == "UK-Cabinet")
    {
        if (aLevel == "0")
            aRet = InfoBarType::Success;
            aRet = InfobarType::SUCCESS;
        else if (aLevel == "1")
            aRet = InfoBarType::Warning;
            aRet = InfobarType::WARNING;
        else if (aLevel == "2")
            aRet = InfoBarType::Warning;
            aRet = InfobarType::WARNING;
        else if (aLevel == "3")
            aRet = InfoBarType::Danger;
            aRet = InfobarType::DANGER;
    }
    else if (aScale == "FIPS-199")
    {
        if (aLevel == "Low")
            aRet = InfoBarType::Success;
            aRet = InfobarType::SUCCESS;
        else if (aLevel == "Moderate")
            aRet = InfoBarType::Warning;
            aRet = InfobarType::WARNING;
        else if (aLevel == "High")
            aRet = InfoBarType::Danger;
            aRet = InfobarType::DANGER;
    }
    return aRet;
}
@@ -890,7 +890,7 @@ void SfxClassificationHelper::UpdateInfobar(SfxViewFrame& rViewFrame)
        aMessage = aMessage.replaceFirst("%1", aBACName);

        rViewFrame.RemoveInfoBar("classification");
        rViewFrame.AppendInfoBar("classification", aMessage, GetImpactLevelType());
        rViewFrame.AppendInfoBar("classification", "", aMessage, GetImpactLevelType());
    }
}

diff --git a/sfx2/source/view/sfxbasecontroller.cxx b/sfx2/source/view/sfxbasecontroller.cxx
index 7812b56..f4cf676 100644
--- a/sfx2/source/view/sfxbasecontroller.cxx
+++ b/sfx2/source/view/sfxbasecontroller.cxx
@@ -99,6 +99,7 @@ using ::com::sun::star::frame::XDispatchProvider;
using ::com::sun::star::document::XViewDataSupplier;
using ::com::sun::star::container::XIndexAccess;
using ::com::sun::star::beans::PropertyValue;
using ::com::sun::star::beans::StringPair;
using ::com::sun::star::uno::Sequence;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::Exception;
@@ -1395,7 +1396,8 @@ void SfxBaseController::ShowInfoBars( )

    // Get the Frame and show the InfoBar if not checked out
    SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
    auto pInfoBar = pViewFrame->AppendInfoBar( "checkout", SfxResId( STR_NONCHECKEDOUT_DOCUMENT ), InfoBarType::Warning);
    auto pInfoBar = pViewFrame->AppendInfoBar("checkout", "", SfxResId(STR_NONCHECKEDOUT_DOCUMENT),
                                              InfobarType::WARNING);
    if (pInfoBar)
    {
        VclPtrInstance<PushButton> xBtn(&pViewFrame->GetWindow());
@@ -1469,4 +1471,64 @@ void SfxBaseController::initialize( const css::uno::Sequence< css::uno::Any >& /
{
}

void SAL_CALL SfxBaseController::appendInfobar(const OUString& sId, const OUString& sPrimaryMessage,
                                               const OUString& sSecondaryMessage,
                                               sal_Int32 aInfobarType,
                                               const Sequence<StringPair>& actionButtons,
                                               sal_Bool bShowCloseButton)
{
    if (aInfobarType < static_cast<sal_Int32>(InfobarType::INFO)
        || aInfobarType > static_cast<sal_Int32>(InfobarType::DANGER))
        throw lang::IllegalArgumentException("Undefined InfobarType: "
                                                 + OUString::number(aInfobarType),
                                             static_cast<::cppu::OWeakObject*>(this), 0);
    SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
    if (pViewFrame->HasInfoBarWithID(sId))
        throw lang::IllegalArgumentException("Infobar with ID '" + sId + "' already existing.",
                                             static_cast<::cppu::OWeakObject*>(this), 0);

    auto pInfoBar
        = pViewFrame->AppendInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
                                    static_cast<InfobarType>(aInfobarType), bShowCloseButton);
    if (!pInfoBar)
        throw uno::RuntimeException("Could not create Infobar");

    auto vActionButtons = comphelper::sequenceToContainer<std::vector<StringPair>>(actionButtons);
    for (auto& actionButton : vActionButtons)
    {
        if (actionButton.First.isEmpty() || actionButton.Second.isEmpty())
            continue;
        VclPtrInstance<PushButton> xBtn(&pViewFrame->GetWindow());
        xBtn->SetText(actionButton.First);
        xBtn->SetSizePixel(xBtn->GetOptimalSize());
        xBtn->SetCommandHandler(actionButton.Second);
        pInfoBar->addButton(xBtn);
    }
}

void SAL_CALL SfxBaseController::updateInfobar(const OUString& sId, const OUString& sPrimaryMessage,
                                               const OUString& sSecondaryMessage,
                                               sal_Int32 aInfobarType)
{
    if (aInfobarType < static_cast<sal_Int32>(InfobarType::INFO)
        || aInfobarType > static_cast<sal_Int32>(InfobarType::DANGER))
        throw lang::IllegalArgumentException("Undefined InfobarType: "
                                                 + OUString::number(aInfobarType),
                                             static_cast<::cppu::OWeakObject*>(this), 0);
    SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
    if (!pViewFrame->HasInfoBarWithID(sId))
        throw css::container::NoSuchElementException("Infobar with ID '" + sId + "' not found.");

    pViewFrame->UpdateInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
                              static_cast<InfobarType>(aInfobarType));
}

void SAL_CALL SfxBaseController::removeInfobar(const OUString& sId)
{
    SfxViewFrame* pViewFrame = m_pData->m_pViewShell->GetFrame();
    if (!pViewFrame->HasInfoBarWithID(sId))
        throw css::container::NoSuchElementException("Infobar with ID '" + sId + "' not found.");
    pViewFrame->RemoveInfoBar(sId);
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/view/viewfrm.cxx b/sfx2/source/view/viewfrm.cxx
index 6dccb52..80d6683 100644
--- a/sfx2/source/view/viewfrm.cxx
+++ b/sfx2/source/view/viewfrm.cxx
@@ -1300,7 +1300,7 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
                    sal_Int32 iLast = sLastVersion.getToken(0,'.').toInt32() * 10 + sLastVersion.getToken(1,'.').toInt32();
                    if ((iCurrent > iLast) && !Application::IsHeadlessModeEnabled() && !bIsUITest)
                    {
                        VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("whatsnew", SfxResId(STR_WHATSNEW_TEXT), InfoBarType::Info);
                        VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("whatsnew", "", SfxResId(STR_WHATSNEW_TEXT), InfobarType::INFO);
                        if (pInfoBar)
                        {
                            VclPtrInstance<PushButton> xWhatsNewButton(&GetWindow());
@@ -1342,7 +1342,7 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
                {
                    bUpdateLastTimeGetInvolvedShown = true;

                    VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("getinvolved", SfxResId(STR_GET_INVOLVED_TEXT), InfoBarType::Info);
                    VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("getinvolved", "", SfxResId(STR_GET_INVOLVED_TEXT), InfobarType::INFO);

                    VclPtrInstance<PushButton> xGetInvolvedButton(&GetWindow());
                    xGetInvolvedButton->SetText(SfxResId(STR_GET_INVOLVED_BUTTON));
@@ -1369,7 +1369,7 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
                {
                    bUpdateLastTimeDonateShown = true;

                    VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("donate", SfxResId(STR_DONATE_TEXT), InfoBarType::Info);
                    VclPtr<SfxInfoBarWindow> pInfoBar = AppendInfoBar("donate", "", SfxResId(STR_DONATE_TEXT), InfobarType::INFO);

                    VclPtrInstance<PushButton> xDonateButton(&GetWindow());
                    xDonateButton->SetText(SfxResId(STR_DONATE_BUTTON));
@@ -1396,7 +1396,7 @@ void SfxViewFrame::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
                {
                    bool bSignPDF = IsSignPDF(m_xObjSh);

                    auto pInfoBar = AppendInfoBar("readonly", SfxResId(bSignPDF ? STR_READONLY_PDF : STR_READONLY_DOCUMENT), InfoBarType::Info);
                    auto pInfoBar = AppendInfoBar("readonly", "", SfxResId(bSignPDF ? STR_READONLY_PDF : STR_READONLY_DOCUMENT), InfobarType::INFO);
                    if (pInfoBar)
                    {
                        if (bSignPDF)
@@ -3283,22 +3283,23 @@ void SfxViewFrame::SetViewFrame( SfxViewFrame* pFrame )
}

VclPtr<SfxInfoBarWindow> SfxViewFrame::AppendInfoBar(const OUString& sId,
                                               const OUString& sMessage,
                                               InfoBarType aInfoBarType)
                                               const OUString& sPrimaryMessage,
                                               const OUString& sSecondaryMessage,
                                               InfobarType aInfobarType, bool bShowCloseButton)
{
    SfxChildWindow* pChild = GetChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
    if (!pChild)
        return nullptr;

    SfxInfoBarContainerWindow* pInfoBarContainer = static_cast<SfxInfoBarContainerWindow*>(pChild->GetWindow());
    auto pInfoBar = pInfoBarContainer->appendInfoBar(sId, sMessage, aInfoBarType, WB_LEFT | WB_VCENTER);
    auto pInfoBar = pInfoBarContainer->appendInfoBar(sId, sPrimaryMessage, sSecondaryMessage,
                                                     aInfobarType, WB_LEFT | WB_VCENTER, bShowCloseButton);
    ShowChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
    return pInfoBar;
}

void SfxViewFrame::UpdateInfoBar( const OUString& sId,
                           const OUString& sMessage,
                           InfoBarType eType )
void SfxViewFrame::UpdateInfoBar(const OUString& sId, const OUString& sPrimaryMessage,
                                 const OUString& sSecondaryMessage, InfobarType eType)
{
    const sal_uInt16 nId = SfxInfoBarContainerChild::GetChildWindowId();

@@ -3313,7 +3314,7 @@ void SfxViewFrame::UpdateInfoBar( const OUString& sId,
        auto pInfoBar = pInfoBarContainer->getInfoBar(sId);

        if (pInfoBar)
             pInfoBar->Update(sMessage, eType);
             pInfoBar->Update(sPrimaryMessage, sSecondaryMessage, eType);
    }
}