Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  salnativewidgets-gtk.cxx

  Sprache: C
 

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * 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/.
 */


#include <sal/config.h>
#include <sal/log.hxx>
#include <osl/module.h>

#include <config_cairo_canvas.h>

#include <unx/gtk/gtkframe.hxx>
#include <unx/gtk/gtkdata.hxx>
#include <unx/gtk/gtkinst.hxx>
#include <unx/gtk/gtkgdi.hxx>
#include <unx/gtk/gtkbackend.hxx>
#include <vcl/BitmapTools.hxx>
#include <vcl/CairoFormats.hxx>
#include <vcl/decoview.hxx>
#include <vcl/settings.hxx>
#include <unx/fontmanager.hxx>
#include <o3tl/string_view.hxx>
#include <scrollbarvalue.hxx>

#include <IconThemeSelector.hxx>
#include "custom-theme.hxx"
#include <vcl/themecolors.hxx>
#include "gtkcairo.hxx"

GtkCssProvider*  GtkSalGraphics::mpCustomThemeProvider = nullptr;
GtkStyleContext* GtkSalGraphics::mpWindowStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpLinkButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpEntryStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpTextViewStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpVScrollbarStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpVScrollbarContentsStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpVScrollbarTroughStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpVScrollbarSliderStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpVScrollbarButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpHScrollbarStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpHScrollbarContentsStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpHScrollbarTroughStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpHScrollbarSliderStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpHScrollbarButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpToolbarStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpToolButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpToolbarSeparatorStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpCheckButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpCheckButtonCheckStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpRadioButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpRadioButtonRadioStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpSpinStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpSpinUpStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpSpinDownStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpComboboxStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpComboboxBoxStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpComboboxEntryStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpComboboxButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpComboboxButtonBoxStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpComboboxButtonArrowStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpListboxStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpListboxBoxStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpListboxButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpListboxButtonBoxStyle= nullptr;
GtkStyleContext* GtkSalGraphics::mpListboxButtonArrowStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpFrameInStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpFrameOutStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpFixedHoriLineStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpFixedVertLineStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpTreeHeaderButtonStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpProgressBarStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpProgressBarTroughStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpProgressBarProgressStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookStackStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookHeaderStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookHeaderTabsStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookHeaderTabsTabStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookHeaderTabsTabLabelStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookHeaderTabsTabActiveLabelStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpNotebookHeaderTabsTabHoverLabelStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpMenuBarStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpMenuBarItemStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpMenuWindowStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpMenuStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpMenuItemStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpMenuItemArrowStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpMenuItemLabelStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpCheckMenuItemStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpCheckMenuItemCheckStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpRadioMenuItemStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpRadioMenuItemRadioStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpSeparatorMenuItemStyle = nullptr;
GtkStyleContext* GtkSalGraphics::mpSeparatorMenuItemSeparatorStyle = nullptr;
gint GtkSalGraphics::mnVerticalSeparatorMinWidth = 0;

#if !GTK_CHECK_VERSION(4, 0, 0)
static void style_context_get_margin(GtkStyleContext *pContext, GtkBorder *pMargin)
{
#if GTK_CHECK_VERSION(4, 0, 0)
    gtk_style_context_get_margin(pContext, pMargin);
#else
    gtk_style_context_get_margin(pContext, gtk_style_context_get_state(pContext), pMargin);
#endif
}
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
static void style_context_get_border(GtkStyleContext* pContext, GtkBorder* pBorder)
{
#if GTK_CHECK_VERSION(4, 0, 0)
    gtk_style_context_get_border(pContext, pBorder);
#else
    gtk_style_context_get_border(pContext, gtk_style_context_get_state(pContext), pBorder);
#endif
}
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
static void style_context_get_padding(GtkStyleContext* pContext, GtkBorder* pPadding)
{
#if GTK_CHECK_VERSION(4, 0, 0)
    gtk_style_context_get_padding(pContext, pPadding);
#else
    gtk_style_context_get_padding(pContext, gtk_style_context_get_state(pContext), pPadding);
#endif
}
#endif

bool GtkSalGraphics::style_loaded = false;
/************************************************************************
 * State conversion
 ************************************************************************/

#if !GTK_CHECK_VERSION(4, 0, 0)
static GtkStateFlags NWConvertVCLStateToGTKState(ControlState nVCLState)
{
    GtkStateFlags nGTKState = GTK_STATE_FLAG_NORMAL;

    if (!( nVCLState & ControlState::ENABLED ))
    {
        nGTKState = GTK_STATE_FLAG_INSENSITIVE;
    }

    if ( nVCLState & ControlState::PRESSED )
    {
        nGTKState = static_cast<GtkStateFlags>(nGTKState | GTK_STATE_FLAG_ACTIVE);
    }

    if ( nVCLState & ControlState::ROLLOVER )
    {
        nGTKState = static_cast<GtkStateFlags>(nGTKState | GTK_STATE_FLAG_PRELIGHT);
    }

    if ( nVCLState & ControlState::SELECTED )
        nGTKState = static_cast<GtkStateFlags>(nGTKState | GTK_STATE_FLAG_SELECTED);

    if ( nVCLState & ControlState::FOCUSED )
        nGTKState = static_cast<GtkStateFlags>(nGTKState | GTK_STATE_FLAG_FOCUSED);

    if (AllSettings::GetLayoutRTL())
    {
        nGTKState = static_cast<GtkStateFlags>(nGTKState | GTK_STATE_FLAG_DIR_RTL);
    }
    else
    {
        nGTKState = static_cast<GtkStateFlags>(nGTKState | GTK_STATE_FLAG_DIR_LTR);
    }

    return nGTKState;
}

namespace {

enum class RenderType {
    BackgroundAndFrame = 1,
    Check,
    Background,
    MenuSeparator,
    ToolbarSeparator,
    Separator,
    Arrow,
    Radio,
    Scrollbar,
    Spinbutton,
    Combobox,
    Expander,
    Icon,
    Progress,
    TabItem,
    Focus
};

}

static void NWCalcArrowRect( const tools::Rectangle& rButton, tools::Rectangle& ;rArrow )
{
    // Size the arrow appropriately
    Size aSize( rButton.GetWidth()/2, rButton.GetHeight()/2 );
    rArrow.SetSize( aSize );

    rArrow.SetPos( Point(
        rButton.Left() + ( rButton.GetWidth()  - rArrow.GetWidth()  ) / 2,
        rButton.Top() + ( rButton.GetHeight() - rArrow.GetHeight() ) / 2
        ) );
}

tools::Rectangle GtkSalGraphics::NWGetSpinButtonRect( ControlPart nPart, tools::Rectangle aAreaRect)
{
    gint w, h;
    gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h);
    gint icon_size = std::max(w, h);

    GtkBorder padding, border;
    style_context_get_padding(mpSpinUpStyle, &padding);
    style_context_get_border(mpSpinUpStyle, &border);

    gint buttonWidth = icon_size + padding.left + padding.right +
        border.left + border.right;

    tools::Rectangle buttonRect(Point(0, aAreaRect.Top()), Size(buttonWidth, 0));
    buttonRect.setHeight(aAreaRect.GetHeight());
    tools::Rectangle partRect(buttonRect);
    if ( nPart == ControlPart::ButtonUp )
    {
        if (AllSettings::GetLayoutRTL())
            partRect.SetPosX(aAreaRect.Left());
        else
            partRect.SetPosX(aAreaRect.Left() + (aAreaRect.GetWidth() - buttonRect.GetWidth()));
    }
    else if( nPart == ControlPart::ButtonDown )
    {
        if (AllSettings::GetLayoutRTL())
            partRect.SetPosX(aAreaRect.Left() + buttonRect.GetWidth());
        else
            partRect.SetPosX(aAreaRect.Left() + (aAreaRect.GetWidth() - 2 * buttonRect.GetWidth()));
    }
    else
    {
        if (AllSettings::GetLayoutRTL())
        {
            partRect.SetRight( aAreaRect.Left() + aAreaRect.GetWidth() );
            partRect.SetLeft( aAreaRect.Left() + (2 * buttonRect.GetWidth()) - 1 );
        }
        else
        {
            partRect.SetRight( (aAreaRect.Left() + (aAreaRect.GetWidth() - 2 * buttonRect.GetWidth())) - 1 );
            partRect.SetLeft( aAreaRect.Left() );
        }
        partRect.SetTop( aAreaRect.Top() );
        partRect.SetBottom( aAreaRect.Bottom() );
    }

    return partRect;
}
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
namespace
{
    void QuerySize(GtkStyleContext *pContext, Size &rSize)
    {
        GtkBorder margin, border, padding;

        style_context_get_margin(pContext, &margin);
        style_context_get_border(pContext, &border);
        style_context_get_padding(pContext, &padding);

        int nMinWidth(0), nMinHeight(0);
        GtkStateFlags stateflags = gtk_style_context_get_state (pContext);
        gtk_style_context_get(pContext, stateflags,
                "min-width", &nMinWidth, "min-height", &nMinHeight, nullptr);
        nMinWidth += margin.left + margin.right + border.left + border.right + padding.left + padding.right;
        nMinHeight += margin.top + margin.bottom + border.top + border.bottom + padding.top + padding.bottom;

        rSize = Size(std::max<tools::Long>(rSize.Width(), nMinWidth), std::max<tools::Long>(rSize.Height(), nMinHeight));
    }
}
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
tools::Rectangle GtkSalGraphics::NWGetScrollButtonRect( ControlPart nPart, tools::Rectangle aAreaRect )
{
    tools::Rectangle  buttonRect;

    gboolean has_forward;
    gboolean has_forward2;
    gboolean has_backward;
    gboolean has_backward2;

    GtkStyleContext* pScrollbarStyle = nullptr;
    if ((nPart == ControlPart::ButtonLeft) || (nPart == ControlPart::ButtonRight))
        pScrollbarStyle = mpHScrollbarStyle;
    else // (nPart == ControlPart::ButtonUp) || (nPart == ControlPart::ButtonDown)
        pScrollbarStyle = mpVScrollbarStyle;

    gtk_style_context_get_style( pScrollbarStyle,
                                 "has-forward-stepper", &has_forward,
                                 "has-secondary-forward-stepper", &has_forward2,
                                 "has-backward-stepper", &has_backward,
                                 "has-secondary-backward-stepper", &has_backward2, nullptr );

    gint nFirst = 0;
    gint nSecond = 0;

    if ( has_forward )   nSecond += 1;
    if ( has_forward2 )  nFirst  += 1;
    if ( has_backward )  nFirst  += 1;
    if ( has_backward2 ) nSecond += 1;

    Size aSize;
    if (nPart == ControlPart::ButtonLeft || nPart == ControlPart::ButtonRight)
    {
        QuerySize(mpHScrollbarStyle, aSize);
        QuerySize(mpHScrollbarContentsStyle, aSize);
        QuerySize(mpHScrollbarButtonStyle, aSize);
    }
    else
    {
        QuerySize(mpVScrollbarStyle, aSize);
        QuerySize(mpVScrollbarContentsStyle, aSize);
        QuerySize(mpVScrollbarButtonStyle, aSize);
    }

    if (nPart == ControlPart::ButtonUp)
    {
        aSize.setHeight( aSize.Height() * nFirst );
        buttonRect.SetLeft(aAreaRect.Left());
        buttonRect.SetTop(aAreaRect.Top());
    }
    else if (nPart == ControlPart::ButtonLeft)
    {
        aSize.setWidth( aSize.Width() * nFirst );
        buttonRect.SetLeft(aAreaRect.Left());
        buttonRect.SetTop(aAreaRect.Top());
    }
    else if (nPart == ControlPart::ButtonDown)
    {
        aSize.setHeight( aSize.Height() * nSecond );
        buttonRect.SetLeft(aAreaRect.Left());
        buttonRect.SetTop(aAreaRect.Top() + aAreaRect.GetHeight() - aSize.Height());
    }
    else if (nPart == ControlPart::ButtonRight)
    {
        aSize.setWidth( aSize.Width() * nSecond );
        buttonRect.SetLeft(aAreaRect.Left() + aAreaRect.GetWidth() - aSize.Width());
        buttonRect.SetTop(aAreaRect.Top());
    }

    buttonRect.SetSize(aSize);

    return buttonRect;
}
#endif

static GtkWidget* gCacheWindow;
static GtkWidget* gDumbContainer;
#if GTK_CHECK_VERSION(4, 0, 0)
static GtkWidget* gVScrollbar;
static GtkWidget* gTextView;
#else
static GtkWidget* gComboBox;
static GtkWidget* gListBox;
static GtkWidget* gSpinBox;
static GtkWidget* gTreeViewWidget;
#endif
static GtkWidget* gHScrollbar;
static GtkWidget* gEntryBox;

namespace
{
    void style_context_set_state(GtkStyleContext* context, GtkStateFlags flags)
    {
#if !GTK_CHECK_VERSION(4, 0, 0)
        do
        {
            gtk_style_context_set_state(context, flags);
        }
        while ((context = gtk_style_context_get_parent(context)));
#else
        gtk_style_context_set_state(context, flags);
#endif
    }

    class StyleContextSave
    {
    private:
        std::vector<std::pair<GtkStyleContext*, GtkStateFlags>> m_aStates;
    public:
        void save(GtkStyleContext* context)
        {
#if !GTK_CHECK_VERSION(4, 0, 0)
            do
            {
                m_aStates.emplace_back(context, gtk_style_context_get_state(context));
            }
            while ((context = gtk_style_context_get_parent(context)));
#else
            m_aStates.emplace_back(context, gtk_style_context_get_state(context));
#endif
        }
        void restore()
        {
            for (auto a = m_aStates.rbegin(); a != m_aStates.rend(); ++a)
            {
                gtk_style_context_set_state(a->first, a->second);
            }
            m_aStates.clear();
        }
    };

#if !GTK_CHECK_VERSION(4, 0, 0)
    tools::Rectangle render_common(GtkStyleContext *pContext, cairo_t *cr, const tools::Rectangle &rIn, GtkStateFlags flags)
    {
        if (!pContext)
            return rIn;

        gtk_style_context_set_state(pContext, flags);

        tools::Rectangle aRect(rIn);
        GtkBorder margin;
        style_context_get_margin(pContext, &margin);

        aRect.AdjustLeft(margin.left );
        aRect.AdjustTop(margin.top );
        aRect.AdjustRight( -(margin.right) );
        aRect.AdjustBottom( -(margin.bottom) );

        gtk_render_background(pContext, cr, aRect.Left(), aRect.Top(),
                                            aRect.GetWidth(), aRect.GetHeight());
        gtk_render_frame(pContext, cr, aRect.Left(), aRect.Top(),
                                       aRect.GetWidth(), aRect.GetHeight());

        GtkBorder border, padding;
        style_context_get_border(pContext, &border);
        style_context_get_padding(pContext, &padding);

        aRect.AdjustLeft(border.left + padding.left );
        aRect.AdjustTop(border.top + padding.top );
        aRect.AdjustRight( -(border.right + padding.right) );
        aRect.AdjustBottom( -(border.bottom + padding.bottom) );

        return aRect;
    }
#endif
}

