Resolves: tdf#122241 support persona header in native gtk3 menubar

Change-Id: Ia4485ec4c342c86f40e8e0bb7e5e1af1a47bb9b9
Reviewed-on: https://gerrit.libreoffice.org/66725
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/include/vcl/menu.hxx b/include/vcl/menu.hxx
index 552a596..f3fb3e7 100644
--- a/include/vcl/menu.hxx
+++ b/include/vcl/menu.hxx
@@ -469,6 +469,9 @@ public:
    tools::Rectangle GetMenuBarButtonRectPixel( sal_uInt16 nId );
    void RemoveMenuBarButton( sal_uInt16 nId );
    void LayoutChanged();
    // get the height of the menubar, return the native menubar height if that is active or the vcl
    // one if not
    int GetMenuBarHeight() const;
};

inline MenuBar& MenuBar::operator=( const MenuBar& rMenu )
diff --git a/vcl/inc/salmenu.hxx b/vcl/inc/salmenu.hxx
index 169d887..0b6cd9e 100644
--- a/vcl/inc/salmenu.hxx
+++ b/vcl/inc/salmenu.hxx
@@ -90,6 +90,10 @@ public:
    // return Rectangle( Point( -1, -1 ), Size( 1, 1 ) ) if menu bar buttons implemented
    // but rectangle cannot be determined
    virtual tools::Rectangle GetMenuBarButtonRectPixel( sal_uInt16 i_nItemId, SalFrame* i_pReferenceFrame );

    virtual int GetMenuBarHeight() const;

    virtual void ApplyPersona();
};

#endif // INCLUDED_VCL_INC_SALMENU_HXX
diff --git a/vcl/inc/unx/gtk/gtksalmenu.hxx b/vcl/inc/unx/gtk/gtksalmenu.hxx
index 9ab4488..bdacee7 100644
--- a/vcl/inc/unx/gtk/gtksalmenu.hxx
+++ b/vcl/inc/unx/gtk/gtksalmenu.hxx
@@ -20,6 +20,7 @@

#include <salmenu.hxx>
#include <unx/gtk/gtkframe.hxx>
#include <unotools/tempfile.hxx>
#include <vcl/idle.hxx>

#if GTK_CHECK_VERSION(3,0,0)
@@ -40,6 +41,10 @@
#  endif
#endif

#if !GTK_CHECK_VERSION(3,0,0)
typedef void GtkCssProvider;
#endif

class MenuItemList;
class GtkSalMenuItem;

@@ -55,8 +60,12 @@ private:
    bool                            mbReturnFocusToDocument;
    bool                            mbAddedGrab;
    GtkWidget*                      mpMenuBarContainerWidget;
    std::unique_ptr<utl::TempFile>  mxPersonaImage;
    BitmapEx                        maPersonaBitmap;
    GtkWidget*                      mpMenuAllowShrinkWidget;
    GtkWidget*                      mpMenuBarWidget;
    GtkCssProvider*                 mpMenuBarContainerProvider;
    GtkCssProvider*                 mpMenuBarProvider;
    GtkWidget*                      mpCloseButton;
    VclPtr<Menu>                    mpVCLMenu;
    GtkSalMenu*                     mpParentSalMenu;
@@ -137,6 +146,8 @@ public:
    virtual void ShowCloseButton(bool bShow) override;
    virtual bool CanGetFocus() const override;
    virtual bool TakeFocus() override;
    virtual int GetMenuBarHeight() const override;
    virtual void ApplyPersona() override;
};

class GtkSalMenuItem : public SalMenuItem
diff --git a/vcl/source/app/salvtables.cxx b/vcl/source/app/salvtables.cxx
index 02f0741..4c343f8 100644
--- a/vcl/source/app/salvtables.cxx
+++ b/vcl/source/app/salvtables.cxx
@@ -205,6 +205,15 @@ tools::Rectangle SalMenu::GetMenuBarButtonRectPixel( sal_uInt16, SalFrame* )
    return tools::Rectangle();
}

int SalMenu::GetMenuBarHeight() const
{
    return 0;
}

