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