#if !GTK_CHECK_VERSION(4, 0, 0)
void GtkSalGraphics::PaintScrollbar(GtkStyleContext *context,
                                    cairo_t *cr,
                                    const tools::Rectangle& rControlRectangle,
                                    ControlPart nPart,
                                    const ImplControlValue& rValue )
{
    assert(rValue.getType() == ControlType::Scrollbar);
    const ScrollbarValue& rScrollbarVal = static_cast<const ScrollbarValue&>(rValue);
    tools::Rectangle        scrollbarRect;
    GtkStateFlags    stateFlags;
    GtkOrientation    scrollbarOrientation;
    tools::Rectangle        thumbRect = rScrollbarVal.maThumbRect;
    tools::Rectangle        button11BoundRect = rScrollbarVal.maButton1Rect;   // backward
    tools::Rectangle        button22BoundRect = rScrollbarVal.maButton2Rect;   // forward
    tools::Rectangle        button12BoundRect = rScrollbarVal.maButton1Rect;   // secondary forward
    tools::Rectangle        button21BoundRect = rScrollbarVal.maButton2Rect;   // secondary backward
    gdouble          arrow1Angle;                                        // backward
    gdouble          arrow2Angle;                                        // forward
    tools::Rectangle        arrowRect;
    gint            slider_width = 0;
    gint            stepper_size = 0;

    // make controlvalue rectangles relative to area
    thumbRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() );
    button11BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() );
    button22BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() );
    button12BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() );
    button21BoundRect.Move( -rControlRectangle.Left(), -rControlRectangle.Top() );

    // Find the overall bounding rect of the control
    scrollbarRect = rControlRectangle;
    if (scrollbarRect.IsEmpty())
        return;

    gint slider_side;
    Size aSize;
    if (nPart == ControlPart::DrawBackgroundHorz)
    {
        QuerySize(mpHScrollbarStyle, aSize);
        QuerySize(mpHScrollbarContentsStyle, aSize);
        QuerySize(mpHScrollbarTroughStyle, aSize);
        QuerySize(mpHScrollbarSliderStyle, aSize);
        slider_side = aSize.Height();
        gtk_style_context_get(mpHScrollbarButtonStyle,
                              gtk_style_context_get_state(mpHScrollbarButtonStyle),
                              "min-height", &slider_width,
                              "min-width", &stepper_size, nullptr);
    }
    else
    {
        QuerySize(mpVScrollbarStyle, aSize);
        QuerySize(mpVScrollbarContentsStyle, aSize);
        QuerySize(mpVScrollbarTroughStyle, aSize);
        QuerySize(mpVScrollbarSliderStyle, aSize);
        slider_side = aSize.Width();
        gtk_style_context_get(mpVScrollbarButtonStyle,
                              gtk_style_context_get_state(mpVScrollbarButtonStyle),
                              "min-width", &slider_width,
                              "min-height", &stepper_size, nullptr);
    }

    gboolean has_forward;
    gboolean has_forward2;
    gboolean has_backward;
    gboolean has_backward2;

    gtk_style_context_get_style( context,
                                 "has-forward-stepper", &has_forward,
                                 "has-secondary-forward-stepper", &has_forward2,
                                 "has-backward-stepper", &has_backward,
                                 "has-secondary-backward-stepper", &has_backward2, nullptr );

    if ( nPart == ControlPart::DrawBackgroundHorz )
    {
        // Center vertically in the track
        scrollbarRect.Move( 0, (scrollbarRect.GetHeight() - slider_side) / 2 );
        scrollbarRect.SetSize( Size( scrollbarRect.GetWidth(), slider_side ) );
        thumbRect.Move( 0, (scrollbarRect.GetHeight() - slider_side) / 2 );
        thumbRect.SetSize( Size( thumbRect.GetWidth(), slider_side ) );

        scrollbarOrientation = GTK_ORIENTATION_HORIZONTAL;
        arrow1Angle = G_PI * 3 / 2;
        arrow2Angle = G_PI / 2;

        if ( has_backward )
        {
            button12BoundRect.Move( stepper_size,
                                    (scrollbarRect.GetHeight() - slider_width) / 2 );
        }

        button11BoundRect.Move( 0, (scrollbarRect.GetHeight() - slider_width) / 2 );
        button11BoundRect.SetSize( Size( stepper_size, slider_width ) );
        button12BoundRect.SetSize( Size( stepper_size, slider_width ) );

        if ( has_backward2 )
        {
            button22BoundRect.Move( stepper_size, (scrollbarRect.GetHeight() - slider_width) / 2 );
            button21BoundRect.Move( 0, (scrollbarRect.GetHeight() - slider_width) / 2 );
        }
        else
        {
            button22BoundRect.Move( 0, (scrollbarRect.GetHeight() - slider_width) / 2 );
        }

        button21BoundRect.SetSize( Size( stepper_size, slider_width ) );
        button22BoundRect.SetSize( Size( stepper_size, slider_width ) );
    }
    else
    {
        // Center horizontally in the track
        scrollbarRect.Move( (scrollbarRect.GetWidth() - slider_side) / 2, 0 );
        scrollbarRect.SetSize( Size( slider_side, scrollbarRect.GetHeight() ) );
        thumbRect.Move( (scrollbarRect.GetWidth() - slider_side) / 2, 0 );
        thumbRect.SetSize( Size( slider_side, thumbRect.GetHeight() ) );

        scrollbarOrientation = GTK_ORIENTATION_VERTICAL;
        arrow1Angle = 0;
        arrow2Angle = G_PI;

        if ( has_backward )
        {
            button12BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2,
                                    stepper_size );
        }
        button11BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, 0 );
        button11BoundRect.SetSize( Size( slider_width, stepper_size ) );
        button12BoundRect.SetSize( Size( slider_width, stepper_size ) );

        if ( has_backward2 )
        {
            button22BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, stepper_size );
            button21BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, 0 );
        }
        else
        {
            button22BoundRect.Move( (scrollbarRect.GetWidth() - slider_width) / 2, 0 );
        }

        button21BoundRect.SetSize( Size( slider_width, stepper_size ) );
        button22BoundRect.SetSize( Size( slider_width, stepper_size ) );
    }

    bool has_slider = !thumbRect.IsEmpty();

    // ----------------- CONTENTS
    GtkStyleContext* pScrollbarContentsStyle = scrollbarOrientation == GTK_ORIENTATION_VERTICAL ?
                                              mpVScrollbarContentsStyle : mpHScrollbarContentsStyle;

    gtk_render_background(gtk_widget_get_style_context(gCacheWindow), cr, 0, 0,
                          scrollbarRect.GetWidth(), scrollbarRect.GetHeight() );

    gtk_render_background(context, cr, 0, 0,
                          scrollbarRect.GetWidth(), scrollbarRect.GetHeight() );
    gtk_render_frame(context, cr, 0, 0,
                     scrollbarRect.GetWidth(), scrollbarRect.GetHeight() );

    gtk_render_background(pScrollbarContentsStyle, cr, 0, 0,
                          scrollbarRect.GetWidth(), scrollbarRect.GetHeight() );
    gtk_render_frame(pScrollbarContentsStyle, cr, 0, 0,
                     scrollbarRect.GetWidth(), scrollbarRect.GetHeight() );

    bool backwardButtonInsensitive =
        rScrollbarVal.mnCur == rScrollbarVal.mnMin;
    bool forwardButtonInsensitive = rScrollbarVal.mnMax == 0 ||
        rScrollbarVal.mnCur + rScrollbarVal.mnVisibleSize >= rScrollbarVal.mnMax;

    // ----------------- BUTTON 1
    if ( has_backward )
    {
        stateFlags = NWConvertVCLStateToGTKState(rScrollbarVal.mnButton1State);
        if ( backwardButtonInsensitive )
            stateFlags = GTK_STATE_FLAG_INSENSITIVE;

        GtkStyleContext* pScrollbarButtonStyle = scrollbarOrientation == GTK_ORIENTATION_VERTICAL ?
                                                 mpVScrollbarButtonStyle : mpHScrollbarButtonStyle;

        gtk_style_context_set_state(pScrollbarButtonStyle, stateFlags);

        gtk_render_background(pScrollbarButtonStyle, cr,
                              button11BoundRect.Left(), button11BoundRect.Top(),
                              button11BoundRect.GetWidth(), button11BoundRect.GetHeight() );
        gtk_render_frame(pScrollbarButtonStyle, cr,
                         button11BoundRect.Left(), button11BoundRect.Top(),
                         button11BoundRect.GetWidth(), button11BoundRect.GetHeight() );

        // ----------------- ARROW 1
        NWCalcArrowRect( button11BoundRect, arrowRect );
        gtk_render_arrow(pScrollbarButtonStyle, cr,
                         arrow1Angle,
                         arrowRect.Left(), arrowRect.Top(),
                         MIN(arrowRect.GetWidth(), arrowRect.GetHeight()) );
    }
    if ( has_forward2 )
    {
        stateFlags = NWConvertVCLStateToGTKState(rScrollbarVal.mnButton2State);
        if ( forwardButtonInsensitive )
            stateFlags = GTK_STATE_FLAG_INSENSITIVE;

        GtkStyleContext* pScrollbarButtonStyle = scrollbarOrientation == GTK_ORIENTATION_VERTICAL ?
                                                 mpVScrollbarButtonStyle : mpHScrollbarButtonStyle;

        gtk_style_context_set_state(pScrollbarButtonStyle, stateFlags);

        gtk_render_background(pScrollbarButtonStyle, cr,
                              button12BoundRect.Left(), button12BoundRect.Top(),
                              button12BoundRect.GetWidth(), button12BoundRect.GetHeight() );
        gtk_render_frame(pScrollbarButtonStyle, cr,
                         button12BoundRect.Left(), button12BoundRect.Top(),
                         button12BoundRect.GetWidth(), button12BoundRect.GetHeight() );

        // ----------------- ARROW 1
        NWCalcArrowRect( button12BoundRect, arrowRect );
        gtk_render_arrow(pScrollbarButtonStyle, cr,
                         arrow2Angle,
                         arrowRect.Left(), arrowRect.Top(),
                         MIN(arrowRect.GetWidth(), arrowRect.GetHeight()) );
    }
    // ----------------- BUTTON 2

    if ( has_forward )
    {
        stateFlags = NWConvertVCLStateToGTKState(rScrollbarVal.mnButton2State);
        if ( forwardButtonInsensitive )
            stateFlags = GTK_STATE_FLAG_INSENSITIVE;

        GtkStyleContext* pScrollbarButtonStyle = scrollbarOrientation == GTK_ORIENTATION_VERTICAL ?
                                                 mpVScrollbarButtonStyle : mpHScrollbarButtonStyle;

        gtk_style_context_set_state(pScrollbarButtonStyle, stateFlags);

        gtk_render_background(pScrollbarButtonStyle, cr,
                       button22BoundRect.Left(), button22BoundRect.Top(),
                       button22BoundRect.GetWidth(), button22BoundRect.GetHeight() );
        gtk_render_frame(pScrollbarButtonStyle, cr,
                       button22BoundRect.Left(), button22BoundRect.Top(),
                       button22BoundRect.GetWidth(), button22BoundRect.GetHeight() );

        // ----------------- ARROW 2
        NWCalcArrowRect( button22BoundRect, arrowRect );
        gtk_render_arrow(pScrollbarButtonStyle, cr,
                         arrow2Angle,
                         arrowRect.Left(), arrowRect.Top(),
                         MIN(arrowRect.GetWidth(), arrowRect.GetHeight()) );
    }

    if ( has_backward2 )
    {
        stateFlags = NWConvertVCLStateToGTKState(rScrollbarVal.mnButton1State);
        if ( backwardButtonInsensitive )
            stateFlags = GTK_STATE_FLAG_INSENSITIVE;

        GtkStyleContext* pScrollbarButtonStyle = scrollbarOrientation == GTK_ORIENTATION_VERTICAL ?
                                                 mpVScrollbarButtonStyle : mpHScrollbarButtonStyle;

        gtk_style_context_set_state(pScrollbarButtonStyle, stateFlags);

        gtk_render_background(pScrollbarButtonStyle, cr,
                              button21BoundRect.Left(), button21BoundRect.Top(),
                              button21BoundRect.GetWidth(), button21BoundRect.GetHeight() );
        gtk_render_frame(pScrollbarButtonStyle, cr,
                         button21BoundRect.Left(), button21BoundRect.Top(),
                         button21BoundRect.GetWidth(), button21BoundRect.GetHeight() );

        // ----------------- ARROW 2
        NWCalcArrowRect( button21BoundRect, arrowRect );
        gtk_render_arrow(pScrollbarButtonStyle, cr,
                         arrow1Angle,
                         arrowRect.Left(), arrowRect.Top(),
                         MIN(arrowRect.GetWidth(), arrowRect.GetHeight()) );
    }

    // ----------------- TROUGH
    // trackrect matches that of ScrollBar::ImplCalc
    tools::Rectangle aTrackRect(Point(0, 0), scrollbarRect.GetSize());
    if (nPart == ControlPart::DrawBackgroundHorz)
    {
        tools::Rectangle aBtn1Rect = NWGetScrollButtonRect(ControlPart::ButtonLeft, aTrackRect);
        tools::Rectangle aBtn2Rect = NWGetScrollButtonRect(ControlPart::ButtonRight, aTrackRect);
        if (!aBtn1Rect.IsWidthEmpty())
            aTrackRect.SetLeft( aBtn1Rect.Right() );
        if (!aBtn2Rect.IsWidthEmpty())
            aTrackRect.SetRight( aBtn2Rect.Left() );
    }
    else
    {
        tools::Rectangle aBtn1Rect = NWGetScrollButtonRect(ControlPart::ButtonUp, aTrackRect);
        tools::Rectangle aBtn2Rect = NWGetScrollButtonRect(ControlPart::ButtonDown, aTrackRect);
        if (!aBtn1Rect.IsHeightEmpty())
            aTrackRect.SetTop( aBtn1Rect.Bottom() + 1 );
        if (!aBtn2Rect.IsHeightEmpty())
            aTrackRect.SetBottom( aBtn2Rect.Top() );
    }

    GtkStyleContext* pScrollbarTroughStyle = scrollbarOrientation == GTK_ORIENTATION_VERTICAL ?
                                              mpVScrollbarTroughStyle : mpHScrollbarTroughStyle;
    gtk_render_background(pScrollbarTroughStyle, cr, aTrackRect.Left(), aTrackRect.Top(),
                          aTrackRect.GetWidth(), aTrackRect.GetHeight() );
    gtk_render_frame(pScrollbarTroughStyle, cr, aTrackRect.Left(), aTrackRect.Top(),
                     aTrackRect.GetWidth(), aTrackRect.GetHeight() );

    // ----------------- THUMB
    if ( !has_slider )
        return;

    stateFlags = NWConvertVCLStateToGTKState(rScrollbarVal.mnThumbState);
    if ( rScrollbarVal.mnThumbState & ControlState::PRESSED )
        stateFlags = static_cast<GtkStateFlags>(stateFlags | GTK_STATE_FLAG_PRELIGHT);

    GtkStyleContext* pScrollbarSliderStyle = scrollbarOrientation == GTK_ORIENTATION_VERTICAL ?
                                              mpVScrollbarSliderStyle : mpHScrollbarSliderStyle;

    gtk_style_context_set_state(pScrollbarSliderStyle, stateFlags);

    GtkBorder margin;
    style_context_get_margin(pScrollbarSliderStyle, &margin);

    gtk_render_background(pScrollbarSliderStyle, cr,
                      thumbRect.Left() + margin.left, thumbRect.Top() + margin.top,
                      thumbRect.GetWidth() - margin.left - margin.right,
                      thumbRect.GetHeight() - margin.top - margin.bottom);

    gtk_render_frame(pScrollbarSliderStyle, cr,
                      thumbRect.Left() + margin.left, thumbRect.Top() + margin.top,
                      thumbRect.GetWidth() - margin.left - margin.right,
                      thumbRect.GetHeight() - margin.top - margin.bottom);
}

void GtkSalGraphics::PaintOneSpinButton( GtkStyleContext *context,
                                         cairo_t *cr,
                                         ControlPart nPart,
                                         tools::Rectangle aAreaRect,
                                         ControlState nState )
{
    GtkBorder            padding, border;

    GtkStateFlags stateFlags = NWConvertVCLStateToGTKState(nState);
    tools::Rectangle buttonRect = NWGetSpinButtonRect( nPart, aAreaRect );

    gtk_style_context_set_state(context, stateFlags);

    style_context_get_padding(context, &padding);
    style_context_get_border(context, &border);

    gtk_render_background(context, cr,
                          buttonRect.Left(), buttonRect.Top(),
                          buttonRect.GetWidth(), buttonRect.GetHeight() );

    gint iconWidth = buttonRect.GetWidth() - padding.left - padding.right - border.left - border.right;
    gint iconHeight = buttonRect.GetHeight() - padding.top - padding.bottom - border.top - border.bottom;

    const char* icon = (nPart == ControlPart::ButtonUp) ? "list-add-symbolic" : "list-remove-symbolic";
    GtkIconTheme *pIconTheme = gtk_icon_theme_get_for_screen(gtk_widget_get_screen(mpWindow));

    gint scale = gtk_style_context_get_scale (context);
    GtkIconInfo *info = gtk_icon_theme_lookup_icon_for_scale(pIconTheme, icon, std::min(iconWidth, iconHeight), scale,
                                                   static_cast<GtkIconLookupFlags>(0));

    GdkPixbuf *pixbuf = gtk_icon_info_load_symbolic_for_context(info, context, nullptr, nullptr);
    g_object_unref(info);

    iconWidth = gdk_pixbuf_get_width(pixbuf)/scale;
    iconHeight = gdk_pixbuf_get_height(pixbuf)/scale;
    tools::Rectangle arrowRect(buttonRect.Center() - Point(iconWidth / 2, iconHeight / 2),
                               Size(iconWidth, iconHeight));

    gtk_style_context_save (context);
    gtk_style_context_set_scale (context, 1);
    gtk_render_icon(context, cr, pixbuf, arrowRect.Left(), arrowRect.Top());
    gtk_style_context_restore (context);
    g_object_unref(pixbuf);

    gtk_render_frame(context, cr,
                     buttonRect.Left(), buttonRect.Top(),
                     buttonRect.GetWidth(), buttonRect.GetHeight() );
}

