/* -*- 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/.
*/
#pragma once
#include <com/sun/star/uno/Exception.hpp>
#include <comphelper/diagnose_ex.hxx>
#include <sal/log.hxx>
#include <vcl/builderbase.hxx>
#include <xmlreader/span.hxx>
#include <xmlreader/xmlreader.hxx>
/* Template class for a Builder to create a hierarchy of widgets from a .ui file
* for dialogs , sidebar , etc .
*
* This class parses the . ui file and calls overridable methods
* so subclasses can create the widgets of a specific toolkit .
*
* The VclBuilder subclass is the implementation using LibreOffice ' s own VCL toolkit
* and the QtBuilder subclass uses native Qt widgets .
*/
template <typename Widget, typename WidgetPtr, typename MenuClass, typename MenuPtr>
class WidgetBuilder : public BuilderBase
{
protected :
struct MenuAndId
{
OUString m_sID;
MenuPtr m_pMenu;
MenuAndId(OUString sId, MenuClass* pMenu)
: m_sID(std::move(sId))
, m_pMenu(pMenu)
{
}
};
WidgetBuilder(std::u16string_view sUIDir, const OUString& rUIFile, bool bLegacy)
: BuilderBase(sUIDir, rUIFile, bLegacy)
{
}
virtual ~WidgetBuilder() = default ;
std::vector<MenuAndId> m_aMenus;
public :
//sID may not exist
MenuClass* get_menu(std::u16string_view sID)
{
for (auto const & menu : m_aMenus)
{
if (menu.m_sID == sID)
return menu.m_pMenu;
}
return nullptr;
}
protected :
void processUIFile(Widget* pParent)
{
try
{
xmlreader::XmlReader reader(getUIFileUrl());
handleChild(pParent, nullptr, reader);
}
catch (const css::uno::Exception& rExcept)
{
TOOLS_WARN_EXCEPTION("vcl.builder" , "Unable to read .ui file " << getUIFileUrl());
reportException(rExcept);
assert(false && "missing ui file or missing gb_CppunitTest_use_uiconfigs dependency" );
throw ;
}
// Set Mnemonic widgets when everything has been imported
for (const MnemonicWidgetMap& rMnemonic : getMnemonicWidgetMaps())
{
setMnemonicWidget(rMnemonic.m_sID, rMnemonic.m_sValue);
}
// Set radiobutton groups when everything has been imported
for (const RadioButtonGroupMap& rGroup : getRadioButtonGroupMaps())
setRadioButtonGroup(rGroup.m_sID, rGroup.m_sValue);
}
// either pParent or pAtkProps must be set, pParent for a child of a widget, pAtkProps for
// collecting the atk info for a GtkMenuItem or tab child
void handleChild(Widget* pParent, stringmap* pAtkProps, xmlreader::XmlReader& reader,
bool bToolbarItem = false )
{
xmlreader::Span name;
int nsId;
OString sType, sInternalChild;
while (reader.nextAttribute(&nsId, &name))
{
if (name == "type" )
{
name = reader.getAttributeValue(false );
sType = OString(name.begin, name.length);
}
else if (name == "internal-child" )
{
name = reader.getAttributeValue(false );
sInternalChild = OString(name.begin, name.length);
}
}
if (sType == "tab" )
{
handleTabChild(pParent, reader);
return ;
}
WidgetPtr pCurrentChild = nullptr;
int nLevel = 1 ;
while (true )
{
xmlreader::XmlReader::Result res
= reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
if (res == xmlreader::XmlReader::Result::Begin)
{
if (name == "object" || name == "placeholder" )
{
pCurrentChild = handleObject(pParent, pAtkProps, reader, sType, sInternalChild,
bToolbarItem);
bool bObjectInserted = pCurrentChild && pParent != pCurrentChild;
if (bObjectInserted)
tweakInsertedChild(pParent, pCurrentChild, sType, sInternalChild);
}
else if (name == "packing" )
{
const stringmap aPackingProperties = collectPackingProperties(reader);
applyPackingProperties(pCurrentChild, pParent, aPackingProperties);
}
else if (name == "interface" )
{
while (reader.nextAttribute(&nsId, &name))
{
if (name == "domain" )
handleInterfaceDomain(reader);
}
++nLevel;
}
else
++nLevel;
}
if (res == xmlreader::XmlReader::Result::End)
--nLevel;
if (!nLevel)
break ;
if (res == xmlreader::XmlReader::Result::Done)
break ;
}
}
WidgetPtr handleObject(Widget* pParent, stringmap* pAtkProps, xmlreader::XmlReader& reader,
std::string_view sType, std::string_view sInternalChild,
bool bToolbarItem)
{
OUString sClass;
OUString sID;
OUString sCustomProperty;
extractClassAndIdAndCustomProperty(reader, sClass, sID, sCustomProperty);
if (sClass == "GtkListStore" || sClass == "GtkTreeStore" )
{
handleListStore(reader, sID, sClass);
return nullptr;
}
else if (sClass == "GtkMenu" )
{
handleMenu(reader, sID);
return nullptr;
}
else if (sClass == "GtkSizeGroup" )
{
handleSizeGroup(reader);
return nullptr;
}
else if (sClass == "AtkObject" )
{
assert((pParent || pAtkProps) && "must have one set" );
assert(!(pParent && pAtkProps) && "must not have both" );
auto aAtkProperties = handleAtkObject(reader);
if (pParent)
applyAtkProperties(pParent, aAtkProperties, bToolbarItem);
if (pAtkProps)
*pAtkProps = std::move(aAtkProperties);
return nullptr;
}
int nLevel = 1 ;
stringmap aProperties, aPangoAttributes;
stringmap aAtkAttributes;
std::vector<ComboBoxTextItem> aItems;
if (!sCustomProperty.isEmpty())
aProperties[u"customproperty" _ustr] = sCustomProperty;
// Internal-children default in glade to not having their visible bits set
// even though they are visible (generally anyway)
if (!sInternalChild.empty())
aProperties[u"visible" _ustr] = "True" ;
WidgetPtr pCurrentChild = nullptr;
while (true )
{
xmlreader::Span name;
int nsId;
xmlreader::XmlReader::Result res
= reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
if (res == xmlreader::XmlReader::Result::Done)
break ;
if (res == xmlreader::XmlReader::Result::Begin)
{
if (name == "child" )
{
if (!pCurrentChild)
{
pCurrentChild = insertObject(pParent, sClass, sType, sID, aProperties,
aPangoAttributes, aAtkAttributes);
}
handleChild(pCurrentChild, nullptr, reader, isToolbarItemClass(sClass));
}
else if (name == "items" )
aItems = handleItems(reader);
else if (name == "style" )
{
int nPriority = 0 ;
std::vector<vcl::EnumContext::Context> aContext
= handleStyle(reader, nPriority);
if (nPriority != 0 )
setPriority(pCurrentChild, nPriority);
if (!aContext.empty())
setContext(pCurrentChild, std::move(aContext));
}
else
{
++nLevel;
if (name == "property" )
collectProperty(reader, aProperties);
else if (name == "attribute" )
collectPangoAttribute(reader, aPangoAttributes);
else if (name == "relation" )
collectAtkRelationAttribute(reader, aAtkAttributes);
else if (name == "role" )
collectAtkRoleAttribute(reader, aAtkAttributes);
else if (name == "action-widget" )
handleActionWidget(reader);
}
}
if (res == xmlreader::XmlReader::Result::End)
{
--nLevel;
}
if (!nLevel)
break ;
}
if (sClass == "GtkAdjustment" )
{
addAdjustment(sID, aProperties);
return nullptr;
}
else if (sClass == "GtkTextBuffer" )
{
addTextBuffer(sID, aProperties);
return nullptr;
}
if (!pCurrentChild)
{
pCurrentChild = insertObject(pParent, sClass, sType, sID, aProperties, aPangoAttributes,
aAtkAttributes);
}
if (!aItems.empty())
insertComboBoxOrListBoxItems(pCurrentChild, aProperties, aItems);
return pCurrentChild;
}
void handleTabChild(Widget* pParent, xmlreader::XmlReader& reader)
{
std::vector<OUString> sIDs;
int nLevel = 1 ;
stringmap aProperties;
stringmap aAtkProperties;
std::vector<vcl::EnumContext::Context> context;
while (true )
{
xmlreader::Span name;
int nsId;
xmlreader::XmlReader::Result res
= reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
if (res == xmlreader::XmlReader::Result::Begin)
{
++nLevel;
if (name == "object" )
{
while (reader.nextAttribute(&nsId, &name))
{
if (name == "id" )
{
name = reader.getAttributeValue(false );
OUString sID(name.begin, name.length, RTL_TEXTENCODING_UTF8);
sal_Int32 nDelim = sID.indexOf(':' );
if (nDelim != -1 )
{
aProperties[u"customproperty" _ustr] = sID.copy(nDelim + 1 );
sID = sID.copy(0 , nDelim);
}
sIDs.push_back(sID);
}
}
}
else if (name == "style" )
{
int nPriority = 0 ;
context = handleStyle(reader, nPriority);
--nLevel;
}
else if (name == "property" )
collectProperty(reader, aProperties);
else if (name == "child" && isHorizontalTabControl(pParent))
{
// just to collect the atk properties (if any) for the label
handleChild(nullptr, &aAtkProperties, reader);
--nLevel;
}
}
if (res == xmlreader::XmlReader::Result::End)
--nLevel;
if (!nLevel)
break ;
if (res == xmlreader::XmlReader::Result::Done)
break ;
}
if (!pParent)
return ;
applyTabChildProperties(pParent, sIDs, context, aProperties, aAtkProperties);
}
void handleMenu(xmlreader::XmlReader& reader, const OUString& rID)
{
MenuPtr pCurrentMenu = createMenu(rID);
int nLevel = 1 ;
stringmap aProperties;
while (true )
{
xmlreader::Span name;
int nsId;
xmlreader::XmlReader::Result res
= reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
if (res == xmlreader::XmlReader::Result::Done)
break ;
if (res == xmlreader::XmlReader::Result::Begin)
{
if (name == "child" )
{
handleMenuChild(pCurrentMenu, reader);
}
else
{
++nLevel;
if (name == "property" )
collectProperty(reader, aProperties);
}
}
if (res == xmlreader::XmlReader::Result::End)
{
--nLevel;
}
if (!nLevel)
break ;
}
m_aMenus.emplace_back(rID, pCurrentMenu);
}
void handleMenuChild(MenuClass* pParent, xmlreader::XmlReader& reader)
{
xmlreader::Span name;
int nsId;
int nLevel = 1 ;
while (true )
{
xmlreader::XmlReader::Result res
= reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
if (res == xmlreader::XmlReader::Result::Begin)
{
if (name == "object" || name == "placeholder" )
{
handleMenuObject(pParent, reader);
}
else
++nLevel;
}
if (res == xmlreader::XmlReader::Result::End)
--nLevel;
if (!nLevel)
break ;
if (res == xmlreader::XmlReader::Result::Done)
break ;
}
}
void handleMenuObject(MenuClass* pParent, xmlreader::XmlReader& reader)
{
OUString sClass;
OUString sID;
OUString sCustomProperty;
MenuClass* pSubMenu = nullptr;
xmlreader::Span name;
int nsId;
while (reader.nextAttribute(&nsId, &name))
{
if (name == "class" )
{
name = reader.getAttributeValue(false );
sClass = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
}
else if (name == "id" )
{
name = reader.getAttributeValue(false );
sID = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
if (isLegacy())
{
sal_Int32 nDelim = sID.indexOf(':' );
if (nDelim != -1 )
{
sCustomProperty = sID.subView(nDelim + 1 );
sID = sID.copy(0 , nDelim);
}
}
}
}
int nLevel = 1 ;
stringmap aProperties;
stringmap aAtkProperties;
accelmap aAccelerators;
if (!sCustomProperty.isEmpty())
aProperties[u"customproperty" _ustr] = sCustomProperty;
while (true )
{
xmlreader::XmlReader::Result res
= reader.nextItem(xmlreader::XmlReader::Text::NONE, &name, &nsId);
if (res == xmlreader::XmlReader::Result::Done)
break ;
if (res == xmlreader::XmlReader::Result::Begin)
{
if (name == "child" )
{
size_t nChildMenuIdx = m_aMenus.size();
handleChild(nullptr, &aAtkProperties, reader);
bool bSubMenuInserted = m_aMenus.size() > nChildMenuIdx;
if (bSubMenuInserted)
pSubMenu = m_aMenus[nChildMenuIdx].m_pMenu;
}
else
{
++nLevel;
if (name == "property" )
collectProperty(reader, aProperties);
else if (name == "accelerator" )
collectAccelerator(reader, aAccelerators);
}
}
if (res == xmlreader::XmlReader::Result::End)
{
--nLevel;
}
if (!nLevel)
break ;
}
insertMenuObject(pParent, pSubMenu, sClass, sID, aProperties, aAtkProperties,
aAccelerators);
}
virtual void applyAtkProperties(Widget* pWidget, const stringmap& rProperties,
bool bToolbarItem)
= 0 ;
virtual void applyPackingProperties(Widget* pCurrentChild, Widget* pParent,
const stringmap& rPackingProperties)
= 0 ;
virtual void applyTabChildProperties(Widget* pParent, const std::vector<OUString>& rIDs,
std::vector<vcl::EnumContext::Context>& rContext,
stringmap& rProperties, stringmap& rAtkProperties)
= 0 ;
virtual void insertComboBoxOrListBoxItems(Widget* pWidget, stringmap& rMap,
const std::vector<ComboBoxTextItem>& rItems)
= 0 ;
virtual WidgetPtr insertObject(Widget* pParent, const OUString& rClass, std::string_view sType,
const OUString& rID, stringmap& rProps,
stringmap& rPangoAttributes, stringmap& rAtkProps)
= 0 ;
virtual void tweakInsertedChild(Widget* pParent, Widget* pCurrentChild, std::string_view sType,
std::string_view sInternalChild)
= 0 ;
virtual void setMnemonicWidget(const OUString& rLabelId, const OUString& rMnemonicWidgetId) = 0 ;
virtual void setRadioButtonGroup(const OUString& rRadioButtonId, const OUString& rRadioGroupId)
= 0 ;
virtual void setPriority(Widget* pWidget, int nPriority) = 0 ;
virtual void setContext(Widget* pWidget, std::vector<vcl::EnumContext::Context>&& aContext) = 0 ;
// Whether the given widget is a horizontal, i.e. non-vertical tab control
virtual bool isHorizontalTabControl(Widget* pWidget) = 0 ;
virtual MenuPtr createMenu(const OUString& rID) = 0 ;
virtual void insertMenuObject(MenuClass* pParent, MenuClass* pSubMenu, const OUString& rClass,
const OUString& rID, stringmap& rProps, stringmap& rAtkProps,
accelmap& rAccels)
= 0 ;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=95 H=96 G=95
¤ Dauer der Verarbeitung: 0.5 Sekunden
¤
*© Formatika GbR, Deutschland