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