void GtkSalGraphics::PaintSpinButton(GtkStateFlags flags,
                                     cairo_t *cr,
                                     const tools::Rectangle& rControlRectangle,
                                     ControlPart nPart,
                                     const ImplControlValue& rValue )
{
    const SpinbuttonValue *pSpinVal = (rValue.getType() == ControlType::SpinButtons) ? static_cast<const SpinbuttonValue *>(&rValue) : nullptr;
    ControlPart upBtnPart = ControlPart::ButtonUp;
    ControlState upBtnState = ControlState::NONE;
    ControlPart downBtnPart = ControlPart::ButtonDown;
    ControlState downBtnState = ControlState::NONE;

    if ( pSpinVal )
    {
        upBtnPart = pSpinVal->mnUpperPart;
        upBtnState = pSpinVal->mnUpperState;

        downBtnPart = pSpinVal->mnLowerPart;
        downBtnState = pSpinVal->mnLowerState;
    }

    if (nPart == ControlPart::Entire)
    {
        gtk_style_context_set_state(mpWindowStyle, flags);

        gtk_render_background(mpWindowStyle, cr,
                              0, 0,
                              rControlRectangle.GetWidth(), rControlRectangle.GetHeight());

        gtk_style_context_set_state(mpSpinStyle, flags);

        gtk_render_background(mpSpinStyle, cr,
                              0, 0,
                              rControlRectangle.GetWidth(), rControlRectangle.GetHeight());
    }

    cairo_translate(cr, -rControlRectangle.Left(), -rControlRectangle.Top());
    PaintOneSpinButton(mpSpinUpStyle, cr, upBtnPart, rControlRectangle, upBtnState );
    PaintOneSpinButton(mpSpinDownStyle, cr, downBtnPart, rControlRectangle, downBtnState );
    cairo_translate(cr, rControlRectangle.Left(), rControlRectangle.Top());

    if (nPart == ControlPart::Entire)
    {
        gtk_render_frame(mpSpinStyle, cr,
                         0, 0,
                         rControlRectangle.GetWidth(), rControlRectangle.GetHeight() );
    }
}

#define FALLBACK_ARROW_SIZE gint(11 * 0.85)

tools::Rectangle GtkSalGraphics::NWGetComboBoxButtonRect(ControlType nType,
                                                   ControlPart nPart,
                                                   tools::Rectangle aAreaRect )
{
    tools::Rectangle    aButtonRect;

    GtkBorder padding;
    if (nType == ControlType::Listbox)
        style_context_get_padding(mpListboxButtonStyle, &padding);
    else
        style_context_get_padding(mpButtonStyle, &padding);

    gint nArrowWidth = FALLBACK_ARROW_SIZE;
    gtk_style_context_get(mpComboboxButtonArrowStyle,
        gtk_style_context_get_state(mpComboboxButtonArrowStyle),
        "min-width", &nArrowWidth, nullptr);

    gint nButtonWidth = nArrowWidth + padding.left + padding.right;
    if( nPart == ControlPart::ButtonDown )
    {
        Point aPos(aAreaRect.Left() + aAreaRect.GetWidth() - nButtonWidth, aAreaRect.Top());
        if (AllSettings::GetLayoutRTL())
            aPos.setX( aAreaRect.Left() );
        aButtonRect.SetSize( Size( nButtonWidth, aAreaRect.GetHeight() ) );
        aButtonRect.SetPos(aPos);
    }
    else if( nPart == ControlPart::SubEdit )
    {
        gint adjust_left = padding.left;
        gint adjust_top = padding.top;
        gint adjust_right = padding.right;
        gint adjust_bottom = padding.bottom;

        aButtonRect.SetSize( Size( aAreaRect.GetWidth() - nButtonWidth - (adjust_left + adjust_right),
                                   aAreaRect.GetHeight() - (adjust_top + adjust_bottom)) );
        Point aEditPos = aAreaRect.TopLeft();
        if (AllSettings::GetLayoutRTL())
            aEditPos.AdjustX(nButtonWidth );
        else
            aEditPos.AdjustX(adjust_left );
        aEditPos.AdjustY(adjust_top );
        aButtonRect.SetPos( aEditPos );
    }

    return aButtonRect;
}

void GtkSalGraphics::PaintCombobox( GtkStateFlags flags, cairo_t *cr,
                                    const tools::Rectangle& rControlRectangle,
                                    ControlType nType,
                                    ControlPart nPart )
{
    tools::Rectangle        areaRect;
    tools::Rectangle        buttonRect;
    tools::Rectangle        arrowRect;

    // Find the overall bounding rect of the buttons's drawing area,
    // plus its actual draw rect excluding adornment
    areaRect = rControlRectangle;

    buttonRect = NWGetComboBoxButtonRect(ControlType::Combobox, ControlPart::ButtonDown, areaRect);

    tools::Rectangle        aEditBoxRect( areaRect );
    aEditBoxRect.SetSize( Size( areaRect.GetWidth() - buttonRect.GetWidth(), aEditBoxRect.GetHeight() ) );
    if (AllSettings::GetLayoutRTL())
        aEditBoxRect.SetPos( Point( areaRect.Left() + buttonRect.GetWidth(), areaRect.Top() ) );

    gint arrow_width = FALLBACK_ARROW_SIZE, arrow_height = FALLBACK_ARROW_SIZE;
    if (nType == ControlType::Combobox)
    {
        gtk_style_context_get(mpComboboxButtonArrowStyle,
            gtk_style_context_get_state(mpComboboxButtonArrowStyle),
            "min-width", &arrow_width, "min-height", &arrow_height, nullptr);
    }
    else if (nType == ControlType::Listbox)
    {
        gtk_style_context_get(mpListboxButtonArrowStyle,
            gtk_style_context_get_state(mpListboxButtonArrowStyle),
            "min-width", &arrow_width, "min-height", &arrow_height, nullptr);
    }

    arrowRect.SetSize(Size(arrow_width, arrow_height));
    arrowRect.SetPos( Point( buttonRect.Left() + static_cast<gint>((buttonRect.GetWidth() - arrowRect.GetWidth()) / 2),
                             buttonRect.Top() + static_cast<gint>((buttonRect.GetHeight() - arrowRect.GetHeight()) / 2) ) );


    tools::Rectangle aRect(Point(0, 0), Size(areaRect.GetWidth(), areaRect.GetHeight()));

    if (nType == ControlType::Combobox)
    {
        if( nPart == ControlPart::Entire )
        {
            render_common(mpComboboxStyle, cr, aRect, flags);
            render_common(mpComboboxBoxStyle, cr, aRect, flags);
            tools::Rectangle aEntryRect(Point(aEditBoxRect.Left() - areaRect.Left(),
                                 aEditBoxRect.Top() - areaRect.Top()),
                                 Size(aEditBoxRect.GetWidth(), aEditBoxRect.GetHeight()));

            GtkJunctionSides eJuncSides = gtk_style_context_get_junction_sides(mpComboboxEntryStyle);
            if (AllSettings::GetLayoutRTL())
                gtk_style_context_set_junction_sides(mpComboboxEntryStyle, GTK_JUNCTION_LEFT);
            else
                gtk_style_context_set_junction_sides(mpComboboxEntryStyle, GTK_JUNCTION_RIGHT);
            render_common(mpComboboxEntryStyle, cr, aEntryRect, flags);
            gtk_style_context_set_junction_sides(mpComboboxEntryStyle, eJuncSides);
        }

        tools::Rectangle aButtonRect(Point(buttonRect.Left() - areaRect.Left(), buttonRect.Top() - areaRect.Top()),
                              Size(buttonRect.GetWidth(), buttonRect.GetHeight()));
        GtkJunctionSides eJuncSides = gtk_style_context_get_junction_sides(mpComboboxButtonStyle);
        if (AllSettings::GetLayoutRTL())
            gtk_style_context_set_junction_sides(mpComboboxButtonStyle, GTK_JUNCTION_RIGHT);
        else
            gtk_style_context_set_junction_sides(mpComboboxButtonStyle, GTK_JUNCTION_LEFT);
        render_common(mpComboboxButtonStyle, cr, aButtonRect, flags);
        gtk_style_context_set_junction_sides(mpComboboxButtonStyle, eJuncSides);

        gtk_render_arrow(mpComboboxButtonArrowStyle, cr,
                         G_PI,
                         (arrowRect.Left() - areaRect.Left()), (arrowRect.Top() - areaRect.Top()),
                         arrowRect.GetWidth() );
    }
    else if (nType == ControlType::Listbox)
    {
        if( nPart == ControlPart::ListboxWindow )
        {
            /* render the popup window with the menu style */
            gtk_render_frame(mpMenuStyle, cr,
                             0, 0,
                             areaRect.GetWidth(), areaRect.GetHeight());
        }
        else
        {
            render_common(mpListboxStyle, cr, aRect, flags);
            render_common(mpListboxButtonStyle, cr, aRect, flags);
            render_common(mpListboxBoxStyle, cr, aRect, flags);

            gtk_render_arrow(mpListboxButtonArrowStyle, cr,
                             G_PI,
                             (arrowRect.Left() - areaRect.Left()), (arrowRect.Top() - areaRect.Top()),
                             arrowRect.GetWidth() );
        }
    }
}

static void appendComboEntry(GtkWidgetPath* pSiblingsPath)
{
    gtk_widget_path_append_type(pSiblingsPath, GTK_TYPE_ENTRY);
    gtk_widget_path_iter_set_object_name(pSiblingsPath, -1, "entry");
    gtk_widget_path_iter_add_class(pSiblingsPath, -1, "combo");
}

static void appendComboButton(GtkWidgetPath* pSiblingsPath)
{
    gtk_widget_path_append_type(pSiblingsPath, GTK_TYPE_BUTTON);
    gtk_widget_path_iter_set_object_name(pSiblingsPath, -1, "button");
    gtk_widget_path_iter_add_class(pSiblingsPath, -1, "combo");
}

static GtkWidgetPath* buildLTRComboSiblingsPath()
{
    GtkWidgetPath* pSiblingsPath = gtk_widget_path_new();

    appendComboEntry(pSiblingsPath);
    appendComboButton(pSiblingsPath);

    return pSiblingsPath;
}

static GtkWidgetPath* buildRTLComboSiblingsPath()
{
    GtkWidgetPath* pSiblingsPath = gtk_widget_path_new();

    appendComboButton(pSiblingsPath);
    appendComboEntry(pSiblingsPath);

    return pSiblingsPath;
}


GtkStyleContext* GtkSalGraphics::makeContext(GtkWidgetPath *pPath, GtkStyleContext *pParent)
{
    GtkStyleContext* context = gtk_style_context_new();
    gtk_style_context_set_screen(context, gtk_widget_get_screen(mpWindow));
    gtk_style_context_set_path(context, pPath);
    if (pParent == nullptr)
    {
        GtkWidget* pTopLevel = widget_get_toplevel(mpWindow);
        GtkStyleContext* pStyle = gtk_widget_get_style_context(pTopLevel);
        gtk_style_context_set_parent(context, pStyle);
        gtk_style_context_set_scale (context, gtk_style_context_get_scale (pStyle));
    }
    else
    {
        gtk_style_context_set_parent(context, pParent);
        gtk_style_context_set_scale (context, gtk_style_context_get_scale (pParent));
    }
    gtk_widget_path_unref(pPath);
    return context;
}

