/* -*- 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/.
*/
// 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();
bool IsAllowedBuiltInIcon(std::u16string_view iconName)
{ // limit the named icons to those known by VclBuilder return VclBuilder::mapStockToSymbol(iconName) != SymbolType::DONTKNOW;
}
if (sName == "can-focus")
{
bChildCanFocus = toBool(xChild->getFirstChild()->getNodeValue()); if (!bChildCanFocus)
{
OUString sParentClass = GetParentObjectType(xChild); if (sParentClass == "GtkBox" || sParentClass == "GtkGrid"
|| sParentClass == "GtkViewport")
{ // 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);
} elseif (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 == "modal")
{
OUString sParentClass = GetParentObjectType(xChild); if (sParentClass == "GtkPopover")
xName->setNodeValue("autohide");
}
if (sName == "visible")
bHasVisible = true;
if (sName == "icon-name")
xPropertyIconName = xChild;
if (sName == "show-arrow")
xRemoveList.push_back(xChild);
if (sName == "events")
xRemoveList.push_back(xChild);
if (sName == "constrain-to")
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, u"max-width-chars"_ustr,
xChild->getFirstChild()->getNodeValue());
xChild->getParentNode()->insertBefore(mMaxWidthChars, xChild);
}
}
// remove 'Help' button label and replace with a help icon instead. Unless the toplevel is a message dialog if (sName == "label" && GetParentObjectType(xChild) == "GtkButton"
&& !ToplevelIsMessageDialog(xChild))
{
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, u"icon-name"_ustr, u"help-browser-symbolic"_ustr);
xChild->getParentNode()->insertBefore(xIconName, xChild);
xRemoveList.push_back(xChild);
}
}
if (sName == "icon-size")
{ if (GetParentObjectType(xChild) == "GtkImage")
{
bHasIconSize = true;
OUString sSize = xChild->getFirstChild()->getNodeValue(); /* old: 3 -> GTK_ICON_SIZE_LARGE_TOOLBAR: Size appropriate for large toolbars (24px) 5 -> GTK_ICON_SIZE_DND: Size appropriate for drag and drop (32px) 6 -> GTK_ICON_SIZE_DIALOG: Size appropriate for dialogs (48px)
if (GetParentObjectType(xChild) == "GtkToolbar")
xRemoveList.push_back(xChild);
}
if (sName == "input-purpose" && GetParentObjectType(xChild) == "GtkSpinButton")
{ // "input-purpose" is a property of GtkEntry, but // GTK 4 GtkSpinButton no longer derives from GtkEntry
xRemoveList.push_back(xChild);
}
if (sName == "truncate-multiline")
{ if (GetParentObjectType(xChild) == "GtkSpinButton")
xRemoveList.push_back(xChild);
}
if (sName == "has-frame")
{ if (GetParentObjectType(xChild) == "GtkSpinButton")
xRemoveList.push_back(xChild);
}
if (sName == "toolbar-style")
{ // is there an equivalent for this ?
xRemoveList.push_back(xChild);
}
if (sName == "shadow-type")
{ if (GetParentObjectType(xChild) == "GtkFrame")
xRemoveList.push_back(xChild); elseif (GetParentObjectType(xChild) == "GtkScrolledWindow")
{ bool bHasFrame = xChild->getFirstChild()->getNodeValue() != "none"; auto xDoc = xChild->getOwnerDocument(); auto xHasFrame = CreateProperty(xDoc, u"has-frame"_ustr,
bHasFrame ? u"True"_ustr : u"False"_ustr);
xChild->getParentNode()->insertBefore(xHasFrame, xChild);
xRemoveList.push_back(xChild);
}
}
if (sName == "always-show-image")
{ if (GetParentObjectType(xChild) == "GtkButton"
|| GetParentObjectType(xChild) == "GtkMenuButton"
|| GetParentObjectType(xChild) == "GtkToggleButton")
{ // 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 == "image-position")
{ 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.
OUString sImagePos = xChild->getFirstChild()->getNodeValue(); if (sImagePos == "top")
eImagePos = GTK_POS_TOP; elseif (sImagePos == "bottom")
eImagePos = GTK_POS_BOTTOM; elseif (sImagePos == "right")
eImagePos = GTK_POS_RIGHT; else
assert(sImagePos == "left");
xRemoveList.push_back(xChild);
}
}
if (sName == "use-underline")
bUseUnderline = toBool(xChild->getFirstChild()->getNodeValue());
if (sName == "orientation")
bVertOrientation = xChild->getFirstChild()->getNodeValue() == "vertical";
if (sName == "relief")
{ if (GetParentObjectType(xChild) == "GtkToggleButton"
|| GetParentObjectType(xChild) == "GtkMenuButton"
|| GetParentObjectType(xChild) == "GtkLinkButton"
|| GetParentObjectType(xChild) == "GtkButton")
{
assert(xChild->getFirstChild()->getNodeValue() == "none"); auto xDoc = xChild->getOwnerDocument(); auto xHasFrame = CreateProperty(xDoc, u"has-frame"_ustr, u"False"_ustr);
xChild->getParentNode()->insertBefore(xHasFrame, xChild);
xRemoveList.push_back(xChild);
}
}
if (sName == "xalign")
{ if (GetParentObjectType(xChild) == "GtkLinkButton"
|| GetParentObjectType(xChild) == "GtkMenuButton"
|| GetParentObjectType(xChild) == "GtkToggleButton"
|| GetParentObjectType(xChild) == "GtkButton")
{ // TODO expand into a GtkLabel child with alignment on that instead
assert(xChild->getFirstChild()->getNodeValue() == "0");
bXAlign = true;
xRemoveList.push_back(xChild);
}
}
if (sName == "use-popover")
{ if (GetParentObjectType(xChild) == "GtkMenuButton")
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, u"propagate-natural-width"_ustr, u"True"_ustr);
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, u"propagate-natural-height"_ustr, u"True"_ustr);
xChild->getParentNode()->insertBefore(xHasFrame, xChild);
}
}
}
if (sName == "popup")
{ if (GetParentObjectType(xChild) == "GtkMenuButton")
{
OUString sMenuName = xChild->getFirstChild()->getNodeValue(); auto xDoc = xChild->getOwnerDocument(); auto xPopover = CreateProperty(xDoc, u"popover"_ustr, sMenuName);
xChild->getParentNode()->insertBefore(xPopover, xChild);
xRemoveList.push_back(xChild);
}
}
if (sName == "image")
{ if (GetParentObjectType(xChild) == "GtkButton"
|| GetParentObjectType(xChild) == "GtkMenuButton"
|| GetParentObjectType(xChild) == "GtkToggleButton")
{ // find the image object, expected to be a child of "interface" 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();
}
// relocate it to be a child of this GtkButton
xGeneratedImageChild = xDoc->createElement("child");
xGeneratedImageChild->appendChild(
xImageNode->getParentNode()->removeChild(xImageNode));
xObjectCandidate->appendChild(xGeneratedImageChild);
}
xRemoveList.push_back(xChild);
}
}
if (sName == "draw-indicator")
{
assert(toBool(xChild->getFirstChild()->getNodeValue())); if (GetParentObjectType(xChild) == "GtkMenuButton" && gtk_get_minor_version() >= 4)
{ auto xDoc = xChild->getOwnerDocument(); auto xAlwaysShowArrow
= CreateProperty(xDoc, u"always-show-arrow"_ustr, u"True"_ustr);
xChild->getParentNode()->insertBefore(xAlwaysShowArrow, xChild);
}
xRemoveList.push_back(xChild);
}
if (bContentArea)
{
css::uno::Reference<css::xml::dom::XNode> xObject = GetChildObject(xChild); if (xObject)
{ auto xDoc = xChild->getOwnerDocument();
auto xVExpand = CreateProperty(xDoc, u"vexpand"_ustr, u"True"_ustr);
insertAsFirstChild(xObject, xVExpand);
if (!sBorderWidth.isEmpty())
{
AddBorderAsMargins(xObject, sBorderWidth);
sBorderWidth.clear();
}
}
}
} elseif (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;
} elseif (sName == "top-attach")
{
xName->setNodeValue("row");
bGridPacking = true;
} elseif (sName == "width")
{
xName->setNodeValue("column-span");
bGridPacking = true;
} elseif (sName == "height")
{
xName->setNodeValue("row-span");
bGridPacking = true;
} elseif (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);
} elseif (sName == "pack-type")
{ // turn parent tag of <child> into <child type="start"> auto xParent = xChild->getParentNode();
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();
css::uno::Reference<css::xml::dom::XNode> xObject = GetChildObject(xParent); if (xObject)
xObject->appendChild(xNew);
}
xRemoveList.push_back(xChild);
} elseif (xChild->getNodeName() == "accelerator")
{ // TODO is anything like this supported anymore in .ui files
xRemoveList.push_back(xChild);
} elseif (xChild->getNodeName() == "relation")
{ auto xDoc = xChild->getOwnerDocument();
css::uno::Reference<css::xml::dom::XNamedNodeMap> xMap = xChild->getAttributes();
css::uno::Reference<css::xml::dom::XNode> xType = xMap->getNamedItem("type"); if (xType->getNodeValue() == "label-for")
{ // there will be a matching labelled-by which should be sufficient in the gtk4 world
xRemoveList.push_back(xChild);
} if (xType->getNodeValue() == "controlled-by")
{ // there will be a matching controller-for converted to -> controls // which should be sufficient in the gtk4 world
xRemoveList.push_back(xChild);
} else
{
css::uno::Reference<css::xml::dom::XNode> xTarget = xMap->getNamedItem("target");
// now remove GtkMenu contents while (true)
{ auto xFirstChild = xChild->getFirstChild(); if (!xFirstChild.is()) break;
xChild->removeChild(xFirstChild);
}
// change to GtkPopoverMenu
xClass->setNodeValue("GtkPopoverMenu");
if (sClass == "GtkButtonBox")
{ if (xInternalChild && xInternalChild->getNodeValue() == "action_area"
&& !ToplevelIsMessageDialog(xChild))
{
xClass->setNodeValue("GtkHeaderBar"); auto xSpacingNode
= CreateProperty(xDoc, u"show-title-buttons"_ustr, u"False"_ustr);
insertAsFirstChild(xChild, 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();
//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 (constauto& rTitleChild : aChildren)
{
xChild->removeChild(rTitleChild.first); if (rTitleChild.second != "help")
++nNonHelpButtonCount;
}
std::reverse(aChildren.begin(), aChildren.end());
for (constauto& 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, u"use-header-bar"_ustr, u"1"_ustr);
SetPropertyOnTopLevel(xContentAreaCandidate, xUseHeaderBar);
if (!aPackEnds.empty())
{
std::reverse(aPackEnds.begin(), aPackEnds.end());
if (!bChildVertOrientation)
{ bool bHasStartObject = false; bool bLastStartExpands = false; if (!aPackStarts.empty())
{
css::uno::Reference<css::xml::dom::XNode> xLastStartObject; for (auto it = aPackStarts.rbegin(); it != aPackStarts.rend(); ++it)
{
xLastStartObject = GetChildObject(*it); if (xLastStartObject.is())
{
bHasStartObject = true; for (css::uno::Reference<css::xml::dom::XNode> xExpandCandidate
= xLastStartObject->getFirstChild();
xExpandCandidate.is();
xExpandCandidate = xExpandCandidate->getNextSibling())
{ if (xExpandCandidate->getNodeName() == "property")
{
css::uno::Reference<css::xml::dom::XNamedNodeMap>
xExpandMap = xExpandCandidate->getAttributes();
css::uno::Reference<css::xml::dom::XNode> xName
= xExpandMap->getNamedItem("name");
OUString sPropName(xName->getNodeValue()); if (sPropName == "hexpand")
{
bLastStartExpands
= toBool(xExpandCandidate->getFirstChild()
->getNodeValue()); break;
}
}
} break;
}
}
}
if (bHasStartObject && !bLastStartExpands)
{ auto xAlign = CreateProperty(xDoc, u"halign"_ustr, u"end"_ustr);
insertAsFirstChild(GetChildObject(aPackEnds[0]), xAlign); auto xExpand = CreateProperty(xDoc, u"hexpand"_ustr, u"True"_ustr);
insertAsFirstChild(GetChildObject(aPackEnds[0]), xExpand);
}
}
for (auto& xPackEnd : aPackEnds)
xChild->appendChild(xPackEnd);
}
} elseif (sClass == "GtkRadioButton")
{
xClass->setNodeValue("GtkCheckButton");
} elseif (sClass == "GtkImage")
{ /* a) keep symbolic icon-names as GtkImage, e.g. writer, format, columns, next/prev buttons b) assume that an explicit icon-size represents a request for a scaled icon so keep those as GtkImage. e.g. hyperlink dialog notebook tab images and calc paste special button images c) turn everything else into a GtkPicture, e.g. help, about. If a GtkPicture ends up used to display just a simple icon that's generally not a problem.
*/ bool bKeepAsImage = false; if (bChildHasIconSize)
bKeepAsImage = true; elseif (xChildPropertyIconName.is())
{
OUString sIconName(xChildPropertyIconName->getFirstChild()->getNodeValue()); bool bHasSymbolicIconName = IsAllowedBuiltInIcon(sIconName); if (bHasSymbolicIconName)
{ if (sIconName != "missing-image")
bKeepAsImage = true; else
{ // If the symbolic icon-name is missing-image then decide to make // it a GtkPicture if it has a parent widget and keep it as GtkImage // if it has just the root "interface" as parent. // for e.g. view, user interface
css::uno::Reference<css::xml::dom::XNode> xParent
= xChild->getParentNode();
bKeepAsImage = xParent->getNodeName() == "interface"; if (!bKeepAsImage)
xChild->removeChild(xChildPropertyIconName);
}
} else
{ // private:graphicrepository/ would be turned by gio (?) into private:///graphicrepository/ // so use private:///graphicrepository/ here. At the moment we just want this to be transported // as-is to postprocess_widget. Though it might be nice to register a protocol handler with gio // to avoid us doing the load in that second pass. auto xUri = CreateProperty(xDoc, u"file"_ustr, "private:///graphicrepository/" + sIconName);
xChild->insertBefore(xUri, xChildPropertyIconName); // calc, insert, header and footer, custom header menubutton icon auto xCanShrink = CreateProperty(xDoc, u"can-shrink"_ustr, u"False"_ustr);
xChild->insertBefore(xCanShrink, xChildPropertyIconName);
xChild->removeChild(xChildPropertyIconName);
}
} if (!bKeepAsImage)
xClass->setNodeValue("GtkPicture");
} elseif (sClass == "GtkPopover" && !bHasVisible)
{ auto xVisible = CreateProperty(xDoc, u"visible"_ustr, u"False"_ustr);
insertAsFirstChild(xChild, xVisible);
} elseif (sClass == "AtkObject")
{
css::uno::Reference<css::xml::dom::XNode> xParentObject = xNode->getParentNode();
css::uno::Reference<css::xml::dom::XElement> xAccessibility
= xDoc->createElement("accessibility");
xParentObject->insertBefore(xAccessibility, xNode);
// move the converted old AtkObject:: properties into a new accessibility parent
css::uno::Reference<css::xml::dom::XNode> xRole; for (css::uno::Reference<css::xml::dom::XNode> xCurrent = xChild->getFirstChild();
xCurrent.is(); xCurrent = xChild->getFirstChild())
{ if (css::uno::Reference<css::xml::dom::XNamedNodeMap> xCurrentMap
= xCurrent->getAttributes())
{
css::uno::Reference<css::xml::dom::XNode> xName
= xCurrentMap->getNamedItem("name");
OUString sName(xName->getNodeValue()); if (sName == "accessible-role")
{
xRole = xCurrent;
}
}
if (xRole)
{ auto xRoleText = xRole->getFirstChild(); if (xRoleText.is())
{
OUString sText = xRoleText->getNodeValue(); // don't really know what the right solution here should be, but "static" isn't // a role that exists in gtk4, nothing seems to match exactly, but maybe "alert" // is a useful place to land for now if (sText == "static")
xRoleText->setNodeValue("alert");
} // move out of accessibility section to property section
xParentObject->insertBefore(xRole->getParentNode()->removeChild(xRole),
xAccessibility);
}
}
// only create the child box for GtkButton/GtkToggleButton if (bChildAlwaysShowImage)
{ auto xImageCandidateNode = xChild->getLastChild(); if (xImageCandidateNode && xImageCandidateNode->getNodeName() != "child")
xImageCandidateNode.clear(); if (xImageCandidateNode)
xChild->removeChild(xImageCandidateNode);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.