[Libreoffice-commits] core.git: vcl/inc vcl/Library_vclplug_gtk4.mk vcl/unx
Caolán McNamara (via logerrit)
logerrit at kemper.freedesktop.org
Thu Jun 3 19:11:14 UTC 2021
vcl/Library_vclplug_gtk4.mk | 1
vcl/inc/unx/gtk/gtkdata.hxx | 2
vcl/unx/gtk3/gtkdata.cxx | 57 ++
vcl/unx/gtk3/gtkinst.cxx | 892 -------------------------------------------
vcl/unx/gtk4/convert3to4.cxx | 889 ++++++++++++++++++++++++++++++++++++++++++
vcl/unx/gtk4/gtkinst.cxx | 2
6 files changed, 957 insertions(+), 886 deletions(-)
New commits:
commit 774be6d793203183fe1856ffb8b720f00b48c2bb
Author: Caolán McNamara <caolanm at redhat.com>
AuthorDate: Thu Jun 3 17:07:33 2021 +0100
Commit: Caolán McNamara <caolanm at redhat.com>
CommitDate: Thu Jun 3 21:10:27 2021 +0200
split .ui conversion code into its own file
Change-Id: Ie27990a497e39ab2fd82a711fa4ec49b472616f6
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116677
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm at redhat.com>
diff --git a/vcl/Library_vclplug_gtk4.mk b/vcl/Library_vclplug_gtk4.mk
index 3f1c3ebe88a7..e3cc415be738 100644
--- a/vcl/Library_vclplug_gtk4.mk
+++ b/vcl/Library_vclplug_gtk4.mk
@@ -84,6 +84,7 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk4,\
vcl/unx/gtk4/fpicker/SalGtkFilePicker \
vcl/unx/gtk4/fpicker/SalGtkFolderPicker \
vcl/unx/gtk4/fpicker/SalGtkPicker \
+ vcl/unx/gtk4/convert3to4 \
vcl/unx/gtk4/gtkdata \
vcl/unx/gtk4/gtkinst \
vcl/unx/gtk4/gtksys \
diff --git a/vcl/inc/unx/gtk/gtkdata.hxx b/vcl/inc/unx/gtk/gtkdata.hxx
index cc37cd12a19b..ef257c7d83b9 100644
--- a/vcl/inc/unx/gtk/gtkdata.hxx
+++ b/vcl/inc/unx/gtk/gtkdata.hxx
@@ -195,6 +195,8 @@ inline GdkGLContext* surface_create_gl_context(GdkSurface* pSurface)
typedef GtkClipboard GdkClipboard;
#endif
+int getButtonPriority(const OString &rType);
+
class GtkSalTimer final : public SalTimer
{
struct SalGtkTimeoutSource *m_pTimeout;
diff --git a/vcl/unx/gtk3/gtkdata.cxx b/vcl/unx/gtk3/gtkdata.cxx
index 5bd818e83491..14877985919c 100644
--- a/vcl/unx/gtk3/gtkdata.cxx
+++ b/vcl/unx/gtk3/gtkdata.cxx
@@ -843,4 +843,61 @@ void GtkSalDisplay::deregisterFrame( SalFrame* pFrame )
SalGenericDisplay::deregisterFrame( pFrame );
}
+namespace {
+
+struct ButtonOrder
+{
+ const char * m_aType;
+ int m_nPriority;
+};
+
+}
+
+int getButtonPriority(const OString &rType)
+{
+ static const size_t N_TYPES = 8;
+ static const ButtonOrder aDiscardCancelSave[N_TYPES] =
+ {
+ { "/discard", 0 },
+ { "/cancel", 1 },
+ { "/close", 1 },
+ { "/no", 2 },
+ { "/open", 3 },
+ { "/save", 3 },
+ { "/yes", 3 },
+ { "/ok", 3 }
+ };
+
+ static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
+ {
+ { "/open", 0 },
+ { "/save", 0 },
+ { "/yes", 0 },
+ { "/ok", 0 },
+ { "/discard", 1 },
+ { "/no", 1 },
+ { "/cancel", 2 },
+ { "/close", 2 }
+ };
+
+ const ButtonOrder* pOrder = &aDiscardCancelSave[0];
+
+ const OUString &rEnv = Application::GetDesktopEnvironment();
+
+ if (rEnv.equalsIgnoreAsciiCase("windows") ||
+ rEnv.equalsIgnoreAsciiCase("tde") ||
+ rEnv.startsWithIgnoreAsciiCase("kde"))
+ {
+ pOrder = &aSaveDiscardCancel[0];
+ }
+
+ for (size_t i = 0; i < N_TYPES; ++i, ++pOrder)
+ {
+ if (rType.endsWith(pOrder->m_aType))
+ return pOrder->m_nPriority;
+ }
+
+ return -1;
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index 3a42aace7132..90cc8603b855 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -27,9 +27,9 @@
#include <headless/svpvd.hxx>
#include <headless/svpbmp.hxx>
#include <vcl/builder.hxx>
-#include <vcl/toolkit/floatwin.hxx>
#include <vcl/inputtypes.hxx>
#include <vcl/transfer.hxx>
+#include <vcl/toolkit/floatwin.hxx>
#include <unx/genpspgraphics.h>
#include <rtl/strbuf.hxx>
#include <sal/log.hxx>
@@ -44,8 +44,6 @@
#if !GTK_CHECK_VERSION(4, 0, 0)
#include "a11y/atkwrapper.hxx"
#endif
-#include <com/sun/star/beans/XPropertySet.hpp>
-#include <com/sun/star/io/TempFile.hpp>
#include <com/sun/star/datatransfer/XTransferable.hpp>
#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
#include <com/sun/star/datatransfer/clipboard/XClipboardEx.hpp>
@@ -59,14 +57,11 @@
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
-#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
-#include <com/sun/star/xml/sax/Writer.hpp>
-#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
#include <comphelper/lok.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/sequence.hxx>
-#include <cppuhelper/compbase.hxx>
#include <comphelper/string.hxx>
+#include <cppuhelper/compbase.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <officecfg/Office/Common.hxx>
@@ -148,16 +143,16 @@ extern "C"
XInitThreads();
#endif
+#if !GTK_CHECK_VERSION(4, 0, 0)
// init gdk thread protection
bool const sup = g_thread_supported();
// extracted from the 'if' to avoid Clang -Wunreachable-code
if ( !sup )
g_thread_init( nullptr );
-#if !GTK_CHECK_VERSION(4, 0, 0)
gdk_threads_set_lock_functions (GdkThreadsEnter, GdkThreadsLeave);
-#endif
SAL_INFO("vcl.gtk", "Hooked gdk threads locks");
+#endif
auto pYieldMutex = std::make_unique<GtkYieldMutex>();
@@ -5173,70 +5168,6 @@ std::unique_ptr<weld::Container> GtkInstanceWidget::weld_parent() const
namespace {
-struct ButtonOrder
-{
- const char * m_aType;
- int m_nPriority;
-};
-
-int getButtonPriority(const OString &rType)
-{
- static const size_t N_TYPES = 8;
- static const ButtonOrder aDiscardCancelSave[N_TYPES] =
- {
- { "/discard", 0 },
- { "/cancel", 1 },
- { "/close", 1 },
- { "/no", 2 },
- { "/open", 3 },
- { "/save", 3 },
- { "/yes", 3 },
- { "/ok", 3 }
- };
-
- static const ButtonOrder aSaveDiscardCancel[N_TYPES] =
- {
- { "/open", 0 },
- { "/save", 0 },
- { "/yes", 0 },
- { "/ok", 0 },
- { "/discard", 1 },
- { "/no", 1 },
- { "/cancel", 2 },
- { "/close", 2 }
- };
-
- const ButtonOrder* pOrder = &aDiscardCancelSave[0];
-
- const OUString &rEnv = Application::GetDesktopEnvironment();
-
- if (rEnv.equalsIgnoreAsciiCase("windows") ||
- rEnv.equalsIgnoreAsciiCase("tde") ||
- rEnv.startsWithIgnoreAsciiCase("kde"))
- {
- pOrder = &aSaveDiscardCancel[0];
- }
-
- for (size_t i = 0; i < N_TYPES; ++i, ++pOrder)
- {
- if (rType.endsWith(pOrder->m_aType))
- return pOrder->m_nPriority;
- }
-
- return -1;
-}
-
-#if GTK_CHECK_VERSION(4, 0, 0)
-typedef std::pair<css::uno::Reference<css::xml::dom::XNode>, OUString> named_node;
-
-bool sortButtonNodes(const named_node& rA, const named_node& rB)
-{
- //order within groups according to platform rules
- return getButtonPriority("/" + rA.second.toUtf8()) <
- getButtonPriority("/" + rB.second.toUtf8());
-}
-#endif
-
bool sortButtons(const GtkWidget* pA, const GtkWidget* pB)
{
//order within groups according to platform rules
@@ -21007,825 +20938,14 @@ bool IsAllowedBuiltInIcon(std::u16string_view iconName)
namespace {
-#if GTK_CHECK_VERSION(4, 0, 0)
-
-// <property name="spacing">6</property>
-Reference<css::xml::dom::XNode> CreateProperty(const css::uno::Reference<css::xml::dom::XDocument>& xDoc,
- const OUString& rPropName, const OUString& rValue)
-{
- css::uno::Reference<css::xml::dom::XElement> xProperty = xDoc->createElement("property");
- css::uno::Reference<css::xml::dom::XAttr> xPropName = xDoc->createAttribute("name");
- xPropName->setValue(rPropName);
- xProperty->setAttributeNode(xPropName);
- css::uno::Reference<css::xml::dom::XText> xValue = xDoc->createTextNode(rValue);
- xProperty->appendChild(xValue);
- return xProperty;
-}
-
-bool ToplevelIsMessageDialog(const Reference<css::xml::dom::XNode>& xNode)
-{
- for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate = xNode->getParentNode();
- xObjectCandidate.is();
- xObjectCandidate = xObjectCandidate->getParentNode())
- {
- if (xObjectCandidate->getNodeName() == "object")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xObjectMap = xObjectCandidate->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xClass = xObjectMap->getNamedItem("class");
- if (xClass->getNodeValue() == "GtkMessageDialog")
- return true;
- }
- }
- return false;
-}
-
-void SetPropertyOnTopLevel(const Reference<css::xml::dom::XNode>& xNode, const Reference<css::xml::dom::XNode>& xProperty)
-{
- for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate = xNode->getParentNode();
- xObjectCandidate.is();
- xObjectCandidate = xObjectCandidate->getParentNode())
- {
- if (xObjectCandidate->getNodeName() == "object")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xObjectMap = xObjectCandidate->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xClass = xObjectMap->getNamedItem("class");
- if (xClass->getNodeValue() == "GtkDialog")
- {
- auto xFirstChild = xObjectCandidate->getFirstChild();
- if (xFirstChild.is())
- xObjectCandidate->insertBefore(xProperty, xFirstChild);
- else
- xObjectCandidate->appendChild(xProperty);
-
- break;
- }
- }
- }
-}
-
-OUString GetParentObjectType(const Reference<css::xml::dom::XNode>& xNode)
-{
- auto xParent = xNode->getParentNode();
- assert(xParent->getNodeName() == "object");
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap = xParent->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xClass = xParentMap->getNamedItem("class");
- return xClass->getNodeValue();
-}
-
-// currently runs the risk of duplicate margin-* properties if there was already such as well
-// as the border
-void AddBorderAsMargins(const Reference<css::xml::dom::XNode>& xNode, const OUString& rBorderWidth)
-{
- auto xDoc = xNode->getOwnerDocument();
-
- auto xMarginEnd = CreateProperty(xDoc, "margin-end", rBorderWidth);
-
- auto xFirstChild = xNode->getFirstChild();
- if (xFirstChild.is())
- xNode->insertBefore(xMarginEnd, xFirstChild);
- else
- xNode->appendChild(xMarginEnd);
-
- xNode->insertBefore(CreateProperty(xDoc, "margin-top", rBorderWidth), xMarginEnd);
- xNode->insertBefore(CreateProperty(xDoc, "margin-bottom", rBorderWidth), xMarginEnd);
- xNode->insertBefore(CreateProperty(xDoc, "margin-start", rBorderWidth), xMarginEnd);
-}
-
-struct ConvertResult
-{
- bool m_bChildCanFocus;
- bool m_bHasVisible;
- bool m_bHasIconName;
- bool m_bAlwaysShowImage;
- css::uno::Reference<css::xml::dom::XNode> m_xPropertyLabel;
-
- ConvertResult(bool bChildCanFocus,
- bool bHasVisible,
- bool bHasIconName,
- bool bAlwaysShowImage,
- const css::uno::Reference<css::xml::dom::XNode>& rPropertyLabel)
- : m_bChildCanFocus(bChildCanFocus)
- , m_bHasVisible(bHasVisible)
- , m_bHasIconName(bHasIconName)
- , m_bAlwaysShowImage(bAlwaysShowImage)
- , m_xPropertyLabel(rPropertyLabel)
- {
- }
-};
-
-ConvertResult Convert3To4(const Reference<css::xml::dom::XNode>& xNode)
-{
- css::uno::Reference<css::xml::dom::XNodeList> xNodeList = xNode->getChildNodes();
- if (!xNodeList.is())
- return ConvertResult(false, false, false, false, nullptr);
-
- std::vector<css::uno::Reference<css::xml::dom::XNode>> xRemoveList;
-
- OUString sBorderWidth;
- bool bChildCanFocus = false;
- bool bHasVisible = false;
- bool bHasIconName = false;
- bool bAlwaysShowImage = false;
- css::uno::Reference<css::xml::dom::XNode> xPropertyLabel;
- css::uno::Reference<css::xml::dom::XNode> xCantFocus;
-
- css::uno::Reference<css::xml::dom::XNode> xChild = xNode->getFirstChild();
- while (xChild.is())
- {
- if (xChild->getNodeName() == "requires")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xLib = xMap->getNamedItem("lib");
- assert(xLib->getNodeValue() == "gtk+");
- xLib->setNodeValue("gtk");
- css::uno::Reference<css::xml::dom::XNode> xVersion = xMap->getNamedItem("version");
- assert(xVersion->getNodeValue() == "3.20");
- xVersion->setNodeValue("4.0");
- }
- else if (xChild->getNodeName() == "property")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xName = xMap->getNamedItem("name");
- OUString sName(xName->getNodeValue().replace('_', '-'));
-
- if (sName == "border-width")
- sBorderWidth = xChild->getFirstChild()->getNodeValue();
-
- if (sName == "has-default")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap = xChild->getParentNode()->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xId = xParentMap->getNamedItem("id");
- auto xDoc = xChild->getOwnerDocument();
- auto xDefaultWidget = CreateProperty(xDoc, "default-widget", xId->getNodeValue());
- SetPropertyOnTopLevel(xChild, xDefaultWidget);
- xRemoveList.push_back(xChild);
- }
-
- if (sName == "has-focus" || sName == "is-focus")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap = xChild->getParentNode()->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xId = xParentMap->getNamedItem("id");
- auto xDoc = xChild->getOwnerDocument();
- auto xDefaultWidget = CreateProperty(xDoc, "focus-widget", xId->getNodeValue());
- SetPropertyOnTopLevel(xChild, xDefaultWidget);
- xRemoveList.push_back(xChild);
- }
-
- if (sName == "can-focus")
- {
- bChildCanFocus = toBool(xChild->getFirstChild()->getNodeValue());
- if (!bChildCanFocus)
- {
- OUString sParentClass = GetParentObjectType(xChild);
- if (sParentClass == "GtkBox" || sParentClass == "GtkGrid")
- {
- // e.g. for the case of notebooks without children yet, just remove the can't focus property
- // from Boxes and Grids
- xRemoveList.push_back(xChild);
- }
- else if (sParentClass == "GtkComboBoxText")
- {
- // this was always a bit finicky in gtk3, fix it up to default to can-focus
- xRemoveList.push_back(xChild);
- }
- else
- {
- // otherwise mark the property as needing removal if there turns out to be a child
- // with can-focus of true, in which case remove this parent conflicting property
- xCantFocus = xChild;
- }
- }
- }
-
- if (sName == "label")
- xPropertyLabel = xChild;
-
- if (sName == "visible")
- bHasVisible = true;
-
- if (sName == "icon-name")
- bHasIconName = true;
-
- if (sName == "events")
- xRemoveList.push_back(xChild);
-
- if (sName == "activates-default")
- {
- if (GetParentObjectType(xChild) == "GtkSpinButton")
- xRemoveList.push_back(xChild);
- }
-
- if (sName == "width-chars")
- {
- if (GetParentObjectType(xChild) == "GtkEntry")
- {
- // I don't quite get what the difference should be wrt width-chars and max-width-chars
- // but glade doesn't write max-width-chars and in gtk4 where we have width-chars, e.g
- // print dialog, then max-width-chars gives the effect we wanted with width-chars
- auto xDoc = xChild->getOwnerDocument();
- auto mMaxWidthChars = CreateProperty(xDoc, "max-width-chars", xChild->getFirstChild()->getNodeValue());
- xChild->getParentNode()->insertBefore(mMaxWidthChars, xChild);
- }
- }
-
- // remove 'Help' button label and replace with a help icon instead
- if (sName == "label" && GetParentObjectType(xChild) == "GtkButton")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap = xChild->getParentNode()->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xId = xParentMap->getNamedItem("id");
- if (xId && xId->getNodeValue() == "help")
- {
- auto xDoc = xChild->getOwnerDocument();
- auto xIconName = CreateProperty(xDoc, "icon-name", "help-browser-symbolic");
- xChild->getParentNode()->insertBefore(xIconName, xChild);
- xRemoveList.push_back(xChild);
- }
- }
-
- if (sName == "icon-size")
- {
- if (GetParentObjectType(xChild) == "GtkImage")
- {
- if (xChild->getFirstChild()->getNodeValue() == "6")
- {
- auto xDoc = xChild->getOwnerDocument();
- // convert old GTK_ICON_SIZE_DIALOG to new GTK_ICON_SIZE_LARGE
- auto xIconSize = CreateProperty(xDoc, "icon-size", "2");
- xChild->getParentNode()->insertBefore(xIconSize, xChild);
- xRemoveList.push_back(xChild);
- }
- else
- SAL_WARN( "vcl.gtk", "what should we do with an icon-size of: " << xChild->getFirstChild()->getNodeValue());
- }
- }
-
- if (sName == "truncate-multiline")
- {
- if (GetParentObjectType(xChild) == "GtkSpinButton")
- xRemoveList.push_back(xChild);
- }
-
- if (sName == "homogeneous")
- {
- // e.g. the buttonbox in xml filter dialog
- if (GetParentObjectType(xChild) == "GtkButtonBox")
- xRemoveList.push_back(xChild);
- }
-
- if (sName == "shadow-type")
- {
- if (GetParentObjectType(xChild) == "GtkFrame")
- xRemoveList.push_back(xChild);
- else if (GetParentObjectType(xChild) == "GtkScrolledWindow")
- {
- bool bHasFrame = xChild->getFirstChild()->getNodeValue() != "none";
- auto xDoc = xChild->getOwnerDocument();
- auto xHasFrame = CreateProperty(xDoc, "has-frame",
- bHasFrame ? OUString("True") : OUString("False"));
- xChild->getParentNode()->insertBefore(xHasFrame, xChild);
- xRemoveList.push_back(xChild);
- }
- }
-
- if (sName == "always-show-image")
- {
- if (GetParentObjectType(xChild) == "GtkButton")
- {
- // we will turn always-show-image into a GtkBox child for
- // GtkButton and a GtkLabel child for the GtkBox and move
- // the label property into it.
- bAlwaysShowImage = toBool(xChild->getFirstChild()->getNodeValue());
- xRemoveList.push_back(xChild);
- }
- }
-
- if (sName == "relief")
- {
- if (GetParentObjectType(xChild) == "GtkLinkButton" ||
- GetParentObjectType(xChild) == "GtkButton")
- {
- assert(xChild->getFirstChild()->getNodeValue() == "none");
- auto xDoc = xChild->getOwnerDocument();
- auto xHasFrame = CreateProperty(xDoc, "has-frame", "False");
- xChild->getParentNode()->insertBefore(xHasFrame, xChild);
- xRemoveList.push_back(xChild);
- }
- }
-
- if (sName == "xalign")
- {
- if (GetParentObjectType(xChild) == "GtkLinkButton" ||
- GetParentObjectType(xChild) == "GtkMenuButton" ||
- GetParentObjectType(xChild) == "GtkButton")
- {
- // TODO expand into a GtkLabel child with alignment on that instead
- assert(xChild->getFirstChild()->getNodeValue() == "0");
- xRemoveList.push_back(xChild);
- }
- }
-
- if (sName == "hscrollbar-policy")
- {
- if (GetParentObjectType(xChild) == "GtkScrolledWindow")
- {
- if (xChild->getFirstChild()->getNodeValue() == "never")
- {
- auto xDoc = xChild->getOwnerDocument();
- auto xHasFrame = CreateProperty(xDoc, "propagate-natural-width", "True");
- xChild->getParentNode()->insertBefore(xHasFrame, xChild);
- }
- }
- }
-
- if (sName == "vscrollbar-policy")
- {
- if (GetParentObjectType(xChild) == "GtkScrolledWindow")
- {
- if (xChild->getFirstChild()->getNodeValue() == "never")
- {
- auto xDoc = xChild->getOwnerDocument();
- auto xHasFrame = CreateProperty(xDoc, "propagate-natural-height", "True");
- xChild->getParentNode()->insertBefore(xHasFrame, xChild);
- }
- }
- }
-
- if (sName == "image")
- {
- if (GetParentObjectType(xChild) == "GtkButton")
- {
- // find the image object, expected to be a child of "interface" and relocate
- // it to be a child of this GtkButton
- auto xObjectCandidate = xChild->getParentNode();
- if (xObjectCandidate->getNodeName() == "object")
- {
- OUString sImageId = xChild->getFirstChild()->getNodeValue();
-
- css::uno::Reference<css::xml::dom::XNode> xRootCandidate = xChild->getParentNode();
- while (xRootCandidate)
- {
- if (xRootCandidate->getNodeName() == "interface")
- break;
- xRootCandidate = xRootCandidate->getParentNode();
- }
-
- css::uno::Reference<css::xml::dom::XNode> xImageNode;
-
- for (auto xImageCandidate = xRootCandidate->getFirstChild(); xImageCandidate.is(); xImageCandidate= xImageCandidate->getNextSibling())
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xImageCandidateMap = xImageCandidate->getAttributes();
- if (!xImageCandidateMap.is())
- continue;
- css::uno::Reference<css::xml::dom::XNode> xId = xImageCandidateMap->getNamedItem("id");
- if (xId && xId->getNodeValue() == sImageId)
- {
- xImageNode = xImageCandidate;
- break;
- }
- }
-
- auto xDoc = xChild->getOwnerDocument();
- css::uno::Reference<css::xml::dom::XElement> xImageChild = xDoc->createElement("child");
- xImageChild->appendChild(xImageNode->getParentNode()->removeChild(xImageNode));
- xObjectCandidate->appendChild(xImageChild);
- }
-
- xRemoveList.push_back(xChild);
- }
- }
-
- if (sName == "draw-indicator")
- {
- assert(toBool(xChild->getFirstChild()->getNodeValue()));
- xRemoveList.push_back(xChild);
- }
-
- if (sName == "type-hint" || sName == "skip-taskbar-hint" ||
- sName == "can-default" || sName == "border-width" ||
- sName == "layout-style" || sName == "no-show-all" ||
- sName == "ignore-hidden" || sName == "window-position")
- {
- xRemoveList.push_back(xChild);
- }
- }
- else if (xChild->getNodeName() == "child")
- {
- bool bContentArea = false;
-
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xName = xMap->getNamedItem("internal-child");
- if (xName)
- {
- OUString sName(xName->getNodeValue());
- if (sName == "vbox")
- {
- xName->setNodeValue("content_area");
- bContentArea = true;
- }
- else if (sName == "accessible")
- xRemoveList.push_back(xChild); // Yikes!, what's the replacement for this going to be
- }
-
- if (bContentArea)
- {
- for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate = xChild->getFirstChild();
- xObjectCandidate.is();
- xObjectCandidate = xObjectCandidate->getNextSibling())
- {
- if (xObjectCandidate->getNodeName() == "object")
- {
- auto xDoc = xChild->getOwnerDocument();
-
- auto xVExpand = CreateProperty(xDoc, "vexpand", "True");
- auto xFirstChild = xObjectCandidate->getFirstChild();
- if (xFirstChild.is())
- xObjectCandidate->insertBefore(xVExpand, xFirstChild);
- else
- xObjectCandidate->appendChild(xVExpand);
-
- if (!sBorderWidth.isEmpty())
- {
- AddBorderAsMargins(xObjectCandidate, sBorderWidth);
- sBorderWidth.clear();
- }
-
- break;
- }
- }
- }
- }
- else if (xChild->getNodeName() == "packing")
- {
- // remove "packing" and if its grid packing insert a replacement "layout" into
- // the associated "object"
- auto xDoc = xChild->getOwnerDocument();
- css::uno::Reference<css::xml::dom::XElement> xNew = xDoc->createElement("layout");
-
- bool bGridPacking = false;
-
- // iterate over all children and append them to the new element
- for (css::uno::Reference<css::xml::dom::XNode> xCurrent = xChild->getFirstChild();
- xCurrent.is();
- xCurrent = xChild->getFirstChild())
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xCurrent->getAttributes();
- if (xMap.is())
- {
- css::uno::Reference<css::xml::dom::XNode> xName = xMap->getNamedItem("name");
- OUString sName(xName->getNodeValue().replace('_', '-'));
- if (sName == "left-attach")
- {
- xName->setNodeValue("column");
- bGridPacking = true;
- }
- else if (sName == "top-attach")
- {
- xName->setNodeValue("row");
- bGridPacking = true;
- }
- else if (sName == "width")
- {
- xName->setNodeValue("column-span");
- bGridPacking = true;
- }
- else if (sName == "height")
- {
- xName->setNodeValue("row-span");
- bGridPacking = true;
- }
- else if (sName == "secondary")
- {
- // turn parent tag of <child> into <child type="start">
- auto xParent = xChild->getParentNode();
- css::uno::Reference<css::xml::dom::XAttr> xTypeStart = xDoc->createAttribute("type");
- xTypeStart->setValue("start");
- css::uno::Reference<css::xml::dom::XElement> xElem(xParent, css::uno::UNO_QUERY_THROW);
- xElem->setAttributeNode(xTypeStart);
- }
- }
- xNew->appendChild(xChild->removeChild(xCurrent));
- }
-
- if (bGridPacking)
- {
- // go back to parent and find the object child and insert this "layout" as a
- // new child of the object
- auto xParent = xChild->getParentNode();
- for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate = xParent->getFirstChild();
- xObjectCandidate.is();
- xObjectCandidate = xObjectCandidate->getNextSibling())
- {
- if (xObjectCandidate->getNodeName() == "object")
- {
- xObjectCandidate->appendChild(xNew);
- break;
- }
- }
- }
-
- xRemoveList.push_back(xChild);
- }
- else if (xChild->getNodeName() == "accessibility")
- {
- // TODO <relation type="labelled-by" target="pagenumcb"/> -> <relation name="labelled-by">pagenumcb</relation>
- xRemoveList.push_back(xChild);
- }
- else if (xChild->getNodeName() == "accelerator")
- {
- // TODO is anything like this supported anymore in .ui files
- xRemoveList.push_back(xChild);
- }
-
- auto xNextChild = xChild->getNextSibling();
-
- bool bChildHasIconName = false;
- bool bChildHasVisible = false;
- bool bChildAlwaysShowImage = false;
- css::uno::Reference<css::xml::dom::XNode> xChildPropertyLabel;
- if (xChild->hasChildNodes())
- {
- auto aChildRes = Convert3To4(xChild);
- bChildCanFocus |= aChildRes.m_bChildCanFocus;
- if (bChildCanFocus && xCantFocus.is())
- {
- xNode->removeChild(xCantFocus);
- xCantFocus.clear();
- }
- if (xChild->getNodeName() == "object")
- {
- bChildHasVisible = aChildRes.m_bHasVisible;
- bChildHasIconName = aChildRes.m_bHasIconName;
- bChildAlwaysShowImage = aChildRes.m_bAlwaysShowImage;
- xChildPropertyLabel = aChildRes.m_xPropertyLabel;
- }
- }
-
- if (xChild->getNodeName() == "object")
- {
- auto xDoc = xChild->getOwnerDocument();
-
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xClass = xMap->getNamedItem("class");
- OUString sClass(xClass->getNodeValue());
-
- auto xInternalChildCandidate = xChild->getParentNode();
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xInternalChildCandidateMap = xInternalChildCandidate->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xId = xInternalChildCandidateMap->getNamedItem("internal-child");
-
- // turn default gtk3 invisibility for widget objects into explicit invisible, but ignore internal-children
- if (!bChildHasVisible && !xId)
- {
- if (sClass == "GtkBox" || sClass == "GtkButton" ||
- sClass == "GtkCalendar" || sClass == "GtkCheckButton" ||
- sClass == "GtkRadioButton" || sClass == "GtkComboBox" ||
- sClass == "GtkComboBoxText" || sClass == "GtkDrawingArea" ||
- sClass == "GtkEntry" || sClass == "GtkExpander" ||
- sClass == "GtkFrame" || sClass == "GtkGrid" ||
- sClass == "GtkImage" || sClass == "GtkLabel" ||
- sClass == "GtkMenuButton" || sClass == "GtkNotebook" ||
- sClass == "GtkOverlay" || sClass == "GtkPaned" ||
- sClass == "GtkProgressBar" || sClass == "GtkScrolledWindow" ||
- sClass == "GtkSeparator" || sClass == "GtkSpinButton" ||
- sClass == "GtkSpinner" || sClass == "GtkTextView" ||
- sClass == "GtkTreeView" || sClass == "GtkViewport" ||
- sClass == "GtkLinkButton" || sClass == "GtkToggleButton" ||
- sClass == "GtkButtonBox")
-
- {
- auto xVisible = CreateProperty(xDoc, "visible", "False");
- auto xFirstChild = xChild->getFirstChild();
- if (xFirstChild.is())
- xChild->insertBefore(xVisible, xFirstChild);
- else
- xChild->appendChild(xVisible);
- }
- }
-
- if (sClass == "GtkButtonBox")
- {
- if (xId && xId->getNodeValue() == "action_area" && !ToplevelIsMessageDialog(xChild))
- {
- xClass->setNodeValue("GtkHeaderBar");
- auto xSpacingNode = CreateProperty(xDoc, "show-title-buttons", "False");
- auto xFirstChild = xChild->getFirstChild();
- if (xFirstChild.is())
- xChild->insertBefore(xSpacingNode, xFirstChild);
- else
- xChild->appendChild(xSpacingNode);
-
- // move the replacement GtkHeaderBar up to before the content_area
- auto xContentAreaCandidate = xChild->getParentNode();
- while (xContentAreaCandidate)
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xChildMap = xContentAreaCandidate->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xName = xChildMap->getNamedItem("internal-child");
- if (xName && xName->getNodeValue() == "content_area")
- {
- auto xActionArea = xChild->getParentNode();
-
- xActionArea->getParentNode()->removeChild(xActionArea);
-
- css::uno::Reference<css::xml::dom::XAttr> xTypeTitleBar = xDoc->createAttribute("type");
- xTypeTitleBar->setValue("titlebar");
- css::uno::Reference<css::xml::dom::XElement> xElem(xActionArea, css::uno::UNO_QUERY_THROW);
- xElem->setAttributeNode(xTypeTitleBar);
- xElem->removeAttribute("internal-child");
-
- xContentAreaCandidate->getParentNode()->insertBefore(xActionArea, xContentAreaCandidate);
-
- std::vector<named_node> aChildren;
-
- css::uno::Reference<css::xml::dom::XNode> xTitleChild = xChild->getFirstChild();
- while (xTitleChild.is())
- {
- auto xNextTitleChild = xTitleChild->getNextSibling();
- if (xTitleChild->getNodeName() == "child")
- {
- OUString sNodeId;
-
- for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate = xTitleChild->getFirstChild();
- xObjectCandidate.is();
- xObjectCandidate = xObjectCandidate->getNextSibling())
- {
- if (xObjectCandidate->getNodeName() == "object")
- {
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xObjectMap = xObjectCandidate->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xObjectId = xObjectMap->getNamedItem("id");
- sNodeId = xObjectId->getNodeValue();
- break;
- }
- }
-
- aChildren.push_back(std::make_pair(xTitleChild, sNodeId));
- }
- else if (xTitleChild->getNodeName() == "property")
- {
- // remove any <property name="homogeneous"> tag
- css::uno::Reference<css::xml::dom::XNamedNodeMap> xTitleChildMap = xTitleChild->getAttributes();
- css::uno::Reference<css::xml::dom::XNode> xPropName = xTitleChildMap->getNamedItem("name");
- OUString sPropName(xPropName->getNodeValue().replace('_', '-'));
- if (sPropName == "homogeneous")
- xChild->removeChild(xTitleChild);
- }
-
- xTitleChild = xNextTitleChild;
- }
-
- //sort child order within parent so that we match the platform button order
- std::stable_sort(aChildren.begin(), aChildren.end(), sortButtonNodes);
-
- int nNonHelpButtonCount = 0;
-
- for (const auto& rTitleChild : aChildren)
- {
- xChild->removeChild(rTitleChild.first);
- if (rTitleChild.second != "help")
- ++nNonHelpButtonCount;
- }
-
- std::reverse(aChildren.begin(), aChildren.end());
-
- for (const auto& rTitleChild : aChildren)
- {
- xChild->appendChild(rTitleChild.first);
-
- css::uno::Reference<css::xml::dom::XElement> xChildElem(rTitleChild.first, css::uno::UNO_QUERY_THROW);
- if (!xChildElem->hasAttribute("type"))
- {
- // turn parent tag of <child> into <child type="end"> except for cancel/close which we'll
- // put at start unless there is nothing at end
- css::uno::Reference<css::xml::dom::XAttr> xTypeEnd = xDoc->createAttribute("type");
- if (nNonHelpButtonCount >= 2 && (rTitleChild.second == "cancel" || rTitleChild.second == "close"))
- xTypeEnd->setValue("start");
- else
- xTypeEnd->setValue("end");
- xChildElem->setAttributeNode(xTypeEnd);
- }
- }
-
- auto xUseHeaderBar = CreateProperty(xDoc, "use-header-bar", "1");
- SetPropertyOnTopLevel(xContentAreaCandidate, xUseHeaderBar);
-
- break;
- }
- xContentAreaCandidate = xContentAreaCandidate->getParentNode();
- }
- }
- else // GtkMessageDialog
- xClass->setNodeValue("GtkBox");
- }
- else if (sClass == "GtkRadioButton")
- {
- xClass->setNodeValue("GtkCheckButton");
- }
- else if (sClass == "GtkImage" && !bChildHasIconName)
- {
- xClass->setNodeValue("GtkPicture");
- }
- else if (sClass == "GtkPopover" && !bHasVisible)
- {
- auto xVisible = CreateProperty(xDoc, "visible", "False");
- auto xFirstChild = xChild->getFirstChild();
- if (xFirstChild.is())
- xChild->insertBefore(xVisible, xFirstChild);
- else
- xChild->appendChild(xVisible);
- }
-
- if (bChildAlwaysShowImage)
- {
- auto xImageCandidateNode = xChild->getLastChild();
- if (xImageCandidateNode && xImageCandidateNode->getNodeName() != "child")
- xImageCandidateNode.clear();
- if (xImageCandidateNode)
- xChild->removeChild(xImageCandidateNode);
-
- css::uno::Reference<css::xml::dom::XElement> xNewChildNode = xDoc->createElement("child");
- css::uno::Reference<css::xml::dom::XElement> xNewObjectNode = xDoc->createElement("object");
- css::uno::Reference<css::xml::dom::XAttr> xBoxClassName = xDoc->createAttribute("class");
- xBoxClassName->setValue("GtkBox");
- xNewObjectNode->setAttributeNode(xBoxClassName);
- xNewChildNode->appendChild(xNewObjectNode);
-
- xChild->appendChild(xNewChildNode);
-
- css::uno::Reference<css::xml::dom::XElement> xNewLabelChildNode = xDoc->createElement("child");
- css::uno::Reference<css::xml::dom::XElement> xNewChildObjectNode = xDoc->createElement("object");
- css::uno::Reference<css::xml::dom::XAttr> xLabelClassName = xDoc->createAttribute("class");
- xLabelClassName->setValue("GtkLabel");
- xNewChildObjectNode->setAttributeNode(xLabelClassName);
- if (xChildPropertyLabel)
- xNewChildObjectNode->appendChild(xChildPropertyLabel->getParentNode()->removeChild(xChildPropertyLabel));
- xNewLabelChildNode->appendChild(xNewChildObjectNode);
-
- if (xImageCandidateNode)
- xNewObjectNode->appendChild(xImageCandidateNode);
- xNewObjectNode->appendChild(xNewLabelChildNode);
- }
- }
-
- xChild = xNextChild;
- }
-
- if (!sBorderWidth.isEmpty())
- AddBorderAsMargins(xNode, sBorderWidth);
-
- for (auto& xRemove : xRemoveList)
- xNode->removeChild(xRemove);
-
- return ConvertResult(bChildCanFocus, bHasVisible, bHasIconName, bAlwaysShowImage, xPropertyLabel);
-}
-#endif
-
void load_ui_file(GtkBuilder* pBuilder, const OUString& rUri)
{
- GError *err = nullptr;
-
#if GTK_CHECK_VERSION(4, 0, 0)
- // load the xml
- css::uno::Reference<css::uno::XComponentContext> xContext = ::comphelper::getProcessComponentContext();
- css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder = xml::dom::DocumentBuilder::create(xContext);
- css::uno::Reference<css::xml::dom::XDocument> xDocument = xBuilder->parseURI(rUri);
-
- // convert it from gtk3 to gtk4
- Convert3To4(xDocument);
-
- css::uno::Reference<css::beans::XPropertySet> xTempFile(io::TempFile::create(xContext), css::uno::UNO_QUERY);
- css::uno::Reference<css::io::XStream> xTempStream(xTempFile, css::uno::UNO_QUERY_THROW);
- xTempFile->setPropertyValue("RemoveFile", css::uno::makeAny(false));
-
- // serialize it back to xml
- css::uno::Reference<css::xml::sax::XSAXSerializable> xSerializer(xDocument, css::uno::UNO_QUERY);
- css::uno::Reference<css::xml::sax::XWriter> xWriter = css::xml::sax::Writer::create(xContext);
- css::uno::Reference<css::io::XOutputStream> xTempOut = xTempStream->getOutputStream();
- xWriter->setOutputStream(xTempOut);
- xSerializer->serialize(css::uno::Reference<css::xml::sax::XDocumentHandler>(xWriter, css::uno::UNO_QUERY_THROW),
- css::uno::Sequence<css::beans::StringPair>());
-
- // feed it to GtkBuilder
- css::uno::Reference<css::io::XSeekable> xTempSeek(xTempStream, css::uno::UNO_QUERY_THROW);
- xTempSeek->seek(0);
- auto xInput = xTempStream->getInputStream();
- css::uno::Sequence<sal_Int8> bytes;
- sal_Int32 nToRead = xInput->available();
- while (true)
- {
- sal_Int32 nRead = xInput->readBytes(bytes, std::max<sal_Int32>(nToRead, 4096));
- if (!nRead)
- break;
- // fprintf(stderr, "text is %s\n", reinterpret_cast<const gchar*>(bytes.getArray()));
- auto rc = gtk_builder_add_from_string(pBuilder, reinterpret_cast<const gchar*>(bytes.getArray()), nRead, &err);
- if (!rc)
- {
- SAL_WARN( "vcl.gtk", "GtkInstanceBuilder: error when calling gtk_builder_add_from_string: " << err->message);
- g_error_free(err);
- }
- assert(rc && "could not load UI file");
- // in the real world the first loop has read the entire file because its all 'available' without blocking
- }
+ builder_add_from_gtk3_file(pBuilder, rUri);
#else
OUString aPath;
osl::FileBase::getSystemPathFromFileURL(rUri, aPath);
+ GError *err = nullptr;
auto rc = gtk_builder_add_from_file(pBuilder, OUStringToOString(aPath, RTL_TEXTENCODING_UTF8).getStr(), &err);
if (!rc)
diff --git a/vcl/unx/gtk4/convert3to4.cxx b/vcl/unx/gtk4/convert3to4.cxx
new file mode 100644
index 000000000000..06ca97f88abb
--- /dev/null
+++ b/vcl/unx/gtk4/convert3to4.cxx
@@ -0,0 +1,889 @@
+/* -*- 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 <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/xml/dom/DocumentBuilder.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/XSAXSerializable.hpp>
+#include <comphelper/processfactory.hxx>
+#include <unx/gtk/gtkdata.hxx>
+#include <vcl/builder.hxx>
+#include "convert3to4.hxx"
+
+namespace
+{
+typedef std::pair<css::uno::Reference<css::xml::dom::XNode>, OUString> named_node;
+
+bool sortButtonNodes(const named_node& rA, const named_node& rB)
+{
+ //order within groups according to platform rules
+ return getButtonPriority("/" + rA.second.toUtf8())
+ < getButtonPriority("/" + rB.second.toUtf8());
+}
+
+// <property name="spacing">6</property>
+css::uno::Reference<css::xml::dom::XNode>
+CreateProperty(const css::uno::Reference<css::xml::dom::XDocument>& xDoc, const OUString& rPropName,
+ const OUString& rValue)
+{
+ css::uno::Reference<css::xml::dom::XElement> xProperty = xDoc->createElement("property");
+ css::uno::Reference<css::xml::dom::XAttr> xPropName = xDoc->createAttribute("name");
+ xPropName->setValue(rPropName);
+ xProperty->setAttributeNode(xPropName);
+ css::uno::Reference<css::xml::dom::XText> xValue = xDoc->createTextNode(rValue);
+ xProperty->appendChild(xValue);
+ return xProperty;
+}
+
+bool ToplevelIsMessageDialog(const css::uno::Reference<css::xml::dom::XNode>& xNode)
+{
+ for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate = xNode->getParentNode();
+ xObjectCandidate.is(); xObjectCandidate = xObjectCandidate->getParentNode())
+ {
+ if (xObjectCandidate->getNodeName() == "object")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xObjectMap
+ = xObjectCandidate->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xClass = xObjectMap->getNamedItem("class");
+ if (xClass->getNodeValue() == "GtkMessageDialog")
+ return true;
+ }
+ }
+ return false;
+}
+
+void SetPropertyOnTopLevel(const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ const css::uno::Reference<css::xml::dom::XNode>& xProperty)
+{
+ for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate = xNode->getParentNode();
+ xObjectCandidate.is(); xObjectCandidate = xObjectCandidate->getParentNode())
+ {
+ if (xObjectCandidate->getNodeName() == "object")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xObjectMap
+ = xObjectCandidate->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xClass = xObjectMap->getNamedItem("class");
+ if (xClass->getNodeValue() == "GtkDialog")
+ {
+ auto xFirstChild = xObjectCandidate->getFirstChild();
+ if (xFirstChild.is())
+ xObjectCandidate->insertBefore(xProperty, xFirstChild);
+ else
+ xObjectCandidate->appendChild(xProperty);
+
+ break;
+ }
+ }
+ }
+}
+
+OUString GetParentObjectType(const css::uno::Reference<css::xml::dom::XNode>& xNode)
+{
+ auto xParent = xNode->getParentNode();
+ assert(xParent->getNodeName() == "object");
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap = xParent->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xClass = xParentMap->getNamedItem("class");
+ return xClass->getNodeValue();
+}
+
+// currently runs the risk of duplicate margin-* properties if there was already such as well
+// as the border
+void AddBorderAsMargins(const css::uno::Reference<css::xml::dom::XNode>& xNode,
+ const OUString& rBorderWidth)
+{
+ auto xDoc = xNode->getOwnerDocument();
+
+ auto xMarginEnd = CreateProperty(xDoc, "margin-end", rBorderWidth);
+
+ auto xFirstChild = xNode->getFirstChild();
+ if (xFirstChild.is())
+ xNode->insertBefore(xMarginEnd, xFirstChild);
+ else
+ xNode->appendChild(xMarginEnd);
+
+ xNode->insertBefore(CreateProperty(xDoc, "margin-top", rBorderWidth), xMarginEnd);
+ xNode->insertBefore(CreateProperty(xDoc, "margin-bottom", rBorderWidth), xMarginEnd);
+ xNode->insertBefore(CreateProperty(xDoc, "margin-start", rBorderWidth), xMarginEnd);
+}
+
+struct ConvertResult
+{
+ bool m_bChildCanFocus;
+ bool m_bHasVisible;
+ bool m_bHasIconName;
+ bool m_bAlwaysShowImage;
+ css::uno::Reference<css::xml::dom::XNode> m_xPropertyLabel;
+
+ ConvertResult(bool bChildCanFocus, bool bHasVisible, bool bHasIconName, bool bAlwaysShowImage,
+ const css::uno::Reference<css::xml::dom::XNode>& rPropertyLabel)
+ : m_bChildCanFocus(bChildCanFocus)
+ , m_bHasVisible(bHasVisible)
+ , m_bHasIconName(bHasIconName)
+ , m_bAlwaysShowImage(bAlwaysShowImage)
+ , m_xPropertyLabel(rPropertyLabel)
+ {
+ }
+};
+
+ConvertResult Convert3To4(const css::uno::Reference<css::xml::dom::XNode>& xNode)
+{
+ css::uno::Reference<css::xml::dom::XNodeList> xNodeList = xNode->getChildNodes();
+ if (!xNodeList.is())
+ return ConvertResult(false, false, false, false, nullptr);
+
+ std::vector<css::uno::Reference<css::xml::dom::XNode>> xRemoveList;
+
+ OUString sBorderWidth;
+ bool bChildCanFocus = false;
+ bool bHasVisible = false;
+ bool bHasIconName = false;
+ bool bAlwaysShowImage = false;
+ css::uno::Reference<css::xml::dom::XNode> xPropertyLabel;
+ css::uno::Reference<css::xml::dom::XNode> xCantFocus;
+
+ css::uno::Reference<css::xml::dom::XNode> xChild = xNode->getFirstChild();
+ while (xChild.is())
+ {
+ if (xChild->getNodeName() == "requires")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xLib = xMap->getNamedItem("lib");
+ assert(xLib->getNodeValue() == "gtk+");
+ xLib->setNodeValue("gtk");
+ css::uno::Reference<css::xml::dom::XNode> xVersion = xMap->getNamedItem("version");
+ assert(xVersion->getNodeValue() == "3.20");
+ xVersion->setNodeValue("4.0");
+ }
+ else if (xChild->getNodeName() == "property")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xName = xMap->getNamedItem("name");
+ OUString sName(xName->getNodeValue().replace('_', '-'));
+
+ if (sName == "border-width")
+ sBorderWidth = xChild->getFirstChild()->getNodeValue();
+
+ if (sName == "has-default")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap
+ = xChild->getParentNode()->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xId = xParentMap->getNamedItem("id");
+ auto xDoc = xChild->getOwnerDocument();
+ auto xDefaultWidget = CreateProperty(xDoc, "default-widget", xId->getNodeValue());
+ SetPropertyOnTopLevel(xChild, xDefaultWidget);
+ xRemoveList.push_back(xChild);
+ }
+
+ if (sName == "has-focus" || sName == "is-focus")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap
+ = xChild->getParentNode()->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xId = xParentMap->getNamedItem("id");
+ auto xDoc = xChild->getOwnerDocument();
+ auto xDefaultWidget = CreateProperty(xDoc, "focus-widget", xId->getNodeValue());
+ SetPropertyOnTopLevel(xChild, xDefaultWidget);
+ xRemoveList.push_back(xChild);
+ }
+
+ if (sName == "can-focus")
+ {
+ bChildCanFocus = toBool(xChild->getFirstChild()->getNodeValue());
+ if (!bChildCanFocus)
+ {
+ OUString sParentClass = GetParentObjectType(xChild);
+ if (sParentClass == "GtkBox" || sParentClass == "GtkGrid")
+ {
+ // e.g. for the case of notebooks without children yet, just remove the can't focus property
+ // from Boxes and Grids
+ xRemoveList.push_back(xChild);
+ }
+ else if (sParentClass == "GtkComboBoxText")
+ {
+ // this was always a bit finicky in gtk3, fix it up to default to can-focus
+ xRemoveList.push_back(xChild);
+ }
+ else
+ {
+ // otherwise mark the property as needing removal if there turns out to be a child
+ // with can-focus of true, in which case remove this parent conflicting property
+ xCantFocus = xChild;
+ }
+ }
+ }
+
+ if (sName == "label")
+ xPropertyLabel = xChild;
+
+ if (sName == "visible")
+ bHasVisible = true;
+
+ if (sName == "icon-name")
+ bHasIconName = true;
+
+ if (sName == "events")
+ xRemoveList.push_back(xChild);
+
+ if (sName == "activates-default")
+ {
+ if (GetParentObjectType(xChild) == "GtkSpinButton")
+ xRemoveList.push_back(xChild);
+ }
+
+ if (sName == "width-chars")
+ {
+ if (GetParentObjectType(xChild) == "GtkEntry")
+ {
+ // I don't quite get what the difference should be wrt width-chars and max-width-chars
+ // but glade doesn't write max-width-chars and in gtk4 where we have width-chars, e.g
+ // print dialog, then max-width-chars gives the effect we wanted with width-chars
+ auto xDoc = xChild->getOwnerDocument();
+ auto mMaxWidthChars = CreateProperty(xDoc, "max-width-chars",
+ xChild->getFirstChild()->getNodeValue());
+ xChild->getParentNode()->insertBefore(mMaxWidthChars, xChild);
+ }
+ }
+
+ // remove 'Help' button label and replace with a help icon instead
+ if (sName == "label" && GetParentObjectType(xChild) == "GtkButton")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xParentMap
+ = xChild->getParentNode()->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xId = xParentMap->getNamedItem("id");
+ if (xId && xId->getNodeValue() == "help")
+ {
+ auto xDoc = xChild->getOwnerDocument();
+ auto xIconName = CreateProperty(xDoc, "icon-name", "help-browser-symbolic");
+ xChild->getParentNode()->insertBefore(xIconName, xChild);
+ xRemoveList.push_back(xChild);
+ }
+ }
+
+ if (sName == "icon-size")
+ {
+ if (GetParentObjectType(xChild) == "GtkImage")
+ {
+ if (xChild->getFirstChild()->getNodeValue() == "6")
+ {
+ auto xDoc = xChild->getOwnerDocument();
+ // convert old GTK_ICON_SIZE_DIALOG to new GTK_ICON_SIZE_LARGE
+ auto xIconSize = CreateProperty(xDoc, "icon-size", "2");
+ xChild->getParentNode()->insertBefore(xIconSize, xChild);
+ xRemoveList.push_back(xChild);
+ }
+ else
+ SAL_WARN("vcl.gtk", "what should we do with an icon-size of: "
+ << xChild->getFirstChild()->getNodeValue());
+ }
+ }
+
+ if (sName == "truncate-multiline")
+ {
+ if (GetParentObjectType(xChild) == "GtkSpinButton")
+ xRemoveList.push_back(xChild);
+ }
+
+ if (sName == "homogeneous")
+ {
+ // e.g. the buttonbox in xml filter dialog
+ if (GetParentObjectType(xChild) == "GtkButtonBox")
+ xRemoveList.push_back(xChild);
+ }
+
+ if (sName == "shadow-type")
+ {
+ if (GetParentObjectType(xChild) == "GtkFrame")
+ xRemoveList.push_back(xChild);
+ else if (GetParentObjectType(xChild) == "GtkScrolledWindow")
+ {
+ bool bHasFrame = xChild->getFirstChild()->getNodeValue() != "none";
+ auto xDoc = xChild->getOwnerDocument();
+ auto xHasFrame = CreateProperty(
+ xDoc, "has-frame", bHasFrame ? OUString("True") : OUString("False"));
+ xChild->getParentNode()->insertBefore(xHasFrame, xChild);
+ xRemoveList.push_back(xChild);
+ }
+ }
+
+ if (sName == "always-show-image")
+ {
+ if (GetParentObjectType(xChild) == "GtkButton")
+ {
+ // we will turn always-show-image into a GtkBox child for
+ // GtkButton and a GtkLabel child for the GtkBox and move
+ // the label property into it.
+ bAlwaysShowImage = toBool(xChild->getFirstChild()->getNodeValue());
+ xRemoveList.push_back(xChild);
+ }
+ }
+
+ if (sName == "relief")
+ {
+ if (GetParentObjectType(xChild) == "GtkLinkButton"
+ || GetParentObjectType(xChild) == "GtkButton")
+ {
+ assert(xChild->getFirstChild()->getNodeValue() == "none");
+ auto xDoc = xChild->getOwnerDocument();
+ auto xHasFrame = CreateProperty(xDoc, "has-frame", "False");
+ xChild->getParentNode()->insertBefore(xHasFrame, xChild);
+ xRemoveList.push_back(xChild);
+ }
+ }
+
+ if (sName == "xalign")
+ {
+ if (GetParentObjectType(xChild) == "GtkLinkButton"
+ || GetParentObjectType(xChild) == "GtkMenuButton"
+ || GetParentObjectType(xChild) == "GtkButton")
+ {
+ // TODO expand into a GtkLabel child with alignment on that instead
+ assert(xChild->getFirstChild()->getNodeValue() == "0");
+ xRemoveList.push_back(xChild);
+ }
+ }
+
+ if (sName == "hscrollbar-policy")
+ {
+ if (GetParentObjectType(xChild) == "GtkScrolledWindow")
+ {
+ if (xChild->getFirstChild()->getNodeValue() == "never")
+ {
+ auto xDoc = xChild->getOwnerDocument();
+ auto xHasFrame = CreateProperty(xDoc, "propagate-natural-width", "True");
+ xChild->getParentNode()->insertBefore(xHasFrame, xChild);
+ }
+ }
+ }
+
+ if (sName == "vscrollbar-policy")
+ {
+ if (GetParentObjectType(xChild) == "GtkScrolledWindow")
+ {
+ if (xChild->getFirstChild()->getNodeValue() == "never")
+ {
+ auto xDoc = xChild->getOwnerDocument();
+ auto xHasFrame = CreateProperty(xDoc, "propagate-natural-height", "True");
+ xChild->getParentNode()->insertBefore(xHasFrame, xChild);
+ }
+ }
+ }
+
+ if (sName == "image")
+ {
+ if (GetParentObjectType(xChild) == "GtkButton")
+ {
+ // find the image object, expected to be a child of "interface" and relocate
+ // it to be a child of this GtkButton
+ auto xObjectCandidate = xChild->getParentNode();
+ if (xObjectCandidate->getNodeName() == "object")
+ {
+ OUString sImageId = xChild->getFirstChild()->getNodeValue();
+
+ css::uno::Reference<css::xml::dom::XNode> xRootCandidate
+ = xChild->getParentNode();
+ while (xRootCandidate)
+ {
+ if (xRootCandidate->getNodeName() == "interface")
+ break;
+ xRootCandidate = xRootCandidate->getParentNode();
+ }
+
+ css::uno::Reference<css::xml::dom::XNode> xImageNode;
+
+ for (auto xImageCandidate = xRootCandidate->getFirstChild();
+ xImageCandidate.is();
+ xImageCandidate = xImageCandidate->getNextSibling())
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xImageCandidateMap
+ = xImageCandidate->getAttributes();
+ if (!xImageCandidateMap.is())
+ continue;
+ css::uno::Reference<css::xml::dom::XNode> xId
+ = xImageCandidateMap->getNamedItem("id");
+ if (xId && xId->getNodeValue() == sImageId)
+ {
+ xImageNode = xImageCandidate;
+ break;
+ }
+ }
+
+ auto xDoc = xChild->getOwnerDocument();
+ css::uno::Reference<css::xml::dom::XElement> xImageChild
+ = xDoc->createElement("child");
+ xImageChild->appendChild(
+ xImageNode->getParentNode()->removeChild(xImageNode));
+ xObjectCandidate->appendChild(xImageChild);
+ }
+
+ xRemoveList.push_back(xChild);
+ }
+ }
+
+ if (sName == "draw-indicator")
+ {
+ assert(toBool(xChild->getFirstChild()->getNodeValue()));
+ xRemoveList.push_back(xChild);
+ }
+
+ if (sName == "type-hint" || sName == "skip-taskbar-hint" || sName == "can-default"
+ || sName == "border-width" || sName == "layout-style" || sName == "no-show-all"
+ || sName == "ignore-hidden" || sName == "window-position")
+ {
+ xRemoveList.push_back(xChild);
+ }
+ }
+ else if (xChild->getNodeName() == "child")
+ {
+ bool bContentArea = false;
+
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xName = xMap->getNamedItem("internal-child");
+ if (xName)
+ {
+ OUString sName(xName->getNodeValue());
+ if (sName == "vbox")
+ {
+ xName->setNodeValue("content_area");
+ bContentArea = true;
+ }
+ else if (sName == "accessible")
+ xRemoveList.push_back(
+ xChild); // Yikes!, what's the replacement for this going to be
+ }
+
+ if (bContentArea)
+ {
+ for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate
+ = xChild->getFirstChild();
+ xObjectCandidate.is(); xObjectCandidate = xObjectCandidate->getNextSibling())
+ {
+ if (xObjectCandidate->getNodeName() == "object")
+ {
+ auto xDoc = xChild->getOwnerDocument();
+
+ auto xVExpand = CreateProperty(xDoc, "vexpand", "True");
+ auto xFirstChild = xObjectCandidate->getFirstChild();
+ if (xFirstChild.is())
+ xObjectCandidate->insertBefore(xVExpand, xFirstChild);
+ else
+ xObjectCandidate->appendChild(xVExpand);
+
+ if (!sBorderWidth.isEmpty())
+ {
+ AddBorderAsMargins(xObjectCandidate, sBorderWidth);
+ sBorderWidth.clear();
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ else if (xChild->getNodeName() == "packing")
+ {
+ // remove "packing" and if its grid packing insert a replacement "layout" into
+ // the associated "object"
+ auto xDoc = xChild->getOwnerDocument();
+ css::uno::Reference<css::xml::dom::XElement> xNew = xDoc->createElement("layout");
+
+ bool bGridPacking = false;
+
+ // iterate over all children and append them to the new element
+ for (css::uno::Reference<css::xml::dom::XNode> xCurrent = xChild->getFirstChild();
+ xCurrent.is(); xCurrent = xChild->getFirstChild())
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xCurrent->getAttributes();
+ if (xMap.is())
+ {
+ css::uno::Reference<css::xml::dom::XNode> xName = xMap->getNamedItem("name");
+ OUString sName(xName->getNodeValue().replace('_', '-'));
+ if (sName == "left-attach")
+ {
+ xName->setNodeValue("column");
+ bGridPacking = true;
+ }
+ else if (sName == "top-attach")
+ {
+ xName->setNodeValue("row");
+ bGridPacking = true;
+ }
+ else if (sName == "width")
+ {
+ xName->setNodeValue("column-span");
+ bGridPacking = true;
+ }
+ else if (sName == "height")
+ {
+ xName->setNodeValue("row-span");
+ bGridPacking = true;
+ }
+ else if (sName == "secondary")
+ {
+ // turn parent tag of <child> into <child type="start">
+ auto xParent = xChild->getParentNode();
+ css::uno::Reference<css::xml::dom::XAttr> xTypeStart
+ = xDoc->createAttribute("type");
+ xTypeStart->setValue("start");
+ css::uno::Reference<css::xml::dom::XElement> xElem(
+ xParent, css::uno::UNO_QUERY_THROW);
+ xElem->setAttributeNode(xTypeStart);
+ }
+ }
+ xNew->appendChild(xChild->removeChild(xCurrent));
+ }
+
+ if (bGridPacking)
+ {
+ // go back to parent and find the object child and insert this "layout" as a
+ // new child of the object
+ auto xParent = xChild->getParentNode();
+ for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate
+ = xParent->getFirstChild();
+ xObjectCandidate.is(); xObjectCandidate = xObjectCandidate->getNextSibling())
+ {
+ if (xObjectCandidate->getNodeName() == "object")
+ {
+ xObjectCandidate->appendChild(xNew);
+ break;
+ }
+ }
+ }
+
+ xRemoveList.push_back(xChild);
+ }
+ else if (xChild->getNodeName() == "accessibility")
+ {
+ // TODO <relation type="labelled-by" target="pagenumcb"/> -> <relation name="labelled-by">pagenumcb</relation>
+ xRemoveList.push_back(xChild);
+ }
+ else if (xChild->getNodeName() == "accelerator")
+ {
+ // TODO is anything like this supported anymore in .ui files
+ xRemoveList.push_back(xChild);
+ }
+
+ auto xNextChild = xChild->getNextSibling();
+
+ bool bChildHasIconName = false;
+ bool bChildHasVisible = false;
+ bool bChildAlwaysShowImage = false;
+ css::uno::Reference<css::xml::dom::XNode> xChildPropertyLabel;
+ if (xChild->hasChildNodes())
+ {
+ auto aChildRes = Convert3To4(xChild);
+ bChildCanFocus |= aChildRes.m_bChildCanFocus;
+ if (bChildCanFocus && xCantFocus.is())
+ {
+ xNode->removeChild(xCantFocus);
+ xCantFocus.clear();
+ }
+ if (xChild->getNodeName() == "object")
+ {
+ bChildHasVisible = aChildRes.m_bHasVisible;
+ bChildHasIconName = aChildRes.m_bHasIconName;
+ bChildAlwaysShowImage = aChildRes.m_bAlwaysShowImage;
+ xChildPropertyLabel = aChildRes.m_xPropertyLabel;
+ }
+ }
+
+ if (xChild->getNodeName() == "object")
+ {
+ auto xDoc = xChild->getOwnerDocument();
+
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xClass = xMap->getNamedItem("class");
+ OUString sClass(xClass->getNodeValue());
+
+ auto xInternalChildCandidate = xChild->getParentNode();
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xInternalChildCandidateMap
+ = xInternalChildCandidate->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xId
+ = xInternalChildCandidateMap->getNamedItem("internal-child");
+
+ // turn default gtk3 invisibility for widget objects into explicit invisible, but ignore internal-children
+ if (!bChildHasVisible && !xId)
+ {
+ if (sClass == "GtkBox" || sClass == "GtkButton" || sClass == "GtkCalendar"
+ || sClass == "GtkCheckButton" || sClass == "GtkRadioButton"
+ || sClass == "GtkComboBox" || sClass == "GtkComboBoxText"
+ || sClass == "GtkDrawingArea" || sClass == "GtkEntry" || sClass == "GtkExpander"
+ || sClass == "GtkFrame" || sClass == "GtkGrid" || sClass == "GtkImage"
+ || sClass == "GtkLabel" || sClass == "GtkMenuButton" || sClass == "GtkNotebook"
+ || sClass == "GtkOverlay" || sClass == "GtkPaned" || sClass == "GtkProgressBar"
+ || sClass == "GtkScrolledWindow" || sClass == "GtkSeparator"
+ || sClass == "GtkSpinButton" || sClass == "GtkSpinner"
+ || sClass == "GtkTextView" || sClass == "GtkTreeView" || sClass == "GtkViewport"
+ || sClass == "GtkLinkButton" || sClass == "GtkToggleButton"
+ || sClass == "GtkButtonBox")
+
+ {
+ auto xVisible = CreateProperty(xDoc, "visible", "False");
+ auto xFirstChild = xChild->getFirstChild();
+ if (xFirstChild.is())
+ xChild->insertBefore(xVisible, xFirstChild);
+ else
+ xChild->appendChild(xVisible);
+ }
+ }
+
+ if (sClass == "GtkButtonBox")
+ {
+ if (xId && xId->getNodeValue() == "action_area" && !ToplevelIsMessageDialog(xChild))
+ {
+ xClass->setNodeValue("GtkHeaderBar");
+ auto xSpacingNode = CreateProperty(xDoc, "show-title-buttons", "False");
+ auto xFirstChild = xChild->getFirstChild();
+ if (xFirstChild.is())
+ xChild->insertBefore(xSpacingNode, xFirstChild);
+ else
+ xChild->appendChild(xSpacingNode);
+
+ // move the replacement GtkHeaderBar up to before the content_area
+ auto xContentAreaCandidate = xChild->getParentNode();
+ while (xContentAreaCandidate)
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xChildMap
+ = xContentAreaCandidate->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xName
+ = xChildMap->getNamedItem("internal-child");
+ if (xName && xName->getNodeValue() == "content_area")
+ {
+ auto xActionArea = xChild->getParentNode();
+
+ xActionArea->getParentNode()->removeChild(xActionArea);
+
+ css::uno::Reference<css::xml::dom::XAttr> xTypeTitleBar
+ = xDoc->createAttribute("type");
+ xTypeTitleBar->setValue("titlebar");
+ css::uno::Reference<css::xml::dom::XElement> xElem(
+ xActionArea, css::uno::UNO_QUERY_THROW);
+ xElem->setAttributeNode(xTypeTitleBar);
+ xElem->removeAttribute("internal-child");
+
+ xContentAreaCandidate->getParentNode()->insertBefore(
+ xActionArea, xContentAreaCandidate);
+
+ std::vector<named_node> aChildren;
+
+ css::uno::Reference<css::xml::dom::XNode> xTitleChild
+ = xChild->getFirstChild();
+ while (xTitleChild.is())
+ {
+ auto xNextTitleChild = xTitleChild->getNextSibling();
+ if (xTitleChild->getNodeName() == "child")
+ {
+ OUString sNodeId;
+
+ for (css::uno::Reference<css::xml::dom::XNode> xObjectCandidate
+ = xTitleChild->getFirstChild();
+ xObjectCandidate.is();
+ xObjectCandidate = xObjectCandidate->getNextSibling())
+ {
+ if (xObjectCandidate->getNodeName() == "object")
+ {
+ css::uno::Reference<css::xml::dom::XNamedNodeMap>
+ xObjectMap = xObjectCandidate->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xObjectId
+ = xObjectMap->getNamedItem("id");
+ sNodeId = xObjectId->getNodeValue();
+ break;
+ }
+ }
+
+ aChildren.push_back(std::make_pair(xTitleChild, sNodeId));
+ }
+ else if (xTitleChild->getNodeName() == "property")
+ {
+ // remove any <property name="homogeneous"> tag
+ css::uno::Reference<css::xml::dom::XNamedNodeMap> xTitleChildMap
+ = xTitleChild->getAttributes();
+ css::uno::Reference<css::xml::dom::XNode> xPropName
+ = xTitleChildMap->getNamedItem("name");
+ OUString sPropName(xPropName->getNodeValue().replace('_', '-'));
+ if (sPropName == "homogeneous")
+ xChild->removeChild(xTitleChild);
+ }
+
+ xTitleChild = xNextTitleChild;
+ }
+
+ //sort child order within parent so that we match the platform button order
+ std::stable_sort(aChildren.begin(), aChildren.end(), sortButtonNodes);
+
+ int nNonHelpButtonCount = 0;
+
+ for (const auto& rTitleChild : aChildren)
+ {
+ xChild->removeChild(rTitleChild.first);
+ if (rTitleChild.second != "help")
+ ++nNonHelpButtonCount;
+ }
+
+ std::reverse(aChildren.begin(), aChildren.end());
+
+ for (const auto& rTitleChild : aChildren)
+ {
+ xChild->appendChild(rTitleChild.first);
+
+ css::uno::Reference<css::xml::dom::XElement> xChildElem(
+ rTitleChild.first, css::uno::UNO_QUERY_THROW);
+ if (!xChildElem->hasAttribute("type"))
+ {
+ // turn parent tag of <child> into <child type="end"> except for cancel/close which we'll
+ // put at start unless there is nothing at end
+ css::uno::Reference<css::xml::dom::XAttr> xTypeEnd
+ = xDoc->createAttribute("type");
+ if (nNonHelpButtonCount >= 2
+ && (rTitleChild.second == "cancel"
+ || rTitleChild.second == "close"))
+ xTypeEnd->setValue("start");
+ else
+ xTypeEnd->setValue("end");
+ xChildElem->setAttributeNode(xTypeEnd);
+ }
+ }
+
+ auto xUseHeaderBar = CreateProperty(xDoc, "use-header-bar", "1");
+ SetPropertyOnTopLevel(xContentAreaCandidate, xUseHeaderBar);
+
+ break;
+ }
+ xContentAreaCandidate = xContentAreaCandidate->getParentNode();
+ }
+ }
+ else // GtkMessageDialog
+ xClass->setNodeValue("GtkBox");
+ }
+ else if (sClass == "GtkRadioButton")
+ {
+ xClass->setNodeValue("GtkCheckButton");
+ }
+ else if (sClass == "GtkImage" && !bChildHasIconName)
+ {
+ xClass->setNodeValue("GtkPicture");
+ }
+ else if (sClass == "GtkPopover" && !bHasVisible)
+ {
+ auto xVisible = CreateProperty(xDoc, "visible", "False");
+ auto xFirstChild = xChild->getFirstChild();
+ if (xFirstChild.is())
+ xChild->insertBefore(xVisible, xFirstChild);
+ else
+ xChild->appendChild(xVisible);
+ }
+
+ if (bChildAlwaysShowImage)
+ {
+ auto xImageCandidateNode = xChild->getLastChild();
+ if (xImageCandidateNode && xImageCandidateNode->getNodeName() != "child")
+ xImageCandidateNode.clear();
+ if (xImageCandidateNode)
+ xChild->removeChild(xImageCandidateNode);
+
+ css::uno::Reference<css::xml::dom::XElement> xNewChildNode
+ = xDoc->createElement("child");
+ css::uno::Reference<css::xml::dom::XElement> xNewObjectNode
+ = xDoc->createElement("object");
+ css::uno::Reference<css::xml::dom::XAttr> xBoxClassName
+ = xDoc->createAttribute("class");
+ xBoxClassName->setValue("GtkBox");
+ xNewObjectNode->setAttributeNode(xBoxClassName);
+ xNewChildNode->appendChild(xNewObjectNode);
+
+ xChild->appendChild(xNewChildNode);
+
+ css::uno::Reference<css::xml::dom::XElement> xNewLabelChildNode
+ = xDoc->createElement("child");
+ css::uno::Reference<css::xml::dom::XElement> xNewChildObjectNode
+ = xDoc->createElement("object");
+ css::uno::Reference<css::xml::dom::XAttr> xLabelClassName
+ = xDoc->createAttribute("class");
+ xLabelClassName->setValue("GtkLabel");
+ xNewChildObjectNode->setAttributeNode(xLabelClassName);
+ if (xChildPropertyLabel)
+ xNewChildObjectNode->appendChild(
+ xChildPropertyLabel->getParentNode()->removeChild(xChildPropertyLabel));
+ xNewLabelChildNode->appendChild(xNewChildObjectNode);
+
+ if (xImageCandidateNode)
+ xNewObjectNode->appendChild(xImageCandidateNode);
+ xNewObjectNode->appendChild(xNewLabelChildNode);
+ }
+ }
+
+ xChild = xNextChild;
+ }
+
+ if (!sBorderWidth.isEmpty())
+ AddBorderAsMargins(xNode, sBorderWidth);
+
+ for (auto& xRemove : xRemoveList)
+ xNode->removeChild(xRemove);
+
+ return ConvertResult(bChildCanFocus, bHasVisible, bHasIconName, bAlwaysShowImage,
+ xPropertyLabel);
+}
+}
+
+void builder_add_from_gtk3_file(GtkBuilder* pBuilder, const OUString& rUri)
+{
+ GError* err = nullptr;
+
+ // load the xml
+ css::uno::Reference<css::uno::XComponentContext> xContext
+ = ::comphelper::getProcessComponentContext();
+ css::uno::Reference<css::xml::dom::XDocumentBuilder> xBuilder
+ = css::xml::dom::DocumentBuilder::create(xContext);
+ css::uno::Reference<css::xml::dom::XDocument> xDocument = xBuilder->parseURI(rUri);
+
+ // convert it from gtk3 to gtk4
+ Convert3To4(xDocument);
+
+ css::uno::Reference<css::beans::XPropertySet> xTempFile(css::io::TempFile::create(xContext),
+ css::uno::UNO_QUERY);
+ css::uno::Reference<css::io::XStream> xTempStream(xTempFile, css::uno::UNO_QUERY_THROW);
+ xTempFile->setPropertyValue("RemoveFile", css::uno::makeAny(false));
+
+ // serialize it back to xml
+ css::uno::Reference<css::xml::sax::XSAXSerializable> xSerializer(xDocument,
+ css::uno::UNO_QUERY);
+ css::uno::Reference<css::xml::sax::XWriter> xWriter = css::xml::sax::Writer::create(xContext);
+ css::uno::Reference<css::io::XOutputStream> xTempOut = xTempStream->getOutputStream();
+ xWriter->setOutputStream(xTempOut);
+ xSerializer->serialize(
+ css::uno::Reference<css::xml::sax::XDocumentHandler>(xWriter, css::uno::UNO_QUERY_THROW),
+ css::uno::Sequence<css::beans::StringPair>());
+
+ // feed it to GtkBuilder
+ css::uno::Reference<css::io::XSeekable> xTempSeek(xTempStream, css::uno::UNO_QUERY_THROW);
+ xTempSeek->seek(0);
+ auto xInput = xTempStream->getInputStream();
+ css::uno::Sequence<sal_Int8> bytes;
+ sal_Int32 nToRead = xInput->available();
+ while (true)
+ {
+ sal_Int32 nRead = xInput->readBytes(bytes, std::max<sal_Int32>(nToRead, 4096));
+ if (!nRead)
+ break;
+ // fprintf(stderr, "text is %s\n", reinterpret_cast<const gchar*>(bytes.getArray()));
+ auto rc = gtk_builder_add_from_string(
+ pBuilder, reinterpret_cast<const gchar*>(bytes.getArray()), nRead, &err);
+ if (!rc)
+ {
+ SAL_WARN("vcl.gtk",
+ "GtkInstanceBuilder: error when calling gtk_builder_add_from_string: "
+ << err->message);
+ g_error_free(err);
+ }
+ assert(rc && "could not load UI file");
+ // in the real world the first loop has read the entire file because its all 'available' without blocking
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk4/gtkinst.cxx b/vcl/unx/gtk4/gtkinst.cxx
index a1066a6d3548..e166777412b0 100644
--- a/vcl/unx/gtk4/gtkinst.cxx
+++ b/vcl/unx/gtk4/gtkinst.cxx
@@ -10,6 +10,8 @@
// make gtk4 plug advertise correctly as gtk4
#define GTK_TOOLKIT_NAME "gtk4"
+#include "convert3to4.hxx"
+
#include "../gtk3/gtkinst.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
More information about the Libreoffice-commits
mailing list