GtkStyleContext* GtkSalGraphics::createStyleContext(GtkControlPart ePart)
{
    switch (ePart)
    {
        case GtkControlPart::ToplevelWindow:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "window");
            gtk_widget_path_iter_add_class(path, -1, "background");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::Button:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "button");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::LinkButton:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "button");
            gtk_widget_path_iter_add_class(path, -1, "link");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::CheckButton:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_CHECK_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "checkbutton");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::CheckButtonCheck:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpCheckButtonStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_CHECK_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "check");
            return makeContext(path, mpCheckButtonStyle);
        }
        case GtkControlPart::RadioButton:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_RADIO_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "radiobutton");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::RadioButtonRadio:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpRadioButtonStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_RADIO_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "radio");
            return makeContext(path, mpRadioButtonStyle);
        }
        case GtkControlPart::ComboboxBoxButtonBoxArrow:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpComboboxButtonBoxStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_RADIO_BUTTON);
            gtk_widget_path_append_type(path, GTK_TYPE_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "arrow");
            return makeContext(path, mpComboboxButtonBoxStyle);
        }
        case GtkControlPart::ListboxBoxButtonBoxArrow:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpListboxButtonBoxStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_RADIO_BUTTON);
            gtk_widget_path_append_type(path, GTK_TYPE_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "arrow");
            return makeContext(path, mpListboxButtonBoxStyle);
        }
        case GtkControlPart::Entry:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_ENTRY);
            gtk_widget_path_iter_set_object_name(path, -1, "entry");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::Combobox:
        case GtkControlPart::Listbox:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "combobox");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::ComboboxBox:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpComboboxStyle));
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "box");
            gtk_widget_path_iter_add_class(path, -1, "horizontal");
            gtk_widget_path_iter_add_class(path, -1, "linked");
            return makeContext(path, mpComboboxStyle);
        }
        case GtkControlPart::ListboxBox:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpListboxStyle));
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "box");
            gtk_widget_path_iter_add_class(path, -1, "horizontal");
            gtk_widget_path_iter_add_class(path, -1, "linked");
            return makeContext(path, mpListboxStyle);
        }
        case GtkControlPart::ComboboxBoxEntry:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpComboboxBoxStyle));
            GtkWidgetPath* pSiblingsPath;
            if (AllSettings::GetLayoutRTL())
            {
                pSiblingsPath = buildRTLComboSiblingsPath();
                gtk_widget_path_append_with_siblings(path, pSiblingsPath, 1);
            }
            else
            {
                pSiblingsPath = buildLTRComboSiblingsPath();
                gtk_widget_path_append_with_siblings(path, pSiblingsPath, 0);
            }
            gtk_widget_path_unref(pSiblingsPath);
            return makeContext(path, mpComboboxBoxStyle);
        }
        case GtkControlPart::ComboboxBoxButton:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpComboboxBoxStyle));
            GtkWidgetPath* pSiblingsPath;
            if (AllSettings::GetLayoutRTL())
            {
                pSiblingsPath = buildRTLComboSiblingsPath();
                gtk_widget_path_append_with_siblings(path, pSiblingsPath, 0);
            }
            else
            {
                pSiblingsPath = buildLTRComboSiblingsPath();
                gtk_widget_path_append_with_siblings(path, pSiblingsPath, 1);
            }
            gtk_widget_path_unref(pSiblingsPath);
            return makeContext(path, mpComboboxBoxStyle);
        }
        case GtkControlPart::ListboxBoxButton:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpListboxBoxStyle));
            GtkWidgetPath* pSiblingsPath = gtk_widget_path_new();

            gtk_widget_path_append_type(pSiblingsPath, GTK_TYPE_BUTTON);
            gtk_widget_path_iter_set_object_name(pSiblingsPath, -1, "button");
            gtk_widget_path_iter_add_class(pSiblingsPath, -1, "combo");

            gtk_widget_path_append_with_siblings(path, pSiblingsPath, 0);
            gtk_widget_path_unref(pSiblingsPath);
            return makeContext(path, mpListboxBoxStyle);
        }
        case GtkControlPart::ComboboxBoxButtonBox:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpComboboxButtonStyle));
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "box");
            gtk_widget_path_iter_add_class(path, -1, "horizontal");
            return makeContext(path, mpComboboxButtonStyle);
        }
        case GtkControlPart::ListboxBoxButtonBox:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpListboxButtonStyle));
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "box");
            gtk_widget_path_iter_add_class(path, -1, "horizontal");
            return makeContext(path, mpListboxButtonStyle);
        }
        case GtkControlPart::SpinButton:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpWindowStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_SPIN_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "spinbutton");
            gtk_widget_path_iter_add_class(path, -1, GTK_STYLE_CLASS_HORIZONTAL);
            return makeContext(path, mpWindowStyle);
        }
        case GtkControlPart::SpinButtonUpButton:
        case GtkControlPart::SpinButtonDownButton:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpSpinStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_SPIN_BUTTON);
            gtk_widget_path_iter_set_object_name(path, -1, "button");
            gtk_widget_path_iter_add_class(path, -1, ePart == GtkControlPart::SpinButtonUpButton "up" : "down");
            return makeContext(path, mpSpinStyle);
        }
        case GtkControlPart::ScrollbarVertical:
        case GtkControlPart::ScrollbarHorizontal:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_SCROLLBAR);
            gtk_widget_path_iter_set_object_name(path, -1, "scrollbar");
            gtk_widget_path_iter_add_class(path, -1, ePart == GtkControlPart::ScrollbarVertical ? "vertical" : "horizontal");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::ScrollbarVerticalContents:
        case GtkControlPart::ScrollbarHorizontalContents:
        {
            GtkStyleContext *pParent =
                (ePart == GtkControlPart::ScrollbarVerticalContents) ? mpVScrollbarStyle : mpHScrollbarStyle;
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(pParent));
            gtk_widget_path_append_type(path, GTK_TYPE_SCROLLBAR);
            gtk_widget_path_iter_set_object_name(path, -1, "contents");
            return makeContext(path, pParent);
        }
        case GtkControlPart::ScrollbarVerticalTrough:
        case GtkControlPart::ScrollbarHorizontalTrough:
        {
            GtkStyleContext *pParent =
                (ePart == GtkControlPart::ScrollbarVerticalTrough) ? mpVScrollbarContentsStyle : mpHScrollbarContentsStyle;
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(pParent));
            gtk_widget_path_append_type(path, GTK_TYPE_SCROLLBAR);
            gtk_widget_path_iter_set_object_name(path, -1, "trough");
            return makeContext(path, pParent);
        }
        case GtkControlPart::ScrollbarVerticalSlider:
        case GtkControlPart::ScrollbarHorizontalSlider:
        {
            GtkStyleContext *pParent =
                (ePart == GtkControlPart::ScrollbarVerticalSlider) ? mpVScrollbarTroughStyle : mpHScrollbarTroughStyle;
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(pParent));
            gtk_widget_path_append_type(path, GTK_TYPE_SCROLLBAR);
            gtk_widget_path_iter_set_object_name(path, -1, "slider");
            return makeContext(path, pParent);
        }
        case GtkControlPart::ScrollbarVerticalButton:
        case GtkControlPart::ScrollbarHorizontalButton:
        {
            GtkStyleContext *pParent =
                (ePart == GtkControlPart::ScrollbarVerticalButton) ? mpVScrollbarStyle : mpHScrollbarStyle;
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(pParent));
            gtk_widget_path_append_type(path, GTK_TYPE_SCROLLBAR);
            gtk_widget_path_iter_set_object_name(path, -1, "button");
            return makeContext(path, pParent);
        }
        case GtkControlPart::ProgressBar:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_PROGRESS_BAR);
            gtk_widget_path_iter_set_object_name(path, -1, "progressbar");
            gtk_widget_path_iter_add_class(path, -1, GTK_STYLE_CLASS_HORIZONTAL);
            return makeContext(path, nullptr);
        }
        case GtkControlPart::ProgressBarTrough:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpProgressBarStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_PROGRESS_BAR);
            gtk_widget_path_iter_set_object_name(path, -1, "trough");
            return makeContext(path, mpProgressBarStyle);
        }
        case GtkControlPart::ProgressBarProgress:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpProgressBarTroughStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_PROGRESS_BAR);
            gtk_widget_path_iter_set_object_name(path, -1, "progress");
            return makeContext(path, mpProgressBarTroughStyle);
        }
        case GtkControlPart::Notebook:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpWindowStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_NOTEBOOK);
            gtk_widget_path_iter_set_object_name(path, -1, "notebook");
            return makeContext(path, mpWindowStyle);
        }
        case GtkControlPart::NotebookStack:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpNotebookStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_NOTEBOOK);
            gtk_widget_path_iter_set_object_name(path, -1, "stack");
            return makeContext(path, mpNotebookStyle);
        }
        case GtkControlPart::NotebookHeader:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpNotebookStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_NOTEBOOK);
            gtk_widget_path_iter_set_object_name(path, -1, "header");
            gtk_widget_path_iter_add_class(path, -1, "frame");
            gtk_widget_path_iter_add_class(path, -1, "top");
            return makeContext(path, mpNotebookStyle);
        }
        case GtkControlPart::NotebookHeaderTabs:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpNotebookHeaderStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_NOTEBOOK);
            gtk_widget_path_iter_set_object_name(path, -1, "tabs");
            gtk_widget_path_iter_add_class(path, -1, "top");
            return makeContext(path, mpNotebookHeaderStyle);
        }
        case GtkControlPart::NotebookHeaderTabsTab:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpNotebookHeaderTabsStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_NOTEBOOK);
            gtk_widget_path_iter_set_object_name(path, -1, "tab");
            gtk_widget_path_iter_add_class(path, -1, "top");
            return makeContext(path, mpNotebookHeaderTabsStyle);
        }
        case GtkControlPart::NotebookHeaderTabsTabLabel:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpNotebookHeaderTabsTabStyle));
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "label");
            return makeContext(path, mpNotebookHeaderTabsTabStyle);
        }
        case GtkControlPart::NotebookHeaderTabsTabActiveLabel:
        case GtkControlPart::NotebookHeaderTabsTabHoverLabel:
            return mpNotebookHeaderTabsTabLabelStyle;
        case GtkControlPart::FrameBorder:
        {
            GtkWidgetPath *path = gtk_widget_path_new();
            gtk_widget_path_append_type(path, GTK_TYPE_FRAME);
            gtk_widget_path_iter_set_object_name(path, -1, "frame");
            gtk_widget_path_iter_add_class(path, -1, "frame");
            return makeContext(path, nullptr);
        }
        case GtkControlPart::MenuBar:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpWindowStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_MENU_BAR);
            gtk_widget_path_iter_set_object_name(path, -1, "menubar");
            gtk_widget_path_iter_add_class(path, -1, "background");
            return makeContext(path, mpWindowStyle);
        }
        case GtkControlPart::MenuBarItem:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuBarStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "menuitem");
            return makeContext(path, mpMenuBarStyle);
        }
        case GtkControlPart::MenuWindow:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuBarItemStyle));
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "window");
            gtk_widget_path_iter_add_class(path, -1, "background");
            gtk_widget_path_iter_add_class(path, -1, "popup");
            return makeContext(path, mpMenuBarItemStyle);
        }
        case GtkControlPart::Menu:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuWindowStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_MENU);
            gtk_widget_path_iter_set_object_name(path, -1, "menu");
            return makeContext(path, mpMenuWindowStyle);
        }
        case GtkControlPart::MenuItem:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "menuitem");
            return makeContext(path, mpMenuStyle);
        }
        case GtkControlPart::MenuItemLabel:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuItemStyle));
            gtk_widget_path_append_type(path, G_TYPE_NONE);
            gtk_widget_path_iter_set_object_name(path, -1, "label");
            return makeContext(path, mpMenuItemStyle);
        }
        case GtkControlPart::MenuItemArrow:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuItemStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "arrow");
            return makeContext(path, mpMenuItemStyle);
        }
        case GtkControlPart::CheckMenuItem:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_CHECK_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "menuitem");
            return makeContext(path, mpMenuStyle);
        }
        case GtkControlPart::CheckMenuItemCheck:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpCheckMenuItemStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_CHECK_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "check");
            return makeContext(path, mpCheckMenuItemStyle);
        }
        case GtkControlPart::RadioMenuItem:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_RADIO_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "menuitem");
            return makeContext(path, mpMenuStyle);
        }
        case GtkControlPart::RadioMenuItemRadio:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpRadioMenuItemStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_RADIO_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "radio");
            return makeContext(path, mpRadioMenuItemStyle);
        }
        case GtkControlPart::SeparatorMenuItem:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpMenuStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_SEPARATOR_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "menuitem");
            return makeContext(path, mpMenuStyle);
        }
        case GtkControlPart::SeparatorMenuItemSeparator:
        {
            GtkWidgetPath *path = gtk_widget_path_copy(gtk_style_context_get_path(mpSeparatorMenuItemStyle));
            gtk_widget_path_append_type(path, GTK_TYPE_SEPARATOR_MENU_ITEM);
            gtk_widget_path_iter_set_object_name(path, -1, "separator");
            return makeContext(path, mpSeparatorMenuItemStyle);
        }
    }

    return nullptr;
}

#ifndef GTK_STYLE_CLASS_POPUP
constexpr OUStringLiteral GTK_STYLE_CLASS_POPUP = u"popup";
#endif
#ifndef GTK_STYLE_CLASS_LABEL
constexpr OUStringLiteral GTK_STYLE_CLASS_LABEL = u"label";
#endif

void GtkSalGraphics::PaintCheckOrRadio(cairo_t *cr, GtkStyleContext *context,
                                       const tools::Rectangle& rControlRectangle, bool bIsCheck, bool bInMenu)
{
    gint indicator_size;
    gtk_style_context_get_style(context, "indicator-size", &indicator_size, nullptr);

    gint x = (rControlRectangle.GetWidth() - indicator_size) / 2;
    gint y = (rControlRectangle.GetHeight() - indicator_size) / 2;

    if (!bInMenu)
        gtk_render_background(context, cr, x, y, indicator_size, indicator_size);

    if (bIsCheck)
        gtk_render_check(context, cr, x, y, indicator_size, indicator_size);
    else
        gtk_render_option(context, cr, x, y, indicator_size, indicator_size);

    gtk_render_frame(context, cr, x, y, indicator_size, indicator_size);
}

void GtkSalGraphics::PaintCheck(cairo_t *cr, GtkStyleContext *context,
                                const tools::Rectangle& rControlRectangle, bool bInMenu)
{
    PaintCheckOrRadio(cr, context, rControlRectangle, true, bInMenu);
}

void GtkSalGraphics::PaintRadio(cairo_t *cr, GtkStyleContext *context,
                                const tools::Rectangle& rControlRectangle, bool bInMenu)
{
    PaintCheckOrRadio(cr, context, rControlRectangle, false, bInMenu);
}

static gfloat getArrowSize(GtkStyleContext* context)
{
    gint min_width, min_weight;
    gtk_style_context_get_style(context, "min-width", &min_width, nullptr);
    gtk_style_context_get_style(context, "min-height", &min_weight, nullptr);
    gfloat arrow_size = 11 * MAX (min_width, min_weight);
    return arrow_size;
}

namespace
{
    void draw_vertical_separator(GtkStyleContext *context, cairo_t *cr, const tools::Rectangle& rControlRegion, gint nSeparatorWidth)
    {
        tools::Long nX = 0;
        tools::Long nY = 0;

        gint nHalfSeparatorWidth = nSeparatorWidth / 2;
        gint nHalfRegionWidth = rControlRegion.GetWidth() / 2;

        nX = nX + nHalfRegionWidth - nHalfSeparatorWidth;
        nY = rControlRegion.GetHeight() > 5 ? 1 : 0;
        int nHeight = rControlRegion.GetHeight() - (2 * nY);

        gtk_render_background(context, cr, nX, nY, nSeparatorWidth, nHeight);
        gtk_render_frame(context, cr, nX, nY, nSeparatorWidth, nHeight);
    }

    void draw_horizontal_separator(GtkStyleContext *context, cairo_t *cr, const tools::Rectangle& rControlRegion)
    {
        tools::Long nX = 0;
        tools::Long nY = 0;

        gint nSeparatorHeight = 1;

        gtk_style_context_get(context,
            gtk_style_context_get_state(context),
            "min-height", &nSeparatorHeight, nullptr);

        gint nHalfSeparatorHeight = nSeparatorHeight / 2;
        gint nHalfRegionHeight = rControlRegion.GetHeight() / 2;

        nY = nY + nHalfRegionHeight - nHalfSeparatorHeight;
        nX = rControlRegion.GetWidth() > 5 ? 1 : 0;
        int nWidth = rControlRegion.GetWidth() - (2 * nX);

        gtk_render_background(context, cr, nX, nY, nWidth, nSeparatorHeight);
        gtk_render_frame(context, cr, nX, nY, nWidth, nSeparatorHeight);
    }
}
#endif

void GtkSalGraphics::handleDamage(const tools::Rectangle& rDamagedRegion)
{
    assert(m_pWidgetDraw);
    assert(!rDamagedRegion.IsEmpty());
    mpFrame->damaged(rDamagedRegion.Left(), rDamagedRegion.Top(), rDamagedRegion.GetWidth(), rDamagedRegion.GetHeight());
}

