diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/unx/gtk3/salnativewidgets-gtk.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | vcl/unx/gtk3/salnativewidgets-gtk.cxx | 3060 |
1 files changed, 3060 insertions, 0 deletions
diff --git a/vcl/unx/gtk3/salnativewidgets-gtk.cxx b/vcl/unx/gtk3/salnativewidgets-gtk.cxx new file mode 100644 index 000000000..64f6357ab --- /dev/null +++ b/vcl/unx/gtk3/salnativewidgets-gtk.cxx @@ -0,0 +1,3060 @@ +/* -*- 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/decoview.hxx> +#include <vcl/settings.hxx> +#include <unx/fontmanager.hxx> + +#include "gtkcairo.hxx" +#include <optional> + +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; + +#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* gHScrollbar; +static GtkWidget* gTextView; +#else +static GtkWidget* gComboBox; +static GtkWidget* gListBox; +static GtkWidget* gSpinBox; +static GtkWidget* gTreeViewWidget; +#endif +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); + stateFlags = gtk_style_context_get_state(context); + + 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(mpListboxBoxStyle, cr, aRect, flags); + + render_common(mpListboxButtonStyle, 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"); + 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) + { + tools::Long nX = 0; + tools::Long nY = 0; + + gint nSeparatorWidth = 1; + + gtk_style_context_get(context, + gtk_style_context_get_state(context), + "min-width", &nSeparatorWidth, nullptr); + + 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; + default: break; + } + 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; + default: break; + } + 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); + break; + } + case RenderType::Separator: + if (nPart == ControlPart::SeparatorHorz) + draw_horizontal_separator(context, cr, rControlRegion); + else + draw_vertical_separator(context, cr, rControlRegion); + 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) + { + gtk_style_context_add_provider(mpComboboxEntryStyle, GTK_STYLE_PROVIDER(pBgCssProvider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } + PaintCombobox(flags, cr, rControlRegion, nType, nPart); + if (pBgCssProvider) + { + gtk_style_context_remove_provider(mpComboboxEntryStyle, 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 ); + + psp::FastPrintFontInfo aInfo; + // set family name + aInfo.m_aFamilyName = OStringToOUString( aFamily, RTL_TEXTENCODING_UTF8 ); + // set italic + switch( eStyle ) + { + case PANGO_STYLE_NORMAL: aInfo.m_eItalic = ITALIC_NONE;break; + case PANGO_STYLE_ITALIC: aInfo.m_eItalic = ITALIC_NORMAL;break; + case PANGO_STYLE_OBLIQUE: aInfo.m_eItalic = ITALIC_OBLIQUE;break; + } + // set weight + if( eWeight <= PANGO_WEIGHT_ULTRALIGHT ) + aInfo.m_eWeight = WEIGHT_ULTRALIGHT; + else if( eWeight <= PANGO_WEIGHT_LIGHT ) + aInfo.m_eWeight = WEIGHT_LIGHT; + else if( eWeight <= PANGO_WEIGHT_NORMAL ) + aInfo.m_eWeight = WEIGHT_NORMAL; + else if( eWeight <= PANGO_WEIGHT_BOLD ) + aInfo.m_eWeight = WEIGHT_BOLD; + else + aInfo.m_eWeight = WEIGHT_ULTRABOLD; + // set width + switch( eStretch ) + { + case PANGO_STRETCH_ULTRA_CONDENSED: aInfo.m_eWidth = WIDTH_ULTRA_CONDENSED;break; + case PANGO_STRETCH_EXTRA_CONDENSED: aInfo.m_eWidth = WIDTH_EXTRA_CONDENSED;break; + case PANGO_STRETCH_CONDENSED: aInfo.m_eWidth = WIDTH_CONDENSED;break; + case PANGO_STRETCH_SEMI_CONDENSED: aInfo.m_eWidth = WIDTH_SEMI_CONDENSED;break; + case PANGO_STRETCH_NORMAL: aInfo.m_eWidth = WIDTH_NORMAL;break; + case PANGO_STRETCH_SEMI_EXPANDED: aInfo.m_eWidth = WIDTH_SEMI_EXPANDED;break; + case PANGO_STRETCH_EXPANDED: aInfo.m_eWidth = WIDTH_EXPANDED;break; + case PANGO_STRETCH_EXTRA_EXPANDED: aInfo.m_eWidth = WIDTH_EXTRA_EXPANDED;break; + case PANGO_STRETCH_ULTRA_EXPANDED: aInfo.m_eWidth = 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" + psp::PrintFontManager::get().matchFont(aInfo, rLocale); + +#if OSL_DEBUG_LEVEL > 1 + SAL_INFO("vcl.gtk3", "font match " + << (aInfo.m_nID != 0 ? "succeeded" : "failed") + << ", name AFTER: \"" + << aInfo.m_aFamilyName + << "\"."); +#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(aInfo.m_aFamilyName, Size(0, nPangoHeight)); + if( aInfo.m_eWeight != WEIGHT_DONTKNOW ) + aFont.SetWeight( aInfo.m_eWeight ); + if( aInfo.m_eWidth != WIDTH_DONTKNOW ) + aFont.SetWidthType( aInfo.m_eWidth ); + if( aInfo.m_eItalic != ITALIC_DONTKNOW ) + aFont.SetItalic( aInfo.m_eItalic ); + if( aInfo.m_ePitch != PITCH_DONTKNOW ) + aFont.SetPitch( aInfo.m_ePitch ); + 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(); + + // 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.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 ); + + // 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); + 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.SetHighlightColor( aHighlightColor ); + aStyleSet.SetHighlightTextColor( 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 ); + + // warning color + GdkRGBA warning_color; + if (gtk_style_context_lookup_color(pCStyle, "warning_color", &warning_color)) + aStyleSet.SetWarningColor(getColor(warning_color)); + + // field background color + style_context_set_state(pCStyle, GTK_STATE_FLAG_NORMAL); + ::Color aBackFieldColor = style_context_get_background_color(pCStyle); + aStyleSet.SetFieldColor( aBackFieldColor ); + // This baby is the default page/paper color + aStyleSet.SetWindowColor( 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); + + 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 ); + aStyleSet.SetMenuBarRolloverColor( aBackColor ); + + style_context_set_state(mpMenuBarItemStyle, GTK_STATE_FLAG_NORMAL); + style_context_get_color(mpMenuBarItemStyle, &text_color); + aTextColor = aStyleSet.GetPersonaMenuBarTextColor().value_or( 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 = aStyleSet.GetPersonaMenuBarTextColor().value_or( 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); + ::Color aHighlightColor = style_context_get_background_color(mpMenuItemLabelStyle); + aStyleSet.SetMenuHighlightColor( aHighlightColor ); + + 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 + + // 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; + +#if GTK_CHECK_VERSION(4, 0, 0) + GtkRequisition natural_size; + gtk_widget_get_preferred_size(gHScrollbar, nullptr, &natural_size); + aStyleSet.SetScrollBarSize(natural_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); + + 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 + 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 ); + + 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); + + // 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; + default: break; + } + + 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 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)); + + 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 + +#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_show(gVScrollbar); + mpVScrollbarStyle = gtk_widget_get_style_context(gVScrollbar); + + gHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, nullptr); + gtk_fixed_put(GTK_FIXED(gDumbContainer), gHScrollbar, 0, 0); + gtk_widget_show(gHScrollbar); + mpHScrollbarStyle = gtk_widget_get_style_context(gHScrollbar); + + gTextView = gtk_text_view_new(); + gtk_fixed_put(GTK_FIXED(gDumbContainer), gTextView, 0, 0); + gtk_widget_show(gTextView); +#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"))) + { + OString sForceDPI(pForceDpi); + rDPIX = rDPIY = sForceDPI.toInt32(); + 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: */ |