ab0eb26f6a
and cid#1607222 Overflowed constant Change-Id: If145bae8d05470173d1af6e7f540f8f40d7ab8b5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174208 Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com> Tested-by: Jenkins
4268 lines
145 KiB
C++
4268 lines
145 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <config_feature_desktop.h>
|
|
#include <config_options.h>
|
|
#include <config_vclplug.h>
|
|
|
|
#include <memory>
|
|
#include <string_view>
|
|
#include <unordered_map>
|
|
#include <com/sun/star/accessibility/AccessibleRole.hpp>
|
|
|
|
#include <comphelper/lok.hxx>
|
|
#include <i18nutil/unicode.hxx>
|
|
#include <jsdialog/enabled.hxx>
|
|
#include <o3tl/string_view.hxx>
|
|
#include <officecfg/Office/Common.hxx>
|
|
#include <osl/module.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <unotools/localedatawrapper.hxx>
|
|
#include <unotools/resmgr.hxx>
|
|
#include <utility>
|
|
#include <vcl/builder.hxx>
|
|
#include <vcl/dialoghelper.hxx>
|
|
#include <vcl/menu.hxx>
|
|
#include <vcl/toolkit/button.hxx>
|
|
#include <vcl/toolkit/dialog.hxx>
|
|
#include <vcl/toolkit/edit.hxx>
|
|
#include <vcl/toolkit/field.hxx>
|
|
#include <vcl/fieldvalues.hxx>
|
|
#include <vcl/toolkit/fmtfield.hxx>
|
|
#include <vcl/toolkit/fixed.hxx>
|
|
#include <vcl/toolkit/fixedhyper.hxx>
|
|
#include <vcl/headbar.hxx>
|
|
#include <vcl/notebookbar/NotebookBarAddonsMerger.hxx>
|
|
#include <vcl/toolkit/ivctrl.hxx>
|
|
#include <vcl/layout.hxx>
|
|
#include <vcl/toolkit/lstbox.hxx>
|
|
#include <vcl/toolkit/menubtn.hxx>
|
|
#include <vcl/mnemonic.hxx>
|
|
#include <vcl/toolkit/prgsbar.hxx>
|
|
#include <vcl/toolkit/scrbar.hxx>
|
|
#include <vcl/split.hxx>
|
|
#include <vcl/svapp.hxx>
|
|
#include <vcl/toolkit/svtabbx.hxx>
|
|
#include <vcl/tabctrl.hxx>
|
|
#include <vcl/tabpage.hxx>
|
|
#include <vcl/toolkit/throbber.hxx>
|
|
#include <vcl/toolbox.hxx>
|
|
#include <vcl/toolkit/treelistentry.hxx>
|
|
#include <vcl/toolkit/vclmedit.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <slider.hxx>
|
|
#include <vcl/weld.hxx>
|
|
#include <vcl/weldutils.hxx>
|
|
#include <vcl/commandinfoprovider.hxx>
|
|
#include <iconview.hxx>
|
|
#include <svdata.hxx>
|
|
#include <bitmaps.hlst>
|
|
#include <managedmenubutton.hxx>
|
|
#include <messagedialog.hxx>
|
|
#include <ContextVBox.hxx>
|
|
#include <DropdownBox.hxx>
|
|
#include <OptionalBox.hxx>
|
|
#include <PriorityMergedHBox.hxx>
|
|
#include <PriorityHBox.hxx>
|
|
#include <window.h>
|
|
#include <xmlreader/xmlreader.hxx>
|
|
#include <desktop/crashreport.hxx>
|
|
#include <calendar.hxx>
|
|
#include <menutogglebutton.hxx>
|
|
#include <salinst.hxx>
|
|
#include <strings.hrc>
|
|
#include <treeglue.hxx>
|
|
#include <verticaltabctrl.hxx>
|
|
#include <wizdlg.hxx>
|
|
#include <tools/svlibrary.h>
|
|
#include <jsdialog/jsdialogbuilder.hxx>
|
|
|
|
#if defined(DISABLE_DYNLOADING) || defined(LINUX)
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
bool toBool(std::u16string_view rValue)
|
|
{
|
|
return (!rValue.empty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
|
|
}
|
|
|
|
namespace
|
|
{
|
|
OUString mapStockToImageResource(std::u16string_view sType)
|
|
{
|
|
if (sType == u"view-refresh")
|
|
return SV_RESID_BITMAP_REFRESH;
|
|
else if (sType == u"dialog-error")
|
|
return IMG_ERROR;
|
|
else if (sType == u"list-add")
|
|
return IMG_ADD;
|
|
else if (sType == u"list-remove")
|
|
return IMG_REMOVE;
|
|
else if (sType == u"edit-copy")
|
|
return IMG_COPY;
|
|
else if (sType == u"edit-paste")
|
|
return IMG_PASTE;
|
|
else if (sType == u"document-open")
|
|
return IMG_OPEN;
|
|
else if (sType == u"open-menu-symbolic")
|
|
return IMG_MENU;
|
|
else if (sType == u"window-close-symbolic")
|
|
return SV_RESID_BITMAP_CLOSEDOC;
|
|
else if (sType == u"x-office-calendar")
|
|
return IMG_CALENDAR;
|
|
else if (sType == u"accessories-character-map")
|
|
return IMG_CHARACTER_MAP;
|
|
return OUString();
|
|
}
|
|
|
|
}
|
|
|
|
SymbolType VclBuilder::mapStockToSymbol(std::u16string_view sType)
|
|
{
|
|
SymbolType eRet = SymbolType::DONTKNOW;
|
|
if (sType == u"media-skip-forward")
|
|
eRet = SymbolType::NEXT;
|
|
else if (sType == u"media-skip-backward")
|
|
eRet = SymbolType::PREV;
|
|
else if (sType == u"media-playback-start")
|
|
eRet = SymbolType::PLAY;
|
|
else if (sType == u"media-playback-stop")
|
|
eRet = SymbolType::STOP;
|
|
else if (sType == u"go-first")
|
|
eRet = SymbolType::FIRST;
|
|
else if (sType == u"go-last")
|
|
eRet = SymbolType::LAST;
|
|
else if (sType == u"go-previous")
|
|
eRet = SymbolType::ARROW_LEFT;
|
|
else if (sType == u"go-next")
|
|
eRet = SymbolType::ARROW_RIGHT;
|
|
else if (sType == u"go-up")
|
|
eRet = SymbolType::ARROW_UP;
|
|
else if (sType == u"go-down")
|
|
eRet = SymbolType::ARROW_DOWN;
|
|
else if (sType == u"missing-image")
|
|
eRet = SymbolType::IMAGE;
|
|
else if (sType == u"help-browser" || sType == u"help-browser-symbolic")
|
|
eRet = SymbolType::HELP;
|
|
else if (sType == u"window-close")
|
|
eRet = SymbolType::CLOSE;
|
|
else if (sType == u"document-new")
|
|
eRet = SymbolType::PLUS;
|
|
else if (sType == u"pan-down-symbolic")
|
|
eRet = SymbolType::SPIN_DOWN;
|
|
else if (sType == u"pan-up-symbolic")
|
|
eRet = SymbolType::SPIN_UP;
|
|
else if (!mapStockToImageResource(sType).isEmpty())
|
|
eRet = SymbolType::IMAGE;
|
|
return eRet;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame);
|
|
|
|
#if defined SAL_LOG_WARN
|
|
bool isButtonType(WindowType nType)
|
|
{
|
|
return nType == WindowType::PUSHBUTTON ||
|
|
nType == WindowType::OKBUTTON ||
|
|
nType == WindowType::CANCELBUTTON ||
|
|
nType == WindowType::HELPBUTTON ||
|
|
nType == WindowType::IMAGEBUTTON ||
|
|
nType == WindowType::MENUBUTTON ||
|
|
nType == WindowType::MOREBUTTON ||
|
|
nType == WindowType::SPINBUTTON;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
std::unique_ptr<weld::Builder> Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile, bool bMobile, sal_uInt64 nLOKWindowId)
|
|
{
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
if (jsdialog::isBuilderEnabledForSidebar(rUIFile))
|
|
return JSInstanceBuilder::CreateSidebarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId);
|
|
else if (jsdialog::isBuilderEnabledForPopup(rUIFile))
|
|
return JSInstanceBuilder::CreatePopupBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
|
|
else if (jsdialog::isBuilderEnabled(rUIFile, bMobile))
|
|
return JSInstanceBuilder::CreateDialogBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
|
|
}
|
|
|
|
return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
|
|
}
|
|
|
|
std::unique_ptr<weld::Builder> Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile, bool bAllowCycleFocusOut, sal_uInt64 nLOKWindowId)
|
|
{
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
// Notebookbar sub controls
|
|
if (jsdialog::isInterimBuilderEnabledForNotebookbar(rUIFile))
|
|
return JSInstanceBuilder::CreateNotebookbarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, css::uno::Reference<css::frame::XFrame>(), nLOKWindowId);
|
|
else if (rUIFile == u"modules/scalc/ui/inputbar.ui")
|
|
return JSInstanceBuilder::CreateFormulabarBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, nLOKWindowId);
|
|
}
|
|
|
|
return ImplGetSVData()->mpDefInst->CreateInterimBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, bAllowCycleFocusOut, nLOKWindowId);
|
|
}
|
|
|
|
weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType,
|
|
VclButtonsType eButtonType, const OUString& rPrimaryMessage,
|
|
const ILibreOfficeKitNotifier* pNotifier)
|
|
{
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
return JSInstanceBuilder::CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage, pNotifier);
|
|
else
|
|
return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
|
|
}
|
|
|
|
weld::Window* Application::GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow)
|
|
{
|
|
return ImplGetSVData()->mpDefInst->GetFrameWeld(rWindow);
|
|
}
|
|
|
|
namespace weld
|
|
{
|
|
OUString MetricSpinButton::MetricToString(FieldUnit rUnit)
|
|
{
|
|
const FieldUnitStringList& rList = ImplGetFieldUnits();
|
|
// return unit's default string (ie, the first one )
|
|
auto it = std::find_if(
|
|
rList.begin(), rList.end(),
|
|
[&rUnit](const std::pair<OUString, FieldUnit>& rItem) { return rItem.second == rUnit; });
|
|
if (it != rList.end())
|
|
return it->first;
|
|
|
|
return OUString();
|
|
}
|
|
|
|
IMPL_LINK_NOARG(MetricSpinButton, spin_button_value_changed, SpinButton&, void)
|
|
{
|
|
signal_value_changed();
|
|
}
|
|
|
|
IMPL_LINK(MetricSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
|
|
{
|
|
OUString sNewText(format_number(rSpinButton.get_value()));
|
|
if (sNewText != rSpinButton.get_text())
|
|
rSpinButton.set_text(sNewText);
|
|
}
|
|
|
|
void MetricSpinButton::update_width_chars()
|
|
{
|
|
sal_Int64 min, max;
|
|
m_xSpinButton->get_range(min, max);
|
|
auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
|
|
m_xSpinButton->get_pixel_size(format_number(max)).Width());
|
|
int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
|
|
m_xSpinButton->set_width_chars(chars);
|
|
}
|
|
|
|
unsigned int SpinButton::Power10(unsigned int n)
|
|
{
|
|
unsigned int nValue = 1;
|
|
for (unsigned int i = 0; i < n; ++i)
|
|
nValue *= 10;
|
|
return nValue;
|
|
}
|
|
|
|
sal_Int64 SpinButton::denormalize(sal_Int64 nValue) const
|
|
{
|
|
const int nFactor = Power10(get_digits());
|
|
|
|
if ((nValue < (std::numeric_limits<sal_Int64>::min() + nFactor)) ||
|
|
(nValue > (std::numeric_limits<sal_Int64>::max() - nFactor)))
|
|
{
|
|
return nValue / nFactor;
|
|
}
|
|
|
|
const int nHalf = nFactor / 2;
|
|
|
|
if (nValue < 0)
|
|
return (nValue - nHalf) / nFactor;
|
|
return (nValue + nHalf) / nFactor;
|
|
}
|
|
|
|
OUString MetricSpinButton::format_number(sal_Int64 nValue) const
|
|
{
|
|
OUString aStr;
|
|
|
|
const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
|
|
|
|
unsigned int nDecimalDigits = m_xSpinButton->get_digits();
|
|
//pawn percent off to icu to decide whether percent is separated from its number for this locale
|
|
if (m_eSrcUnit == FieldUnit::PERCENT)
|
|
{
|
|
double fValue = nValue;
|
|
fValue /= SpinButton::Power10(nDecimalDigits);
|
|
aStr = unicode::formatPercent(fValue, rLocaleData.getLanguageTag());
|
|
}
|
|
else
|
|
{
|
|
aStr = rLocaleData.getNum(nValue, nDecimalDigits, true, true);
|
|
OUString aSuffix = MetricToString(m_eSrcUnit);
|
|
if (m_eSrcUnit != FieldUnit::NONE && m_eSrcUnit != FieldUnit::DEGREE && m_eSrcUnit != FieldUnit::INCH && m_eSrcUnit != FieldUnit::FOOT)
|
|
aStr += " ";
|
|
if (m_eSrcUnit == FieldUnit::INCH)
|
|
{
|
|
OUString sDoublePrime = u"\u2033"_ustr;
|
|
if (aSuffix != "\"" && aSuffix != sDoublePrime)
|
|
aStr += " ";
|
|
else
|
|
aSuffix = sDoublePrime;
|
|
}
|
|
else if (m_eSrcUnit == FieldUnit::FOOT)
|
|
{
|
|
OUString sPrime = u"\u2032"_ustr;
|
|
if (aSuffix != "'" && aSuffix != sPrime)
|
|
aStr += " ";
|
|
else
|
|
aSuffix = sPrime;
|
|
}
|
|
|
|
assert(m_eSrcUnit != FieldUnit::PERCENT);
|
|
aStr += aSuffix;
|
|
}
|
|
|
|
return aStr;
|
|
}
|
|
|
|
void MetricSpinButton::set_digits(unsigned int digits)
|
|
{
|
|
sal_Int64 step, page;
|
|
get_increments(step, page, m_eSrcUnit);
|
|
sal_Int64 value = get_value(m_eSrcUnit);
|
|
m_xSpinButton->set_digits(digits);
|
|
set_increments(step, page, m_eSrcUnit);
|
|
set_value(value, m_eSrcUnit);
|
|
update_width_chars();
|
|
}
|
|
|
|
void MetricSpinButton::set_unit(FieldUnit eUnit)
|
|
{
|
|
if (eUnit != m_eSrcUnit)
|
|
{
|
|
sal_Int64 step, page;
|
|
get_increments(step, page, m_eSrcUnit);
|
|
sal_Int64 value = get_value(m_eSrcUnit);
|
|
m_eSrcUnit = eUnit;
|
|
set_increments(step, page, m_eSrcUnit);
|
|
set_value(value, m_eSrcUnit);
|
|
spin_button_output(*m_xSpinButton);
|
|
update_width_chars();
|
|
}
|
|
}
|
|
|
|
sal_Int64 MetricSpinButton::ConvertValue(sal_Int64 nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const
|
|
{
|
|
return vcl::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit);
|
|
}
|
|
|
|
IMPL_LINK(MetricSpinButton, spin_button_input, int*, result, bool)
|
|
{
|
|
const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
|
|
double fResult(0.0);
|
|
bool bRet = vcl::TextToValue(get_text(), fResult, 0, m_xSpinButton->get_digits(), rLocaleData, m_eSrcUnit);
|
|
if (bRet)
|
|
{
|
|
if (fResult > SAL_MAX_INT32)
|
|
fResult = SAL_MAX_INT32;
|
|
else if (fResult < SAL_MIN_INT32)
|
|
fResult = SAL_MIN_INT32;
|
|
*result = fResult;
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
EntryTreeView::EntryTreeView(std::unique_ptr<Entry> xEntry, std::unique_ptr<TreeView> xTreeView)
|
|
: m_xEntry(std::move(xEntry))
|
|
, m_xTreeView(std::move(xTreeView))
|
|
{
|
|
m_xTreeView->connect_changed(LINK(this, EntryTreeView, ClickHdl));
|
|
m_xEntry->connect_changed(LINK(this, EntryTreeView, ModifyHdl));
|
|
}
|
|
|
|
IMPL_LINK(EntryTreeView, ClickHdl, weld::TreeView&, rView, void)
|
|
{
|
|
m_xEntry->set_text(rView.get_selected_text());
|
|
m_aChangeHdl.Call(*this);
|
|
}
|
|
|
|
IMPL_LINK_NOARG(EntryTreeView, ModifyHdl, weld::Entry&, void)
|
|
{
|
|
m_aChangeHdl.Call(*this);
|
|
}
|
|
|
|
void EntryTreeView::set_height_request_by_rows(int nRows)
|
|
{
|
|
int nHeight = nRows == -1 ? -1 : m_xTreeView->get_height_rows(nRows);
|
|
m_xTreeView->set_size_request(m_xTreeView->get_size_request().Width(), nHeight);
|
|
}
|
|
|
|
size_t GetAbsPos(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
|
|
{
|
|
size_t nAbsPos = 0;
|
|
|
|
std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
|
|
if (!rTreeView.get_iter_first(*xEntry))
|
|
xEntry.reset();
|
|
|
|
while (xEntry && rTreeView.iter_compare(*xEntry, rIter) != 0)
|
|
{
|
|
if (!rTreeView.iter_next(*xEntry))
|
|
xEntry.reset();
|
|
nAbsPos++;
|
|
}
|
|
|
|
return nAbsPos;
|
|
}
|
|
|
|
bool IsEntryVisible(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
|
|
{
|
|
// short circuit for the common case
|
|
if (rTreeView.get_iter_depth(rIter) == 0)
|
|
return true;
|
|
|
|
std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
|
|
bool bRetVal = false;
|
|
do
|
|
{
|
|
if (rTreeView.get_iter_depth(*xEntry) == 0)
|
|
{
|
|
bRetVal = true;
|
|
break;
|
|
}
|
|
} while (rTreeView.iter_parent(*xEntry) && rTreeView.get_row_expanded(*xEntry));
|
|
return bRetVal;
|
|
}
|
|
}
|
|
|
|
// static
|
|
void BuilderBase::reportException(const css::uno::Exception& rExcept)
|
|
{
|
|
CrashReporter::addKeyValue(u"VclBuilderException"_ustr,
|
|
"Unable to read .ui file: " + rExcept.Message, CrashReporter::Write);
|
|
}
|
|
|
|
BuilderBase::BuilderBase(std::u16string_view sUIDir, const OUString& rUIFile, bool bLegacy)
|
|
: m_pParserState(new ParserState)
|
|
, m_sUIFileUrl(sUIDir + rUIFile)
|
|
, m_sHelpRoot(rUIFile)
|
|
, m_bLegacy(bLegacy)
|
|
{
|
|
const sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.');
|
|
if (nIdx != -1)
|
|
m_sHelpRoot = m_sHelpRoot.copy(0, nIdx);
|
|
m_sHelpRoot += "/";
|
|
}
|
|
|
|
const std::locale& BuilderBase::getResLocale() const
|
|
{
|
|
assert(m_pParserState && "parser state no more valid");
|
|
return m_pParserState->m_aResLocale;
|
|
}
|
|
|
|
const std::vector<BuilderBase::SizeGroup>& BuilderBase::getSizeGroups() const
|
|
{
|
|
assert(m_pParserState && "parser state no more valid");
|
|
return m_pParserState->m_aSizeGroups;
|
|
}
|
|
|
|
OUString BuilderBase::finalizeValue(const OString& rContext, const OString& rValue,
|
|
const bool bTranslate) const
|
|
{
|
|
OUString sFinalValue;
|
|
if (bTranslate)
|
|
{
|
|
sFinalValue
|
|
= Translate::get(TranslateId{ rContext.getStr(), rValue.getStr() }, getResLocale());
|
|
}
|
|
else
|
|
sFinalValue = OUString::fromUtf8(rValue);
|
|
|
|
if (ResHookProc pStringReplace = Translate::GetReadStringHook())
|
|
sFinalValue = (*pStringReplace)(sFinalValue);
|
|
|
|
return sFinalValue;
|
|
}
|
|
|
|
void BuilderBase::resetParserState() { m_pParserState.reset(); }
|
|
|
|
VclBuilder::VclBuilder(vcl::Window* pParent, std::u16string_view sUIDir, const OUString& sUIFile,
|
|
OUString sID, css::uno::Reference<css::frame::XFrame> xFrame,
|
|
bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem)
|
|
: WidgetBuilder(sUIDir, sUIFile, bLegacy)
|
|
, m_pNotebookBarAddonsItem(pNotebookBarAddonsItem
|
|
? new NotebookBarAddonsItem(*pNotebookBarAddonsItem)
|
|
: new NotebookBarAddonsItem{})
|
|
, m_sID(std::move(sID))
|
|
, m_pParent(pParent)
|
|
, m_bToplevelParentFound(false)
|
|
, m_pVclParserState(new VclParserState)
|
|
, m_xFrame(std::move(xFrame))
|
|
{
|
|
m_bToplevelHasDeferredInit = pParent &&
|
|
((pParent->IsSystemWindow() && static_cast<SystemWindow*>(pParent)->isDeferredInit()) ||
|
|
(pParent->IsDockingWindow() && static_cast<DockingWindow*>(pParent)->isDeferredInit()));
|
|
m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit;
|
|
|
|
processUIFile(pParent);
|
|
|
|
//Set Mnemonic widgets when everything has been imported
|
|
for (auto const& mnemonicWidget : m_pVclParserState->m_aMnemonicWidgetMaps)
|
|
{
|
|
FixedText *pOne = get<FixedText>(mnemonicWidget.m_sID);
|
|
vcl::Window *pOther = get(mnemonicWidget.m_sValue);
|
|
SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << mnemonicWidget.m_sID
|
|
<< " or target " << mnemonicWidget.m_sValue << " member of Mnemonic Widget Mapping");
|
|
if (pOne && pOther)
|
|
pOne->set_mnemonic_widget(pOther);
|
|
}
|
|
|
|
//Set a11y relations and role when everything has been imported
|
|
for (auto const& elemAtk : m_pVclParserState->m_aAtkInfo)
|
|
{
|
|
vcl::Window *pSource = elemAtk.first;
|
|
const stringmap &rMap = elemAtk.second;
|
|
|
|
for (auto const& [ rType, rParam ] : rMap)
|
|
{
|
|
if (rType == "role")
|
|
{
|
|
sal_Int16 role = BuilderUtils::getRoleFromName(rParam);
|
|
if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN)
|
|
pSource->SetAccessibleRole(role);
|
|
}
|
|
else
|
|
{
|
|
vcl::Window *pTarget = get(rParam);
|
|
SAL_WARN_IF(!pTarget, "vcl", "missing parameter of a11y relation: " << rParam);
|
|
if (!pTarget)
|
|
continue;
|
|
if (rType == "labelled-by")
|
|
pSource->SetAccessibleRelationLabeledBy(pTarget);
|
|
else if (rType == "label-for")
|
|
pSource->SetAccessibleRelationLabelFor(pTarget);
|
|
else
|
|
{
|
|
SAL_WARN("vcl.builder", "unhandled a11y relation :" << rType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Set radiobutton groups when everything has been imported
|
|
for (auto const& elem : m_pVclParserState->m_aGroupMaps)
|
|
{
|
|
RadioButton *pOne = get<RadioButton>(elem.m_sID);
|
|
RadioButton *pOther = get<RadioButton>(elem.m_sValue);
|
|
SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group");
|
|
if (pOne && pOther)
|
|
{
|
|
if (isLegacy())
|
|
pOne->group(*pOther);
|
|
else
|
|
{
|
|
pOther->group(*pOne);
|
|
std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
o3tl::sorted_vector<OUString> models;
|
|
#endif
|
|
//Set ComboBox models when everything has been imported
|
|
for (auto const& elem : m_pVclParserState->m_aModelMaps)
|
|
{
|
|
assert(models.insert(elem.m_sValue).second && "a liststore or treestore is used in duplicate widgets");
|
|
vcl::Window* pTarget = get(elem.m_sID);
|
|
ListBox *pListBoxTarget = dynamic_cast<ListBox*>(pTarget);
|
|
ComboBox *pComboBoxTarget = dynamic_cast<ComboBox*>(pTarget);
|
|
SvTabListBox *pTreeBoxTarget = dynamic_cast<SvTabListBox*>(pTarget);
|
|
// pStore may be empty
|
|
const ListStore *pStore = get_model_by_name(elem.m_sValue);
|
|
SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget && !dynamic_cast<IconView*>(pTarget), "vcl", "missing elements of combobox");
|
|
if (pListBoxTarget && pStore)
|
|
mungeModel(*pListBoxTarget, *pStore, elem.m_nActiveId);
|
|
else if (pComboBoxTarget && pStore)
|
|
mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId);
|
|
else if (pTreeBoxTarget && pStore)
|
|
mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId);
|
|
}
|
|
|
|
//Set TextView buffers when everything has been imported
|
|
for (auto const& elem : m_pVclParserState->m_aTextBufferMaps)
|
|
{
|
|
VclMultiLineEdit *pTarget = get<VclMultiLineEdit>(elem.m_sID);
|
|
const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue);
|
|
SAL_WARN_IF(!pTarget || !pBuffer, "vcl", "missing elements of textview/textbuffer");
|
|
if (pTarget && pBuffer)
|
|
mungeTextBuffer(*pTarget, *pBuffer);
|
|
}
|
|
|
|
//Set SpinButton adjustments when everything has been imported
|
|
for (auto const& elem : m_pVclParserState->m_aNumericFormatterAdjustmentMaps)
|
|
{
|
|
NumericFormatter *pTarget = dynamic_cast<NumericFormatter*>(get(elem.m_sID));
|
|
const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue);
|
|
SAL_WARN_IF(!pTarget, "vcl", "missing NumericFormatter element of spinbutton/adjustment");
|
|
SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
|
|
if (pTarget && pAdjustment)
|
|
mungeAdjustment(*pTarget, *pAdjustment);
|
|
}
|
|
|
|
for (auto const& elem : m_pVclParserState->m_aFormattedFormatterAdjustmentMaps)
|
|
{
|
|
FormattedField *pTarget = dynamic_cast<FormattedField*>(get(elem.m_sID));
|
|
const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue);
|
|
SAL_WARN_IF(!pTarget, "vcl", "missing FormattedField element of spinbutton/adjustment");
|
|
SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
|
|
if (pTarget && pAdjustment)
|
|
mungeAdjustment(*pTarget, *pAdjustment);
|
|
}
|
|
|
|
//Set ScrollBar adjustments when everything has been imported
|
|
for (auto const& elem : m_pVclParserState->m_aScrollAdjustmentMaps)
|
|
{
|
|
ScrollBar *pTarget = get<ScrollBar>(elem.m_sID);
|
|
const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue);
|
|
SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scrollbar/adjustment");
|
|
if (pTarget && pAdjustment)
|
|
mungeAdjustment(*pTarget, *pAdjustment);
|
|
}
|
|
|
|
//Set Scale(Slider) adjustments
|
|
for (auto const& elem : m_pVclParserState->m_aSliderAdjustmentMaps)
|
|
{
|
|
Slider* pTarget = dynamic_cast<Slider*>(get(elem.m_sID));
|
|
const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue);
|
|
SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scale(slider)/adjustment");
|
|
if (pTarget && pAdjustment)
|
|
{
|
|
mungeAdjustment(*pTarget, *pAdjustment);
|
|
}
|
|
}
|
|
|
|
//Set size-groups when all widgets have been imported
|
|
for (auto const& sizeGroup : getSizeGroups())
|
|
{
|
|
std::shared_ptr<VclSizeGroup> xGroup(std::make_shared<VclSizeGroup>());
|
|
|
|
for (auto const& [ rKey, rValue ] : sizeGroup.m_aProperties)
|
|
xGroup->set_property(rKey, rValue);
|
|
|
|
for (auto const& elem : sizeGroup.m_aWidgets)
|
|
{
|
|
vcl::Window* pWindow = get(elem);
|
|
pWindow->add_to_size_group(xGroup);
|
|
}
|
|
}
|
|
|
|
//Set button images when everything has been imported
|
|
std::set<OUString> aImagesToBeRemoved;
|
|
for (auto const& elem : m_pVclParserState->m_aButtonImageWidgetMaps)
|
|
{
|
|
PushButton *pTargetButton = nullptr;
|
|
RadioButton *pTargetRadio = nullptr;
|
|
Button *pTarget = nullptr;
|
|
|
|
if (!elem.m_bRadio)
|
|
{
|
|
pTargetButton = get<PushButton>(elem.m_sID);
|
|
pTarget = pTargetButton;
|
|
}
|
|
else
|
|
{
|
|
pTargetRadio = get<RadioButton>(elem.m_sID);
|
|
pTarget = pTargetRadio;
|
|
}
|
|
|
|
FixedImage *pImage = get<FixedImage>(elem.m_sValue);
|
|
SAL_WARN_IF(!pTarget || !pImage,
|
|
"vcl", "missing elements of button/image/stock");
|
|
if (!pTarget || !pImage)
|
|
continue;
|
|
aImagesToBeRemoved.insert(elem.m_sValue);
|
|
|
|
if (!elem.m_bRadio)
|
|
{
|
|
const Image& rImage = pImage->GetImage();
|
|
SymbolType eSymbol = mapStockToSymbol(rImage.GetStock());
|
|
if (eSymbol != SymbolType::IMAGE && eSymbol != SymbolType::DONTKNOW)
|
|
{
|
|
pTargetButton->SetSymbol(eSymbol);
|
|
//fdo#76457 keep symbol images small e.g. tools->customize->menu
|
|
//but images the right size. Really the PushButton::CalcMinimumSize
|
|
//and PushButton::ImplDrawPushButton are the better place to handle
|
|
//this, but its such a train-wreck
|
|
pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
|
|
}
|
|
else
|
|
{
|
|
pTargetButton->SetModeImage(rImage);
|
|
if (pImage->GetStyle() & WB_SMALLSTYLE)
|
|
{
|
|
Size aSz(rImage.GetSizePixel());
|
|
aSz.AdjustWidth(6);
|
|
aSz.AdjustHeight(6);
|
|
if (pTargetButton->get_width_request() == -1)
|
|
pTargetButton->set_width_request(aSz.Width());
|
|
if (pTargetButton->get_height_request() == -1)
|
|
pTargetButton->set_height_request(aSz.Height());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
pTargetRadio->SetModeRadioImage(pImage->GetImage());
|
|
|
|
auto aFind = m_pVclParserState->m_aImageSizeMap.find(elem.m_sValue);
|
|
if (aFind != m_pVclParserState->m_aImageSizeMap.end())
|
|
{
|
|
switch (aFind->second)
|
|
{
|
|
case 1:
|
|
pTarget->SetSmallSymbol();
|
|
break;
|
|
case 2:
|
|
assert(pImage->GetStyle() & WB_SMALLSTYLE);
|
|
pTarget->SetStyle(pTarget->GetStyle() | WB_SMALLSTYLE);
|
|
break;
|
|
case 3:
|
|
pTarget->SetStyle(pTarget->GetStyle() | WB_SMALLSTYLE);
|
|
// large toolbar, make bigger than normal (4)
|
|
pTarget->set_width_request(pTarget->GetOptimalSize().Width() * 1.5);
|
|
pTarget->set_height_request(pTarget->GetOptimalSize().Height() * 1.5);
|
|
break;
|
|
case 4:
|
|
break;
|
|
default:
|
|
SAL_WARN("vcl.builder", "unsupported image size " << aFind->second);
|
|
break;
|
|
}
|
|
m_pVclParserState->m_aImageSizeMap.erase(aFind);
|
|
}
|
|
}
|
|
|
|
//There may be duplicate use of an Image, so we used a set to collect and
|
|
//now we can remove them from the tree after their final munge
|
|
for (auto const& elem : aImagesToBeRemoved)
|
|
{
|
|
delete_by_name(elem);
|
|
}
|
|
|
|
//Set button menus when everything has been imported
|
|
for (auto const& elem : m_pVclParserState->m_aButtonMenuMaps)
|
|
{
|
|
MenuButton *pTarget = get<MenuButton>(elem.m_sID);
|
|
PopupMenu *pMenu = get_menu(elem.m_sValue);
|
|
SAL_WARN_IF(!pTarget || !pMenu,
|
|
"vcl", "missing elements of button/menu");
|
|
if (!pTarget || !pMenu)
|
|
continue;
|
|
pTarget->SetPopupMenu(pMenu);
|
|
}
|
|
|
|
//Remove ScrollWindow parent widgets whose children in vcl implement scrolling
|
|
//internally.
|
|
for (auto const& elem : m_pVclParserState->m_aRedundantParentWidgets)
|
|
{
|
|
delete_by_window(elem.first);
|
|
}
|
|
|
|
//fdo#67378 merge the label into the disclosure button
|
|
for (auto const& elem : m_pVclParserState->m_aExpanderWidgets)
|
|
{
|
|
vcl::Window *pChild = elem->get_child();
|
|
vcl::Window* pLabel = elem->GetWindow(GetWindowType::LastChild);
|
|
if (pLabel && pLabel != pChild && pLabel->GetType() == WindowType::FIXEDTEXT)
|
|
{
|
|
FixedText *pLabelWidget = static_cast<FixedText*>(pLabel);
|
|
elem->set_label(pLabelWidget->GetText());
|
|
if (pLabelWidget->IsControlFont())
|
|
elem->get_label_widget()->SetControlFont(pLabelWidget->GetControlFont());
|
|
delete_by_window(pLabel);
|
|
}
|
|
}
|
|
|
|
// create message dialog message area now
|
|
for (auto const& elem : m_pVclParserState->m_aMessageDialogs)
|
|
elem->create_message_area();
|
|
|
|
//drop maps, etc. that we don't need again
|
|
resetParserState();
|
|
|
|
SAL_WARN_IF(!m_sID.isEmpty() && (!m_bToplevelParentFound && !get_by_name(m_sID)), "vcl.builder",
|
|
"Requested top level widget \"" << m_sID << "\" not found in " << sUIFile);
|
|
|
|
#if defined SAL_LOG_WARN
|
|
if (m_bToplevelParentFound && m_pParent->IsDialog())
|
|
{
|
|
int nButtons = 0;
|
|
bool bHasDefButton = false;
|
|
for (auto const& child : m_aChildren)
|
|
{
|
|
if (isButtonType(child.m_pWindow->GetType()))
|
|
{
|
|
++nButtons;
|
|
if (child.m_pWindow->GetStyle() & WB_DEFBUTTON)
|
|
{
|
|
bHasDefButton = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
SAL_WARN_IF(nButtons && !bHasDefButton, "vcl.builder", "No default button defined in " << sUIFile);
|
|
}
|
|
#endif
|
|
|
|
const bool bHideHelp = comphelper::LibreOfficeKit::isActive() &&
|
|
officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
|
|
if (bHideHelp)
|
|
{
|
|
if (vcl::Window *pHelpButton = get(u"help"))
|
|
pHelpButton->Hide();
|
|
}
|
|
}
|
|
|
|
VclBuilder::~VclBuilder()
|
|
{
|
|
disposeBuilder();
|
|
}
|
|
|
|
void VclBuilder::disposeBuilder()
|
|
{
|
|
for (std::vector<WinAndId>::reverse_iterator aI = m_aChildren.rbegin(),
|
|
aEnd = m_aChildren.rend(); aI != aEnd; ++aI)
|
|
{
|
|
aI->m_pWindow.disposeAndClear();
|
|
}
|
|
m_aChildren.clear();
|
|
|
|
for (std::vector<MenuAndId>::reverse_iterator aI = m_aMenus.rbegin(),
|
|
aEnd = m_aMenus.rend(); aI != aEnd; ++aI)
|
|
{
|
|
aI->m_pMenu.disposeAndClear();
|
|
}
|
|
m_aMenus.clear();
|
|
m_pParent.clear();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool extractHasFrame(VclBuilder::stringmap& rMap)
|
|
{
|
|
bool bHasFrame = true;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"has-frame"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bHasFrame = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bHasFrame;
|
|
}
|
|
|
|
bool extractDrawValue(VclBuilder::stringmap& rMap)
|
|
{
|
|
bool bDrawValue = true;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"draw-value"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bDrawValue = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bDrawValue;
|
|
}
|
|
|
|
OUString extractPopupMenu(VclBuilder::stringmap& rMap)
|
|
{
|
|
OUString sRet;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"popup"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sRet = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sRet;
|
|
}
|
|
|
|
OUString extractWidgetName(VclBuilder::stringmap& rMap)
|
|
{
|
|
OUString sRet;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"name"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sRet = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sRet;
|
|
}
|
|
|
|
OUString extractValuePos(VclBuilder::stringmap& rMap)
|
|
{
|
|
OUString sRet(u"top"_ustr);
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"value-pos"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sRet = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sRet;
|
|
}
|
|
|
|
OUString extractTypeHint(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sRet(u"normal"_ustr);
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"type-hint"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sRet = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sRet;
|
|
}
|
|
|
|
bool extractResizable(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bResizable = true;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"resizable"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bResizable = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bResizable;
|
|
}
|
|
|
|
#if HAVE_FEATURE_DESKTOP
|
|
bool extractModal(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bModal = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"modal"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bModal = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bModal;
|
|
}
|
|
#endif
|
|
|
|
bool extractDecorated(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bDecorated = true;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"decorated"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bDecorated = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bDecorated;
|
|
}
|
|
|
|
bool extractCloseable(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bCloseable = true;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"deletable"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bCloseable = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bCloseable;
|
|
}
|
|
|
|
bool extractEntry(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bHasEntry = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"has-entry"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bHasEntry = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bHasEntry;
|
|
}
|
|
|
|
bool extractVerticalTabPos(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bVertical = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"tab-pos"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bVertical = aFind->second.equalsIgnoreAsciiCase("left") ||
|
|
aFind->second.equalsIgnoreAsciiCase("right");
|
|
rMap.erase(aFind);
|
|
}
|
|
return bVertical;
|
|
}
|
|
|
|
bool extractVerticalTabsWithIcons(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bWithIcons = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"group-name"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bWithIcons = aFind->second.equalsIgnoreAsciiCase("icons");
|
|
rMap.erase(aFind);
|
|
}
|
|
return bWithIcons;
|
|
}
|
|
|
|
bool extractInconsistent(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bInconsistent = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"inconsistent"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bInconsistent = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bInconsistent;
|
|
}
|
|
|
|
OUString extractIconName(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sIconName;
|
|
// allow pixbuf, but prefer icon-name
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"pixbuf"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sIconName = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
}
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"icon-name"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sIconName = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
}
|
|
if (sIconName == "missing-image")
|
|
return OUString();
|
|
OUString sReplace = mapStockToImageResource(sIconName);
|
|
return !sReplace.isEmpty() ? sReplace : sIconName;
|
|
}
|
|
|
|
WinBits extractRelief(VclBuilder::stringmap &rMap)
|
|
{
|
|
WinBits nBits = WB_3DLOOK;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"relief"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
assert(aFind->second != "half" && "relief of 'half' unsupported");
|
|
if (aFind->second == "none")
|
|
nBits = WB_FLATBUTTON;
|
|
rMap.erase(aFind);
|
|
}
|
|
return nBits;
|
|
}
|
|
|
|
OUString extractLabel(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sType;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"label"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sType = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sType;
|
|
}
|
|
|
|
OUString extractActionName(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sActionName;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"action-name"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sActionName = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sActionName;
|
|
}
|
|
|
|
bool extractVisible(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bRet = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"visible"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bRet = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
Size extractSizeRequest(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sWidthRequest(u"0"_ustr);
|
|
OUString sHeightRequest(u"0"_ustr);
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"width-request"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sWidthRequest = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
aFind = rMap.find(u"height-request"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sHeightRequest = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32());
|
|
}
|
|
|
|
OUString extractTooltipText(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sTooltipText;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"tooltip-text"_ustr);
|
|
if (aFind == rMap.end())
|
|
aFind = rMap.find(u"tooltip-markup"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sTooltipText = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sTooltipText;
|
|
}
|
|
|
|
float extractAlignment(VclBuilder::stringmap &rMap)
|
|
{
|
|
float f = 0.0;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"alignment"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
f = aFind->second.toFloat();
|
|
rMap.erase(aFind);
|
|
}
|
|
return f;
|
|
}
|
|
|
|
OUString extractTitle(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sTitle;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"title"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sTitle = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sTitle;
|
|
}
|
|
|
|
bool extractHeadersVisible(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bHeadersVisible = true;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"headers-visible"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bHeadersVisible = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bHeadersVisible;
|
|
}
|
|
|
|
bool extractSortIndicator(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bSortIndicator = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"sort-indicator"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bSortIndicator = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bSortIndicator;
|
|
}
|
|
|
|
bool extractClickable(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bClickable = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"clickable"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bClickable = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bClickable;
|
|
}
|
|
|
|
void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame)
|
|
{
|
|
if (!rFrame.is())
|
|
return;
|
|
|
|
OUString aCommand(extractActionName(rMap));
|
|
if (aCommand.isEmpty())
|
|
return;
|
|
|
|
OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
|
|
auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, aModuleName);
|
|
OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
|
|
if (!aLabel.isEmpty())
|
|
pButton->SetText(aLabel);
|
|
|
|
OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(aCommand, aProperties, rFrame));
|
|
if (!aTooltip.isEmpty())
|
|
pButton->SetQuickHelpText(aTooltip);
|
|
|
|
Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, rFrame));
|
|
pButton->SetModeImage(aImage);
|
|
|
|
pButton->SetCommandHandler(aCommand, rFrame);
|
|
}
|
|
|
|
VclPtr<Button> extractStockAndBuildPushButton(vcl::Window *pParent, VclBuilder::stringmap &rMap, bool bToggle)
|
|
{
|
|
WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER;
|
|
if (bToggle)
|
|
nBits |= WB_TOGGLE;
|
|
|
|
nBits |= extractRelief(rMap);
|
|
|
|
VclPtr<Button> xWindow = VclPtr<PushButton>::Create(pParent, nBits);
|
|
return xWindow;
|
|
}
|
|
|
|
VclPtr<MenuButton> extractStockAndBuildMenuButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
|
|
{
|
|
WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
|
|
|
|
nBits |= extractRelief(rMap);
|
|
|
|
VclPtr<MenuButton> xWindow = VclPtr<MenuButton>::Create(pParent, nBits);
|
|
return xWindow;
|
|
}
|
|
|
|
VclPtr<MenuButton> extractStockAndBuildMenuToggleButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
|
|
{
|
|
WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
|
|
|
|
nBits |= extractRelief(rMap);
|
|
|
|
VclPtr<MenuButton> xWindow = VclPtr<MenuToggleButton>::Create(pParent, nBits);
|
|
return xWindow;
|
|
}
|
|
|
|
WinBits extractDeferredBits(VclBuilder::stringmap &rMap)
|
|
{
|
|
WinBits nBits = WB_3DLOOK|WB_HIDE;
|
|
if (extractResizable(rMap))
|
|
nBits |= WB_SIZEABLE;
|
|
if (extractCloseable(rMap))
|
|
nBits |= WB_CLOSEABLE;
|
|
if (!extractDecorated(rMap))
|
|
nBits |= WB_OWNERDRAWDECORATION;
|
|
OUString sType(extractTypeHint(rMap));
|
|
if (sType == "utility")
|
|
nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_MOVEABLE;
|
|
else if (sType == "popup-menu")
|
|
nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_POPUP;
|
|
else if (sType == "dock")
|
|
nBits |= WB_DOCKABLE | WB_MOVEABLE;
|
|
else
|
|
nBits |= WB_MOVEABLE;
|
|
return nBits;
|
|
}
|
|
}
|
|
|
|
void VclBuilder::extractGroup(const OUString &id, stringmap &rMap)
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"group"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
OUString sID = aFind->second;
|
|
sal_Int32 nDelim = sID.indexOf(':');
|
|
if (nDelim != -1)
|
|
sID = sID.copy(0, nDelim);
|
|
m_pVclParserState->m_aGroupMaps.emplace_back(id, sID);
|
|
rMap.erase(aFind);
|
|
}
|
|
}
|
|
|
|
void VclBuilder::connectNumericFormatterAdjustment(const OUString &id, const OUString &rAdjustment)
|
|
{
|
|
if (!rAdjustment.isEmpty())
|
|
m_pVclParserState->m_aNumericFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
|
|
}
|
|
|
|
void VclBuilder::connectFormattedFormatterAdjustment(const OUString &id, const OUString &rAdjustment)
|
|
{
|
|
if (!rAdjustment.isEmpty())
|
|
m_pVclParserState->m_aFormattedFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
|
|
}
|
|
|
|
bool VclBuilder::extractAdjustmentToMap(const OUString& id, VclBuilder::stringmap& rMap, std::vector<WidgetAdjustmentMap>& rAdjustmentMap)
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"adjustment"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
rAdjustmentMap.emplace_back(id, aFind->second);
|
|
rMap.erase(aFind);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
sal_Int32 extractActive(VclBuilder::stringmap &rMap)
|
|
{
|
|
sal_Int32 nActiveId = 0;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"active"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
nActiveId = aFind->second.toInt32();
|
|
rMap.erase(aFind);
|
|
}
|
|
return nActiveId;
|
|
}
|
|
|
|
bool extractSelectable(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bSelectable = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"selectable"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bSelectable = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bSelectable;
|
|
}
|
|
|
|
OUString extractAdjustment(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sAdjustment;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"adjustment"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sAdjustment= aFind->second;
|
|
rMap.erase(aFind);
|
|
return sAdjustment;
|
|
}
|
|
return sAdjustment;
|
|
}
|
|
|
|
bool extractDrawIndicator(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bDrawIndicator = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"draw-indicator"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bDrawIndicator = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bDrawIndicator;
|
|
}
|
|
}
|
|
|
|
void VclBuilder::extractModel(const OUString &id, stringmap &rMap)
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"model"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
m_pVclParserState->m_aModelMaps.emplace_back(id, aFind->second,
|
|
extractActive(rMap));
|
|
rMap.erase(aFind);
|
|
}
|
|
}
|
|
|
|
void VclBuilder::extractBuffer(const OUString &id, stringmap &rMap)
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"buffer"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
m_pVclParserState->m_aTextBufferMaps.emplace_back(id, aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
}
|
|
|
|
int VclBuilder::getImageSize(const stringmap &rMap)
|
|
{
|
|
int nSize = 4;
|
|
auto aFind = rMap.find(u"icon-size"_ustr);
|
|
if (aFind != rMap.end())
|
|
nSize = aFind->second.toInt32();
|
|
return nSize;
|
|
}
|
|
|
|
void VclBuilder::extractButtonImage(const OUString &id, stringmap &rMap, bool bRadio)
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"image"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
m_pVclParserState->m_aButtonImageWidgetMaps.emplace_back(id, aFind->second, bRadio);
|
|
rMap.erase(aFind);
|
|
}
|
|
}
|
|
|
|
void VclBuilder::extractMnemonicWidget(const OUString &rLabelID, stringmap &rMap)
|
|
{
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"mnemonic-widget"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
OUString sID = aFind->second;
|
|
sal_Int32 nDelim = sID.indexOf(':');
|
|
if (nDelim != -1)
|
|
sID = sID.copy(0, nDelim);
|
|
m_pVclParserState->m_aMnemonicWidgetMaps.emplace_back(rLabelID, sID);
|
|
rMap.erase(aFind);
|
|
}
|
|
}
|
|
|
|
vcl::Window* VclBuilder::prepareWidgetOwnScrolling(vcl::Window *pParent, WinBits &rWinStyle)
|
|
{
|
|
//For Widgets that manage their own scrolling, if one appears as a child of
|
|
//a scrolling window shoehorn that scrolling settings to this widget and
|
|
//return the real parent to use
|
|
if (pParent && pParent->GetType() == WindowType::SCROLLWINDOW)
|
|
{
|
|
WinBits nScrollBits = pParent->GetStyle();
|
|
nScrollBits &= (WB_AUTOHSCROLL|WB_HSCROLL|WB_AUTOVSCROLL|WB_VSCROLL);
|
|
rWinStyle |= nScrollBits;
|
|
if (static_cast<VclScrolledWindow*>(pParent)->HasVisibleBorder())
|
|
rWinStyle |= WB_BORDER;
|
|
pParent = pParent->GetParent();
|
|
}
|
|
|
|
return pParent;
|
|
}
|
|
|
|
void VclBuilder::cleanupWidgetOwnScrolling(vcl::Window *pScrollParent, vcl::Window *pWindow, stringmap &rMap)
|
|
{
|
|
//remove the redundant scrolling parent
|
|
sal_Int32 nWidthReq = pScrollParent->get_width_request();
|
|
rMap[u"width-request"_ustr] = OUString::number(nWidthReq);
|
|
sal_Int32 nHeightReq = pScrollParent->get_height_request();
|
|
rMap[u"height-request"_ustr] = OUString::number(nHeightReq);
|
|
|
|
m_pVclParserState->m_aRedundantParentWidgets[pScrollParent] = pWindow;
|
|
}
|
|
|
|
#ifndef DISABLE_DYNLOADING
|
|
|
|
extern "C" { static void thisModule() {} }
|
|
|
|
namespace {
|
|
|
|
// Don't unload the module on destruction
|
|
class NoAutoUnloadModule : public osl::Module
|
|
{
|
|
public:
|
|
~NoAutoUnloadModule() { release(); }
|
|
};
|
|
|
|
}
|
|
|
|
typedef std::map<OUString, std::shared_ptr<NoAutoUnloadModule>> ModuleMap;
|
|
static ModuleMap g_aModuleMap;
|
|
|
|
#if ENABLE_MERGELIBS
|
|
static std::shared_ptr<NoAutoUnloadModule> g_pMergedLib = std::make_shared<NoAutoUnloadModule>();
|
|
#endif
|
|
|
|
#ifndef SAL_DLLPREFIX
|
|
# define SAL_DLLPREFIX ""
|
|
#endif
|
|
|
|
#endif
|
|
|
|
namespace vcl {
|
|
|
|
void VclBuilderPreload()
|
|
{
|
|
#ifndef DISABLE_DYNLOADING
|
|
|
|
#if ENABLE_MERGELIBS
|
|
g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
|
|
#else
|
|
// find -name '*ui*' | xargs grep 'class=".*lo-' |
|
|
// sed 's/.*class="//' | sed 's/-.*$//' | sort | uniq
|
|
static const char *aWidgetLibs[] = {
|
|
"sfxlo", "svtlo"
|
|
};
|
|
for (const auto & lib : aWidgetLibs)
|
|
{
|
|
std::unique_ptr<NoAutoUnloadModule> pModule(new NoAutoUnloadModule);
|
|
OUString sModule = SAL_DLLPREFIX + OUString::createFromAscii(lib) + SAL_DLLEXTENSION;
|
|
if (pModule->loadRelative(&thisModule, sModule))
|
|
g_aModuleMap.insert(std::make_pair(sModule, std::move(pModule)));
|
|
}
|
|
#endif // ENABLE_MERGELIBS
|
|
#endif // DISABLE_DYNLOADING
|
|
}
|
|
|
|
}
|
|
|
|
#if defined DISABLE_DYNLOADING && !HAVE_FEATURE_DESKTOP
|
|
|
|
// This ifdef branch is mainly for building for the Collabora Online
|
|
// -based mobile apps for Android and iOS.
|
|
|
|
extern "C" VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name);
|
|
|
|
#elif defined EMSCRIPTEN && !ENABLE_QT5
|
|
|
|
// This branch is mainly for building for WASM, and especially for
|
|
// Collabora Online in the browser, where code from core and Collabora
|
|
// Online is compiled to WASM and linked into a single WASM binary.
|
|
// (Not for Allotropia's Qt-based LibreOffice in the browser.)
|
|
|
|
// When building core for WASM it doesn't use the same
|
|
// solenv/bin/native-code.py thing as the mobile apps, even if in both
|
|
// cases everything is linked statically. So there is no generated
|
|
// native-code.h, and we can't use lo_get_custom_widget_func() from
|
|
// that. So cheat and duplicate the code from an existing generated
|
|
// native-code.h. It's just a handful of lines anyway.
|
|
|
|
extern "C" void makeNotebookbarTabControl(VclPtr<vcl::Window> &rRet, const VclPtr<vcl::Window> &pParent, VclBuilder::stringmap &rVec);
|
|
extern "C" void makeNotebookbarToolBox(VclPtr<vcl::Window> &rRet, const VclPtr<vcl::Window> &pParent, VclBuilder::stringmap &rVec);
|
|
|
|
static struct { const char *name; VclBuilder::customMakeWidget func; } custom_widgets[] = {
|
|
{ "makeNotebookbarTabControl", makeNotebookbarTabControl },
|
|
{ "makeNotebookbarToolBox", makeNotebookbarToolBox },
|
|
};
|
|
|
|
static VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name)
|
|
{
|
|
for (size_t i = 0; i < sizeof(custom_widgets) / sizeof(custom_widgets[0]); i++)
|
|
if (strcmp(name, custom_widgets[i].name) == 0)
|
|
return custom_widgets[i].func;
|
|
return nullptr;
|
|
}
|
|
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
// Takes a string like "sfxlo-NotebookbarToolBox"
|
|
VclBuilder::customMakeWidget GetCustomMakeWidget(const OUString& rName)
|
|
{
|
|
const OUString name = rName == "sfxlo-SidebarToolBox" ? u"sfxlo-NotebookbarToolBox"_ustr : rName;
|
|
VclBuilder::customMakeWidget pFunction = nullptr;
|
|
if (sal_Int32 nDelim = name.indexOf('-'); nDelim != -1)
|
|
{
|
|
const OUString sFunction(OUString::Concat("make") + name.subView(nDelim + 1));
|
|
|
|
#ifndef DISABLE_DYNLOADING
|
|
const OUString sModule = OUString::Concat(SAL_DLLPREFIX)
|
|
+ name.subView(0, nDelim)
|
|
+ SAL_DLLEXTENSION;
|
|
ModuleMap::iterator aI = g_aModuleMap.find(sModule);
|
|
if (aI == g_aModuleMap.end())
|
|
{
|
|
std::shared_ptr<NoAutoUnloadModule> pModule;
|
|
#if ENABLE_MERGELIBS
|
|
if (!g_pMergedLib->is())
|
|
g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
|
|
if ((pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
|
|
g_pMergedLib->getFunctionSymbol(sFunction))))
|
|
pModule = g_pMergedLib;
|
|
#endif
|
|
if (!pFunction)
|
|
{
|
|
pModule = std::make_shared<NoAutoUnloadModule>();
|
|
bool ok = pModule->loadRelative(&thisModule, sModule);
|
|
if (!ok)
|
|
{
|
|
#ifdef LINUX
|
|
// in the case of preloading, we don't have eg. the
|
|
// libcuilo.so, but still need to dlsym the symbols -
|
|
// which are already in-process
|
|
if (comphelper::LibreOfficeKit::isActive())
|
|
{
|
|
pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(dlsym(RTLD_DEFAULT, OUStringToOString(sFunction, RTL_TEXTENCODING_UTF8).getStr()));
|
|
ok = !!pFunction;
|
|
assert(ok && "couldn't even directly dlsym the sFunction (available via preload)");
|
|
}
|
|
#endif
|
|
assert(ok && "bad module name in .ui");
|
|
}
|
|
else
|
|
{
|
|
pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
|
|
pModule->getFunctionSymbol(sFunction));
|
|
}
|
|
}
|
|
g_aModuleMap.insert(std::make_pair(sModule, pModule));
|
|
}
|
|
else
|
|
pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
|
|
aI->second->getFunctionSymbol(sFunction));
|
|
#elif !HAVE_FEATURE_DESKTOP || (defined EMSCRIPTEN && !ENABLE_QT5)
|
|
// This ifdef branch is mainly for building for either the
|
|
// Android or iOS apps, or the Collabora Online as WASM thing.
|
|
pFunction = lo_get_custom_widget_func(sFunction.toUtf8().getStr());
|
|
SAL_WARN_IF(!pFunction, "vcl.builder", "Could not find " << sFunction);
|
|
assert(pFunction);
|
|
#else
|
|
pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
|
|
osl_getFunctionSymbol((oslModule)RTLD_DEFAULT, sFunction.pData));
|
|
#endif
|
|
}
|
|
return pFunction;
|
|
}
|
|
}
|
|
|
|
VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OUString &name, const OUString &id,
|
|
stringmap &rMap)
|
|
{
|
|
bool bIsPlaceHolder = name.isEmpty();
|
|
bool bVertical = false;
|
|
|
|
if (pParent && (pParent->GetType() == WindowType::TABCONTROL ||
|
|
pParent->GetType() == WindowType::VERTICALTABCONTROL))
|
|
{
|
|
bool bTopLevel(name == "GtkDialog" || name == "GtkMessageDialog" ||
|
|
name == "GtkWindow" || name == "GtkPopover" || name == "GtkAssistant");
|
|
if (!bTopLevel)
|
|
{
|
|
if (pParent->GetType() == WindowType::TABCONTROL)
|
|
{
|
|
//We have to add a page
|
|
//make default pageid == position
|
|
TabControl *pTabControl = static_cast<TabControl*>(pParent);
|
|
sal_uInt16 nNewPageCount = pTabControl->GetPageCount()+1;
|
|
sal_uInt16 nNewPageId = nNewPageCount;
|
|
pTabControl->InsertPage(nNewPageId, OUString());
|
|
pTabControl->SetCurPageId(nNewPageId);
|
|
SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
|
|
if (!bIsPlaceHolder)
|
|
{
|
|
VclPtrInstance<TabPage> pPage(pTabControl);
|
|
pPage->Show();
|
|
|
|
//Make up a name for it
|
|
OUString sTabPageId = get_by_window(pParent) +
|
|
"-page" +
|
|
OUString::number(nNewPageCount);
|
|
m_aChildren.emplace_back(sTabPageId, pPage, false);
|
|
pPage->SetHelpId(getHelpRoot() + sTabPageId);
|
|
|
|
pParent = pPage;
|
|
|
|
pTabControl->SetTabPage(nNewPageId, pPage);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VerticalTabControl *pTabControl = static_cast<VerticalTabControl*>(pParent);
|
|
SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
|
|
if (!bIsPlaceHolder)
|
|
pParent = pTabControl->GetPageParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bIsPlaceHolder || name == "GtkTreeSelection")
|
|
return nullptr;
|
|
|
|
ToolBox *pToolBox = (pParent && pParent->GetType() == WindowType::TOOLBOX) ? static_cast<ToolBox*>(pParent) : nullptr;
|
|
|
|
extractButtonImage(id, rMap, name == "GtkRadioButton");
|
|
|
|
VclPtr<vcl::Window> xWindow;
|
|
if (name == "GtkDialog" || name == "GtkAssistant")
|
|
{
|
|
// WB_ALLOWMENUBAR because we don't know in advance if we will encounter
|
|
// a menubar, and menubars need a BorderWindow in the toplevel, and
|
|
// such border windows need to be in created during the dialog ctor
|
|
WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_ALLOWMENUBAR;
|
|
if (extractResizable(rMap))
|
|
nBits |= WB_SIZEABLE;
|
|
if (extractCloseable(rMap))
|
|
nBits |= WB_CLOSEABLE;
|
|
Dialog::InitFlag eInit = !pParent ? Dialog::InitFlag::NoParent : Dialog::InitFlag::Default;
|
|
if (name == "GtkAssistant")
|
|
xWindow = VclPtr<vcl::RoadmapWizard>::Create(pParent, nBits, eInit);
|
|
else
|
|
xWindow = VclPtr<Dialog>::Create(pParent, nBits, eInit);
|
|
#if HAVE_FEATURE_DESKTOP
|
|
if (!extractModal(rMap))
|
|
xWindow->SetType(WindowType::MODELESSDIALOG);
|
|
#endif
|
|
}
|
|
else if (name == "GtkMessageDialog")
|
|
{
|
|
WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE;
|
|
if (extractResizable(rMap))
|
|
nBits |= WB_SIZEABLE;
|
|
VclPtr<MessageDialog> xDialog(VclPtr<MessageDialog>::Create(pParent, nBits));
|
|
m_pVclParserState->m_aMessageDialogs.push_back(xDialog);
|
|
xWindow = xDialog;
|
|
#if defined _WIN32
|
|
xWindow->set_border_width(3);
|
|
#else
|
|
xWindow->set_border_width(12);
|
|
#endif
|
|
}
|
|
else if (name == "GtkBox" || name == "GtkStatusbar")
|
|
{
|
|
bVertical = hasOrientationVertical(rMap);
|
|
if (bVertical)
|
|
xWindow = VclPtr<VclVBox>::Create(pParent);
|
|
else
|
|
xWindow = VclPtr<VclHBox>::Create(pParent);
|
|
|
|
if (name == "GtkStatusbar")
|
|
xWindow->SetAccessibleRole(css::accessibility::AccessibleRole::STATUS_BAR);
|
|
}
|
|
else if (name == "GtkPaned")
|
|
{
|
|
bVertical = hasOrientationVertical(rMap);
|
|
if (bVertical)
|
|
xWindow = VclPtr<VclVPaned>::Create(pParent);
|
|
else
|
|
xWindow = VclPtr<VclHPaned>::Create(pParent);
|
|
}
|
|
else if (name == "GtkHBox")
|
|
xWindow = VclPtr<VclHBox>::Create(pParent);
|
|
else if (name == "GtkVBox")
|
|
xWindow = VclPtr<VclVBox>::Create(pParent);
|
|
else if (name == "GtkButtonBox")
|
|
{
|
|
bVertical = hasOrientationVertical(rMap);
|
|
if (bVertical)
|
|
xWindow = VclPtr<VclVButtonBox>::Create(pParent);
|
|
else
|
|
xWindow = VclPtr<VclHButtonBox>::Create(pParent);
|
|
}
|
|
else if (name == "GtkHButtonBox")
|
|
xWindow = VclPtr<VclHButtonBox>::Create(pParent);
|
|
else if (name == "GtkVButtonBox")
|
|
xWindow = VclPtr<VclVButtonBox>::Create(pParent);
|
|
else if (name == "GtkGrid")
|
|
xWindow = VclPtr<VclGrid>::Create(pParent);
|
|
else if (name == "GtkFrame")
|
|
xWindow = VclPtr<VclFrame>::Create(pParent);
|
|
else if (name == "GtkExpander")
|
|
{
|
|
VclPtrInstance<VclExpander> pExpander(pParent);
|
|
m_pVclParserState->m_aExpanderWidgets.push_back(pExpander);
|
|
xWindow = pExpander;
|
|
}
|
|
else if (name == "GtkButton" || (!isLegacy() && name == "GtkToggleButton"))
|
|
{
|
|
VclPtr<Button> xButton;
|
|
OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
|
|
if (sMenu.isEmpty())
|
|
xButton = extractStockAndBuildPushButton(pParent, rMap, name == "GtkToggleButton");
|
|
else
|
|
{
|
|
assert(isLegacy() && "use GtkMenuButton");
|
|
xButton = extractStockAndBuildMenuButton(pParent, rMap);
|
|
m_pVclParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
|
|
}
|
|
xButton->SetImageAlign(ImageAlign::Left); //default to left
|
|
setupFromActionName(xButton, rMap, m_xFrame);
|
|
xWindow = xButton;
|
|
}
|
|
else if (name == "GtkMenuButton")
|
|
{
|
|
VclPtr<MenuButton> xButton;
|
|
|
|
OUString sMenu = extractPopupMenu(rMap);
|
|
if (!sMenu.isEmpty())
|
|
m_pVclParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
|
|
|
|
OUString sType = extractWidgetName(rMap);
|
|
if (sType.isEmpty())
|
|
{
|
|
xButton = extractStockAndBuildMenuButton(pParent, rMap);
|
|
xButton->SetAccessibleRole(css::accessibility::AccessibleRole::BUTTON_MENU);
|
|
}
|
|
else
|
|
{
|
|
xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
|
|
}
|
|
|
|
xButton->SetImageAlign(ImageAlign::Left); //default to left
|
|
|
|
if (!extractDrawIndicator(rMap))
|
|
xButton->SetDropDown(PushButtonDropdownStyle::NONE);
|
|
|
|
setupFromActionName(xButton, rMap, m_xFrame);
|
|
xWindow = xButton;
|
|
}
|
|
else if (name == "GtkToggleButton" && isLegacy())
|
|
{
|
|
VclPtr<Button> xButton;
|
|
OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
|
|
assert(sMenu.getLength() && "not implemented yet");
|
|
xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
|
|
m_pVclParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
|
|
xButton->SetImageAlign(ImageAlign::Left); //default to left
|
|
setupFromActionName(xButton, rMap, m_xFrame);
|
|
xWindow = xButton;
|
|
}
|
|
else if (name == "GtkRadioButton")
|
|
{
|
|
extractGroup(id, rMap);
|
|
WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
|
|
VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, true, nBits);
|
|
xButton->SetImageAlign(ImageAlign::Left); //default to left
|
|
xWindow = xButton;
|
|
}
|
|
else if (name == "GtkCheckButton")
|
|
{
|
|
WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
|
|
bool bIsTriState = extractInconsistent(rMap);
|
|
VclPtr<CheckBox> xCheckBox = VclPtr<CheckBox>::Create(pParent, nBits);
|
|
if (bIsTriState)
|
|
{
|
|
xCheckBox->EnableTriState(true);
|
|
xCheckBox->SetState(TRISTATE_INDET);
|
|
}
|
|
xCheckBox->SetImageAlign(ImageAlign::Left); //default to left
|
|
|
|
xWindow = xCheckBox;
|
|
}
|
|
else if (name == "GtkSpinButton")
|
|
{
|
|
OUString sAdjustment = extractAdjustment(rMap);
|
|
|
|
WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_3DLOOK|WB_SPIN|WB_REPEAT;
|
|
if (extractHasFrame(rMap))
|
|
nBits |= WB_BORDER;
|
|
|
|
connectFormattedFormatterAdjustment(id, sAdjustment);
|
|
VclPtrInstance<FormattedField> xField(pParent, nBits);
|
|
xField->GetFormatter().SetMinValue(0);
|
|
xWindow = xField;
|
|
}
|
|
else if (name == "GtkLinkButton")
|
|
xWindow = VclPtr<FixedHyperlink>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_NOLABEL);
|
|
else if (name == "GtkComboBox" || name == "GtkComboBoxText")
|
|
{
|
|
extractModel(id, rMap);
|
|
|
|
WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
|
|
|
|
bool bDropdown = BuilderUtils::extractDropdown(rMap);
|
|
|
|
if (bDropdown)
|
|
nBits |= WB_DROPDOWN;
|
|
|
|
if (extractEntry(rMap))
|
|
{
|
|
VclPtrInstance<ComboBox> xComboBox(pParent, nBits);
|
|
xComboBox->EnableAutoSize(true);
|
|
xWindow = xComboBox;
|
|
}
|
|
else
|
|
{
|
|
VclPtrInstance<ListBox> xListBox(pParent, nBits|WB_SIMPLEMODE);
|
|
xListBox->EnableAutoSize(true);
|
|
xWindow = xListBox;
|
|
}
|
|
}
|
|
else if (name == "VclOptionalBox" || name == "sfxlo-OptionalBox")
|
|
{
|
|
// tdf#135495 fallback sfxlo-OptionalBox to VclOptionalBox as a stopgap
|
|
xWindow = VclPtr<OptionalBox>::Create(pParent);
|
|
}
|
|
else if (name == "svtlo-ManagedMenuButton")
|
|
{
|
|
// like tdf#135495 keep the name svtlo-ManagedMenuButton even though it's a misnomer
|
|
// and is not dlsymed from the svt library
|
|
xWindow = VclPtr<ManagedMenuButton>::Create(pParent, WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_FLATBUTTON);
|
|
OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
|
|
if (!sMenu.isEmpty())
|
|
m_pVclParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
|
|
setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
|
|
}
|
|
else if (name == "sfxlo-PriorityMergedHBox")
|
|
{
|
|
// like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
|
|
xWindow = VclPtr<PriorityMergedHBox>::Create(pParent);
|
|
}
|
|
else if (name == "sfxlo-PriorityHBox")
|
|
{
|
|
// like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
|
|
xWindow = VclPtr<PriorityHBox>::Create(pParent);
|
|
}
|
|
else if (name == "sfxlo-DropdownBox")
|
|
{
|
|
// like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
|
|
xWindow = VclPtr<DropdownBox>::Create(pParent);
|
|
}
|
|
else if (name == "sfxlo-ContextVBox")
|
|
{
|
|
// like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
|
|
xWindow = VclPtr<ContextVBox>::Create(pParent);
|
|
}
|
|
else if (name == "GtkIconView")
|
|
{
|
|
assert(rMap.find(u"model"_ustr) != rMap.end() && "GtkIconView must have a model");
|
|
|
|
//window we want to apply the packing props for this GtkIconView to
|
|
VclPtr<vcl::Window> xWindowForPackingProps;
|
|
extractModel(id, rMap);
|
|
WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
|
|
//IconView manages its own scrolling,
|
|
vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
|
|
|
|
VclPtr<IconView> xBox = VclPtr<IconView>::Create(pRealParent, nWinStyle);
|
|
xWindowForPackingProps = xBox;
|
|
|
|
xWindow = xBox;
|
|
xBox->SetNoAutoCurEntry(true);
|
|
xBox->SetQuickSearch(true);
|
|
|
|
if (pRealParent != pParent)
|
|
cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
|
|
}
|
|
else if (name == "GtkTreeView")
|
|
{
|
|
if (!isLegacy())
|
|
{
|
|
assert(rMap.find(u"model"_ustr) != rMap.end() && "GtkTreeView must have a model");
|
|
}
|
|
|
|
//window we want to apply the packing props for this GtkTreeView to
|
|
VclPtr<vcl::Window> xWindowForPackingProps;
|
|
//To-Do
|
|
//a) make SvHeaderTabListBox/SvTabListBox the default target for GtkTreeView
|
|
//b) remove the non-drop down mode of ListBox and convert
|
|
// everything over to SvHeaderTabListBox/SvTabListBox
|
|
extractModel(id, rMap);
|
|
WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
|
|
if (isLegacy())
|
|
{
|
|
OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
|
|
if (!sBorder.isEmpty())
|
|
nWinStyle |= WB_BORDER;
|
|
}
|
|
else
|
|
{
|
|
nWinStyle |= WB_HASBUTTONS | WB_HASBUTTONSATROOT;
|
|
}
|
|
//ListBox/SvHeaderTabListBox manages its own scrolling,
|
|
vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
|
|
if (isLegacy())
|
|
{
|
|
xWindow = VclPtr<ListBox>::Create(pRealParent, nWinStyle | WB_SIMPLEMODE);
|
|
xWindowForPackingProps = xWindow;
|
|
}
|
|
else
|
|
{
|
|
VclPtr<SvTabListBox> xBox;
|
|
bool bHeadersVisible = extractHeadersVisible(rMap);
|
|
if (bHeadersVisible)
|
|
{
|
|
VclPtr<VclVBox> xContainer = VclPtr<VclVBox>::Create(pRealParent);
|
|
OUString containerid(id + "-container");
|
|
xContainer->SetHelpId(getHelpRoot() + containerid);
|
|
m_aChildren.emplace_back(containerid, xContainer, true);
|
|
|
|
VclPtrInstance<HeaderBar> xHeader(xContainer, WB_BUTTONSTYLE | WB_BORDER | WB_TABSTOP | WB_3DLOOK);
|
|
xHeader->set_width_request(0); // let the headerbar width not affect the size request
|
|
OUString headerid(id + "-header");
|
|
xHeader->SetHelpId(getHelpRoot() + headerid);
|
|
m_aChildren.emplace_back(headerid, xHeader, true);
|
|
|
|
VclPtr<LclHeaderTabListBox> xHeaderBox = VclPtr<LclHeaderTabListBox>::Create(xContainer, nWinStyle);
|
|
xHeaderBox->InitHeaderBar(xHeader);
|
|
xContainer->set_expand(true);
|
|
xHeader->Show();
|
|
xContainer->Show();
|
|
xBox = xHeaderBox;
|
|
xWindowForPackingProps = xContainer;
|
|
}
|
|
else
|
|
{
|
|
xBox = VclPtr<LclTabListBox>::Create(pRealParent, nWinStyle);
|
|
xWindowForPackingProps = xBox;
|
|
}
|
|
xWindow = xBox;
|
|
xBox->SetNoAutoCurEntry(true);
|
|
xBox->SetQuickSearch(true);
|
|
xBox->SetSpaceBetweenEntries(3);
|
|
xBox->SetEntryHeight(16);
|
|
xBox->SetHighlightRange(); // select over the whole width
|
|
}
|
|
if (pRealParent != pParent)
|
|
cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
|
|
}
|
|
else if (name == "GtkTreeViewColumn")
|
|
{
|
|
if (!isLegacy())
|
|
{
|
|
SvHeaderTabListBox* pTreeView = dynamic_cast<SvHeaderTabListBox*>(pParent);
|
|
if (HeaderBar* pHeaderBar = pTreeView ? pTreeView->GetHeaderBar() : nullptr)
|
|
{
|
|
HeaderBarItemBits nBits = HeaderBarItemBits::LEFTIMAGE;
|
|
if (extractClickable(rMap))
|
|
nBits |= HeaderBarItemBits::CLICKABLE;
|
|
if (extractSortIndicator(rMap))
|
|
nBits |= HeaderBarItemBits::DOWNARROW;
|
|
float fAlign = extractAlignment(rMap);
|
|
if (fAlign == 0.0)
|
|
nBits |= HeaderBarItemBits::LEFT;
|
|
else if (fAlign == 1.0)
|
|
nBits |= HeaderBarItemBits::RIGHT;
|
|
else if (fAlign == 0.5)
|
|
nBits |= HeaderBarItemBits::CENTER;
|
|
auto nItemId = pHeaderBar->GetItemCount() + 1;
|
|
OUString sTitle(extractTitle(rMap));
|
|
pHeaderBar->InsertItem(nItemId, sTitle, 100, nBits);
|
|
}
|
|
}
|
|
}
|
|
else if (name == "GtkLabel")
|
|
{
|
|
WinBits nWinStyle = WB_CENTER|WB_VCENTER|WB_3DLOOK;
|
|
extractMnemonicWidget(id, rMap);
|
|
if (extractSelectable(rMap))
|
|
xWindow = VclPtr<SelectableFixedText>::Create(pParent, nWinStyle);
|
|
else
|
|
xWindow = VclPtr<FixedText>::Create(pParent, nWinStyle);
|
|
}
|
|
else if (name == "GtkImage")
|
|
{
|
|
VclPtr<FixedImage> xFixedImage = VclPtr<FixedImage>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_SCALE);
|
|
OUString sIconName = extractIconName(rMap);
|
|
if (!sIconName.isEmpty())
|
|
xFixedImage->SetImage(FixedImage::loadThemeImage(sIconName));
|
|
m_pVclParserState->m_aImageSizeMap[id] = getImageSize(rMap);
|
|
xWindow = xFixedImage;
|
|
//such parentless GtkImages are temps used to set icons on buttons
|
|
//default them to hidden to stop e.g. insert->index entry flicking temp
|
|
//full screen windows
|
|
if (!pParent)
|
|
{
|
|
rMap[u"visible"_ustr] = "false";
|
|
}
|
|
}
|
|
else if (name == "GtkSeparator")
|
|
{
|
|
bVertical = hasOrientationVertical(rMap);
|
|
xWindow = VclPtr<FixedLine>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
|
|
}
|
|
else if (name == "GtkScrollbar")
|
|
{
|
|
extractAdjustmentToMap(id, rMap, m_pVclParserState->m_aScrollAdjustmentMaps);
|
|
bVertical = hasOrientationVertical(rMap);
|
|
xWindow = VclPtr<ScrollBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
|
|
}
|
|
else if (name == "GtkProgressBar")
|
|
{
|
|
extractAdjustmentToMap(id, rMap, m_pVclParserState->m_aScrollAdjustmentMaps);
|
|
bVertical = hasOrientationVertical(rMap);
|
|
xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ, ProgressBar::BarStyle::Progress);
|
|
}
|
|
else if (name == "GtkLevelBar")
|
|
{
|
|
extractAdjustmentToMap(id, rMap, m_pVclParserState->m_aScrollAdjustmentMaps);
|
|
bVertical = hasOrientationVertical(rMap);
|
|
xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ, ProgressBar::BarStyle::Level);
|
|
}
|
|
else if (name == "GtkScrolledWindow")
|
|
{
|
|
xWindow = VclPtr<VclScrolledWindow>::Create(pParent);
|
|
}
|
|
else if (name == "GtkViewport")
|
|
{
|
|
xWindow = VclPtr<VclViewport>::Create(pParent);
|
|
}
|
|
else if (name == "GtkEventBox")
|
|
{
|
|
xWindow = VclPtr<VclEventBox>::Create(pParent);
|
|
}
|
|
else if (name == "GtkEntry")
|
|
{
|
|
WinBits nWinStyle = WB_LEFT|WB_VCENTER|WB_3DLOOK;
|
|
if (extractHasFrame(rMap))
|
|
nWinStyle |= WB_BORDER;
|
|
xWindow = VclPtr<Edit>::Create(pParent, nWinStyle);
|
|
BuilderUtils::ensureDefaultWidthChars(rMap);
|
|
}
|
|
else if (name == "GtkNotebook")
|
|
{
|
|
if (!extractVerticalTabPos(rMap))
|
|
xWindow = VclPtr<TabControl>::Create(pParent, WB_STDTABCONTROL|WB_3DLOOK);
|
|
else
|
|
xWindow = VclPtr<VerticalTabControl>::Create(pParent, extractVerticalTabsWithIcons(rMap));
|
|
}
|
|
else if (name == "GtkDrawingArea")
|
|
{
|
|
xWindow = VclPtr<VclDrawingArea>::Create(pParent, WB_TABSTOP);
|
|
}
|
|
else if (name == "GtkTextView")
|
|
{
|
|
extractBuffer(id, rMap);
|
|
|
|
WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT;
|
|
//VclMultiLineEdit manages its own scrolling,
|
|
vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
|
|
xWindow = VclPtr<VclMultiLineEdit>::Create(pRealParent, nWinStyle);
|
|
if (pRealParent != pParent)
|
|
cleanupWidgetOwnScrolling(pParent, xWindow, rMap);
|
|
}
|
|
else if (name == "GtkSpinner")
|
|
{
|
|
xWindow = VclPtr<Throbber>::Create(pParent, WB_3DLOOK);
|
|
}
|
|
else if (name == "GtkScale")
|
|
{
|
|
extractAdjustmentToMap(id, rMap, m_pVclParserState->m_aSliderAdjustmentMaps);
|
|
bool bDrawValue = extractDrawValue(rMap);
|
|
if (bDrawValue)
|
|
{
|
|
OUString sValuePos = extractValuePos(rMap);
|
|
(void)sValuePos;
|
|
}
|
|
bVertical = hasOrientationVertical(rMap);
|
|
|
|
WinBits nWinStyle = bVertical ? WB_VERT : WB_HORZ;
|
|
|
|
xWindow = VclPtr<Slider>::Create(pParent, nWinStyle);
|
|
}
|
|
else if (name == "GtkToolbar")
|
|
{
|
|
xWindow = VclPtr<ToolBox>::Create(pParent, WB_3DLOOK | WB_TABSTOP);
|
|
}
|
|
else if(name == "NotebookBarAddonsToolMergePoint")
|
|
{
|
|
customMakeWidget pFunction = GetCustomMakeWidget(u"sfxlo-NotebookbarToolBox"_ustr);
|
|
if(pFunction != nullptr)
|
|
NotebookBarAddonsMerger::MergeNotebookBarAddons(pParent, pFunction, m_xFrame, *m_pNotebookBarAddonsItem, rMap);
|
|
return nullptr;
|
|
}
|
|
else if (isToolbarItemClass(name))
|
|
{
|
|
if (pToolBox)
|
|
{
|
|
OUString aCommand(extractActionName(rMap));
|
|
|
|
ToolBoxItemId nItemId(0);
|
|
ToolBoxItemBits nBits = ToolBoxItemBits::NONE;
|
|
if (name == "GtkMenuToolButton")
|
|
nBits |= ToolBoxItemBits::DROPDOWN;
|
|
else if (name == "GtkToggleToolButton")
|
|
nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::CHECKABLE;
|
|
else if (name == "GtkRadioToolButton")
|
|
nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::RADIOCHECK;
|
|
|
|
if (!aCommand.isEmpty() && m_xFrame.is())
|
|
{
|
|
pToolBox->InsertItem(aCommand, m_xFrame, nBits, extractSizeRequest(rMap));
|
|
nItemId = pToolBox->GetItemId(aCommand);
|
|
}
|
|
else
|
|
{
|
|
nItemId = ToolBoxItemId(pToolBox->GetItemCount() + 1);
|
|
//TODO: ImplToolItems::size_type -> sal_uInt16!
|
|
if (aCommand.isEmpty() && !isLegacy())
|
|
aCommand = id;
|
|
pToolBox->InsertItem(nItemId, extractLabel(rMap), aCommand, nBits);
|
|
}
|
|
|
|
pToolBox->SetHelpId(nItemId, getHelpRoot() + id);
|
|
OUString sTooltip(extractTooltipText(rMap));
|
|
if (!sTooltip.isEmpty())
|
|
pToolBox->SetQuickHelpText(nItemId, sTooltip);
|
|
|
|
OUString sIconName(extractIconName(rMap));
|
|
if (!sIconName.isEmpty())
|
|
pToolBox->SetItemImage(nItemId, FixedImage::loadThemeImage(sIconName));
|
|
|
|
if (!extractVisible(rMap))
|
|
pToolBox->HideItem(nItemId);
|
|
|
|
m_pVclParserState->m_nLastToolbarId = nItemId;
|
|
|
|
return nullptr; // no widget to be created
|
|
}
|
|
}
|
|
else if (name == "GtkSeparatorToolItem")
|
|
{
|
|
if (pToolBox)
|
|
{
|
|
pToolBox->InsertSeparator();
|
|
return nullptr; // no widget to be created
|
|
}
|
|
}
|
|
else if (name == "GtkWindow")
|
|
{
|
|
WinBits nBits = extractDeferredBits(rMap);
|
|
if (nBits & WB_DOCKABLE)
|
|
xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_MOVEABLE);
|
|
else
|
|
xWindow = VclPtr<FloatingWindow>::Create(pParent, nBits|WB_MOVEABLE);
|
|
}
|
|
else if (name == "GtkPopover")
|
|
{
|
|
WinBits nBits = extractDeferredBits(rMap);
|
|
xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_DOCKABLE|WB_MOVEABLE);
|
|
}
|
|
else if (name == "GtkCalendar")
|
|
{
|
|
WinBits nBits = extractDeferredBits(rMap);
|
|
xWindow = VclPtr<Calendar>::Create(pParent, nBits);
|
|
}
|
|
else
|
|
{
|
|
if (customMakeWidget pFunction = GetCustomMakeWidget(name))
|
|
{
|
|
pFunction(xWindow, pParent, rMap);
|
|
if (xWindow->GetType() == WindowType::PUSHBUTTON)
|
|
setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
|
|
else if (xWindow->GetType() == WindowType::MENUBUTTON)
|
|
{
|
|
OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
|
|
if (!sMenu.isEmpty())
|
|
m_pVclParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
|
|
setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
|
|
}
|
|
}
|
|
}
|
|
|
|
SAL_INFO_IF(!xWindow, "vcl.builder", "probably need to implement " << name << " or add a make" << name << " function");
|
|
if (xWindow)
|
|
{
|
|
// child windows of disabled windows are made disabled by vcl by default, we don't want that
|
|
WindowImpl *pWindowImpl = xWindow->ImplGetWindowImpl();
|
|
pWindowImpl->mbDisabled = false;
|
|
|
|
xWindow->SetHelpId(getHelpRoot() + id);
|
|
SAL_INFO("vcl.builder", "for name '" << name << "' and id '" << id <<
|
|
"', created " << xWindow.get() << " child of " <<
|
|
pParent << "(" << xWindow->ImplGetWindowImpl()->mpParent.get() << "/" <<
|
|
xWindow->ImplGetWindowImpl()->mpRealParent.get() << "/" <<
|
|
xWindow->ImplGetWindowImpl()->mpBorderWindow.get() << ") with helpid " <<
|
|
xWindow->GetHelpId());
|
|
m_aChildren.emplace_back(id, xWindow, bVertical);
|
|
|
|
// if the parent was a toolbox set it as an itemwindow for the latest itemid
|
|
if (pToolBox)
|
|
{
|
|
Size aSize(xWindow->GetSizePixel());
|
|
aSize.setHeight(xWindow->get_preferred_size().Height());
|
|
xWindow->SetSizePixel(aSize);
|
|
pToolBox->SetItemWindow(m_pVclParserState->m_nLastToolbarId, xWindow);
|
|
pToolBox->SetItemExpand(m_pVclParserState->m_nLastToolbarId, true);
|
|
}
|
|
}
|
|
return xWindow;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
//return true for window types which exist in vcl but are not themselves
|
|
//represented in the .ui format, i.e. only their children exist.
|
|
bool isConsideredGtkPseudo(vcl::Window const *pWindow)
|
|
{
|
|
return pWindow->GetType() == WindowType::TABPAGE;
|
|
}
|
|
}
|
|
|
|
//Any properties from .ui load we couldn't set because of potential virtual methods
|
|
//during ctor are applied here
|
|
void VclBuilder::setDeferredProperties()
|
|
{
|
|
if (!m_bToplevelHasDeferredProperties)
|
|
return;
|
|
stringmap aDeferredProperties;
|
|
aDeferredProperties.swap(m_aDeferredProperties);
|
|
m_bToplevelHasDeferredProperties = false;
|
|
BuilderUtils::set_properties(m_pParent, aDeferredProperties);
|
|
}
|
|
|
|
namespace BuilderUtils
|
|
{
|
|
void set_properties(vcl::Window *pWindow, const VclBuilder::stringmap &rProps)
|
|
{
|
|
for (auto const& [rKey, rValue] : rProps)
|
|
pWindow->set_property(rKey, rValue);
|
|
}
|
|
|
|
OUString convertMnemonicMarkup(std::u16string_view rIn)
|
|
{
|
|
OUStringBuffer aRet(rIn);
|
|
for (sal_Int32 nI = 0; nI < aRet.getLength(); ++nI)
|
|
{
|
|
if (aRet[nI] == '_' && nI+1 < aRet.getLength())
|
|
{
|
|
if (aRet[nI+1] != '_')
|
|
aRet[nI] = MNEMONIC_CHAR;
|
|
else
|
|
aRet.remove(nI, 1);
|
|
++nI;
|
|
}
|
|
}
|
|
return aRet.makeStringAndClear();
|
|
}
|
|
|
|
OUString extractCustomProperty(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sCustomProperty;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"customproperty"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
sCustomProperty = aFind->second;
|
|
rMap.erase(aFind);
|
|
}
|
|
return sCustomProperty;
|
|
}
|
|
|
|
void ensureDefaultWidthChars(VclBuilder::stringmap &rMap)
|
|
{
|
|
OUString sWidthChars(u"width-chars"_ustr);
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(sWidthChars);
|
|
if (aFind == rMap.end())
|
|
rMap[sWidthChars] = "20";
|
|
}
|
|
|
|
bool extractDropdown(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bDropdown = true;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"dropdown"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bDropdown = toBool(aFind->second);
|
|
rMap.erase(aFind);
|
|
}
|
|
return bDropdown;
|
|
}
|
|
|
|
void reorderWithinParent(vcl::Window &rWindow, sal_uInt16 nNewPosition)
|
|
{
|
|
WindowImpl *pWindowImpl = rWindow.ImplGetWindowImpl();
|
|
if (pWindowImpl->mpParent != pWindowImpl->mpRealParent)
|
|
{
|
|
assert(pWindowImpl->mpBorderWindow == pWindowImpl->mpParent);
|
|
assert(pWindowImpl->mpBorderWindow->ImplGetWindowImpl()->mpParent == pWindowImpl->mpRealParent);
|
|
reorderWithinParent(*pWindowImpl->mpBorderWindow, nNewPosition);
|
|
return;
|
|
}
|
|
rWindow.reorderWithinParent(nNewPosition);
|
|
}
|
|
|
|
void reorderWithinParent(std::vector<vcl::Window*>& rChilds, bool bIsButtonBox)
|
|
{
|
|
for (size_t i = 0; i < rChilds.size(); ++i)
|
|
{
|
|
reorderWithinParent(*rChilds[i], i);
|
|
|
|
if (!bIsButtonBox)
|
|
continue;
|
|
|
|
//The first member of the group for legacy code needs WB_GROUP set and the
|
|
//others not
|
|
WinBits nBits = rChilds[i]->GetStyle();
|
|
nBits &= ~WB_GROUP;
|
|
if (i == 0)
|
|
nBits |= WB_GROUP;
|
|
rChilds[i]->SetStyle(nBits);
|
|
}
|
|
}
|
|
|
|
sal_Int16 getRoleFromName(const OUString& roleName)
|
|
{
|
|
using namespace com::sun::star::accessibility;
|
|
|
|
static const std::unordered_map<OUString, sal_Int16> aAtkRoleToAccessibleRole = {
|
|
/* This is in atkobject.h's AtkRole order */
|
|
{ "invalid", AccessibleRole::UNKNOWN },
|
|
{ "accelerator label", AccessibleRole::UNKNOWN },
|
|
{ "alert", AccessibleRole::ALERT },
|
|
{ "animation", AccessibleRole::UNKNOWN },
|
|
{ "arrow", AccessibleRole::UNKNOWN },
|
|
{ "calendar", AccessibleRole::UNKNOWN },
|
|
{ "canvas", AccessibleRole::CANVAS },
|
|
{ "check box", AccessibleRole::CHECK_BOX },
|
|
{ "check menu item", AccessibleRole::CHECK_MENU_ITEM },
|
|
{ "color chooser", AccessibleRole::COLOR_CHOOSER },
|
|
{ "column header", AccessibleRole::COLUMN_HEADER },
|
|
{ "combo box", AccessibleRole::COMBO_BOX },
|
|
{ "date editor", AccessibleRole::DATE_EDITOR },
|
|
{ "desktop icon", AccessibleRole::DESKTOP_ICON },
|
|
{ "desktop frame", AccessibleRole::DESKTOP_PANE }, // ?
|
|
{ "dial", AccessibleRole::UNKNOWN },
|
|
{ "dialog", AccessibleRole::DIALOG },
|
|
{ "directory pane", AccessibleRole::DIRECTORY_PANE },
|
|
{ "drawing area", AccessibleRole::UNKNOWN },
|
|
{ "file chooser", AccessibleRole::FILE_CHOOSER },
|
|
{ "filler", AccessibleRole::FILLER },
|
|
{ "font chooser", AccessibleRole::FONT_CHOOSER },
|
|
{ "frame", AccessibleRole::FRAME },
|
|
{ "glass pane", AccessibleRole::GLASS_PANE },
|
|
{ "html container", AccessibleRole::UNKNOWN },
|
|
{ "icon", AccessibleRole::ICON },
|
|
{ "image", AccessibleRole::GRAPHIC },
|
|
{ "internal frame", AccessibleRole::INTERNAL_FRAME },
|
|
{ "label", AccessibleRole::LABEL },
|
|
{ "layered pane", AccessibleRole::LAYERED_PANE },
|
|
{ "list", AccessibleRole::LIST },
|
|
{ "list item", AccessibleRole::LIST_ITEM },
|
|
{ "menu", AccessibleRole::MENU },
|
|
{ "menu bar", AccessibleRole::MENU_BAR },
|
|
{ "menu item", AccessibleRole::MENU_ITEM },
|
|
{ "option pane", AccessibleRole::OPTION_PANE },
|
|
{ "page tab", AccessibleRole::PAGE_TAB },
|
|
{ "page tab list", AccessibleRole::PAGE_TAB_LIST },
|
|
{ "panel", AccessibleRole::PANEL }, // or SHAPE or TEXT_FRAME ?
|
|
{ "password text", AccessibleRole::PASSWORD_TEXT },
|
|
{ "popup menu", AccessibleRole::POPUP_MENU },
|
|
{ "progress bar", AccessibleRole::PROGRESS_BAR },
|
|
{ "push button", AccessibleRole::PUSH_BUTTON }, // or BUTTON_DROPDOWN or BUTTON_MENU
|
|
{ "radio button", AccessibleRole::RADIO_BUTTON },
|
|
{ "radio menu item", AccessibleRole::RADIO_MENU_ITEM },
|
|
{ "root pane", AccessibleRole::ROOT_PANE },
|
|
{ "row header", AccessibleRole::ROW_HEADER },
|
|
{ "scroll bar", AccessibleRole::SCROLL_BAR },
|
|
{ "scroll pane", AccessibleRole::SCROLL_PANE },
|
|
{ "separator", AccessibleRole::SEPARATOR },
|
|
{ "slider", AccessibleRole::SLIDER },
|
|
{ "split pane", AccessibleRole::SPLIT_PANE },
|
|
{ "spin button", AccessibleRole::SPIN_BOX }, // ?
|
|
{ "statusbar", AccessibleRole::STATUS_BAR },
|
|
{ "table", AccessibleRole::TABLE },
|
|
{ "table cell", AccessibleRole::TABLE_CELL },
|
|
{ "table column header", AccessibleRole::COLUMN_HEADER }, // approximate
|
|
{ "table row header", AccessibleRole::ROW_HEADER }, // approximate
|
|
{ "tear off menu item", AccessibleRole::UNKNOWN },
|
|
{ "terminal", AccessibleRole::UNKNOWN },
|
|
{ "text", AccessibleRole::TEXT },
|
|
{ "toggle button", AccessibleRole::TOGGLE_BUTTON },
|
|
{ "tool bar", AccessibleRole::TOOL_BAR },
|
|
{ "tool tip", AccessibleRole::TOOL_TIP },
|
|
{ "tree", AccessibleRole::TREE },
|
|
{ "tree table", AccessibleRole::TREE_TABLE },
|
|
{ "unknown", AccessibleRole::UNKNOWN },
|
|
{ "viewport", AccessibleRole::VIEW_PORT },
|
|
{ "window", AccessibleRole::WINDOW },
|
|
{ "header", AccessibleRole::HEADER },
|
|
{ "footer", AccessibleRole::FOOTER },
|
|
{ "paragraph", AccessibleRole::PARAGRAPH },
|
|
{ "ruler", AccessibleRole::RULER },
|
|
{ "application", AccessibleRole::UNKNOWN },
|
|
{ "autocomplete", AccessibleRole::UNKNOWN },
|
|
{ "edit bar", AccessibleRole::EDIT_BAR },
|
|
{ "embedded", AccessibleRole::EMBEDDED_OBJECT },
|
|
{ "entry", AccessibleRole::UNKNOWN },
|
|
{ "chart", AccessibleRole::CHART },
|
|
{ "caption", AccessibleRole::CAPTION },
|
|
{ "document frame", AccessibleRole::DOCUMENT },
|
|
{ "heading", AccessibleRole::HEADING },
|
|
{ "page", AccessibleRole::PAGE },
|
|
{ "section", AccessibleRole::SECTION },
|
|
{ "redundant object", AccessibleRole::UNKNOWN },
|
|
{ "form", AccessibleRole::FORM },
|
|
{ "link", AccessibleRole::HYPER_LINK },
|
|
{ "input method window", AccessibleRole::UNKNOWN },
|
|
{ "table row", AccessibleRole::UNKNOWN },
|
|
{ "tree item", AccessibleRole::TREE_ITEM },
|
|
{ "document spreadsheet", AccessibleRole::DOCUMENT_SPREADSHEET },
|
|
{ "document presentation", AccessibleRole::DOCUMENT_PRESENTATION },
|
|
{ "document text", AccessibleRole::DOCUMENT_TEXT },
|
|
{ "document web", AccessibleRole::DOCUMENT }, // approximate
|
|
{ "document email", AccessibleRole::DOCUMENT }, // approximate
|
|
{ "comment", AccessibleRole::COMMENT }, // or NOTE or END_NOTE or FOOTNOTE or SCROLL_PANE
|
|
{ "list box", AccessibleRole::UNKNOWN },
|
|
{ "grouping", AccessibleRole::GROUP_BOX },
|
|
{ "image map", AccessibleRole::IMAGE_MAP },
|
|
{ "notification", AccessibleRole::NOTIFICATION },
|
|
{ "info bar", AccessibleRole::UNKNOWN },
|
|
{ "level bar", AccessibleRole::UNKNOWN },
|
|
{ "title bar", AccessibleRole::UNKNOWN },
|
|
{ "block quote", AccessibleRole::BLOCK_QUOTE },
|
|
{ "audio", AccessibleRole::UNKNOWN },
|
|
{ "video", AccessibleRole::UNKNOWN },
|
|
{ "definition", AccessibleRole::UNKNOWN },
|
|
{ "article", AccessibleRole::UNKNOWN },
|
|
{ "landmark", AccessibleRole::UNKNOWN },
|
|
{ "log", AccessibleRole::UNKNOWN },
|
|
{ "marquee", AccessibleRole::UNKNOWN },
|
|
{ "math", AccessibleRole::UNKNOWN },
|
|
{ "rating", AccessibleRole::UNKNOWN },
|
|
{ "timer", AccessibleRole::UNKNOWN },
|
|
{ "description list", AccessibleRole::UNKNOWN },
|
|
{ "description term", AccessibleRole::UNKNOWN },
|
|
{ "description value", AccessibleRole::UNKNOWN },
|
|
{ "static", AccessibleRole::STATIC },
|
|
{ "math fraction", AccessibleRole::UNKNOWN },
|
|
{ "math root", AccessibleRole::UNKNOWN },
|
|
{ "subscript", AccessibleRole::UNKNOWN },
|
|
{ "superscript", AccessibleRole::UNKNOWN },
|
|
{ "footnote", AccessibleRole::FOOTNOTE },
|
|
};
|
|
|
|
auto it = aAtkRoleToAccessibleRole.find(roleName);
|
|
if (it == aAtkRoleToAccessibleRole.end())
|
|
return AccessibleRole::UNKNOWN;
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
VclPtr<vcl::Window> VclBuilder::insertObject(vcl::Window *pParent, const OUString &rClass,
|
|
const OUString &rID, stringmap &rProps, stringmap &rPango, stringmap &rAtk)
|
|
{
|
|
VclPtr<vcl::Window> pCurrentChild;
|
|
|
|
if (m_pParent && !isConsideredGtkPseudo(m_pParent) && !m_sID.isEmpty() && rID == m_sID)
|
|
{
|
|
pCurrentChild = m_pParent;
|
|
|
|
//toplevels default to resizable and apparently you can't change them
|
|
//afterwards, so we need to wait until now before we can truly
|
|
//initialize the dialog.
|
|
if (pParent && pParent->IsSystemWindow())
|
|
{
|
|
SystemWindow *pSysWin = static_cast<SystemWindow*>(pCurrentChild.get());
|
|
pSysWin->doDeferredInit(extractDeferredBits(rProps));
|
|
m_bToplevelHasDeferredInit = false;
|
|
}
|
|
else if (pParent && pParent->IsDockingWindow())
|
|
{
|
|
DockingWindow *pDockWin = static_cast<DockingWindow*>(pCurrentChild.get());
|
|
pDockWin->doDeferredInit(extractDeferredBits(rProps));
|
|
m_bToplevelHasDeferredInit = false;
|
|
}
|
|
|
|
if (pCurrentChild->GetHelpId().isEmpty())
|
|
{
|
|
pCurrentChild->SetHelpId(getHelpRoot() + m_sID);
|
|
SAL_INFO("vcl.builder", "for toplevel dialog " << this << " " <<
|
|
rID << ", set helpid " << pCurrentChild->GetHelpId());
|
|
}
|
|
m_bToplevelParentFound = true;
|
|
}
|
|
else
|
|
{
|
|
//if we're being inserting under a toplevel dialog whose init is
|
|
//deferred due to waiting to encounter it in this .ui, and it hasn't
|
|
//been seen yet, then make unattached widgets parent-less toplevels
|
|
if (pParent == m_pParent.get() && m_bToplevelHasDeferredInit)
|
|
pParent = nullptr;
|
|
pCurrentChild = makeObject(pParent, rClass, rID, rProps);
|
|
}
|
|
|
|
if (pCurrentChild)
|
|
{
|
|
pCurrentChild->set_id(rID);
|
|
if (pCurrentChild == m_pParent.get() && m_bToplevelHasDeferredProperties)
|
|
m_aDeferredProperties = rProps;
|
|
else
|
|
BuilderUtils::set_properties(pCurrentChild, rProps);
|
|
|
|
// tdf#119827 handle size before scale so we can trivially
|
|
// scale on the current font size whether size is present
|
|
// or not.
|
|
VclBuilder::stringmap::iterator aSize = rPango.find(u"size"_ustr);
|
|
if (aSize != rPango.end())
|
|
{
|
|
pCurrentChild->set_font_attribute(aSize->first, aSize->second);
|
|
rPango.erase(aSize);
|
|
}
|
|
for (auto const& [ rKey, rValue ] : rPango)
|
|
pCurrentChild->set_font_attribute(rKey, rValue);
|
|
|
|
m_pVclParserState->m_aAtkInfo[pCurrentChild] = rAtk;
|
|
}
|
|
|
|
rProps.clear();
|
|
rPango.clear();
|
|
rAtk.clear();
|
|
|
|
if (!pCurrentChild)
|
|
{
|
|
bool bToolbarParent = (pParent && pParent->GetType() == WindowType::TOOLBOX);
|
|
pCurrentChild = (m_aChildren.empty() || bToolbarParent) ? pParent : m_aChildren.back().m_pWindow.get();
|
|
}
|
|
return pCurrentChild;
|
|
}
|
|
|
|
void VclBuilder::handleTabChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
|
|
{
|
|
TabControl *pTabControl = pParent && pParent->GetType() == WindowType::TABCONTROL ?
|
|
static_cast<TabControl*>(pParent) : nullptr;
|
|
|
|
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 (pTabControl && name == "child")
|
|
{
|
|
// 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;
|
|
|
|
VerticalTabControl *pVerticalTabControl = pParent->GetType() == WindowType::VERTICALTABCONTROL ?
|
|
static_cast<VerticalTabControl*>(pParent) : nullptr;
|
|
assert(pTabControl || pVerticalTabControl);
|
|
VclBuilder::stringmap::iterator aFind = aProperties.find(u"label"_ustr);
|
|
if (aFind != aProperties.end())
|
|
{
|
|
OUString sTooltip(extractTooltipText(aProperties));
|
|
if (pTabControl)
|
|
{
|
|
sal_uInt16 nPageId = pTabControl->GetCurPageId();
|
|
pTabControl->SetPageText(nPageId, aFind->second);
|
|
pTabControl->SetPageName(nPageId, sIDs.back());
|
|
pTabControl->SetHelpText(nPageId, sTooltip);
|
|
if (!context.empty())
|
|
{
|
|
TabPage* pPage = pTabControl->GetTabPage(nPageId);
|
|
pPage->SetContext(std::move(context));
|
|
}
|
|
|
|
for (auto const& [ rKey, rValue ] : aAtkProperties)
|
|
{
|
|
if (rKey == "AtkObject::accessible-name")
|
|
pTabControl->SetAccessibleName(nPageId, rValue);
|
|
else if (rKey == "AtkObject::accessible-description")
|
|
pTabControl->SetAccessibleDescription(nPageId, rValue);
|
|
else
|
|
SAL_INFO("vcl.builder", "unhandled atk property: " << rKey);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
OUString sLabel(BuilderUtils::convertMnemonicMarkup(aFind->second));
|
|
OUString sIconName(extractIconName(aProperties));
|
|
pVerticalTabControl->InsertPage(sIDs.front(), sLabel, FixedImage::loadThemeImage(sIconName), sTooltip,
|
|
pVerticalTabControl->GetPageParent()->GetWindow(GetWindowType::LastChild));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pTabControl)
|
|
pTabControl->RemovePage(pTabControl->GetCurPageId());
|
|
}
|
|
}
|
|
|
|
//so that tabbing between controls goes in a visually sensible sequence
|
|
//we sort these into a best-tab-order sequence
|
|
bool VclBuilder::sortIntoBestTabTraversalOrder::operator()(const vcl::Window *pA, const vcl::Window *pB) const
|
|
{
|
|
//sort child order within parent list by grid position
|
|
sal_Int32 nTopA = pA->get_grid_top_attach();
|
|
sal_Int32 nTopB = pB->get_grid_top_attach();
|
|
if (nTopA < nTopB)
|
|
return true;
|
|
if (nTopA > nTopB)
|
|
return false;
|
|
sal_Int32 nLeftA = pA->get_grid_left_attach();
|
|
sal_Int32 nLeftB = pB->get_grid_left_attach();
|
|
if (nLeftA < nLeftB)
|
|
return true;
|
|
if (nLeftA > nLeftB)
|
|
return false;
|
|
//sort into two groups of pack start and pack end
|
|
VclPackType ePackA = pA->get_pack_type();
|
|
VclPackType ePackB = pB->get_pack_type();
|
|
if (ePackA < ePackB)
|
|
return true;
|
|
if (ePackA > ePackB)
|
|
return false;
|
|
bool bVerticalContainer = m_pBuilder->get_window_packing_data(pA->GetParent()).m_bVerticalOrient;
|
|
bool bPackA = pA->get_secondary();
|
|
bool bPackB = pB->get_secondary();
|
|
if (!bVerticalContainer)
|
|
{
|
|
//for horizontal boxes group secondaries before primaries
|
|
if (bPackA > bPackB)
|
|
return true;
|
|
if (bPackA < bPackB)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
//for vertical boxes group secondaries after primaries
|
|
if (bPackA < bPackB)
|
|
return true;
|
|
if (bPackA > bPackB)
|
|
return false;
|
|
}
|
|
//honour relative box positions with pack group, (numerical order is reversed
|
|
//for VclPackType::End, they are packed from the end back, but here we need
|
|
//them in visual layout order so that tabbing works as expected)
|
|
sal_Int32 nPackA = m_pBuilder->get_window_packing_data(pA).m_nPosition;
|
|
sal_Int32 nPackB = m_pBuilder->get_window_packing_data(pB).m_nPosition;
|
|
if (nPackA < nPackB)
|
|
return ePackA == VclPackType::Start;
|
|
if (nPackA > nPackB)
|
|
return ePackA != VclPackType::Start;
|
|
//sort labels of Frames before body
|
|
if (pA->GetParent() == pB->GetParent())
|
|
{
|
|
const VclFrame *pFrameParent = dynamic_cast<const VclFrame*>(pA->GetParent());
|
|
if (pFrameParent)
|
|
{
|
|
const vcl::Window *pLabel = pFrameParent->get_label_widget();
|
|
int nFramePosA = (pA == pLabel) ? 0 : 1;
|
|
int nFramePosB = (pB == pLabel) ? 0 : 1;
|
|
return nFramePosA < nFramePosB;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void VclBuilder::tweakInsertedChild(vcl::Window *pParent, vcl::Window* pCurrentChild,
|
|
std::string_view sType, std::string_view sInternalChild)
|
|
{
|
|
assert(pCurrentChild);
|
|
|
|
//Internal-children default in glade to not having their visible bits set
|
|
//even though they are visible (generally anyway)
|
|
if (!sInternalChild.empty())
|
|
pCurrentChild->Show();
|
|
|
|
//Select the first page if it's a notebook
|
|
if (pCurrentChild->GetType() == WindowType::TABCONTROL)
|
|
{
|
|
TabControl *pTabControl = static_cast<TabControl*>(pCurrentChild);
|
|
pTabControl->SetCurPageId(pTabControl->GetPageId(0));
|
|
|
|
//To-Do add reorder capability to the TabControl
|
|
}
|
|
else
|
|
{
|
|
// We want to sort labels before contents of frames
|
|
// for keyboard traversal, especially if there
|
|
// are multiple widgets using the same mnemonic
|
|
if (sType == "label")
|
|
{
|
|
if (VclFrame *pFrameParent = dynamic_cast<VclFrame*>(pParent))
|
|
pFrameParent->designate_label(pCurrentChild);
|
|
}
|
|
if (sInternalChild.starts_with("vbox") || sInternalChild.starts_with("messagedialog-vbox"))
|
|
{
|
|
if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pParent))
|
|
pBoxParent->set_content_area(static_cast<VclBox*>(pCurrentChild)); // FIXME-VCLPTR
|
|
}
|
|
else if (sInternalChild.starts_with("action_area") || sInternalChild.starts_with("messagedialog-action_area"))
|
|
{
|
|
vcl::Window *pContentArea = pCurrentChild->GetParent();
|
|
if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pContentArea ? pContentArea->GetParent() : nullptr))
|
|
{
|
|
pBoxParent->set_action_area(static_cast<VclButtonBox*>(pCurrentChild)); // FIXME-VCLPTR
|
|
}
|
|
}
|
|
|
|
bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pCurrentChild) != nullptr;
|
|
|
|
//To-Do make reorder a virtual in Window, move this foo
|
|
//there and see above
|
|
std::vector<vcl::Window*> aChilds;
|
|
for (vcl::Window* pChild = pCurrentChild->GetWindow(GetWindowType::FirstChild); pChild;
|
|
pChild = pChild->GetWindow(GetWindowType::Next))
|
|
{
|
|
if (bIsButtonBox)
|
|
{
|
|
if (PushButton* pPushButton = dynamic_cast<PushButton*>(pChild))
|
|
pPushButton->setAction(true);
|
|
}
|
|
|
|
aChilds.push_back(pChild);
|
|
}
|
|
|
|
//sort child order within parent so that tabbing
|
|
//between controls goes in a visually sensible sequence
|
|
std::stable_sort(aChilds.begin(), aChilds.end(), sortIntoBestTabTraversalOrder(this));
|
|
BuilderUtils::reorderWithinParent(aChilds, bIsButtonBox);
|
|
}
|
|
}
|
|
|
|
void BuilderBase::collectPangoAttribute(xmlreader::XmlReader& reader, stringmap& rMap)
|
|
{
|
|
xmlreader::Span span;
|
|
int nsId;
|
|
|
|
OUString sProperty;
|
|
OUString sValue;
|
|
|
|
while (reader.nextAttribute(&nsId, &span))
|
|
{
|
|
if (span == "name")
|
|
{
|
|
span = reader.getAttributeValue(false);
|
|
sProperty = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
else if (span == "value")
|
|
{
|
|
span = reader.getAttributeValue(false);
|
|
sValue = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
}
|
|
|
|
if (!sProperty.isEmpty())
|
|
rMap[sProperty] = sValue;
|
|
}
|
|
|
|
void BuilderBase::collectAtkRelationAttribute(xmlreader::XmlReader& reader, stringmap& rMap)
|
|
{
|
|
xmlreader::Span span;
|
|
int nsId;
|
|
|
|
OUString sProperty;
|
|
OUString sValue;
|
|
|
|
while (reader.nextAttribute(&nsId, &span))
|
|
{
|
|
if (span == "type")
|
|
{
|
|
span = reader.getAttributeValue(false);
|
|
sProperty = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
else if (span == "target")
|
|
{
|
|
span = reader.getAttributeValue(false);
|
|
sValue = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
|
|
sal_Int32 nDelim = sValue.indexOf(':');
|
|
if (nDelim != -1)
|
|
sValue = sValue.copy(0, nDelim);
|
|
}
|
|
}
|
|
|
|
if (!sProperty.isEmpty())
|
|
rMap[sProperty] = sValue;
|
|
}
|
|
|
|
void BuilderBase::collectAtkRoleAttribute(xmlreader::XmlReader& reader, stringmap& rMap)
|
|
{
|
|
xmlreader::Span span;
|
|
int nsId;
|
|
|
|
OUString sProperty;
|
|
|
|
while (reader.nextAttribute(&nsId, &span))
|
|
{
|
|
if (span == "type")
|
|
{
|
|
span = reader.getAttributeValue(false);
|
|
sProperty = OUString(span.begin, span.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
}
|
|
|
|
if (!sProperty.isEmpty())
|
|
rMap[u"role"_ustr] = sProperty;
|
|
}
|
|
|
|
void BuilderBase::handleRow(xmlreader::XmlReader& reader, const OUString& rID)
|
|
{
|
|
int nLevel = 1;
|
|
|
|
ListStore::row aRow;
|
|
|
|
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)
|
|
{
|
|
++nLevel;
|
|
if (name == "col")
|
|
{
|
|
bool bTranslated = false;
|
|
sal_uInt32 nId = 0;
|
|
OString sContext;
|
|
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "id")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
nId = o3tl::toUInt32(std::string_view(name.begin, name.length));
|
|
}
|
|
else if (nId == 0 && name == "translatable" && reader.getAttributeValue(false) == "yes")
|
|
{
|
|
bTranslated = true;
|
|
}
|
|
else if (name == "context")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sContext = OString(name.begin, name.length);
|
|
}
|
|
}
|
|
|
|
(void)reader.nextItem(
|
|
xmlreader::XmlReader::Text::Raw, &name, &nsId);
|
|
|
|
OString sValue(name.begin, name.length);
|
|
OUString sFinalValue;
|
|
if (bTranslated)
|
|
{
|
|
sFinalValue = Translate::get(TranslateId{ sContext.getStr(), sValue.getStr() },
|
|
getResLocale());
|
|
}
|
|
else
|
|
sFinalValue = OUString::fromUtf8(sValue);
|
|
|
|
|
|
if (aRow.size() < nId+1)
|
|
aRow.resize(nId+1);
|
|
aRow[nId] = sFinalValue;
|
|
}
|
|
}
|
|
|
|
if (res == xmlreader::XmlReader::Result::End)
|
|
{
|
|
--nLevel;
|
|
}
|
|
|
|
if (!nLevel)
|
|
break;
|
|
}
|
|
|
|
m_pParserState->m_aModels[rID].m_aEntries.push_back(aRow);
|
|
}
|
|
|
|
void BuilderBase::handleListStore(xmlreader::XmlReader& reader, const OUString& rID, std::u16string_view rClass)
|
|
{
|
|
int nLevel = 1;
|
|
|
|
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 == "row")
|
|
{
|
|
bool bNotTreeStore = rClass != u"GtkTreeStore";
|
|
if (bNotTreeStore)
|
|
handleRow(reader, rID);
|
|
assert(bNotTreeStore && "gtk, as the time of writing, doesn't support data in GtkTreeStore serialization");
|
|
}
|
|
else
|
|
++nLevel;
|
|
}
|
|
|
|
if (res == xmlreader::XmlReader::Result::End)
|
|
{
|
|
--nLevel;
|
|
}
|
|
|
|
if (!nLevel)
|
|
break;
|
|
}
|
|
}
|
|
|
|
BuilderBase::stringmap BuilderBase::handleAtkObject(xmlreader::XmlReader& reader) const
|
|
{
|
|
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)
|
|
{
|
|
++nLevel;
|
|
if (name == "property")
|
|
collectProperty(reader, aProperties);
|
|
}
|
|
|
|
if (res == xmlreader::XmlReader::Result::End)
|
|
{
|
|
--nLevel;
|
|
}
|
|
|
|
if (!nLevel)
|
|
break;
|
|
}
|
|
|
|
return aProperties;
|
|
}
|
|
|
|
void VclBuilder::applyAtkProperties(vcl::Window *pWindow, const stringmap& rProperties, bool bToolbarItem)
|
|
{
|
|
assert(pWindow);
|
|
for (auto const& [ rKey, rValue ] : rProperties)
|
|
{
|
|
if (bToolbarItem)
|
|
{
|
|
// apply property to the corresponding toolbar item (which is not a vcl::Window itself)
|
|
// rather than the toolbar itself
|
|
ToolBox* pToolBox = dynamic_cast<ToolBox*>(pWindow);
|
|
if (pToolBox)
|
|
{
|
|
if (rKey == u"AtkObject::accessible-name")
|
|
pToolBox->SetAccessibleName(m_pVclParserState->m_nLastToolbarId, rValue);
|
|
}
|
|
}
|
|
else if (pWindow && rKey.match("AtkObject::"))
|
|
pWindow->set_property(rKey.copy(RTL_CONSTASCII_LENGTH("AtkObject::")), rValue);
|
|
else
|
|
SAL_WARN("vcl.builder", "unhandled atk prop: " << rKey);
|
|
}
|
|
}
|
|
|
|
void VclBuilder::setPriority(vcl::Window* pWindow, int nPriority)
|
|
{
|
|
vcl::IPrioritable* pPrioritable = dynamic_cast<vcl::IPrioritable*>(pWindow);
|
|
SAL_WARN_IF(!pPrioritable, "vcl", "priority set for not supported item");
|
|
if (pPrioritable)
|
|
pPrioritable->SetPriority(nPriority);
|
|
}
|
|
void VclBuilder::setContext(vcl::Window* pWindow, std::vector<vcl::EnumContext::Context>&& aContext)
|
|
{
|
|
vcl::IContext* pContextControl = dynamic_cast<vcl::IContext*>(pWindow);
|
|
SAL_WARN_IF(!pContextControl, "vcl", "context set for not supported item");
|
|
if (pContextControl)
|
|
pContextControl->SetContext(std::move(aContext));
|
|
}
|
|
|
|
std::vector<ComboBoxTextItem> BuilderBase::handleItems(xmlreader::XmlReader& reader) const
|
|
{
|
|
int nLevel = 1;
|
|
|
|
std::vector<ComboBoxTextItem> aItems;
|
|
|
|
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)
|
|
{
|
|
++nLevel;
|
|
if (name == "item")
|
|
{
|
|
bool bTranslated = false;
|
|
OString sContext;
|
|
OUString sId;
|
|
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "translatable" && reader.getAttributeValue(false) == "yes")
|
|
{
|
|
bTranslated = true;
|
|
}
|
|
else if (name == "context")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sContext = OString(name.begin, name.length);
|
|
}
|
|
else if (name == "id")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sId = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
}
|
|
|
|
(void)reader.nextItem(
|
|
xmlreader::XmlReader::Text::Raw, &name, &nsId);
|
|
|
|
OString sValue(name.begin, name.length);
|
|
const OUString sFinalValue = finalizeValue(sContext, sValue, bTranslated);
|
|
aItems.emplace_back(sFinalValue, sId);
|
|
}
|
|
}
|
|
|
|
if (res == xmlreader::XmlReader::Result::End)
|
|
{
|
|
--nLevel;
|
|
}
|
|
|
|
if (!nLevel)
|
|
break;
|
|
}
|
|
|
|
return aItems;
|
|
}
|
|
|
|
void VclBuilder::handleMenu(xmlreader::XmlReader& reader, vcl::Window* pParent, const OUString& rID,
|
|
bool bMenuBar)
|
|
{
|
|
VclPtr<Menu> pCurrentMenu;
|
|
if (bMenuBar)
|
|
pCurrentMenu = VclPtr<MenuBar>::Create();
|
|
else
|
|
pCurrentMenu = VclPtr<PopupMenu>::Create();
|
|
|
|
pCurrentMenu->set_id(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);
|
|
|
|
if (bMenuBar && pParent)
|
|
{
|
|
if (SystemWindow* pTopLevel = pParent->GetSystemWindow())
|
|
pTopLevel->SetMenuBar(dynamic_cast<MenuBar*>(pCurrentMenu.get()));
|
|
}
|
|
}
|
|
|
|
void VclBuilder::handleMenuChild(Menu *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 VclBuilder::handleMenuObject(Menu *pParent, xmlreader::XmlReader &reader)
|
|
{
|
|
OUString sClass;
|
|
OUString sID;
|
|
OUString sCustomProperty;
|
|
PopupMenu *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 = dynamic_cast<PopupMenu*>(m_aMenus[nChildMenuIdx].m_pMenu.get());
|
|
}
|
|
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);
|
|
}
|
|
|
|
void BuilderBase::handleSizeGroup(xmlreader::XmlReader& reader)
|
|
{
|
|
m_pParserState->m_aSizeGroups.emplace_back();
|
|
SizeGroup &rSizeGroup = m_pParserState->m_aSizeGroups.back();
|
|
|
|
int nLevel = 1;
|
|
|
|
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)
|
|
{
|
|
++nLevel;
|
|
if (name == "widget")
|
|
{
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "name")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
OUString sWidget(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
sal_Int32 nDelim = sWidget.indexOf(':');
|
|
if (nDelim != -1)
|
|
sWidget = sWidget.copy(0, nDelim);
|
|
rSizeGroup.m_aWidgets.push_back(sWidget);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (name == "property")
|
|
collectProperty(reader, rSizeGroup.m_aProperties);
|
|
}
|
|
}
|
|
|
|
if (res == xmlreader::XmlReader::Result::End)
|
|
{
|
|
--nLevel;
|
|
}
|
|
|
|
if (!nLevel)
|
|
break;
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
vcl::KeyCode makeKeyCode(const std::pair<OUString,OUString> &rKey)
|
|
{
|
|
bool bShift = rKey.second.indexOf("GDK_SHIFT_MASK") != -1;
|
|
bool bMod1 = rKey.second.indexOf("GDK_CONTROL_MASK") != -1;
|
|
bool bMod2 = rKey.second.indexOf("GDK_ALT_MASK") != -1;
|
|
bool bMod3 = rKey.second.indexOf("GDK_MOD2_MASK") != -1;
|
|
|
|
if (rKey.first == "Insert")
|
|
return vcl::KeyCode(KEY_INSERT, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first == "Delete")
|
|
return vcl::KeyCode(KEY_DELETE, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first == "Return")
|
|
return vcl::KeyCode(KEY_RETURN, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first == "Up")
|
|
return vcl::KeyCode(KEY_UP, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first == "Down")
|
|
return vcl::KeyCode(KEY_DOWN, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first == "Left")
|
|
return vcl::KeyCode(KEY_LEFT, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first == "Right")
|
|
return vcl::KeyCode(KEY_RIGHT, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first == "asterisk")
|
|
return vcl::KeyCode(KEY_MULTIPLY, bShift, bMod1, bMod2, bMod3);
|
|
else if (rKey.first.getLength() > 1 && rKey.first[0] == 'F')
|
|
{
|
|
sal_uInt32 nIndex = o3tl::toUInt32(rKey.first.subView(1));
|
|
assert(nIndex >= 1 && nIndex <= 26);
|
|
return vcl::KeyCode(KEY_F1 + nIndex - 1, bShift, bMod1, bMod2, bMod3);
|
|
}
|
|
|
|
assert (rKey.first.getLength() == 1);
|
|
sal_Unicode cChar = rKey.first.toChar();
|
|
|
|
if (cChar >= 'a' && cChar <= 'z')
|
|
return vcl::KeyCode(KEY_A + (cChar - 'a'), bShift, bMod1, bMod2, bMod3);
|
|
else if (cChar >= 'A' && cChar <= 'Z')
|
|
return vcl::KeyCode(KEY_A + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
|
|
else if (cChar >= '0' && cChar <= '9')
|
|
return vcl::KeyCode(KEY_0 + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
|
|
|
|
return vcl::KeyCode(cChar, bShift, bMod1, bMod2, bMod3);
|
|
}
|
|
}
|
|
|
|
void VclBuilder::insertMenuObject(Menu *pParent, PopupMenu *pSubMenu, const OUString &rClass, const OUString &rID,
|
|
stringmap &rProps, stringmap &rAtkProps, accelmap &rAccels)
|
|
{
|
|
sal_uInt16 nOldCount = pParent->GetItemCount();
|
|
sal_uInt16 nNewId = ++m_pVclParserState->m_nLastMenuItemId;
|
|
|
|
if(rClass == "NotebookBarAddonsMenuMergePoint")
|
|
{
|
|
NotebookBarAddonsMerger::MergeNotebookBarMenuAddons(pParent, nNewId, rID, *m_pNotebookBarAddonsItem);
|
|
m_pVclParserState->m_nLastMenuItemId = pParent->GetItemCount();
|
|
}
|
|
else if (rClass == "GtkMenuItem")
|
|
{
|
|
OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
|
|
OUString aCommand(extractActionName(rProps));
|
|
pParent->InsertItem(nNewId, sLabel, MenuItemBits::NONE , rID);
|
|
pParent->SetItemCommand(nNewId, aCommand);
|
|
if (pSubMenu)
|
|
pParent->SetPopupMenu(nNewId, pSubMenu);
|
|
}
|
|
else if (rClass == "GtkCheckMenuItem")
|
|
{
|
|
OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
|
|
OUString aCommand(extractActionName(rProps));
|
|
pParent->InsertItem(nNewId, sLabel, MenuItemBits::CHECKABLE, rID);
|
|
pParent->SetItemCommand(nNewId, aCommand);
|
|
}
|
|
else if (rClass == "GtkRadioMenuItem")
|
|
{
|
|
OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
|
|
OUString aCommand(extractActionName(rProps));
|
|
pParent->InsertItem(nNewId, sLabel, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, rID);
|
|
pParent->SetItemCommand(nNewId, aCommand);
|
|
}
|
|
else if (rClass == "GtkSeparatorMenuItem")
|
|
{
|
|
pParent->InsertSeparator(rID);
|
|
}
|
|
|
|
SAL_WARN_IF(nOldCount == pParent->GetItemCount(), "vcl.builder", "probably need to implement " << rClass);
|
|
|
|
if (nOldCount != pParent->GetItemCount())
|
|
{
|
|
pParent->SetHelpId(nNewId, getHelpRoot() + rID);
|
|
if (!extractVisible(rProps))
|
|
pParent->HideItem(nNewId);
|
|
|
|
for (auto const& [ rKey, rValue ] : rProps)
|
|
{
|
|
if (rKey == "tooltip-markup")
|
|
pParent->SetTipHelpText(nNewId, rValue);
|
|
else if (rKey == "tooltip-text")
|
|
pParent->SetTipHelpText(nNewId, rValue);
|
|
else
|
|
SAL_INFO("vcl.builder", "unhandled property: " << rKey);
|
|
}
|
|
|
|
for (auto const& [ rKey, rValue ] : rAtkProps)
|
|
{
|
|
if (rKey == "AtkObject::accessible-name")
|
|
pParent->SetAccessibleName(nNewId, rValue);
|
|
else if (rKey == "AtkObject::accessible-description")
|
|
pParent->SetAccessibleDescription(nNewId, rValue);
|
|
else
|
|
SAL_INFO("vcl.builder", "unhandled atk property: " << rKey);
|
|
}
|
|
|
|
for (auto const& [ rSignal, rValue ] : rAccels)
|
|
{
|
|
if (rSignal == "activate")
|
|
pParent->SetAccelKey(nNewId, makeKeyCode(rValue));
|
|
else
|
|
SAL_INFO("vcl.builder", "unhandled accelerator for: " << rSignal);
|
|
}
|
|
}
|
|
|
|
rProps.clear();
|
|
}
|
|
|
|
/// Insert items to a ComboBox or a ListBox.
|
|
/// They have no common ancestor that would have 'InsertEntry()', so use a template.
|
|
template<typename T> static bool insertItems(vcl::Window *pWindow, VclBuilder::stringmap &rMap,
|
|
std::vector<std::unique_ptr<OUString>>& rUserData,
|
|
const std::vector<ComboBoxTextItem> &rItems)
|
|
{
|
|
T *pContainer = dynamic_cast<T*>(pWindow);
|
|
if (!pContainer)
|
|
return false;
|
|
|
|
sal_uInt16 nActiveId = extractActive(rMap);
|
|
for (auto const& item : rItems)
|
|
{
|
|
sal_Int32 nPos = pContainer->InsertEntry(item.m_sItem);
|
|
if (!item.m_sId.isEmpty())
|
|
{
|
|
rUserData.emplace_back(std::make_unique<OUString>(item.m_sId));
|
|
pContainer->SetEntryData(nPos, rUserData.back().get());
|
|
}
|
|
}
|
|
if (nActiveId < rItems.size())
|
|
pContainer->SelectEntryPos(nActiveId);
|
|
|
|
return true;
|
|
}
|
|
|
|
void BuilderBase::extractClassAndIdAndCustomProperty(xmlreader::XmlReader& reader, OUString& rClass,
|
|
OUString& rId, OUString& rCustomProperty)
|
|
{
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "class")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
rClass = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
else if (name == "id")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
rId = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
if (isLegacy())
|
|
{
|
|
sal_Int32 nDelim = rId.indexOf(':');
|
|
if (nDelim != -1)
|
|
{
|
|
rCustomProperty = rId.subView(nDelim+1);
|
|
rId = rId.copy(0, nDelim);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BuilderBase::handleInterfaceDomain(xmlreader::XmlReader& rReader)
|
|
{
|
|
xmlreader::Span name = rReader.getAttributeValue(false);
|
|
const OString sPrefixName(name.begin, name.length);
|
|
m_pParserState->m_aResLocale = Translate::Create(sPrefixName);
|
|
}
|
|
|
|
BuilderBase::stringmap BuilderBase::collectPackingProperties(xmlreader::XmlReader& reader)
|
|
{
|
|
int nLevel = 1;
|
|
stringmap aPackingProperties;
|
|
|
|
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)
|
|
{
|
|
++nLevel;
|
|
if (name == "property")
|
|
collectProperty(reader, aPackingProperties);
|
|
}
|
|
|
|
if (res == xmlreader::XmlReader::Result::End)
|
|
{
|
|
--nLevel;
|
|
}
|
|
|
|
if (!nLevel)
|
|
break;
|
|
}
|
|
|
|
return aPackingProperties;
|
|
}
|
|
|
|
void VclBuilder::applyPackingProperties(vcl::Window* pCurrent, vcl::Window* pParent,
|
|
const stringmap& rPackingProperties)
|
|
{
|
|
if (!pCurrent)
|
|
return;
|
|
|
|
//ToolBoxItems are not true widgets just elements
|
|
//of the ToolBox itself
|
|
ToolBox *pToolBoxParent = nullptr;
|
|
if (pCurrent == pParent)
|
|
pToolBoxParent = dynamic_cast<ToolBox*>(pParent);
|
|
|
|
if (pCurrent->GetType() == WindowType::SCROLLWINDOW)
|
|
{
|
|
auto aFind = m_pVclParserState->m_aRedundantParentWidgets.find(VclPtr<vcl::Window>(pCurrent));
|
|
if (aFind != m_pVclParserState->m_aRedundantParentWidgets.end())
|
|
{
|
|
pCurrent = aFind->second;
|
|
assert(pCurrent);
|
|
}
|
|
}
|
|
|
|
for (auto const& [rKey, rValue] : rPackingProperties)
|
|
{
|
|
if (rKey == u"expand" || rKey == u"resize")
|
|
{
|
|
bool bTrue = toBool(rValue);
|
|
if (pToolBoxParent)
|
|
pToolBoxParent->SetItemExpand(m_pVclParserState->m_nLastToolbarId, bTrue);
|
|
else
|
|
pCurrent->set_expand(bTrue);
|
|
continue;
|
|
}
|
|
|
|
if (pToolBoxParent)
|
|
continue;
|
|
|
|
if (rKey == u"fill")
|
|
{
|
|
pCurrent->set_fill(toBool(rValue));
|
|
}
|
|
else if (rKey == u"pack-type")
|
|
{
|
|
VclPackType ePackType = (!rValue.isEmpty() && (rValue[0] == 'e' || rValue[0] == 'E')) ? VclPackType::End : VclPackType::Start;
|
|
pCurrent->set_pack_type(ePackType);
|
|
}
|
|
else if (rKey == u"left-attach")
|
|
{
|
|
pCurrent->set_grid_left_attach(rValue.toInt32());
|
|
}
|
|
else if (rKey == u"top-attach")
|
|
{
|
|
pCurrent->set_grid_top_attach(rValue.toInt32());
|
|
}
|
|
else if (rKey == u"width")
|
|
{
|
|
pCurrent->set_grid_width(rValue.toInt32());
|
|
}
|
|
else if (rKey == u"height")
|
|
{
|
|
pCurrent->set_grid_height(rValue.toInt32());
|
|
}
|
|
else if (rKey == u"padding")
|
|
{
|
|
pCurrent->set_padding(rValue.toInt32());
|
|
}
|
|
else if (rKey == u"position")
|
|
{
|
|
set_window_packing_position(pCurrent, rValue.toInt32());
|
|
}
|
|
else if (rKey == u"secondary")
|
|
{
|
|
pCurrent->set_secondary(toBool(rValue));
|
|
}
|
|
else if (rKey == u"non-homogeneous")
|
|
{
|
|
pCurrent->set_non_homogeneous(toBool(rValue));
|
|
}
|
|
else if (rKey == u"homogeneous")
|
|
{
|
|
pCurrent->set_non_homogeneous(!toBool(rValue));
|
|
}
|
|
else
|
|
{
|
|
SAL_WARN_IF(rKey != u"shrink", "vcl.builder", "unknown packing: " << rKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<vcl::EnumContext::Context> BuilderBase::handleStyle(xmlreader::XmlReader &reader, int &nPriority)
|
|
{
|
|
std::vector<vcl::EnumContext::Context> aContext;
|
|
|
|
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::Done)
|
|
break;
|
|
|
|
if (res == xmlreader::XmlReader::Result::Begin)
|
|
{
|
|
++nLevel;
|
|
if (name == "class")
|
|
{
|
|
OUString classStyle = getStyleClass(reader);
|
|
std::u16string_view rest;
|
|
|
|
if (classStyle.startsWith("context-", &rest))
|
|
{
|
|
aContext.push_back(vcl::EnumContext::GetContextEnum(OUString(rest)));
|
|
}
|
|
else if (classStyle.startsWith("priority-", &rest))
|
|
{
|
|
nPriority = o3tl::toInt32(rest);
|
|
}
|
|
else if (classStyle != "small-button" && classStyle != "destructive-action" && classStyle != "suggested-action")
|
|
{
|
|
SAL_WARN("vcl.builder", "unknown class: " << classStyle);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (res == xmlreader::XmlReader::Result::End)
|
|
{
|
|
--nLevel;
|
|
}
|
|
|
|
if (!nLevel)
|
|
break;
|
|
}
|
|
|
|
return aContext;
|
|
}
|
|
|
|
OUString BuilderBase::getStyleClass(xmlreader::XmlReader &reader)
|
|
{
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
OUString aRet;
|
|
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "name")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
aRet = OUString (name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
}
|
|
|
|
return aRet;
|
|
}
|
|
|
|
bool BuilderBase::hasOrientationVertical(VclBuilder::stringmap &rMap)
|
|
{
|
|
bool bVertical = false;
|
|
VclBuilder::stringmap::iterator aFind = rMap.find(u"orientation"_ustr);
|
|
if (aFind != rMap.end())
|
|
{
|
|
bVertical = aFind->second.equalsIgnoreAsciiCase("vertical");
|
|
rMap.erase(aFind);
|
|
}
|
|
return bVertical;
|
|
}
|
|
|
|
void BuilderBase::collectProperty(xmlreader::XmlReader& reader, stringmap& rMap) const
|
|
{
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
|
|
OUString sProperty;
|
|
OString sContext;
|
|
|
|
bool bTranslated = false;
|
|
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "name")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sProperty = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
else if (name == "context")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sContext = OString(name.begin, name.length);
|
|
}
|
|
else if (name == "translatable" && reader.getAttributeValue(false) == "yes")
|
|
{
|
|
bTranslated = true;
|
|
}
|
|
}
|
|
|
|
(void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
|
|
|
|
if (!sProperty.isEmpty())
|
|
{
|
|
OString sValue(name.begin, name.length);
|
|
const OUString sFinalValue = finalizeValue(sContext, sValue, bTranslated);
|
|
sProperty = sProperty.replace('_', '-');
|
|
rMap[sProperty] = sFinalValue;
|
|
}
|
|
}
|
|
|
|
void BuilderBase::handleActionWidget(xmlreader::XmlReader &reader)
|
|
{
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
|
|
OString sResponse;
|
|
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "response")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sResponse = OString(name.begin, name.length);
|
|
}
|
|
}
|
|
|
|
(void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
|
|
OUString sID(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
sal_Int32 nDelim = sID.indexOf(':');
|
|
if (nDelim != -1)
|
|
sID = sID.copy(0, nDelim);
|
|
|
|
short nResponse = sResponse.toInt32();
|
|
switch (nResponse)
|
|
{
|
|
case -5:
|
|
nResponse = RET_OK;
|
|
break;
|
|
case -6:
|
|
nResponse = RET_CANCEL;
|
|
break;
|
|
case -7:
|
|
nResponse = RET_CLOSE;
|
|
break;
|
|
case -8:
|
|
nResponse = RET_YES;
|
|
break;
|
|
case -9:
|
|
nResponse = RET_NO;
|
|
break;
|
|
case -11:
|
|
nResponse = RET_HELP;
|
|
break;
|
|
default:
|
|
assert(nResponse >= 100 && "keep non-canned responses in range 100+ to avoid collision with vcl RET_*");
|
|
break;
|
|
}
|
|
|
|
set_response(sID, nResponse);
|
|
}
|
|
|
|
void BuilderBase::collectAccelerator(xmlreader::XmlReader& reader, accelmap& rMap)
|
|
{
|
|
xmlreader::Span name;
|
|
int nsId;
|
|
|
|
OUString sProperty;
|
|
OUString sValue;
|
|
OUString sModifiers;
|
|
|
|
while (reader.nextAttribute(&nsId, &name))
|
|
{
|
|
if (name == "key")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sValue = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
else if (name == "signal")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sProperty = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
else if (name == "modifiers")
|
|
{
|
|
name = reader.getAttributeValue(false);
|
|
sModifiers = OUString(name.begin, name.length, RTL_TEXTENCODING_UTF8);
|
|
}
|
|
}
|
|
|
|
if (!sProperty.isEmpty() && !sValue.isEmpty())
|
|
{
|
|
rMap[sProperty] = std::make_pair(sValue, sModifiers);
|
|
}
|
|
}
|
|
|
|
bool BuilderBase::isToolbarItemClass(std::u16string_view sClass)
|
|
{
|
|
return sClass == u"GtkToolButton" || sClass == u"GtkMenuToolButton"
|
|
|| sClass == u"GtkToggleToolButton" || sClass == u"GtkRadioToolButton"
|
|
|| sClass == u"GtkToolItem";
|
|
}
|
|
|
|
vcl::Window *VclBuilder::get_widget_root()
|
|
{
|
|
return m_aChildren.empty() ? nullptr : m_aChildren[0].m_pWindow.get();
|
|
}
|
|
|
|
void VclBuilder::resetParserState()
|
|
{
|
|
m_pVclParserState.reset();
|
|
BuilderBase::resetParserState();
|
|
}
|
|
|
|
vcl::Window *VclBuilder::get_by_name(std::u16string_view sID)
|
|
{
|
|
for (auto const& child : m_aChildren)
|
|
{
|
|
if (child.m_sID == sID)
|
|
return child.m_pWindow;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
PopupMenu *VclBuilder::get_menu(std::u16string_view sID)
|
|
{
|
|
for (auto const& menu : m_aMenus)
|
|
{
|
|
if (menu.m_sID == sID)
|
|
return dynamic_cast<PopupMenu*>(menu.m_pMenu.get());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void VclBuilder::set_response(std::u16string_view sID, short nResponse)
|
|
{
|
|
PushButton* pPushButton = get<PushButton>(sID);
|
|
assert(pPushButton);
|
|
Dialog* pDialog = pPushButton->GetParentDialog();
|
|
assert(pDialog);
|
|
pDialog->add_button(pPushButton, nResponse, false);
|
|
return;
|
|
}
|
|
|
|
void VclBuilder::delete_by_name(const OUString& sID)
|
|
{
|
|
auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
|
|
[&sID](WinAndId& rItem) { return rItem.m_sID == sID; });
|
|
if (aI != m_aChildren.end())
|
|
{
|
|
aI->m_pWindow.disposeAndClear();
|
|
m_aChildren.erase(aI);
|
|
}
|
|
}
|
|
|
|
void VclBuilder::delete_by_window(vcl::Window *pWindow)
|
|
{
|
|
drop_ownership(pWindow);
|
|
pWindow->disposeOnce();
|
|
}
|
|
|
|
void VclBuilder::drop_ownership(const vcl::Window *pWindow)
|
|
{
|
|
auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
|
|
[&pWindow](WinAndId& rItem) { return rItem.m_pWindow == pWindow; });
|
|
if (aI != m_aChildren.end())
|
|
m_aChildren.erase(aI);
|
|
}
|
|
|
|
OUString VclBuilder::get_by_window(const vcl::Window *pWindow) const
|
|
{
|
|
for (auto const& child : m_aChildren)
|
|
{
|
|
if (child.m_pWindow == pWindow)
|
|
return child.m_sID;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
VclBuilder::PackingData VclBuilder::get_window_packing_data(const vcl::Window *pWindow) const
|
|
{
|
|
//We've stored the return of new Control, some of these get
|
|
//border windows placed around them which are what you get
|
|
//from GetChild, so scoot up a level if necessary to get the
|
|
//window whose position value we have
|
|
const vcl::Window *pPropHolder = pWindow->ImplGetWindow();
|
|
|
|
for (auto const& child : m_aChildren)
|
|
{
|
|
if (child.m_pWindow == pPropHolder)
|
|
return child.m_aPackingData;
|
|
}
|
|
|
|
return PackingData();
|
|
}
|
|
|
|
void VclBuilder::set_window_packing_position(const vcl::Window *pWindow, sal_Int32 nPosition)
|
|
{
|
|
for (auto & child : m_aChildren)
|
|
{
|
|
if (child.m_pWindow == pWindow)
|
|
child.m_aPackingData.m_nPosition = nPosition;
|
|
}
|
|
}
|
|
|
|
const BuilderBase::ListStore* BuilderBase::get_model_by_name(const OUString& sID) const
|
|
{
|
|
const auto aI = m_pParserState->m_aModels.find(sID);
|
|
if (aI != m_pParserState->m_aModels.end())
|
|
return &(aI->second);
|
|
return nullptr;
|
|
}
|
|
|
|
void BuilderBase::addTextBuffer(const OUString& sID, const TextBuffer& rTextBuffer)
|
|
{
|
|
m_pParserState->m_aTextBuffers[sID] = rTextBuffer;
|
|
}
|
|
|
|
const BuilderBase::TextBuffer* BuilderBase::get_buffer_by_name(const OUString& sID) const
|
|
{
|
|
const auto aI = m_pParserState->m_aTextBuffers.find(sID);
|
|
if (aI != m_pParserState->m_aTextBuffers.end())
|
|
return &(aI->second);
|
|
return nullptr;
|
|
}
|
|
|
|
void BuilderBase::addAdjustment(const OUString& sID, const Adjustment& rAdjustment)
|
|
{
|
|
m_pParserState->m_aAdjustments[sID] = rAdjustment;
|
|
}
|
|
|
|
const BuilderBase::Adjustment* BuilderBase::get_adjustment_by_name(const OUString& sID) const
|
|
{
|
|
const auto aI = m_pParserState->m_aAdjustments.find(sID);
|
|
if (aI != m_pParserState->m_aAdjustments.end())
|
|
return &(aI->second);
|
|
return nullptr;
|
|
}
|
|
|
|
void VclBuilder::mungeModel(ComboBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
|
|
{
|
|
for (auto const& entry : rStore.m_aEntries)
|
|
{
|
|
const ListStore::row &rRow = entry;
|
|
sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
|
|
if (rRow.size() > 1)
|
|
{
|
|
if (isLegacy())
|
|
{
|
|
sal_IntPtr nValue = rRow[1].toInt32();
|
|
rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
|
|
}
|
|
else
|
|
{
|
|
if (!rRow[1].isEmpty())
|
|
{
|
|
m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
|
|
rTarget.SetEntryData(nEntry, m_aUserData.back().get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nActiveId < rStore.m_aEntries.size())
|
|
rTarget.SelectEntryPos(nActiveId);
|
|
}
|
|
|
|
void VclBuilder::mungeModel(ListBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
|
|
{
|
|
for (auto const& entry : rStore.m_aEntries)
|
|
{
|
|
const ListStore::row &rRow = entry;
|
|
sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
|
|
if (rRow.size() > 1)
|
|
{
|
|
if (isLegacy())
|
|
{
|
|
sal_IntPtr nValue = rRow[1].toInt32();
|
|
rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
|
|
}
|
|
else
|
|
{
|
|
if (!rRow[1].isEmpty())
|
|
{
|
|
m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
|
|
rTarget.SetEntryData(nEntry, m_aUserData.back().get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nActiveId < rStore.m_aEntries.size())
|
|
rTarget.SelectEntryPos(nActiveId);
|
|
}
|
|
|
|
void VclBuilder::mungeModel(SvTabListBox& rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
|
|
{
|
|
for (auto const& entry : rStore.m_aEntries)
|
|
{
|
|
const ListStore::row &rRow = entry;
|
|
auto pEntry = rTarget.InsertEntry(rRow[0]);
|
|
if (rRow.size() > 1)
|
|
{
|
|
if (isLegacy())
|
|
{
|
|
sal_IntPtr nValue = rRow[1].toInt32();
|
|
pEntry->SetUserData(reinterpret_cast<void*>(nValue));
|
|
}
|
|
else
|
|
{
|
|
if (!rRow[1].isEmpty())
|
|
{
|
|
m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
|
|
pEntry->SetUserData(m_aUserData.back().get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nActiveId < rStore.m_aEntries.size())
|
|
{
|
|
SvTreeListEntry* pEntry = rTarget.GetEntry(nullptr, nActiveId);
|
|
rTarget.Select(pEntry);
|
|
}
|
|
}
|
|
|
|
void VclBuilder::insertComboBoxOrListBoxItems(vcl::Window *pWindow, VclBuilder::stringmap &rMap,
|
|
const std::vector<ComboBoxTextItem>& rItems)
|
|
{
|
|
// try to fill-in the items
|
|
if (!insertItems<ComboBox>(pWindow, rMap, m_aUserData, rItems))
|
|
insertItems<ListBox>(pWindow, rMap, m_aUserData, rItems);
|
|
}
|
|
|
|
void VclBuilder::mungeAdjustment(NumericFormatter &rTarget, const Adjustment &rAdjustment)
|
|
{
|
|
int nMul = rtl_math_pow10Exp(1, rTarget.GetDecimalDigits());
|
|
|
|
for (auto const& [ rKey, rValue ] : rAdjustment)
|
|
{
|
|
if (rKey == "upper")
|
|
{
|
|
sal_Int64 nUpper = rValue.toDouble() * nMul;
|
|
rTarget.SetMax(nUpper);
|
|
rTarget.SetLast(nUpper);
|
|
}
|
|
else if (rKey == "lower")
|
|
{
|
|
sal_Int64 nLower = rValue.toDouble() * nMul;
|
|
rTarget.SetMin(nLower);
|
|
rTarget.SetFirst(nLower);
|
|
}
|
|
else if (rKey == "value")
|
|
{
|
|
sal_Int64 nValue = rValue.toDouble() * nMul;
|
|
rTarget.SetValue(nValue);
|
|
}
|
|
else if (rKey == "step-increment")
|
|
{
|
|
sal_Int64 nSpinSize = rValue.toDouble() * nMul;
|
|
rTarget.SetSpinSize(nSpinSize);
|
|
}
|
|
else
|
|
{
|
|
SAL_INFO("vcl.builder", "unhandled property :" << rKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VclBuilder::mungeAdjustment(FormattedField &rTarget, const Adjustment &rAdjustment)
|
|
{
|
|
double nMaxValue = 0, nMinValue = 0, nValue = 0, nSpinSize = 0;
|
|
|
|
for (auto const& [ rKey, rValue ] : rAdjustment)
|
|
{
|
|
if (rKey == "upper")
|
|
nMaxValue = rValue.toDouble();
|
|
else if (rKey == "lower")
|
|
nMinValue = rValue.toDouble();
|
|
else if (rKey == "value")
|
|
nValue = rValue.toDouble();
|
|
else if (rKey == "step-increment")
|
|
nSpinSize = rValue.toDouble();
|
|
else
|
|
SAL_INFO("vcl.builder", "unhandled property :" << rKey);
|
|
}
|
|
|
|
Formatter& rFormatter = rTarget.GetFormatter();
|
|
rFormatter.SetMinValue(nMinValue);
|
|
rFormatter.SetMaxValue(nMaxValue);
|
|
rFormatter.SetValue(nValue);
|
|
rFormatter.SetSpinSize(nSpinSize);
|
|
}
|
|
|
|
void VclBuilder::mungeAdjustment(ScrollBar &rTarget, const Adjustment &rAdjustment)
|
|
{
|
|
for (auto const& [ rKey, rValue ] : rAdjustment)
|
|
{
|
|
if (rKey == "upper")
|
|
rTarget.SetRangeMax(rValue.toInt32());
|
|
else if (rKey == "lower")
|
|
rTarget.SetRangeMin(rValue.toInt32());
|
|
else if (rKey == "value")
|
|
rTarget.SetThumbPos(rValue.toInt32());
|
|
else if (rKey == "step-increment")
|
|
rTarget.SetLineSize(rValue.toInt32());
|
|
else if (rKey == "page-increment")
|
|
rTarget.SetPageSize(rValue.toInt32());
|
|
else
|
|
{
|
|
SAL_INFO("vcl.builder", "unhandled property :" << rKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VclBuilder::mungeAdjustment(Slider& rTarget, const Adjustment& rAdjustment)
|
|
{
|
|
for (auto const& [ rKey, rValue ] : rAdjustment)
|
|
{
|
|
if (rKey == "upper")
|
|
rTarget.SetRangeMax(rValue.toInt32());
|
|
else if (rKey == "lower")
|
|
rTarget.SetRangeMin(rValue.toInt32());
|
|
else if (rKey == "value")
|
|
rTarget.SetThumbPos(rValue.toInt32());
|
|
else if (rKey == "step-increment")
|
|
rTarget.SetLineSize(rValue.toInt32());
|
|
else if (rKey == "page-increment")
|
|
rTarget.SetPageSize(rValue.toInt32());
|
|
else
|
|
{
|
|
SAL_INFO("vcl.builder", "unhandled property :" << rKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VclBuilder::mungeTextBuffer(VclMultiLineEdit &rTarget, const TextBuffer &rTextBuffer)
|
|
{
|
|
for (auto const& [ rKey, rValue ] : rTextBuffer)
|
|
{
|
|
if (rKey == "text")
|
|
rTarget.SetText(rValue);
|
|
else
|
|
{
|
|
SAL_INFO("vcl.builder", "unhandled property :" << rKey);
|
|
}
|
|
}
|
|
}
|
|
|
|
VclBuilder::VclParserState::VclParserState()
|
|
: m_nLastToolbarId(0)
|
|
, m_nLastMenuItemId(0)
|
|
{}
|
|
|
|
VclBuilder::MenuAndId::MenuAndId(OUString aId, Menu *pMenu)
|
|
: m_sID(std::move(aId))
|
|
, m_pMenu(pMenu)
|
|
{}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|