#if !GTK_CHECK_VERSION(4, 0, 0)
bool GtkSalGraphics::drawNativeControl( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion,
                                            ControlState nState, const ImplControlValue& rValue,
                                            const OUString&, const Color& rBackgroundColor)
{
    RenderType renderType = nPart == ControlPart::Focus ? RenderType::Focus : RenderType::BackgroundAndFrame;
    GtkStyleContext *context = nullptr;
    GdkPixbuf *pixbuf = nullptr;
    bool bInMenu = false;

    GtkStateFlags flags = NWConvertVCLStateToGTKState(nState);

    switch(nType)
    {
    case ControlType::Spinbox:
    case ControlType::SpinButtons:
        context = mpSpinStyle;
        renderType = RenderType::Spinbutton;
        break;
    case ControlType::Editbox:
        context = mpEntryStyle;
        break;
    case ControlType::MultilineEditbox:
        context = mpTextViewStyle;
        break;
    case ControlType::Combobox:
        context = mpComboboxStyle;
        renderType = RenderType::Combobox;
        break;
    case ControlType::Listbox:
        if (nPart == ControlPart::Focus)
        {
            renderType = RenderType::Focus;
            context = mpListboxButtonStyle;
        }
        else
        {
            renderType = RenderType::Combobox;
            context = mpListboxStyle;
        }
        break;
    case ControlType::MenuPopup:
        bInMenu = true;

        // map selected menu entries in vcl parlance to gtk prelight
        if (nPart >= ControlPart::MenuItem && nPart <= ControlPart::SubmenuArrow && (nState & ControlState::SELECTED))
            flags = static_cast<GtkStateFlags>(flags | GTK_STATE_FLAG_PRELIGHT);
        flags = static_cast<GtkStateFlags>(flags & ~GTK_STATE_FLAG_ACTIVE);
        switch(nPart)
        {
        case ControlPart::MenuItem:
            context = mpMenuItemStyle;
            renderType = RenderType::BackgroundAndFrame;
            break;
        case ControlPart::MenuItemCheckMark:
            context = mpCheckMenuItemCheckStyle;
            renderType = RenderType::Check;
            nType = ControlType::Checkbox;
            if (nState & ControlState::PRESSED)
            {
                flags = static_cast<GtkStateFlags>(flags | GTK_STATE_FLAG_CHECKED);
            }
            break;
        case ControlPart::MenuItemRadioMark:
            context = mpRadioMenuItemRadioStyle;
            renderType = RenderType::Radio;
            nType = ControlType::Radiobutton;
            if (nState & ControlState::PRESSED)
            {
                flags = static_cast<GtkStateFlags>(flags | GTK_STATE_FLAG_CHECKED);
            }
            break;
        case ControlPart::Separator:
            context = mpSeparatorMenuItemSeparatorStyle;
            flags = GtkStateFlags(GTK_STATE_FLAG_BACKDROP | GTK_STATE_FLAG_INSENSITIVE); //GTK_STATE_FLAG_BACKDROP hack ?
            renderType = RenderType::MenuSeparator;
            break;
        case ControlPart::SubmenuArrow:
            context = mpMenuItemArrowStyle;
            renderType = RenderType::Arrow;
            break;
        case ControlPart::Entire:
            context = mpMenuStyle;
            renderType = RenderType::Background;
            break;
        defaultbreak;
        }
        break;
    case ControlType::Toolbar:
        switch(nPart)
        {
        case ControlPart::DrawBackgroundHorz:
        case ControlPart::DrawBackgroundVert:
            context = mpToolbarStyle;
            break;
        case ControlPart::Button:
            /* For all checkbuttons in the toolbars */
            flags = static_cast<GtkStateFlags>(flags |
                    ( (rValue.getTristateVal() == ButtonValue::On) ? GTK_STATE_FLAG_CHECKED : GTK_STATE_FLAG_NORMAL));
            context = mpToolButtonStyle;
            break;
        case ControlPart::SeparatorVert:
            context = mpToolbarSeparatorStyle;
            renderType = RenderType::ToolbarSeparator;
            break;
        default:
            return false;
        }
        break;
    case ControlType::Radiobutton:
        flags = static_cast<GtkStateFlags>(flags |
                ( (rValue.getTristateVal() == ButtonValue::On) ? GTK_STATE_FLAG_CHECKED : GTK_STATE_FLAG_NORMAL));
        context = mpRadioButtonRadioStyle;
        renderType = nPart == ControlPart::Focus ? RenderType::Focus : RenderType::Radio;
        break;
    case ControlType::Checkbox:
        flags = static_cast<GtkStateFlags>(flags |
                ( (rValue.getTristateVal() == ButtonValue::On) ? GTK_STATE_FLAG_CHECKED :
                  (rValue.getTristateVal() == ButtonValue::Mixed) ? GTK_STATE_FLAG_INCONSISTENT :
                  GTK_STATE_FLAG_NORMAL));
        context = mpCheckButtonCheckStyle;
        renderType = nPart == ControlPart::Focus ? RenderType::Focus : RenderType::Check;
        break;
    case ControlType::Pushbutton:
        context = mpButtonStyle;
        break;
    case ControlType::Scrollbar:
        switch(nPart)
        {
        case ControlPart::DrawBackgroundVert:
        case ControlPart::DrawBackgroundHorz:
            context = (nPart == ControlPart::DrawBackgroundVert)
                ? mpVScrollbarStyle : mpHScrollbarStyle;
            renderType = RenderType::Scrollbar;
            break;
        defaultbreak;
        }
        break;
    case ControlType::ListNet:
        return true;
    case ControlType::TabPane:
        context = mpNotebookStyle;
        break;
    case ControlType::TabBody:
        context = mpNotebookStackStyle;
        break;
    case ControlType::TabHeader:
        context = mpNotebookHeaderStyle;
        break;
    case ControlType::TabItem:
        context = mpNotebookHeaderTabsTabStyle;
        if (nState & ControlState::SELECTED)
            flags = static_cast<GtkStateFlags>(flags | GTK_STATE_FLAG_CHECKED);
        renderType = RenderType::TabItem;
        break;
    case ControlType::WindowBackground:
        context = gtk_widget_get_style_context(widget_get_toplevel(mpWindow));
        break;
    case ControlType::Frame:
    {
        DrawFrameStyle nStyle = static_cast<DrawFrameStyle>(rValue.getNumericVal() & 0x0f);
        if (nStyle == DrawFrameStyle::In)
            context = mpFrameOutStyle;
        else
            context = mpFrameInStyle;
        break;
    }
    case ControlType::Menubar:
        if (nPart == ControlPart::MenuItem)
        {
            context = mpMenuBarItemStyle;

            flags = (!(nState & ControlState::ENABLED)) ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL;
            if (nState & ControlState::SELECTED)
                flags = static_cast<GtkStateFlags>(flags | GTK_STATE_FLAG_PRELIGHT);
        }
        else
        {
            context = mpMenuBarStyle;
        }
        break;
    case ControlType::Fixedline:
        context = nPart == ControlPart::SeparatorHorz ? mpFixedHoriLineStyle : mpFixedVertLineStyle;
        renderType = RenderType::Separator;
        break;
    case ControlType::ListNode:
    {
        context = mpTreeHeaderButtonStyle;
        ButtonValue aButtonValue = rValue.getTristateVal();
        if (aButtonValue == ButtonValue::On)
            flags = static_cast<GtkStateFlags>(flags | GTK_STATE_FLAG_CHECKED);
        renderType = RenderType::Expander;
        break;
    }
    case ControlType::ListHeader:
        context = mpTreeHeaderButtonStyle;
        if (nPart == ControlPart::Arrow)
        {
            const char* icon = (rValue.getNumericVal() & 1) ? "pan-down-symbolic" : "pan-up-symbolic";
            GtkIconTheme *pIconTheme = gtk_icon_theme_get_for_screen(gtk_widget_get_screen(mpWindow));
            pixbuf = gtk_icon_theme_load_icon_for_scale(pIconTheme, icon,
                                                        std::max(rControlRegion.GetWidth(), rControlRegion.GetHeight()),
                                                        gtk_style_context_get_scale (context),
                                                        static_cast<GtkIconLookupFlags>(0), nullptr);
            flags = GTK_STATE_FLAG_SELECTED;
            renderType = RenderType::Icon;
        }
        break;
    case ControlType::Progress:
        context = mpProgressBarProgressStyle;
        renderType = RenderType::Progress;
        break;
    default:
        return false;
    }

    cairo_t *cr = getCairoContext();
    clipRegion(cr);
    cairo_translate(cr, rControlRegion.Left(), rControlRegion.Top());

    tools::Long nX = 0;
    tools::Long nY = 0;
    tools::Long nWidth = rControlRegion.GetWidth();
    tools::Long nHeight = rControlRegion.GetHeight();

    StyleContextSave aContextState;
    aContextState.save(context);
    style_context_set_state(context, flags);

    // apply background in style, if explicitly set
    // note: for more complex controls that use multiple styles for their elements,
    // background may have to be applied for more of those as well (s. case RenderType::Combobox below)
    GtkCssProvider* pBgCssProvider = nullptr;
    if (rBackgroundColor != COL_AUTO)
    {
        const OUString sColorCss = "* { background-color: #" + rBackgroundColor.AsRGBHexString() + "; }";
        const OString aResult = OUStringToOString(sColorCss, RTL_TEXTENCODING_UTF8);
        pBgCssProvider =  gtk_css_provider_new();
        css_provider_load_from_data(pBgCssProvider, aResult.getStr(), aResult.getLength());
        gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(pBgCssProvider),
                                       GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
    }

    switch(renderType)
    {
    case RenderType::Background:
    case RenderType::BackgroundAndFrame:
        gtk_render_background(context, cr, nX, nY, nWidth, nHeight);
        if (renderType == RenderType::BackgroundAndFrame)
        {
            gtk_render_frame(context, cr, nX, nY, nWidth, nHeight);
        }
        break;
    case RenderType::Check:
    {
        PaintCheck(cr, context, rControlRegion, bInMenu);
        break;
    }
    case RenderType::Radio:
    {
        PaintRadio(cr, context, rControlRegion, bInMenu);
        break;
    }
    case RenderType::MenuSeparator:
        gtk_render_line(context, cr,
                        0, rControlRegion.GetHeight() / 2,
                        rControlRegion.GetWidth() - 1, rControlRegion.GetHeight() / 2);
        break;
    case RenderType::ToolbarSeparator:
    {
        draw_vertical_separator(context, cr, rControlRegion, mnVerticalSeparatorMinWidth);
        break;
    }
    case RenderType::Separator:
        if (nPart == ControlPart::SeparatorHorz)
            draw_horizontal_separator(context, cr, rControlRegion);
        else
            draw_vertical_separator(context, cr, rControlRegion, mnVerticalSeparatorMinWidth);
        break;
    case RenderType::Arrow:
        gtk_render_arrow(context, cr,
                         G_PI / 2, 0, 0,
                         MIN(rControlRegion.GetWidth(), 1 + rControlRegion.GetHeight()));
        break;
    case RenderType::Expander:
        gtk_render_expander(context, cr, -2, -2, nWidth+4, nHeight+4);
        break;
    case RenderType::Scrollbar:
        PaintScrollbar(context, cr, rControlRegion, nPart, rValue);
        break;
    case RenderType::Spinbutton:
        PaintSpinButton(flags, cr, rControlRegion, nPart, rValue);
        break;
    case RenderType::Combobox:
        if (pBgCssProvider)
        {
            if (nType == ControlType::Combobox)
            {
                gtk_style_context_add_provider(mpComboboxEntryStyle, GTK_STYLE_PROVIDER(pBgCssProvider),
                                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
            }
            else if (nType == ControlType::Listbox)
            {
                gtk_style_context_add_provider(mpListboxBoxStyle, GTK_STYLE_PROVIDER(pBgCssProvider),
                                               GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
            }
        }
        PaintCombobox(flags, cr, rControlRegion, nType, nPart);
        if (pBgCssProvider)
        {
            if (nType == ControlType::Combobox)
                gtk_style_context_remove_provider(mpComboboxEntryStyle, GTK_STYLE_PROVIDER(pBgCssProvider));
            else if (nType == ControlType::Listbox)
                gtk_style_context_remove_provider(mpListboxBoxStyle, GTK_STYLE_PROVIDER(pBgCssProvider));
        }
        break;
    case RenderType::Icon:
        gtk_style_context_save (context);
        gtk_style_context_set_scale (context, 1);
        gtk_render_icon(context, cr, pixbuf, nX, nY);
        gtk_style_context_restore (context);
        g_object_unref(pixbuf);
        break;
    case RenderType::Focus:
    {
        if (nType == ControlType::Checkbox ||
            nType == ControlType::Radiobutton)
        {
            nX -= 2; nY -=2;
            nHeight += 4; nWidth += 4;
        }
        else
        {
            GtkBorder border;

            style_context_get_border(context, &border);

            nX += border.left;
            nY += border.top;
            nWidth -= border.left + border.right;
            nHeight -= border.top + border.bottom;
        }

        gtk_render_focus(context, cr, nX, nY, nWidth, nHeight);

        break;
    }
    case RenderType::Progress:
    {
        gtk_render_background(mpProgressBarTroughStyle, cr, nX, nY, nWidth, nHeight);

        tools::Long nProgressWidth = rValue.getNumericVal();
        if (nProgressWidth)
        {
            GtkBorder padding;
            style_context_get_padding(context, &padding);

            nX += padding.left;
            nY += padding.top;
            nHeight -= (padding.top + padding.bottom);
            nProgressWidth -= (padding.left + padding.right);
            gtk_render_background(context, cr, nX, nY, nProgressWidth, nHeight);
            gtk_render_frame(context, cr, nX, nY, nProgressWidth, nHeight);
        }

        gtk_render_frame(mpProgressBarTroughStyle, cr, nX, nY, nWidth, nHeight);

        break;
    }
    case RenderType::TabItem:
    {
        gint initial_gap(0);
        gtk_style_context_get_style(mpNotebookStyle,
                                "initial-gap", &initial_gap,
                                nullptr);

        nX += initial_gap/2;
        nWidth -= initial_gap;
        tools::Rectangle aRect(Point(nX, nY), Size(nWidth, nHeight));
        render_common(mpNotebookHeaderTabsTabStyle, cr, aRect, flags);
        break;
    }
    default:
        break;
    }

    if (pBgCssProvider)
    {
        gtk_style_context_remove_provider(context, GTK_STYLE_PROVIDER(pBgCssProvider));
    }
    aContextState.restore();

    cairo_destroy(cr); // unref

    if (!rControlRegion.IsEmpty())
        mpFrame->damaged(rControlRegion.Left(), rControlRegion.Top(), rControlRegion.GetWidth(), rControlRegion.GetHeight());

    return true;
}

static tools::Rectangle GetWidgetSize(const tools::Rectangle& rControlRegion, GtkWidget* widget)
{
    GtkRequisition aReq;
    gtk_widget_get_preferred_size(widget, nullptr, &aReq);
    tools::Long nHeight = std::max<tools::Long>(rControlRegion.GetHeight(), aReq.height);
    return tools::Rectangle(rControlRegion.TopLeft(), Size(rControlRegion.GetWidth(), nHeight));
}

static tools::Rectangle AdjustRectForTextBordersPadding(GtkStyleContext* pStyle, tools::Long nContentWidth, tools::Long nContentHeight, const tools::Rectangle& rControlRegion)
{
    GtkBorder border;
    style_context_get_border(pStyle, &border);

    GtkBorder padding;
    style_context_get_padding(pStyle, &padding);

    gint nWidgetHeight = nContentHeight + padding.top + padding.bottom + border.top + border.bottom;
    nWidgetHeight = std::max(std::max<gint>(nWidgetHeight, rControlRegion.GetHeight()), 34);

    gint nWidgetWidth = nContentWidth + padding.left + padding.right + border.left + border.right;
    nWidgetWidth = std::max<gint>(nWidgetWidth, rControlRegion.GetWidth());

    tools::Rectangle aEditRect(rControlRegion.TopLeft(), Size(nWidgetWidth, nWidgetHeight));

    return aEditRect;
}

bool GtkSalGraphics::getNativeControlRegion( ControlType nType, ControlPart nPart, const tools::Rectangle& rControlRegion, ControlState,
                                                const ImplControlValue& rValue, const OUString&,
                                                tools::Rectangle &rNativeBoundingRegion, tools::Rectangle &rNativeContentRegion )
{
    /* TODO: all this functions needs improvements */
    tools::Rectangle aEditRect = rControlRegion;
    gint indicator_size, indicator_spacing;

    if(((nType == ControlType::Checkbox) || (nType == ControlType::Radiobutton)) &&
       nPart == ControlPart::Entire)
    {
        rNativeBoundingRegion = rControlRegion;

        GtkStyleContext *pButtonStyle = (nType == ControlType::Checkbox) ? mpCheckButtonCheckStyle : mpRadioButtonRadioStyle;


        gtk_style_context_get_style( pButtonStyle,
                                     "indicator-size", &indicator_size,
                                     "indicator-spacing", &indicator_spacing,
                                     nullptr );

        GtkBorder border;
        style_context_get_border(pButtonStyle, &border);

        GtkBorder padding;
        style_context_get_padding(pButtonStyle, &padding);


        indicator_size += 2*indicator_spacing + border.left + padding.left + border.right + padding.right;
        tools::Rectangle aIndicatorRect( Point( 0,
                                         (rControlRegion.GetHeight()-indicator_size)/2),
                                  Size( indicator_size, indicator_size ) );
        rNativeContentRegion = aIndicatorRect;

        return true;
    }
    else if( nType == ControlType::MenuPopup)
    {
        if ((nPart == ControlPart::MenuItemCheckMark) ||
            (nPart == ControlPart::MenuItemRadioMark) )
        {
            indicator_size = 0;

            GtkStyleContext *pMenuItemStyle = (nPart == ControlPart::MenuItemCheckMark ) ? mpCheckMenuItemCheckStyle
                                                                                         : mpRadioMenuItemRadioStyle;

            gtk_style_context_get_style( pMenuItemStyle,
                                         "indicator-size", &indicator_size,
                                         nullptr );

            gint point = MAX(0, rControlRegion.GetHeight() - indicator_size);
            aEditRect = tools::Rectangle( Point( 0, point / 2),
                                   Size( indicator_size, indicator_size ) );
        }
        else if (nPart == ControlPart::Separator)
        {
            gint separator_height, separator_width, wide_separators;

            gtk_style_context_get_style (mpSeparatorMenuItemSeparatorStyle,
                                         "wide-separators",  &wide_separators,
                                         "separator-width",  &separator_width,
                                         "separator-height", &separator_height,
                                         nullptr);

            aEditRect = tools::Rectangle( aEditRect.TopLeft(),
                                   Size( aEditRect.GetWidth(), wide_separators ? separator_height : 1 ) );
        }
        else if (nPart == ControlPart::SubmenuArrow)
        {
            gfloat arrow_size = getArrowSize(mpMenuItemArrowStyle);
            aEditRect = tools::Rectangle( aEditRect.TopLeft(),
                                   Size( arrow_size, arrow_size ) );
        }
    }
    else if ( (nType==ControlType::Scrollbar) &&
              ((nPart==ControlPart::ButtonLeft) || (nPart==ControlPart::ButtonRight) ||
               (nPart==ControlPart::ButtonUp) || (nPart==ControlPart::ButtonDown)  ) )
    {
        rNativeBoundingRegion = NWGetScrollButtonRect( nPart, rControlRegion );
        rNativeContentRegion = rNativeBoundingRegion;

        if (!rNativeContentRegion.GetWidth())
            rNativeContentRegion.SetRight( rNativeContentRegion.Left() + 1 );
        if (!rNativeContentRegion.GetHeight())
            rNativeContentRegion.SetBottom( rNativeContentRegion.Top() + 1 );

        return true;
    }
    else if ( (nType==ControlType::Spinbox) &&
              ((nPart==ControlPart::ButtonUp) || (nPart==ControlPart::ButtonDown) ||
               (nPart==ControlPart::SubEdit)) )
    {
        tools::Rectangle aControlRegion(GetWidgetSize(rControlRegion, gSpinBox));
        aEditRect = NWGetSpinButtonRect(nPart, aControlRegion);
    }
    else if ( (nType==ControlType::Combobox) &&
              ((nPart==ControlPart::ButtonDown) || (nPart==ControlPart::SubEdit)) )
    {
        aEditRect = NWGetComboBoxButtonRect(nType, nPart, rControlRegion);
    }
    else if ( (nType==ControlType::Listbox) &&
              ((nPart==ControlPart::ButtonDown) || (nPart==ControlPart::SubEdit)) )
    {
        aEditRect = NWGetComboBoxButtonRect(nType, nPart, rControlRegion);
    }
    else if (nType == ControlType::Editbox && nPart == ControlPart::Entire)
    {
        aEditRect = GetWidgetSize(rControlRegion, gEntryBox);
    }
    else if (nType == ControlType::Listbox && nPart == ControlPart::Entire)
    {
        aEditRect = GetWidgetSize(rControlRegion, gListBox);
    }
    else if (nType == ControlType::Combobox && nPart == ControlPart::Entire)
    {
        aEditRect = GetWidgetSize(rControlRegion, gComboBox);
    }
    else if (nType == ControlType::Spinbox && nPart == ControlPart::Entire)
    {
        aEditRect = GetWidgetSize(rControlRegion, gSpinBox);
    }
    else if (nType == ControlType::TabItem && nPart == ControlPart::Entire)
    {
        const TabitemValue& rTabitemValue = static_cast<const TabitemValue&>(rValue);
        const tools::Rectangle& rTabitemRect = rTabitemValue.getContentRect();

        aEditRect = AdjustRectForTextBordersPadding(mpNotebookHeaderTabsTabStyle, rTabitemRect.GetWidth(),
                                                    rTabitemRect.GetHeight(), rControlRegion);
    }
    else if (nType == ControlType::Frame && nPart == ControlPart::Border)
    {
        aEditRect = rControlRegion;

        GtkBorder padding;
        style_context_get_padding(mpFrameInStyle, &padding);

        GtkBorder border;
        style_context_get_border(mpFrameInStyle, &border);

        int x1 = aEditRect.Left();
        int y1 = aEditRect.Top();
        int x2 = aEditRect.Right();
        int y2 = aEditRect.Bottom();

        rNativeBoundingRegion = aEditRect;
        rNativeContentRegion = tools::Rectangle(x1 + (padding.left + border.left),
                                         y1 + (padding.top + border.top),
                                         x2 - (padding.right + border.right),
                                         y2 - (padding.bottom + border.bottom));

        return true;
    }
    else
    {
        return false;
    }

    rNativeBoundingRegion = aEditRect;
    rNativeContentRegion = rNativeBoundingRegion;

    return true;
}
#endif

/************************************************************************
 * helper for GtkSalFrame
 ************************************************************************/

static ::Color getColor( const GdkRGBA& rCol )
{
    return ::Color( static_cast<int>(rCol.red * 0xFFFF) >> 8, static_cast<int>(rCol.green * 0xFFFF) >> 8, static_cast<int>(rCol.blue * 0xFFFF) >> 8 );
}

static ::Color style_context_get_background_color(GtkStyleContext* pStyle)
{
#if !GTK_CHECK_VERSION(4, 0, 0)
    GdkRGBA background_color;
    gtk_style_context_get_background_color(pStyle, gtk_style_context_get_state(pStyle), &background_color);
    return getColor(background_color);
#else
    cairo_surface_t *target = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1);
    cairo_t* cr = cairo_create(target);
    gtk_render_background(pStyle, cr, 0, 0, 1, 1);
    cairo_destroy(cr);

    cairo_surface_flush(target);
    vcl::bitmap::lookup_table const & unpremultiply_table = vcl::bitmap::get_unpremultiply_table();
    unsigned char *data = cairo_image_surface_get_data(target);
    sal_uInt8 a = data[SVP_CAIRO_ALPHA];
    sal_uInt8 b = unpremultiply_table[a][data[SVP_CAIRO_BLUE]];
    sal_uInt8 g = unpremultiply_table[a][data[SVP_CAIRO_GREEN]];
    sal_uInt8 r = unpremultiply_table[a][data[SVP_CAIRO_RED]];
    Color aColor(r, g, b);
    cairo_surface_destroy(target);

    return aColor;
#endif
}

#if !GTK_CHECK_VERSION(4, 0, 0)
static vcl::Font getFont(GtkStyleContext* pStyle, const css::lang::Locale& rLocale)
{
    const PangoFontDescription* font = gtk_style_context_get_font(pStyle, gtk_style_context_get_state(pStyle));
    return pango_to_vcl(font, rLocale);
}
#endif

vcl::Font pango_to_vcl(const PangoFontDescription* font, const css::lang::Locale& rLocale)
{
    OString    aFamily        = pango_font_description_get_family( font );
    PangoStyle    eStyle    = pango_font_description_get_style( font );
    PangoWeight    eWeight    = pango_font_description_get_weight( font );
    PangoStretch eStretch = pango_font_description_get_stretch( font );

    FontAttributes aDFA;

    // set family name
    aDFA.SetFamilyName(OStringToOUString(aFamily, RTL_TEXTENCODING_UTF8));

    // set italic
    switch( eStyle )
    {
        case PANGO_STYLE_NORMAL:    aDFA.SetItalic(ITALIC_NONE);break;
        case PANGO_STYLE_ITALIC:    aDFA.SetItalic(ITALIC_NORMAL);break;
        case PANGO_STYLE_OBLIQUE:    aDFA.SetItalic(ITALIC_OBLIQUE);break;
    }

    // set weight
    if( eWeight <= PANGO_WEIGHT_ULTRALIGHT )
        aDFA.SetWeight(WEIGHT_ULTRALIGHT);
    else if( eWeight <= PANGO_WEIGHT_LIGHT )
        aDFA.SetWeight(WEIGHT_LIGHT);
    else if( eWeight <= PANGO_WEIGHT_NORMAL )
        aDFA.SetWeight(WEIGHT_NORMAL);
    else if( eWeight <= PANGO_WEIGHT_BOLD )
        aDFA.SetWeight(WEIGHT_BOLD);
    else
        aDFA.SetWeight(WEIGHT_ULTRABOLD);

    // set width
    switch( eStretch )
    {
        case PANGO_STRETCH_ULTRA_CONDENSED:    aDFA.SetWidthType(WIDTH_ULTRA_CONDENSED);break;
        case PANGO_STRETCH_EXTRA_CONDENSED:    aDFA.SetWidthType(WIDTH_EXTRA_CONDENSED);break;
        case PANGO_STRETCH_CONDENSED:        aDFA.SetWidthType(WIDTH_CONDENSED);break;
        case PANGO_STRETCH_SEMI_CONDENSED:    aDFA.SetWidthType(WIDTH_SEMI_CONDENSED);break;
        case PANGO_STRETCH_NORMAL:            aDFA.SetWidthType(WIDTH_NORMAL);break;
        case PANGO_STRETCH_SEMI_EXPANDED:    aDFA.SetWidthType(WIDTH_SEMI_EXPANDED);break;
        case PANGO_STRETCH_EXPANDED:        aDFA.SetWidthType(WIDTH_EXPANDED);break;
        case PANGO_STRETCH_EXTRA_EXPANDED:    aDFA.SetWidthType(WIDTH_EXTRA_EXPANDED);break;
        case PANGO_STRETCH_ULTRA_EXPANDED:    aDFA.SetWidthType(WIDTH_ULTRA_EXPANDED);break;
    }

#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.gtk3""font name BEFORE system match: \""
            << aFamily << "\".");
#endif

    // match font to e.g. resolve "Sans"
    bool bFound = psp::PrintFontManager::get().matchFont(aDFA, rLocale);

#if OSL_DEBUG_LEVEL > 1
    SAL_INFO("vcl.gtk3""font match "
            << (bFound ? "succeeded" : "failed")
            << ", name AFTER: \""
            << aDFA.GetFamilyName()
            << "\".");
#else
    (void) bFound;
#endif

    int nPangoHeight = pango_font_description_get_size(font) / PANGO_SCALE;

    if (pango_font_description_get_size_is_absolute(font))
    {
        const sal_Int32 nDPIY = 96;
        nPangoHeight = nPangoHeight * 72;
        nPangoHeight = nPangoHeight + nDPIY / 2;
        nPangoHeight = nPangoHeight / nDPIY;
    }

    vcl::Font aFont(aDFA.GetFamilyName(), Size(0, nPangoHeight));
    if (aDFA.GetWeight() != WEIGHT_DONTKNOW)
        aFont.SetWeight(aDFA.GetWeight());
    if (aDFA.GetWidthType() != WIDTH_DONTKNOW)
        aFont.SetWidthType(aDFA.GetWidthType());
    if (aDFA.GetItalic() != ITALIC_DONTKNOW)
        aFont.SetItalic(aDFA.GetItalic());
    if (aDFA.GetPitch() != PITCH_DONTKNOW)
        aFont.SetPitch(aDFA.GetPitch());
    return aFont;
}