void SalMenu::ApplyPersona()
{
}

SalMenuItem::~SalMenuItem()
{
}
diff --git a/vcl/source/window/dockingarea.cxx b/vcl/source/window/dockingarea.cxx
index 2f27d3e..fed3fa5 100644
--- a/vcl/source/window/dockingarea.cxx
+++ b/vcl/source/window/dockingarea.cxx
@@ -131,14 +131,9 @@ void DockingAreaWindow::ApplySettings(vcl::RenderContext& rRenderContext)

        // we need to shift the bitmap vertically so that it spans over the
        // menubar conveniently
        long nMenubarHeight = 0;
        SystemWindow* pSysWin = GetSystemWindow();
        if (pSysWin && pSysWin->GetMenuBar())
        {
            vcl::Window* pMenubarWin = pSysWin->GetMenuBar()->GetWindow();
            if (pMenubarWin)
                nMenubarHeight = pMenubarWin->GetOutputHeightPixel();
        }
        MenuBar* pMenuBar = pSysWin ? pSysWin->GetMenuBar() : nullptr;
        int nMenubarHeight = pMenuBar ? pMenuBar->GetMenuBarHeight() : 0;
        aWallpaper.SetRect(tools::Rectangle(Point(0, -nMenubarHeight),
                           Size(rRenderContext.GetOutputWidthPixel(),
                                rRenderContext.GetOutputHeightPixel() + nMenubarHeight)));
diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx
index 24b0c04a..4509ad4 100644
--- a/vcl/source/window/menu.cxx
+++ b/vcl/source/window/menu.cxx
@@ -2671,6 +2671,21 @@ bool MenuBar::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
    return pMenuWin && pMenuWin->HandleMenuButtonEvent(i_nButtonId);
}

int MenuBar::GetMenuBarHeight() const
{
    MenuBar* pMenuBar = const_cast<MenuBar*>(this);
    const SalMenu *pNativeMenu = pMenuBar->ImplGetSalMenu();
    int nMenubarHeight;
    if (pNativeMenu)
        nMenubarHeight = pNativeMenu->GetMenuBarHeight();
    else
    {
        vcl::Window* pMenubarWin = GetWindow();
        nMenubarHeight = pMenubarWin ? pMenubarWin->GetOutputHeightPixel() : 0;
    }
    return nMenubarHeight;
}

// bool PopupMenu::bAnyPopupInExecute = false;

