/* -*- 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/.
*/
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) returntrue;
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;
}
}
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;
}
const std::vector<BuilderBase::MnemonicWidgetMap>& BuilderBase::getMnemonicWidgetMaps() const {
assert(m_pParserState && "parser state no more valid"); return m_pParserState->m_aMnemonicWidgetMaps;
}
const std::vector<BuilderBase::RadioButtonGroupMap>& BuilderBase::getRadioButtonGroupMaps() const {
assert(m_pParserState && "parser state no more valid"); return m_pParserState->m_aRadioButtonGroupMaps;
}
//Set a11y relations and role when everything has been imported for (autoconst& elemAtk : m_pVclParserState->m_aAtkInfo)
{
vcl::Window *pSource = elemAtk.first; const stringmap &rMap = elemAtk.second;
for (autoconst& [ rType, rParam ] : rMap)
{ if (rType == "role")
{
sal_Int16 role = BuilderUtils::getRoleFromName(rParam); if (role != css::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); elseif (rType == "label-for")
pSource->SetAccessibleRelationLabelFor(pTarget); else
{
SAL_WARN("vcl.builder", "unhandled a11y relation :" << rType);
}
}
}
}
#ifndef NDEBUG
o3tl::sorted_vector<OUString> models; #endif //Set ComboBox models when everything has been imported for (autoconst& 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); elseif (pComboBoxTarget && pStore)
mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId); elseif (pTreeBoxTarget && pStore)
mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId);
}
//Set TextView buffers when everything has been imported for (autoconst& 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 (autoconst& 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 (autoconst& 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 (autoconst& 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 (autoconst& 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 (autoconst& sizeGroup : getSizeGroups())
{
std::shared_ptr<VclSizeGroup> xGroup(std::make_shared<VclSizeGroup>());
for (autoconst& [ rKey, rValue ] : sizeGroup.m_aProperties)
xGroup->set_property(rKey, rValue);
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 (autoconst& elem : aImagesToBeRemoved)
{
delete_by_name(elem);
}
//Set button menus when everything has been imported for (autoconst& 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, true);
}
//Remove ScrollWindow parent widgets whose children in vcl implement scrolling //internally. for (autoconst& elem : m_pVclParserState->m_aRedundantParentWidgets)
{
delete_by_window(elem.first);
}
//fdo#67378 merge the label into the disclosure button for (autoconst& 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 (autoconst& 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);
#ifdefined SAL_LOG_WARN if (m_bToplevelParentFound && m_pParent->IsDialog())
{ int nButtons = 0; bool bHasDefButton = false; for (autoconst& 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
constbool 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.reset();
}
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();
}
// 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.
#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;
}
}
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;
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
} elseif (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; #ifdefined _WIN32
xWindow->set_border_width(3); #else
xWindow->set_border_width(12); #endif
} elseif (name == "GtkBox" || name == "GtkStatusbar")
{
bVertical = hasOrientationVertical(rMap); if (bVertical)
xWindow = VclPtr<VclVBox>::Create(pParent); else
xWindow = VclPtr<VclHBox>::Create(pParent);
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;
}
} elseif (name == "VclOptionalBox" || name == "sfxlo-OptionalBox")
{ // tdf#135495 fallback sfxlo-OptionalBox to VclOptionalBox as a stopgap
xWindow = VclPtr<OptionalBox>::Create(pParent);
} elseif (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);
} elseif (name == "sfxlo-PriorityMergedHBox")
{ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
xWindow = VclPtr<PriorityMergedHBox>::Create(pParent);
} elseif (name == "sfxlo-PriorityHBox")
{ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
xWindow = VclPtr<PriorityHBox>::Create(pParent);
} elseif (name == "sfxlo-DropdownBox")
{ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
xWindow = VclPtr<DropdownBox>::Create(pParent);
} elseif (name == "sfxlo-ContextVBox")
{ // like tdf#135495 above, keep the sfxlo-PriorityMergedHBox even though its not in sfx anymore
xWindow = VclPtr<ContextVBox>::Create(pParent);
} elseif (name == "GtkIconView")
{
assert(rMap.contains(u"model"_ustr) && "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;
if (pRealParent != pParent)
cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
} elseif (name == "GtkTreeView")
{ if (!isLegacy())
{
assert(rMap.contains(u"model"_ustr) && "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);
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, loadThemeImage(sIconName));
if (!extractVisible(rMap))
pToolBox->HideItem(nItemId);
m_pVclParserState->m_nLastToolbarId = nItemId;
return nullptr; // no widget to be created
}
} elseif (name == "GtkSeparatorToolItem")
{ if (pToolBox)
{
pToolBox->InsertSeparator(); return nullptr; // no widget to be created
}
} elseif (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);
} elseif (name == "GtkPopover")
{
WinBits nBits = extractDeferredBits(rMap); // If a Popover is not modal don't grab focus when it pops up if (!extractModal(rMap))
nBits |= WB_NOPOINTERFOCUS;
xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_DOCKABLE|WB_MOVEABLE);
} elseif (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); elseif (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);
}
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);
}
}
//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;
} elseif (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 (autoconst& [ rKey, rValue ] : rPango)
pCurrentChild->set_font_attribute(rKey, rValue);
//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) returntrue; if (nTopA > nTopB) returnfalse;
sal_Int32 nLeftA = pA->get_grid_left_attach();
sal_Int32 nLeftB = pB->get_grid_left_attach(); if (nLeftA < nLeftB) returntrue; if (nLeftA > nLeftB) returnfalse; //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) returntrue; if (ePackA > ePackB) returnfalse; 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) returntrue; if (bPackA < bPackB) returnfalse;
} else
{ //for vertical boxes group secondaries after primaries if (bPackA < bPackB) returntrue; if (bPackA > bPackB) returnfalse;
} //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;
}
} returnfalse;
}
//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
} elseif (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
}
}
//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;
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 (autoconst& [ 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);
}
} elseif (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::setMnemonicWidget(const OUString& rLabelId, const OUString& rMnemonicWidgetId)
{
FixedText* pOne = get<FixedText>(rLabelId);
vcl::Window* pOther = get(rMnemonicWidgetId);
SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << rLabelId << " or target " << rMnemonicWidgetId
<< " member of Mnemonic Widget Mapping"); if (pOne && pOther)
pOne->set_mnemonic_widget(pOther);
}
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));
}
/// Insert items to a ComboBox or a ListBox. /// They have no common ancestor that would have 'InsertEntry()', so use a template. template<typename T> staticbool 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) returnfalse;
sal_uInt16 nActiveId = BuilderBase::extractActive(rMap); for (autoconst& 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);
//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 (autoconst& [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;
}
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 (autoconst& 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 (autoconst& child : m_aChildren)
{ if (child.m_pWindow == pPropHolder) return child.m_aPackingData;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.