bool GtkSalGraphics::updateSettings(AllSettings& rSettings)
{
    GtkWidget* pTopLevel = widget_get_toplevel(mpWindow);
    GtkStyleContext* pStyle = gtk_widget_get_style_context(pTopLevel);
    StyleContextSave aContextState;
    aContextState.save(pStyle);
    GtkSettings* pSettings = gtk_widget_get_settings(pTopLevel);
    StyleSettings aStyleSet = rSettings.GetStyleSettings();

#if GTK_CHECK_VERSION(4, 0, 0)
    CustomTheme::ApplyCustomTheme(gtk_widget_get_display(pTopLevel), &mpCustomThemeProvider);
#else
    CustomTheme::ApplyCustomTheme(gtk_widget_get_screen(pTopLevel), &mpCustomThemeProvider);
#endif

    // text colors
    GdkRGBA text_color;
    style_context_set_state(pStyle, GTK_STATE_FLAG_NORMAL);
    style_context_get_color(pStyle, &text_color);
    ::Color aTextColor = getColor( text_color );
    aStyleSet.SetDialogTextColor( aTextColor );
    aStyleSet.SetButtonTextColor( aTextColor );
    aStyleSet.SetDefaultActionButtonTextColor(aTextColor);
    aStyleSet.SetActionButtonTextColor(aTextColor);
    aStyleSet.SetListBoxWindowTextColor( aTextColor );
    aStyleSet.SetRadioCheckTextColor( aTextColor );
    aStyleSet.SetGroupTextColor( aTextColor );
    aStyleSet.SetLabelTextColor( aTextColor );
    aStyleSet.SetWindowTextColor( aTextColor );
    aStyleSet.SetFieldTextColor( aTextColor );

    // background colors
    ::Color aBackColor = style_context_get_background_color(pStyle);
    aStyleSet.BatchSetBackgrounds( aBackColor );
    aStyleSet.SetWindowColor(aBackColor);

    // UI font
#if GTK_CHECK_VERSION(4, 0, 0)
    gchar* pFontname = nullptr;
    g_object_get(pSettings, "gtk-font-name", &pFontname, nullptr);
    PangoFontDescription* pFontDesc = pango_font_description_from_string(pFontname);
    g_free(pFontname);
    vcl::Font aFont(pango_to_vcl(pFontDesc, rSettings.GetUILanguageTag().getLocale()));
    pango_font_description_free(pFontDesc);
#else
    vcl::Font aFont(getFont(pStyle, rSettings.GetUILanguageTag().getLocale()));
#endif

    aStyleSet.BatchSetFonts( aFont, aFont);

    aFont.SetWeight( WEIGHT_BOLD );
    aStyleSet.SetTitleFont( aFont );
    aStyleSet.SetFloatTitleFont( aFont );

    // mouse over text colors
    style_context_set_state(pStyle, GTK_STATE_FLAG_PRELIGHT);
    style_context_get_color(pStyle, &text_color);
    aTextColor = getColor(text_color);
    aStyleSet.SetDefaultButtonTextColor(aTextColor);
    aStyleSet.SetDefaultButtonRolloverTextColor(aTextColor);
    aStyleSet.SetDefaultButtonPressedRolloverTextColor(aTextColor);
    aStyleSet.SetButtonRolloverTextColor(aTextColor);
    aStyleSet.SetDefaultActionButtonRolloverTextColor(aTextColor);
    aStyleSet.SetDefaultActionButtonPressedRolloverTextColor(aTextColor);
    aStyleSet.SetActionButtonRolloverTextColor(aTextColor);
    aStyleSet.SetActionButtonPressedRolloverTextColor(aTextColor);
    aStyleSet.SetFlatButtonTextColor(aTextColor);
    aStyleSet.SetFlatButtonPressedRolloverTextColor(aTextColor);
    aStyleSet.SetFlatButtonRolloverTextColor(aTextColor);
    aStyleSet.SetFieldRolloverTextColor(aTextColor);

    aContextState.restore();

    // button mouse over colors
    {
        GdkRGBA normal_button_rollover_text_color, pressed_button_rollover_text_color;
        aContextState.save(mpButtonStyle);
        style_context_set_state(mpButtonStyle, GTK_STATE_FLAG_PRELIGHT);
        style_context_get_color(mpButtonStyle, &normal_button_rollover_text_color);
        aTextColor = getColor(normal_button_rollover_text_color);
        aStyleSet.SetButtonRolloverTextColor( aTextColor );
        style_context_set_state(mpButtonStyle, static_cast<GtkStateFlags>(GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_ACTIVE));
        style_context_get_color(mpButtonStyle, &pressed_button_rollover_text_color);
        aTextColor = getColor(pressed_button_rollover_text_color);
        style_context_set_state(mpButtonStyle, GTK_STATE_FLAG_NORMAL);
        aStyleSet.SetButtonPressedRolloverTextColor( aTextColor );
        aContextState.restore();
    }

#if !GTK_CHECK_VERSION(4, 0, 0)
    // tooltip colors
    {
        GtkWidgetPath *pCPath = gtk_widget_path_new();
        guint pos = gtk_widget_path_append_type(pCPath, GTK_TYPE_WINDOW);
        gtk_widget_path_iter_add_class(pCPath, pos, GTK_STYLE_CLASS_TOOLTIP);
        pos = gtk_widget_path_append_type (pCPath, GTK_TYPE_LABEL);
        gtk_widget_path_iter_add_class(pCPath, pos, GTK_STYLE_CLASS_LABEL);
        GtkStyleContext *pCStyle = makeContext (pCPath, nullptr);
        aContextState.save(pCStyle);

        GdkRGBA tooltip_fg_color;
        style_context_set_state(pCStyle, GTK_STATE_FLAG_NORMAL);
        style_context_get_color(pCStyle, &tooltip_fg_color);
        ::Color aTooltipBgColor = style_context_get_background_color(pCStyle);

        aContextState.restore();
        g_object_unref( pCStyle );

        aStyleSet.SetHelpColor(aTooltipBgColor);
        aStyleSet.SetHelpTextColor( getColor( tooltip_fg_color ));
    }
#endif

    GdkRGBA color;
    {
#if !GTK_CHECK_VERSION(4, 0, 0)
        // construct style context for text view
        GtkWidgetPath *pCPath = gtk_widget_path_new();
        gtk_widget_path_append_type( pCPath, GTK_TYPE_TEXT_VIEW );
        gtk_widget_path_iter_add_class( pCPath, -1, GTK_STYLE_CLASS_VIEW );
        GtkStyleContext *pCStyle = makeContext( pCPath, nullptr );
#else
        GtkStyleContext *pCStyle = gtk_widget_get_style_context(gTextView);
#endif
        aContextState.save(pCStyle);

        // highlighting colors
        style_context_set_state(pCStyle, GTK_STATE_FLAG_SELECTED);
        ::Color aHighlightColor = style_context_get_background_color(pCStyle);
        style_context_get_color(pCStyle, &text_color);
        ::Color aHighlightTextColor = getColor( text_color );
        aStyleSet.SetAccentColor( aHighlightColor ); // https://debugpointnews.com/gnome-native-accent-colour-announcement/
        aStyleSet.SetHighlightColor( aHighlightColor );
        aStyleSet.SetMenuHighlightColor( aHighlightColor );
        aStyleSet.SetHighlightTextColor( aHighlightTextColor );
        aStyleSet.SetListBoxWindowHighlightColor( aHighlightColor );
        aStyleSet.SetListBoxWindowHighlightTextColor( aHighlightTextColor );
        // make active like highlight, except with a small contrast. Note, see
        // a GtkListBoxRow in a GtkStackSidebar for a gtk widget with a
        // difference between highlighted and highlighted with focus.
        aHighlightColor.IncreaseLuminance(16);
        aStyleSet.SetActiveColor( aHighlightColor );
        aStyleSet.SetActiveTextColor( aHighlightTextColor );

        // field background color
        style_context_set_state(pCStyle, GTK_STATE_FLAG_NORMAL);
        ::Color aBackFieldColor = style_context_get_background_color(pCStyle);
        aStyleSet.SetFieldColor( aBackFieldColor );
        // listbox background color
        aStyleSet.SetListBoxWindowBackgroundColor( aBackFieldColor );

#if GTK_CHECK_VERSION(4, 0, 0)
        double caretAspectRatio = 0.04f;
        g_object_get(pSettings, "gtk-cursor-aspect-ratio", &caretAspectRatio, nullptr);
#else
        // Cursor width
        gfloat caretAspectRatio = 0.04f;
        gtk_style_context_get_style( pCStyle, "cursor-aspect-ratio", &caretAspectRatio, nullptr );
#endif
        // Assume 20px tall for the ratio computation, which should give reasonable results
        aStyleSet.SetCursorSize(20 * caretAspectRatio + 1);

        // Dark shadow color
        style_context_set_state(pCStyle, GTK_STATE_FLAG_INSENSITIVE);
        style_context_get_color(pCStyle, &color);
        ::Color aDarkShadowColor = getColor( color );
        aStyleSet.SetDarkShadowColor( aDarkShadowColor );

        ::Color aShadowColor(aBackColor);
        if (aDarkShadowColor.GetLuminance() > aBackColor.GetLuminance())
            aShadowColor.IncreaseLuminance(64);
        else
            aShadowColor.DecreaseLuminance(64);
        aStyleSet.SetShadowColor(aShadowColor);

        ::Color aDisabledColor(aBackFieldColor);
        if (aBackFieldColor.GetLuminance() > aBackColor.GetLuminance())
            aDisabledColor.IncreaseLuminance(8);
        else
            aDisabledColor.DecreaseLuminance(8);
        aStyleSet.SetDisableColor(aDisabledColor);

        aContextState.restore();
#if !GTK_CHECK_VERSION(4, 0, 0)
        g_object_unref( pCStyle );
#endif

        // Tab colors
        aStyleSet.SetActiveTabColor( aBackFieldColor ); // same as the window color.
        aStyleSet.SetInactiveTabColor( aBackColor );
    }

    // menu disabled entries handling
    aStyleSet.SetSkipDisabledInMenus( true );
    aStyleSet.SetPreferredContextMenuShortcuts( false );

#if !GTK_CHECK_VERSION(4, 0, 0)
    aContextState.save(mpMenuItemLabelStyle);

    // menu colors
    style_context_set_state(mpMenuStyle, GTK_STATE_FLAG_NORMAL);
    aBackColor = style_context_get_background_color(mpMenuStyle);
    aStyleSet.SetMenuColor( aBackColor );

    // menu bar
    style_context_set_state(mpMenuBarStyle, GTK_STATE_FLAG_NORMAL);
    aBackColor = style_context_get_background_color(mpMenuBarStyle);
    aStyleSet.SetMenuBarColor( aBackColor );

    style_context_set_state(mpMenuBarStyle, GTK_STATE_FLAG_SELECTED);
    aBackColor = style_context_get_background_color(mpMenuBarStyle);
    aStyleSet.SetMenuBarRolloverColor( aBackColor );

    style_context_set_state(mpMenuBarItemStyle, GTK_STATE_FLAG_NORMAL);
    style_context_get_color(mpMenuBarItemStyle, &text_color);
    aTextColor = getColor( text_color );
    aStyleSet.SetMenuBarTextColor( aTextColor );
    aStyleSet.SetMenuBarRolloverTextColor( aTextColor );

    style_context_set_state(mpMenuBarItemStyle, GTK_STATE_FLAG_PRELIGHT);
    style_context_get_color(mpMenuBarItemStyle, &text_color);
    aTextColor =  getColor( text_color );
    aStyleSet.SetMenuBarHighlightTextColor( aTextColor );

    // menu items
    style_context_set_state(mpMenuItemLabelStyle, GTK_STATE_FLAG_NORMAL);
    style_context_get_color(mpMenuItemLabelStyle, &color);
    aTextColor = getColor(color);
    aStyleSet.SetMenuTextColor(aTextColor);

    style_context_set_state(mpMenuItemLabelStyle, GTK_STATE_FLAG_PRELIGHT);
    style_context_get_color(mpMenuItemLabelStyle, &color);
    ::Color aHighlightTextColor = getColor( color );
    aStyleSet.SetMenuHighlightTextColor( aHighlightTextColor );

    aContextState.restore();
#endif

    // hyperlink colors
    aContextState.save(mpLinkButtonStyle);
    style_context_set_state(mpLinkButtonStyle, GTK_STATE_FLAG_LINK);
    style_context_get_color(mpLinkButtonStyle, &text_color);
    aStyleSet.SetLinkColor(getColor(text_color));
    style_context_set_state(mpLinkButtonStyle, GTK_STATE_FLAG_VISITED);
    style_context_get_color(mpLinkButtonStyle, &text_color);
    aStyleSet.SetVisitedLinkColor(getColor(text_color));
    aContextState.restore();

#if !GTK_CHECK_VERSION(4, 0, 0)
    {
        GtkStyleContext *pCStyle = mpNotebookHeaderTabsTabLabelStyle;
        aContextState.save(pCStyle);
        style_context_set_state(pCStyle, GTK_STATE_FLAG_NORMAL);
        style_context_get_color(pCStyle, &text_color);
        aTextColor = getColor( text_color );
        aStyleSet.SetTabTextColor(aTextColor);
        aStyleSet.SetTabFont(getFont(mpNotebookHeaderTabsTabLabelStyle, rSettings.GetUILanguageTag().getLocale()));
        aContextState.restore();
    }

    {
        GtkStyleContext *pCStyle = mpToolButtonStyle;
        aContextState.save(pCStyle);
        style_context_set_state(pCStyle, GTK_STATE_FLAG_NORMAL);
        style_context_get_color(pCStyle, &text_color);
        aTextColor = getColor( text_color );
        aStyleSet.SetToolTextColor(aTextColor);
        aStyleSet.SetToolFont(getFont(mpToolButtonStyle, rSettings.GetUILanguageTag().getLocale()));
        aContextState.restore();
    }

    // mouse over text colors
    {
        GtkStyleContext *pCStyle = mpNotebookHeaderTabsTabHoverLabelStyle;
        aContextState.save(pCStyle);
        style_context_set_state(pCStyle, GTK_STATE_FLAG_PRELIGHT);
        style_context_get_color(pCStyle, &text_color);
        aTextColor = getColor( text_color );
        aStyleSet.SetTabRolloverTextColor(aTextColor);
        aContextState.restore();
    }

    {
        GtkStyleContext *pCStyle = mpNotebookHeaderTabsTabActiveLabelStyle;
        aContextState.save(pCStyle);
        style_context_set_state(pCStyle, GTK_STATE_FLAG_CHECKED);
        style_context_get_color(pCStyle, &text_color);
        aTextColor = getColor( text_color );
        aStyleSet.SetTabHighlightTextColor(aTextColor);
        aContextState.restore();
    }
#endif

    // match native GtkComboBox behavior of putting text cursor to start of text
    // without text selection when combobox entry is selected
    aStyleSet.SetComboBoxTextSelectionMode(ComboBoxTextSelectionMode::CursorToStart);

    // get cursor blink time
    gboolean blink = false;

    g_object_get( pSettings, "gtk-cursor-blink", &blink, nullptr );
    if( blink )
    {
        gint blink_time = static_cast<gint>(STYLE_CURSOR_NOBLINKTIME);
        g_object_get( pSettings, "gtk-cursor-blink-time", &blink_time, nullptr );
        // set the blink_time if there is a setting and it is reasonable
        // else leave the default value
        if( blink_time > 100 )
            aStyleSet.SetCursorBlinkTime( blink_time/2 );
    }
    else
        aStyleSet.SetCursorBlinkTime( STYLE_CURSOR_NOBLINKTIME );

    MouseSettings aMouseSettings = rSettings.GetMouseSettings();
    int iDoubleClickTime, iDoubleClickDistance, iDragThreshold;
    static const int MENU_POPUP_DELAY = 225;
    g_object_get( pSettings,
                  "gtk-double-click-time", &iDoubleClickTime,
                  "gtk-double-click-distance", &iDoubleClickDistance,
                  "gtk-dnd-drag-threshold", &iDragThreshold,
                  nullptr );
    aMouseSettings.SetDoubleClickTime( iDoubleClickTime );
    aMouseSettings.SetDoubleClickWidth( iDoubleClickDistance );
    aMouseSettings.SetDoubleClickHeight( iDoubleClickDistance );
    aMouseSettings.SetStartDragWidth( iDragThreshold );
    aMouseSettings.SetStartDragHeight( iDragThreshold );
    aMouseSettings.SetMenuDelay( MENU_POPUP_DELAY );
    rSettings.SetMouseSettings( aMouseSettings );

    gboolean primarybuttonwarps = false;
    g_object_get( pSettings,
        "gtk-primary-button-warps-slider", &primarybuttonwarps,
        nullptr );
    aStyleSet.SetPreferredUseImagesInMenus(false);
    aStyleSet.SetPrimaryButtonWarpsSlider(primarybuttonwarps);

    // set scrollbar settings
    gint min_slider_length = 21;

    GtkRequisition natural_horz_scroll_size;
    gtk_widget_get_preferred_size(gHScrollbar, nullptr, &natural_horz_scroll_size);

#if GTK_CHECK_VERSION(4, 0, 0)
    aStyleSet.SetScrollBarSize(natural_horz_scroll_size.height);
#else
    // Grab some button style attributes
    Size aSize;
    QuerySize(mpHScrollbarStyle, aSize);
    QuerySize(mpHScrollbarContentsStyle, aSize);
    QuerySize(mpHScrollbarTroughStyle, aSize);
    QuerySize(mpHScrollbarSliderStyle, aSize);

    gboolean has_forward, has_forward2, has_backward, has_backward2;
    gtk_style_context_get_style(mpHScrollbarStyle,
                                "has-forward-stepper", &has_forward,
                                "has-secondary-forward-stepper", &has_forward2,
                                "has-backward-stepper", &has_backward,
                                "has-secondary-backward-stepper", &has_backward2, nullptr);
    if (has_forward || has_backward || has_forward2 || has_backward2)
        QuerySize(mpHScrollbarButtonStyle, aSize);

    // Recent breeze (Mar 2024) has 17 vs 10, while Adwaita still reports 14 vs 14.
    if (natural_horz_scroll_size.height > aSize.Height())
        aSize.setHeight(natural_horz_scroll_size.height);

    aStyleSet.SetScrollBarSize(aSize.Height());

    gtk_style_context_get(mpVScrollbarSliderStyle, gtk_style_context_get_state(mpVScrollbarSliderStyle),
                          "min-height", &min_slider_length,
                          nullptr);
#endif
    aStyleSet.SetMinThumbSize(min_slider_length);

    // preferred icon style
    if (!ThemeColors::VclPluginCanUseThemeColors())
    {
        gchar* pIconThemeName = nullptr;
        gboolean bDarkIconTheme = false;
        g_object_get(pSettings, "gtk-icon-theme-name", &pIconThemeName,
                "gtk-application-prefer-dark-theme", &bDarkIconTheme,
                nullptr );
        OUString sIconThemeName(OUString::createFromAscii(pIconThemeName));
        aStyleSet.SetPreferredIconTheme(sIconThemeName, bDarkIconTheme);
        g_free( pIconThemeName );
    }
    else
    {
        aStyleSet.SetPreferredIconTheme(vcl::IconThemeSelector::GetIconThemeForDesktopEnvironment(
            Application::GetDesktopEnvironment(), ThemeColors::GetThemeColors().GetWindowColor().IsDark()));
    }

    aStyleSet.SetToolbarIconSize( ToolbarIconSize::Large );

    gchar* pThemeName = nullptr;
    g_object_get( pSettings, "gtk-theme-name", &pThemeName, nullptr );
    SAL_INFO("vcl.gtk3""Theme name is \""
            << pThemeName
            << "\".");
    // High contrast
    aStyleSet.SetHighContrastMode(g_strcmp0(pThemeName, "HighContrast") == 0);
    g_free(pThemeName);
    aStyleSet.SetSystemColorsLoaded(true);

    // finally update the collected settings
    rSettings.SetStyleSettings( aStyleSet );

    return true;
}

