nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / window / builder.cxx
blob0dafe38951803383eb4bb20b3dcaa16455674eab
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <config_feature_desktop.h>
11 #include <config_options.h>
13 #include <memory>
14 #include <string_view>
15 #include <unordered_map>
16 #include <com/sun/star/accessibility/AccessibleRole.hpp>
18 #include <comphelper/lok.hxx>
19 #include <i18nutil/unicode.hxx>
20 #include <officecfg/Office/Common.hxx>
21 #include <osl/module.hxx>
22 #include <sal/log.hxx>
23 #include <unotools/localedatawrapper.hxx>
24 #include <unotools/resmgr.hxx>
25 #include <vcl/builder.hxx>
26 #include <vcl/toolkit/button.hxx>
27 #include <vcl/toolkit/dialog.hxx>
28 #include <vcl/toolkit/edit.hxx>
29 #include <vcl/toolkit/field.hxx>
30 #include <vcl/fieldvalues.hxx>
31 #include <vcl/toolkit/fmtfield.hxx>
32 #include <vcl/toolkit/fixed.hxx>
33 #include <vcl/toolkit/fixedhyper.hxx>
34 #include <vcl/headbar.hxx>
35 #include <vcl/IPrioritable.hxx>
36 #include <vcl/toolkit/ivctrl.hxx>
37 #include <vcl/layout.hxx>
38 #include <vcl/toolkit/lstbox.hxx>
39 #include <vcl/menubtn.hxx>
40 #include <vcl/mnemonic.hxx>
41 #include <vcl/toolkit/prgsbar.hxx>
42 #include <vcl/scrbar.hxx>
43 #include <vcl/split.hxx>
44 #include <vcl/svapp.hxx>
45 #include <vcl/toolkit/svtabbx.hxx>
46 #include <vcl/tabctrl.hxx>
47 #include <vcl/tabpage.hxx>
48 #include <vcl/toolkit/throbber.hxx>
49 #include <vcl/toolbox.hxx>
50 #include <vcl/toolkit/treelistentry.hxx>
51 #include <vcl/toolkit/vclmedit.hxx>
52 #include <vcl/settings.hxx>
53 #include <slider.hxx>
54 #include <vcl/weld.hxx>
55 #include <vcl/weldutils.hxx>
56 #include <vcl/commandinfoprovider.hxx>
57 #include <iconview.hxx>
58 #include <svdata.hxx>
59 #include <bitmaps.hlst>
60 #include <messagedialog.hxx>
61 #include <OptionalBox.hxx>
62 #include <window.h>
63 #include <xmlreader/xmlreader.hxx>
64 #include <desktop/crashreport.hxx>
65 #include <calendar.hxx>
66 #include <menutogglebutton.hxx>
67 #include <salinst.hxx>
68 #include <strings.hrc>
69 #include <treeglue.hxx>
70 #include <tools/diagnose_ex.h>
71 #include <verticaltabctrl.hxx>
72 #include <wizdlg.hxx>
73 #include <tools/svlibrary.h>
74 #include <jsdialog/jsdialogbuilder.hxx>
76 #if defined(DISABLE_DYNLOADING) || defined(LINUX)
77 #include <dlfcn.h>
78 #endif
80 static bool toBool(std::string_view rValue)
82 return (!rValue.empty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
85 namespace
87 OUString mapStockToImageResource(const OUString& sType)
89 if (sType == "gtk-index")
90 return SV_RESID_BITMAP_INDEX;
91 else if (sType == "gtk-refresh")
92 return SV_RESID_BITMAP_REFRESH;
93 else if (sType == "gtk-apply")
94 return IMG_APPLY;
95 else if (sType == "gtk-dialog-error")
96 return IMG_ERROR;
97 else if (sType == "gtk-add")
98 return IMG_ADD;
99 else if (sType == "gtk-remove")
100 return IMG_REMOVE;
101 else if (sType == "gtk-copy")
102 return IMG_COPY;
103 else if (sType == "gtk-paste")
104 return IMG_PASTE;
105 return OUString();
108 SymbolType mapStockToSymbol(const OUString& sType)
110 SymbolType eRet = SymbolType::DONTKNOW;
111 if (sType == "gtk-media-next")
112 eRet = SymbolType::NEXT;
113 else if (sType == "gtk-media-previous")
114 eRet = SymbolType::PREV;
115 else if (sType == "gtk-media-play")
116 eRet = SymbolType::PLAY;
117 else if (sType == "gtk-media-stop")
118 eRet = SymbolType::STOP;
119 else if (sType == "gtk-goto-first")
120 eRet = SymbolType::FIRST;
121 else if (sType == "gtk-goto-last")
122 eRet = SymbolType::LAST;
123 else if (sType == "gtk-go-back")
124 eRet = SymbolType::ARROW_LEFT;
125 else if (sType == "gtk-go-forward")
126 eRet = SymbolType::ARROW_RIGHT;
127 else if (sType == "gtk-go-up")
128 eRet = SymbolType::ARROW_UP;
129 else if (sType == "gtk-go-down")
130 eRet = SymbolType::ARROW_DOWN;
131 else if (sType == "gtk-missing-image")
132 eRet = SymbolType::IMAGE;
133 else if (sType == "gtk-help")
134 eRet = SymbolType::HELP;
135 else if (sType == "gtk-close")
136 eRet = SymbolType::CLOSE;
137 else if (sType == "gtk-new")
138 eRet = SymbolType::PLUS;
139 else if (!mapStockToImageResource(sType).isEmpty())
140 eRet = SymbolType::IMAGE;
141 return eRet;
144 void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame);
147 #if defined SAL_LOG_WARN
148 namespace
150 bool isButtonType(WindowType nType)
152 return nType == WindowType::PUSHBUTTON ||
153 nType == WindowType::OKBUTTON ||
154 nType == WindowType::CANCELBUTTON ||
155 nType == WindowType::HELPBUTTON ||
156 nType == WindowType::IMAGEBUTTON ||
157 nType == WindowType::MENUBUTTON ||
158 nType == WindowType::MOREBUTTON ||
159 nType == WindowType::SPINBUTTON;
162 #endif
164 weld::Builder* Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile, bool bMobile)
166 bool bUseJSBuilder = false;
168 if (bMobile)
170 if (rUIFile == "modules/swriter/ui/wordcount-mobile.ui" ||
171 rUIFile == "svx/ui/findreplacedialog-mobile.ui" ||
172 rUIFile == "modules/swriter/ui/watermarkdialog.ui" ||
173 rUIFile == "modules/scalc/ui/validationdialog.ui" ||
174 rUIFile == "modules/scalc/ui/validationcriteriapage.ui" ||
175 rUIFile == "modules/scalc/ui/validationhelptabpage-mobile.ui" ||
176 rUIFile == "modules/scalc/ui/erroralerttabpage-mobile.ui" ||
177 rUIFile == "modules/scalc/ui/validationdialog.ui")
178 bUseJSBuilder = true;
181 if (bUseJSBuilder)
182 return new JSInstanceBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
183 else
184 return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile);
187 weld::Builder* Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile, bool bAllowCycleFocusOut, sal_uInt64 nLOKWindowId)
189 if (comphelper::LibreOfficeKit::isActive()
190 && (rUIFile == "svx/ui/stylespreview.ui"
191 || rUIFile == "modules/scalc/ui/numberbox.ui"))
193 return new JSInstanceBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, css::uno::Reference<css::frame::XFrame>(), nLOKWindowId);
196 return ImplGetSVData()->mpDefInst->CreateInterimBuilder(pParent, AllSettings::GetUIRootDir(), rUIFile, bAllowCycleFocusOut, nLOKWindowId);
199 weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType,
200 VclButtonsType eButtonType, const OUString& rPrimaryMessage,
201 bool bMobile)
203 if (bMobile)
204 return JSInstanceBuilder::CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
205 else
206 return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
209 weld::Window* Application::GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow)
211 return ImplGetSVData()->mpDefInst->GetFrameWeld(rWindow);
214 namespace weld
216 OUString MetricSpinButton::MetricToString(FieldUnit rUnit)
218 const FieldUnitStringList& rList = ImplGetFieldUnits();
219 // return unit's default string (ie, the first one )
220 auto it = std::find_if(
221 rList.begin(), rList.end(),
222 [&rUnit](const std::pair<OUString, FieldUnit>& rItem) { return rItem.second == rUnit; });
223 if (it != rList.end())
224 return it->first;
226 return OUString();
229 IMPL_LINK_NOARG(MetricSpinButton, spin_button_value_changed, SpinButton&, void)
231 signal_value_changed();
234 IMPL_LINK(MetricSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
236 OUString sNewText(format_number(rSpinButton.get_value()));
237 if (sNewText != rSpinButton.get_text())
238 rSpinButton.set_text(sNewText);
241 void MetricSpinButton::update_width_chars()
243 int min, max;
244 m_xSpinButton->get_range(min, max);
245 auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
246 m_xSpinButton->get_pixel_size(format_number(max)).Width());
247 int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
248 m_xSpinButton->set_width_chars(chars);
251 unsigned int SpinButton::Power10(unsigned int n)
253 unsigned int nValue = 1;
254 for (unsigned int i = 0; i < n; ++i)
255 nValue *= 10;
256 return nValue;
259 int SpinButton::denormalize(int nValue) const
261 const int nFactor = Power10(get_digits());
263 if ((nValue < (SAL_MIN_INT32 + nFactor)) || (nValue > (SAL_MAX_INT32 - nFactor)))
265 return nValue / nFactor;
268 const int nHalf = nFactor / 2;
270 if (nValue < 0)
271 return (nValue - nHalf) / nFactor;
272 return (nValue + nHalf) / nFactor;
275 OUString MetricSpinButton::format_number(int nValue) const
277 OUString aStr;
279 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
281 unsigned int nDecimalDigits = m_xSpinButton->get_digits();
282 //pawn percent off to icu to decide whether percent is separated from its number for this locale
283 if (m_eSrcUnit == FieldUnit::PERCENT)
285 double fValue = nValue;
286 fValue /= SpinButton::Power10(nDecimalDigits);
287 aStr = unicode::formatPercent(fValue, rLocaleData.getLanguageTag());
289 else
291 aStr = rLocaleData.getNum(nValue, nDecimalDigits, true, true);
292 OUString aSuffix = MetricToString(m_eSrcUnit);
293 if (m_eSrcUnit != FieldUnit::NONE && m_eSrcUnit != FieldUnit::DEGREE && m_eSrcUnit != FieldUnit::INCH && m_eSrcUnit != FieldUnit::FOOT)
294 aStr += " ";
295 if (m_eSrcUnit == FieldUnit::INCH)
297 OUString sDoublePrime = u"\u2033";
298 if (aSuffix != "\"" && aSuffix != sDoublePrime)
299 aStr += " ";
300 else
301 aSuffix = sDoublePrime;
303 else if (m_eSrcUnit == FieldUnit::FOOT)
305 OUString sPrime = u"\u2032";
306 if (aSuffix != "'" && aSuffix != sPrime)
307 aStr += " ";
308 else
309 aSuffix = sPrime;
312 assert(m_eSrcUnit != FieldUnit::PERCENT);
313 aStr += aSuffix;
316 return aStr;
319 void MetricSpinButton::set_digits(unsigned int digits)
321 int step, page;
322 get_increments(step, page, m_eSrcUnit);
323 int value = get_value(m_eSrcUnit);
324 m_xSpinButton->set_digits(digits);
325 set_increments(step, page, m_eSrcUnit);
326 set_value(value, m_eSrcUnit);
327 update_width_chars();
330 void MetricSpinButton::set_unit(FieldUnit eUnit)
332 if (eUnit != m_eSrcUnit)
334 int step, page;
335 get_increments(step, page, m_eSrcUnit);
336 int value = get_value(m_eSrcUnit);
337 m_eSrcUnit = eUnit;
338 set_increments(step, page, m_eSrcUnit);
339 set_value(value, m_eSrcUnit);
340 spin_button_output(*m_xSpinButton);
341 update_width_chars();
345 int MetricSpinButton::ConvertValue(int nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const
347 auto nRet = vcl::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit);
348 if (nRet > SAL_MAX_INT32)
349 nRet = SAL_MAX_INT32;
350 else if (nRet < SAL_MIN_INT32)
351 nRet = SAL_MIN_INT32;
352 return nRet;
355 IMPL_LINK(MetricSpinButton, spin_button_input, int*, result, bool)
357 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
358 double fResult(0.0);
359 bool bRet = vcl::TextToValue(get_text(), fResult, 0, m_xSpinButton->get_digits(), rLocaleData, m_eSrcUnit);
360 if (bRet)
362 if (fResult > SAL_MAX_INT32)
363 fResult = SAL_MAX_INT32;
364 else if (fResult < SAL_MIN_INT32)
365 fResult = SAL_MIN_INT32;
366 *result = fResult;
368 return bRet;
371 EntryTreeView::EntryTreeView(std::unique_ptr<Entry> xEntry, std::unique_ptr<TreeView> xTreeView)
372 : m_xEntry(std::move(xEntry))
373 , m_xTreeView(std::move(xTreeView))
375 m_xTreeView->connect_changed(LINK(this, EntryTreeView, ClickHdl));
376 m_xEntry->connect_changed(LINK(this, EntryTreeView, ModifyHdl));
379 IMPL_LINK(EntryTreeView, ClickHdl, weld::TreeView&, rView, void)
381 m_xEntry->set_text(rView.get_selected_text());
382 m_aChangeHdl.Call(*this);
385 IMPL_LINK_NOARG(EntryTreeView, ModifyHdl, weld::Entry&, void)
387 m_aChangeHdl.Call(*this);
390 void EntryTreeView::set_height_request_by_rows(int nRows)
392 int nHeight = nRows == -1 ? -1 : m_xTreeView->get_height_rows(nRows);
393 m_xTreeView->set_size_request(m_xTreeView->get_size_request().Width(), nHeight);
396 size_t GetAbsPos(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
398 size_t nAbsPos = 0;
400 std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
401 if (!rTreeView.get_iter_first(*xEntry))
402 xEntry.reset();
404 while (xEntry && rTreeView.iter_compare(*xEntry, rIter) != 0)
406 if (!rTreeView.iter_next(*xEntry))
407 xEntry.reset();
408 nAbsPos++;
411 return nAbsPos;
414 bool IsEntryVisible(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
416 // short circuit for the common case
417 if (rTreeView.get_iter_depth(rIter) == 0)
418 return true;
420 std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
421 bool bRetVal = false;
424 if (rTreeView.get_iter_depth(*xEntry) == 0)
426 bRetVal = true;
427 break;
429 } while (rTreeView.iter_parent(*xEntry) && rTreeView.get_row_expanded(*xEntry));
430 return bRetVal;
434 VclBuilder::VclBuilder(vcl::Window* pParent, const OUString& sUIDir, const OUString& sUIFile,
435 const OString& sID, const css::uno::Reference<css::frame::XFrame>& rFrame,
436 bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem)
437 : m_pNotebookBarAddonsItem(pNotebookBarAddonsItem
438 ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem)
439 : new NotebookBarAddonsItem{})
440 , m_sID(sID)
441 , m_sHelpRoot(OUStringToOString(sUIFile, RTL_TEXTENCODING_UTF8))
442 , m_pStringReplace(Translate::GetReadStringHook())
443 , m_pParent(pParent)
444 , m_bToplevelParentFound(false)
445 , m_bLegacy(bLegacy)
446 , m_pParserState(new ParserState)
447 , m_xFrame(rFrame)
449 m_bToplevelHasDeferredInit = pParent &&
450 ((pParent->IsSystemWindow() && static_cast<SystemWindow*>(pParent)->isDeferredInit()) ||
451 (pParent->IsDockingWindow() && static_cast<DockingWindow*>(pParent)->isDeferredInit()));
452 m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit;
454 sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.');
455 if (nIdx != -1)
456 m_sHelpRoot = m_sHelpRoot.copy(0, nIdx);
457 m_sHelpRoot += OString('/');
459 OUString sUri = sUIDir + sUIFile;
463 xmlreader::XmlReader reader(sUri);
465 handleChild(pParent, nullptr, reader);
467 catch (const css::uno::Exception &rExcept)
469 DBG_UNHANDLED_EXCEPTION("vcl.builder", "Unable to read .ui file");
470 CrashReporter::addKeyValue("VclBuilderException", "Unable to read .ui file: " + rExcept.Message, CrashReporter::Write);
471 throw;
474 //Set Mnemonic widgets when everything has been imported
475 for (auto const& mnemonicWidget : m_pParserState->m_aMnemonicWidgetMaps)
477 FixedText *pOne = get<FixedText>(mnemonicWidget.m_sID);
478 vcl::Window *pOther = get(mnemonicWidget.m_sValue.toUtf8());
479 SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << mnemonicWidget.m_sID
480 << " or target " << mnemonicWidget.m_sValue << " member of Mnemonic Widget Mapping");
481 if (pOne && pOther)
482 pOne->set_mnemonic_widget(pOther);
485 //Set a11y relations and role when everything has been imported
486 for (auto const& elemAtk : m_pParserState->m_aAtkInfo)
488 vcl::Window *pSource = elemAtk.first;
489 const stringmap &rMap = elemAtk.second;
491 for (auto const& elemMap : rMap)
493 const OString &rType = elemMap.first;
494 const OUString &rParam = elemMap.second;
495 if (rType == "role")
497 sal_Int16 role = BuilderUtils::getRoleFromName(rParam.toUtf8());
498 if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN)
499 pSource->SetAccessibleRole(role);
501 else
503 vcl::Window *pTarget = get(rParam.toUtf8());
504 SAL_WARN_IF(!pTarget, "vcl", "missing parameter of a11y relation: " << rParam);
505 if (!pTarget)
506 continue;
507 if (rType == "labelled-by")
508 pSource->SetAccessibleRelationLabeledBy(pTarget);
509 else if (rType == "label-for")
510 pSource->SetAccessibleRelationLabelFor(pTarget);
511 else if (rType == "member-of")
512 pSource->SetAccessibleRelationMemberOf(pTarget);
513 else
515 SAL_WARN("vcl.builder", "unhandled a11y relation :" << rType);
521 //Set radiobutton groups when everything has been imported
522 for (auto const& elem : m_pParserState->m_aGroupMaps)
524 RadioButton *pOne = get<RadioButton>(elem.m_sID);
525 RadioButton *pOther = get<RadioButton>(elem.m_sValue);
526 SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group");
527 if (pOne && pOther)
529 if (m_bLegacy)
530 pOne->group(*pOther);
531 else
533 pOther->group(*pOne);
534 std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
539 //Set ComboBox models when everything has been imported
540 for (auto const& elem : m_pParserState->m_aModelMaps)
542 vcl::Window* pTarget = get(elem.m_sID);
543 ListBox *pListBoxTarget = dynamic_cast<ListBox*>(pTarget);
544 ComboBox *pComboBoxTarget = dynamic_cast<ComboBox*>(pTarget);
545 SvTabListBox *pTreeBoxTarget = dynamic_cast<SvTabListBox*>(pTarget);
546 // pStore may be empty
547 const ListStore *pStore = get_model_by_name(elem.m_sValue.toUtf8());
548 SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget, "vcl", "missing elements of combobox");
549 if (pListBoxTarget && pStore)
550 mungeModel(*pListBoxTarget, *pStore, elem.m_nActiveId);
551 else if (pComboBoxTarget && pStore)
552 mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId);
553 else if (pTreeBoxTarget && pStore)
554 mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId);
557 //Set TextView buffers when everything has been imported
558 for (auto const& elem : m_pParserState->m_aTextBufferMaps)
560 VclMultiLineEdit *pTarget = get<VclMultiLineEdit>(elem.m_sID);
561 const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue.toUtf8());
562 SAL_WARN_IF(!pTarget || !pBuffer, "vcl", "missing elements of textview/textbuffer");
563 if (pTarget && pBuffer)
564 mungeTextBuffer(*pTarget, *pBuffer);
567 //Set SpinButton adjustments when everything has been imported
568 for (auto const& elem : m_pParserState->m_aNumericFormatterAdjustmentMaps)
570 NumericFormatter *pTarget = dynamic_cast<NumericFormatter*>(get(elem.m_sID));
571 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
572 SAL_WARN_IF(!pTarget, "vcl", "missing NumericFormatter element of spinbutton/adjustment");
573 SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
574 if (pTarget && pAdjustment)
575 mungeAdjustment(*pTarget, *pAdjustment);
578 for (auto const& elem : m_pParserState->m_aFormattedFormatterAdjustmentMaps)
580 FormattedField *pTarget = dynamic_cast<FormattedField*>(get(elem.m_sID));
581 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
582 SAL_WARN_IF(!pTarget, "vcl", "missing FormattedField element of spinbutton/adjustment");
583 SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
584 if (pTarget && pAdjustment)
585 mungeAdjustment(*pTarget, *pAdjustment);
588 //Set ScrollBar adjustments when everything has been imported
589 for (auto const& elem : m_pParserState->m_aScrollAdjustmentMaps)
591 ScrollBar *pTarget = get<ScrollBar>(elem.m_sID);
592 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
593 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scrollbar/adjustment");
594 if (pTarget && pAdjustment)
595 mungeAdjustment(*pTarget, *pAdjustment);
598 //Set Scale(Slider) adjustments
599 for (auto const& elem : m_pParserState->m_aSliderAdjustmentMaps)
601 Slider* pTarget = dynamic_cast<Slider*>(get(elem.m_sID));
602 const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
603 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scale(slider)/adjustment");
604 if (pTarget && pAdjustment)
606 mungeAdjustment(*pTarget, *pAdjustment);
610 //Set size-groups when all widgets have been imported
611 for (auto const& sizeGroup : m_pParserState->m_aSizeGroups)
613 std::shared_ptr<VclSizeGroup> xGroup(std::make_shared<VclSizeGroup>());
615 for (auto const& elem : sizeGroup.m_aProperties)
617 const OString &rKey = elem.first;
618 const OUString &rValue = elem.second;
619 xGroup->set_property(rKey, rValue);
622 for (auto const& elem : sizeGroup.m_aWidgets)
624 vcl::Window* pWindow = get(elem.getStr());
625 pWindow->add_to_size_group(xGroup);
629 //Set button images when everything has been imported
630 std::set<OUString> aImagesToBeRemoved;
631 for (auto const& elem : m_pParserState->m_aButtonImageWidgetMaps)
633 PushButton *pTargetButton = nullptr;
634 RadioButton *pTargetRadio = nullptr;
635 Button *pTarget = nullptr;
637 if (!elem.m_bRadio)
639 pTargetButton = get<PushButton>(elem.m_sID);
640 pTarget = pTargetButton;
642 else
644 pTargetRadio = get<RadioButton>(elem.m_sID);
645 pTarget = pTargetRadio;
648 FixedImage *pImage = get<FixedImage>(elem.m_sValue.toUtf8());
649 SAL_WARN_IF(!pTarget || !pImage,
650 "vcl", "missing elements of button/image/stock");
651 if (!pTarget || !pImage)
652 continue;
653 aImagesToBeRemoved.insert(elem.m_sValue);
655 VclBuilder::StockMap::iterator aFind = m_pParserState->m_aStockMap.find(elem.m_sValue.toUtf8());
656 if (aFind == m_pParserState->m_aStockMap.end())
658 if (!elem.m_bRadio)
660 const Image& rImage = pImage->GetImage();
661 if (rImage.GetStock() == "pan-down-symbolic")
662 pTargetButton->SetSymbol(SymbolType::SPIN_DOWN);
663 else if (rImage.GetStock() == "pan-up-symbolic")
664 pTargetButton->SetSymbol(SymbolType::SPIN_UP);
665 else
666 pTargetButton->SetModeImage(rImage);
667 if (pImage->GetStyle() & WB_SMALLSTYLE)
669 pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
670 Size aSz(pTargetButton->GetModeImage().GetSizePixel());
671 aSz.AdjustWidth(6);
672 aSz.AdjustHeight(6);
673 if (pTargetButton->get_width_request() == -1)
674 pTargetButton->set_width_request(aSz.Width());
675 if (pTargetButton->get_height_request() == -1)
676 pTargetButton->set_height_request(aSz.Height());
679 else
680 pTargetRadio->SetModeRadioImage(pImage->GetImage());
682 else
684 const stockinfo &rImageInfo = aFind->second;
685 SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
686 SAL_WARN_IF(eType == SymbolType::DONTKNOW, "vcl", "missing stock image element for button");
687 if (eType == SymbolType::DONTKNOW)
688 continue;
689 if (!elem.m_bRadio)
691 pTargetButton->SetSymbol(eType);
692 //fdo#76457 keep symbol images small e.g. tools->customize->menu
693 //but images the right size. Really the PushButton::CalcMinimumSize
694 //and PushButton::ImplDrawPushButton are the better place to handle
695 //this, but its such a train-wreck
696 if (eType != SymbolType::IMAGE)
697 pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
699 else
700 SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl.builder", "unimplemented symbol type for radiobuttons");
701 if (eType == SymbolType::IMAGE)
703 Image const aImage(StockImage::Yes,
704 mapStockToImageResource(rImageInfo.m_sStock));
705 if (!elem.m_bRadio)
706 pTargetButton->SetModeImage(aImage);
707 else
708 pTargetRadio->SetModeRadioImage(aImage);
710 switch (rImageInfo.m_nSize)
712 case 1:
713 pTarget->SetSmallSymbol();
714 break;
715 case 3:
716 // large toolbar, make bigger than normal (4)
717 pTarget->set_width_request(pTarget->GetOptimalSize().Width() * 1.5);
718 pTarget->set_height_request(pTarget->GetOptimalSize().Height() * 1.5);
719 break;
720 case 4:
721 break;
722 default:
723 SAL_WARN("vcl.builder", "unsupported image size " << rImageInfo.m_nSize);
724 break;
729 //There may be duplicate use of an Image, so we used a set to collect and
730 //now we can remove them from the tree after their final munge
731 for (auto const& elem : aImagesToBeRemoved)
733 delete_by_name(elem.toUtf8());
736 //fill in any stock icons in surviving images
737 for (auto const& elem : m_pParserState->m_aStockMap)
739 FixedImage *pImage = get<FixedImage>(elem.first);
740 SAL_WARN_IF(!pImage, "vcl", "missing elements of image/stock: " << elem.first);
741 if (!pImage)
742 continue;
744 const stockinfo &rImageInfo = elem.second;
745 if (rImageInfo.m_sStock == "gtk-missing-image")
746 continue;
748 SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
749 SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl", "unimplemented symbol type for images");
750 if (eType != SymbolType::IMAGE)
751 continue;
753 Image const aImage(StockImage::Yes,
754 mapStockToImageResource(rImageInfo.m_sStock));
755 pImage->SetImage(aImage);
758 //Set button menus when everything has been imported
759 for (auto const& elem : m_pParserState->m_aButtonMenuMaps)
761 MenuButton *pTarget = get<MenuButton>(elem.m_sID);
762 PopupMenu *pMenu = get_menu(elem.m_sValue.toUtf8());
763 SAL_WARN_IF(!pTarget || !pMenu,
764 "vcl", "missing elements of button/menu");
765 if (!pTarget || !pMenu)
766 continue;
767 pTarget->SetPopupMenu(pMenu);
770 //Remove ScrollWindow parent widgets whose children in vcl implement scrolling
771 //internally.
772 for (auto const& elem : m_pParserState->m_aRedundantParentWidgets)
774 delete_by_window(elem.first);
777 //fdo#67378 merge the label into the disclosure button
778 for (auto const& elem : m_pParserState->m_aExpanderWidgets)
780 vcl::Window *pChild = elem->get_child();
781 vcl::Window* pLabel = elem->GetWindow(GetWindowType::LastChild);
782 if (pLabel && pLabel != pChild && pLabel->GetType() == WindowType::FIXEDTEXT)
784 FixedText *pLabelWidget = static_cast<FixedText*>(pLabel);
785 elem->set_label(pLabelWidget->GetText());
786 if (pLabelWidget->IsControlFont())
787 elem->get_label_widget()->SetControlFont(pLabelWidget->GetControlFont());
788 delete_by_window(pLabel);
792 // create message dialog message area now
793 for (auto const& elem : m_pParserState->m_aMessageDialogs)
794 elem->create_message_area();
796 //drop maps, etc. that we don't need again
797 m_pParserState.reset();
799 SAL_WARN_IF(!m_sID.isEmpty() && (!m_bToplevelParentFound && !get_by_name(m_sID)), "vcl.builder",
800 "Requested top level widget \"" << m_sID << "\" not found in " << sUIFile);
802 #if defined SAL_LOG_WARN
803 if (m_bToplevelParentFound && m_pParent->IsDialog())
805 int nButtons = 0;
806 bool bHasDefButton = false;
807 for (auto const& child : m_aChildren)
809 if (isButtonType(child.m_pWindow->GetType()))
811 ++nButtons;
812 if (child.m_pWindow->GetStyle() & WB_DEFBUTTON)
814 bHasDefButton = true;
815 break;
819 SAL_WARN_IF(nButtons && !bHasDefButton, "vcl.builder", "No default button defined in " << sUIFile);
821 #endif
823 const bool bHideHelp = comphelper::LibreOfficeKit::isActive() &&
824 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
825 if (bHideHelp)
827 if (vcl::Window *pHelpButton = get("help"))
828 pHelpButton->Hide();
832 VclBuilder::~VclBuilder()
834 disposeBuilder();
837 void VclBuilder::disposeBuilder()
839 for (std::vector<WinAndId>::reverse_iterator aI = m_aChildren.rbegin(),
840 aEnd = m_aChildren.rend(); aI != aEnd; ++aI)
842 aI->m_pWindow.disposeAndClear();
844 m_aChildren.clear();
846 for (std::vector<MenuAndId>::reverse_iterator aI = m_aMenus.rbegin(),
847 aEnd = m_aMenus.rend(); aI != aEnd; ++aI)
849 aI->m_pMenu.disposeAndClear();
851 m_aMenus.clear();
852 m_pParent.clear();
855 namespace
857 bool extractHasFrame(VclBuilder::stringmap& rMap)
859 bool bHasFrame = true;
860 VclBuilder::stringmap::iterator aFind = rMap.find("has-frame");
861 if (aFind != rMap.end())
863 bHasFrame = toBool(aFind->second);
864 rMap.erase(aFind);
866 return bHasFrame;
869 bool extractDrawValue(VclBuilder::stringmap& rMap)
871 bool bDrawValue = true;
872 VclBuilder::stringmap::iterator aFind = rMap.find("draw-value");
873 if (aFind != rMap.end())
875 bDrawValue = toBool(aFind->second);
876 rMap.erase(aFind);
878 return bDrawValue;
881 OUString extractPopupMenu(VclBuilder::stringmap& rMap)
883 OUString sRet;
884 VclBuilder::stringmap::iterator aFind = rMap.find("popup");
885 if (aFind != rMap.end())
887 sRet = aFind->second;
888 rMap.erase(aFind);
890 return sRet;
893 OUString extractWidgetName(VclBuilder::stringmap& rMap)
895 OUString sRet;
896 VclBuilder::stringmap::iterator aFind = rMap.find("name");
897 if (aFind != rMap.end())
899 sRet = aFind->second;
900 rMap.erase(aFind);
902 return sRet;
905 OUString extractValuePos(VclBuilder::stringmap& rMap)
907 OUString sRet("top");
908 VclBuilder::stringmap::iterator aFind = rMap.find("value-pos");
909 if (aFind != rMap.end())
911 sRet = aFind->second;
912 rMap.erase(aFind);
914 return sRet;
917 OUString extractTypeHint(VclBuilder::stringmap &rMap)
919 OUString sRet("normal");
920 VclBuilder::stringmap::iterator aFind = rMap.find("type-hint");
921 if (aFind != rMap.end())
923 sRet = aFind->second;
924 rMap.erase(aFind);
926 return sRet;
929 bool extractResizable(VclBuilder::stringmap &rMap)
931 bool bResizable = true;
932 VclBuilder::stringmap::iterator aFind = rMap.find("resizable");
933 if (aFind != rMap.end())
935 bResizable = toBool(aFind->second);
936 rMap.erase(aFind);
938 return bResizable;
941 #if HAVE_FEATURE_DESKTOP
942 bool extractModal(VclBuilder::stringmap &rMap)
944 bool bModal = false;
945 VclBuilder::stringmap::iterator aFind = rMap.find("modal");
946 if (aFind != rMap.end())
948 bModal = toBool(aFind->second);
949 rMap.erase(aFind);
951 return bModal;
953 #endif
955 bool extractDecorated(VclBuilder::stringmap &rMap)
957 bool bDecorated = true;
958 VclBuilder::stringmap::iterator aFind = rMap.find("decorated");
959 if (aFind != rMap.end())
961 bDecorated = toBool(aFind->second);
962 rMap.erase(aFind);
964 return bDecorated;
967 bool extractCloseable(VclBuilder::stringmap &rMap)
969 bool bCloseable = true;
970 VclBuilder::stringmap::iterator aFind = rMap.find("deletable");
971 if (aFind != rMap.end())
973 bCloseable = toBool(aFind->second);
974 rMap.erase(aFind);
976 return bCloseable;
979 bool extractEntry(VclBuilder::stringmap &rMap)
981 bool bHasEntry = false;
982 VclBuilder::stringmap::iterator aFind = rMap.find("has-entry");
983 if (aFind != rMap.end())
985 bHasEntry = toBool(aFind->second);
986 rMap.erase(aFind);
988 return bHasEntry;
991 bool extractOrientation(VclBuilder::stringmap &rMap)
993 bool bVertical = false;
994 VclBuilder::stringmap::iterator aFind = rMap.find("orientation");
995 if (aFind != rMap.end())
997 bVertical = aFind->second.equalsIgnoreAsciiCase("vertical");
998 rMap.erase(aFind);
1000 return bVertical;
1003 bool extractVerticalTabPos(VclBuilder::stringmap &rMap)
1005 bool bVertical = false;
1006 VclBuilder::stringmap::iterator aFind = rMap.find("tab-pos");
1007 if (aFind != rMap.end())
1009 bVertical = aFind->second.equalsIgnoreAsciiCase("left") ||
1010 aFind->second.equalsIgnoreAsciiCase("right");
1011 rMap.erase(aFind);
1013 return bVertical;
1016 bool extractInconsistent(VclBuilder::stringmap &rMap)
1018 bool bInconsistent = false;
1019 VclBuilder::stringmap::iterator aFind = rMap.find("inconsistent");
1020 if (aFind != rMap.end())
1022 bInconsistent = toBool(aFind->second);
1023 rMap.erase(aFind);
1025 return bInconsistent;
1028 OUString extractIconName(VclBuilder::stringmap &rMap)
1030 OUString sIconName;
1031 VclBuilder::stringmap::iterator aFind = rMap.find(OString("icon-name"));
1032 if (aFind != rMap.end())
1034 sIconName = aFind->second;
1035 rMap.erase(aFind);
1037 return sIconName;
1040 OUString extractStockId(VclBuilder::stringmap &rMap)
1042 OUString sIconName;
1043 VclBuilder::stringmap::iterator aFind = rMap.find(OString("stock-id"));
1044 if (aFind != rMap.end())
1046 sIconName = aFind->second;
1047 rMap.erase(aFind);
1049 return sIconName;
1052 OUString getStockText(const OUString &rType)
1054 if (rType == "gtk-ok")
1055 return VclResId(SV_BUTTONTEXT_OK);
1056 else if (rType == "gtk-cancel")
1057 return VclResId(SV_BUTTONTEXT_CANCEL);
1058 else if (rType == "gtk-help")
1059 return VclResId(SV_BUTTONTEXT_HELP);
1060 else if (rType == "gtk-close")
1061 return VclResId(SV_BUTTONTEXT_CLOSE);
1062 else if (rType == "gtk-revert-to-saved")
1063 return VclResId(SV_BUTTONTEXT_RESET);
1064 else if (rType == "gtk-add")
1065 return VclResId(SV_BUTTONTEXT_ADD);
1066 else if (rType == "gtk-delete")
1067 return VclResId(SV_BUTTONTEXT_DELETE);
1068 else if (rType == "gtk-remove")
1069 return VclResId(SV_BUTTONTEXT_REMOVE);
1070 else if (rType == "gtk-new")
1071 return VclResId(SV_BUTTONTEXT_NEW);
1072 else if (rType == "gtk-edit")
1073 return VclResId(SV_BUTTONTEXT_EDIT);
1074 else if (rType == "gtk-apply")
1075 return VclResId(SV_BUTTONTEXT_APPLY);
1076 else if (rType == "gtk-save")
1077 return VclResId(SV_BUTTONTEXT_SAVE);
1078 else if (rType == "gtk-open")
1079 return VclResId(SV_BUTTONTEXT_OPEN);
1080 else if (rType == "gtk-undo")
1081 return VclResId(SV_BUTTONTEXT_UNDO);
1082 else if (rType == "gtk-paste")
1083 return VclResId(SV_BUTTONTEXT_PASTE);
1084 else if (rType == "gtk-media-next")
1085 return VclResId(SV_BUTTONTEXT_NEXT);
1086 else if (rType == "gtk-media-previous")
1087 return VclResId(SV_BUTTONTEXT_PREV);
1088 else if (rType == "gtk-go-up")
1089 return VclResId(SV_BUTTONTEXT_GO_UP);
1090 else if (rType == "gtk-go-down")
1091 return VclResId(SV_BUTTONTEXT_GO_DOWN);
1092 else if (rType == "gtk-clear")
1093 return VclResId(SV_BUTTONTEXT_CLEAR);
1094 else if (rType == "gtk-media-play")
1095 return VclResId(SV_BUTTONTEXT_PLAY);
1096 else if (rType == "gtk-find")
1097 return VclResId(SV_BUTTONTEXT_FIND);
1098 else if (rType == "gtk-stop")
1099 return VclResId(SV_BUTTONTEXT_STOP);
1100 else if (rType == "gtk-connect")
1101 return VclResId(SV_BUTTONTEXT_CONNECT);
1102 else if (rType == "gtk-yes")
1103 return VclResId(SV_BUTTONTEXT_YES);
1104 else if (rType == "gtk-no")
1105 return VclResId(SV_BUTTONTEXT_NO);
1106 SAL_WARN("vcl.builder", "unknown stock type: " << rType);
1107 return OUString();
1110 bool extractStock(VclBuilder::stringmap &rMap)
1112 bool bIsStock = false;
1113 VclBuilder::stringmap::iterator aFind = rMap.find(OString("use-stock"));
1114 if (aFind != rMap.end())
1116 bIsStock = toBool(aFind->second);
1117 rMap.erase(aFind);
1119 return bIsStock;
1122 WinBits extractRelief(VclBuilder::stringmap &rMap)
1124 WinBits nBits = WB_3DLOOK;
1125 VclBuilder::stringmap::iterator aFind = rMap.find(OString("relief"));
1126 if (aFind != rMap.end())
1128 if (aFind->second == "half")
1129 nBits = WB_FLATBUTTON | WB_BEVELBUTTON;
1130 else if (aFind->second == "none")
1131 nBits = WB_FLATBUTTON;
1132 rMap.erase(aFind);
1134 return nBits;
1137 OUString extractLabel(VclBuilder::stringmap &rMap)
1139 OUString sType;
1140 VclBuilder::stringmap::iterator aFind = rMap.find(OString("label"));
1141 if (aFind != rMap.end())
1143 sType = aFind->second;
1144 rMap.erase(aFind);
1146 return sType;
1149 OUString extractActionName(VclBuilder::stringmap &rMap)
1151 OUString sActionName;
1152 VclBuilder::stringmap::iterator aFind = rMap.find(OString("action-name"));
1153 if (aFind != rMap.end())
1155 sActionName = aFind->second;
1156 rMap.erase(aFind);
1158 return sActionName;
1161 bool extractVisible(VclBuilder::stringmap &rMap)
1163 bool bRet = false;
1164 VclBuilder::stringmap::iterator aFind = rMap.find(OString("visible"));
1165 if (aFind != rMap.end())
1167 bRet = toBool(aFind->second);
1168 rMap.erase(aFind);
1170 return bRet;
1173 Size extractSizeRequest(VclBuilder::stringmap &rMap)
1175 OUString sWidthRequest("0");
1176 OUString sHeightRequest("0");
1177 VclBuilder::stringmap::iterator aFind = rMap.find(OString("width-request"));
1178 if (aFind != rMap.end())
1180 sWidthRequest = aFind->second;
1181 rMap.erase(aFind);
1183 aFind = rMap.find("height-request");
1184 if (aFind != rMap.end())
1186 sHeightRequest = aFind->second;
1187 rMap.erase(aFind);
1189 return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32());
1192 OUString extractTooltipText(VclBuilder::stringmap &rMap)
1194 OUString sTooltipText;
1195 VclBuilder::stringmap::iterator aFind = rMap.find(OString("tooltip-text"));
1196 if (aFind == rMap.end())
1197 aFind = rMap.find(OString("tooltip-markup"));
1198 if (aFind != rMap.end())
1200 sTooltipText = aFind->second;
1201 rMap.erase(aFind);
1203 return sTooltipText;
1206 float extractAlignment(VclBuilder::stringmap &rMap)
1208 float f = 0.0;
1209 VclBuilder::stringmap::iterator aFind = rMap.find(OString("alignment"));
1210 if (aFind != rMap.end())
1212 f = aFind->second.toFloat();
1213 rMap.erase(aFind);
1215 return f;
1218 OUString extractTitle(VclBuilder::stringmap &rMap)
1220 OUString sTitle;
1221 VclBuilder::stringmap::iterator aFind = rMap.find(OString("title"));
1222 if (aFind != rMap.end())
1224 sTitle = aFind->second;
1225 rMap.erase(aFind);
1227 return sTitle;
1230 bool extractHeadersVisible(VclBuilder::stringmap &rMap)
1232 bool bHeadersVisible = true;
1233 VclBuilder::stringmap::iterator aFind = rMap.find(OString("headers-visible"));
1234 if (aFind != rMap.end())
1236 bHeadersVisible = toBool(aFind->second);
1237 rMap.erase(aFind);
1239 return bHeadersVisible;
1242 bool extractSortIndicator(VclBuilder::stringmap &rMap)
1244 bool bSortIndicator = false;
1245 VclBuilder::stringmap::iterator aFind = rMap.find(OString("sort-indicator"));
1246 if (aFind != rMap.end())
1248 bSortIndicator = toBool(aFind->second);
1249 rMap.erase(aFind);
1251 return bSortIndicator;
1254 bool extractClickable(VclBuilder::stringmap &rMap)
1256 bool bClickable = false;
1257 VclBuilder::stringmap::iterator aFind = rMap.find(OString("clickable"));
1258 if (aFind != rMap.end())
1260 bClickable = toBool(aFind->second);
1261 rMap.erase(aFind);
1263 return bClickable;
1266 void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame)
1268 if (!rFrame.is())
1269 return;
1271 OUString aCommand(extractActionName(rMap));
1272 if (aCommand.isEmpty())
1273 return;
1275 OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
1276 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, aModuleName);
1277 OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
1278 if (!aLabel.isEmpty())
1279 pButton->SetText(aLabel);
1281 OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(aCommand, aProperties, rFrame));
1282 if (!aTooltip.isEmpty())
1283 pButton->SetQuickHelpText(aTooltip);
1285 Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, rFrame));
1286 pButton->SetModeImage(aImage);
1288 pButton->SetCommandHandler(aCommand);
1291 VclPtr<Button> extractStockAndBuildPushButton(vcl::Window *pParent, VclBuilder::stringmap &rMap, bool bToggle, bool bLegacy)
1293 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER;
1294 if (bToggle)
1295 nBits |= WB_TOGGLE;
1297 nBits |= extractRelief(rMap);
1299 VclPtr<Button> xWindow;
1301 if (extractStock(rMap))
1303 OUString sType = extractLabel(rMap);
1304 if (bLegacy)
1306 if (sType == "gtk-ok")
1307 xWindow = VclPtr<OKButton>::Create(pParent, nBits);
1308 else if (sType == "gtk-cancel")
1309 xWindow = VclPtr<CancelButton>::Create(pParent, nBits);
1310 else if (sType == "gtk-close")
1311 xWindow = VclPtr<CloseButton>::Create(pParent, nBits);
1312 else if (sType == "gtk-help")
1313 xWindow = VclPtr<HelpButton>::Create(pParent, nBits);
1315 if (!xWindow)
1317 xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1318 xWindow->SetText(getStockText(sType));
1322 if (!xWindow)
1323 xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1324 return xWindow;
1327 VclPtr<MenuButton> extractStockAndBuildMenuButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1329 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1331 nBits |= extractRelief(rMap);
1333 VclPtr<MenuButton> xWindow = VclPtr<MenuButton>::Create(pParent, nBits);
1335 if (extractStock(rMap))
1337 xWindow->SetText(getStockText(extractLabel(rMap)));
1340 return xWindow;
1343 VclPtr<MenuButton> extractStockAndBuildMenuToggleButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1345 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1347 nBits |= extractRelief(rMap);
1349 VclPtr<MenuButton> xWindow = VclPtr<MenuToggleButton>::Create(pParent, nBits);
1351 if (extractStock(rMap))
1353 xWindow->SetText(getStockText(extractLabel(rMap)));
1356 return xWindow;
1359 WinBits extractDeferredBits(VclBuilder::stringmap &rMap)
1361 WinBits nBits = WB_3DLOOK|WB_HIDE;
1362 if (extractResizable(rMap))
1363 nBits |= WB_SIZEABLE;
1364 if (extractCloseable(rMap))
1365 nBits |= WB_CLOSEABLE;
1366 if (!extractDecorated(rMap))
1367 nBits |= WB_OWNERDRAWDECORATION;
1368 OUString sType(extractTypeHint(rMap));
1369 if (sType == "utility")
1370 nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_MOVEABLE;
1371 else if (sType == "popup-menu")
1372 nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_POPUP;
1373 else if (sType == "dock")
1374 nBits |= WB_DOCKABLE | WB_MOVEABLE;
1375 else
1376 nBits |= WB_MOVEABLE;
1377 return nBits;
1381 void VclBuilder::extractGroup(const OString &id, stringmap &rMap)
1383 VclBuilder::stringmap::iterator aFind = rMap.find(OString("group"));
1384 if (aFind != rMap.end())
1386 OUString sID = aFind->second;
1387 sal_Int32 nDelim = sID.indexOf(':');
1388 if (nDelim != -1)
1389 sID = sID.copy(0, nDelim);
1390 m_pParserState->m_aGroupMaps.emplace_back(id, sID.toUtf8());
1391 rMap.erase(aFind);
1395 void VclBuilder::connectNumericFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1397 if (!rAdjustment.isEmpty())
1398 m_pParserState->m_aNumericFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1401 void VclBuilder::connectFormattedFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1403 if (!rAdjustment.isEmpty())
1404 m_pParserState->m_aFormattedFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1407 bool VclBuilder::extractAdjustmentToMap(const OString& id, VclBuilder::stringmap& rMap, std::vector<WidgetAdjustmentMap>& rAdjustmentMap)
1409 VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1410 if (aFind != rMap.end())
1412 rAdjustmentMap.emplace_back(id, aFind->second);
1413 rMap.erase(aFind);
1414 return true;
1416 return false;
1419 namespace
1421 sal_Int32 extractActive(VclBuilder::stringmap &rMap)
1423 sal_Int32 nActiveId = 0;
1424 VclBuilder::stringmap::iterator aFind = rMap.find(OString("active"));
1425 if (aFind != rMap.end())
1427 nActiveId = aFind->second.toInt32();
1428 rMap.erase(aFind);
1430 return nActiveId;
1433 bool extractSelectable(VclBuilder::stringmap &rMap)
1435 bool bSelectable = false;
1436 VclBuilder::stringmap::iterator aFind = rMap.find(OString("selectable"));
1437 if (aFind != rMap.end())
1439 bSelectable = toBool(aFind->second);
1440 rMap.erase(aFind);
1442 return bSelectable;
1445 OUString extractAdjustment(VclBuilder::stringmap &rMap)
1447 OUString sAdjustment;
1448 VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1449 if (aFind != rMap.end())
1451 sAdjustment= aFind->second;
1452 rMap.erase(aFind);
1453 return sAdjustment;
1455 return sAdjustment;
1458 bool extractDrawIndicator(VclBuilder::stringmap &rMap)
1460 bool bDrawIndicator = false;
1461 VclBuilder::stringmap::iterator aFind = rMap.find(OString("draw-indicator"));
1462 if (aFind != rMap.end())
1464 bDrawIndicator = toBool(aFind->second);
1465 rMap.erase(aFind);
1467 return bDrawIndicator;
1471 void VclBuilder::extractModel(const OString &id, stringmap &rMap)
1473 VclBuilder::stringmap::iterator aFind = rMap.find(OString("model"));
1474 if (aFind != rMap.end())
1476 m_pParserState->m_aModelMaps.emplace_back(id, aFind->second,
1477 extractActive(rMap));
1478 rMap.erase(aFind);
1482 void VclBuilder::extractBuffer(const OString &id, stringmap &rMap)
1484 VclBuilder::stringmap::iterator aFind = rMap.find(OString("buffer"));
1485 if (aFind != rMap.end())
1487 m_pParserState->m_aTextBufferMaps.emplace_back(id, aFind->second);
1488 rMap.erase(aFind);
1492 void VclBuilder::extractStock(const OString &id, stringmap &rMap)
1494 VclBuilder::stringmap::iterator aFind = rMap.find(OString("stock"));
1495 if (aFind == rMap.end())
1496 return;
1498 stockinfo aInfo;
1499 aInfo.m_sStock = aFind->second;
1500 rMap.erase(aFind);
1501 aFind = rMap.find(OString("icon-size"));
1502 if (aFind != rMap.end())
1504 aInfo.m_nSize = aFind->second.toInt32();
1505 rMap.erase(aFind);
1507 m_pParserState->m_aStockMap[id] = aInfo;
1510 void VclBuilder::extractButtonImage(const OString &id, stringmap &rMap, bool bRadio)
1512 VclBuilder::stringmap::iterator aFind = rMap.find(OString("image"));
1513 if (aFind != rMap.end())
1515 m_pParserState->m_aButtonImageWidgetMaps.emplace_back(id, aFind->second, bRadio);
1516 rMap.erase(aFind);
1520 void VclBuilder::extractMnemonicWidget(const OString &rLabelID, stringmap &rMap)
1522 VclBuilder::stringmap::iterator aFind = rMap.find(OString("mnemonic-widget"));
1523 if (aFind != rMap.end())
1525 OUString sID = aFind->second;
1526 sal_Int32 nDelim = sID.indexOf(':');
1527 if (nDelim != -1)
1528 sID = sID.copy(0, nDelim);
1529 m_pParserState->m_aMnemonicWidgetMaps.emplace_back(rLabelID, sID);
1530 rMap.erase(aFind);
1534 vcl::Window* VclBuilder::prepareWidgetOwnScrolling(vcl::Window *pParent, WinBits &rWinStyle)
1536 //For Widgets that manage their own scrolling, if one appears as a child of
1537 //a scrolling window shoehorn that scrolling settings to this widget and
1538 //return the real parent to use
1539 if (pParent && pParent->GetType() == WindowType::SCROLLWINDOW)
1541 WinBits nScrollBits = pParent->GetStyle();
1542 nScrollBits &= (WB_AUTOHSCROLL|WB_HSCROLL|WB_AUTOVSCROLL|WB_VSCROLL);
1543 rWinStyle |= nScrollBits;
1544 if (static_cast<VclScrolledWindow*>(pParent)->HasVisibleBorder())
1545 rWinStyle |= WB_BORDER;
1546 pParent = pParent->GetParent();
1549 return pParent;
1552 void VclBuilder::cleanupWidgetOwnScrolling(vcl::Window *pScrollParent, vcl::Window *pWindow, stringmap &rMap)
1554 //remove the redundant scrolling parent
1555 sal_Int32 nWidthReq = pScrollParent->get_width_request();
1556 rMap[OString("width-request")] = OUString::number(nWidthReq);
1557 sal_Int32 nHeightReq = pScrollParent->get_height_request();
1558 rMap[OString("height-request")] = OUString::number(nHeightReq);
1560 m_pParserState->m_aRedundantParentWidgets[pScrollParent] = pWindow;
1563 #ifndef DISABLE_DYNLOADING
1565 extern "C" { static void thisModule() {} }
1567 namespace {
1569 // Don't unload the module on destruction
1570 class NoAutoUnloadModule : public osl::Module
1572 public:
1573 ~NoAutoUnloadModule() { release(); }
1578 typedef std::map<OUString, std::shared_ptr<NoAutoUnloadModule>> ModuleMap;
1579 static ModuleMap g_aModuleMap;
1581 #if ENABLE_MERGELIBS
1582 static std::shared_ptr<NoAutoUnloadModule> g_pMergedLib = std::make_shared<NoAutoUnloadModule>();
1583 #endif
1585 #ifndef SAL_DLLPREFIX
1586 # define SAL_DLLPREFIX ""
1587 #endif
1589 #endif
1591 void VclBuilder::preload()
1593 #ifndef DISABLE_DYNLOADING
1595 #if ENABLE_MERGELIBS
1596 g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1597 #else
1598 // find -name '*ui*' | xargs grep 'class=".*lo-' |
1599 // sed 's/.*class="//' | sed 's/-.*$//' | sort | uniq
1600 static const char *aWidgetLibs[] = {
1601 "sfxlo", "svtlo"
1603 for (const auto & lib : aWidgetLibs)
1605 std::unique_ptr<NoAutoUnloadModule> pModule(new NoAutoUnloadModule);
1606 OUString sModule = SAL_DLLPREFIX + OUString::createFromAscii(lib) + SAL_DLLEXTENSION;
1607 if (pModule->loadRelative(&thisModule, sModule))
1608 g_aModuleMap.insert(std::make_pair(sModule, std::move(pModule)));
1610 #endif // ENABLE_MERGELIBS
1611 #endif // DISABLE_DYNLOADING
1614 #if defined DISABLE_DYNLOADING && !HAVE_FEATURE_DESKTOP
1615 extern "C" VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name);
1616 #endif
1618 namespace
1620 // Takes a string like "sfxlo-NotebookbarToolBox"
1621 VclBuilder::customMakeWidget GetCustomMakeWidget(const OString& rName)
1623 const OString name = rName == "sfxlo-SidebarToolBox" ? "sfxlo-NotebookbarToolBox" : rName;
1624 VclBuilder::customMakeWidget pFunction = nullptr;
1625 if (sal_Int32 nDelim = name.indexOf('-'); nDelim != -1)
1627 const OString aFunction(OString::Concat("make") + name.subView(nDelim + 1));
1628 const OUString sFunction(OStringToOUString(aFunction, RTL_TEXTENCODING_UTF8));
1630 #ifndef DISABLE_DYNLOADING
1631 const OUString sModule = SAL_DLLPREFIX
1632 + OStringToOUString(name.copy(0, nDelim), RTL_TEXTENCODING_UTF8)
1633 + SAL_DLLEXTENSION;
1634 ModuleMap::iterator aI = g_aModuleMap.find(sModule);
1635 if (aI == g_aModuleMap.end())
1637 std::shared_ptr<NoAutoUnloadModule> pModule;
1638 #if ENABLE_MERGELIBS
1639 if (!g_pMergedLib->is())
1640 g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1641 if ((pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1642 g_pMergedLib->getFunctionSymbol(sFunction))))
1643 pModule = g_pMergedLib;
1644 #endif
1645 if (!pFunction)
1647 pModule = std::make_shared<NoAutoUnloadModule>();
1648 bool ok = pModule->loadRelative(&thisModule, sModule);
1649 if (!ok)
1651 #ifdef LINUX
1652 // in the case of preloading, we don't have eg. the
1653 // libcuilo.so, but still need to dlsym the symbols -
1654 // which are already in-process
1655 if (comphelper::LibreOfficeKit::isActive())
1657 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(dlsym(RTLD_DEFAULT, aFunction.getStr()));
1658 ok = !!pFunction;
1659 assert(ok && "couldn't even directly dlsym the sFunction (available via preload)");
1661 #endif
1662 assert(ok && "bad module name in .ui");
1664 else
1666 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1667 pModule->getFunctionSymbol(sFunction));
1670 g_aModuleMap.insert(std::make_pair(sModule, pModule));
1672 else
1673 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1674 aI->second->getFunctionSymbol(sFunction));
1675 #elif !HAVE_FEATURE_DESKTOP
1676 pFunction = lo_get_custom_widget_func(sFunction.toUtf8().getStr());
1677 SAL_WARN_IF(!pFunction, "vcl.builder", "Could not find " << sFunction);
1678 assert(pFunction);
1679 #else
1680 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1681 osl_getFunctionSymbol((oslModule)RTLD_DEFAULT, sFunction.pData));
1682 #endif
1684 return pFunction;
1688 VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString &name, const OString &id,
1689 stringmap &rMap)
1691 bool bIsPlaceHolder = name.isEmpty();
1692 bool bVertical = false;
1694 if (pParent && (pParent->GetType() == WindowType::TABCONTROL ||
1695 pParent->GetType() == WindowType::VERTICALTABCONTROL))
1697 bool bTopLevel(name == "GtkDialog" || name == "GtkMessageDialog" ||
1698 name == "GtkWindow" || name == "GtkPopover" || name == "GtkAssistant");
1699 if (!bTopLevel)
1701 if (pParent->GetType() == WindowType::TABCONTROL)
1703 //We have to add a page
1704 //make default pageid == position
1705 TabControl *pTabControl = static_cast<TabControl*>(pParent);
1706 sal_uInt16 nNewPageCount = pTabControl->GetPageCount()+1;
1707 sal_uInt16 nNewPageId = nNewPageCount;
1708 pTabControl->InsertPage(nNewPageId, OUString());
1709 pTabControl->SetCurPageId(nNewPageId);
1710 SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
1711 if (!bIsPlaceHolder)
1713 VclPtrInstance<TabPage> pPage(pTabControl);
1714 pPage->Show();
1716 //Make up a name for it
1717 OString sTabPageId = get_by_window(pParent) +
1718 "-page" +
1719 OString::number(nNewPageCount);
1720 m_aChildren.emplace_back(sTabPageId, pPage, false);
1721 pPage->SetHelpId(m_sHelpRoot + sTabPageId);
1723 pParent = pPage;
1725 pTabControl->SetTabPage(nNewPageId, pPage);
1728 else
1730 VerticalTabControl *pTabControl = static_cast<VerticalTabControl*>(pParent);
1731 SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
1732 if (!bIsPlaceHolder)
1733 pParent = pTabControl->GetPageParent();
1738 if (bIsPlaceHolder || name == "GtkTreeSelection")
1739 return nullptr;
1741 ToolBox *pToolBox = (pParent && pParent->GetType() == WindowType::TOOLBOX) ? static_cast<ToolBox*>(pParent) : nullptr;
1743 extractButtonImage(id, rMap, name == "GtkRadioButton");
1745 VclPtr<vcl::Window> xWindow;
1746 if (name == "GtkDialog" || name == "GtkAssistant")
1748 // WB_ALLOWMENUBAR because we don't know in advance if we will encounter
1749 // a menubar, and menubars need a BorderWindow in the toplevel, and
1750 // such border windows need to be in created during the dialog ctor
1751 WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_ALLOWMENUBAR;
1752 if (extractResizable(rMap))
1753 nBits |= WB_SIZEABLE;
1754 if (extractCloseable(rMap))
1755 nBits |= WB_CLOSEABLE;
1756 Dialog::InitFlag eInit = !pParent ? Dialog::InitFlag::NoParent : Dialog::InitFlag::Default;
1757 if (name == "GtkAssistant")
1758 xWindow = VclPtr<vcl::RoadmapWizard>::Create(pParent, nBits, eInit);
1759 else
1760 xWindow = VclPtr<Dialog>::Create(pParent, nBits, eInit);
1761 #if HAVE_FEATURE_DESKTOP
1762 if (!extractModal(rMap))
1763 xWindow->SetType(WindowType::MODELESSDIALOG);
1764 #endif
1766 else if (name == "GtkMessageDialog")
1768 WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE;
1769 if (extractResizable(rMap))
1770 nBits |= WB_SIZEABLE;
1771 VclPtr<MessageDialog> xDialog(VclPtr<MessageDialog>::Create(pParent, nBits));
1772 m_pParserState->m_aMessageDialogs.push_back(xDialog);
1773 xWindow = xDialog;
1774 #if defined _WIN32
1775 xWindow->set_border_width(3);
1776 #else
1777 xWindow->set_border_width(12);
1778 #endif
1780 else if (name == "GtkBox" || name == "GtkStatusbar")
1782 bVertical = extractOrientation(rMap);
1783 if (bVertical)
1784 xWindow = VclPtr<VclVBox>::Create(pParent);
1785 else
1786 xWindow = VclPtr<VclHBox>::Create(pParent);
1788 else if (name == "GtkPaned")
1790 bVertical = extractOrientation(rMap);
1791 if (bVertical)
1792 xWindow = VclPtr<VclVPaned>::Create(pParent);
1793 else
1794 xWindow = VclPtr<VclHPaned>::Create(pParent);
1796 else if (name == "GtkHBox")
1797 xWindow = VclPtr<VclHBox>::Create(pParent);
1798 else if (name == "GtkVBox")
1799 xWindow = VclPtr<VclVBox>::Create(pParent);
1800 else if (name == "GtkButtonBox")
1802 bVertical = extractOrientation(rMap);
1803 if (bVertical)
1804 xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1805 else
1806 xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1808 else if (name == "GtkHButtonBox")
1809 xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1810 else if (name == "GtkVButtonBox")
1811 xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1812 else if (name == "GtkGrid")
1813 xWindow = VclPtr<VclGrid>::Create(pParent);
1814 else if (name == "GtkFrame")
1815 xWindow = VclPtr<VclFrame>::Create(pParent);
1816 else if (name == "GtkExpander")
1818 VclPtrInstance<VclExpander> pExpander(pParent);
1819 m_pParserState->m_aExpanderWidgets.push_back(pExpander);
1820 xWindow = pExpander;
1822 else if (name == "GtkAlignment")
1823 xWindow = VclPtr<VclAlignment>::Create(pParent);
1824 else if (name == "GtkButton" || (!m_bLegacy && name == "GtkToggleButton"))
1826 VclPtr<Button> xButton;
1827 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1828 if (sMenu.isEmpty())
1829 xButton = extractStockAndBuildPushButton(pParent, rMap, name == "GtkToggleButton", m_bLegacy);
1830 else
1832 assert(m_bLegacy && "use GtkMenuButton");
1833 xButton = extractStockAndBuildMenuButton(pParent, rMap);
1834 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1836 xButton->SetImageAlign(ImageAlign::Left); //default to left
1837 setupFromActionName(xButton, rMap, m_xFrame);
1838 xWindow = xButton;
1840 else if (name == "GtkMenuButton")
1842 VclPtr<MenuButton> xButton;
1844 OUString sMenu = extractPopupMenu(rMap);
1845 if (!sMenu.isEmpty())
1846 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1848 OUString sType = extractWidgetName(rMap);
1849 if (sType.isEmpty())
1851 xButton = extractStockAndBuildMenuButton(pParent, rMap);
1852 xButton->SetAccessibleRole(css::accessibility::AccessibleRole::BUTTON_MENU);
1854 else
1856 xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
1859 xButton->SetImageAlign(ImageAlign::Left); //default to left
1861 if (!extractDrawIndicator(rMap))
1862 xButton->SetDropDown(PushButtonDropdownStyle::NONE);
1864 setupFromActionName(xButton, rMap, m_xFrame);
1865 xWindow = xButton;
1867 else if (name == "GtkToggleButton" && m_bLegacy)
1869 VclPtr<Button> xButton;
1870 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1871 assert(sMenu.getLength() && "not implemented yet");
1872 xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
1873 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1874 xButton->SetImageAlign(ImageAlign::Left); //default to left
1875 setupFromActionName(xButton, rMap, m_xFrame);
1876 xWindow = xButton;
1878 else if (name == "GtkRadioButton")
1880 extractGroup(id, rMap);
1881 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1882 VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, true, nBits);
1883 xButton->SetImageAlign(ImageAlign::Left); //default to left
1884 xWindow = xButton;
1886 if (::extractStock(rMap))
1888 xWindow->SetText(getStockText(extractLabel(rMap)));
1891 else if (name == "GtkCheckButton")
1893 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1894 bool bIsTriState = extractInconsistent(rMap);
1895 VclPtr<CheckBox> xCheckBox = VclPtr<CheckBox>::Create(pParent, nBits);
1896 if (bIsTriState)
1898 xCheckBox->EnableTriState(true);
1899 xCheckBox->SetState(TRISTATE_INDET);
1901 xCheckBox->SetImageAlign(ImageAlign::Left); //default to left
1903 xWindow = xCheckBox;
1905 if (::extractStock(rMap))
1907 xWindow->SetText(getStockText(extractLabel(rMap)));
1910 else if (name == "GtkSpinButton")
1912 OUString sAdjustment = extractAdjustment(rMap);
1914 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_3DLOOK|WB_SPIN|WB_REPEAT;
1915 if (extractHasFrame(rMap))
1916 nBits |= WB_BORDER;
1918 connectFormattedFormatterAdjustment(id, sAdjustment);
1919 VclPtrInstance<FormattedField> xField(pParent, nBits);
1920 xField->GetFormatter().SetMinValue(0);
1921 xWindow = xField;
1923 else if (name == "GtkLinkButton")
1924 xWindow = VclPtr<FixedHyperlink>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_NOLABEL);
1925 else if (name == "GtkComboBox" || name == "GtkComboBoxText")
1927 extractModel(id, rMap);
1929 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1931 bool bDropdown = BuilderUtils::extractDropdown(rMap);
1933 if (bDropdown)
1934 nBits |= WB_DROPDOWN;
1936 if (extractEntry(rMap))
1938 VclPtrInstance<ComboBox> xComboBox(pParent, nBits);
1939 xComboBox->EnableAutoSize(true);
1940 xWindow = xComboBox;
1942 else
1944 VclPtrInstance<ListBox> xListBox(pParent, nBits|WB_SIMPLEMODE);
1945 xListBox->EnableAutoSize(true);
1946 xWindow = xListBox;
1949 else if (name == "VclOptionalBox" || name == "sfxlo-OptionalBox")
1951 // tdf#135495 fallback sfxlo-OptionalBox to VclOptionalBox as a stopgap
1952 xWindow = VclPtr<OptionalBox>::Create(pParent);
1954 else if (name == "GtkIconView")
1956 assert(rMap.find(OString("model")) != rMap.end() && "GtkIconView must have a model");
1958 //window we want to apply the packing props for this GtkIconView to
1959 VclPtr<vcl::Window> xWindowForPackingProps;
1960 extractModel(id, rMap);
1961 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1962 //IconView manages its own scrolling,
1963 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
1965 VclPtr<IconView> xBox = VclPtr<IconView>::Create(pRealParent, nWinStyle);
1966 xWindowForPackingProps = xBox;
1968 xWindow = xBox;
1969 xBox->SetNoAutoCurEntry(true);
1970 xBox->SetQuickSearch(true);
1972 if (pRealParent != pParent)
1973 cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
1975 else if (name == "GtkTreeView")
1977 if (!m_bLegacy)
1979 assert(rMap.find(OString("model")) != rMap.end() && "GtkTreeView must have a model");
1982 //window we want to apply the packing props for this GtkTreeView to
1983 VclPtr<vcl::Window> xWindowForPackingProps;
1984 //To-Do
1985 //a) make SvHeaderTabListBox/SvTabListBox the default target for GtkTreeView
1986 //b) remove the non-drop down mode of ListBox and convert
1987 // everything over to SvHeaderTabListBox/SvTabListBox
1988 //c) remove the users of makeSvTabListBox and makeSvTreeListBox
1989 extractModel(id, rMap);
1990 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1991 if (m_bLegacy)
1993 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
1994 if (!sBorder.isEmpty())
1995 nWinStyle |= WB_BORDER;
1997 else
1999 nWinStyle |= WB_HASBUTTONS | WB_HASBUTTONSATROOT;
2001 //ListBox/SvHeaderTabListBox manages its own scrolling,
2002 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2003 if (m_bLegacy)
2005 xWindow = VclPtr<ListBox>::Create(pRealParent, nWinStyle | WB_SIMPLEMODE);
2006 xWindowForPackingProps = xWindow;
2008 else
2010 VclPtr<SvTabListBox> xBox;
2011 bool bHeadersVisible = extractHeadersVisible(rMap);
2012 if (bHeadersVisible)
2014 VclPtr<VclVBox> xContainer = VclPtr<VclVBox>::Create(pRealParent);
2015 OString containerid(id + "-container");
2016 xContainer->SetHelpId(m_sHelpRoot + containerid);
2017 m_aChildren.emplace_back(containerid, xContainer, true);
2019 VclPtrInstance<HeaderBar> xHeader(xContainer, WB_BUTTONSTYLE | WB_BORDER | WB_TABSTOP | WB_3DLOOK);
2020 xHeader->set_width_request(0); // let the headerbar width not affect the size request
2021 OString headerid(id + "-header");
2022 xHeader->SetHelpId(m_sHelpRoot + headerid);
2023 m_aChildren.emplace_back(headerid, xHeader, true);
2025 VclPtr<LclHeaderTabListBox> xHeaderBox = VclPtr<LclHeaderTabListBox>::Create(xContainer, nWinStyle);
2026 xHeaderBox->InitHeaderBar(xHeader);
2027 xContainer->set_expand(true);
2028 xHeader->Show();
2029 xContainer->Show();
2030 xBox = xHeaderBox;
2031 xWindowForPackingProps = xContainer;
2033 else
2035 xBox = VclPtr<LclTabListBox>::Create(pRealParent, nWinStyle);
2036 xWindowForPackingProps = xBox;
2038 xWindow = xBox;
2039 xBox->SetNoAutoCurEntry(true);
2040 xBox->SetQuickSearch(true);
2041 xBox->SetSpaceBetweenEntries(3);
2042 xBox->SetEntryHeight(16);
2043 xBox->SetHighlightRange(); // select over the whole width
2045 if (pRealParent != pParent)
2046 cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
2048 else if (name == "GtkTreeViewColumn")
2050 if (!m_bLegacy)
2052 SvHeaderTabListBox* pTreeView = dynamic_cast<SvHeaderTabListBox*>(pParent);
2053 if (HeaderBar* pHeaderBar = pTreeView ? pTreeView->GetHeaderBar() : nullptr)
2055 HeaderBarItemBits nBits = HeaderBarItemBits::LEFTIMAGE;
2056 if (extractClickable(rMap))
2057 nBits |= HeaderBarItemBits::CLICKABLE;
2058 if (extractSortIndicator(rMap))
2059 nBits |= HeaderBarItemBits::DOWNARROW;
2060 float fAlign = extractAlignment(rMap);
2061 if (fAlign == 0.0)
2062 nBits |= HeaderBarItemBits::LEFT;
2063 else if (fAlign == 1.0)
2064 nBits |= HeaderBarItemBits::RIGHT;
2065 else if (fAlign == 0.5)
2066 nBits |= HeaderBarItemBits::CENTER;
2067 auto nItemId = pHeaderBar->GetItemCount() + 1;
2068 OUString sTitle(extractTitle(rMap));
2069 pHeaderBar->InsertItem(nItemId, sTitle, 100, nBits);
2073 else if (name == "GtkLabel")
2075 WinBits nWinStyle = WB_CENTER|WB_VCENTER|WB_3DLOOK;
2076 extractMnemonicWidget(id, rMap);
2077 if (extractSelectable(rMap))
2078 xWindow = VclPtr<SelectableFixedText>::Create(pParent, nWinStyle);
2079 else
2080 xWindow = VclPtr<FixedText>::Create(pParent, nWinStyle);
2082 else if (name == "GtkImage")
2084 extractStock(id, rMap);
2085 xWindow = VclPtr<FixedImage>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_SCALE);
2086 //such parentless GtkImages are temps used to set icons on buttons
2087 //default them to hidden to stop e.g. insert->index entry flicking temp
2088 //full screen windows
2089 if (!pParent)
2091 rMap["visible"] = "false";
2094 else if (name == "GtkSeparator")
2096 bVertical = extractOrientation(rMap);
2097 xWindow = VclPtr<FixedLine>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2099 else if (name == "GtkScrollbar")
2101 extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2102 bVertical = extractOrientation(rMap);
2103 xWindow = VclPtr<ScrollBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2105 else if (name == "GtkProgressBar")
2107 extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2108 bVertical = extractOrientation(rMap);
2109 xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2111 else if (name == "GtkScrolledWindow")
2113 xWindow = VclPtr<VclScrolledWindow>::Create(pParent);
2115 else if (name == "GtkViewport")
2117 xWindow = VclPtr<VclViewport>::Create(pParent);
2119 else if (name == "GtkEventBox")
2121 xWindow = VclPtr<VclEventBox>::Create(pParent);
2123 else if (name == "GtkEntry")
2125 WinBits nWinStyle = WB_LEFT|WB_VCENTER|WB_3DLOOK;
2126 if (extractHasFrame(rMap))
2127 nWinStyle |= WB_BORDER;
2128 xWindow = VclPtr<Edit>::Create(pParent, nWinStyle);
2129 BuilderUtils::ensureDefaultWidthChars(rMap);
2131 else if (name == "GtkNotebook")
2133 if (!extractVerticalTabPos(rMap))
2134 xWindow = VclPtr<TabControl>::Create(pParent, WB_STDTABCONTROL|WB_3DLOOK);
2135 else
2136 xWindow = VclPtr<VerticalTabControl>::Create(pParent);
2138 else if (name == "GtkDrawingArea")
2140 xWindow = VclPtr<VclDrawingArea>::Create(pParent, WB_TABSTOP);
2142 else if (name == "GtkTextView")
2144 extractBuffer(id, rMap);
2146 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT;
2147 //VclMultiLineEdit manages its own scrolling,
2148 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2149 xWindow = VclPtr<VclMultiLineEdit>::Create(pRealParent, nWinStyle);
2150 if (pRealParent != pParent)
2151 cleanupWidgetOwnScrolling(pParent, xWindow, rMap);
2153 else if (name == "GtkSpinner")
2155 xWindow = VclPtr<Throbber>::Create(pParent, WB_3DLOOK);
2157 else if (name == "GtkScale")
2159 extractAdjustmentToMap(id, rMap, m_pParserState->m_aSliderAdjustmentMaps);
2160 bool bDrawValue = extractDrawValue(rMap);
2161 if (bDrawValue)
2163 OUString sValuePos = extractValuePos(rMap);
2164 (void)sValuePos;
2166 bVertical = extractOrientation(rMap);
2168 WinBits nWinStyle = bVertical ? WB_VERT : WB_HORZ;
2170 xWindow = VclPtr<Slider>::Create(pParent, nWinStyle);
2172 else if (name == "GtkToolbar")
2174 xWindow = VclPtr<ToolBox>::Create(pParent, WB_3DLOOK | WB_TABSTOP);
2176 else if(name == "NotebookBarAddonsToolMergePoint")
2178 customMakeWidget pFunction = GetCustomMakeWidget("sfxlo-NotebookbarToolBox");
2179 if(pFunction != nullptr)
2180 NotebookBarAddonsMerger::MergeNotebookBarAddons(pParent, pFunction, m_xFrame, *m_pNotebookBarAddonsItem, rMap);
2181 return nullptr;
2183 else if (name == "GtkToolButton" || name == "GtkMenuToolButton" ||
2184 name == "GtkToggleToolButton" || name == "GtkRadioToolButton" || name == "GtkToolItem")
2186 if (pToolBox)
2188 OUString aCommand(extractActionName(rMap));
2190 sal_uInt16 nItemId = 0;
2191 ToolBoxItemBits nBits = ToolBoxItemBits::NONE;
2192 if (name == "GtkMenuToolButton")
2193 nBits |= ToolBoxItemBits::DROPDOWN;
2194 else if (name == "GtkToggleToolButton")
2195 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::CHECKABLE;
2196 else if (name == "GtkRadioToolButton")
2197 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::RADIOCHECK;
2199 if (!aCommand.isEmpty() && m_xFrame.is())
2201 pToolBox->InsertItem(aCommand, m_xFrame, nBits, extractSizeRequest(rMap));
2202 nItemId = pToolBox->GetItemId(aCommand);
2204 else
2206 nItemId = pToolBox->GetItemCount() + 1;
2207 //TODO: ImplToolItems::size_type -> sal_uInt16!
2208 pToolBox->InsertItem(nItemId, extractLabel(rMap), nBits);
2209 if (aCommand.isEmpty() && !m_bLegacy)
2210 aCommand = OUString::fromUtf8(id);
2211 pToolBox->SetItemCommand(nItemId, aCommand);
2214 pToolBox->SetHelpId(nItemId, m_sHelpRoot + id);
2215 OUString sTooltip(extractTooltipText(rMap));
2216 if (!sTooltip.isEmpty())
2217 pToolBox->SetQuickHelpText(nItemId, sTooltip);
2219 OUString sIconName(extractIconName(rMap));
2220 if (sIconName.isEmpty())
2221 sIconName = mapStockToImageResource(extractStockId(rMap));
2222 if (!sIconName.isEmpty())
2223 pToolBox->SetItemImage(nItemId, FixedImage::loadThemeImage(sIconName));
2225 if (!extractVisible(rMap))
2226 pToolBox->HideItem(nItemId);
2228 m_pParserState->m_nLastToolbarId = nItemId;
2230 return nullptr; // no widget to be created
2233 else if (name == "GtkSeparatorToolItem")
2235 if (pToolBox)
2237 pToolBox->InsertSeparator();
2238 return nullptr; // no widget to be created
2241 else if (name == "GtkWindow")
2243 WinBits nBits = extractDeferredBits(rMap);
2244 if (nBits & WB_DOCKABLE)
2245 xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2246 else
2247 xWindow = VclPtr<FloatingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2249 else if (name == "GtkPopover")
2251 WinBits nBits = extractDeferredBits(rMap);
2252 xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_DOCKABLE|WB_MOVEABLE);
2254 else if (name == "GtkCalendar")
2256 WinBits nBits = extractDeferredBits(rMap);
2257 xWindow = VclPtr<Calendar>::Create(pParent, nBits);
2259 else
2261 if (customMakeWidget pFunction = GetCustomMakeWidget(name))
2263 pFunction(xWindow, pParent, rMap);
2264 if (xWindow->GetType() == WindowType::PUSHBUTTON)
2265 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2266 else if (xWindow->GetType() == WindowType::MENUBUTTON)
2268 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
2269 if (!sMenu.isEmpty())
2270 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
2271 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2276 SAL_INFO_IF(!xWindow, "vcl.builder", "probably need to implement " << name << " or add a make" << name << " function");
2277 if (xWindow)
2279 // child windows of disabled windows are made disabled by vcl by default, we don't want that
2280 WindowImpl *pWindowImpl = xWindow->ImplGetWindowImpl();
2281 pWindowImpl->mbDisabled = false;
2283 xWindow->SetHelpId(m_sHelpRoot + id);
2284 SAL_INFO("vcl.builder", "for name '" << name << "' and id '" << id <<
2285 "', created " << xWindow.get() << " child of " <<
2286 pParent << "(" << xWindow->ImplGetWindowImpl()->mpParent.get() << "/" <<
2287 xWindow->ImplGetWindowImpl()->mpRealParent.get() << "/" <<
2288 xWindow->ImplGetWindowImpl()->mpBorderWindow.get() << ") with helpid " <<
2289 xWindow->GetHelpId());
2290 m_aChildren.emplace_back(id, xWindow, bVertical);
2292 // if the parent was a toolbox set it as an itemwindow for the latest itemid
2293 if (pToolBox)
2295 Size aSize(xWindow->GetSizePixel());
2296 aSize.setHeight(xWindow->get_preferred_size().Height());
2297 xWindow->SetSizePixel(aSize);
2298 pToolBox->SetItemWindow(m_pParserState->m_nLastToolbarId, xWindow);
2299 pToolBox->SetItemExpand(m_pParserState->m_nLastToolbarId, true);
2302 return xWindow;
2305 namespace
2307 //return true for window types which exist in vcl but are not themselves
2308 //represented in the .ui format, i.e. only their children exist.
2309 bool isConsideredGtkPseudo(vcl::Window const *pWindow)
2311 return pWindow->GetType() == WindowType::TABPAGE;
2315 //Any properties from .ui load we couldn't set because of potential virtual methods
2316 //during ctor are applied here
2317 void VclBuilder::setDeferredProperties()
2319 if (!m_bToplevelHasDeferredProperties)
2320 return;
2321 stringmap aDeferredProperties;
2322 aDeferredProperties.swap(m_aDeferredProperties);
2323 m_bToplevelHasDeferredProperties = false;
2324 BuilderUtils::set_properties(m_pParent, aDeferredProperties);
2327 namespace BuilderUtils
2329 void set_properties(vcl::Window *pWindow, const VclBuilder::stringmap &rProps)
2331 for (auto const& prop : rProps)
2333 const OString &rKey = prop.first;
2334 const OUString &rValue = prop.second;
2335 pWindow->set_property(rKey, rValue);
2339 OUString convertMnemonicMarkup(const OUString &rIn)
2341 OUStringBuffer aRet(rIn);
2342 for (sal_Int32 nI = 0; nI < aRet.getLength(); ++nI)
2344 if (aRet[nI] == '_' && nI+1 < aRet.getLength())
2346 if (aRet[nI+1] != '_')
2347 aRet[nI] = MNEMONIC_CHAR;
2348 else
2349 aRet.remove(nI, 1);
2350 ++nI;
2353 return aRet.makeStringAndClear();
2356 OUString extractCustomProperty(VclBuilder::stringmap &rMap)
2358 OUString sCustomProperty;
2359 VclBuilder::stringmap::iterator aFind = rMap.find(OString("customproperty"));
2360 if (aFind != rMap.end())
2362 sCustomProperty = aFind->second;
2363 rMap.erase(aFind);
2365 return sCustomProperty;
2368 void ensureDefaultWidthChars(VclBuilder::stringmap &rMap)
2370 OString sWidthChars("width-chars");
2371 VclBuilder::stringmap::iterator aFind = rMap.find(sWidthChars);
2372 if (aFind == rMap.end())
2373 rMap[sWidthChars] = "25";
2376 bool extractDropdown(VclBuilder::stringmap &rMap)
2378 bool bDropdown = true;
2379 VclBuilder::stringmap::iterator aFind = rMap.find(OString("dropdown"));
2380 if (aFind != rMap.end())
2382 bDropdown = toBool(aFind->second);
2383 rMap.erase(aFind);
2385 return bDropdown;
2388 void reorderWithinParent(vcl::Window &rWindow, sal_uInt16 nNewPosition)
2390 WindowImpl *pWindowImpl = rWindow.ImplGetWindowImpl();
2391 if (pWindowImpl->mpParent != pWindowImpl->mpRealParent)
2393 assert(pWindowImpl->mpBorderWindow == pWindowImpl->mpParent);
2394 assert(pWindowImpl->mpBorderWindow->ImplGetWindowImpl()->mpParent == pWindowImpl->mpRealParent);
2395 reorderWithinParent(*pWindowImpl->mpBorderWindow, nNewPosition);
2396 return;
2398 rWindow.reorderWithinParent(nNewPosition);
2401 void reorderWithinParent(std::vector<vcl::Window*>& rChilds, bool bIsButtonBox)
2403 for (size_t i = 0; i < rChilds.size(); ++i)
2405 reorderWithinParent(*rChilds[i], i);
2407 if (!bIsButtonBox)
2408 continue;
2410 //The first member of the group for legacy code needs WB_GROUP set and the
2411 //others not
2412 WinBits nBits = rChilds[i]->GetStyle();
2413 nBits &= ~WB_GROUP;
2414 if (i == 0)
2415 nBits |= WB_GROUP;
2416 rChilds[i]->SetStyle(nBits);
2420 sal_Int16 getRoleFromName(const OString& roleName)
2422 using namespace com::sun::star::accessibility;
2424 static const std::unordered_map<OString, sal_Int16> aAtkRoleToAccessibleRole = {
2425 /* This is in atkobject.h's AtkRole order */
2426 { "invalid", AccessibleRole::UNKNOWN },
2427 { "accelerator label", AccessibleRole::UNKNOWN },
2428 { "alert", AccessibleRole::ALERT },
2429 { "animation", AccessibleRole::UNKNOWN },
2430 { "arrow", AccessibleRole::UNKNOWN },
2431 { "calendar", AccessibleRole::UNKNOWN },
2432 { "canvas", AccessibleRole::CANVAS },
2433 { "check box", AccessibleRole::CHECK_BOX },
2434 { "check menu item", AccessibleRole::CHECK_MENU_ITEM },
2435 { "color chooser", AccessibleRole::COLOR_CHOOSER },
2436 { "column header", AccessibleRole::COLUMN_HEADER },
2437 { "combo box", AccessibleRole::COMBO_BOX },
2438 { "date editor", AccessibleRole::DATE_EDITOR },
2439 { "desktop icon", AccessibleRole::DESKTOP_ICON },
2440 { "desktop frame", AccessibleRole::DESKTOP_PANE }, // ?
2441 { "dial", AccessibleRole::UNKNOWN },
2442 { "dialog", AccessibleRole::DIALOG },
2443 { "directory pane", AccessibleRole::DIRECTORY_PANE },
2444 { "drawing area", AccessibleRole::UNKNOWN },
2445 { "file chooser", AccessibleRole::FILE_CHOOSER },
2446 { "filler", AccessibleRole::FILLER },
2447 { "font chooser", AccessibleRole::FONT_CHOOSER },
2448 { "frame", AccessibleRole::FRAME },
2449 { "glass pane", AccessibleRole::GLASS_PANE },
2450 { "html container", AccessibleRole::UNKNOWN },
2451 { "icon", AccessibleRole::ICON },
2452 { "image", AccessibleRole::GRAPHIC },
2453 { "internal frame", AccessibleRole::INTERNAL_FRAME },
2454 { "label", AccessibleRole::LABEL },
2455 { "layered pane", AccessibleRole::LAYERED_PANE },
2456 { "list", AccessibleRole::LIST },
2457 { "list item", AccessibleRole::LIST_ITEM },
2458 { "menu", AccessibleRole::MENU },
2459 { "menu bar", AccessibleRole::MENU_BAR },
2460 { "menu item", AccessibleRole::MENU_ITEM },
2461 { "option pane", AccessibleRole::OPTION_PANE },
2462 { "page tab", AccessibleRole::PAGE_TAB },
2463 { "page tab list", AccessibleRole::PAGE_TAB_LIST },
2464 { "panel", AccessibleRole::PANEL }, // or SHAPE or TEXT_FRAME ?
2465 { "password text", AccessibleRole::PASSWORD_TEXT },
2466 { "popup menu", AccessibleRole::POPUP_MENU },
2467 { "progress bar", AccessibleRole::PROGRESS_BAR },
2468 { "push button", AccessibleRole::PUSH_BUTTON }, // or BUTTON_DROPDOWN or BUTTON_MENU
2469 { "radio button", AccessibleRole::RADIO_BUTTON },
2470 { "radio menu item", AccessibleRole::RADIO_MENU_ITEM },
2471 { "root pane", AccessibleRole::ROOT_PANE },
2472 { "row header", AccessibleRole::ROW_HEADER },
2473 { "scroll bar", AccessibleRole::SCROLL_BAR },
2474 { "scroll pane", AccessibleRole::SCROLL_PANE },
2475 { "separator", AccessibleRole::SEPARATOR },
2476 { "slider", AccessibleRole::SLIDER },
2477 { "split pane", AccessibleRole::SPLIT_PANE },
2478 { "spin button", AccessibleRole::SPIN_BOX }, // ?
2479 { "statusbar", AccessibleRole::STATUS_BAR },
2480 { "table", AccessibleRole::TABLE },
2481 { "table cell", AccessibleRole::TABLE_CELL },
2482 { "table column header", AccessibleRole::COLUMN_HEADER }, // approximate
2483 { "table row header", AccessibleRole::ROW_HEADER }, // approximate
2484 { "tear off menu item", AccessibleRole::UNKNOWN },
2485 { "terminal", AccessibleRole::UNKNOWN },
2486 { "text", AccessibleRole::TEXT },
2487 { "toggle button", AccessibleRole::TOGGLE_BUTTON },
2488 { "tool bar", AccessibleRole::TOOL_BAR },
2489 { "tool tip", AccessibleRole::TOOL_TIP },
2490 { "tree", AccessibleRole::TREE },
2491 { "tree table", AccessibleRole::TREE_TABLE },
2492 { "unknown", AccessibleRole::UNKNOWN },
2493 { "viewport", AccessibleRole::VIEW_PORT },
2494 { "window", AccessibleRole::WINDOW },
2495 { "header", AccessibleRole::HEADER },
2496 { "footer", AccessibleRole::FOOTER },
2497 { "paragraph", AccessibleRole::PARAGRAPH },
2498 { "ruler", AccessibleRole::RULER },
2499 { "application", AccessibleRole::UNKNOWN },
2500 { "autocomplete", AccessibleRole::UNKNOWN },
2501 { "edit bar", AccessibleRole::EDIT_BAR },
2502 { "embedded", AccessibleRole::EMBEDDED_OBJECT },
2503 { "entry", AccessibleRole::UNKNOWN },
2504 { "chart", AccessibleRole::CHART },
2505 { "caption", AccessibleRole::CAPTION },
2506 { "document frame", AccessibleRole::DOCUMENT },
2507 { "heading", AccessibleRole::HEADING },
2508 { "page", AccessibleRole::PAGE },
2509 { "section", AccessibleRole::SECTION },
2510 { "redundant object", AccessibleRole::UNKNOWN },
2511 { "form", AccessibleRole::FORM },
2512 { "link", AccessibleRole::HYPER_LINK },
2513 { "input method window", AccessibleRole::UNKNOWN },
2514 { "table row", AccessibleRole::UNKNOWN },
2515 { "tree item", AccessibleRole::TREE_ITEM },
2516 { "document spreadsheet", AccessibleRole::DOCUMENT_SPREADSHEET },
2517 { "document presentation", AccessibleRole::DOCUMENT_PRESENTATION },
2518 { "document text", AccessibleRole::DOCUMENT_TEXT },
2519 { "document web", AccessibleRole::DOCUMENT }, // approximate
2520 { "document email", AccessibleRole::DOCUMENT }, // approximate
2521 { "comment", AccessibleRole::COMMENT }, // or NOTE or END_NOTE or FOOTNOTE or SCROLL_PANE
2522 { "list box", AccessibleRole::UNKNOWN },
2523 { "grouping", AccessibleRole::GROUP_BOX },
2524 { "image map", AccessibleRole::IMAGE_MAP },
2525 { "notification", AccessibleRole::UNKNOWN },
2526 { "info bar", AccessibleRole::UNKNOWN },
2527 { "level bar", AccessibleRole::UNKNOWN },
2528 { "title bar", AccessibleRole::UNKNOWN },
2529 { "block quote", AccessibleRole::UNKNOWN },
2530 { "audio", AccessibleRole::UNKNOWN },
2531 { "video", AccessibleRole::UNKNOWN },
2532 { "definition", AccessibleRole::UNKNOWN },
2533 { "article", AccessibleRole::UNKNOWN },
2534 { "landmark", AccessibleRole::UNKNOWN },
2535 { "log", AccessibleRole::UNKNOWN },
2536 { "marquee", AccessibleRole::UNKNOWN },
2537 { "math", AccessibleRole::UNKNOWN },
2538 { "rating", AccessibleRole::UNKNOWN },
2539 { "timer", AccessibleRole::UNKNOWN },
2540 { "description list", AccessibleRole::UNKNOWN },
2541 { "description term", AccessibleRole::UNKNOWN },
2542 { "description value", AccessibleRole::UNKNOWN },
2543 { "static", AccessibleRole::STATIC },
2544 { "math fraction", AccessibleRole::UNKNOWN },
2545 { "math root", AccessibleRole::UNKNOWN },
2546 { "subscript", AccessibleRole::UNKNOWN },
2547 { "superscript", AccessibleRole::UNKNOWN },
2548 { "footnote", AccessibleRole::FOOTNOTE },
2551 auto it = aAtkRoleToAccessibleRole.find(roleName);
2552 if (it == aAtkRoleToAccessibleRole.end())
2553 return AccessibleRole::UNKNOWN;
2554 return it->second;
2558 VclPtr<vcl::Window> VclBuilder::insertObject(vcl::Window *pParent, const OString &rClass,
2559 const OString &rID, stringmap &rProps, stringmap &rPango, stringmap &rAtk)
2561 VclPtr<vcl::Window> pCurrentChild;
2563 if (m_pParent && !isConsideredGtkPseudo(m_pParent) && !m_sID.isEmpty() && rID == m_sID)
2565 pCurrentChild = m_pParent;
2567 //toplevels default to resizable and apparently you can't change them
2568 //afterwards, so we need to wait until now before we can truly
2569 //initialize the dialog.
2570 if (pParent && pParent->IsSystemWindow())
2572 SystemWindow *pSysWin = static_cast<SystemWindow*>(pCurrentChild.get());
2573 pSysWin->doDeferredInit(extractDeferredBits(rProps));
2574 m_bToplevelHasDeferredInit = false;
2576 else if (pParent && pParent->IsDockingWindow())
2578 DockingWindow *pDockWin = static_cast<DockingWindow*>(pCurrentChild.get());
2579 pDockWin->doDeferredInit(extractDeferredBits(rProps));
2580 m_bToplevelHasDeferredInit = false;
2583 if (pCurrentChild->GetHelpId().isEmpty())
2585 pCurrentChild->SetHelpId(m_sHelpRoot + m_sID);
2586 SAL_INFO("vcl.builder", "for toplevel dialog " << this << " " <<
2587 rID << ", set helpid " << pCurrentChild->GetHelpId());
2589 m_bToplevelParentFound = true;
2591 else
2593 //if we're being inserting under a toplevel dialog whose init is
2594 //deferred due to waiting to encounter it in this .ui, and it hasn't
2595 //been seen yet, then make unattached widgets parent-less toplevels
2596 if (pParent == m_pParent.get() && m_bToplevelHasDeferredInit)
2597 pParent = nullptr;
2598 pCurrentChild = makeObject(pParent, rClass, rID, rProps);
2601 if (pCurrentChild)
2603 pCurrentChild->set_id(OStringToOUString(rID, RTL_TEXTENCODING_UTF8));
2604 if (pCurrentChild == m_pParent.get() && m_bToplevelHasDeferredProperties)
2605 m_aDeferredProperties = rProps;
2606 else
2607 BuilderUtils::set_properties(pCurrentChild, rProps);
2609 for (auto const& elem : rPango)
2611 const OString &rKey = elem.first;
2612 const OUString &rValue = elem.second;
2613 pCurrentChild->set_font_attribute(rKey, rValue);
2616 m_pParserState->m_aAtkInfo[pCurrentChild] = rAtk;
2619 rProps.clear();
2620 rPango.clear();
2621 rAtk.clear();
2623 if (!pCurrentChild)
2625 bool bToolbarParent = (pParent && pParent->GetType() == WindowType::TOOLBOX);
2626 pCurrentChild = (m_aChildren.empty() || bToolbarParent) ? pParent : m_aChildren.back().m_pWindow.get();
2628 return pCurrentChild;
2631 void VclBuilder::handleTabChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
2633 TabControl *pTabControl = pParent && pParent->GetType() == WindowType::TABCONTROL ?
2634 static_cast<TabControl*>(pParent) : nullptr;
2636 std::vector<OString> sIDs;
2638 int nLevel = 1;
2639 stringmap aProperties;
2640 stringmap aAtkProperties;
2641 std::vector<vcl::EnumContext::Context> context;
2643 while(true)
2645 xmlreader::Span name;
2646 int nsId;
2648 xmlreader::XmlReader::Result res = reader.nextItem(
2649 xmlreader::XmlReader::Text::NONE, &name, &nsId);
2651 if (res == xmlreader::XmlReader::Result::Begin)
2653 ++nLevel;
2654 if (name == "object")
2656 while (reader.nextAttribute(&nsId, &name))
2658 if (name == "id")
2660 name = reader.getAttributeValue(false);
2661 OString sID(name.begin, name.length);
2662 sal_Int32 nDelim = sID.indexOf(':');
2663 if (nDelim != -1)
2665 OString sPattern = sID.copy(nDelim+1);
2666 aProperties[OString("customproperty")] = OUString::fromUtf8(sPattern);
2667 sID = sID.copy(0, nDelim);
2669 sIDs.push_back(sID);
2673 else if (name == "style")
2675 int nPriority = 0;
2676 context = handleStyle(reader, nPriority);
2677 --nLevel;
2679 else if (name == "property")
2680 collectProperty(reader, aProperties);
2681 else if (pTabControl && name == "child")
2683 // just to collect the atk properties (if any) for the label
2684 handleChild(nullptr, &aAtkProperties, reader);
2685 --nLevel;
2689 if (res == xmlreader::XmlReader::Result::End)
2690 --nLevel;
2692 if (!nLevel)
2693 break;
2695 if (res == xmlreader::XmlReader::Result::Done)
2696 break;
2699 if (!pParent)
2700 return;
2702 VerticalTabControl *pVerticalTabControl = pParent->GetType() == WindowType::VERTICALTABCONTROL ?
2703 static_cast<VerticalTabControl*>(pParent) : nullptr;
2704 assert(pTabControl || pVerticalTabControl);
2705 VclBuilder::stringmap::iterator aFind = aProperties.find(OString("label"));
2706 if (aFind != aProperties.end())
2708 if (pTabControl)
2710 sal_uInt16 nPageId = pTabControl->GetCurPageId();
2711 pTabControl->SetPageText(nPageId, aFind->second);
2712 pTabControl->SetPageName(nPageId, sIDs.back());
2713 if (!context.empty())
2715 TabPage* pPage = pTabControl->GetTabPage(nPageId);
2716 pPage->SetContext(context);
2719 for (auto const& prop : aAtkProperties)
2721 const OString &rKey = prop.first;
2722 const OUString &rValue = prop.second;
2724 if (rKey == "AtkObject::accessible-name")
2725 pTabControl->SetAccessibleName(nPageId, rValue);
2726 else if (rKey == "AtkObject::accessible-description")
2727 pTabControl->SetAccessibleDescription(nPageId, rValue);
2728 else
2729 SAL_INFO("vcl.builder", "unhandled atk property: " << rKey);
2733 else
2735 OUString sLabel(aFind->second);
2736 OUString sIconName(extractIconName(aProperties));
2737 OUString sTooltip(extractTooltipText(aProperties));
2738 pVerticalTabControl->InsertPage(sIDs.front(), sLabel, FixedImage::loadThemeImage(sIconName), sTooltip,
2739 pVerticalTabControl->GetPageParent()->GetWindow(GetWindowType::LastChild));
2742 else
2744 if (pTabControl)
2745 pTabControl->RemovePage(pTabControl->GetCurPageId());
2749 //so that tabbing between controls goes in a visually sensible sequence
2750 //we sort these into a best-tab-order sequence
2751 bool VclBuilder::sortIntoBestTabTraversalOrder::operator()(const vcl::Window *pA, const vcl::Window *pB) const
2753 //sort child order within parent list by grid position
2754 sal_Int32 nTopA = pA->get_grid_top_attach();
2755 sal_Int32 nTopB = pB->get_grid_top_attach();
2756 if (nTopA < nTopB)
2757 return true;
2758 if (nTopA > nTopB)
2759 return false;
2760 sal_Int32 nLeftA = pA->get_grid_left_attach();
2761 sal_Int32 nLeftB = pB->get_grid_left_attach();
2762 if (nLeftA < nLeftB)
2763 return true;
2764 if (nLeftA > nLeftB)
2765 return false;
2766 //sort into two groups of pack start and pack end
2767 VclPackType ePackA = pA->get_pack_type();
2768 VclPackType ePackB = pB->get_pack_type();
2769 if (ePackA < ePackB)
2770 return true;
2771 if (ePackA > ePackB)
2772 return false;
2773 bool bVerticalContainer = m_pBuilder->get_window_packing_data(pA->GetParent()).m_bVerticalOrient;
2774 bool bPackA = pA->get_secondary();
2775 bool bPackB = pB->get_secondary();
2776 if (!bVerticalContainer)
2778 //for horizontal boxes group secondaries before primaries
2779 if (bPackA > bPackB)
2780 return true;
2781 if (bPackA < bPackB)
2782 return false;
2784 else
2786 //for vertical boxes group secondaries after primaries
2787 if (bPackA < bPackB)
2788 return true;
2789 if (bPackA > bPackB)
2790 return false;
2792 //honour relative box positions with pack group, (numerical order is reversed
2793 //for VclPackType::End, they are packed from the end back, but here we need
2794 //them in visual layout order so that tabbing works as expected)
2795 sal_Int32 nPackA = m_pBuilder->get_window_packing_data(pA).m_nPosition;
2796 sal_Int32 nPackB = m_pBuilder->get_window_packing_data(pB).m_nPosition;
2797 if (nPackA < nPackB)
2798 return ePackA == VclPackType::Start;
2799 if (nPackA > nPackB)
2800 return ePackA != VclPackType::Start;
2801 //sort labels of Frames before body
2802 if (pA->GetParent() == pB->GetParent())
2804 const VclFrame *pFrameParent = dynamic_cast<const VclFrame*>(pA->GetParent());
2805 if (pFrameParent)
2807 const vcl::Window *pLabel = pFrameParent->get_label_widget();
2808 int nFramePosA = (pA == pLabel) ? 0 : 1;
2809 int nFramePosB = (pB == pLabel) ? 0 : 1;
2810 return nFramePosA < nFramePosB;
2813 return false;
2816 void VclBuilder::handleChild(vcl::Window *pParent, stringmap* pAtkProps, xmlreader::XmlReader &reader)
2818 vcl::Window *pCurrentChild = nullptr;
2820 xmlreader::Span name;
2821 int nsId;
2822 OString sType, sInternalChild;
2824 while (reader.nextAttribute(&nsId, &name))
2826 if (name == "type")
2828 name = reader.getAttributeValue(false);
2829 sType = OString(name.begin, name.length);
2831 else if (name == "internal-child")
2833 name = reader.getAttributeValue(false);
2834 sInternalChild = OString(name.begin, name.length);
2838 if (sType == "tab")
2840 handleTabChild(pParent, reader);
2841 return;
2844 int nLevel = 1;
2845 while(true)
2847 xmlreader::XmlReader::Result res = reader.nextItem(
2848 xmlreader::XmlReader::Text::NONE, &name, &nsId);
2850 if (res == xmlreader::XmlReader::Result::Begin)
2852 if (name == "object" || name == "placeholder")
2854 pCurrentChild = handleObject(pParent, pAtkProps, reader).get();
2856 bool bObjectInserted = pCurrentChild && pParent != pCurrentChild;
2858 if (bObjectInserted)
2860 //Internal-children default in glade to not having their visible bits set
2861 //even though they are visible (generally anyway)
2862 if (!sInternalChild.isEmpty())
2863 pCurrentChild->Show();
2865 //Select the first page if it's a notebook
2866 if (pCurrentChild->GetType() == WindowType::TABCONTROL)
2868 TabControl *pTabControl = static_cast<TabControl*>(pCurrentChild);
2869 pTabControl->SetCurPageId(pTabControl->GetPageId(0));
2871 //To-Do add reorder capability to the TabControl
2873 else
2875 // We want to sort labels before contents of frames
2876 // for keyboard traversal, especially if there
2877 // are multiple widgets using the same mnemonic
2878 if (sType == "label")
2880 if (VclFrame *pFrameParent = dynamic_cast<VclFrame*>(pParent))
2881 pFrameParent->designate_label(pCurrentChild);
2883 if (sInternalChild.startsWith("vbox") || sInternalChild.startsWith("messagedialog-vbox"))
2885 if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pParent))
2886 pBoxParent->set_content_area(static_cast<VclBox*>(pCurrentChild)); // FIXME-VCLPTR
2888 else if (sInternalChild.startsWith("action_area") || sInternalChild.startsWith("messagedialog-action_area"))
2890 vcl::Window *pContentArea = pCurrentChild->GetParent();
2891 if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pContentArea ? pContentArea->GetParent() : nullptr))
2893 pBoxParent->set_action_area(static_cast<VclButtonBox*>(pCurrentChild)); // FIXME-VCLPTR
2897 bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pCurrentChild) != nullptr;
2899 //To-Do make reorder a virtual in Window, move this foo
2900 //there and see above
2901 std::vector<vcl::Window*> aChilds;
2902 for (vcl::Window* pChild = pCurrentChild->GetWindow(GetWindowType::FirstChild); pChild;
2903 pChild = pChild->GetWindow(GetWindowType::Next))
2905 if (bIsButtonBox)
2907 if (PushButton* pPushButton = dynamic_cast<PushButton*>(pChild))
2908 pPushButton->setAction(true);
2911 aChilds.push_back(pChild);
2914 //sort child order within parent so that tabbing
2915 //between controls goes in a visually sensible sequence
2916 std::stable_sort(aChilds.begin(), aChilds.end(), sortIntoBestTabTraversalOrder(this));
2917 BuilderUtils::reorderWithinParent(aChilds, bIsButtonBox);
2921 else if (name == "packing")
2923 handlePacking(pCurrentChild, pParent, reader);
2925 else if (name == "interface")
2927 while (reader.nextAttribute(&nsId, &name))
2929 if (name == "domain")
2931 name = reader.getAttributeValue(false);
2932 sType = OString(name.begin, name.length);
2933 m_pParserState->m_aResLocale = Translate::Create(sType.getStr());
2936 ++nLevel;
2938 else
2939 ++nLevel;
2942 if (res == xmlreader::XmlReader::Result::End)
2943 --nLevel;
2945 if (!nLevel)
2946 break;
2948 if (res == xmlreader::XmlReader::Result::Done)
2949 break;
2953 void VclBuilder::collectPangoAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
2955 xmlreader::Span span;
2956 int nsId;
2958 OString sProperty;
2959 OString sValue;
2961 while (reader.nextAttribute(&nsId, &span))
2963 if (span == "name")
2965 span = reader.getAttributeValue(false);
2966 sProperty = OString(span.begin, span.length);
2968 else if (span == "value")
2970 span = reader.getAttributeValue(false);
2971 sValue = OString(span.begin, span.length);
2975 if (!sProperty.isEmpty())
2976 rMap[sProperty] = OUString::fromUtf8(sValue);
2979 void VclBuilder::collectAtkRelationAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
2981 xmlreader::Span span;
2982 int nsId;
2984 OString sProperty;
2985 OString sValue;
2987 while (reader.nextAttribute(&nsId, &span))
2989 if (span == "type")
2991 span = reader.getAttributeValue(false);
2992 sProperty = OString(span.begin, span.length);
2994 else if (span == "target")
2996 span = reader.getAttributeValue(false);
2997 sValue = OString(span.begin, span.length);
2998 sal_Int32 nDelim = sValue.indexOf(':');
2999 if (nDelim != -1)
3000 sValue = sValue.copy(0, nDelim);
3004 if (!sProperty.isEmpty())
3005 rMap[sProperty] = OUString::fromUtf8(sValue);
3008 void VclBuilder::collectAtkRoleAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3010 xmlreader::Span span;
3011 int nsId;
3013 OString sProperty;
3015 while (reader.nextAttribute(&nsId, &span))
3017 if (span == "type")
3019 span = reader.getAttributeValue(false);
3020 sProperty = OString(span.begin, span.length);
3024 if (!sProperty.isEmpty())
3025 rMap["role"] = OUString::fromUtf8(sProperty);
3028 void VclBuilder::handleRow(xmlreader::XmlReader &reader, const OString &rID)
3030 int nLevel = 1;
3032 ListStore::row aRow;
3034 while(true)
3036 xmlreader::Span name;
3037 int nsId;
3039 xmlreader::XmlReader::Result res = reader.nextItem(
3040 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3042 if (res == xmlreader::XmlReader::Result::Done)
3043 break;
3045 if (res == xmlreader::XmlReader::Result::Begin)
3047 ++nLevel;
3048 if (name == "col")
3050 bool bTranslated = false;
3051 sal_uInt32 nId = 0;
3052 OString sContext;
3054 while (reader.nextAttribute(&nsId, &name))
3056 if (name == "id")
3058 name = reader.getAttributeValue(false);
3059 nId = OString(name.begin, name.length).toUInt32();
3061 else if (nId == 0 && name == "translatable" && reader.getAttributeValue(false) == "yes")
3063 bTranslated = true;
3065 else if (name == "context")
3067 name = reader.getAttributeValue(false);
3068 sContext = OString(name.begin, name.length);
3072 (void)reader.nextItem(
3073 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3075 OString sValue(name.begin, name.length);
3076 OUString sFinalValue;
3077 if (bTranslated)
3079 if (!sContext.isEmpty())
3080 sValue = sContext + "\004" + sValue;
3081 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3083 else
3084 sFinalValue = OUString::fromUtf8(sValue);
3087 if (aRow.size() < nId+1)
3088 aRow.resize(nId+1);
3089 aRow[nId] = sFinalValue;
3093 if (res == xmlreader::XmlReader::Result::End)
3095 --nLevel;
3098 if (!nLevel)
3099 break;
3102 m_pParserState->m_aModels[rID].m_aEntries.push_back(aRow);
3105 void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OString &rID, const OString &rClass)
3107 int nLevel = 1;
3109 while(true)
3111 xmlreader::Span name;
3112 int nsId;
3114 xmlreader::XmlReader::Result res = reader.nextItem(
3115 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3117 if (res == xmlreader::XmlReader::Result::Done)
3118 break;
3120 if (res == xmlreader::XmlReader::Result::Begin)
3122 if (name == "row")
3124 bool bNotTreeStore = rClass != "GtkTreeStore";
3125 if (bNotTreeStore)
3126 handleRow(reader, rID);
3127 assert(bNotTreeStore && "gtk, as the time of writing, doesn't support data in GtkTreeStore serialization");
3129 else
3130 ++nLevel;
3133 if (res == xmlreader::XmlReader::Result::End)
3135 --nLevel;
3138 if (!nLevel)
3139 break;
3143 VclBuilder::stringmap VclBuilder::handleAtkObject(xmlreader::XmlReader &reader)
3145 int nLevel = 1;
3147 stringmap aProperties;
3149 while (true)
3151 xmlreader::Span name;
3152 int nsId;
3154 xmlreader::XmlReader::Result res = reader.nextItem(
3155 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3157 if (res == xmlreader::XmlReader::Result::Done)
3158 break;
3160 if (res == xmlreader::XmlReader::Result::Begin)
3162 ++nLevel;
3163 if (name == "property")
3164 collectProperty(reader, aProperties);
3167 if (res == xmlreader::XmlReader::Result::End)
3169 --nLevel;
3172 if (!nLevel)
3173 break;
3176 return aProperties;
3179 void VclBuilder::applyAtkProperties(vcl::Window *pWindow, const stringmap& rProperties)
3181 assert(pWindow);
3182 for (auto const& prop : rProperties)
3184 const OString &rKey = prop.first;
3185 const OUString &rValue = prop.second;
3187 if (pWindow && rKey.match("AtkObject::"))
3188 pWindow->set_property(rKey.copy(RTL_CONSTASCII_LENGTH("AtkObject::")), rValue);
3189 else
3190 SAL_WARN("vcl.builder", "unhandled atk prop: " << rKey);
3194 std::vector<ComboBoxTextItem> VclBuilder::handleItems(xmlreader::XmlReader &reader) const
3196 int nLevel = 1;
3198 std::vector<ComboBoxTextItem> aItems;
3200 while(true)
3202 xmlreader::Span name;
3203 int nsId;
3205 xmlreader::XmlReader::Result res = reader.nextItem(
3206 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3208 if (res == xmlreader::XmlReader::Result::Done)
3209 break;
3211 if (res == xmlreader::XmlReader::Result::Begin)
3213 ++nLevel;
3214 if (name == "item")
3216 bool bTranslated = false;
3217 OString sContext, sId;
3219 while (reader.nextAttribute(&nsId, &name))
3221 if (name == "translatable" && reader.getAttributeValue(false) == "yes")
3223 bTranslated = true;
3225 else if (name == "context")
3227 name = reader.getAttributeValue(false);
3228 sContext = OString(name.begin, name.length);
3230 else if (name == "id")
3232 name = reader.getAttributeValue(false);
3233 sId = OString(name.begin, name.length);
3237 (void)reader.nextItem(
3238 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3240 OString sValue(name.begin, name.length);
3241 OUString sFinalValue;
3242 if (bTranslated)
3244 if (!sContext.isEmpty())
3245 sValue = sContext + "\004" + sValue;
3246 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3248 else
3249 sFinalValue = OUString::fromUtf8(sValue);
3251 if (m_pStringReplace)
3252 sFinalValue = (*m_pStringReplace)(sFinalValue);
3254 aItems.emplace_back(sFinalValue, sId);
3258 if (res == xmlreader::XmlReader::Result::End)
3260 --nLevel;
3263 if (!nLevel)
3264 break;
3267 return aItems;
3270 VclPtr<Menu> VclBuilder::handleMenu(xmlreader::XmlReader &reader, const OString &rID, bool bMenuBar)
3272 VclPtr<Menu> pCurrentMenu;
3273 if (bMenuBar)
3274 pCurrentMenu = VclPtr<MenuBar>::Create();
3275 else
3276 pCurrentMenu = VclPtr<PopupMenu>::Create();
3278 int nLevel = 1;
3280 stringmap aProperties;
3282 while(true)
3284 xmlreader::Span name;
3285 int nsId;
3287 xmlreader::XmlReader::Result res = reader.nextItem(
3288 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3290 if (res == xmlreader::XmlReader::Result::Done)
3291 break;
3293 if (res == xmlreader::XmlReader::Result::Begin)
3295 if (name == "child")
3297 handleMenuChild(pCurrentMenu, reader);
3299 else
3301 ++nLevel;
3302 if (name == "property")
3303 collectProperty(reader, aProperties);
3307 if (res == xmlreader::XmlReader::Result::End)
3309 --nLevel;
3312 if (!nLevel)
3313 break;
3316 m_aMenus.emplace_back(rID, pCurrentMenu);
3318 return pCurrentMenu;
3321 void VclBuilder::handleMenuChild(Menu *pParent, xmlreader::XmlReader &reader)
3323 xmlreader::Span name;
3324 int nsId;
3326 int nLevel = 1;
3327 while(true)
3329 xmlreader::XmlReader::Result res = reader.nextItem(
3330 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3332 if (res == xmlreader::XmlReader::Result::Begin)
3334 if (name == "object" || name == "placeholder")
3336 handleMenuObject(pParent, reader);
3338 else
3339 ++nLevel;
3342 if (res == xmlreader::XmlReader::Result::End)
3343 --nLevel;
3345 if (!nLevel)
3346 break;
3348 if (res == xmlreader::XmlReader::Result::Done)
3349 break;
3353 void VclBuilder::handleMenuObject(Menu *pParent, xmlreader::XmlReader &reader)
3355 OString sClass;
3356 OString sID;
3357 OUString sCustomProperty;
3358 PopupMenu *pSubMenu = nullptr;
3360 xmlreader::Span name;
3361 int nsId;
3363 while (reader.nextAttribute(&nsId, &name))
3365 if (name == "class")
3367 name = reader.getAttributeValue(false);
3368 sClass = OString(name.begin, name.length);
3370 else if (name == "id")
3372 name = reader.getAttributeValue(false);
3373 sID = OString(name.begin, name.length);
3374 sal_Int32 nDelim = sID.indexOf(':');
3375 if (nDelim != -1)
3377 sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3378 sID = sID.copy(0, nDelim);
3383 int nLevel = 1;
3385 stringmap aProperties;
3386 stringmap aAtkProperties;
3387 accelmap aAccelerators;
3389 if (!sCustomProperty.isEmpty())
3390 aProperties[OString("customproperty")] = sCustomProperty;
3392 while(true)
3394 xmlreader::XmlReader::Result res = reader.nextItem(
3395 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3397 if (res == xmlreader::XmlReader::Result::Done)
3398 break;
3400 if (res == xmlreader::XmlReader::Result::Begin)
3402 if (name == "child")
3404 size_t nChildMenuIdx = m_aMenus.size();
3405 handleChild(nullptr, &aAtkProperties, reader);
3406 bool bSubMenuInserted = m_aMenus.size() > nChildMenuIdx;
3407 if (bSubMenuInserted)
3408 pSubMenu = dynamic_cast<PopupMenu*>(m_aMenus[nChildMenuIdx].m_pMenu.get());
3410 else
3412 ++nLevel;
3413 if (name == "property")
3414 collectProperty(reader, aProperties);
3415 else if (name == "accelerator")
3416 collectAccelerator(reader, aAccelerators);
3420 if (res == xmlreader::XmlReader::Result::End)
3422 --nLevel;
3425 if (!nLevel)
3426 break;
3429 insertMenuObject(pParent, pSubMenu, sClass, sID, aProperties, aAtkProperties, aAccelerators);
3432 void VclBuilder::handleSizeGroup(xmlreader::XmlReader &reader)
3434 m_pParserState->m_aSizeGroups.emplace_back();
3435 SizeGroup &rSizeGroup = m_pParserState->m_aSizeGroups.back();
3437 int nLevel = 1;
3439 while(true)
3441 xmlreader::Span name;
3442 int nsId;
3444 xmlreader::XmlReader::Result res = reader.nextItem(
3445 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3447 if (res == xmlreader::XmlReader::Result::Done)
3448 break;
3450 if (res == xmlreader::XmlReader::Result::Begin)
3452 ++nLevel;
3453 if (name == "widget")
3455 while (reader.nextAttribute(&nsId, &name))
3457 if (name == "name")
3459 name = reader.getAttributeValue(false);
3460 OString sWidget(name.begin, name.length);
3461 sal_Int32 nDelim = sWidget.indexOf(':');
3462 if (nDelim != -1)
3463 sWidget = sWidget.copy(0, nDelim);
3464 rSizeGroup.m_aWidgets.push_back(sWidget);
3468 else
3470 if (name == "property")
3471 collectProperty(reader, rSizeGroup.m_aProperties);
3475 if (res == xmlreader::XmlReader::Result::End)
3477 --nLevel;
3480 if (!nLevel)
3481 break;
3485 namespace
3487 vcl::KeyCode makeKeyCode(const std::pair<OString,OString> &rKey)
3489 bool bShift = rKey.second.indexOf("GDK_SHIFT_MASK") != -1;
3490 bool bMod1 = rKey.second.indexOf("GDK_CONTROL_MASK") != -1;
3491 bool bMod2 = rKey.second.indexOf("GDK_MOD1_MASK") != -1;
3492 bool bMod3 = rKey.second.indexOf("GDK_MOD2_MASK") != -1;
3494 if (rKey.first == "Insert")
3495 return vcl::KeyCode(KEY_INSERT, bShift, bMod1, bMod2, bMod3);
3496 else if (rKey.first == "Delete")
3497 return vcl::KeyCode(KEY_DELETE, bShift, bMod1, bMod2, bMod3);
3498 else if (rKey.first == "Return")
3499 return vcl::KeyCode(KEY_RETURN, bShift, bMod1, bMod2, bMod3);
3500 else if (rKey.first == "Up")
3501 return vcl::KeyCode(KEY_UP, bShift, bMod1, bMod2, bMod3);
3502 else if (rKey.first == "Down")
3503 return vcl::KeyCode(KEY_DOWN, bShift, bMod1, bMod2, bMod3);
3504 else if (rKey.first == "Left")
3505 return vcl::KeyCode(KEY_LEFT, bShift, bMod1, bMod2, bMod3);
3506 else if (rKey.first == "Right")
3507 return vcl::KeyCode(KEY_RIGHT, bShift, bMod1, bMod2, bMod3);
3508 else if (rKey.first == "asterisk")
3509 return vcl::KeyCode(KEY_MULTIPLY, bShift, bMod1, bMod2, bMod3);
3510 else if (rKey.first.getLength() > 1 && rKey.first[0] == 'F')
3512 sal_uInt32 nIndex = rKey.first.copy(1).toUInt32();
3513 assert(nIndex >= 1 && nIndex <= 26);
3514 return vcl::KeyCode(KEY_F1 + nIndex - 1, bShift, bMod1, bMod2, bMod3);
3517 assert (rKey.first.getLength() == 1);
3518 char cChar = rKey.first.toChar();
3520 if (cChar >= 'a' && cChar <= 'z')
3521 return vcl::KeyCode(KEY_A + (cChar - 'a'), bShift, bMod1, bMod2, bMod3);
3522 else if (cChar >= 'A' && cChar <= 'Z')
3523 return vcl::KeyCode(KEY_A + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3524 else if (cChar >= '0' && cChar <= '9')
3525 return vcl::KeyCode(KEY_0 + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3527 return vcl::KeyCode(cChar, bShift, bMod1, bMod2, bMod3);
3531 void VclBuilder::insertMenuObject(Menu *pParent, PopupMenu *pSubMenu, const OString &rClass, const OString &rID,
3532 stringmap &rProps, stringmap &rAtkProps, accelmap &rAccels)
3534 sal_uInt16 nOldCount = pParent->GetItemCount();
3535 sal_uInt16 nNewId = ++m_pParserState->m_nLastMenuItemId;
3537 if(rClass == "NotebookBarAddonsMenuMergePoint")
3539 NotebookBarAddonsMerger::MergeNotebookBarMenuAddons(pParent, nNewId, rID, *m_pNotebookBarAddonsItem);
3540 m_pParserState->m_nLastMenuItemId = pParent->GetItemCount();
3542 else if (rClass == "GtkMenuItem")
3544 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3545 OUString aCommand(extractActionName(rProps));
3546 pParent->InsertItem(nNewId, sLabel, MenuItemBits::NONE , rID);
3547 pParent->SetItemCommand(nNewId, aCommand);
3548 if (pSubMenu)
3549 pParent->SetPopupMenu(nNewId, pSubMenu);
3551 else if (rClass == "GtkCheckMenuItem")
3553 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3554 OUString aCommand(extractActionName(rProps));
3555 pParent->InsertItem(nNewId, sLabel, MenuItemBits::CHECKABLE, rID);
3556 pParent->SetItemCommand(nNewId, aCommand);
3558 else if (rClass == "GtkRadioMenuItem")
3560 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3561 OUString aCommand(extractActionName(rProps));
3562 pParent->InsertItem(nNewId, sLabel, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, rID);
3563 pParent->SetItemCommand(nNewId, aCommand);
3565 else if (rClass == "GtkSeparatorMenuItem")
3567 pParent->InsertSeparator(rID);
3570 SAL_WARN_IF(nOldCount == pParent->GetItemCount(), "vcl.builder", "probably need to implement " << rClass);
3572 if (nOldCount != pParent->GetItemCount())
3574 pParent->SetHelpId(nNewId, m_sHelpRoot + rID);
3575 if (!extractVisible(rProps))
3576 pParent->HideItem(nNewId);
3578 for (auto const& prop : rProps)
3580 const OString &rKey = prop.first;
3581 const OUString &rValue = prop.second;
3583 if (rKey == "tooltip-markup")
3584 pParent->SetTipHelpText(nNewId, rValue);
3585 else if (rKey == "tooltip-text")
3586 pParent->SetTipHelpText(nNewId, rValue);
3587 else
3588 SAL_INFO("vcl.builder", "unhandled property: " << rKey);
3591 for (auto const& prop : rAtkProps)
3593 const OString &rKey = prop.first;
3594 const OUString &rValue = prop.second;
3596 if (rKey == "AtkObject::accessible-name")
3597 pParent->SetAccessibleName(nNewId, rValue);
3598 else if (rKey == "AtkObject::accessible-description")
3599 pParent->SetAccessibleDescription(nNewId, rValue);
3600 else
3601 SAL_INFO("vcl.builder", "unhandled atk property: " << rKey);
3604 for (auto const& accel : rAccels)
3606 const OString &rSignal = accel.first;
3607 const auto &rValue = accel.second;
3609 if (rSignal == "activate")
3610 pParent->SetAccelKey(nNewId, makeKeyCode(rValue));
3611 else
3612 SAL_INFO("vcl.builder", "unhandled accelerator for: " << rSignal);
3616 rProps.clear();
3619 /// Insert items to a ComboBox or a ListBox.
3620 /// They have no common ancestor that would have 'InsertEntry()', so use a template.
3621 template<typename T> static bool insertItems(vcl::Window *pWindow, VclBuilder::stringmap &rMap,
3622 std::vector<std::unique_ptr<OUString>>& rUserData,
3623 const std::vector<ComboBoxTextItem> &rItems)
3625 T *pContainer = dynamic_cast<T*>(pWindow);
3626 if (!pContainer)
3627 return false;
3629 sal_uInt16 nActiveId = extractActive(rMap);
3630 for (auto const& item : rItems)
3632 sal_Int32 nPos = pContainer->InsertEntry(item.m_sItem);
3633 if (!item.m_sId.isEmpty())
3635 rUserData.emplace_back(std::make_unique<OUString>(OUString::fromUtf8(item.m_sId)));
3636 pContainer->SetEntryData(nPos, rUserData.back().get());
3639 if (nActiveId < rItems.size())
3640 pContainer->SelectEntryPos(nActiveId);
3642 return true;
3645 VclPtr<vcl::Window> VclBuilder::handleObject(vcl::Window *pParent, stringmap *pAtkProps, xmlreader::XmlReader &reader)
3647 OString sClass;
3648 OString sID;
3649 OUString sCustomProperty;
3651 xmlreader::Span name;
3652 int nsId;
3654 while (reader.nextAttribute(&nsId, &name))
3656 if (name == "class")
3658 name = reader.getAttributeValue(false);
3659 sClass = OString(name.begin, name.length);
3661 else if (name == "id")
3663 name = reader.getAttributeValue(false);
3664 sID = OString(name.begin, name.length);
3665 if (m_bLegacy)
3667 sal_Int32 nDelim = sID.indexOf(':');
3668 if (nDelim != -1)
3670 sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3671 sID = sID.copy(0, nDelim);
3677 if (sClass == "GtkListStore" || sClass == "GtkTreeStore")
3679 handleListStore(reader, sID, sClass);
3680 return nullptr;
3682 else if (sClass == "GtkMenu")
3684 handleMenu(reader, sID, false);
3685 return nullptr;
3687 else if (sClass == "GtkMenuBar")
3689 VclPtr<Menu> xMenu = handleMenu(reader, sID, true);
3690 if (SystemWindow* pTopLevel = pParent ? pParent->GetSystemWindow() : nullptr)
3691 pTopLevel->SetMenuBar(dynamic_cast<MenuBar*>(xMenu.get()));
3692 return nullptr;
3694 else if (sClass == "GtkSizeGroup")
3696 handleSizeGroup(reader);
3697 return nullptr;
3699 else if (sClass == "AtkObject")
3701 assert((pParent || pAtkProps) && "must have one set");
3702 assert(!(pParent && pAtkProps) && "must not have both");
3703 auto aAtkProperties = handleAtkObject(reader);
3704 if (pParent)
3705 applyAtkProperties(pParent, aAtkProperties);
3706 if (pAtkProps)
3707 *pAtkProps = aAtkProperties;
3708 return nullptr;
3711 int nLevel = 1;
3713 stringmap aProperties, aPangoAttributes;
3714 stringmap aAtkAttributes;
3715 std::vector<ComboBoxTextItem> aItems;
3717 if (!sCustomProperty.isEmpty())
3718 aProperties[OString("customproperty")] = sCustomProperty;
3720 VclPtr<vcl::Window> pCurrentChild;
3721 while(true)
3723 xmlreader::XmlReader::Result res = reader.nextItem(
3724 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3726 if (res == xmlreader::XmlReader::Result::Done)
3727 break;
3729 if (res == xmlreader::XmlReader::Result::Begin)
3731 if (name == "child")
3733 if (!pCurrentChild)
3735 pCurrentChild = insertObject(pParent, sClass, sID,
3736 aProperties, aPangoAttributes, aAtkAttributes);
3738 handleChild(pCurrentChild, nullptr, reader);
3740 else if (name == "items")
3741 aItems = handleItems(reader);
3742 else if (name == "style")
3744 int nPriority = 0;
3745 std::vector<vcl::EnumContext::Context> aContext = handleStyle(reader, nPriority);
3746 if (nPriority != 0)
3748 vcl::IPrioritable* pPrioritable = dynamic_cast<vcl::IPrioritable*>(pCurrentChild.get());
3749 SAL_WARN_IF(!pPrioritable, "vcl", "priority set for not supported item");
3750 if (pPrioritable)
3751 pPrioritable->SetPriority(nPriority);
3753 if (!aContext.empty())
3755 vcl::IContext* pContextControl = dynamic_cast<vcl::IContext*>(pCurrentChild.get());
3756 SAL_WARN_IF(!pContextControl, "vcl", "context set for not supported item");
3757 if (pContextControl)
3758 pContextControl->SetContext(aContext);
3761 else
3763 ++nLevel;
3764 if (name == "property")
3765 collectProperty(reader, aProperties);
3766 else if (name == "attribute")
3767 collectPangoAttribute(reader, aPangoAttributes);
3768 else if (name == "relation")
3769 collectAtkRelationAttribute(reader, aAtkAttributes);
3770 else if (name == "role")
3771 collectAtkRoleAttribute(reader, aAtkAttributes);
3772 else if (name == "action-widget")
3773 handleActionWidget(reader);
3777 if (res == xmlreader::XmlReader::Result::End)
3779 --nLevel;
3782 if (!nLevel)
3783 break;
3786 if (sClass == "GtkAdjustment")
3788 m_pParserState->m_aAdjustments[sID] = aProperties;
3789 return nullptr;
3791 else if (sClass == "GtkTextBuffer")
3793 m_pParserState->m_aTextBuffers[sID] = aProperties;
3794 return nullptr;
3797 if (!pCurrentChild)
3799 pCurrentChild = insertObject(pParent, sClass, sID, aProperties,
3800 aPangoAttributes, aAtkAttributes);
3803 if (!aItems.empty())
3805 // try to fill-in the items
3806 if (!insertItems<ComboBox>(pCurrentChild, aProperties, m_aUserData, aItems))
3807 insertItems<ListBox>(pCurrentChild, aProperties, m_aUserData, aItems);
3810 return pCurrentChild;
3813 void VclBuilder::handlePacking(vcl::Window *pCurrent, vcl::Window *pParent, xmlreader::XmlReader &reader)
3815 xmlreader::Span name;
3816 int nsId;
3818 int nLevel = 1;
3820 while(true)
3822 xmlreader::XmlReader::Result res = reader.nextItem(
3823 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3825 if (res == xmlreader::XmlReader::Result::Done)
3826 break;
3828 if (res == xmlreader::XmlReader::Result::Begin)
3830 ++nLevel;
3831 if (name == "property")
3832 applyPackingProperty(pCurrent, pParent, reader);
3835 if (res == xmlreader::XmlReader::Result::End)
3837 --nLevel;
3840 if (!nLevel)
3841 break;
3845 void VclBuilder::applyPackingProperty(vcl::Window *pCurrent,
3846 vcl::Window *pParent,
3847 xmlreader::XmlReader &reader)
3849 if (!pCurrent)
3850 return;
3852 //ToolBoxItems are not true widgets just elements
3853 //of the ToolBox itself
3854 ToolBox *pToolBoxParent = nullptr;
3855 if (pCurrent == pParent)
3856 pToolBoxParent = dynamic_cast<ToolBox*>(pParent);
3858 xmlreader::Span name;
3859 int nsId;
3861 if (pCurrent->GetType() == WindowType::SCROLLWINDOW)
3863 auto aFind = m_pParserState->m_aRedundantParentWidgets.find(VclPtr<vcl::Window>(pCurrent));
3864 if (aFind != m_pParserState->m_aRedundantParentWidgets.end())
3866 pCurrent = aFind->second;
3867 assert(pCurrent);
3871 while (reader.nextAttribute(&nsId, &name))
3873 if (name == "name")
3875 name = reader.getAttributeValue(false);
3876 OString sKey(name.begin, name.length);
3877 sKey = sKey.replace('_', '-');
3878 (void)reader.nextItem(
3879 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3880 OString sValue(name.begin, name.length);
3882 if (sKey == "expand" || sKey == "resize")
3884 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3885 if (pToolBoxParent)
3886 pToolBoxParent->SetItemExpand(m_pParserState->m_nLastToolbarId, bTrue);
3887 else
3888 pCurrent->set_expand(bTrue);
3889 continue;
3892 if (pToolBoxParent)
3893 continue;
3895 if (sKey == "fill")
3897 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3898 pCurrent->set_fill(bTrue);
3900 else if (sKey == "pack-type")
3902 VclPackType ePackType = (!sValue.isEmpty() && (sValue[0] == 'e' || sValue[0] == 'E')) ? VclPackType::End : VclPackType::Start;
3903 pCurrent->set_pack_type(ePackType);
3905 else if (sKey == "left-attach")
3907 pCurrent->set_grid_left_attach(sValue.toInt32());
3909 else if (sKey == "top-attach")
3911 pCurrent->set_grid_top_attach(sValue.toInt32());
3913 else if (sKey == "width")
3915 pCurrent->set_grid_width(sValue.toInt32());
3917 else if (sKey == "height")
3919 pCurrent->set_grid_height(sValue.toInt32());
3921 else if (sKey == "padding")
3923 pCurrent->set_padding(sValue.toInt32());
3925 else if (sKey == "position")
3927 set_window_packing_position(pCurrent, sValue.toInt32());
3929 else if (sKey == "secondary")
3931 pCurrent->set_secondary(toBool(sValue));
3933 else if (sKey == "non-homogeneous")
3935 pCurrent->set_non_homogeneous(toBool(sValue));
3937 else if (sKey == "homogeneous")
3939 pCurrent->set_non_homogeneous(!toBool(sValue));
3941 else
3943 SAL_WARN("vcl.builder", "unknown packing: " << sKey);
3949 std::vector<vcl::EnumContext::Context> VclBuilder::handleStyle(xmlreader::XmlReader &reader, int &nPriority)
3951 std::vector<vcl::EnumContext::Context> aContext;
3953 xmlreader::Span name;
3954 int nsId;
3956 int nLevel = 1;
3958 while(true)
3960 xmlreader::XmlReader::Result res = reader.nextItem(
3961 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3963 if (res == xmlreader::XmlReader::Result::Done)
3964 break;
3966 if (res == xmlreader::XmlReader::Result::Begin)
3968 ++nLevel;
3969 if (name == "class")
3971 OString classStyle = getStyleClass(reader);
3973 if (classStyle.startsWith("context-"))
3975 OString sContext = classStyle.copy(classStyle.indexOf('-') + 1);
3976 OUString sContext2(sContext.getStr(), sContext.getLength(), RTL_TEXTENCODING_UTF8);
3977 aContext.push_back(vcl::EnumContext::GetContextEnum(sContext2));
3979 else if (classStyle.startsWith("priority-"))
3981 OString aPriority = classStyle.copy(classStyle.indexOf('-') + 1);
3982 OUString aPriority2(aPriority.getStr(), aPriority.getLength(), RTL_TEXTENCODING_UTF8);
3983 nPriority = aPriority2.toInt32();
3985 else if (classStyle != "small-button")
3987 SAL_WARN("vcl.builder", "unknown class: " << classStyle);
3992 if (res == xmlreader::XmlReader::Result::End)
3994 --nLevel;
3997 if (!nLevel)
3998 break;
4001 return aContext;
4004 OString VclBuilder::getStyleClass(xmlreader::XmlReader &reader)
4006 xmlreader::Span name;
4007 int nsId;
4008 OString aRet;
4010 while (reader.nextAttribute(&nsId, &name))
4012 if (name == "name")
4014 name = reader.getAttributeValue(false);
4015 aRet = OString (name.begin, name.length);
4019 return aRet;
4022 void VclBuilder::collectProperty(xmlreader::XmlReader &reader, stringmap &rMap) const
4024 xmlreader::Span name;
4025 int nsId;
4027 OString sProperty, sContext;
4029 bool bTranslated = false;
4031 while (reader.nextAttribute(&nsId, &name))
4033 if (name == "name")
4035 name = reader.getAttributeValue(false);
4036 sProperty = OString(name.begin, name.length);
4038 else if (name == "context")
4040 name = reader.getAttributeValue(false);
4041 sContext = OString(name.begin, name.length);
4043 else if (name == "translatable" && reader.getAttributeValue(false) == "yes")
4045 bTranslated = true;
4049 (void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4050 OString sValue(name.begin, name.length);
4051 OUString sFinalValue;
4052 if (bTranslated)
4054 if (!sContext.isEmpty())
4055 sValue = sContext + "\004" + sValue;
4056 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
4058 else
4059 sFinalValue = OUString::fromUtf8(sValue);
4061 if (!sProperty.isEmpty())
4063 sProperty = sProperty.replace('_', '-');
4064 if (m_pStringReplace)
4065 sFinalValue = (*m_pStringReplace)(sFinalValue);
4066 rMap[sProperty] = sFinalValue;
4070 void VclBuilder::handleActionWidget(xmlreader::XmlReader &reader)
4072 xmlreader::Span name;
4073 int nsId;
4075 OString sResponse;
4077 while (reader.nextAttribute(&nsId, &name))
4079 if (name == "response")
4081 name = reader.getAttributeValue(false);
4082 sResponse = OString(name.begin, name.length);
4086 (void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4087 OString sID(name.begin, name.length);
4088 sal_Int32 nDelim = sID.indexOf(':');
4089 if (nDelim != -1)
4090 sID = sID.copy(0, nDelim);
4091 set_response(sID, sResponse.toInt32());
4094 void VclBuilder::collectAccelerator(xmlreader::XmlReader &reader, accelmap &rMap)
4096 xmlreader::Span name;
4097 int nsId;
4099 OString sProperty;
4100 OString sValue;
4101 OString sModifiers;
4103 while (reader.nextAttribute(&nsId, &name))
4105 if (name == "key")
4107 name = reader.getAttributeValue(false);
4108 sValue = OString(name.begin, name.length);
4110 else if (name == "signal")
4112 name = reader.getAttributeValue(false);
4113 sProperty = OString(name.begin, name.length);
4115 else if (name == "modifiers")
4117 name = reader.getAttributeValue(false);
4118 sModifiers = OString(name.begin, name.length);
4122 if (!sProperty.isEmpty() && !sValue.isEmpty())
4124 rMap[sProperty] = std::make_pair(sValue, sModifiers);
4128 vcl::Window *VclBuilder::get_widget_root()
4130 return m_aChildren.empty() ? nullptr : m_aChildren[0].m_pWindow.get();
4133 vcl::Window *VclBuilder::get_by_name(const OString& sID)
4135 for (auto const& child : m_aChildren)
4137 if (child.m_sID == sID)
4138 return child.m_pWindow;
4141 return nullptr;
4144 PopupMenu *VclBuilder::get_menu(const OString& sID)
4146 for (auto const& menu : m_aMenus)
4148 if (menu.m_sID == sID)
4149 return dynamic_cast<PopupMenu*>(menu.m_pMenu.get());
4152 return nullptr;
4155 void VclBuilder::set_response(const OString& sID, short nResponse)
4157 switch (nResponse)
4159 case -5:
4160 nResponse = RET_OK;
4161 break;
4162 case -6:
4163 nResponse = RET_CANCEL;
4164 break;
4165 case -7:
4166 nResponse = RET_CLOSE;
4167 break;
4168 case -8:
4169 nResponse = RET_YES;
4170 break;
4171 case -9:
4172 nResponse = RET_NO;
4173 break;
4174 case -11:
4175 nResponse = RET_HELP;
4176 break;
4177 default:
4178 assert(nResponse >= 100 && "keep non-canned responses in range 100+ to avoid collision with vcl RET_*");
4179 break;
4182 for (const auto & child : m_aChildren)
4184 if (child.m_sID == sID)
4186 PushButton* pPushButton = dynamic_cast<PushButton*>(child.m_pWindow.get());
4187 assert(pPushButton);
4188 Dialog* pDialog = pPushButton->GetParentDialog();
4189 assert(pDialog);
4190 pDialog->add_button(pPushButton, nResponse, false);
4191 return;
4195 assert(false);
4198 void VclBuilder::delete_by_name(const OString& sID)
4200 auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4201 [&sID](WinAndId& rItem) { return rItem.m_sID == sID; });
4202 if (aI != m_aChildren.end())
4204 aI->m_pWindow.disposeAndClear();
4205 m_aChildren.erase(aI);
4209 void VclBuilder::delete_by_window(vcl::Window *pWindow)
4211 drop_ownership(pWindow);
4212 pWindow->disposeOnce();
4215 void VclBuilder::drop_ownership(const vcl::Window *pWindow)
4217 auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4218 [&pWindow](WinAndId& rItem) { return rItem.m_pWindow == pWindow; });
4219 if (aI != m_aChildren.end())
4220 m_aChildren.erase(aI);
4223 OString VclBuilder::get_by_window(const vcl::Window *pWindow) const
4225 for (auto const& child : m_aChildren)
4227 if (child.m_pWindow == pWindow)
4228 return child.m_sID;
4231 return OString();
4234 VclBuilder::PackingData VclBuilder::get_window_packing_data(const vcl::Window *pWindow) const
4236 //We've stored the return of new Control, some of these get
4237 //border windows placed around them which are what you get
4238 //from GetChild, so scoot up a level if necessary to get the
4239 //window whose position value we have
4240 const vcl::Window *pPropHolder = pWindow->ImplGetWindow();
4242 for (auto const& child : m_aChildren)
4244 if (child.m_pWindow == pPropHolder)
4245 return child.m_aPackingData;
4248 return PackingData();
4251 void VclBuilder::set_window_packing_position(const vcl::Window *pWindow, sal_Int32 nPosition)
4253 for (auto & child : m_aChildren)
4255 if (child.m_pWindow == pWindow)
4256 child.m_aPackingData.m_nPosition = nPosition;
4260 const VclBuilder::ListStore *VclBuilder::get_model_by_name(const OString& sID) const
4262 std::map<OString, ListStore>::const_iterator aI = m_pParserState->m_aModels.find(sID);
4263 if (aI != m_pParserState->m_aModels.end())
4264 return &(aI->second);
4265 return nullptr;
4268 const VclBuilder::TextBuffer *VclBuilder::get_buffer_by_name(const OString& sID) const
4270 std::map<OString, TextBuffer>::const_iterator aI = m_pParserState->m_aTextBuffers.find(sID);
4271 if (aI != m_pParserState->m_aTextBuffers.end())
4272 return &(aI->second);
4273 return nullptr;
4276 const VclBuilder::Adjustment *VclBuilder::get_adjustment_by_name(const OString& sID) const
4278 std::map<OString, Adjustment>::const_iterator aI = m_pParserState->m_aAdjustments.find(sID);
4279 if (aI != m_pParserState->m_aAdjustments.end())
4280 return &(aI->second);
4281 return nullptr;
4284 void VclBuilder::mungeModel(ComboBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4286 for (auto const& entry : rStore.m_aEntries)
4288 const ListStore::row &rRow = entry;
4289 sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4290 if (rRow.size() > 1)
4292 if (m_bLegacy)
4294 sal_IntPtr nValue = rRow[1].toInt32();
4295 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4297 else
4299 if (!rRow[1].isEmpty())
4301 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4302 rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4307 if (nActiveId < rStore.m_aEntries.size())
4308 rTarget.SelectEntryPos(nActiveId);
4311 void VclBuilder::mungeModel(ListBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4313 for (auto const& entry : rStore.m_aEntries)
4315 const ListStore::row &rRow = entry;
4316 sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4317 if (rRow.size() > 1)
4319 if (m_bLegacy)
4321 sal_IntPtr nValue = rRow[1].toInt32();
4322 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4324 else
4326 if (!rRow[1].isEmpty())
4328 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4329 rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4334 if (nActiveId < rStore.m_aEntries.size())
4335 rTarget.SelectEntryPos(nActiveId);
4338 void VclBuilder::mungeModel(SvTabListBox& rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4340 for (auto const& entry : rStore.m_aEntries)
4342 const ListStore::row &rRow = entry;
4343 auto pEntry = rTarget.InsertEntry(rRow[0]);
4344 if (rRow.size() > 1)
4346 if (m_bLegacy)
4348 sal_IntPtr nValue = rRow[1].toInt32();
4349 pEntry->SetUserData(reinterpret_cast<void*>(nValue));
4351 else
4353 if (!rRow[1].isEmpty())
4355 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4356 pEntry->SetUserData(m_aUserData.back().get());
4361 if (nActiveId < rStore.m_aEntries.size())
4363 SvTreeListEntry* pEntry = rTarget.GetEntry(nullptr, nActiveId);
4364 rTarget.Select(pEntry);
4368 void VclBuilder::mungeAdjustment(NumericFormatter &rTarget, const Adjustment &rAdjustment)
4370 int nMul = rtl_math_pow10Exp(1, rTarget.GetDecimalDigits());
4372 for (auto const& elem : rAdjustment)
4374 const OString &rKey = elem.first;
4375 const OUString &rValue = elem.second;
4377 if (rKey == "upper")
4379 sal_Int64 nUpper = rValue.toDouble() * nMul;
4380 rTarget.SetMax(nUpper);
4381 rTarget.SetLast(nUpper);
4383 else if (rKey == "lower")
4385 sal_Int64 nLower = rValue.toDouble() * nMul;
4386 rTarget.SetMin(nLower);
4387 rTarget.SetFirst(nLower);
4389 else if (rKey == "value")
4391 sal_Int64 nValue = rValue.toDouble() * nMul;
4392 rTarget.SetValue(nValue);
4394 else if (rKey == "step-increment")
4396 sal_Int64 nSpinSize = rValue.toDouble() * nMul;
4397 rTarget.SetSpinSize(nSpinSize);
4399 else
4401 SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4406 void VclBuilder::mungeAdjustment(FormattedField &rTarget, const Adjustment &rAdjustment)
4408 double nMaxValue = 0, nMinValue = 0, nValue = 0, nSpinSize = 0;
4410 for (auto const& elem : rAdjustment)
4412 const OString &rKey = elem.first;
4413 const OUString &rValue = elem.second;
4415 if (rKey == "upper")
4416 nMaxValue = rValue.toDouble();
4417 else if (rKey == "lower")
4418 nMinValue = rValue.toDouble();
4419 else if (rKey == "value")
4420 nValue = rValue.toDouble();
4421 else if (rKey == "step-increment")
4422 nSpinSize = rValue.toDouble();
4423 else
4424 SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4427 Formatter& rFormatter = rTarget.GetFormatter();
4428 rFormatter.SetMinValue(nMinValue);
4429 rFormatter.SetMaxValue(nMaxValue);
4430 rFormatter.SetValue(nValue);
4431 rFormatter.SetSpinSize(nSpinSize);
4434 void VclBuilder::mungeAdjustment(ScrollBar &rTarget, const Adjustment &rAdjustment)
4436 for (auto const& elem : rAdjustment)
4438 const OString &rKey = elem.first;
4439 const OUString &rValue = elem.second;
4441 if (rKey == "upper")
4442 rTarget.SetRangeMax(rValue.toInt32());
4443 else if (rKey == "lower")
4444 rTarget.SetRangeMin(rValue.toInt32());
4445 else if (rKey == "value")
4446 rTarget.SetThumbPos(rValue.toInt32());
4447 else if (rKey == "step-increment")
4448 rTarget.SetLineSize(rValue.toInt32());
4449 else if (rKey == "page-increment")
4450 rTarget.SetPageSize(rValue.toInt32());
4451 else
4453 SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4458 void VclBuilder::mungeAdjustment(Slider& rTarget, const Adjustment& rAdjustment)
4460 for (auto const& elem : rAdjustment)
4462 const OString &rKey = elem.first;
4463 const OUString &rValue = elem.second;
4465 if (rKey == "upper")
4466 rTarget.SetRangeMax(rValue.toInt32());
4467 else if (rKey == "lower")
4468 rTarget.SetRangeMin(rValue.toInt32());
4469 else if (rKey == "value")
4470 rTarget.SetThumbPos(rValue.toInt32());
4471 else if (rKey == "step-increment")
4472 rTarget.SetLineSize(rValue.toInt32());
4473 else if (rKey == "page-increment")
4474 rTarget.SetPageSize(rValue.toInt32());
4475 else
4477 SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4482 void VclBuilder::mungeTextBuffer(VclMultiLineEdit &rTarget, const TextBuffer &rTextBuffer)
4484 for (auto const& elem : rTextBuffer)
4486 const OString &rKey = elem.first;
4487 const OUString &rValue = elem.second;
4489 if (rKey == "text")
4490 rTarget.SetText(rValue);
4491 else
4493 SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4498 VclBuilder::ParserState::ParserState()
4499 : m_nLastToolbarId(0)
4500 , m_nLastMenuItemId(0)
4503 VclBuilder::MenuAndId::MenuAndId(const OString &rId, Menu *pMenu)
4504 : m_sID(rId)
4505 , m_pMenu(pMenu)
4508 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */