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