#if !GTK_CHECK_VERSION(4, 0, 0)
bool GtkSalGraphics::isNativeControlSupported( ControlType nType, ControlPart nPart )
{
    switch(nType)
    {
        case ControlType::Pushbutton:
        case ControlType::Radiobutton:
        case ControlType::Checkbox:
        case ControlType::Progress:
        case ControlType::ListNode:
        case ControlType::ListNet:
            if (nPart==ControlPart::Entire || nPart == ControlPart::Focus)
                return true;
            break;

        case ControlType::Scrollbar:
            if(nPart==ControlPart::DrawBackgroundHorz || nPart==ControlPart::DrawBackgroundVert ||
               nPart==ControlPart::Entire       || nPart==ControlPart::HasThreeButtons)
                return true;
            break;

        case ControlType::Editbox:
        case ControlType::MultilineEditbox:
            if (nPart==ControlPart::Entire || nPart==ControlPart::HasBackgroundTexture)
                return true;
            break;

        case ControlType::Combobox:
            if (nPart==ControlPart::Entire || nPart==ControlPart::HasBackgroundTexture || nPart == ControlPart::AllButtons)
                return true;
            break;

        case ControlType::Spinbox:
            if (nPart==ControlPart::Entire || nPart==ControlPart::HasBackgroundTexture || nPart == ControlPart::AllButtons || nPart == ControlPart::ButtonUp || nPart == ControlPart::ButtonDown)
                return true;
            break;

        case ControlType::SpinButtons:
            if (nPart==ControlPart::Entire || nPart==ControlPart::AllButtons)
                return true;
            break;

        case ControlType::Frame:
        case ControlType::WindowBackground:
            return true;

        case ControlType::TabItem:
        case ControlType::TabHeader:
        case ControlType::TabPane:
        case ControlType::TabBody:
            if(nPart==ControlPart::Entire || nPart==ControlPart::TabsDrawRtl)
                return true;
            break;

        case ControlType::Listbox:
            if (nPart==ControlPart::Entire || nPart==ControlPart::ListboxWindow || nPart==ControlPart::HasBackgroundTexture || nPart == ControlPart::Focus)
                return true;
            break;

        case ControlType::Toolbar:
            if( nPart==ControlPart::Entire
//                ||  nPart==ControlPart::DrawBackgroundHorz
//                ||  nPart==ControlPart::DrawBackgroundVert
//                ||  nPart==ControlPart::ThumbHorz
//                ||  nPart==ControlPart::ThumbVert
                ||  nPart==ControlPart::Button
//                ||  nPart==ControlPart::SeparatorHorz
                ||  nPart==ControlPart::SeparatorVert
                )
                return true;
            break;

        case ControlType::Menubar:
            if (nPart==ControlPart::Entire || nPart==ControlPart::MenuItem)
                return true;
            break;

        case ControlType::MenuPopup:
            if (nPart==ControlPart::Entire
                ||  nPart==ControlPart::MenuItem
                ||  nPart==ControlPart::MenuItemCheckMark
                ||  nPart==ControlPart::MenuItemRadioMark
                ||  nPart==ControlPart::Separator
                ||  nPart==ControlPart::SubmenuArrow
            )
                return true;
            break;

//        case ControlType::Slider:
//            if(nPart == ControlPart::TrackHorzArea || nPart == ControlPart::TrackVertArea)
//                return true;
//            break;

        case ControlType::Fixedline:
            if (nPart == ControlPart::SeparatorVert || nPart == ControlPart::SeparatorHorz)
                return true;
            break;

        case ControlType::ListHeader:
            if (nPart == ControlPart::Button || nPart == ControlPart::Arrow)
                return true;
            break;
        defaultbreak;
    }

    SAL_INFO("vcl.gtk""Unhandled is native supported for Type:" << static_cast<int>(nType) << ", Part" << static_cast<int>(nPart));
    return false;
}
#endif