MenuFloatingWindow * PopupMenu::ImplGetFloatingWindow() const {
diff --git a/vcl/source/window/menubarwindow.cxx b/vcl/source/window/menubarwindow.cxx
index f45cb3e..6607d44 100644
--- a/vcl/source/window/menubarwindow.cxx
+++ b/vcl/source/window/menubarwindow.cxx
@@ -1076,6 +1076,9 @@ void MenuBarWindow::ApplySettings(vcl::RenderContext& rRenderContext)
    SetPointFont(rRenderContext, rStyleSettings.GetMenuFont());

    const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader();
    SalMenu *pNativeMenu = pMenu ? pMenu->ImplGetSalMenu() : nullptr;
    if (pNativeMenu)
        pNativeMenu->ApplyPersona();
    if (!rPersonaBitmap.IsEmpty())
    {
        Wallpaper aWallpaper(rPersonaBitmap);
diff --git a/vcl/unx/gtk/gtksalmenu.cxx b/vcl/unx/gtk/gtksalmenu.cxx
index 8329c37..9ad1052 100644
--- a/vcl/unx/gtk/gtksalmenu.cxx
+++ b/vcl/unx/gtk/gtksalmenu.cxx
@@ -534,6 +534,8 @@ GtkSalMenu::GtkSalMenu( bool bMenuBar ) :
    mpMenuBarContainerWidget( nullptr ),
    mpMenuAllowShrinkWidget( nullptr ),
    mpMenuBarWidget( nullptr ),
    mpMenuBarContainerProvider( nullptr ),
    mpMenuBarProvider( nullptr ),
    mpCloseButton( nullptr ),
    mpVCLMenu( nullptr ),
    mpParentSalMenu( nullptr ),
@@ -834,6 +836,7 @@ void GtkSalMenu::CreateMenuBarWidget()
    gtk_grid_attach(GTK_GRID(mpMenuBarContainerWidget), mpMenuAllowShrinkWidget, 0, 0, 1, 1);

    mpMenuBarWidget = gtk_menu_bar_new_from_model(mpMenuModel);

    gtk_widget_insert_action_group(mpMenuBarWidget, "win", mpActionGroup);
    gtk_widget_set_hexpand(GTK_WIDGET(mpMenuBarWidget), true);
    gtk_widget_set_hexpand(mpMenuAllowShrinkWidget, true);
@@ -851,6 +854,67 @@ void GtkSalMenu::CreateMenuBarWidget()
#endif
}

void GtkSalMenu::ApplyPersona()
{
#if GTK_CHECK_VERSION(3,0,0)
    assert(mbMenuBar);
    // I'm dubious about the persona theming feature, but as it exists, lets try and support
    // it, apply the image to the mpMenuBarContainerWidget
    const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader();

    GtkStyleContext *pMenuBarContainerContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarContainerWidget));
    if (mpMenuBarContainerProvider)
    {
        gtk_style_context_remove_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider));
        mpMenuBarContainerProvider = nullptr;
    }
    GtkStyleContext *pMenuBarContext = gtk_widget_get_style_context(GTK_WIDGET(mpMenuBarWidget));
    if (mpMenuBarProvider)
    {
        gtk_style_context_remove_provider(pMenuBarContext, GTK_STYLE_PROVIDER(mpMenuBarProvider));
        mpMenuBarProvider = nullptr;
    }

    if (!rPersonaBitmap.IsEmpty())
    {
        if (maPersonaBitmap != rPersonaBitmap)
        {
            vcl::PNGWriter aPNGWriter(rPersonaBitmap);
            mxPersonaImage.reset(new utl::TempFile);
            mxPersonaImage->EnableKillingFile(true);
            SvStream* pStream = mxPersonaImage->GetStream(StreamMode::WRITE);
            aPNGWriter.Write(*pStream);
            mxPersonaImage->CloseStream();
        }

        mpMenuBarContainerProvider = gtk_css_provider_new();
        OUString aBuffer = "* { background-image: url(\"" + mxPersonaImage->GetURL() + "\"); background-position: top right; }";
        OString aResult = OUStringToOString(aBuffer, RTL_TEXTENCODING_UTF8);
        gtk_css_provider_load_from_data(mpMenuBarContainerProvider, aResult.getStr(), aResult.getLength(), nullptr);
        gtk_style_context_add_provider(pMenuBarContainerContext, GTK_STYLE_PROVIDER(mpMenuBarContainerProvider),
                                       GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);


        // force the menubar to be transparent when persona is active otherwise for
        // me the menubar becomes gray when its in the backdrop
        mpMenuBarProvider = gtk_css_provider_new();
        static const gchar data[] = "* { "
          "background-image: none;"
          "background-color: transparent;"
          "}";
        gtk_css_provider_load_from_data(mpMenuBarProvider, data, -1, nullptr);
        gtk_style_context_add_provider(pMenuBarContext,
                                       GTK_STYLE_PROVIDER(mpMenuBarProvider),
                                       GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }
    maPersonaBitmap = rPersonaBitmap;
#else
    (void)maPersonaBitmap;
    (void)mpMenuBarContainerProvider;
    (void)mpMenuBarProvider;
#endif
}

void GtkSalMenu::DestroyMenuBarWidget()
{
#if GTK_CHECK_VERSION(3,0,0)
@@ -1331,6 +1395,15 @@ void GtkSalMenu::GetSystemMenuData( SystemMenuData* )
{
}

int GtkSalMenu::GetMenuBarHeight() const
{
#if GTK_CHECK_VERSION(3,0,0)
    return mpMenuBarWidget ? gtk_widget_get_allocated_height(mpMenuBarWidget) : 0;
#else
    return 0;
#endif
}

/*
 * GtkSalMenuItem
 */