xref: /core/vcl/source/window/builder.cxx (revision 7e4a3eee)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 #include <config_feature_desktop.h>
11 #include <config_options.h>
12 
13 #include <memory>
14 #include <unordered_map>
15 #include <com/sun/star/accessibility/AccessibleRole.hpp>
16 
17 #include <comphelper/lok.hxx>
18 #include <i18nutil/unicode.hxx>
19 #include <officecfg/Office/Common.hxx>
20 #include <osl/module.hxx>
21 #include <sal/log.hxx>
22 #include <unotools/localedatawrapper.hxx>
23 #include <unotools/resmgr.hxx>
24 #include <vcl/builder.hxx>
25 #include <vcl/toolkit/button.hxx>
26 #include <vcl/toolkit/dialog.hxx>
27 #include <vcl/edit.hxx>
28 #include <vcl/toolkit/field.hxx>
29 #include <vcl/fieldvalues.hxx>
30 #include <vcl/fmtfield.hxx>
31 #include <vcl/toolkit/fixed.hxx>
32 #include <vcl/toolkit/fixedhyper.hxx>
33 #include <vcl/headbar.hxx>
34 #include <vcl/IPrioritable.hxx>
35 #include <vcl/ivctrl.hxx>
36 #include <vcl/layout.hxx>
37 #include <vcl/toolkit/lstbox.hxx>
38 #include <vcl/menubtn.hxx>
39 #include <vcl/mnemonic.hxx>
40 #include <vcl/toolkit/prgsbar.hxx>
41 #include <vcl/scrbar.hxx>
42 #include <vcl/split.hxx>
43 #include <vcl/svapp.hxx>
44 #include <vcl/toolkit/svtabbx.hxx>
45 #include <vcl/tabctrl.hxx>
46 #include <vcl/tabpage.hxx>
47 #include <vcl/toolkit/throbber.hxx>
48 #include <vcl/toolbox.hxx>
49 #include <vcl/treelistentry.hxx>
50 #include <vcl/vclmedit.hxx>
51 #include <vcl/settings.hxx>
52 #include <slider.hxx>
53 #include <vcl/weld.hxx>
54 #include <vcl/weldutils.hxx>
55 #include <vcl/commandinfoprovider.hxx>
56 #include <iconview.hxx>
57 #include <svdata.hxx>
58 #include <bitmaps.hlst>
59 #include <messagedialog.hxx>
60 #include <OptionalBox.hxx>
61 #include <window.h>
62 #include <xmlreader/xmlreader.hxx>
63 #include <desktop/crashreport.hxx>
64 #include <calendar.hxx>
65 #include <salinst.hxx>
66 #include <strings.hrc>
67 #include <treeglue.hxx>
68 #include <tools/diagnose_ex.h>
69 #include <wizdlg.hxx>
70 #include <tools/svlibrary.h>
71 #include <jsdialog/jsdialogbuilder.hxx>
72 
73 #if defined(DISABLE_DYNLOADING) || defined(LINUX)
74 #include <dlfcn.h>
75 #endif
76 
77 static bool toBool(const OString &rValue)
78 {
79     return (!rValue.isEmpty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
80 }
81 
82 namespace
83 {
84     OUString mapStockToImageResource(const OUString& sType)
85     {
86         if (sType == "gtk-index")
87             return SV_RESID_BITMAP_INDEX;
88         else if (sType == "gtk-refresh")
89             return SV_RESID_BITMAP_REFRESH;
90         else if (sType == "gtk-apply")
91             return IMG_APPLY;
92         else if (sType == "gtk-dialog-error")
93             return IMG_ERROR;
94         else if (sType == "gtk-add")
95             return IMG_ADD;
96         else if (sType == "gtk-remove")
97             return IMG_REMOVE;
98         else if (sType == "gtk-copy")
99             return IMG_COPY;
100         else if (sType == "gtk-paste")
101             return IMG_PASTE;
102         return OUString();
103     }
104 
105     SymbolType mapStockToSymbol(const OUString& sType)
106     {
107         SymbolType eRet = SymbolType::DONTKNOW;
108         if (sType == "gtk-media-next")
109             eRet = SymbolType::NEXT;
110         else if (sType == "gtk-media-previous")
111             eRet = SymbolType::PREV;
112         else if (sType == "gtk-media-play")
113             eRet = SymbolType::PLAY;
114         else if (sType == "gtk-media-stop")
115             eRet = SymbolType::STOP;
116         else if (sType == "gtk-goto-first")
117             eRet = SymbolType::FIRST;
118         else if (sType == "gtk-goto-last")
119             eRet = SymbolType::LAST;
120         else if (sType == "gtk-go-back")
121             eRet = SymbolType::ARROW_LEFT;
122         else if (sType == "gtk-go-forward")
123             eRet = SymbolType::ARROW_RIGHT;
124         else if (sType == "gtk-go-up")
125             eRet = SymbolType::ARROW_UP;
126         else if (sType == "gtk-go-down")
127             eRet = SymbolType::ARROW_DOWN;
128         else if (sType == "gtk-missing-image")
129             eRet = SymbolType::IMAGE;
130         else if (sType == "gtk-help")
131             eRet = SymbolType::HELP;
132         else if (sType == "gtk-close")
133             eRet = SymbolType::CLOSE;
134         else if (!mapStockToImageResource(sType).isEmpty())
135             eRet = SymbolType::IMAGE;
136         return eRet;
137     }
138 
139     void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame);
140 }
141 
142 #if defined SAL_LOG_WARN
143 namespace
144 {
145     bool isButtonType(WindowType nType)
146     {
147         return nType == WindowType::PUSHBUTTON ||
148                nType == WindowType::OKBUTTON ||
149                nType == WindowType::CANCELBUTTON ||
150                nType == WindowType::HELPBUTTON ||
151                nType == WindowType::IMAGEBUTTON ||
152                nType == WindowType::MENUBUTTON ||
153                nType == WindowType::MOREBUTTON ||
154                nType == WindowType::SPINBUTTON;
155     }
156 }
157 #endif
158 
159 weld::Builder* Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile, bool bMobile)
160 {
161     bool bUseJSBuilder = false;
162 
163     if (bMobile)
164     {
165         if (rUIFile == "modules/swriter/ui/wordcount-mobile.ui")
166             bUseJSBuilder = true;
167     }
168 
169     if (bUseJSBuilder)
170         return new JSInstanceBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile);
171     else
172         return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile);
173 }
174 
175 weld::Builder* Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile)
176 {
177     return ImplGetSVData()->mpDefInst->CreateInterimBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile);
178 }
179 
180 weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType,
181                                                       VclButtonsType eButtonType, const OUString& rPrimaryMessage,
182                                                       bool bMobile)
183 {
184     if (bMobile)
185         return JSInstanceBuilder::CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
186     else
187         return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
188 }
189 
190 weld::Window* Application::GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow)
191 {
192     return ImplGetSVData()->mpDefInst->GetFrameWeld(rWindow);
193 }
194 
195 namespace weld
196 {
197     OUString MetricSpinButton::MetricToString(FieldUnit rUnit)
198     {
199         const FieldUnitStringList& rList = ImplGetFieldUnits();
200         // return unit's default string (ie, the first one )
201         auto it = std::find_if(
202             rList.begin(), rList.end(),
203             [&rUnit](const std::pair<OUString, FieldUnit>& rItem) { return rItem.second == rUnit; });
204         if (it != rList.end())
205             return it->first;
206 
207         return OUString();
208     }
209 
210     IMPL_LINK_NOARG(MetricSpinButton, spin_button_value_changed, SpinButton&, void)
211     {
212         signal_value_changed();
213     }
214 
215     IMPL_LINK(MetricSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
216     {
217         OUString sNewText(format_number(rSpinButton.get_value()));
218         if (sNewText != rSpinButton.get_text())
219             rSpinButton.set_text(sNewText);
220     }
221 
222     void MetricSpinButton::update_width_chars()
223     {
224         int min, max;
225         m_xSpinButton->get_range(min, max);
226         auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
227                               m_xSpinButton->get_pixel_size(format_number(max)).Width());
228         int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
229         m_xSpinButton->set_width_chars(chars);
230     }
231 
232     unsigned int SpinButton::Power10(unsigned int n)
233     {
234         unsigned int nValue = 1;
235         for (unsigned int i = 0; i < n; ++i)
236             nValue *= 10;
237         return nValue;
238     }
239 
240     int SpinButton::denormalize(int nValue) const
241     {
242         const int nFactor = Power10(get_digits());
243 
244         if ((nValue < (SAL_MIN_INT32 + nFactor)) || (nValue > (SAL_MAX_INT32 - nFactor)))
245         {
246             return nValue / nFactor;
247         }
248 
249         const int nHalf = nFactor / 2;
250 
251         if (nValue < 0)
252             return (nValue - nHalf) / nFactor;
253         return (nValue + nHalf) / nFactor;
254     }
255 
256     OUString MetricSpinButton::format_number(int nValue) const
257     {
258         OUString aStr;
259 
260         const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
261 
262         unsigned int nDecimalDigits = m_xSpinButton->get_digits();
263         //pawn percent off to icu to decide whether percent is separated from its number for this locale
264         if (m_eSrcUnit == FieldUnit::PERCENT)
265         {
266             double fValue = nValue;
267             fValue /= SpinButton::Power10(nDecimalDigits);
268             aStr = unicode::formatPercent(fValue, rLocaleData.getLanguageTag());
269         }
270         else
271         {
272             aStr = rLocaleData.getNum(nValue, nDecimalDigits, true, true);
273             OUString aSuffix = MetricToString(m_eSrcUnit);
274             if (m_eSrcUnit != FieldUnit::NONE && m_eSrcUnit != FieldUnit::DEGREE && m_eSrcUnit != FieldUnit::INCH && m_eSrcUnit != FieldUnit::FOOT)
275                 aStr += " ";
276             if (m_eSrcUnit == FieldUnit::INCH)
277             {
278                 OUString sDoublePrime = u"\u2033";
279                 if (aSuffix != "\"" && aSuffix != sDoublePrime)
280                     aStr += " ";
281                 else
282                     aSuffix = sDoublePrime;
283             }
284             else if (m_eSrcUnit == FieldUnit::FOOT)
285             {
286                 OUString sPrime = u"\u2032";
287                 if (aSuffix != "'" && aSuffix != sPrime)
288                     aStr += " ";
289                 else
290                     aSuffix = sPrime;
291             }
292 
293             assert(m_eSrcUnit != FieldUnit::PERCENT);
294             aStr += aSuffix;
295         }
296 
297         return aStr;
298     }
299 
300     void MetricSpinButton::set_digits(unsigned int digits)
301     {
302         int step, page;
303         get_increments(step, page, m_eSrcUnit);
304         int value = get_value(m_eSrcUnit);
305         m_xSpinButton->set_digits(digits);
306         set_increments(step, page, m_eSrcUnit);
307         set_value(value, m_eSrcUnit);
308         update_width_chars();
309     }
310 
311     void MetricSpinButton::set_unit(FieldUnit eUnit)
312     {
313         if (eUnit != m_eSrcUnit)
314         {
315             int step, page;
316             get_increments(step, page, m_eSrcUnit);
317             int value = get_value(m_eSrcUnit);
318             m_eSrcUnit = eUnit;
319             set_increments(step, page, m_eSrcUnit);
320             set_value(value, m_eSrcUnit);
321             spin_button_output(*m_xSpinButton);
322             update_width_chars();
323         }
324     }
325 
326     int MetricSpinButton::ConvertValue(int nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const
327     {
328         return vcl::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit);
329     }
330 
331     IMPL_LINK(MetricSpinButton, spin_button_input, int*, result, bool)
332     {
333         const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
334         double fResult(0.0);
335         bool bRet = vcl::TextToValue(get_text(), fResult, 0, m_xSpinButton->get_digits(), rLocaleData, m_eSrcUnit);
336         if (bRet)
337         {
338             if (fResult > SAL_MAX_INT32)
339                 fResult = SAL_MAX_INT32;
340             else if (fResult < SAL_MIN_INT32)
341                 fResult = SAL_MIN_INT32;
342             *result = fResult;
343         }
344         return bRet;
345     }
346 
347     IMPL_LINK_NOARG(TimeSpinButton, spin_button_cursor_position, Entry&, void)
348     {
349         int nStartPos, nEndPos;
350         m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);
351 
352         const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
353         const int nTimeArea = TimeFormatter::GetTimeArea(m_eFormat, m_xSpinButton->get_text(), nEndPos,
354                                                          rLocaleData);
355 
356         int nIncrements = 1;
357 
358         if (nTimeArea == 1)
359             nIncrements = 1000 * 60 * 60;
360         else if (nTimeArea == 2)
361             nIncrements = 1000 * 60;
362         else if (nTimeArea == 3)
363             nIncrements = 1000;
364 
365         m_xSpinButton->set_increments(nIncrements, nIncrements * 10);
366     }
367 
368     IMPL_LINK_NOARG(TimeSpinButton, spin_button_value_changed, SpinButton&, void)
369     {
370         signal_value_changed();
371     }
372 
373     IMPL_LINK(TimeSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
374     {
375         int nStartPos, nEndPos;
376         rSpinButton.get_selection_bounds(nStartPos, nEndPos);
377         rSpinButton.set_text(format_number(rSpinButton.get_value()));
378         rSpinButton.set_position(nEndPos);
379     }
380 
381     IMPL_LINK(TimeSpinButton, spin_button_input, int*, result, bool)
382     {
383         int nStartPos, nEndPos;
384         m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);
385 
386         const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
387         tools::Time aResult(0);
388         bool bRet = TimeFormatter::TextToTime(m_xSpinButton->get_text(), aResult, m_eFormat, true, rLocaleData);
389         if (bRet)
390             *result = ConvertValue(aResult);
391         return bRet;
392     }
393 
394     void TimeSpinButton::update_width_chars()
395     {
396         int min, max;
397         m_xSpinButton->get_range(min, max);
398         auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
399                               m_xSpinButton->get_pixel_size(format_number(max)).Width());
400         int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
401         m_xSpinButton->set_width_chars(chars);
402     }
403 
404     tools::Time TimeSpinButton::ConvertValue(int nValue)
405     {
406         tools::Time aTime(0);
407         aTime.MakeTimeFromMS(nValue);
408         return aTime;
409     }
410 
411     int TimeSpinButton::ConvertValue(const tools::Time& rTime)
412     {
413         return rTime.GetMSFromTime();
414     }
415 
416     OUString TimeSpinButton::format_number(int nValue) const
417     {
418         const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
419         return TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, TimeFormat::Hour24, true, rLocaleData);
420     }
421 
422     EntryTreeView::EntryTreeView(std::unique_ptr<Entry> xEntry, std::unique_ptr<TreeView> xTreeView)
423         : m_xEntry(std::move(xEntry))
424         , m_xTreeView(std::move(xTreeView))
425     {
426         m_xTreeView->connect_changed(LINK(this, EntryTreeView, ClickHdl));
427         m_xEntry->connect_changed(LINK(this, EntryTreeView, ModifyHdl));
428     }
429 
430     IMPL_LINK(EntryTreeView, ClickHdl, weld::TreeView&, rView, void)
431     {
432         m_xEntry->set_text(rView.get_selected_text());
433         m_aChangeHdl.Call(*this);
434     }
435 
436     IMPL_LINK_NOARG(EntryTreeView, ModifyHdl, weld::Entry&, void)
437     {
438         m_aChangeHdl.Call(*this);
439     }
440 
441     void EntryTreeView::set_height_request_by_rows(int nRows)
442     {
443         int nHeight = nRows == -1 ? -1 : m_xTreeView->get_height_rows(nRows);
444         m_xTreeView->set_size_request(m_xTreeView->get_size_request().Width(), nHeight);
445     }
446 
447     size_t GetAbsPos(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
448     {
449         size_t nAbsPos = 0;
450 
451         std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
452         if (!rTreeView.get_iter_first(*xEntry))
453             xEntry.reset();
454 
455         while (xEntry && rTreeView.iter_compare(*xEntry, rIter) != 0)
456         {
457             if (!rTreeView.iter_next(*xEntry))
458                 xEntry.reset();
459             nAbsPos++;
460         }
461 
462         return nAbsPos;
463     }
464 
465     bool IsEntryVisible(const weld::TreeView& rTreeView, const weld::TreeIter& rIter)
466     {
467         // short circuit for the common case
468         if (rTreeView.get_iter_depth(rIter) == 0)
469             return true;
470 
471         std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator(&rIter));
472         bool bRetVal = false;
473         do
474         {
475             if (rTreeView.get_iter_depth(*xEntry) == 0)
476             {
477                 bRetVal = true;
478                 break;
479             }
480         }  while (rTreeView.iter_parent(*xEntry) && rTreeView.get_row_expanded(*xEntry));
481         return bRetVal;
482     }
483 }
484 
485 VclBuilder::VclBuilder(vcl::Window* pParent, const OUString& sUIDir, const OUString& sUIFile,
486                        const OString& sID, const css::uno::Reference<css::frame::XFrame>& rFrame,
487                        bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem)
488     : m_pNotebookBarAddonsItem(pNotebookBarAddonsItem
489                                    ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem)
490                                    : new NotebookBarAddonsItem{})
491     , m_sID(sID)
492     , m_sHelpRoot(OUStringToOString(sUIFile, RTL_TEXTENCODING_UTF8))
493     , m_pStringReplace(Translate::GetReadStringHook())
494     , m_pParent(pParent)
495     , m_bToplevelParentFound(false)
496     , m_bLegacy(bLegacy)
497     , m_pParserState(new ParserState)
498     , m_xFrame(rFrame)
499 {
500     m_bToplevelHasDeferredInit = pParent &&
501         ((pParent->IsSystemWindow() && static_cast<SystemWindow*>(pParent)->isDeferredInit()) ||
502          (pParent->IsDockingWindow() && static_cast<DockingWindow*>(pParent)->isDeferredInit()));
503     m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit;
504 
505     sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.');
506     if (nIdx != -1)
507         m_sHelpRoot = m_sHelpRoot.copy(0, nIdx);
508     m_sHelpRoot += OString('/');
509 
510     OUString sUri = sUIDir + sUIFile;
511 
512     try
513     {
514         xmlreader::XmlReader reader(sUri);
515 
516         handleChild(pParent, reader);
517     }
518     catch (const css::uno::Exception &rExcept)
519     {
520         DBG_UNHANDLED_EXCEPTION("vcl.builder", "Unable to read .ui file");
521         CrashReporter::addKeyValue("VclBuilderException", "Unable to read .ui file: " + rExcept.Message, CrashReporter::Write);
522         throw;
523     }
524 
525     //Set Mnemonic widgets when everything has been imported
526     for (auto const& mnemonicWidget : m_pParserState->m_aMnemonicWidgetMaps)
527     {
528         FixedText *pOne = get<FixedText>(mnemonicWidget.m_sID);
529         vcl::Window *pOther = get(mnemonicWidget.m_sValue.toUtf8());
530         SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << mnemonicWidget.m_sID
531             << " or target " << mnemonicWidget.m_sValue << " member of Mnemonic Widget Mapping");
532         if (pOne && pOther)
533             pOne->set_mnemonic_widget(pOther);
534     }
535 
536     //Set a11y relations and role when everything has been imported
537     for (auto const& elemAtk : m_pParserState->m_aAtkInfo)
538     {
539         vcl::Window *pSource = elemAtk.first;
540         const stringmap &rMap = elemAtk.second;
541 
542         for (auto const& elemMap : rMap)
543         {
544             const OString &rType = elemMap.first;
545             const OUString &rParam = elemMap.second;
546             if (rType == "role")
547             {
548                 sal_Int16 role = BuilderUtils::getRoleFromName(rParam.toUtf8());
549                 if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN)
550                     pSource->SetAccessibleRole(role);
551             }
552             else
553             {
554                 vcl::Window *pTarget = get(rParam.toUtf8());
555                 SAL_WARN_IF(!pTarget, "vcl", "missing parameter of a11y relation: " << rParam);
556                 if (!pTarget)
557                     continue;
558                 if (rType == "labelled-by")
559                     pSource->SetAccessibleRelationLabeledBy(pTarget);
560                 else if (rType == "label-for")
561                     pSource->SetAccessibleRelationLabelFor(pTarget);
562                 else if (rType == "member-of")
563                     pSource->SetAccessibleRelationMemberOf(pTarget);
564                 else
565                 {
566                     SAL_WARN("vcl.builder", "unhandled a11y relation :" << rType);
567                 }
568             }
569         }
570     }
571 
572     //Set radiobutton groups when everything has been imported
573     for (auto const& elem : m_pParserState->m_aGroupMaps)
574     {
575         RadioButton *pOne = get<RadioButton>(elem.m_sID);
576         RadioButton *pOther = get<RadioButton>(elem.m_sValue);
577         SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group");
578         if (pOne && pOther)
579         {
580             if (m_bLegacy)
581                 pOne->group(*pOther);
582             else
583             {
584                 pOther->group(*pOne);
585                 std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
586             }
587         }
588     }
589 
590     //Set ComboBox models when everything has been imported
591     for (auto const& elem : m_pParserState->m_aModelMaps)
592     {
593         vcl::Window* pTarget = get(elem.m_sID);
594         ListBox *pListBoxTarget = dynamic_cast<ListBox*>(pTarget);
595         ComboBox *pComboBoxTarget = dynamic_cast<ComboBox*>(pTarget);
596         SvTabListBox *pTreeBoxTarget = dynamic_cast<SvTabListBox*>(pTarget);
597         // pStore may be empty
598         const ListStore *pStore = get_model_by_name(elem.m_sValue.toUtf8());
599         SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget, "vcl", "missing elements of combobox");
600         if (pListBoxTarget && pStore)
601             mungeModel(*pListBoxTarget, *pStore, elem.m_nActiveId);
602         else if (pComboBoxTarget && pStore)
603             mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId);
604         else if (pTreeBoxTarget && pStore)
605             mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId);
606     }
607 
608     //Set TextView buffers when everything has been imported
609     for (auto const& elem : m_pParserState->m_aTextBufferMaps)
610     {
611         VclMultiLineEdit *pTarget = get<VclMultiLineEdit>(elem.m_sID);
612         const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue.toUtf8());
613         SAL_WARN_IF(!pTarget || !pBuffer, "vcl", "missing elements of textview/textbuffer");
614         if (pTarget && pBuffer)
615             mungeTextBuffer(*pTarget, *pBuffer);
616     }
617 
618     //Set SpinButton adjustments when everything has been imported
619     for (auto const& elem : m_pParserState->m_aNumericFormatterAdjustmentMaps)
620     {
621         NumericFormatter *pTarget = dynamic_cast<NumericFormatter*>(get(elem.m_sID));
622         const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
623         SAL_WARN_IF(!pTarget, "vcl", "missing NumericFormatter element of spinbutton/adjustment");
624         SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
625         if (pTarget && pAdjustment)
626             mungeAdjustment(*pTarget, *pAdjustment);
627     }
628 
629     for (auto const& elem : m_pParserState->m_aFormattedFormatterAdjustmentMaps)
630     {
631         FormattedField *pTarget = dynamic_cast<FormattedField*>(get(elem.m_sID));
632         const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
633         SAL_WARN_IF(!pTarget, "vcl", "missing FormattedField element of spinbutton/adjustment");
634         SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
635         if (pTarget && pAdjustment)
636             mungeAdjustment(*pTarget, *pAdjustment);
637     }
638 
639     //Set ScrollBar adjustments when everything has been imported
640     for (auto const& elem : m_pParserState->m_aScrollAdjustmentMaps)
641     {
642         ScrollBar *pTarget = get<ScrollBar>(elem.m_sID);
643         const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
644         SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scrollbar/adjustment");
645         if (pTarget && pAdjustment)
646             mungeAdjustment(*pTarget, *pAdjustment);
647     }
648 
649     //Set Scale(Slider) adjustments
650     for (auto const& elem : m_pParserState->m_aSliderAdjustmentMaps)
651     {
652         Slider* pTarget = dynamic_cast<Slider*>(get(elem.m_sID));
653         const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
654         SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scale(slider)/adjustment");
655         if (pTarget && pAdjustment)
656         {
657             mungeAdjustment(*pTarget, *pAdjustment);
658         }
659     }
660 
661     //Set size-groups when all widgets have been imported
662     for (auto const& sizeGroup : m_pParserState->m_aSizeGroups)
663     {
664         std::shared_ptr<VclSizeGroup> xGroup(std::make_shared<VclSizeGroup>());
665 
666         for (auto const& elem : sizeGroup.m_aProperties)
667         {
668             const OString &rKey = elem.first;
669             const OUString &rValue = elem.second;
670             xGroup->set_property(rKey, rValue);
671         }
672 
673         for (auto const& elem : sizeGroup.m_aWidgets)
674         {
675             vcl::Window* pWindow = get(elem.getStr());
676             pWindow->add_to_size_group(xGroup);
677         }
678     }
679 
680     //Set button images when everything has been imported
681     std::set<OUString> aImagesToBeRemoved;
682     for (auto const& elem : m_pParserState->m_aButtonImageWidgetMaps)
683     {
684         PushButton *pTargetButton = nullptr;
685         RadioButton *pTargetRadio = nullptr;
686         Button *pTarget = nullptr;
687 
688         if (!elem.m_bRadio)
689         {
690             pTargetButton = get<PushButton>(elem.m_sID);
691             pTarget = pTargetButton;
692         }
693         else
694         {
695             pTargetRadio = get<RadioButton>(elem.m_sID);
696             pTarget = pTargetRadio;
697         }
698 
699         FixedImage *pImage = get<FixedImage>(elem.m_sValue.toUtf8());
700         SAL_WARN_IF(!pTarget || !pImage,
701             "vcl", "missing elements of button/image/stock");
702         if (!pTarget || !pImage)
703             continue;
704         aImagesToBeRemoved.insert(elem.m_sValue);
705 
706         VclBuilder::StockMap::iterator aFind = m_pParserState->m_aStockMap.find(elem.m_sValue.toUtf8());
707         if (aFind == m_pParserState->m_aStockMap.end())
708         {
709             if (!elem.m_bRadio)
710             {
711                 pTargetButton->SetModeImage(pImage->GetImage());
712                 if (pImage->GetStyle() & WB_SMALLSTYLE)
713                 {
714                     pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
715                     Size aSz(pTargetButton->GetModeImage().GetSizePixel());
716                     aSz.AdjustWidth(6);
717                     aSz.AdjustHeight(6);
718                     if (pTargetButton->get_width_request() == -1)
719                         pTargetButton->set_width_request(aSz.Width());
720                     if (pTargetButton->get_height_request() == -1)
721                         pTargetButton->set_height_request(aSz.Height());
722                 }
723             }
724             else
725                 pTargetRadio->SetModeRadioImage(pImage->GetImage());
726         }
727         else
728         {
729             const stockinfo &rImageInfo = aFind->second;
730             SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
731             SAL_WARN_IF(eType == SymbolType::DONTKNOW, "vcl", "missing stock image element for button");
732             if (eType == SymbolType::DONTKNOW)
733                 continue;
734             if (!elem.m_bRadio)
735             {
736                 pTargetButton->SetSymbol(eType);
737                 //fdo#76457 keep symbol images small e.g. tools->customize->menu
738                 //but images the right size. Really the PushButton::CalcMinimumSize
739                 //and PushButton::ImplDrawPushButton are the better place to handle
740                 //this, but its such a train-wreck
741                 if (eType != SymbolType::IMAGE)
742                     pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
743             }
744             else
745                 SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl.builder", "unimplemented symbol type for radiobuttons");
746             if (eType == SymbolType::IMAGE)
747             {
748                 Image const aImage(StockImage::Yes,
749                                    mapStockToImageResource(rImageInfo.m_sStock));
750                 if (!elem.m_bRadio)
751                     pTargetButton->SetModeImage(aImage);
752                 else
753                     pTargetRadio->SetModeRadioImage(aImage);
754             }
755             switch (rImageInfo.m_nSize)
756             {
757                 case 1:
758                     pTarget->SetSmallSymbol();
759                     break;
760                 case 3:
761                     // large toolbar, make bigger than normal (4)
762                     pTarget->set_width_request(pTarget->GetOptimalSize().Width() * 1.5);
763                     pTarget->set_height_request(pTarget->GetOptimalSize().Height() * 1.5);
764                     break;
765                 case 4:
766                     break;
767                 default:
768                     SAL_WARN("vcl.builder", "unsupported image size " << rImageInfo.m_nSize);
769                     break;
770             }
771         }
772     }
773 
774     //There may be duplicate use of an Image, so we used a set to collect and
775     //now we can remove them from the tree after their final munge
776     for (auto const& elem : aImagesToBeRemoved)
777     {
778         delete_by_name(elem.toUtf8());
779     }
780 
781     //fill in any stock icons in surviving images
782     for (auto const& elem : m_pParserState->m_aStockMap)
783     {
784         FixedImage *pImage = get<FixedImage>(elem.first);
785         SAL_WARN_IF(!pImage, "vcl", "missing elements of image/stock: " << elem.first);
786         if (!pImage)
787             continue;
788 
789         const stockinfo &rImageInfo = elem.second;
790         if (rImageInfo.m_sStock == "gtk-missing-image")
791             continue;
792 
793         SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
794         SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl", "unimplemented symbol type for images");
795         if (eType != SymbolType::IMAGE)
796             continue;
797 
798         Image const aImage(StockImage::Yes,
799                            mapStockToImageResource(rImageInfo.m_sStock));
800         pImage->SetImage(aImage);
801     }
802 
803     //Set button menus when everything has been imported
804     for (auto const& elem : m_pParserState->m_aButtonMenuMaps)
805     {
806         MenuButton *pTarget = get<MenuButton>(elem.m_sID);
807         PopupMenu *pMenu = get_menu(elem.m_sValue.toUtf8());
808         SAL_WARN_IF(!pTarget || !pMenu,
809             "vcl", "missing elements of button/menu");
810         if (!pTarget || !pMenu)
811             continue;
812         pTarget->SetPopupMenu(pMenu);
813     }
814 
815     //Remove ScrollWindow parent widgets whose children in vcl implement scrolling
816     //internally.
817     for (auto const& elem : m_pParserState->m_aRedundantParentWidgets)
818     {
819         delete_by_window(elem.first);
820     }
821 
822     //fdo#67378 merge the label into the disclosure button
823     for (auto const& elem : m_pParserState->m_aExpanderWidgets)
824     {
825         vcl::Window *pChild = elem->get_child();
826         vcl::Window* pLabel = elem->GetWindow(GetWindowType::LastChild);
827         if (pLabel && pLabel != pChild && pLabel->GetType() == WindowType::FIXEDTEXT)
828         {
829             FixedText *pLabelWidget = static_cast<FixedText*>(pLabel);
830             elem->set_label(pLabelWidget->GetText());
831             delete_by_window(pLabel);
832         }
833     }
834 
835     // create message dialog message area now
836     for (auto const& elem : m_pParserState->m_aMessageDialogs)
837         elem->create_message_area();
838 
839     //drop maps, etc. that we don't need again
840     m_pParserState.reset();
841 
842     SAL_WARN_IF(!m_sID.isEmpty() && (!m_bToplevelParentFound && !get_by_name(m_sID)), "vcl.builder",
843         "Requested top level widget \"" << m_sID << "\" not found in " << sUIFile);
844 
845 #if defined SAL_LOG_WARN
846     if (m_bToplevelParentFound && m_pParent->IsDialog())
847     {
848         int nButtons = 0;
849         bool bHasDefButton = false;
850         for (auto const& child : m_aChildren)
851         {
852             if (isButtonType(child.m_pWindow->GetType()))
853             {
854                 ++nButtons;
855                 if (child.m_pWindow->GetStyle() & WB_DEFBUTTON)
856                 {
857                     bHasDefButton = true;
858                     break;
859                 }
860             }
861         }
862         SAL_WARN_IF(nButtons && !bHasDefButton, "vcl.builder", "No default button defined in " << sUIFile);
863     }
864 #endif
865 
866     const bool bHideHelp = comphelper::LibreOfficeKit::isActive() &&
867         officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
868     if (bHideHelp)
869     {
870         if (vcl::Window *pHelpButton = get("help"))
871             pHelpButton->Hide();
872     }
873 }
874 
875 VclBuilder::~VclBuilder()
876 {
877     disposeBuilder();
878 }
879 
880 void VclBuilder::disposeBuilder()
881 {
882     for (std::vector<WinAndId>::reverse_iterator aI = m_aChildren.rbegin(),
883          aEnd = m_aChildren.rend(); aI != aEnd; ++aI)
884     {
885         aI->m_pWindow.disposeAndClear();
886     }
887     m_aChildren.clear();
888 
889     for (std::vector<MenuAndId>::reverse_iterator aI = m_aMenus.rbegin(),
890          aEnd = m_aMenus.rend(); aI != aEnd; ++aI)
891     {
892         aI->m_pMenu.disposeAndClear();
893     }
894     m_aMenus.clear();
895     m_pParent.clear();
896 }
897 
898 namespace
899 {
900     bool extractDrawValue(VclBuilder::stringmap& rMap)
901     {
902         bool bDrawValue = true;
903         VclBuilder::stringmap::iterator aFind = rMap.find("draw_value");
904         if (aFind != rMap.end())
905         {
906             bDrawValue = toBool(aFind->second);
907             rMap.erase(aFind);
908         }
909         return bDrawValue;
910     }
911 
912     OUString extractPopupMenu(VclBuilder::stringmap& rMap)
913     {
914         OUString sRet;
915         VclBuilder::stringmap::iterator aFind = rMap.find("popup");
916         if (aFind != rMap.end())
917         {
918             sRet = aFind->second;
919             rMap.erase(aFind);
920         }
921         return sRet;
922     }
923 
924     OUString extractValuePos(VclBuilder::stringmap& rMap)
925     {
926         OUString sRet("top");
927         VclBuilder::stringmap::iterator aFind = rMap.find("value_pos");
928         if (aFind != rMap.end())
929         {
930             sRet = aFind->second;
931             rMap.erase(aFind);
932         }
933         return sRet;
934     }
935 
936     OUString extractTypeHint(VclBuilder::stringmap &rMap)
937     {
938         OUString sRet("normal");
939         VclBuilder::stringmap::iterator aFind = rMap.find("type-hint");
940         if (aFind != rMap.end())
941         {
942             sRet = aFind->second;
943             rMap.erase(aFind);
944         }
945         return sRet;
946     }
947 
948     bool extractResizable(VclBuilder::stringmap &rMap)
949     {
950         bool bResizable = true;
951         VclBuilder::stringmap::iterator aFind = rMap.find("resizable");
952         if (aFind != rMap.end())
953         {
954             bResizable = toBool(aFind->second);
955             rMap.erase(aFind);
956         }
957         return bResizable;
958     }
959 
960 #if HAVE_FEATURE_DESKTOP
961     bool extractModal(VclBuilder::stringmap &rMap)
962     {
963         bool bModal = false;
964         VclBuilder::stringmap::iterator aFind = rMap.find("modal");
965         if (aFind != rMap.end())
966         {
967             bModal = toBool(aFind->second);
968             rMap.erase(aFind);
969         }
970         return bModal;
971     }
972 #endif
973 
974     bool extractDecorated(VclBuilder::stringmap &rMap)
975     {
976         bool bDecorated = true;
977         VclBuilder::stringmap::iterator aFind = rMap.find("decorated");
978         if (aFind != rMap.end())
979         {
980             bDecorated = toBool(aFind->second);
981             rMap.erase(aFind);
982         }
983         return bDecorated;
984     }
985 
986     bool extractCloseable(VclBuilder::stringmap &rMap)
987     {
988         bool bCloseable = true;
989         VclBuilder::stringmap::iterator aFind = rMap.find("deletable");
990         if (aFind != rMap.end())
991         {
992             bCloseable = toBool(aFind->second);
993             rMap.erase(aFind);
994         }
995         return bCloseable;
996     }
997 
998     bool extractEntry(VclBuilder::stringmap &rMap)
999     {
1000         bool bHasEntry = false;
1001         VclBuilder::stringmap::iterator aFind = rMap.find("has-entry");
1002         if (aFind != rMap.end())
1003         {
1004             bHasEntry = toBool(aFind->second);
1005             rMap.erase(aFind);
1006         }
1007         return bHasEntry;
1008     }
1009 
1010     bool extractOrientation(VclBuilder::stringmap &rMap)
1011     {
1012         bool bVertical = false;
1013         VclBuilder::stringmap::iterator aFind = rMap.find("orientation");
1014         if (aFind != rMap.end())
1015         {
1016             bVertical = aFind->second.equalsIgnoreAsciiCase("vertical");
1017             rMap.erase(aFind);
1018         }
1019         return bVertical;
1020     }
1021 
1022     bool extractVerticalTabPos(VclBuilder::stringmap &rMap)
1023     {
1024         bool bVertical = false;
1025         VclBuilder::stringmap::iterator aFind = rMap.find("tab-pos");
1026         if (aFind != rMap.end())
1027         {
1028             bVertical = aFind->second.equalsIgnoreAsciiCase("left") ||
1029                         aFind->second.equalsIgnoreAsciiCase("right");
1030             rMap.erase(aFind);
1031         }
1032         return bVertical;
1033     }
1034 
1035     bool extractInconsistent(VclBuilder::stringmap &rMap)
1036     {
1037         bool bInconsistent = false;
1038         VclBuilder::stringmap::iterator aFind = rMap.find("inconsistent");
1039         if (aFind != rMap.end())
1040         {
1041             bInconsistent = toBool(aFind->second);
1042             rMap.erase(aFind);
1043         }
1044         return bInconsistent;
1045     }
1046 
1047     OUString extractIconName(VclBuilder::stringmap &rMap)
1048     {
1049         OUString sIconName;
1050         VclBuilder::stringmap::iterator aFind = rMap.find(OString("icon-name"));
1051         if (aFind != rMap.end())
1052         {
1053             sIconName = aFind->second;
1054             rMap.erase(aFind);
1055         }
1056         return sIconName;
1057     }
1058 
1059     OUString extractStockId(VclBuilder::stringmap &rMap)
1060     {
1061         OUString sIconName;
1062         VclBuilder::stringmap::iterator aFind = rMap.find(OString("stock-id"));
1063         if (aFind != rMap.end())
1064         {
1065             sIconName = aFind->second;
1066             rMap.erase(aFind);
1067         }
1068         return sIconName;
1069     }
1070 
1071     OUString getStockText(const OUString &rType)
1072     {
1073         if (rType == "gtk-ok")
1074             return VclResId(SV_BUTTONTEXT_OK);
1075         else if (rType == "gtk-cancel")
1076             return VclResId(SV_BUTTONTEXT_CANCEL);
1077         else if (rType == "gtk-help")
1078             return VclResId(SV_BUTTONTEXT_HELP);
1079         else if (rType == "gtk-close")
1080             return VclResId(SV_BUTTONTEXT_CLOSE);
1081         else if (rType == "gtk-revert-to-saved")
1082             return VclResId(SV_BUTTONTEXT_RESET);
1083         else if (rType == "gtk-add")
1084             return VclResId(SV_BUTTONTEXT_ADD);
1085         else if (rType == "gtk-delete")
1086             return VclResId(SV_BUTTONTEXT_DELETE);
1087         else if (rType == "gtk-remove")
1088             return VclResId(SV_BUTTONTEXT_REMOVE);
1089         else if (rType == "gtk-new")
1090             return VclResId(SV_BUTTONTEXT_NEW);
1091         else if (rType == "gtk-edit")
1092             return VclResId(SV_BUTTONTEXT_EDIT);
1093         else if (rType == "gtk-apply")
1094             return VclResId(SV_BUTTONTEXT_APPLY);
1095         else if (rType == "gtk-save")
1096             return VclResId(SV_BUTTONTEXT_SAVE);
1097         else if (rType == "gtk-open")
1098             return VclResId(SV_BUTTONTEXT_OPEN);
1099         else if (rType == "gtk-undo")
1100             return VclResId(SV_BUTTONTEXT_UNDO);
1101         else if (rType == "gtk-paste")
1102             return VclResId(SV_BUTTONTEXT_PASTE);
1103         else if (rType == "gtk-media-next")
1104             return VclResId(SV_BUTTONTEXT_NEXT);
1105         else if (rType == "gtk-media-previous")
1106             return VclResId(SV_BUTTONTEXT_PREV);
1107         else if (rType == "gtk-go-up")
1108             return VclResId(SV_BUTTONTEXT_GO_UP);
1109         else if (rType == "gtk-go-down")
1110             return VclResId(SV_BUTTONTEXT_GO_DOWN);
1111         else if (rType == "gtk-clear")
1112             return VclResId(SV_BUTTONTEXT_CLEAR);
1113         else if (rType == "gtk-media-play")
1114             return VclResId(SV_BUTTONTEXT_PLAY);
1115         else if (rType == "gtk-find")
1116             return VclResId(SV_BUTTONTEXT_FIND);
1117         else if (rType == "gtk-stop")
1118             return VclResId(SV_BUTTONTEXT_STOP);
1119         else if (rType == "gtk-connect")
1120             return VclResId(SV_BUTTONTEXT_CONNECT);
1121         else if (rType == "gtk-yes")
1122             return VclResId(SV_BUTTONTEXT_YES);
1123         else if (rType == "gtk-no")
1124             return VclResId(SV_BUTTONTEXT_NO);
1125         SAL_WARN("vcl.builder", "unknown stock type: " << rType);
1126         return OUString();
1127     }
1128 
1129     bool extractStock(VclBuilder::stringmap &rMap)
1130     {
1131         bool bIsStock = false;
1132         VclBuilder::stringmap::iterator aFind = rMap.find(OString("use-stock"));
1133         if (aFind != rMap.end())
1134         {
1135             bIsStock = toBool(aFind->second);
1136             rMap.erase(aFind);
1137         }
1138         return bIsStock;
1139     }
1140 
1141     WinBits extractRelief(VclBuilder::stringmap &rMap)
1142     {
1143         WinBits nBits = WB_3DLOOK;
1144         VclBuilder::stringmap::iterator aFind = rMap.find(OString("relief"));
1145         if (aFind != rMap.end())
1146         {
1147             if (aFind->second == "half")
1148                 nBits = WB_FLATBUTTON | WB_BEVELBUTTON;
1149             else if (aFind->second == "none")
1150                 nBits = WB_FLATBUTTON;
1151             rMap.erase(aFind);
1152         }
1153         return nBits;
1154     }
1155 
1156     OUString extractLabel(VclBuilder::stringmap &rMap)
1157     {
1158         OUString sType;
1159         VclBuilder::stringmap::iterator aFind = rMap.find(OString("label"));
1160         if (aFind != rMap.end())
1161         {
1162             sType = aFind->second;
1163             rMap.erase(aFind);
1164         }
1165         return sType;
1166     }
1167 
1168     OUString extractActionName(VclBuilder::stringmap &rMap)
1169     {
1170         OUString sActionName;
1171         VclBuilder::stringmap::iterator aFind = rMap.find(OString("action-name"));
1172         if (aFind != rMap.end())
1173         {
1174             sActionName = aFind->second;
1175             rMap.erase(aFind);
1176         }
1177         return sActionName;
1178     }
1179 
1180     bool extractVisible(VclBuilder::stringmap &rMap)
1181     {
1182         bool bRet = false;
1183         VclBuilder::stringmap::iterator aFind = rMap.find(OString("visible"));
1184         if (aFind != rMap.end())
1185         {
1186             bRet = toBool(aFind->second);
1187             rMap.erase(aFind);
1188         }
1189         return bRet;
1190     }
1191 
1192     Size extractSizeRequest(VclBuilder::stringmap &rMap)
1193     {
1194         OUString sWidthRequest("0");
1195         OUString sHeightRequest("0");
1196         VclBuilder::stringmap::iterator aFind = rMap.find(OString("width-request"));
1197         if (aFind != rMap.end())
1198         {
1199             sWidthRequest = aFind->second;
1200             rMap.erase(aFind);
1201         }
1202         aFind = rMap.find("height-request");
1203         if (aFind != rMap.end())
1204         {
1205             sHeightRequest = aFind->second;
1206             rMap.erase(aFind);
1207         }
1208         return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32());
1209     }
1210 
1211     OUString extractTooltipText(VclBuilder::stringmap &rMap)
1212     {
1213         OUString sTooltipText;
1214         VclBuilder::stringmap::iterator aFind = rMap.find(OString("tooltip-text"));
1215         if (aFind == rMap.end())
1216             aFind = rMap.find(OString("tooltip-markup"));
1217         if (aFind != rMap.end())
1218         {
1219             sTooltipText = aFind->second;
1220             rMap.erase(aFind);
1221         }
1222         return sTooltipText;
1223     }
1224 
1225     float extractAlignment(VclBuilder::stringmap &rMap)
1226     {
1227         float f = 0.0;
1228         VclBuilder::stringmap::iterator aFind = rMap.find(OString("alignment"));
1229         if (aFind != rMap.end())
1230         {
1231             f = aFind->second.toFloat();
1232             rMap.erase(aFind);
1233         }
1234         return f;
1235     }
1236 
1237     OUString extractTitle(VclBuilder::stringmap &rMap)
1238     {
1239         OUString sTitle;
1240         VclBuilder::stringmap::iterator aFind = rMap.find(OString("title"));
1241         if (aFind != rMap.end())
1242         {
1243             sTitle = aFind->second;
1244             rMap.erase(aFind);
1245         }
1246         return sTitle;
1247     }
1248 
1249     bool extractHeadersVisible(VclBuilder::stringmap &rMap)
1250     {
1251         bool bHeadersVisible = true;
1252         VclBuilder::stringmap::iterator aFind = rMap.find(OString("headers-visible"));
1253         if (aFind != rMap.end())
1254         {
1255             bHeadersVisible = toBool(aFind->second);
1256             rMap.erase(aFind);
1257         }
1258         return bHeadersVisible;
1259     }
1260 
1261     bool extractSortIndicator(VclBuilder::stringmap &rMap)
1262     {
1263         bool bSortIndicator = false;
1264         VclBuilder::stringmap::iterator aFind = rMap.find(OString("sort-indicator"));
1265         if (aFind != rMap.end())
1266         {
1267             bSortIndicator = toBool(aFind->second);
1268             rMap.erase(aFind);
1269         }
1270         return bSortIndicator;
1271     }
1272 
1273     bool extractClickable(VclBuilder::stringmap &rMap)
1274     {
1275         bool bClickable = false;
1276         VclBuilder::stringmap::iterator aFind = rMap.find(OString("clickable"));
1277         if (aFind != rMap.end())
1278         {
1279             bClickable = toBool(aFind->second);
1280             rMap.erase(aFind);
1281         }
1282         return bClickable;
1283     }
1284 
1285     void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame)
1286     {
1287         if (!rFrame.is())
1288             return;
1289 
1290         OUString aCommand(extractActionName(rMap));
1291         if (aCommand.isEmpty())
1292             return;
1293 
1294         OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
1295         auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, aModuleName);
1296         OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
1297         if (!aLabel.isEmpty())
1298             pButton->SetText(aLabel);
1299 
1300         OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(aCommand, aProperties, rFrame));
1301         if (!aTooltip.isEmpty())
1302             pButton->SetQuickHelpText(aTooltip);
1303 
1304         Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, rFrame));
1305         pButton->SetModeImage(aImage);
1306 
1307         pButton->SetCommandHandler(aCommand);
1308     }
1309 
1310     VclPtr<Button> extractStockAndBuildPushButton(vcl::Window *pParent, VclBuilder::stringmap &rMap, bool bToggle, bool bLegacy)
1311     {
1312         WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER;
1313         if (bToggle)
1314             nBits |= WB_TOGGLE;
1315 
1316         nBits |= extractRelief(rMap);
1317 
1318         VclPtr<Button> xWindow;
1319 
1320         if (extractStock(rMap))
1321         {
1322             OUString sType = extractLabel(rMap);
1323             if (bLegacy)
1324             {
1325                 if (sType == "gtk-ok")
1326                     xWindow = VclPtr<OKButton>::Create(pParent, nBits);
1327                 else if (sType == "gtk-cancel")
1328                     xWindow = VclPtr<CancelButton>::Create(pParent, nBits);
1329                 else if (sType == "gtk-close")
1330                     xWindow = VclPtr<CloseButton>::Create(pParent, nBits);
1331                 else if (sType == "gtk-help")
1332                     xWindow = VclPtr<HelpButton>::Create(pParent, nBits);
1333             }
1334             if (!xWindow)
1335             {
1336                 xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1337                 xWindow->SetText(getStockText(sType));
1338             }
1339         }
1340 
1341         if (!xWindow)
1342             xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1343         return xWindow;
1344     }
1345 
1346     VclPtr<MenuButton> extractStockAndBuildMenuButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1347     {
1348         WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1349 
1350         nBits |= extractRelief(rMap);
1351 
1352         VclPtr<MenuButton> xWindow = VclPtr<MenuButton>::Create(pParent, nBits);
1353 
1354         if (extractStock(rMap))
1355         {
1356             xWindow->SetText(getStockText(extractLabel(rMap)));
1357         }
1358 
1359         return xWindow;
1360     }
1361 
1362     VclPtr<Button> extractStockAndBuildMenuToggleButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1363     {
1364         WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1365 
1366         nBits |= extractRelief(rMap);
1367 
1368         VclPtr<Button> xWindow = VclPtr<MenuToggleButton>::Create(pParent, nBits);
1369 
1370         if (extractStock(rMap))
1371         {
1372             xWindow->SetText(getStockText(extractLabel(rMap)));
1373         }
1374 
1375         return xWindow;
1376     }
1377 
1378     WinBits extractDeferredBits(VclBuilder::stringmap &rMap)
1379     {
1380         WinBits nBits = WB_3DLOOK|WB_HIDE;
1381         if (extractResizable(rMap))
1382             nBits |= WB_SIZEABLE;
1383         if (extractCloseable(rMap))
1384             nBits |= WB_CLOSEABLE;
1385         OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
1386         if (!sBorder.isEmpty())
1387             nBits |= WB_BORDER;
1388         if (!extractDecorated(rMap))
1389             nBits |= WB_OWNERDRAWDECORATION;
1390         OUString sType(extractTypeHint(rMap));
1391         if (sType == "utility")
1392             nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_MOVEABLE;
1393         else if (sType == "popup-menu")
1394             nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_POPUP;
1395         else if (sType == "dock")
1396             nBits |= WB_DOCKABLE | WB_MOVEABLE;
1397         else
1398             nBits |= WB_MOVEABLE;
1399         return nBits;
1400     }
1401 }
1402 
1403 void VclBuilder::extractGroup(const OString &id, stringmap &rMap)
1404 {
1405     VclBuilder::stringmap::iterator aFind = rMap.find(OString("group"));
1406     if (aFind != rMap.end())
1407     {
1408         OUString sID = aFind->second;
1409         sal_Int32 nDelim = sID.indexOf(':');
1410         if (nDelim != -1)
1411             sID = sID.copy(0, nDelim);
1412         m_pParserState->m_aGroupMaps.emplace_back(id, sID.toUtf8());
1413         rMap.erase(aFind);
1414     }
1415 }
1416 
1417 void VclBuilder::connectNumericFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1418 {
1419     if (!rAdjustment.isEmpty())
1420         m_pParserState->m_aNumericFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1421 }
1422 
1423 void VclBuilder::connectFormattedFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1424 {
1425     if (!rAdjustment.isEmpty())
1426         m_pParserState->m_aFormattedFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1427 }
1428 
1429 bool VclBuilder::extractAdjustmentToMap(const OString& id, VclBuilder::stringmap& rMap, std::vector<WidgetAdjustmentMap>& rAdjustmentMap)
1430 {
1431     VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1432     if (aFind != rMap.end())
1433     {
1434         rAdjustmentMap.emplace_back(id, aFind->second);
1435         rMap.erase(aFind);
1436         return true;
1437     }
1438     return false;
1439 }
1440 
1441 namespace
1442 {
1443     sal_Int32 extractActive(VclBuilder::stringmap &rMap)
1444     {
1445         sal_Int32 nActiveId = 0;
1446         VclBuilder::stringmap::iterator aFind = rMap.find(OString("active"));
1447         if (aFind != rMap.end())
1448         {
1449             nActiveId = aFind->second.toInt32();
1450             rMap.erase(aFind);
1451         }
1452         return nActiveId;
1453     }
1454 
1455     bool extractSelectable(VclBuilder::stringmap &rMap)
1456     {
1457         bool bSelectable = false;
1458         VclBuilder::stringmap::iterator aFind = rMap.find(OString("selectable"));
1459         if (aFind != rMap.end())
1460         {
1461             bSelectable = toBool(aFind->second);
1462             rMap.erase(aFind);
1463         }
1464         return bSelectable;
1465     }
1466 
1467     OUString extractAdjustment(VclBuilder::stringmap &rMap)
1468     {
1469         OUString sAdjustment;
1470         VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1471         if (aFind != rMap.end())
1472         {
1473             sAdjustment= aFind->second;
1474             rMap.erase(aFind);
1475             return sAdjustment;
1476         }
1477         return sAdjustment;
1478     }
1479 
1480     bool extractDrawIndicator(VclBuilder::stringmap &rMap)
1481     {
1482         bool bDrawIndicator = false;
1483         VclBuilder::stringmap::iterator aFind = rMap.find(OString("draw-indicator"));
1484         if (aFind != rMap.end())
1485         {
1486             bDrawIndicator = toBool(aFind->second);
1487             rMap.erase(aFind);
1488         }
1489         return bDrawIndicator;
1490     }
1491 }
1492 
1493 void VclBuilder::extractModel(const OString &id, stringmap &rMap)
1494 {
1495     VclBuilder::stringmap::iterator aFind = rMap.find(OString("model"));
1496     if (aFind != rMap.end())
1497     {
1498         m_pParserState->m_aModelMaps.emplace_back(id, aFind->second,
1499             extractActive(rMap));
1500         rMap.erase(aFind);
1501     }
1502 }
1503 
1504 void VclBuilder::extractBuffer(const OString &id, stringmap &rMap)
1505 {
1506     VclBuilder::stringmap::iterator aFind = rMap.find(OString("buffer"));
1507     if (aFind != rMap.end())
1508     {
1509         m_pParserState->m_aTextBufferMaps.emplace_back(id, aFind->second);
1510         rMap.erase(aFind);
1511     }
1512 }
1513 
1514 void VclBuilder::extractStock(const OString &id, stringmap &rMap)
1515 {
1516     VclBuilder::stringmap::iterator aFind = rMap.find(OString("stock"));
1517     if (aFind != rMap.end())
1518     {
1519         stockinfo aInfo;
1520         aInfo.m_sStock = aFind->second;
1521         rMap.erase(aFind);
1522         aFind = rMap.find(OString("icon-size"));
1523         if (aFind != rMap.end())
1524         {
1525             aInfo.m_nSize = aFind->second.toInt32();
1526             rMap.erase(aFind);
1527         }
1528         m_pParserState->m_aStockMap[id] = aInfo;
1529     }
1530 }
1531 
1532 void VclBuilder::extractButtonImage(const OString &id, stringmap &rMap, bool bRadio)
1533 {
1534     VclBuilder::stringmap::iterator aFind = rMap.find(OString("image"));
1535     if (aFind != rMap.end())
1536     {
1537         m_pParserState->m_aButtonImageWidgetMaps.emplace_back(id, aFind->second, bRadio);
1538         rMap.erase(aFind);
1539     }
1540 }
1541 
1542 void VclBuilder::extractMnemonicWidget(const OString &rLabelID, stringmap &rMap)
1543 {
1544     VclBuilder::stringmap::iterator aFind = rMap.find(OString("mnemonic-widget"));
1545     if (aFind != rMap.end())
1546     {
1547         OUString sID = aFind->second;
1548         sal_Int32 nDelim = sID.indexOf(':');
1549         if (nDelim != -1)
1550             sID = sID.copy(0, nDelim);
1551         m_pParserState->m_aMnemonicWidgetMaps.emplace_back(rLabelID, sID);
1552         rMap.erase(aFind);
1553     }
1554 }
1555 
1556 vcl::Window* VclBuilder::prepareWidgetOwnScrolling(vcl::Window *pParent, WinBits &rWinStyle)
1557 {
1558     //For Widgets that manage their own scrolling, if one appears as a child of
1559     //a scrolling window shoehorn that scrolling settings to this widget and
1560     //return the real parent to use
1561     if (pParent && pParent->GetType() == WindowType::SCROLLWINDOW)
1562     {
1563         WinBits nScrollBits = pParent->GetStyle();
1564         nScrollBits &= (WB_AUTOHSCROLL|WB_HSCROLL|WB_AUTOVSCROLL|WB_VSCROLL);
1565         rWinStyle |= nScrollBits | WB_BORDER;
1566         pParent = pParent->GetParent();
1567     }
1568 
1569     return pParent;
1570 }
1571 
1572 void VclBuilder::cleanupWidgetOwnScrolling(vcl::Window *pScrollParent, vcl::Window *pWindow, stringmap &rMap)
1573 {
1574     //remove the redundant scrolling parent
1575     sal_Int32 nWidthReq = pScrollParent->get_width_request();
1576     rMap[OString("width-request")] = OUString::number(nWidthReq);
1577     sal_Int32 nHeightReq = pScrollParent->get_height_request();
1578     rMap[OString("height-request")] = OUString::number(nHeightReq);
1579 
1580     m_pParserState->m_aRedundantParentWidgets[pScrollParent] = pWindow;
1581 }
1582 
1583 #ifndef DISABLE_DYNLOADING
1584 
1585 extern "C" { static void thisModule() {} }
1586 
1587 namespace {
1588 
1589 // Don't unload the module on destruction
1590 class NoAutoUnloadModule : public osl::Module
1591 {
1592 public:
1593     ~NoAutoUnloadModule() { release(); }
1594 };
1595 
1596 }
1597 
1598 typedef std::map<OUString, std::shared_ptr<NoAutoUnloadModule>> ModuleMap;
1599 static ModuleMap g_aModuleMap;
1600 
1601 #if ENABLE_MERGELIBS
1602 static std::shared_ptr<NoAutoUnloadModule> g_pMergedLib = std::make_shared<NoAutoUnloadModule>();
1603 #endif
1604 
1605 #ifndef SAL_DLLPREFIX
1606 #  define SAL_DLLPREFIX ""
1607 #endif
1608 
1609 #endif
1610 
1611 void VclBuilder::preload()
1612 {
1613 #ifndef DISABLE_DYNLOADING
1614 
1615 #if ENABLE_MERGELIBS
1616     g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1617 #else
1618 // find -name '*ui*' | xargs grep 'class=".*lo-' |
1619 //     sed 's/.*class="//' | sed 's/-.*$//' | sort | uniq
1620     static const char *aWidgetLibs[] = {
1621         "sfxlo",  "svtlo",     "svxcorelo", "foruilo",
1622         "vcllo",  "svxlo",     "cuilo",     "swlo",
1623         "swuilo", "sclo",      "sdlo",      "chartcontrollerlo",
1624         "smlo",   "scuilo",    "basctllo",  "sduilo",
1625         "scnlo",  "xsltdlglo", "pcrlo" // "dbulo"
1626     };
1627     for (const auto & lib : aWidgetLibs)
1628     {
1629         std::unique_ptr<NoAutoUnloadModule> pModule(new NoAutoUnloadModule);
1630         OUString sModule = SAL_DLLPREFIX + OUString::createFromAscii(lib) + SAL_DLLEXTENSION;
1631         if (pModule->loadRelative(&thisModule, sModule))
1632             g_aModuleMap.insert(std::make_pair(sModule, std::move(pModule)));
1633     }
1634 #endif // ENABLE_MERGELIBS
1635 #endif // DISABLE_DYNLOADING
1636 }
1637 
1638 #if defined DISABLE_DYNLOADING && !HAVE_FEATURE_DESKTOP
1639 extern "C" VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name);
1640 #endif
1641 
1642 namespace
1643 {
1644 // Takes a string like "sfxlo-SidebarToolBox"
1645 VclBuilder::customMakeWidget GetCustomMakeWidget(const OString& name)
1646 {
1647     VclBuilder::customMakeWidget pFunction = nullptr;
1648     if (sal_Int32 nDelim = name.indexOf('-'); nDelim != -1)
1649     {
1650         const OString aFunction("make" + name.copy(nDelim + 1));
1651         const OUString sFunction(OStringToOUString(aFunction, RTL_TEXTENCODING_UTF8));
1652 
1653 #ifndef DISABLE_DYNLOADING
1654         const OUString sModule = SAL_DLLPREFIX
1655                                  + OStringToOUString(name.copy(0, nDelim), RTL_TEXTENCODING_UTF8)
1656                                  + SAL_DLLEXTENSION;
1657         ModuleMap::iterator aI = g_aModuleMap.find(sModule);
1658         if (aI == g_aModuleMap.end())
1659         {
1660             std::shared_ptr<NoAutoUnloadModule> pModule;
1661 #if ENABLE_MERGELIBS
1662             if (!g_pMergedLib->is())
1663                 g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1664             if ((pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1665                      g_pMergedLib->getFunctionSymbol(sFunction))))
1666                 pModule = g_pMergedLib;
1667 #endif
1668             if (!pFunction)
1669             {
1670                 pModule = std::make_shared<NoAutoUnloadModule>();
1671                 bool ok = pModule->loadRelative(&thisModule, sModule);
1672                 if (!ok)
1673                 {
1674 #ifdef LINUX
1675                     // in the case of preloading, we don't have eg. the
1676                     // libcuilo.so, but still need to dlsym the symbols -
1677                     // which are already in-process
1678                     if (comphelper::LibreOfficeKit::isActive())
1679                     {
1680                         pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(dlsym(RTLD_DEFAULT, aFunction.getStr()));
1681                         ok = !!pFunction;
1682                         assert(ok && "couldn't even directly dlsym the sFunction (available via preload)");
1683                     }
1684 #endif
1685                     assert(ok && "bad module name in .ui");
1686                 }
1687                 else
1688                 {
1689                     pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1690                             pModule->getFunctionSymbol(sFunction));
1691                 }
1692             }
1693             g_aModuleMap.insert(std::make_pair(sModule, pModule));
1694         }
1695         else
1696             pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1697                 aI->second->getFunctionSymbol(sFunction));
1698 #elif !HAVE_FEATURE_DESKTOP
1699         pFunction = lo_get_custom_widget_func(sFunction.toUtf8().getStr());
1700         SAL_WARN_IF(!pFunction, "vcl.builder", "Could not find " << sFunction);
1701         assert(pFunction);
1702 #else
1703         pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1704             osl_getFunctionSymbol((oslModule)RTLD_DEFAULT, sFunction.pData));
1705 #endif
1706     }
1707     return pFunction;
1708 }
1709 }
1710 
1711 VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString &name, const OString &id,
1712     stringmap &rMap)
1713 {
1714     bool bIsPlaceHolder = name.isEmpty();
1715     bool bVertical = false;
1716 
1717     if (pParent && (pParent->GetType() == WindowType::TABCONTROL ||
1718                     pParent->GetType() == WindowType::VERTICALTABCONTROL))
1719     {
1720         bool bTopLevel(name == "GtkDialog" || name == "GtkMessageDialog" ||
1721                        name == "GtkWindow" || name == "GtkPopover" || name == "GtkAssistant");
1722         if (!bTopLevel)
1723         {
1724             if (pParent->GetType() == WindowType::TABCONTROL)
1725             {
1726                 //We have to add a page
1727                 //make default pageid == position
1728                 TabControl *pTabControl = static_cast<TabControl*>(pParent);
1729                 sal_uInt16 nNewPageCount = pTabControl->GetPageCount()+1;
1730                 sal_uInt16 nNewPageId = nNewPageCount;
1731                 pTabControl->InsertPage(nNewPageId, OUString());
1732                 pTabControl->SetCurPageId(nNewPageId);
1733                 SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
1734                 if (!bIsPlaceHolder)
1735                 {
1736                     VclPtrInstance<TabPage> pPage(pTabControl);
1737                     pPage->Show();
1738 
1739                     //Make up a name for it
1740                     OString sTabPageId = get_by_window(pParent) +
1741                         "-page" +
1742                         OString::number(nNewPageCount);
1743                     m_aChildren.emplace_back(sTabPageId, pPage, false);
1744                     pPage->SetHelpId(m_sHelpRoot + sTabPageId);
1745 
1746                     pParent = pPage;
1747 
1748                     pTabControl->SetTabPage(nNewPageId, pPage);
1749                 }
1750             }
1751             else
1752             {
1753                 VerticalTabControl *pTabControl = static_cast<VerticalTabControl*>(pParent);
1754                 SAL_WARN_IF(bIsPlaceHolder, "vcl.builder", "we should have no placeholders for tabpages");
1755                 if (!bIsPlaceHolder)
1756                     pParent = pTabControl->GetPageParent();
1757             }
1758         }
1759     }
1760 
1761     if (bIsPlaceHolder || name == "GtkTreeSelection")
1762         return nullptr;
1763 
1764     ToolBox *pToolBox = (pParent && pParent->GetType() == WindowType::TOOLBOX) ? static_cast<ToolBox*>(pParent) : nullptr;
1765 
1766     extractButtonImage(id, rMap, name == "GtkRadioButton");
1767 
1768     VclPtr<vcl::Window> xWindow;
1769     if (name == "GtkDialog" || name == "GtkAssistant")
1770     {
1771         // WB_ALLOWMENUBAR because we don't know in advance if we will encounter
1772         // a menubar, and menubars need a BorderWindow in the toplevel, and
1773         // such border windows need to be in created during the dialog ctor
1774         WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_ALLOWMENUBAR;
1775         if (extractResizable(rMap))
1776             nBits |= WB_SIZEABLE;
1777         if (extractCloseable(rMap))
1778             nBits |= WB_CLOSEABLE;
1779         Dialog::InitFlag eInit = !pParent ? Dialog::InitFlag::NoParent : Dialog::InitFlag::Default;
1780         if (name == "GtkAssistant")
1781             xWindow = VclPtr<vcl::RoadmapWizard>::Create(pParent, nBits, eInit);
1782         else
1783             xWindow = VclPtr<Dialog>::Create(pParent, nBits, eInit);
1784 #if HAVE_FEATURE_DESKTOP
1785         if (!m_bLegacy && !extractModal(rMap))
1786             xWindow->SetType(WindowType::MODELESSDIALOG);
1787 #endif
1788     }
1789     else if (name == "GtkMessageDialog")
1790     {
1791         WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE;
1792         if (extractResizable(rMap))
1793             nBits |= WB_SIZEABLE;
1794         VclPtr<MessageDialog> xDialog(VclPtr<MessageDialog>::Create(pParent, nBits));
1795         m_pParserState->m_aMessageDialogs.push_back(xDialog);
1796         xWindow = xDialog;
1797 #if defined _WIN32
1798         xWindow->set_border_width(3);
1799 #else
1800         xWindow->set_border_width(12);
1801 #endif
1802     }
1803     else if (name == "GtkBox" || name == "GtkStatusbar")
1804     {
1805         bVertical = extractOrientation(rMap);
1806         if (bVertical)
1807             xWindow = VclPtr<VclVBox>::Create(pParent);
1808         else
1809             xWindow = VclPtr<VclHBox>::Create(pParent);
1810     }
1811     else if (name == "GtkPaned")
1812     {
1813         bVertical = extractOrientation(rMap);
1814         if (bVertical)
1815             xWindow = VclPtr<VclVPaned>::Create(pParent);
1816         else
1817             xWindow = VclPtr<VclHPaned>::Create(pParent);
1818     }
1819     else if (name == "GtkHBox")
1820         xWindow = VclPtr<VclHBox>::Create(pParent);
1821     else if (name == "GtkVBox")
1822         xWindow = VclPtr<VclVBox>::Create(pParent);
1823     else if (name == "GtkButtonBox")
1824     {
1825         bVertical = extractOrientation(rMap);
1826         if (bVertical)
1827             xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1828         else
1829             xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1830     }
1831     else if (name == "GtkHButtonBox")
1832         xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1833     else if (name == "GtkVButtonBox")
1834         xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1835     else if (name == "GtkGrid")
1836         xWindow = VclPtr<VclGrid>::Create(pParent);
1837     else if (name == "GtkFrame")
1838         xWindow = VclPtr<VclFrame>::Create(pParent);
1839     else if (name == "GtkExpander")
1840     {
1841         VclPtrInstance<VclExpander> pExpander(pParent);
1842         m_pParserState->m_aExpanderWidgets.push_back(pExpander);
1843         xWindow = pExpander;
1844     }
1845     else if (name == "GtkAlignment")
1846         xWindow = VclPtr<VclAlignment>::Create(pParent);
1847     else if (name == "GtkButton" || (!m_bLegacy && name == "GtkToggleButton"))
1848     {
1849         VclPtr<Button> xButton;
1850         OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1851         if (sMenu.isEmpty())
1852             xButton = extractStockAndBuildPushButton(pParent, rMap, name == "GtkToggleButton", m_bLegacy);
1853         else
1854         {
1855             assert(m_bLegacy && "use GtkMenuButton");
1856             xButton = extractStockAndBuildMenuButton(pParent, rMap);
1857             m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1858         }
1859         xButton->SetImageAlign(ImageAlign::Left); //default to left
1860         setupFromActionName(xButton, rMap, m_xFrame);
1861         xWindow = xButton;
1862     }
1863     else if (name == "GtkMenuButton")
1864     {
1865         VclPtr<MenuButton> xButton = extractStockAndBuildMenuButton(pParent, rMap);
1866         OUString sMenu = extractPopupMenu(rMap);
1867         if (!sMenu.isEmpty())
1868             m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1869         xButton->SetImageAlign(ImageAlign::Left); //default to left
1870         xButton->SetAccessibleRole(css::accessibility::AccessibleRole::BUTTON_MENU);
1871 
1872         if (!extractDrawIndicator(rMap))
1873             xButton->SetDropDown(PushButtonDropdownStyle::NONE);
1874 
1875         setupFromActionName(xButton, rMap, m_xFrame);
1876         xWindow = xButton;
1877     }
1878     else if (name == "GtkToggleButton" && m_bLegacy)
1879     {
1880         VclPtr<Button> xButton;
1881         OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1882         assert(sMenu.getLength() && "not implemented yet");
1883         xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
1884         m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1885         xButton->SetImageAlign(ImageAlign::Left); //default to left
1886         setupFromActionName(xButton, rMap, m_xFrame);
1887         xWindow = xButton;
1888     }
1889     else if (name == "GtkRadioButton")
1890     {
1891         extractGroup(id, rMap);
1892         WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1893         VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, nBits);
1894         xButton->SetImageAlign(ImageAlign::Left); //default to left
1895         xWindow = xButton;
1896 
1897         if (::extractStock(rMap))
1898         {
1899             xWindow->SetText(getStockText(extractLabel(rMap)));
1900         }
1901     }
1902     else if (name == "GtkCheckButton")
1903     {
1904         WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1905         bool bIsTriState = extractInconsistent(rMap);
1906         VclPtr<CheckBox> xCheckBox = VclPtr<CheckBox>::Create(pParent, nBits);
1907         if (bIsTriState)
1908         {
1909             xCheckBox->EnableTriState(true);
1910             xCheckBox->SetState(TRISTATE_INDET);
1911         }
1912         xCheckBox->SetImageAlign(ImageAlign::Left); //default to left
1913 
1914         xWindow = xCheckBox;
1915 
1916         if (::extractStock(rMap))
1917         {
1918             xWindow->SetText(getStockText(extractLabel(rMap)));
1919         }
1920     }
1921     else if (name == "GtkSpinButton")
1922     {
1923         OUString sAdjustment = extractAdjustment(rMap);
1924 
1925         WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_BORDER|WB_3DLOOK|WB_SPIN|WB_REPEAT;
1926 
1927         if (m_bLegacy)
1928         {
1929             connectNumericFormatterAdjustment(id, sAdjustment);
1930             xWindow = VclPtr<NumericField>::Create(pParent, nBits);
1931         }
1932         else
1933         {
1934             connectFormattedFormatterAdjustment(id, sAdjustment);
1935             VclPtrInstance<FormattedField> xField(pParent, nBits);
1936             xField->SetMinValue(0);
1937             xWindow = xField;
1938         }
1939     }
1940     else if (name == "GtkLinkButton")
1941         xWindow = VclPtr<FixedHyperlink>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_NOLABEL);
1942     else if (name == "GtkComboBox" || name == "GtkComboBoxText")
1943     {
1944         extractModel(id, rMap);
1945 
1946         WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1947 
1948         bool bDropdown = BuilderUtils::extractDropdown(rMap);
1949 
1950         if (bDropdown)
1951             nBits |= WB_DROPDOWN;
1952 
1953         if (extractEntry(rMap))
1954         {
1955             VclPtrInstance<ComboBox> xComboBox(pParent, nBits);
1956             xComboBox->EnableAutoSize(true);
1957             xWindow = xComboBox;
1958         }
1959         else
1960         {
1961             VclPtrInstance<ListBox> xListBox(pParent, nBits|WB_SIMPLEMODE);
1962             xListBox->EnableAutoSize(true);
1963             xWindow = xListBox;
1964         }
1965     }
1966     else if (name == "VclOptionalBox")
1967     {
1968         xWindow = VclPtr<OptionalBox>::Create(pParent);
1969     }
1970     else if (name == "GtkIconView")
1971     {
1972         assert(rMap.find(OString("model")) != rMap.end() && "GtkIconView must have a model");
1973 
1974         //window we want to apply the packing props for this GtkIconView to
1975         VclPtr<vcl::Window> xWindowForPackingProps;
1976         extractModel(id, rMap);
1977         WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1978         //IconView manages its own scrolling,
1979         vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
1980         if (pRealParent != pParent)
1981             nWinStyle |= WB_BORDER;
1982 
1983         VclPtr<IconView> xBox = VclPtr<IconView>::Create(pRealParent, nWinStyle);
1984         xWindowForPackingProps = xBox;
1985 
1986         xWindow = xBox;
1987         xBox->SetNoAutoCurEntry(true);
1988         xBox->SetQuickSearch(true);
1989 
1990         if (pRealParent != pParent)
1991             cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
1992     }
1993     else if (name == "GtkTreeView")
1994     {
1995         if (!m_bLegacy)
1996         {
1997             assert(rMap.find(OString("model")) != rMap.end() && "GtkTreeView must have a model");
1998         }
1999 
2000         //window we want to apply the packing props for this GtkTreeView to
2001         VclPtr<vcl::Window> xWindowForPackingProps;
2002         //To-Do
2003         //a) make SvHeaderTabListBox/SvTabListBox the default target for GtkTreeView
2004         //b) remove the non-drop down mode of ListBox and convert
2005         //   everything over to SvHeaderTabListBox/SvTabListBox
2006         //c) remove the users of makeSvTabListBox and makeSvTreeListBox
2007         extractModel(id, rMap);
2008         WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
2009         if (m_bLegacy)
2010         {
2011             OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2012             if (!sBorder.isEmpty())
2013                 nWinStyle |= WB_BORDER;
2014         }
2015         else
2016         {
2017             nWinStyle |= WB_HASBUTTONS | WB_HASBUTTONSATROOT;
2018         }
2019         //ListBox/SvHeaderTabListBox manages its own scrolling,
2020         vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2021         if (pRealParent != pParent)
2022             nWinStyle |= WB_BORDER;
2023         if (m_bLegacy)
2024         {
2025             xWindow = VclPtr<ListBox>::Create(pRealParent, nWinStyle | WB_SIMPLEMODE);
2026             xWindowForPackingProps = xWindow;
2027         }
2028         else
2029         {
2030             VclPtr<SvTabListBox> xBox;
2031             bool bHeadersVisible = extractHeadersVisible(rMap);
2032             if (bHeadersVisible)
2033             {
2034                 VclPtr<VclVBox> xContainer = VclPtr<VclVBox>::Create(pRealParent);
2035                 OString containerid(id + "-container");
2036                 xContainer->SetHelpId(m_sHelpRoot + containerid);
2037                 m_aChildren.emplace_back(containerid, xContainer, true);
2038 
2039                 VclPtrInstance<HeaderBar> xHeader(xContainer, WB_BUTTONSTYLE | WB_BORDER | WB_TABSTOP | WB_3DLOOK);
2040                 xHeader->set_width_request(0); // let the headerbar width not affect the size request
2041                 OString headerid(id + "-header");
2042                 xHeader->SetHelpId(m_sHelpRoot + headerid);
2043                 m_aChildren.emplace_back(headerid, xHeader, true);
2044 
2045                 VclPtr<LclHeaderTabListBox> xHeaderBox = VclPtr<LclHeaderTabListBox>::Create(xContainer, nWinStyle);
2046                 xHeaderBox->InitHeaderBar(xHeader);
2047                 xContainer->set_expand(true);
2048                 xHeader->Show();
2049                 xContainer->Show();
2050                 xBox = xHeaderBox;
2051                 xWindowForPackingProps = xContainer;
2052             }
2053             else
2054             {
2055                 xBox = VclPtr<LclTabListBox>::Create(pRealParent, nWinStyle);
2056                 xWindowForPackingProps = xBox;
2057             }
2058             xWindow = xBox;
2059             xBox->SetNoAutoCurEntry(true);
2060             xBox->SetQuickSearch(true);
2061             xBox->SetSpaceBetweenEntries(3);
2062             xBox->SetEntryHeight(16);
2063             xBox->SetHighlightRange(); // select over the whole width
2064         }
2065         if (pRealParent != pParent)
2066             cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
2067     }
2068     else if (name == "GtkTreeViewColumn")
2069     {
2070         if (!m_bLegacy)
2071         {
2072             SvHeaderTabListBox* pTreeView = dynamic_cast<SvHeaderTabListBox*>(pParent);
2073             if (HeaderBar* pHeaderBar = pTreeView ? pTreeView->GetHeaderBar() : nullptr)
2074             {
2075                 HeaderBarItemBits nBits = HeaderBarItemBits::LEFTIMAGE;
2076                 if (extractClickable(rMap))
2077                     nBits |= HeaderBarItemBits::CLICKABLE;
2078                 if (extractSortIndicator(rMap))
2079                     nBits |= HeaderBarItemBits::DOWNARROW;
2080                 float fAlign = extractAlignment(rMap);
2081                 if (fAlign == 0.0)
2082                     nBits |= HeaderBarItemBits::LEFT;
2083                 else if (fAlign == 1.0)
2084                     nBits |= HeaderBarItemBits::RIGHT;
2085                 else if (fAlign == 0.5)
2086                     nBits |= HeaderBarItemBits::CENTER;
2087                 auto nItemId = pHeaderBar->GetItemCount() + 1;
2088                 OUString sTitle(extractTitle(rMap));
2089                 pHeaderBar->InsertItem(nItemId, sTitle, 100, nBits);
2090             }
2091         }
2092     }
2093     else if (name == "GtkLabel")
2094     {
2095         WinBits nWinStyle = WB_CENTER|WB_VCENTER|WB_3DLOOK;
2096         extractMnemonicWidget(id, rMap);
2097         if (extractSelectable(rMap))
2098             xWindow = VclPtr<SelectableFixedText>::Create(pParent, nWinStyle);
2099         else
2100             xWindow = VclPtr<FixedText>::Create(pParent, nWinStyle);
2101     }
2102     else if (name == "GtkImage")
2103     {
2104         extractStock(id, rMap);
2105         xWindow = VclPtr<FixedImage>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_SCALE);
2106         //such parentless GtkImages are temps used to set icons on buttons
2107         //default them to hidden to stop e.g. insert->index entry flicking temp
2108         //full screen windows
2109         if (!pParent)
2110         {
2111             rMap["visible"] = "false";
2112         }
2113     }
2114     else if (name == "GtkSeparator")
2115     {
2116         bVertical = extractOrientation(rMap);
2117         xWindow = VclPtr<FixedLine>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2118     }
2119     else if (name == "GtkScrollbar")
2120     {
2121         extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2122         bVertical = extractOrientation(rMap);
2123         xWindow = VclPtr<ScrollBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2124     }
2125     else if (name == "GtkProgressBar")
2126     {
2127         extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2128         bVertical = extractOrientation(rMap);
2129         xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2130     }
2131     else if (name == "GtkScrolledWindow")
2132     {
2133         xWindow = VclPtr<VclScrolledWindow>::Create(pParent);
2134     }
2135     else if (name == "GtkViewport")
2136     {
2137         xWindow = VclPtr<VclViewport>::Create(pParent);
2138     }
2139     else if (name == "GtkEventBox")
2140     {
2141         xWindow = VclPtr<VclEventBox>::Create(pParent);
2142     }
2143     else if (name == "GtkEntry")
2144     {
2145         xWindow = VclPtr<Edit>::Create(pParent, WB_LEFT|WB_VCENTER|WB_BORDER|WB_3DLOOK);
2146         BuilderUtils::ensureDefaultWidthChars(rMap);
2147     }
2148     else if (name == "GtkNotebook")
2149     {
2150         if (!extractVerticalTabPos(rMap))
2151             xWindow = VclPtr<TabControl>::Create(pParent, WB_STDTABCONTROL|WB_3DLOOK);
2152         else
2153             xWindow = VclPtr<VerticalTabControl>::Create(pParent);
2154     }
2155     else if (name == "GtkDrawingArea")
2156     {
2157         xWindow = VclPtr<VclDrawingArea>::Create(pParent, WB_TABSTOP);
2158     }
2159     else if (name == "GtkTextView")
2160     {
2161         extractBuffer(id, rMap);
2162 
2163         WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT;
2164         //VclMultiLineEdit manages its own scrolling,
2165         vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2166         if (pRealParent != pParent)
2167             nWinStyle |= WB_BORDER;
2168         xWindow = VclPtr<VclMultiLineEdit>::Create(pRealParent, nWinStyle);
2169         if (pRealParent != pParent)
2170             cleanupWidgetOwnScrolling(pParent, xWindow, rMap);
2171     }
2172     else if (name == "GtkSpinner")
2173     {
2174         xWindow = VclPtr<Throbber>::Create(pParent, WB_3DLOOK);
2175     }
2176     else if (name == "GtkScale")
2177     {
2178         extractAdjustmentToMap(id, rMap, m_pParserState->m_aSliderAdjustmentMaps);
2179         bool bDrawValue = extractDrawValue(rMap);
2180         if (bDrawValue)
2181         {
2182             OUString sValuePos = extractValuePos(rMap);
2183             (void)sValuePos;
2184         }
2185         bVertical = extractOrientation(rMap);
2186 
2187         WinBits nWinStyle = bVertical ? WB_VERT : WB_HORZ;
2188 
2189         xWindow = VclPtr<Slider>::Create(pParent, nWinStyle);
2190     }
2191     else if (name == "GtkToolbar")
2192     {
2193         xWindow = VclPtr<ToolBox>::Create(pParent, WB_3DLOOK | WB_TABSTOP);
2194     }
2195     else if(name == "NotebookBarAddonsToolMergePoint")
2196     {
2197         customMakeWidget pFunction = GetCustomMakeWidget("sfxlo-NotebookbarToolBox");
2198         if(pFunction != nullptr)
2199             NotebookBarAddonsMerger::MergeNotebookBarAddons(pParent, pFunction, m_xFrame, *m_pNotebookBarAddonsItem, rMap);
2200         return nullptr;
2201     }
2202     else if (name == "GtkToolButton" || name == "GtkMenuToolButton" ||
2203              name == "GtkToggleToolButton" || name == "GtkRadioToolButton" || name == "GtkToolItem")
2204     {
2205         if (pToolBox)
2206         {
2207             OUString aCommand(extractActionName(rMap));
2208 
2209             sal_uInt16 nItemId = 0;
2210             ToolBoxItemBits nBits = ToolBoxItemBits::NONE;
2211             if (name == "GtkMenuToolButton")
2212                 nBits |= ToolBoxItemBits::DROPDOWN;
2213             else if (name == "GtkToggleToolButton")
2214                 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::CHECKABLE;
2215             else if (name == "GtkRadioToolButton")
2216                 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::RADIOCHECK;
2217 
2218             if (!aCommand.isEmpty() && m_xFrame.is())
2219             {
2220                 pToolBox->InsertItem(aCommand, m_xFrame, nBits, extractSizeRequest(rMap));
2221                 nItemId = pToolBox->GetItemId(aCommand);
2222             }
2223             else
2224             {
2225                 nItemId = pToolBox->GetItemCount() + 1;
2226                     //TODO: ImplToolItems::size_type -> sal_uInt16!
2227                 pToolBox->InsertItem(nItemId, extractLabel(rMap), nBits);
2228                 if (aCommand.isEmpty() && !m_bLegacy)
2229                     aCommand = OUString::fromUtf8(id);
2230                 pToolBox->SetItemCommand(nItemId, aCommand);
2231             }
2232 
2233             pToolBox->SetHelpId(nItemId, m_sHelpRoot + id);
2234             OUString sTooltip(extractTooltipText(rMap));
2235             if (!sTooltip.isEmpty())
2236                 pToolBox->SetQuickHelpText(nItemId, sTooltip);
2237 
2238             OUString sIconName(extractIconName(rMap));
2239             if (sIconName.isEmpty())
2240                 sIconName = mapStockToImageResource(extractStockId(rMap));
2241             if (!sIconName.isEmpty())
2242                 pToolBox->SetItemImage(nItemId, FixedImage::loadThemeImage(sIconName));
2243 
2244             if (!extractVisible(rMap))
2245                 pToolBox->HideItem(nItemId);
2246 
2247             m_pParserState->m_nLastToolbarId = nItemId;
2248 
2249             return nullptr; // no widget to be created
2250         }
2251     }
2252     else if (name == "GtkSeparatorToolItem")
2253     {
2254         if (pToolBox)
2255         {
2256             pToolBox->InsertSeparator();
2257             return nullptr; // no widget to be created
2258         }
2259     }
2260     else if (name == "GtkWindow")
2261     {
2262         WinBits nBits = extractDeferredBits(rMap);
2263         if (nBits & WB_DOCKABLE)
2264             xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2265         else
2266             xWindow = VclPtr<FloatingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2267     }
2268     else if (name == "GtkPopover")
2269     {
2270         WinBits nBits = extractDeferredBits(rMap);
2271         xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_DOCKABLE|WB_MOVEABLE);
2272     }
2273     else if (name == "GtkCalendar")
2274     {
2275         WinBits nBits = extractDeferredBits(rMap);
2276         xWindow = VclPtr<Calendar>::Create(pParent, nBits);
2277     }
2278     else
2279     {
2280         if (customMakeWidget pFunction = GetCustomMakeWidget(name))
2281         {
2282             pFunction(xWindow, pParent, rMap);
2283             if (xWindow->GetType() == WindowType::PUSHBUTTON)
2284                 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2285             else if (xWindow->GetType() == WindowType::MENUBUTTON)
2286             {
2287                 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
2288                 if (!sMenu.isEmpty())
2289                     m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
2290                 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2291             }
2292         }
2293     }
2294 
2295     SAL_INFO_IF(!xWindow, "vcl.builder", "probably need to implement " << name << " or add a make" << name << " function");
2296     if (xWindow)
2297     {
2298         // child windows of disabled windows are made disabled by vcl by default, we don't want that
2299         WindowImpl *pWindowImpl = xWindow->ImplGetWindowImpl();
2300         pWindowImpl->mbDisabled = false;
2301 
2302         xWindow->SetHelpId(m_sHelpRoot + id);
2303         SAL_INFO("vcl.builder", "for name '" << name << "' and id '" << id <<
2304             "', created " << xWindow.get() << " child of " <<
2305             pParent << "(" << xWindow->ImplGetWindowImpl()->mpParent.get() << "/" <<
2306             xWindow->ImplGetWindowImpl()->mpRealParent.get() << "/" <<
2307             xWindow->ImplGetWindowImpl()->mpBorderWindow.get() << ") with helpid " <<
2308             xWindow->GetHelpId());
2309         m_aChildren.emplace_back(id, xWindow, bVertical);
2310 
2311         // if the parent was a toolbox set it as an itemwindow for the latest itemid
2312         if (pToolBox)
2313         {
2314             Size aSize(xWindow->GetSizePixel());
2315             aSize.setHeight(xWindow->get_preferred_size().Height());
2316             xWindow->SetSizePixel(aSize);
2317             pToolBox->SetItemWindow(m_pParserState->m_nLastToolbarId, xWindow);
2318             pToolBox->SetItemExpand(m_pParserState->m_nLastToolbarId, true);
2319         }
2320     }
2321     return xWindow;
2322 }
2323 
2324 namespace
2325 {
2326     //return true for window types which exist in vcl but are not themselves
2327     //represented in the .ui format, i.e. only their children exist.
2328     bool isConsideredGtkPseudo(vcl::Window const *pWindow)
2329     {
2330         return pWindow->GetType() == WindowType::TABPAGE;
2331     }
2332 }
2333 
2334 //Any properties from .ui load we couldn't set because of potential virtual methods
2335 //during ctor are applied here
2336 void VclBuilder::setDeferredProperties()
2337 {
2338     if (!m_bToplevelHasDeferredProperties)
2339         return;
2340     stringmap aDeferredProperties;
2341     aDeferredProperties.swap(m_aDeferredProperties);
2342     m_bToplevelHasDeferredProperties = false;
2343     BuilderUtils::set_properties(m_pParent, aDeferredProperties);
2344 }
2345 
2346 namespace BuilderUtils
2347 {
2348     void set_properties(vcl::Window *pWindow, const VclBuilder::stringmap &rProps)
2349     {
2350         for (auto const& prop : rProps)
2351         {
2352             const OString &rKey = prop.first;
2353             const OUString &rValue = prop.second;
2354             pWindow->set_property(rKey, rValue);
2355         }
2356     }
2357 
2358     OUString convertMnemonicMarkup(const OUString &rIn)
2359     {
2360         OUStringBuffer aRet(rIn);
2361         for (sal_Int32 nI = 0; nI < aRet.getLength(); ++nI)
2362         {
2363             if (aRet[nI] == '_' && nI+1 < aRet.getLength())
2364             {
2365                 if (aRet[nI+1] != '_')
2366                     aRet[nI] = MNEMONIC_CHAR;
2367                 else
2368                     aRet.remove(nI, 1);
2369                 ++nI;
2370             }
2371         }
2372         return aRet.makeStringAndClear();
2373     }
2374 
2375     OUString extractCustomProperty(VclBuilder::stringmap &rMap)
2376     {
2377         OUString sCustomProperty;
2378         VclBuilder::stringmap::iterator aFind = rMap.find(OString("customproperty"));
2379         if (aFind != rMap.end())
2380         {
2381             sCustomProperty = aFind->second;
2382             rMap.erase(aFind);
2383         }
2384         return sCustomProperty;
2385     }
2386 
2387     void ensureDefaultWidthChars(VclBuilder::stringmap &rMap)
2388     {
2389         OString sWidthChars("width-chars");
2390         VclBuilder::stringmap::iterator aFind = rMap.find(sWidthChars);
2391         if (aFind == rMap.end())
2392             rMap[sWidthChars] = "25";
2393     }
2394 
2395     bool extractDropdown(VclBuilder::stringmap &rMap)
2396     {
2397         bool bDropdown = true;
2398         VclBuilder::stringmap::iterator aFind = rMap.find(OString("dropdown"));
2399         if (aFind != rMap.end())
2400         {
2401             bDropdown = toBool(aFind->second);
2402             rMap.erase(aFind);
2403         }
2404         return bDropdown;
2405     }
2406 
2407     void reorderWithinParent(vcl::Window &rWindow, sal_uInt16 nNewPosition)
2408     {
2409         WindowImpl *pWindowImpl = rWindow.ImplGetWindowImpl();
2410         if (pWindowImpl->mpParent != pWindowImpl->mpRealParent)
2411         {
2412             assert(pWindowImpl->mpBorderWindow == pWindowImpl->mpParent);
2413             assert(pWindowImpl->mpBorderWindow->ImplGetWindowImpl()->mpParent == pWindowImpl->mpRealParent);
2414             reorderWithinParent(*pWindowImpl->mpBorderWindow, nNewPosition);
2415             return;
2416         }
2417         rWindow.reorderWithinParent(nNewPosition);
2418     }
2419 
2420     void reorderWithinParent(std::vector<vcl::Window*>& rChilds, bool bIsButtonBox)
2421     {
2422         for (size_t i = 0; i < rChilds.size(); ++i)
2423         {
2424             reorderWithinParent(*rChilds[i], i);
2425 
2426             if (!bIsButtonBox)
2427                 continue;
2428 
2429             //The first member of the group for legacy code needs WB_GROUP set and the
2430             //others not
2431             WinBits nBits = rChilds[i]->GetStyle();
2432             nBits &= ~WB_GROUP;
2433             if (i == 0)
2434                 nBits |= WB_GROUP;
2435             rChilds[i]->SetStyle(nBits);
2436         }
2437     }
2438 
2439     sal_Int16 getRoleFromName(const OString& roleName)
2440     {
2441         using namespace com::sun::star::accessibility;
2442 
2443         static const std::unordered_map<OString, sal_Int16> aAtkRoleToAccessibleRole = {
2444             /* This is in atkobject.h's AtkRole order */
2445             { "invalid",               AccessibleRole::UNKNOWN },
2446             { "accelerator label",     AccessibleRole::UNKNOWN },
2447             { "alert",                 AccessibleRole::ALERT },
2448             { "animation",             AccessibleRole::UNKNOWN },
2449             { "arrow",                 AccessibleRole::UNKNOWN },
2450             { "calendar",              AccessibleRole::UNKNOWN },
2451             { "canvas",                AccessibleRole::CANVAS },
2452             { "check box",             AccessibleRole::CHECK_BOX },
2453             { "check menu item",       AccessibleRole::CHECK_MENU_ITEM },
2454             { "color chooser",         AccessibleRole::COLOR_CHOOSER },
2455             { "column header",         AccessibleRole::COLUMN_HEADER },
2456             { "combo box",             AccessibleRole::COMBO_BOX },
2457             { "date editor",           AccessibleRole::DATE_EDITOR },
2458             { "desktop icon",          AccessibleRole::DESKTOP_ICON },
2459             { "desktop frame",         AccessibleRole::DESKTOP_PANE }, // ?
2460             { "dial",                  AccessibleRole::UNKNOWN },
2461             { "dialog",                AccessibleRole::DIALOG },
2462             { "directory pane",        AccessibleRole::DIRECTORY_PANE },
2463             { "drawing area",          AccessibleRole::UNKNOWN },
2464             { "file chooser",          AccessibleRole::FILE_CHOOSER },
2465             { "filler",                AccessibleRole::FILLER },
2466             { "font chooser",          AccessibleRole::FONT_CHOOSER },
2467             { "frame",                 AccessibleRole::FRAME },
2468             { "glass pane",            AccessibleRole::GLASS_PANE },
2469             { "html container",        AccessibleRole::UNKNOWN },
2470             { "icon",                  AccessibleRole::ICON },
2471             { "image",                 AccessibleRole::GRAPHIC },
2472             { "internal frame",        AccessibleRole::INTERNAL_FRAME },
2473             { "label",                 AccessibleRole::LABEL },
2474             { "layered pane",          AccessibleRole::LAYERED_PANE },
2475             { "list",                  AccessibleRole::LIST },
2476             { "list item",             AccessibleRole::LIST_ITEM },
2477             { "menu",                  AccessibleRole::MENU },
2478             { "menu bar",              AccessibleRole::MENU_BAR },
2479             { "menu item",             AccessibleRole::MENU_ITEM },
2480             { "option pane",           AccessibleRole::OPTION_PANE },
2481             { "page tab",              AccessibleRole::PAGE_TAB },
2482             { "page tab list",         AccessibleRole::PAGE_TAB_LIST },
2483             { "panel",                 AccessibleRole::PANEL }, // or SHAPE or TEXT_FRAME ?
2484             { "password text",         AccessibleRole::PASSWORD_TEXT },
2485             { "popup menu",            AccessibleRole::POPUP_MENU },
2486             { "progress bar",          AccessibleRole::PROGRESS_BAR },
2487             { "push button",           AccessibleRole::PUSH_BUTTON }, // or BUTTON_DROPDOWN or BUTTON_MENU
2488             { "radio button",          AccessibleRole::RADIO_BUTTON },
2489             { "radio menu item",       AccessibleRole::RADIO_MENU_ITEM },
2490             { "root pane",             AccessibleRole::ROOT_PANE },
2491             { "row header",            AccessibleRole::ROW_HEADER },
2492             { "scroll bar",            AccessibleRole::SCROLL_BAR },
2493             { "scroll pane",           AccessibleRole::SCROLL_PANE },
2494             { "separator",             AccessibleRole::SEPARATOR },
2495             { "slider",                AccessibleRole::SLIDER },
2496             { "split pane",            AccessibleRole::SPLIT_PANE },
2497             { "spin button",           AccessibleRole::SPIN_BOX }, // ?
2498             { "statusbar",             AccessibleRole::STATUS_BAR },
2499             { "table",                 AccessibleRole::TABLE },
2500             { "table cell",            AccessibleRole::TABLE_CELL },
2501             { "table column header",   AccessibleRole::COLUMN_HEADER }, // approximate
2502             { "table row header",      AccessibleRole::ROW_HEADER }, // approximate
2503             { "tear off menu item",    AccessibleRole::UNKNOWN },
2504             { "terminal",              AccessibleRole::UNKNOWN },
2505             { "text",                  AccessibleRole::TEXT },
2506             { "toggle button",         AccessibleRole::TOGGLE_BUTTON },
2507             { "tool bar",              AccessibleRole::TOOL_BAR },
2508             { "tool tip",              AccessibleRole::TOOL_TIP },
2509             { "tree",                  AccessibleRole::TREE },
2510             { "tree table",            AccessibleRole::TREE_TABLE },
2511             { "unknown",               AccessibleRole::UNKNOWN },
2512             { "viewport",              AccessibleRole::VIEW_PORT },
2513             { "window",                AccessibleRole::WINDOW },
2514             { "header",                AccessibleRole::HEADER },
2515             { "footer",                AccessibleRole::FOOTER },
2516             { "paragraph",             AccessibleRole::PARAGRAPH },
2517             { "ruler",                 AccessibleRole::RULER },
2518             { "application",           AccessibleRole::UNKNOWN },
2519             { "autocomplete",          AccessibleRole::UNKNOWN },
2520             { "edit bar",              AccessibleRole::EDIT_BAR },
2521             { "embedded",              AccessibleRole::EMBEDDED_OBJECT },
2522             { "entry",                 AccessibleRole::UNKNOWN },
2523             { "chart",                 AccessibleRole::CHART },
2524             { "caption",               AccessibleRole::CAPTION },
2525             { "document frame",        AccessibleRole::DOCUMENT },
2526             { "heading",               AccessibleRole::HEADING },
2527             { "page",                  AccessibleRole::PAGE },
2528             { "section",               AccessibleRole::SECTION },
2529             { "redundant object",      AccessibleRole::UNKNOWN },
2530             { "form",                  AccessibleRole::FORM },
2531             { "link",                  AccessibleRole::HYPER_LINK },
2532             { "input method window",   AccessibleRole::UNKNOWN },
2533             { "table row",             AccessibleRole::UNKNOWN },
2534             { "tree item",             AccessibleRole::TREE_ITEM },
2535             { "document spreadsheet",  AccessibleRole::DOCUMENT_SPREADSHEET },
2536             { "document presentation", AccessibleRole::DOCUMENT_PRESENTATION },
2537             { "document text",         AccessibleRole::DOCUMENT_TEXT },
2538             { "document web",          AccessibleRole::DOCUMENT }, // approximate
2539             { "document email",        AccessibleRole::DOCUMENT }, // approximate
2540             { "comment",               AccessibleRole::COMMENT }, // or NOTE or END_NOTE or FOOTNOTE or SCROLL_PANE
2541             { "list box",              AccessibleRole::UNKNOWN },
2542             { "grouping",              AccessibleRole::GROUP_BOX },
2543             { "image map",             AccessibleRole::IMAGE_MAP },
2544             { "notification",          AccessibleRole::UNKNOWN },
2545             { "info bar",              AccessibleRole::UNKNOWN },
2546             { "level bar",             AccessibleRole::UNKNOWN },
2547             { "title bar",             AccessibleRole::UNKNOWN },
2548             { "block quote",           AccessibleRole::UNKNOWN },
2549             { "audio",                 AccessibleRole::UNKNOWN },
2550             { "video",                 AccessibleRole::UNKNOWN },
2551             { "definition",            AccessibleRole::UNKNOWN },
2552             { "article",               AccessibleRole::UNKNOWN },
2553             { "landmark",              AccessibleRole::UNKNOWN },
2554             { "log",                   AccessibleRole::UNKNOWN },
2555             { "marquee",               AccessibleRole::UNKNOWN },
2556             { "math",                  AccessibleRole::UNKNOWN },
2557             { "rating",                AccessibleRole::UNKNOWN },
2558             { "timer",                 AccessibleRole::UNKNOWN },
2559             { "description list",      AccessibleRole::UNKNOWN },
2560             { "description term",      AccessibleRole::UNKNOWN },
2561             { "description value",     AccessibleRole::UNKNOWN },
2562             { "static",                AccessibleRole::STATIC },
2563             { "math fraction",         AccessibleRole::UNKNOWN },
2564             { "math root",             AccessibleRole::UNKNOWN },
2565             { "subscript",             AccessibleRole::UNKNOWN },
2566             { "superscript",           AccessibleRole::UNKNOWN },
2567             { "footnote",              AccessibleRole::FOOTNOTE },
2568         };
2569 
2570         auto it = aAtkRoleToAccessibleRole.find(roleName);
2571         if (it == aAtkRoleToAccessibleRole.end())
2572             return AccessibleRole::UNKNOWN;
2573         return it->second;
2574     }
2575 }
2576 
2577 VclPtr<vcl::Window> VclBuilder::insertObject(vcl::Window *pParent, const OString &rClass,
2578     const OString &rID, stringmap &rProps, stringmap &rPango, stringmap &rAtk)
2579 {
2580     VclPtr<vcl::Window> pCurrentChild;
2581 
2582     if (m_pParent && !isConsideredGtkPseudo(m_pParent) && !m_sID.isEmpty() && rID == m_sID)
2583     {
2584         pCurrentChild = m_pParent;
2585 
2586         //toplevels default to resizable and apparently you can't change them
2587         //afterwards, so we need to wait until now before we can truly
2588         //initialize the dialog.
2589         if (pParent && pParent->IsSystemWindow())
2590         {
2591             SystemWindow *pSysWin = static_cast<SystemWindow*>(pCurrentChild.get());
2592             pSysWin->doDeferredInit(extractDeferredBits(rProps));
2593             m_bToplevelHasDeferredInit = false;
2594         }
2595         else if (pParent && pParent->IsDockingWindow())
2596         {
2597             DockingWindow *pDockWin = static_cast<DockingWindow*>(pCurrentChild.get());
2598             pDockWin->doDeferredInit(extractDeferredBits(rProps));
2599             m_bToplevelHasDeferredInit = false;
2600         }
2601 
2602         if (pCurrentChild->GetHelpId().isEmpty())
2603         {
2604             pCurrentChild->SetHelpId(m_sHelpRoot + m_sID);
2605             SAL_INFO("vcl.builder", "for toplevel dialog " << this << " " <<
2606                 rID << ", set helpid " << pCurrentChild->GetHelpId());
2607         }
2608         m_bToplevelParentFound = true;
2609     }
2610     else
2611     {
2612         //if we're being inserting under a toplevel dialog whose init is
2613         //deferred due to waiting to encounter it in this .ui, and it hasn't
2614         //been seen yet, then make unattached widgets parent-less toplevels
2615         if (pParent == m_pParent.get() && m_bToplevelHasDeferredInit)
2616             pParent = nullptr;
2617         pCurrentChild = makeObject(pParent, rClass, rID, rProps);
2618     }
2619 
2620     if (pCurrentChild)
2621     {
2622         pCurrentChild->set_id(OStringToOUString(rID, RTL_TEXTENCODING_UTF8));
2623         if (pCurrentChild == m_pParent.get() && m_bToplevelHasDeferredProperties)
2624             m_aDeferredProperties = rProps;
2625         else
2626             BuilderUtils::set_properties(pCurrentChild, rProps);
2627 
2628         for (auto const& elem : rPango)
2629         {
2630             const OString &rKey = elem.first;
2631             const OUString &rValue = elem.second;
2632             pCurrentChild->set_font_attribute(rKey, rValue);
2633         }
2634 
2635         m_pParserState->m_aAtkInfo[pCurrentChild] = rAtk;
2636     }
2637 
2638     rProps.clear();
2639     rPango.clear();
2640     rAtk.clear();
2641 
2642     if (!pCurrentChild)
2643     {
2644         bool bToolbarParent = (pParent && pParent->GetType() == WindowType::TOOLBOX);
2645         pCurrentChild = (m_aChildren.empty() || bToolbarParent) ? pParent : m_aChildren.back().m_pWindow.get();
2646     }
2647     return pCurrentChild;
2648 }
2649 
2650 void VclBuilder::handleTabChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
2651 {
2652     std::vector<OString> sIDs;
2653 
2654     int nLevel = 1;
2655     stringmap aProperties;
2656     std::vector<vcl::EnumContext::Context> context;
2657 
2658     while(true)
2659     {
2660         xmlreader::Span name;
2661         int nsId;
2662 
2663         xmlreader::XmlReader::Result res = reader.nextItem(
2664             xmlreader::XmlReader::Text::NONE, &name, &nsId);
2665 
2666         if (res == xmlreader::XmlReader::Result::Begin)
2667         {
2668             ++nLevel;
2669             if (name == "object")
2670             {
2671                 while (reader.nextAttribute(&nsId, &name))
2672                 {
2673                     if (name == "id")
2674                     {
2675                         name = reader.getAttributeValue(false);
2676                         OString sID(name.begin, name.length);
2677                         sal_Int32 nDelim = sID.indexOf(':');
2678                         if (nDelim != -1)
2679                         {
2680                             OString sPattern = sID.copy(nDelim+1);
2681                             aProperties[OString("customproperty")] = OUString::fromUtf8(sPattern);
2682                             sID = sID.copy(0, nDelim);
2683                         }
2684                         sIDs.push_back(sID);
2685                     }
2686                 }
2687             }
2688             else if (name == "style")
2689             {
2690                 int nPriority = 0;
2691                 context = handleStyle(reader, nPriority);
2692                 --nLevel;
2693             }
2694             else if (name == "property")
2695                 collectProperty(reader, aProperties);
2696         }
2697 
2698         if (res == xmlreader::XmlReader::Result::End)
2699             --nLevel;
2700 
2701         if (!nLevel)
2702             break;
2703 
2704         if (res == xmlreader::XmlReader::Result::Done)
2705             break;
2706     }
2707 
2708     if (!pParent)
2709         return;
2710 
2711     TabControl *pTabControl = pParent->GetType() == WindowType::TABCONTROL ?
2712         static_cast<TabControl*>(pParent) : nullptr;
2713     VerticalTabControl *pVerticalTabControl = pParent->GetType() == WindowType::VERTICALTABCONTROL ?
2714         static_cast<VerticalTabControl*>(pParent) : nullptr;
2715     assert(pTabControl || pVerticalTabControl);
2716     VclBuilder::stringmap::iterator aFind = aProperties.find(OString("label"));
2717     if (aFind != aProperties.end())
2718     {
2719         if (pTabControl)
2720         {
2721             sal_uInt16 nPageId = pTabControl->GetCurPageId();
2722             pTabControl->SetPageText(nPageId, aFind->second);
2723             pTabControl->SetPageName(nPageId, sIDs.back());
2724             if (!context.empty())
2725             {
2726                 TabPage* pPage = pTabControl->GetTabPage(nPageId);
2727                 pPage->SetContext(context);
2728             }
2729         }
2730         else
2731         {
2732             OUString sLabel(aFind->second);
2733             OUString sIconName(extractIconName(aProperties));
2734             OUString sTooltip(extractTooltipText(aProperties));
2735             pVerticalTabControl->InsertPage(sIDs.front(), sLabel, FixedImage::loadThemeImage(sIconName), sTooltip,
2736                                             pVerticalTabControl->GetPageParent()->GetWindow(GetWindowType::LastChild));
2737         }
2738     }
2739     else
2740     {
2741         if (pTabControl)
2742             pTabControl->RemovePage(pTabControl->GetCurPageId());
2743     }
2744 }
2745 
2746 //so that tabbing between controls goes in a visually sensible sequence
2747 //we sort these into a best-tab-order sequence
2748 bool VclBuilder::sortIntoBestTabTraversalOrder::operator()(const vcl::Window *pA, const vcl::Window *pB) const
2749 {
2750     //sort child order within parent list by grid position
2751     sal_Int32 nTopA = pA->get_grid_top_attach();
2752     sal_Int32 nTopB = pB->get_grid_top_attach();
2753     if (nTopA < nTopB)
2754         return true;
2755     if (nTopA > nTopB)
2756         return false;
2757     sal_Int32 nLeftA = pA->get_grid_left_attach();
2758     sal_Int32 nLeftB = pB->get_grid_left_attach();
2759     if (nLeftA < nLeftB)
2760         return true;
2761     if (nLeftA > nLeftB)
2762         return false;
2763     //sort into two groups of pack start and pack end
2764     VclPackType ePackA = pA->get_pack_type();
2765     VclPackType ePackB = pB->get_pack_type();
2766     if (ePackA < ePackB)
2767         return true;
2768     if (ePackA > ePackB)
2769         return false;
2770     bool bVerticalContainer = m_pBuilder->get_window_packing_data(pA->GetParent()).m_bVerticalOrient;
2771     bool bPackA = pA->get_secondary();
2772     bool bPackB = pB->get_secondary();
2773     if (!bVerticalContainer)
2774     {
2775         //for horizontal boxes group secondaries before primaries
2776         if (bPackA > bPackB)
2777             return true;
2778         if (bPackA < bPackB)
2779             return false;
2780     }
2781     else
2782     {
2783         //for vertical boxes group secondaries after primaries
2784         if (bPackA < bPackB)
2785             return true;
2786         if (bPackA > bPackB)
2787             return false;
2788     }
2789     //honour relative box positions with pack group, (numerical order is reversed
2790     //for VclPackType::End, they are packed from the end back, but here we need
2791     //them in visual layout order so that tabbing works as expected)
2792     sal_Int32 nPackA = m_pBuilder->get_window_packing_data(pA).m_nPosition;
2793     sal_Int32 nPackB = m_pBuilder->get_window_packing_data(pB).m_nPosition;
2794     if (nPackA < nPackB)
2795         return ePackA == VclPackType::Start;
2796     if (nPackA > nPackB)
2797         return ePackA != VclPackType::Start;
2798     //sort labels of Frames before body
2799     if (pA->GetParent() == pB->GetParent())
2800     {
2801         const VclFrame *pFrameParent = dynamic_cast<const VclFrame*>(pA->GetParent());
2802         if (pFrameParent)
2803         {
2804             const vcl::Window *pLabel = pFrameParent->get_label_widget();
2805             int nFramePosA = (pA == pLabel) ? 0 : 1;
2806             int nFramePosB = (pB == pLabel) ? 0 : 1;
2807             return nFramePosA < nFramePosB;
2808         }
2809     }
2810     return false;
2811 }
2812 
2813 void VclBuilder::handleChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
2814 {
2815     vcl::Window *pCurrentChild = nullptr;
2816 
2817     xmlreader::Span name;
2818     int nsId;
2819     OString sType, sInternalChild;
2820 
2821     while (reader.nextAttribute(&nsId, &name))
2822     {
2823         if (name == "type")
2824         {
2825             name = reader.getAttributeValue(false);
2826             sType = OString(name.begin, name.length);
2827         }
2828         else if (name == "internal-child")
2829         {
2830             name = reader.getAttributeValue(false);
2831             sInternalChild = OString(name.begin, name.length);
2832         }
2833     }
2834 
2835     if (sType == "tab")
2836     {
2837         handleTabChild(pParent, reader);
2838         return;
2839     }
2840 
2841     int nLevel = 1;
2842     while(true)
2843     {
2844         xmlreader::XmlReader::Result res = reader.nextItem(
2845             xmlreader::XmlReader::Text::NONE, &name, &nsId);
2846 
2847         if (res == xmlreader::XmlReader::Result::Begin)
2848         {
2849             if (name == "object" || name == "placeholder")
2850             {
2851                 pCurrentChild = handleObject(pParent, reader).get();
2852 
2853                 bool bObjectInserted = pCurrentChild && pParent != pCurrentChild;
2854 
2855                 if (bObjectInserted)
2856                 {
2857                     //Internal-children default in glade to not having their visible bits set
2858                     //even though they are visible (generally anyway)
2859                     if (!sInternalChild.isEmpty())
2860                         pCurrentChild->Show();
2861 
2862                     //Select the first page if it's a notebook
2863                     if (pCurrentChild->GetType() == WindowType::TABCONTROL)
2864                     {
2865                         TabControl *pTabControl = static_cast<TabControl*>(pCurrentChild);
2866                         pTabControl->SetCurPageId(pTabControl->GetPageId(0));
2867 
2868                         //To-Do add reorder capability to the TabControl
2869                     }
2870                     else
2871                     {
2872                         // We want to sort labels before contents of frames
2873                         // for keyboard traversal, especially if there
2874                         // are multiple widgets using the same mnemonic
2875                         if (sType == "label")
2876                         {
2877                             if (VclFrame *pFrameParent = dynamic_cast<VclFrame*>(pParent))
2878                                 pFrameParent->designate_label(pCurrentChild);
2879                         }
2880                         if (sInternalChild.startsWith("vbox") || sInternalChild.startsWith("messagedialog-vbox"))
2881                         {
2882                             if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pParent))
2883                                 pBoxParent->set_content_area(static_cast<VclBox*>(pCurrentChild)); // FIXME-VCLPTR
2884                         }
2885                         else if (sInternalChild.startsWith("action_area") || sInternalChild.startsWith("messagedialog-action_area"))
2886                         {
2887                             vcl::Window *pContentArea = pCurrentChild->GetParent();
2888                             if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pContentArea ? pContentArea->GetParent() : nullptr))
2889                             {
2890                                 pBoxParent->set_action_area(static_cast<VclButtonBox*>(pCurrentChild)); // FIXME-VCLPTR
2891                             }
2892                         }
2893 
2894                         bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pCurrentChild) != nullptr;
2895 
2896                         //To-Do make reorder a virtual in Window, move this foo
2897                         //there and see above
2898                         std::vector<vcl::Window*> aChilds;
2899                         for (vcl::Window* pChild = pCurrentChild->GetWindow(GetWindowType::FirstChild); pChild;
2900                             pChild = pChild->GetWindow(GetWindowType::Next))
2901                         {
2902                             if (bIsButtonBox)
2903                             {
2904                                 if (PushButton* pPushButton = dynamic_cast<PushButton*>(pChild))
2905                                     pPushButton->setAction(true);
2906                             }
2907 
2908                             aChilds.push_back(pChild);
2909                         }
2910 
2911                         //sort child order within parent so that tabbing
2912                         //between controls goes in a visually sensible sequence
2913                         std::stable_sort(aChilds.begin(), aChilds.end(), sortIntoBestTabTraversalOrder(this));
2914                         BuilderUtils::reorderWithinParent(aChilds, bIsButtonBox);
2915                     }
2916                 }
2917             }
2918             else if (name == "packing")
2919             {
2920                 handlePacking(pCurrentChild, pParent, reader);
2921             }
2922             else if (name == "interface")
2923             {
2924                 while (reader.nextAttribute(&nsId, &name))
2925                 {
2926                     if (name == "domain")
2927                     {
2928                         name = reader.getAttributeValue(false);
2929                         sType = OString(name.begin, name.length);
2930                         m_pParserState->m_aResLocale = Translate::Create(sType.getStr());
2931                     }
2932                 }
2933                 ++nLevel;
2934             }
2935             else
2936                 ++nLevel;
2937         }
2938 
2939         if (res == xmlreader::XmlReader::Result::End)
2940             --nLevel;
2941 
2942         if (!nLevel)
2943             break;
2944 
2945         if (res == xmlreader::XmlReader::Result::Done)
2946             break;
2947     }
2948 }
2949 
2950 void VclBuilder::collectPangoAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
2951 {
2952     xmlreader::Span span;
2953     int nsId;
2954 
2955     OString sProperty;
2956     OString sValue;
2957 
2958     while (reader.nextAttribute(&nsId, &span))
2959     {
2960         if (span == "name")
2961         {
2962             span = reader.getAttributeValue(false);
2963             sProperty = OString(span.begin, span.length);
2964         }
2965         else if (span == "value")
2966         {
2967             span = reader.getAttributeValue(false);
2968             sValue = OString(span.begin, span.length);
2969         }
2970     }
2971 
2972     if (!sProperty.isEmpty())
2973         rMap[sProperty] = OUString::fromUtf8(sValue);
2974 }
2975 
2976 void VclBuilder::collectAtkRelationAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
2977 {
2978     xmlreader::Span span;
2979     int nsId;
2980 
2981     OString sProperty;
2982     OString sValue;
2983 
2984     while (reader.nextAttribute(&nsId, &span))
2985     {
2986         if (span == "type")
2987         {
2988             span = reader.getAttributeValue(false);
2989             sProperty = OString(span.begin, span.length);
2990         }
2991         else if (span == "target")
2992         {
2993             span = reader.getAttributeValue(false);
2994             sValue = OString(span.begin, span.length);
2995             sal_Int32 nDelim = sValue.indexOf(':');
2996             if (nDelim != -1)
2997                 sValue = sValue.copy(0, nDelim);
2998         }
2999     }
3000 
3001     if (!sProperty.isEmpty())
3002         rMap[sProperty] = OUString::fromUtf8(sValue);
3003 }
3004 
3005 void VclBuilder::collectAtkRoleAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3006 {
3007     xmlreader::Span span;
3008     int nsId;
3009 
3010     OString sProperty;
3011 
3012     while (reader.nextAttribute(&nsId, &span))
3013     {
3014         if (span == "type")
3015         {
3016             span = reader.getAttributeValue(false);
3017             sProperty = OString(span.begin, span.length);
3018         }
3019     }
3020 
3021     if (!sProperty.isEmpty())
3022         rMap["role"] = OUString::fromUtf8(sProperty);
3023 }
3024 
3025 void VclBuilder::handleRow(xmlreader::XmlReader &reader, const OString &rID)
3026 {
3027     int nLevel = 1;
3028 
3029     ListStore::row aRow;
3030 
3031     while(true)
3032     {
3033         xmlreader::Span name;
3034         int nsId;
3035 
3036         xmlreader::XmlReader::Result res = reader.nextItem(
3037             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3038 
3039         if (res == xmlreader::XmlReader::Result::Done)
3040             break;
3041 
3042         if (res == xmlreader::XmlReader::Result::Begin)
3043         {
3044             ++nLevel;
3045             if (name == "col")
3046             {
3047                 bool bTranslated = false;
3048                 sal_uInt32 nId = 0;
3049                 OString sContext;
3050 
3051                 while (reader.nextAttribute(&nsId, &name))
3052                 {
3053                     if (name == "id")
3054                     {
3055                         name = reader.getAttributeValue(false);
3056                         nId = OString(name.begin, name.length).toInt32();
3057                     }
3058                     else if (nId == 0 && name == "translatable" && reader.getAttributeValue(false) == "yes")
3059                     {
3060                         bTranslated = true;
3061                     }
3062                     else if (name == "context")
3063                     {
3064                         name = reader.getAttributeValue(false);
3065                         sContext = OString(name.begin, name.length);
3066                     }
3067                 }
3068 
3069                 (void)reader.nextItem(
3070                     xmlreader::XmlReader::Text::Raw, &name, &nsId);
3071 
3072                 OString sValue(name.begin, name.length);
3073                 OUString sFinalValue;
3074                 if (bTranslated)
3075                 {
3076                     if (!sContext.isEmpty())
3077                         sValue = sContext + "\004" + sValue;
3078                     sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3079                 }
3080                 else
3081                     sFinalValue = OUString::fromUtf8(sValue);
3082 
3083 
3084                 if (aRow.size() < nId+1)
3085                     aRow.resize(nId+1);
3086                 aRow[nId] = sFinalValue;
3087             }
3088         }
3089 
3090         if (res == xmlreader::XmlReader::Result::End)
3091         {
3092             --nLevel;
3093         }
3094 
3095         if (!nLevel)
3096             break;
3097     }
3098 
3099     m_pParserState->m_aModels[rID].m_aEntries.push_back(aRow);
3100 }
3101 
3102 void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OString &rID, const OString &rClass)
3103 {
3104     int nLevel = 1;
3105 
3106     while(true)
3107     {
3108         xmlreader::Span name;
3109         int nsId;
3110 
3111         xmlreader::XmlReader::Result res = reader.nextItem(
3112             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3113 
3114         if (res == xmlreader::XmlReader::Result::Done)
3115             break;
3116 
3117         if (res == xmlreader::XmlReader::Result::Begin)
3118         {
3119             if (name == "row")
3120             {
3121                 bool bNotTreeStore = rClass != "GtkTreeStore";
3122                 if (bNotTreeStore)
3123                     handleRow(reader, rID);
3124                 assert(bNotTreeStore && "gtk, as the time of writing, doesn't support data in GtkTreeStore serialization");
3125             }
3126             else
3127                 ++nLevel;
3128         }
3129 
3130         if (res == xmlreader::XmlReader::Result::End)
3131         {
3132             --nLevel;
3133         }
3134 
3135         if (!nLevel)
3136             break;
3137     }
3138 }
3139 
3140 void VclBuilder::handleAtkObject(xmlreader::XmlReader &reader, vcl::Window *pWindow)
3141 {
3142     assert(pWindow);
3143 
3144     int nLevel = 1;
3145 
3146     stringmap aProperties;
3147 
3148     while(true)
3149     {
3150         xmlreader::Span name;
3151         int nsId;
3152 
3153         xmlreader::XmlReader::Result res = reader.nextItem(
3154             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3155 
3156         if (res == xmlreader::XmlReader::Result::Done)
3157             break;
3158 
3159         if (res == xmlreader::XmlReader::Result::Begin)
3160         {
3161             ++nLevel;
3162             if (name == "property")
3163                 collectProperty(reader, aProperties);
3164         }
3165 
3166         if (res == xmlreader::XmlReader::Result::End)
3167         {
3168             --nLevel;
3169         }
3170 
3171         if (!nLevel)
3172             break;
3173     }
3174 
3175     for (auto const& prop : aProperties)
3176     {
3177         const OString &rKey = prop.first;
3178         const OUString &rValue = prop.second;
3179 
3180         if (pWindow && rKey.match("AtkObject::"))
3181             pWindow->set_property(rKey.copy(RTL_CONSTASCII_LENGTH("AtkObject::")), rValue);
3182         else
3183             SAL_WARN("vcl.builder", "unhandled atk prop: " << rKey);
3184     }
3185 }
3186 
3187 std::vector<ComboBoxTextItem> VclBuilder::handleItems(xmlreader::XmlReader &reader) const
3188 {
3189     int nLevel = 1;
3190 
3191     std::vector<ComboBoxTextItem> aItems;
3192 
3193     while(true)
3194     {
3195         xmlreader::Span name;
3196         int nsId;
3197 
3198         xmlreader::XmlReader::Result res = reader.nextItem(
3199             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3200 
3201         if (res == xmlreader::XmlReader::Result::Done)
3202             break;
3203 
3204         if (res == xmlreader::XmlReader::Result::Begin)
3205         {
3206             ++nLevel;
3207             if (name == "item")
3208             {
3209                 bool bTranslated = false;
3210                 OString sContext, sId;
3211 
3212                 while (reader.nextAttribute(&nsId, &name))
3213                 {
3214                     if (name == "translatable" && reader.getAttributeValue(false) == "yes")
3215                     {
3216                         bTranslated = true;
3217                     }
3218                     else if (name == "context")
3219                     {
3220                         name = reader.getAttributeValue(false);
3221                         sContext = OString(name.begin, name.length);
3222                     }
3223                     else if (name == "id")
3224                     {
3225                         name = reader.getAttributeValue(false);
3226                         sId = OString(name.begin, name.length);
3227                     }
3228                 }
3229 
3230                 (void)reader.nextItem(
3231                     xmlreader::XmlReader::Text::Raw, &name, &nsId);
3232 
3233                 OString sValue(name.begin, name.length);
3234                 OUString sFinalValue;
3235                 if (bTranslated)
3236                 {
3237                     if (!sContext.isEmpty())
3238                         sValue = sContext + "\004" + sValue;
3239                     sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3240                 }
3241                 else
3242                     sFinalValue = OUString::fromUtf8(sValue);
3243 
3244                 if (m_pStringReplace)
3245                     sFinalValue = (*m_pStringReplace)(sFinalValue);
3246 
3247                 aItems.emplace_back(sFinalValue, sId);
3248             }
3249         }
3250 
3251         if (res == xmlreader::XmlReader::Result::End)
3252         {
3253             --nLevel;
3254         }
3255 
3256         if (!nLevel)
3257             break;
3258     }
3259 
3260     return aItems;
3261 }
3262 
3263 VclPtr<Menu> VclBuilder::handleMenu(xmlreader::XmlReader &reader, const OString &rID, bool bMenuBar)
3264 {
3265     VclPtr<Menu> pCurrentMenu;
3266     if (bMenuBar)
3267         pCurrentMenu = VclPtr<MenuBar>::Create();
3268     else
3269         pCurrentMenu = VclPtr<PopupMenu>::Create();
3270 
3271     int nLevel = 1;
3272 
3273     stringmap aProperties;
3274 
3275     while(true)
3276     {
3277         xmlreader::Span name;
3278         int nsId;
3279 
3280         xmlreader::XmlReader::Result res = reader.nextItem(
3281             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3282 
3283         if (res == xmlreader::XmlReader::Result::Done)
3284             break;
3285 
3286         if (res == xmlreader::XmlReader::Result::Begin)
3287         {
3288             if (name == "child")
3289             {
3290                 handleMenuChild(pCurrentMenu, reader);
3291             }
3292             else
3293             {
3294                 ++nLevel;
3295                 if (name == "property")
3296                     collectProperty(reader, aProperties);
3297             }
3298         }
3299 
3300         if (res == xmlreader::XmlReader::Result::End)
3301         {
3302             --nLevel;
3303         }
3304 
3305         if (!nLevel)
3306             break;
3307     }
3308 
3309     m_aMenus.emplace_back(rID, pCurrentMenu);
3310 
3311     return pCurrentMenu;
3312 }
3313 
3314 void VclBuilder::handleMenuChild(Menu *pParent, xmlreader::XmlReader &reader)
3315 {
3316     xmlreader::Span name;
3317     int nsId;
3318 
3319     int nLevel = 1;
3320     while(true)
3321     {
3322         xmlreader::XmlReader::Result res = reader.nextItem(
3323             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3324 
3325         if (res == xmlreader::XmlReader::Result::Begin)
3326         {
3327             if (name == "object" || name == "placeholder")
3328             {
3329                 handleMenuObject(pParent, reader);
3330             }
3331             else
3332                 ++nLevel;
3333         }
3334 
3335         if (res == xmlreader::XmlReader::Result::End)
3336             --nLevel;
3337 
3338         if (!nLevel)
3339             break;
3340 
3341         if (res == xmlreader::XmlReader::Result::Done)
3342             break;
3343     }
3344 }
3345 
3346 void VclBuilder::handleMenuObject(Menu *pParent, xmlreader::XmlReader &reader)
3347 {
3348     OString sClass;
3349     OString sID;
3350     OUString sCustomProperty;
3351     PopupMenu *pSubMenu = nullptr;
3352 
3353     xmlreader::Span name;
3354     int nsId;
3355 
3356     while (reader.nextAttribute(&nsId, &name))
3357     {
3358         if (name == "class")
3359         {
3360             name = reader.getAttributeValue(false);
3361             sClass = OString(name.begin, name.length);
3362         }
3363         else if (name == "id")
3364         {
3365             name = reader.getAttributeValue(false);
3366             sID = OString(name.begin, name.length);
3367             sal_Int32 nDelim = sID.indexOf(':');
3368             if (nDelim != -1)
3369             {
3370                 sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3371                 sID = sID.copy(0, nDelim);
3372             }
3373         }
3374     }
3375 
3376     int nLevel = 1;
3377 
3378     stringmap aProperties;
3379     accelmap aAccelerators;
3380 
3381     if (!sCustomProperty.isEmpty())
3382         aProperties[OString("customproperty")] = sCustomProperty;
3383 
3384     while(true)
3385     {
3386         xmlreader::XmlReader::Result res = reader.nextItem(
3387             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3388 
3389         if (res == xmlreader::XmlReader::Result::Done)
3390             break;
3391 
3392         if (res == xmlreader::XmlReader::Result::Begin)
3393         {
3394             if (name == "child")
3395             {
3396                 size_t nChildMenuIdx = m_aMenus.size();
3397                 handleChild(nullptr, reader);
3398                 assert(m_aMenus.size() > nChildMenuIdx && "menu not inserted");
3399                 pSubMenu = dynamic_cast<PopupMenu*>(m_aMenus[nChildMenuIdx].m_pMenu.get());
3400             }
3401             else
3402             {
3403                 ++nLevel;
3404                 if (name == "property")
3405                     collectProperty(reader, aProperties);
3406                 else if (name == "accelerator")
3407                     collectAccelerator(reader, aAccelerators);
3408             }
3409         }
3410 
3411         if (res == xmlreader::XmlReader::Result::End)
3412         {
3413             --nLevel;
3414         }
3415 
3416         if (!nLevel)
3417             break;
3418     }
3419 
3420     insertMenuObject(pParent, pSubMenu, sClass, sID, aProperties, aAccelerators);
3421 }
3422 
3423 void VclBuilder::handleSizeGroup(xmlreader::XmlReader &reader)
3424 {
3425     m_pParserState->m_aSizeGroups.emplace_back();
3426     SizeGroup &rSizeGroup = m_pParserState->m_aSizeGroups.back();
3427 
3428     int nLevel = 1;
3429 
3430     while(true)
3431     {
3432         xmlreader::Span name;
3433         int nsId;
3434 
3435         xmlreader::XmlReader::Result res = reader.nextItem(
3436             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3437 
3438         if (res == xmlreader::XmlReader::Result::Done)
3439             break;
3440 
3441         if (res == xmlreader::XmlReader::Result::Begin)
3442         {
3443             ++nLevel;
3444             if (name == "widget")
3445             {
3446                 while (reader.nextAttribute(&nsId, &name))
3447                 {
3448                     if (name == "name")
3449                     {
3450                         name = reader.getAttributeValue(false);
3451                         OString sWidget(name.begin, name.length);
3452                         sal_Int32 nDelim = sWidget.indexOf(':');
3453                         if (nDelim != -1)
3454                             sWidget = sWidget.copy(0, nDelim);
3455                         rSizeGroup.m_aWidgets.push_back(sWidget);
3456                     }
3457                 }
3458             }
3459             else
3460             {
3461                 if (name == "property")
3462                     collectProperty(reader, rSizeGroup.m_aProperties);
3463             }
3464         }
3465 
3466         if (res == xmlreader::XmlReader::Result::End)
3467         {
3468             --nLevel;
3469         }
3470 
3471         if (!nLevel)
3472             break;
3473     }
3474 }
3475 
3476 namespace
3477 {
3478     vcl::KeyCode makeKeyCode(const std::pair<OString,OString> &rKey)
3479     {
3480         bool bShift = rKey.second.indexOf("GDK_SHIFT_MASK") != -1;
3481         bool bMod1 = rKey.second.indexOf("GDK_CONTROL_MASK") != -1;
3482         bool bMod2 = rKey.second.indexOf("GDK_MOD1_MASK") != -1;
3483         bool bMod3 = rKey.second.indexOf("GDK_MOD2_MASK") != -1;
3484 
3485         if (rKey.first == "Insert")
3486             return vcl::KeyCode(KEY_INSERT, bShift, bMod1, bMod2, bMod3);
3487         else if (rKey.first == "Delete")
3488             return vcl::KeyCode(KEY_DELETE, bShift, bMod1, bMod2, bMod3);
3489         else if (rKey.first == "Return")
3490             return vcl::KeyCode(KEY_RETURN, bShift, bMod1, bMod2, bMod3);
3491         else if (rKey.first == "Up")
3492             return vcl::KeyCode(KEY_UP, bShift, bMod1, bMod2, bMod3);
3493         else if (rKey.first == "Down")
3494             return vcl::KeyCode(KEY_DOWN, bShift, bMod1, bMod2, bMod3);
3495         else if (rKey.first == "Left")
3496             return vcl::KeyCode(KEY_LEFT, bShift, bMod1, bMod2, bMod3);
3497         else if (rKey.first == "Right")
3498             return vcl::KeyCode(KEY_RIGHT, bShift, bMod1, bMod2, bMod3);
3499         else if (rKey.first == "asterisk")
3500             return vcl::KeyCode(KEY_MULTIPLY, bShift, bMod1, bMod2, bMod3);
3501 
3502         assert (rKey.first.getLength() == 1);
3503         char cChar = rKey.first.toChar();
3504 
3505         if (cChar >= 'a' && cChar <= 'z')
3506             return vcl::KeyCode(KEY_A + (cChar - 'a'), bShift, bMod1, bMod2, bMod3);
3507         else if (cChar >= 'A' && cChar <= 'Z')
3508             return vcl::KeyCode(KEY_A + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3509         else if (cChar >= '0' && cChar <= '9')
3510             return vcl::KeyCode(KEY_0 + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3511 
3512         return vcl::KeyCode(cChar, bShift, bMod1, bMod2, bMod3);
3513     }
3514 }
3515 
3516 void VclBuilder::insertMenuObject(Menu *pParent, PopupMenu *pSubMenu, const OString &rClass, const OString &rID,
3517     stringmap &rProps, accelmap &rAccels)
3518 {
3519     sal_uInt16 nOldCount = pParent->GetItemCount();
3520     sal_uInt16 nNewId = ++m_pParserState->m_nLastMenuItemId;
3521 
3522     if(rClass == "NotebookBarAddonsMenuMergePoint")
3523     {
3524         NotebookBarAddonsMerger::MergeNotebookBarMenuAddons(pParent, nNewId, rID, *m_pNotebookBarAddonsItem);
3525         m_pParserState->m_nLastMenuItemId = pParent->GetItemCount();
3526     }
3527     else if (rClass == "GtkMenuItem")
3528     {
3529         OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3530         OUString aCommand(extractActionName(rProps));
3531         pParent->InsertItem(nNewId, sLabel, MenuItemBits::NONE , rID);
3532         pParent->SetItemCommand(nNewId, aCommand);
3533         if (pSubMenu)
3534             pParent->SetPopupMenu(nNewId, pSubMenu);
3535     }
3536     else if (rClass == "GtkCheckMenuItem")
3537     {
3538         OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3539         OUString aCommand(extractActionName(rProps));
3540         pParent->InsertItem(nNewId, sLabel, MenuItemBits::CHECKABLE, rID);
3541         pParent->SetItemCommand(nNewId, aCommand);
3542     }
3543     else if (rClass == "GtkRadioMenuItem")
3544     {
3545         OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3546         OUString aCommand(extractActionName(rProps));
3547         pParent->InsertItem(nNewId, sLabel, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, rID);
3548         pParent->SetItemCommand(nNewId, aCommand);
3549     }
3550     else if (rClass == "GtkSeparatorMenuItem")
3551     {
3552         pParent->InsertSeparator(rID);
3553     }
3554 
3555     SAL_WARN_IF(nOldCount == pParent->GetItemCount(), "vcl.builder", "probably need to implement " << rClass);
3556 
3557     if (nOldCount != pParent->GetItemCount())
3558     {
3559         pParent->SetHelpId(nNewId, m_sHelpRoot + rID);
3560         if (!extractVisible(rProps))
3561             pParent->HideItem(nNewId);
3562 
3563         for (auto const& prop : rProps)
3564         {
3565             const OString &rKey = prop.first;
3566             const OUString &rValue = prop.second;
3567 
3568             if (rKey == "tooltip-markup")
3569                 pParent->SetTipHelpText(nNewId, rValue);
3570             else if (rKey == "tooltip-text")
3571                 pParent->SetTipHelpText(nNewId, rValue);
3572             else
3573                 SAL_INFO("vcl.builder", "unhandled property: " << rKey);
3574         }
3575 
3576         for (auto const& accel : rAccels)
3577         {
3578             const OString &rSignal = accel.first;
3579             const auto &rValue = accel.second;
3580 
3581             if (rSignal == "activate")
3582                 pParent->SetAccelKey(nNewId, makeKeyCode(rValue));
3583             else
3584                 SAL_INFO("vcl.builder", "unhandled accelerator for: " << rSignal);
3585         }
3586     }
3587 
3588     rProps.clear();
3589 }
3590 
3591 /// Insert items to a ComboBox or a ListBox.
3592 /// They have no common ancestor that would have 'InsertEntry()', so use a template.
3593 template<typename T> static bool insertItems(vcl::Window *pWindow, VclBuilder::stringmap &rMap,
3594                                              std::vector<std::unique_ptr<OUString>>& rUserData,
3595                                              const std::vector<ComboBoxTextItem> &rItems)
3596 {
3597     T *pContainer = dynamic_cast<T*>(pWindow);
3598     if (!pContainer)
3599         return false;
3600 
3601     sal_uInt16 nActiveId = extractActive(rMap);
3602     for (auto const& item : rItems)
3603     {
3604         sal_Int32 nPos = pContainer->InsertEntry(item.m_sItem);
3605         if (!item.m_sId.isEmpty())
3606         {
3607             rUserData.emplace_back(std::make_unique<OUString>(OUString::fromUtf8(item.m_sId)));
3608             pContainer->SetEntryData(nPos, rUserData.back().get());
3609         }
3610     }
3611     if (nActiveId < rItems.size())
3612         pContainer->SelectEntryPos(nActiveId);
3613 
3614     return true;
3615 }
3616 
3617 VclPtr<vcl::Window> VclBuilder::handleObject(vcl::Window *pParent, xmlreader::XmlReader &reader)
3618 {
3619     OString sClass;
3620     OString sID;
3621     OUString sCustomProperty;
3622 
3623     xmlreader::Span name;
3624     int nsId;
3625 
3626     while (reader.nextAttribute(&nsId, &name))
3627     {
3628         if (name == "class")
3629         {
3630             name = reader.getAttributeValue(false);
3631             sClass = OString(name.begin, name.length);
3632         }
3633         else if (name == "id")
3634         {
3635             name = reader.getAttributeValue(false);
3636             sID = OString(name.begin, name.length);
3637             if (m_bLegacy)
3638             {
3639                 sal_Int32 nDelim = sID.indexOf(':');
3640                 if (nDelim != -1)
3641                 {
3642                     sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3643                     sID = sID.copy(0, nDelim);
3644                 }
3645             }
3646         }
3647     }
3648 
3649     if (sClass == "GtkListStore" || sClass == "GtkTreeStore")
3650     {
3651         handleListStore(reader, sID, sClass);
3652         return nullptr;
3653     }
3654     else if (sClass == "GtkMenu")
3655     {
3656         handleMenu(reader, sID, false);
3657         return nullptr;
3658     }
3659     else if (sClass == "GtkMenuBar")
3660     {
3661         VclPtr<Menu> xMenu = handleMenu(reader, sID, true);
3662         if (SystemWindow* pTopLevel = pParent ? pParent->GetSystemWindow() : nullptr)
3663             pTopLevel->SetMenuBar(dynamic_cast<MenuBar*>(xMenu.get()));
3664         return nullptr;
3665     }
3666     else if (sClass == "GtkSizeGroup")
3667     {
3668         handleSizeGroup(reader);
3669         return nullptr;
3670     }
3671     else if (sClass == "AtkObject")
3672     {
3673         handleAtkObject(reader, pParent);
3674         return nullptr;
3675     }
3676 
3677     int nLevel = 1;
3678 
3679     stringmap aProperties, aPangoAttributes;
3680     stringmap aAtkAttributes;
3681     std::vector<ComboBoxTextItem> aItems;
3682 
3683     if (!sCustomProperty.isEmpty())
3684         aProperties[OString("customproperty")] = sCustomProperty;
3685 
3686     VclPtr<vcl::Window> pCurrentChild;
3687     while(true)
3688     {
3689         xmlreader::XmlReader::Result res = reader.nextItem(
3690             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3691 
3692         if (res == xmlreader::XmlReader::Result::Done)
3693             break;
3694 
3695         if (res == xmlreader::XmlReader::Result::Begin)
3696         {
3697             if (name == "child")
3698             {
3699                 if (!pCurrentChild)
3700                 {
3701                     pCurrentChild = insertObject(pParent, sClass, sID,
3702                         aProperties, aPangoAttributes, aAtkAttributes);
3703                 }
3704                 handleChild(pCurrentChild, reader);
3705             }
3706             else if (name == "items")
3707                 aItems = handleItems(reader);
3708             else if (name == "style")
3709             {
3710                 int nPriority = 0;
3711                 std::vector<vcl::EnumContext::Context> aContext = handleStyle(reader, nPriority);
3712                 if (nPriority != 0)
3713                 {
3714                     vcl::IPrioritable* pPrioritable = dynamic_cast<vcl::IPrioritable*>(pCurrentChild.get());
3715                     SAL_WARN_IF(!pPrioritable, "vcl", "priority set for not supported item");
3716                     if (pPrioritable)
3717                         pPrioritable->SetPriority(nPriority);
3718                 }
3719                 if (!aContext.empty())
3720                 {
3721                     vcl::IContext* pContextControl = dynamic_cast<vcl::IContext*>(pCurrentChild.get());
3722                     SAL_WARN_IF(!pContextControl, "vcl", "context set for not supported item");
3723                     if (pContextControl)
3724                         pContextControl->SetContext(aContext);
3725                 }
3726             }
3727             else
3728             {
3729                 ++nLevel;
3730                 if (name == "property")
3731                     collectProperty(reader, aProperties);
3732                 else if (name == "attribute")
3733                     collectPangoAttribute(reader, aPangoAttributes);
3734                 else if (name == "relation")
3735                     collectAtkRelationAttribute(reader, aAtkAttributes);
3736                 else if (name == "role")
3737                     collectAtkRoleAttribute(reader, aAtkAttributes);
3738                 else if (name == "action-widget")
3739                     handleActionWidget(reader);
3740             }
3741         }
3742 
3743         if (res == xmlreader::XmlReader::Result::End)
3744         {
3745             --nLevel;
3746         }
3747 
3748         if (!nLevel)
3749             break;
3750     }
3751 
3752     if (sClass == "GtkAdjustment")
3753     {
3754         m_pParserState->m_aAdjustments[sID] = aProperties;
3755         return nullptr;
3756     }
3757     else if (sClass == "GtkTextBuffer")
3758     {
3759         m_pParserState->m_aTextBuffers[sID] = aProperties;
3760         return nullptr;
3761     }
3762 
3763     if (!pCurrentChild)
3764     {
3765         pCurrentChild = insertObject(pParent, sClass, sID, aProperties,
3766             aPangoAttributes, aAtkAttributes);
3767     }
3768 
3769     if (!aItems.empty())
3770     {
3771         // try to fill-in the items
3772         if (!insertItems<ComboBox>(pCurrentChild, aProperties, m_aUserData, aItems))
3773             insertItems<ListBox>(pCurrentChild, aProperties, m_aUserData, aItems);
3774     }
3775 
3776     return pCurrentChild;
3777 }
3778 
3779 void VclBuilder::handlePacking(vcl::Window *pCurrent, vcl::Window *pParent, xmlreader::XmlReader &reader)
3780 {
3781     xmlreader::Span name;
3782     int nsId;
3783 
3784     int nLevel = 1;
3785 
3786     while(true)
3787     {
3788         xmlreader::XmlReader::Result res = reader.nextItem(
3789             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3790 
3791         if (res == xmlreader::XmlReader::Result::Done)
3792             break;
3793 
3794         if (res == xmlreader::XmlReader::Result::Begin)
3795         {
3796             ++nLevel;
3797             if (name == "property")
3798                 applyPackingProperty(pCurrent, pParent, reader);
3799         }
3800 
3801         if (res == xmlreader::XmlReader::Result::End)
3802         {
3803             --nLevel;
3804         }
3805 
3806         if (!nLevel)
3807             break;
3808     }
3809 }
3810 
3811 void VclBuilder::applyPackingProperty(vcl::Window *pCurrent,
3812     vcl::Window *pParent,
3813     xmlreader::XmlReader &reader)
3814 {
3815     if (!pCurrent)
3816         return;
3817 
3818     //ToolBoxItems are not true widgets just elements
3819     //of the ToolBox itself
3820     ToolBox *pToolBoxParent = nullptr;
3821     if (pCurrent == pParent)
3822         pToolBoxParent = dynamic_cast<ToolBox*>(pParent);
3823 
3824     xmlreader::Span name;
3825     int nsId;
3826 
3827     if (pCurrent->GetType() == WindowType::SCROLLWINDOW)
3828     {
3829         auto aFind = m_pParserState->m_aRedundantParentWidgets.find(VclPtr<vcl::Window>(pCurrent));
3830         if (aFind != m_pParserState->m_aRedundantParentWidgets.end())
3831         {
3832             pCurrent = aFind->second;
3833             assert(pCurrent);
3834         }
3835     }
3836 
3837     while (reader.nextAttribute(&nsId, &name))
3838     {
3839         if (name == "name")
3840         {
3841             name = reader.getAttributeValue(false);
3842             OString sKey(name.begin, name.length);
3843             sKey = sKey.replace('_', '-');
3844             (void)reader.nextItem(
3845                 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3846             OString sValue(name.begin, name.length);
3847 
3848             if (sKey == "expand" || sKey == "resize")
3849             {
3850                 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3851                 if (pToolBoxParent)
3852                     pToolBoxParent->SetItemExpand(m_pParserState->m_nLastToolbarId, bTrue);
3853                 else
3854                     pCurrent->set_expand(bTrue);
3855                 continue;
3856             }
3857 
3858             if (pToolBoxParent)
3859                 continue;
3860 
3861             if (sKey == "fill")
3862             {
3863                 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3864                 pCurrent->set_fill(bTrue);
3865             }
3866             else if (sKey == "pack-type")
3867             {
3868                 VclPackType ePackType = (!sValue.isEmpty() && (sValue[0] == 'e' || sValue[0] == 'E')) ? VclPackType::End : VclPackType::Start;
3869                 pCurrent->set_pack_type(ePackType);
3870             }
3871             else if (sKey == "left-attach")
3872             {
3873                 pCurrent->set_grid_left_attach(sValue.toInt32());
3874             }
3875             else if (sKey == "top-attach")
3876             {
3877                 pCurrent->set_grid_top_attach(sValue.toInt32());
3878             }
3879             else if (sKey == "width")
3880             {
3881                 pCurrent->set_grid_width(sValue.toInt32());
3882             }
3883             else if (sKey == "height")
3884             {
3885                 pCurrent->set_grid_height(sValue.toInt32());
3886             }
3887             else if (sKey == "padding")
3888             {
3889                 pCurrent->set_padding(sValue.toInt32());
3890             }
3891             else if (sKey == "position")
3892             {
3893                 set_window_packing_position(pCurrent, sValue.toInt32());
3894             }
3895             else if (sKey == "secondary")
3896             {
3897                 pCurrent->set_secondary(toBool(sValue));
3898             }
3899             else if (sKey == "non-homogeneous")
3900             {
3901                 pCurrent->set_non_homogeneous(toBool(sValue));
3902             }
3903             else if (sKey == "homogeneous")
3904             {
3905                 pCurrent->set_non_homogeneous(!toBool(sValue));
3906             }
3907             else
3908             {
3909                 SAL_WARN("vcl.builder", "unknown packing: " << sKey);
3910             }
3911         }
3912     }
3913 }
3914 
3915 std::vector<vcl::EnumContext::Context> VclBuilder::handleStyle(xmlreader::XmlReader &reader, int &nPriority)
3916 {
3917     std::vector<vcl::EnumContext::Context> aContext;
3918 
3919     xmlreader::Span name;
3920     int nsId;
3921 
3922     int nLevel = 1;
3923 
3924     while(true)
3925     {
3926         xmlreader::XmlReader::Result res = reader.nextItem(
3927             xmlreader::XmlReader::Text::NONE, &name, &nsId);
3928 
3929         if (res == xmlreader::XmlReader::Result::Done)
3930             break;
3931 
3932         if (res == xmlreader::XmlReader::Result::Begin)
3933         {
3934             ++nLevel;
3935             if (name == "class")
3936             {
3937                 OString classStyle = getStyleClass(reader);
3938 
3939                 if (classStyle.startsWith("context-"))
3940                 {
3941                     OString sContext = classStyle.copy(classStyle.indexOf('-') + 1);
3942                     OUString sContext2(sContext.getStr(), sContext.getLength(), RTL_TEXTENCODING_UTF8);
3943                     aContext.push_back(vcl::EnumContext::GetContextEnum(sContext2));
3944                 }
3945                 else if (classStyle.startsWith("priority-"))
3946                 {
3947                     OString aPriority = classStyle.copy(classStyle.indexOf('-') + 1);
3948                     OUString aPriority2(aPriority.getStr(), aPriority.getLength(), RTL_TEXTENCODING_UTF8);
3949                     nPriority = aPriority2.toInt32();
3950                 }
3951                 else
3952                 {
3953                     SAL_WARN("vcl.builder", "unknown class: " << classStyle);
3954                 }
3955             }
3956         }
3957 
3958         if (res == xmlreader::XmlReader::Result::End)
3959         {
3960             --nLevel;
3961         }
3962 
3963         if (!nLevel)
3964             break;
3965     }
3966 
3967     return aContext;
3968 }
3969 
3970 OString VclBuilder::getStyleClass(xmlreader::XmlReader &reader)
3971 {
3972     xmlreader::Span name;
3973     int nsId;
3974     OString aRet;
3975 
3976     while (reader.nextAttribute(&nsId, &name))
3977     {
3978         if (name == "name")
3979         {
3980             name = reader.getAttributeValue(false);
3981             aRet = OString (name.begin, name.length);
3982         }
3983     }
3984 
3985     return aRet;
3986 }
3987 
3988 void VclBuilder::collectProperty(xmlreader::XmlReader &reader, stringmap &rMap) const
3989 {
3990     xmlreader::Span name;
3991     int nsId;
3992 
3993     OString sProperty, sContext;
3994 
3995     bool bTranslated = false;
3996 
3997     while (reader.nextAttribute(&nsId, &name))
3998     {
3999         if (name == "name")
4000         {
4001             name = reader.getAttributeValue(false);
4002             sProperty = OString(name.begin, name.length);
4003         }
4004         else if (name == "context")
4005         {
4006             name = reader.getAttributeValue(false);
4007             sContext = OString(name.begin, name.length);
4008         }
4009         else if (name == "translatable" && reader.getAttributeValue(false) == "yes")
4010         {
4011             bTranslated = true;
4012         }
4013     }
4014 
4015     (void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4016     OString sValue(name.begin, name.length);
4017     OUString sFinalValue;
4018     if (bTranslated)
4019     {
4020         if (!sContext.isEmpty())
4021             sValue = sContext + "\004" + sValue;
4022         sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
4023     }
4024     else
4025         sFinalValue = OUString::fromUtf8(sValue);
4026 
4027     if (!sProperty.isEmpty())
4028     {
4029         sProperty = sProperty.replace('_', '-');
4030         if (m_pStringReplace)
4031             sFinalValue = (*m_pStringReplace)(sFinalValue);
4032         rMap[sProperty] = sFinalValue;
4033     }
4034 }
4035 
4036 void VclBuilder::handleActionWidget(xmlreader::XmlReader &reader)
4037 {
4038     xmlreader::Span name;
4039     int nsId;
4040 
4041     OString sResponse;
4042 
4043     while (reader.nextAttribute(&nsId, &name))
4044     {
4045         if (name == "response")
4046         {
4047             name = reader.getAttributeValue(false);
4048             sResponse = OString(name.begin, name.length);
4049         }
4050     }
4051 
4052     (void)reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4053     OString sID(name.begin, name.length);
4054     sal_Int32 nDelim = sID.indexOf(':');
4055     if (nDelim != -1)
4056         sID = sID.copy(0, nDelim);
4057     set_response(sID, sResponse.toInt32());
4058 }
4059 
4060 void VclBuilder::collectAccelerator(xmlreader::XmlReader &reader, accelmap &rMap)
4061 {
4062     xmlreader::Span name;
4063     int nsId;
4064 
4065     OString sProperty;
4066     OString sValue;
4067     OString sModifiers;
4068 
4069     while (reader.nextAttribute(&nsId, &name))
4070     {
4071         if (name == "key")
4072         {
4073             name = reader.getAttributeValue(false);
4074             sValue = OString(name.begin, name.length);
4075         }
4076         else if (name == "signal")
4077         {
4078             name = reader.getAttributeValue(false);
4079             sProperty = OString(name.begin, name.length);
4080         }
4081         else if (name == "modifiers")
4082         {
4083             name = reader.getAttributeValue(false);
4084             sModifiers = OString(name.begin, name.length);
4085         }
4086     }
4087 
4088     if (!sProperty.isEmpty() && !sValue.isEmpty())
4089     {
4090         rMap[sProperty] = std::make_pair(sValue, sModifiers);
4091     }
4092 }
4093 
4094 vcl::Window *VclBuilder::get_widget_root()
4095 {
4096     return m_aChildren.empty() ? nullptr : m_aChildren[0].m_pWindow.get();
4097 }
4098 
4099 vcl::Window *VclBuilder::get_by_name(const OString& sID)
4100 {
4101     for (auto const& child : m_aChildren)
4102     {
4103         if (child.m_sID == sID)
4104             return child.m_pWindow;
4105     }
4106 
4107     return nullptr;
4108 }
4109 
4110 PopupMenu *VclBuilder::get_menu(const OString& sID)
4111 {
4112     for (auto const& menu : m_aMenus)
4113     {
4114         if (menu.m_sID == sID)
4115             return dynamic_cast<PopupMenu*>(menu.m_pMenu.get());
4116     }
4117 
4118     return nullptr;
4119 }
4120 
4121 void VclBuilder::set_response(const OString& sID, short nResponse)
4122 {
4123     switch (nResponse)
4124     {
4125         case -5:
4126             nResponse = RET_OK;
4127             break;
4128         case -6:
4129             nResponse = RET_CANCEL;
4130             break;
4131         case -7:
4132             nResponse = RET_CLOSE;
4133             break;
4134         case -8:
4135             nResponse = RET_YES;
4136             break;
4137         case -9:
4138             nResponse = RET_NO;
4139             break;
4140         case -11:
4141             nResponse = RET_HELP;
4142             break;
4143         default:
4144             assert(nResponse >= 100 && "keep non-canned responses in range 100+ to avoid collision with vcl RET_*");
4145             break;
4146     }
4147 
4148     for (const auto & child : m_aChildren)
4149     {
4150         if (child.m_sID == sID)
4151         {
4152             PushButton* pPushButton = dynamic_cast<PushButton*>(child.m_pWindow.get());
4153             assert(pPushButton);
4154             Dialog* pDialog = pPushButton->GetParentDialog();
4155             assert(pDialog);
4156             pDialog->add_button(pPushButton, nResponse, false);
4157             return;
4158         }
4159     }
4160 
4161     assert(false);
4162 }
4163 
4164 void VclBuilder::delete_by_name(const OString& sID)
4165 {
4166     auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4167         [&sID](WinAndId& rItem) { return rItem.m_sID == sID; });
4168     if (aI != m_aChildren.end())
4169     {
4170         aI->m_pWindow.disposeAndClear();
4171         m_aChildren.erase(aI);
4172     }
4173 }
4174 
4175 void VclBuilder::delete_by_window(vcl::Window *pWindow)
4176 {
4177     drop_ownership(pWindow);
4178     pWindow->disposeOnce();
4179 }
4180 
4181 void VclBuilder::drop_ownership(const vcl::Window *pWindow)
4182 {
4183     auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4184         [&pWindow](WinAndId& rItem) { return rItem.m_pWindow == pWindow; });
4185     if (aI != m_aChildren.end())
4186         m_aChildren.erase(aI);
4187 }
4188 
4189 OString VclBuilder::get_by_window(const vcl::Window *pWindow) const
4190 {
4191     for (auto const& child : m_aChildren)
4192     {
4193         if (child.m_pWindow == pWindow)
4194             return child.m_sID;
4195     }
4196 
4197     return OString();
4198 }
4199 
4200 VclBuilder::PackingData VclBuilder::get_window_packing_data(const vcl::Window *pWindow) const
4201 {
4202     //We've stored the return of new Control, some of these get
4203     //border windows placed around them which are what you get
4204     //from GetChild, so scoot up a level if necessary to get the
4205     //window whose position value we have
4206     const vcl::Window *pPropHolder = pWindow->ImplGetWindow();
4207 
4208     for (auto const& child : m_aChildren)
4209     {
4210         if (child.m_pWindow == pPropHolder)
4211             return child.m_aPackingData;
4212     }
4213 
4214     return PackingData();
4215 }
4216 
4217 void VclBuilder::set_window_packing_position(const vcl::Window *pWindow, sal_Int32 nPosition)
4218 {
4219     for (auto & child : m_aChildren)
4220     {
4221         if (child.m_pWindow == pWindow)
4222             child.m_aPackingData.m_nPosition = nPosition;
4223     }
4224 }
4225 
4226 const VclBuilder::ListStore *VclBuilder::get_model_by_name(const OString& sID) const
4227 {
4228     std::map<OString, ListStore>::const_iterator aI = m_pParserState->m_aModels.find(sID);
4229     if (aI != m_pParserState->m_aModels.end())
4230         return &(aI->second);
4231     return nullptr;
4232 }
4233 
4234 const VclBuilder::TextBuffer *VclBuilder::get_buffer_by_name(const OString& sID) const
4235 {
4236     std::map<OString, TextBuffer>::const_iterator aI = m_pParserState->m_aTextBuffers.find(sID);
4237     if (aI != m_pParserState->m_aTextBuffers.end())
4238         return &(aI->second);
4239     return nullptr;
4240 }
4241 
4242 const VclBuilder::Adjustment *VclBuilder::get_adjustment_by_name(const OString& sID) const
4243 {
4244     std::map<OString, Adjustment>::const_iterator aI = m_pParserState->m_aAdjustments.find(sID);
4245     if (aI != m_pParserState->m_aAdjustments.end())
4246         return &(aI->second);
4247     return nullptr;
4248 }
4249 
4250 void VclBuilder::mungeModel(ComboBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4251 {
4252     for (auto const& entry : rStore.m_aEntries)
4253     {
4254         const ListStore::row &rRow = entry;
4255         sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4256         if (rRow.size() > 1)
4257         {
4258             if (m_bLegacy)
4259             {
4260                 sal_IntPtr nValue = rRow[1].toInt32();
4261                 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4262             }
4263             else
4264             {
4265                 if (!rRow[1].isEmpty())
4266                 {
4267                     m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4268                     rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4269                 }
4270             }
4271         }
4272     }
4273     if (nActiveId < rStore.m_aEntries.size())
4274         rTarget.SelectEntryPos(nActiveId);
4275 }
4276 
4277 void VclBuilder::mungeModel(ListBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4278 {
4279     for (auto const& entry : rStore.m_aEntries)
4280     {
4281         const ListStore::row &rRow = entry;
4282         sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4283         if (rRow.size() > 1)
4284         {
4285             if (m_bLegacy)
4286             {
4287                 sal_IntPtr nValue = rRow[1].toInt32();
4288                 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4289             }
4290             else
4291             {
4292                 if (!rRow[1].isEmpty())
4293                 {
4294                     m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4295                     rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4296                 }
4297             }
4298         }
4299     }
4300     if (nActiveId < rStore.m_aEntries.size())
4301         rTarget.SelectEntryPos(nActiveId);
4302 }
4303 
4304 void VclBuilder::mungeModel(SvTabListBox& rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4305 {
4306     for (auto const& entry : rStore.m_aEntries)
4307     {
4308         const ListStore::row &rRow = entry;
4309         auto pEntry = rTarget.InsertEntry(rRow[0]);
4310         if (rRow.size() > 1)
4311         {
4312             if (m_bLegacy)
4313             {
4314                 sal_IntPtr nValue = rRow[1].toInt32();
4315                 pEntry->SetUserData(reinterpret_cast<void*>(nValue));
4316             }
4317             else
4318             {
4319                 if (!rRow[1].isEmpty())
4320                 {
4321                     m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4322                     pEntry->SetUserData(m_aUserData.back().get());
4323                 }
4324             }
4325         }
4326     }
4327     if (nActiveId < rStore.m_aEntries.size())
4328     {
4329         SvTreeListEntry* pEntry = rTarget.GetEntry(nullptr, nActiveId);
4330         rTarget.Select(pEntry);
4331     }
4332 }
4333 
4334 void VclBuilder::mungeAdjustment(NumericFormatter &rTarget, const Adjustment &rAdjustment)
4335 {
4336     int nMul = rtl_math_pow10Exp(1, rTarget.GetDecimalDigits());
4337 
4338     for (auto const& elem : rAdjustment)
4339     {
4340         const OString &rKey = elem.first;
4341         const OUString &rValue = elem.second;
4342 
4343         if (rKey == "upper")
4344         {
4345             sal_Int64 nUpper = rValue.toDouble() * nMul;
4346             rTarget.SetMax(nUpper);
4347             rTarget.SetLast(nUpper);
4348         }
4349         else if (rKey == "lower")
4350         {
4351             sal_Int64 nLower = rValue.toDouble() * nMul;
4352             rTarget.SetMin(nLower);
4353             rTarget.SetFirst(nLower);
4354         }
4355         else if (rKey == "value")
4356         {
4357             sal_Int64 nValue = rValue.toDouble() * nMul;
4358             rTarget.SetValue(nValue);
4359         }
4360         else if (rKey == "step-increment")
4361         {
4362             sal_Int64 nSpinSize = rValue.toDouble() * nMul;
4363             rTarget.SetSpinSize(nSpinSize);
4364         }
4365         else
4366         {
4367             SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4368         }
4369     }
4370 }
4371 
4372 void VclBuilder::mungeAdjustment(FormattedField &rTarget, const Adjustment &rAdjustment)
4373 {
4374     double nMaxValue = 0, nMinValue = 0, nValue = 0, nSpinSize = 0;
4375 
4376     for (auto const& elem : rAdjustment)
4377     {
4378         const OString &rKey = elem.first;
4379         const OUString &rValue = elem.second;
4380 
4381         if (rKey == "upper")
4382             nMaxValue = rValue.toDouble();
4383         else if (rKey == "lower")
4384             nMinValue = rValue.toDouble();
4385         else if (rKey == "value")
4386             nValue = rValue.toDouble();
4387         else if (rKey == "step-increment")
4388             nSpinSize = rValue.toDouble();
4389         else
4390             SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4391     }
4392 
4393     rTarget.SetMinValue(nMinValue);
4394     rTarget.SetMaxValue(nMaxValue);
4395     rTarget.SetValue(nValue);
4396     rTarget.SetSpinSize(nSpinSize);
4397 }
4398 
4399 void VclBuilder::mungeAdjustment(ScrollBar &rTarget, const Adjustment &rAdjustment)
4400 {
4401     for (auto const& elem : rAdjustment)
4402     {
4403         const OString &rKey = elem.first;
4404         const OUString &rValue = elem.second;
4405 
4406         if (rKey == "upper")
4407             rTarget.SetRangeMax(rValue.toInt32());
4408         else if (rKey == "lower")
4409             rTarget.SetRangeMin(rValue.toInt32());
4410         else if (rKey == "value")
4411             rTarget.SetThumbPos(rValue.toInt32());
4412         else if (rKey == "step-increment")
4413             rTarget.SetLineSize(rValue.toInt32());
4414         else if (rKey == "page-increment")
4415             rTarget.SetPageSize(rValue.toInt32());
4416         else
4417         {
4418             SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4419         }
4420     }
4421 }
4422 
4423 void VclBuilder::mungeAdjustment(Slider& rTarget, const Adjustment& rAdjustment)
4424 {
4425     for (auto const& elem : rAdjustment)
4426     {
4427         const OString &rKey = elem.first;
4428         const OUString &rValue = elem.second;
4429 
4430         if (rKey == "upper")
4431             rTarget.SetRangeMax(rValue.toInt32());
4432         else if (rKey == "lower")
4433             rTarget.SetRangeMin(rValue.toInt32());
4434         else if (rKey == "value")
4435             rTarget.SetThumbPos(rValue.toInt32());
4436         else if (rKey == "step-increment")
4437             rTarget.SetLineSize(rValue.toInt32());
4438         else if (rKey == "page-increment")
4439             rTarget.SetPageSize(rValue.toInt32());
4440         else
4441         {
4442             SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4443         }
4444     }
4445 }
4446 
4447 void VclBuilder::mungeTextBuffer(VclMultiLineEdit &rTarget, const TextBuffer &rTextBuffer)
4448 {
4449     for (auto const& elem : rTextBuffer)
4450     {
4451         const OString &rKey = elem.first;
4452         const OUString &rValue = elem.second;
4453 
4454         if (rKey == "text")
4455             rTarget.SetText(rValue);
4456         else
4457         {
4458             SAL_INFO("vcl.builder", "unhandled property :" << rKey);
4459         }
4460     }
4461 }
4462 
4463 VclBuilder::ParserState::ParserState()
4464     : m_nLastToolbarId(0)
4465     , m_nLastMenuItemId(0)
4466 {}
4467 
4468 VclBuilder::MenuAndId::MenuAndId(const OString &rId, Menu *pMenu)
4469     : m_sID(rId)
4470     , m_pMenu(pMenu)
4471 {}
4472 
4473 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
4474