#if ENABLE_CAIRO_CANVAS

bool GtkSalGraphics::SupportsCairo() const
{
    return true;
}

cairo::SurfaceSharedPtr GtkSalGraphics::CreateSurface(const cairo::CairoSurfaceSharedPtr& rSurface) const
{
    return std::make_shared<cairo::Gtk3Surface>(rSurface);
}

cairo::SurfaceSharedPtr GtkSalGraphics::CreateSurface(const OutputDevice& /*rRefDevice*/, int x, int y, int width, int height) const
{
    return std::make_shared<cairo::Gtk3Surface>(this, x, y, width, height);
}

#endif

void GtkSalGraphics::WidgetQueueDraw() const
{
    //request gtk to sync the entire contents
    mpFrame->queue_draw();
}

namespace {

void getStyleContext(GtkStyleContext** style, GtkWidget* widget)
{
#if GTK_CHECK_VERSION(4, 0, 0)
    gtk_fixed_put(GTK_FIXED(gDumbContainer), widget, 0, 0);
#else
    gtk_container_add(GTK_CONTAINER(gDumbContainer), widget);
#endif
    *style = gtk_widget_get_style_context(widget);
    g_object_ref(*style);
}

}

void GtkSalData::initNWF()
{
    ImplSVData* pSVData = ImplGetSVData();
    pSVData->maNWFData.mbFlatMenu = true;
    pSVData->maNWFData.mbDockingAreaAvoidTBFrames = true;
    pSVData->maNWFData.mbCanDrawWidgetAnySize = true;
    pSVData->maNWFData.mbDDListBoxNoTextArea = true;
    pSVData->maNWFData.mbNoFocusRects = true;
    pSVData->maNWFData.mbNoFocusRectsForFlatButtons = true;
    pSVData->maNWFData.mbAutoAccel = true;
#if GTK_CHECK_VERSION(4, 0, 0)
    pSVData->maNWFData.mbNoFrameJunctionForPopups = true;
#endif

#if defined(GDK_WINDOWING_WAYLAND)
    //gnome#768128 for the car crash that is wayland
    //and floating dockable toolbars
    GdkDisplay *pDisplay = gdk_display_get_default();
    if (DLSYM_GDK_IS_WAYLAND_DISPLAY(pDisplay))
        pSVData->maNWFData.mbCanDetermineWindowPosition = false;
#endif
}

void GtkSalData::deInitNWF()
{
#if !GTK_CHECK_VERSION(4, 0, 0)
    if (gCacheWindow)
        gtk_widget_destroy(gCacheWindow);
#endif
}

GtkSalGraphics::GtkSalGraphics( GtkSalFrame *pFrame, GtkWidget *pWindow )
    : mpFrame( pFrame ),
      mpWindow( pWindow )
{
    if (style_loaded)
        return;

    style_loaded = true;

    /* Load the GtkStyleContexts, it might be a bit slow, but usually,
     * gtk apps create a lot of widgets at startup, so, it shouldn't be
     * too slow */


#if GTK_CHECK_VERSION(4, 0, 0)
    gCacheWindow = gtk_window_new();
#else
    gCacheWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
#endif
    gDumbContainer = gtk_fixed_new();
#if GTK_CHECK_VERSION(4, 0, 0)
    gtk_window_set_child(GTK_WINDOW(gCacheWindow), gDumbContainer);
#else
    gtk_container_add(GTK_CONTAINER(gCacheWindow), gDumbContainer);
#endif
    gtk_widget_realize(gDumbContainer);
    gtk_widget_realize(gCacheWindow);

    gEntryBox = gtk_entry_new();
#if GTK_CHECK_VERSION(4, 0, 0)
    gtk_fixed_put(GTK_FIXED(gDumbContainer), gEntryBox, 0, 0);
#else
    gtk_container_add(GTK_CONTAINER(gDumbContainer), gEntryBox);
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
    mpWindowStyle = createStyleContext(GtkControlPart::ToplevelWindow);
    mpEntryStyle = createStyleContext(GtkControlPart::Entry);
#else
    mpWindowStyle = gtk_widget_get_style_context(gCacheWindow);
    getStyleContext(&mpEntryStyle, gtk_entry_new());
#endif

    getStyleContext(&mpTextViewStyle, gtk_text_view_new());

#if !GTK_CHECK_VERSION(4, 0, 0)
    mpButtonStyle = createStyleContext(GtkControlPart::Button);
    mpLinkButtonStyle = createStyleContext(GtkControlPart::LinkButton);
#else
    getStyleContext(&mpButtonStyle, gtk_button_new());
    getStyleContext(&mpLinkButtonStyle, gtk_link_button_new("https://www.libreoffice.org"));
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
    GtkWidget* pToolbar = gtk_toolbar_new();
    mpToolbarStyle = gtk_widget_get_style_context(pToolbar);
    gtk_style_context_add_class(mpToolbarStyle, GTK_STYLE_CLASS_TOOLBAR);

    GtkToolItem *item = gtk_separator_tool_item_new();
    gtk_toolbar_insert(GTK_TOOLBAR(pToolbar), item, -1);
    mpToolbarSeparatorStyle = gtk_widget_get_style_context(GTK_WIDGET(item));
    gtk_style_context_get(mpToolbarSeparatorStyle,
        gtk_style_context_get_state(mpToolbarSeparatorStyle),
        "min-width", &mnVerticalSeparatorMinWidth, nullptr);

    GtkWidget *pButton = gtk_button_new();
    item = gtk_tool_button_new(pButton, nullptr);
    gtk_toolbar_insert(GTK_TOOLBAR(pToolbar), item, -1);
    mpToolButtonStyle = gtk_widget_get_style_context(GTK_WIDGET(pButton));
#endif

    gHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, nullptr);
    gtk_fixed_put(GTK_FIXED(gDumbContainer), gHScrollbar, 0, 0);
    gtk_widget_set_visible(gHScrollbar, true);

#if GTK_CHECK_VERSION(4, 0, 0)
    gVScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, nullptr);
    gtk_fixed_put(GTK_FIXED(gDumbContainer), gVScrollbar, 0, 0);
    gtk_widget_set_visible(gVScrollbar, true);
    mpVScrollbarStyle = gtk_widget_get_style_context(gVScrollbar);

    mpHScrollbarStyle = gtk_widget_get_style_context(gHScrollbar);

    gTextView = gtk_text_view_new();
    gtk_fixed_put(GTK_FIXED(gDumbContainer), gTextView, 0, 0);
    gtk_widget_set_visible(gTextView, true);
#else
    mpVScrollbarStyle = createStyleContext(GtkControlPart::ScrollbarVertical);
    mpVScrollbarContentsStyle = createStyleContext(GtkControlPart::ScrollbarVerticalContents);
    mpVScrollbarTroughStyle = createStyleContext(GtkControlPart::ScrollbarVerticalTrough);
    mpVScrollbarSliderStyle = createStyleContext(GtkControlPart::ScrollbarVerticalSlider);
    mpVScrollbarButtonStyle = createStyleContext(GtkControlPart::ScrollbarVerticalButton);
    mpHScrollbarStyle = createStyleContext(GtkControlPart::ScrollbarHorizontal);
    mpHScrollbarContentsStyle = createStyleContext(GtkControlPart::ScrollbarHorizontalContents);
    mpHScrollbarTroughStyle = createStyleContext(GtkControlPart::ScrollbarHorizontalTrough);
    mpHScrollbarSliderStyle = createStyleContext(GtkControlPart::ScrollbarHorizontalSlider);
    mpHScrollbarButtonStyle = createStyleContext(GtkControlPart::ScrollbarHorizontalButton);
#endif

#if !GTK_CHECK_VERSION(4, 0, 0)
    mpCheckButtonStyle = createStyleContext(GtkControlPart::CheckButton);
    mpCheckButtonCheckStyle = createStyleContext(GtkControlPart::CheckButtonCheck);

    mpRadioButtonStyle = createStyleContext(GtkControlPart::RadioButton);
    mpRadioButtonRadioStyle = createStyleContext(GtkControlPart::RadioButtonRadio);

    /* Spinbutton */
    gSpinBox = gtk_spin_button_new(nullptr, 0, 0);
    gtk_container_add(GTK_CONTAINER(gDumbContainer), gSpinBox);
    mpSpinStyle = createStyleContext(GtkControlPart::SpinButton);
    mpSpinUpStyle = createStyleContext(GtkControlPart::SpinButtonUpButton);
    mpSpinDownStyle = createStyleContext(GtkControlPart::SpinButtonDownButton);

    /* NoteBook */
    mpNotebookStyle = createStyleContext(GtkControlPart::Notebook);
    mpNotebookStackStyle = createStyleContext(GtkControlPart::NotebookStack);
    mpNotebookHeaderStyle = createStyleContext(GtkControlPart::NotebookHeader);
    mpNotebookHeaderTabsStyle = createStyleContext(GtkControlPart::NotebookHeaderTabs);
    mpNotebookHeaderTabsTabStyle = createStyleContext(GtkControlPart::NotebookHeaderTabsTab);
    mpNotebookHeaderTabsTabLabelStyle = createStyleContext(GtkControlPart::NotebookHeaderTabsTabLabel);
    mpNotebookHeaderTabsTabActiveLabelStyle = createStyleContext(GtkControlPart::NotebookHeaderTabsTabActiveLabel);
    mpNotebookHeaderTabsTabHoverLabelStyle = createStyleContext(GtkControlPart::NotebookHeaderTabsTabHoverLabel);

    /* Combobox */
    gComboBox = gtk_combo_box_text_new_with_entry();
    gtk_container_add(GTK_CONTAINER(gDumbContainer), gComboBox);
    mpComboboxStyle = createStyleContext(GtkControlPart::Combobox);
    mpComboboxBoxStyle = createStyleContext(GtkControlPart::ComboboxBox);
    mpComboboxEntryStyle = createStyleContext(GtkControlPart::ComboboxBoxEntry);
    mpComboboxButtonStyle = createStyleContext(GtkControlPart::ComboboxBoxButton);
    mpComboboxButtonBoxStyle = createStyleContext(GtkControlPart::ComboboxBoxButtonBox);
    mpComboboxButtonArrowStyle = createStyleContext(GtkControlPart::ComboboxBoxButtonBoxArrow);

    /* Listbox */
    gListBox = gtk_combo_box_text_new();
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(gListBox), "sample");
    gtk_container_add(GTK_CONTAINER(gDumbContainer), gListBox);
    mpListboxStyle = createStyleContext(GtkControlPart::Listbox);
    mpListboxBoxStyle = createStyleContext(GtkControlPart::ListboxBox);
    mpListboxButtonStyle = createStyleContext(GtkControlPart::ListboxBoxButton);
    mpListboxButtonBoxStyle = createStyleContext(GtkControlPart::ListboxBoxButtonBox);
    mpListboxButtonArrowStyle = createStyleContext(GtkControlPart::ListboxBoxButtonBoxArrow);

    mpMenuBarStyle = createStyleContext(GtkControlPart::MenuBar);
    mpMenuBarItemStyle = createStyleContext(GtkControlPart::MenuBarItem);

    /* Menu */
    mpMenuWindowStyle = createStyleContext(GtkControlPart::MenuWindow);
    mpMenuStyle = createStyleContext(GtkControlPart::Menu);

    mpMenuItemStyle = createStyleContext(GtkControlPart::MenuItem);
    mpMenuItemLabelStyle = createStyleContext(GtkControlPart::MenuItemLabel);
    mpMenuItemArrowStyle = createStyleContext(GtkControlPart::MenuItemArrow);
    mpCheckMenuItemStyle = createStyleContext(GtkControlPart::CheckMenuItem);
    mpCheckMenuItemCheckStyle = createStyleContext(GtkControlPart::CheckMenuItemCheck);
    mpRadioMenuItemStyle = createStyleContext(GtkControlPart::RadioMenuItem);
    mpRadioMenuItemRadioStyle = createStyleContext(GtkControlPart::RadioMenuItemRadio);
    mpSeparatorMenuItemStyle = createStyleContext(GtkControlPart::SeparatorMenuItem);
    mpSeparatorMenuItemSeparatorStyle = createStyleContext(GtkControlPart::SeparatorMenuItemSeparator);

    /* Frames */
    mpFrameOutStyle = mpFrameInStyle = createStyleContext(GtkControlPart::FrameBorder);
    getStyleContext(&mpFixedHoriLineStyle, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL));
    getStyleContext(&mpFixedVertLineStyle, gtk_separator_new(GTK_ORIENTATION_VERTICAL));


    /* Tree List */
    gTreeViewWidget = gtk_tree_view_new();
    gtk_container_add(GTK_CONTAINER(gDumbContainer), gTreeViewWidget);

    GtkTreeViewColumn* firstTreeViewColumn = gtk_tree_view_column_new();
    gtk_tree_view_column_set_title(firstTreeViewColumn, "M");
    gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn);

    GtkTreeViewColumn* middleTreeViewColumn = gtk_tree_view_column_new();
    gtk_tree_view_column_set_title(middleTreeViewColumn, "M");
    gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), middleTreeViewColumn);
    gtk_tree_view_set_expander_column(GTK_TREE_VIEW(gTreeViewWidget), middleTreeViewColumn);

    GtkTreeViewColumn* lastTreeViewColumn = gtk_tree_view_column_new();
    gtk_tree_view_column_set_title(lastTreeViewColumn, "M");
    gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn);

    /* Use the middle column's header for our button */
    GtkWidget* pTreeHeaderCellWidget = gtk_tree_view_column_get_button(middleTreeViewColumn);
    mpTreeHeaderButtonStyle = gtk_widget_get_style_context(pTreeHeaderCellWidget);

    /* Progress Bar */
    mpProgressBarStyle = createStyleContext(GtkControlPart::ProgressBar);
    mpProgressBarTroughStyle = createStyleContext(GtkControlPart::ProgressBarTrough);
    mpProgressBarProgressStyle = createStyleContext(GtkControlPart::ProgressBarProgress);

    gtk_widget_show_all(gDumbContainer);
#endif
}

void GtkSalGraphics::GetResolution(sal_Int32& rDPIX, sal_Int32& rDPIY)
{
    char* pForceDpi;
    if ((pForceDpi = getenv("SAL_FORCEDPI")))
    {
        rDPIX = rDPIY = o3tl::toInt32(std::string_view(pForceDpi));
        return;
    }

#if !GTK_CHECK_VERSION(4, 0, 0)
    GdkScreen* pScreen = gtk_widget_get_screen(mpWindow);
    double fResolution = -1.0;
    g_object_get(pScreen, "resolution", &fResolution, nullptr);

    if (fResolution > 0.0)
        rDPIX = rDPIY = sal_Int32(fResolution);
    else
        rDPIX = rDPIY = 96;
#else
    rDPIX = rDPIY = 96;
#endif
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5 in Prozent
C=97 H=99 G=97

¤ Dauer der Verarbeitung: 0.62 Sekunden  (vorverarbeitet am  2026-05-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge