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