bump product version to 6.4.0.3
[LibreOffice.git] / vcl / source / window / builder.cxx
blob91bf9857b102a26e1dbb66885d0cf5c9cd589ed9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <config_feature_desktop.h>
12 #include <memory>
13 #include <unordered_map>
14 #include <com/sun/star/accessibility/AccessibleRole.hpp>
16 #include <comphelper/lok.hxx>
17 #include <i18nutil/unicode.hxx>
18 #include <officecfg/Office/Common.hxx>
19 #include <osl/module.hxx>
20 #include <sal/log.hxx>
21 #include <unotools/localedatawrapper.hxx>
22 #include <unotools/resmgr.hxx>
23 #include <vcl/builder.hxx>
24 #include <vcl/button.hxx>
25 #include <vcl/calendar.hxx>
26 #include <vcl/dialog.hxx>
27 #include <vcl/edit.hxx>
28 #include <vcl/field.hxx>
29 #include <vcl/fmtfield.hxx>
30 #include <vcl/fixed.hxx>
31 #include <vcl/toolkit/fixedhyper.hxx>
32 #include <vcl/headbar.hxx>
33 #include <vcl/IPrioritable.hxx>
34 #include <vcl/ivctrl.hxx>
35 #include <vcl/layout.hxx>
36 #include <vcl/lstbox.hxx>
37 #include <vcl/menubtn.hxx>
38 #include <vcl/mnemonic.hxx>
39 #include <vcl/toolkit/prgsbar.hxx>
40 #include <vcl/scrbar.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/svtabbx.hxx>
43 #include <vcl/tabctrl.hxx>
44 #include <vcl/tabpage.hxx>
45 #include <vcl/toolkit/throbber.hxx>
46 #include <vcl/toolbox.hxx>
47 #include <vcl/treelistentry.hxx>
48 #include <vcl/vclmedit.hxx>
49 #include <vcl/settings.hxx>
50 #include <vcl/slider.hxx>
51 #include <vcl/weld.hxx>
52 #include <vcl/commandinfoprovider.hxx>
53 #include <iconview.hxx>
54 #include <svdata.hxx>
55 #include <bitmaps.hlst>
56 #include <messagedialog.hxx>
57 #include <window.h>
58 #include <xmlreader/xmlreader.hxx>
59 #include <desktop/crashreport.hxx>
60 #include <salinst.hxx>
61 #include <strings.hrc>
62 #include <aboutdialog.hxx>
63 #include <treeglue.hxx>
64 #include <tools/diagnose_ex.h>
65 #include <wizdlg.hxx>
66 #include <tools/svlibrary.h>
68 #ifdef DISABLE_DYNLOADING
69 #include <dlfcn.h>
70 #endif
72 static bool toBool(const OString &rValue)
74 return (!rValue.isEmpty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
77 namespace
79 OUString mapStockToImageResource(const OUString& sType)
81 if (sType == "gtk-index")
82 return SV_RESID_BITMAP_INDEX;
83 else if (sType == "gtk-refresh")
84 return SV_RESID_BITMAP_REFRESH;
85 else if (sType == "gtk-apply")
86 return IMG_APPLY;
87 else if (sType == "gtk-dialog-error")
88 return IMG_ERROR;
89 else if (sType == "gtk-add")
90 return IMG_ADD;
91 else if (sType == "gtk-remove")
92 return IMG_REMOVE;
93 return OUString();
96 SymbolType mapStockToSymbol(const OUString& sType)
98 SymbolType eRet = SymbolType::DONTKNOW;
99 if (sType == "gtk-media-next")
100 eRet = SymbolType::NEXT;
101 else if (sType == "gtk-media-previous")
102 eRet = SymbolType::PREV;
103 else if (sType == "gtk-media-play")
104 eRet = SymbolType::PLAY;
105 else if (sType == "gtk-media-stop")
106 eRet = SymbolType::STOP;
107 else if (sType == "gtk-goto-first")
108 eRet = SymbolType::FIRST;
109 else if (sType == "gtk-goto-last")
110 eRet = SymbolType::LAST;
111 else if (sType == "gtk-go-back")
112 eRet = SymbolType::ARROW_LEFT;
113 else if (sType == "gtk-go-forward")
114 eRet = SymbolType::ARROW_RIGHT;
115 else if (sType == "gtk-go-up")
116 eRet = SymbolType::ARROW_UP;
117 else if (sType == "gtk-go-down")
118 eRet = SymbolType::ARROW_DOWN;
119 else if (sType == "gtk-missing-image")
120 eRet = SymbolType::IMAGE;
121 else if (sType == "gtk-help")
122 eRet = SymbolType::HELP;
123 else if (sType == "gtk-close")
124 eRet = SymbolType::CLOSE;
125 else if (!mapStockToImageResource(sType).isEmpty())
126 eRet = SymbolType::IMAGE;
127 return eRet;
130 void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame);
133 #if defined SAL_LOG_WARN
134 namespace
136 bool isButtonType(WindowType nType)
138 return nType == WindowType::PUSHBUTTON ||
139 nType == WindowType::OKBUTTON ||
140 nType == WindowType::CANCELBUTTON ||
141 nType == WindowType::HELPBUTTON ||
142 nType == WindowType::IMAGEBUTTON ||
143 nType == WindowType::MENUBUTTON ||
144 nType == WindowType::MOREBUTTON ||
145 nType == WindowType::SPINBUTTON;
148 #endif
150 weld::Builder* Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile)
152 return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile);
155 weld::Builder* Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile)
157 return SalInstance::CreateInterimBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile);
160 weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType,
161 VclButtonsType eButtonType, const OUString& rPrimaryMessage)
163 return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
166 weld::Window* Application::GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow)
168 return ImplGetSVData()->mpDefInst->GetFrameWeld(rWindow);
171 namespace weld
173 OUString MetricSpinButton::MetricToString(FieldUnit rUnit)
175 const FieldUnitStringList& rList = ImplGetFieldUnits();
176 // return unit's default string (ie, the first one )
177 auto it = std::find_if(
178 rList.begin(), rList.end(),
179 [&rUnit](const std::pair<OUString, FieldUnit>& rItem) { return rItem.second == rUnit; });
180 if (it != rList.end())
181 return it->first;
183 return OUString();
186 IMPL_LINK_NOARG(MetricSpinButton, spin_button_value_changed, SpinButton&, void)
188 signal_value_changed();
191 IMPL_LINK(MetricSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
193 OUString sNewText(format_number(rSpinButton.get_value()));
194 if (sNewText != rSpinButton.get_text())
195 rSpinButton.set_text(sNewText);
198 void MetricSpinButton::update_width_chars()
200 int min, max;
201 m_xSpinButton->get_range(min, max);
202 auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
203 m_xSpinButton->get_pixel_size(format_number(max)).Width());
204 int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
205 m_xSpinButton->set_width_chars(chars);
208 unsigned int SpinButton::Power10(unsigned int n)
210 unsigned int nValue = 1;
211 for (unsigned int i = 0; i < n; ++i)
212 nValue *= 10;
213 return nValue;
216 int SpinButton::denormalize(int nValue) const
218 const int nFactor = Power10(get_digits());
220 if ((nValue < (SAL_MIN_INT32 + nFactor)) || (nValue > (SAL_MAX_INT32 - nFactor)))
222 return nValue / nFactor;
225 const int nHalf = nFactor / 2;
227 if (nValue < 0)
228 return (nValue - nHalf) / nFactor;
229 return (nValue + nHalf) / nFactor;
232 OUString MetricSpinButton::format_number(int nValue) const
234 OUString aStr;
236 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
238 unsigned int nDecimalDigits = m_xSpinButton->get_digits();
239 //pawn percent off to icu to decide whether percent is separated from its number for this locale
240 if (m_eSrcUnit == FieldUnit::PERCENT)
242 double fValue = nValue;
243 fValue /= SpinButton::Power10(nDecimalDigits);
244 aStr = unicode::formatPercent(fValue, rLocaleData.getLanguageTag());
246 else
248 aStr = rLocaleData.getNum(nValue, nDecimalDigits, true, true);
249 OUString aSuffix = MetricToString(m_eSrcUnit);
250 if (m_eSrcUnit != FieldUnit::NONE && m_eSrcUnit != FieldUnit::DEGREE && m_eSrcUnit != FieldUnit::INCH)
251 aStr += " ";
252 if (m_eSrcUnit == FieldUnit::INCH)
254 OUString sDoublePrime = u"\u2033";
255 if (aSuffix != "\"" && aSuffix != sDoublePrime)
256 aStr += " ";
257 else
258 aSuffix = sDoublePrime;
260 assert(m_eSrcUnit != FieldUnit::PERCENT);
261 aStr += aSuffix;
264 return aStr;
267 void MetricSpinButton::set_digits(unsigned int digits)
269 int step, page;
270 get_increments(step, page, m_eSrcUnit);
271 int value = get_value(m_eSrcUnit);
272 m_xSpinButton->set_digits(digits);
273 set_increments(step, page, m_eSrcUnit);
274 set_value(value, m_eSrcUnit);
275 update_width_chars();
278 void MetricSpinButton::set_unit(FieldUnit eUnit)
280 if (eUnit != m_eSrcUnit)
282 int step, page;
283 get_increments(step, page, m_eSrcUnit);
284 int value = get_value(m_eSrcUnit);
285 m_eSrcUnit = eUnit;
286 set_increments(step, page, m_eSrcUnit);
287 set_value(value, m_eSrcUnit);
288 spin_button_output(*m_xSpinButton);
289 update_width_chars();
293 int MetricSpinButton::ConvertValue(int nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const
295 return MetricField::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit);
298 IMPL_LINK(MetricSpinButton, spin_button_input, int*, result, bool)
300 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
301 double fResult(0.0);
302 bool bRet = MetricFormatter::TextToValue(get_text(), fResult, 0, m_xSpinButton->get_digits(), rLocaleData, m_eSrcUnit);
303 if (bRet)
305 if (fResult > SAL_MAX_INT32)
306 fResult = SAL_MAX_INT32;
307 else if (fResult < SAL_MIN_INT32)
308 fResult = SAL_MIN_INT32;
309 *result = fResult;
311 return bRet;
314 IMPL_LINK_NOARG(TimeSpinButton, spin_button_cursor_position, Entry&, void)
316 int nStartPos, nEndPos;
317 m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);
319 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
320 const int nTimeArea = TimeFormatter::GetTimeArea(m_eFormat, m_xSpinButton->get_text(), nEndPos,
321 rLocaleData);
323 int nIncrements = 1;
325 if (nTimeArea == 1)
326 nIncrements = 1000 * 60 * 60;
327 else if (nTimeArea == 2)
328 nIncrements = 1000 * 60;
329 else if (nTimeArea == 3)
330 nIncrements = 1000;
332 m_xSpinButton->set_increments(nIncrements, nIncrements * 10);
335 IMPL_LINK_NOARG(TimeSpinButton, spin_button_value_changed, SpinButton&, void)
337 signal_value_changed();
340 IMPL_LINK(TimeSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
342 int nStartPos, nEndPos;
343 rSpinButton.get_selection_bounds(nStartPos, nEndPos);
344 rSpinButton.set_text(format_number(rSpinButton.get_value()));
345 rSpinButton.set_position(nEndPos);
348 IMPL_LINK(TimeSpinButton, spin_button_input, int*, result, bool)
350 int nStartPos, nEndPos;
351 m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);
353 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
354 tools::Time aResult(0);
355 bool bRet = TimeFormatter::TextToTime(m_xSpinButton->get_text(), aResult, m_eFormat, true, rLocaleData);
356 if (bRet)
357 *result = ConvertValue(aResult);
358 return bRet;
361 void TimeSpinButton::update_width_chars()
363 int min, max;
364 m_xSpinButton->get_range(min, max);
365 auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
366 m_xSpinButton->get_pixel_size(format_number(max)).Width());
367 int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
368 m_xSpinButton->set_width_chars(chars);
371 tools::Time TimeSpinButton::ConvertValue(int nValue)
373 tools::Time aTime(0);
374 aTime.MakeTimeFromMS(nValue);
375 return aTime;
378 int TimeSpinButton::ConvertValue(const tools::Time& rTime)
380 return rTime.GetMSFromTime();
383 OUString TimeSpinButton::format_number(int nValue) const
385 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
386 return TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, TimeFormat::Hour24, true, rLocaleData);
389 EntryTreeView::EntryTreeView(std::unique_ptr<Entry> xEntry, std::unique_ptr<TreeView> xTreeView)
390 : m_xEntry(std::move(xEntry))
391 , m_xTreeView(std::move(xTreeView))
393 m_xTreeView->connect_changed(LINK(this, EntryTreeView, ClickHdl));
394 m_xEntry->connect_changed(LINK(this, EntryTreeView, ModifyHdl));
397 IMPL_LINK(EntryTreeView, ClickHdl, weld::TreeView&, rView, void)
399 m_xEntry->set_text(rView.get_selected_text());
400 m_aChangeHdl.Call(*this);
403 IMPL_LINK_NOARG(EntryTreeView, ModifyHdl, weld::Entry&, void)
405 m_aChangeHdl.Call(*this);
408 void EntryTreeView::set_height_request_by_rows(int nRows)
410 int nHeight = nRows == -1 ? -1 : m_xTreeView->get_height_rows(nRows);
411 m_xTreeView->set_size_request(m_xTreeView->get_size_request().Width(), nHeight);
415 VclBuilder::VclBuilder(vcl::Window* pParent, const OUString& sUIDir, const OUString& sUIFile,
416 const OString& sID, const css::uno::Reference<css::frame::XFrame>& rFrame,
417 bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem)
418 : m_pNotebookBarAddonsItem(pNotebookBarAddonsItem
419 ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem)
420 : new NotebookBarAddonsItem{})
421 , m_sID(sID)
422 , m_sHelpRoot(OUStringToOString(sUIFile, RTL_TEXTENCODING_UTF8))
423 , m_pStringReplace(Translate::GetReadStringHook())
424 , m_pParent(pParent)
425 , m_bToplevelParentFound(false)
426 , m_bLegacy(bLegacy)
427 , m_pParserState(new ParserState)
428 , m_xFrame(rFrame)
430 m_bToplevelHasDeferredInit = pParent &&
431 ((pParent->IsSystemWindow() && static_cast<SystemWindow*>(pParent)->isDeferredInit()) ||
432 (pParent->IsDockingWindow() && static_cast<DockingWindow*>(pParent)->isDeferredInit()));
433 m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit;
435 sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.');
436 if (nIdx != -1)
437 m_sHelpRoot = m_sHelpRoot.copy(0, nIdx);
438 m_sHelpRoot += OString('/');
440 OUString sUri = sUIDir + sUIFile;
444 xmlreader::XmlReader reader(sUri);
446 handleChild(pParent, reader);
448 catch (const css::uno::Exception &rExcept)
450 DBG_UNHANDLED_EXCEPTION("vcl.layout", "Unable to read .ui file");
451 CrashReporter::addKeyValue("VclBuilderException", "Unable to read .ui file: " + rExcept.Message, CrashReporter::Write);
452 throw;
455 //Set Mnemonic widgets when everything has been imported
456 for (auto const& mnemonicWidget : m_pParserState->m_aMnemonicWidgetMaps)
458 FixedText *pOne = get<FixedText>(mnemonicWidget.m_sID);
459 vcl::Window *pOther = get<vcl::Window>(mnemonicWidget.m_sValue.toUtf8());
460 SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << mnemonicWidget.m_sID
461 << " or target " << mnemonicWidget.m_sValue << " member of Mnemonic Widget Mapping");
462 if (pOne && pOther)
463 pOne->set_mnemonic_widget(pOther);
466 //Set a11y relations and role when everything has been imported
467 for (auto const& elemAtk : m_pParserState->m_aAtkInfo)
469 vcl::Window *pSource = elemAtk.first;
470 const stringmap &rMap = elemAtk.second;
472 for (auto const& elemMap : rMap)
474 const OString &rType = elemMap.first;
475 const OUString &rParam = elemMap.second;
476 if (rType == "role")
478 sal_Int16 role = BuilderUtils::getRoleFromName(rParam.toUtf8());
479 if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN)
480 pSource->SetAccessibleRole(role);
482 else
484 vcl::Window *pTarget = get<vcl::Window>(rParam.toUtf8());
485 SAL_WARN_IF(!pTarget, "vcl", "missing parameter of a11y relation: " << rParam);
486 if (!pTarget)
487 continue;
488 if (rType == "labelled-by")
489 pSource->SetAccessibleRelationLabeledBy(pTarget);
490 else if (rType == "label-for")
491 pSource->SetAccessibleRelationLabelFor(pTarget);
492 else if (rType == "member-of")
493 pSource->SetAccessibleRelationMemberOf(pTarget);
494 else
496 SAL_WARN("vcl.layout", "unhandled a11y relation :" << rType);
502 //Set radiobutton groups when everything has been imported
503 for (auto const& elem : m_pParserState->m_aGroupMaps)
505 RadioButton *pOne = get<RadioButton>(elem.m_sID);
506 RadioButton *pOther = get<RadioButton>(elem.m_sValue);
507 SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group");
508 if (pOne && pOther)
510 if (m_bLegacy)
511 pOne->group(*pOther);
512 else
514 pOther->group(*pOne);
515 std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
520 //Set ComboBox models when everything has been imported
521 for (auto const& elem : m_pParserState->m_aModelMaps)
523 vcl::Window* pTarget = get<vcl::Window>(elem.m_sID);
524 ListBox *pListBoxTarget = dynamic_cast<ListBox*>(pTarget);
525 ComboBox *pComboBoxTarget = dynamic_cast<ComboBox*>(pTarget);
526 SvTabListBox *pTreeBoxTarget = dynamic_cast<SvTabListBox*>(pTarget);
527 // pStore may be empty
528 const ListStore *pStore = get_model_by_name(elem.m_sValue.toUtf8());
529 SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget, "vcl", "missing elements of combobox");
530 if (pListBoxTarget && pStore)
531 mungeModel(*pListBoxTarget, *pStore, elem.m_nActiveId);
532 else if (pComboBoxTarget && pStore)
533 mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId);
534 else if (pTreeBoxTarget && pStore)
535 mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId);
538 //Set TextView buffers when everything has been imported
539 for (auto const& elem : m_pParserState->m_aTextBufferMaps)
541 VclMultiLineEdit *pTarget = get<VclMultiLineEdit>(elem.m_sID);
542 const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue.toUtf8());
543 SAL_WARN_IF(!pTarget || !pBuffer, "vcl", "missing elements of textview/textbuffer");
544 if (pTarget && pBuffer)
545 mungeTextBuffer(*pTarget, *pBuffer);
548 //Set SpinButton adjustments when everything has been imported
549 for (auto const& elem : m_pParserState->m_aNumericFormatterAdjustmentMaps)
551 NumericFormatter *pTarget = dynamic_cast<NumericFormatter*>(get<vcl::Window>(elem.m_sID));
552 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
553 SAL_WARN_IF(!pTarget, "vcl", "missing NumericFormatter element of spinbutton/adjustment");
554 SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
555 if (pTarget && pAdjustment)
556 mungeAdjustment(*pTarget, *pAdjustment);
559 for (auto const& elem : m_pParserState->m_aFormattedFormatterAdjustmentMaps)
561 FormattedField *pTarget = dynamic_cast<FormattedField*>(get<vcl::Window>(elem.m_sID));
562 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
563 SAL_WARN_IF(!pTarget, "vcl", "missing FormattedField element of spinbutton/adjustment");
564 SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
565 if (pTarget && pAdjustment)
566 mungeAdjustment(*pTarget, *pAdjustment);
569 for (auto const& elem : m_pParserState->m_aTimeFormatterAdjustmentMaps)
571 TimeField *pTarget = dynamic_cast<TimeField*>(get<vcl::Window>(elem.m_sID));
572 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
573 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of spinbutton/adjustment");
574 if (pTarget && pAdjustment)
575 mungeAdjustment(*pTarget, *pAdjustment);
578 for (auto const& elem : m_pParserState->m_aDateFormatterAdjustmentMaps)
580 DateField *pTarget = dynamic_cast<DateField*>(get<vcl::Window>(elem.m_sID));
581 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
582 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of spinbutton/adjustment");
583 if (pTarget && pAdjustment)
584 mungeAdjustment(*pTarget, *pAdjustment);
587 //Set ScrollBar adjustments when everything has been imported
588 for (auto const& elem : m_pParserState->m_aScrollAdjustmentMaps)
590 ScrollBar *pTarget = get<ScrollBar>(elem.m_sID);
591 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
592 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scrollbar/adjustment");
593 if (pTarget && pAdjustment)
594 mungeAdjustment(*pTarget, *pAdjustment);
597 //Set Scale(Slider) adjustments
598 for (auto const& elem : m_pParserState->m_aSliderAdjustmentMaps)
600 Slider* pTarget = dynamic_cast<Slider*>(get<vcl::Window>(elem.m_sID));
601 const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
602 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scale(slider)/adjustment");
603 if (pTarget && pAdjustment)
605 mungeAdjustment(*pTarget, *pAdjustment);
609 //Set size-groups when all widgets have been imported
610 for (auto const& sizeGroup : m_pParserState->m_aSizeGroups)
612 std::shared_ptr<VclSizeGroup> xGroup(std::make_shared<VclSizeGroup>());
614 for (auto const& elem : sizeGroup.m_aProperties)
616 const OString &rKey = elem.first;
617 const OUString &rValue = elem.second;
618 xGroup->set_property(rKey, rValue);
621 for (auto const& elem : sizeGroup.m_aWidgets)
623 vcl::Window* pWindow = get<vcl::Window>(elem.getStr());
624 pWindow->add_to_size_group(xGroup);
628 //Set button images when everything has been imported
629 std::set<OUString> aImagesToBeRemoved;
630 for (auto const& elem : m_pParserState->m_aButtonImageWidgetMaps)
632 PushButton *pTargetButton = nullptr;
633 RadioButton *pTargetRadio = nullptr;
634 Button *pTarget = nullptr;
636 if (!elem.m_bRadio)
638 pTargetButton = get<PushButton>(elem.m_sID);
639 pTarget = pTargetButton;
641 else
643 pTargetRadio = get<RadioButton>(elem.m_sID);
644 pTarget = pTargetRadio;
647 FixedImage *pImage = get<FixedImage>(elem.m_sValue.toUtf8());
648 SAL_WARN_IF(!pTarget || !pImage,
649 "vcl", "missing elements of button/image/stock");
650 if (!pTarget || !pImage)
651 continue;
652 aImagesToBeRemoved.insert(elem.m_sValue);
654 VclBuilder::StockMap::iterator aFind = m_pParserState->m_aStockMap.find(elem.m_sValue.toUtf8());
655 if (aFind == m_pParserState->m_aStockMap.end())
657 if (!elem.m_bRadio)
658 pTargetButton->SetModeImage(pImage->GetImage());
659 else
660 pTargetRadio->SetModeRadioImage(pImage->GetImage());
662 else
664 const stockinfo &rImageInfo = aFind->second;
665 SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
666 SAL_WARN_IF(eType == SymbolType::DONTKNOW, "vcl", "missing stock image element for button");
667 if (eType == SymbolType::DONTKNOW)
668 continue;
669 if (!elem.m_bRadio)
671 pTargetButton->SetSymbol(eType);
672 //fdo#76457 keep symbol images small e.g. tools->customize->menu
673 //but images the right size. Really the PushButton::CalcMinimumSize
674 //and PushButton::ImplDrawPushButton are the better place to handle
675 //this, but its such a train-wreck
676 if (eType != SymbolType::IMAGE)
677 pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
679 else
680 SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl.layout", "unimplemented symbol type for radiobuttons");
681 if (eType == SymbolType::IMAGE)
683 Image const aImage(StockImage::Yes,
684 mapStockToImageResource(rImageInfo.m_sStock));
685 if (!elem.m_bRadio)
686 pTargetButton->SetModeImage(aImage);
687 else
688 pTargetRadio->SetModeRadioImage(aImage);
690 switch (rImageInfo.m_nSize)
692 case 1:
693 pTarget->SetSmallSymbol();
694 break;
695 case 3:
696 // large toolbar, make bigger than normal (4)
697 pTarget->set_width_request(pTarget->GetOptimalSize().Width() * 1.5);
698 pTarget->set_height_request(pTarget->GetOptimalSize().Height() * 1.5);
699 break;
700 case 4:
701 break;
702 default:
703 SAL_WARN("vcl.layout", "unsupported image size " << rImageInfo.m_nSize);
704 break;
709 //There may be duplicate use of an Image, so we used a set to collect and
710 //now we can remove them from the tree after their final munge
711 for (auto const& elem : aImagesToBeRemoved)
713 delete_by_name(elem.toUtf8());
716 //fill in any stock icons in surviving images
717 for (auto const& elem : m_pParserState->m_aStockMap)
719 FixedImage *pImage = get<FixedImage>(elem.first);
720 SAL_WARN_IF(!pImage, "vcl", "missing elements of image/stock: " << elem.first);
721 if (!pImage)
722 continue;
724 const stockinfo &rImageInfo = elem.second;
725 if (rImageInfo.m_sStock == "gtk-missing-image")
726 continue;
728 SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
729 SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl", "unimplemented symbol type for images");
730 if (eType != SymbolType::IMAGE)
731 continue;
733 Image const aImage(StockImage::Yes,
734 mapStockToImageResource(rImageInfo.m_sStock));
735 pImage->SetImage(aImage);
738 //Set button menus when everything has been imported
739 for (auto const& elem : m_pParserState->m_aButtonMenuMaps)
741 MenuButton *pTarget = get<MenuButton>(elem.m_sID);
742 PopupMenu *pMenu = get_menu(elem.m_sValue.toUtf8());
743 SAL_WARN_IF(!pTarget || !pMenu,
744 "vcl", "missing elements of button/menu");
745 if (!pTarget || !pMenu)
746 continue;
747 pTarget->SetPopupMenu(pMenu);
750 //Remove ScrollWindow parent widgets whose children in vcl implement scrolling
751 //internally.
752 for (auto const& elem : m_pParserState->m_aRedundantParentWidgets)
754 delete_by_window(elem.first);
757 //fdo#67378 merge the label into the disclosure button
758 for (auto const& elem : m_pParserState->m_aExpanderWidgets)
760 vcl::Window *pChild = elem->get_child();
761 vcl::Window* pLabel = elem->GetWindow(GetWindowType::LastChild);
762 if (pLabel && pLabel != pChild && pLabel->GetType() == WindowType::FIXEDTEXT)
764 FixedText *pLabelWidget = static_cast<FixedText*>(pLabel);
765 elem->set_label(pLabelWidget->GetText());
766 delete_by_window(pLabel);
770 // create message dialog message area now
771 for (auto const& elem : m_pParserState->m_aMessageDialogs)
772 elem->create_message_area();
774 //drop maps, etc. that we don't need again
775 m_pParserState.reset();
777 SAL_WARN_IF(!m_sID.isEmpty() && (!m_bToplevelParentFound && !get_by_name(m_sID)), "vcl.layout",
778 "Requested top level widget \"" << m_sID << "\" not found in " << sUIFile);
780 #if defined SAL_LOG_WARN
781 if (m_bToplevelParentFound && m_pParent->IsDialog())
783 int nButtons = 0;
784 bool bHasDefButton = false;
785 for (auto const& child : m_aChildren)
787 if (isButtonType(child.m_pWindow->GetType()))
789 ++nButtons;
790 if (child.m_pWindow->GetStyle() & WB_DEFBUTTON)
792 bHasDefButton = true;
793 break;
797 SAL_WARN_IF(nButtons && !bHasDefButton, "vcl.layout", "No default button defined in " << sUIFile);
799 #endif
801 const bool bHideHelp = comphelper::LibreOfficeKit::isActive() &&
802 officecfg::Office::Common::Help::HelpRootURL::get().isEmpty();
803 if (bHideHelp)
805 if (vcl::Window *pHelpButton = get<vcl::Window>("help"))
806 pHelpButton->Hide();
810 VclBuilder::~VclBuilder()
812 disposeBuilder();
815 void VclBuilder::disposeBuilder()
817 for (std::vector<WinAndId>::reverse_iterator aI = m_aChildren.rbegin(),
818 aEnd = m_aChildren.rend(); aI != aEnd; ++aI)
820 aI->m_pWindow.disposeAndClear();
822 m_aChildren.clear();
824 for (std::vector<MenuAndId>::reverse_iterator aI = m_aMenus.rbegin(),
825 aEnd = m_aMenus.rend(); aI != aEnd; ++aI)
827 aI->m_pMenu.disposeAndClear();
829 m_aMenus.clear();
830 m_pParent.clear();
833 namespace
835 bool extractDrawValue(VclBuilder::stringmap& rMap)
837 bool bDrawValue = true;
838 VclBuilder::stringmap::iterator aFind = rMap.find("draw_value");
839 if (aFind != rMap.end())
841 bDrawValue = toBool(aFind->second);
842 rMap.erase(aFind);
844 return bDrawValue;
847 OUString extractPopupMenu(VclBuilder::stringmap& rMap)
849 OUString sRet;
850 VclBuilder::stringmap::iterator aFind = rMap.find("popup");
851 if (aFind != rMap.end())
853 sRet = aFind->second;
854 rMap.erase(aFind);
856 return sRet;
859 OUString extractValuePos(VclBuilder::stringmap& rMap)
861 OUString sRet("top");
862 VclBuilder::stringmap::iterator aFind = rMap.find("value_pos");
863 if (aFind != rMap.end())
865 sRet = aFind->second;
866 rMap.erase(aFind);
868 return sRet;
871 OUString extractTypeHint(VclBuilder::stringmap &rMap)
873 OUString sRet("normal");
874 VclBuilder::stringmap::iterator aFind = rMap.find("type-hint");
875 if (aFind != rMap.end())
877 sRet = aFind->second;
878 rMap.erase(aFind);
880 return sRet;
883 bool extractResizable(VclBuilder::stringmap &rMap)
885 bool bResizable = true;
886 VclBuilder::stringmap::iterator aFind = rMap.find("resizable");
887 if (aFind != rMap.end())
889 bResizable = toBool(aFind->second);
890 rMap.erase(aFind);
892 return bResizable;
895 #if HAVE_FEATURE_DESKTOP
896 bool extractModal(VclBuilder::stringmap &rMap)
898 bool bModal = false;
899 VclBuilder::stringmap::iterator aFind = rMap.find("modal");
900 if (aFind != rMap.end())
902 bModal = toBool(aFind->second);
903 rMap.erase(aFind);
905 return bModal;
907 #endif
909 bool extractDecorated(VclBuilder::stringmap &rMap)
911 bool bDecorated = true;
912 VclBuilder::stringmap::iterator aFind = rMap.find("decorated");
913 if (aFind != rMap.end())
915 bDecorated = toBool(aFind->second);
916 rMap.erase(aFind);
918 return bDecorated;
921 bool extractCloseable(VclBuilder::stringmap &rMap)
923 bool bCloseable = true;
924 VclBuilder::stringmap::iterator aFind = rMap.find("deletable");
925 if (aFind != rMap.end())
927 bCloseable = toBool(aFind->second);
928 rMap.erase(aFind);
930 return bCloseable;
933 bool extractEntry(VclBuilder::stringmap &rMap)
935 bool bHasEntry = false;
936 VclBuilder::stringmap::iterator aFind = rMap.find("has-entry");
937 if (aFind != rMap.end())
939 bHasEntry = toBool(aFind->second);
940 rMap.erase(aFind);
942 return bHasEntry;
945 bool extractOrientation(VclBuilder::stringmap &rMap)
947 bool bVertical = false;
948 VclBuilder::stringmap::iterator aFind = rMap.find("orientation");
949 if (aFind != rMap.end())
951 bVertical = aFind->second.equalsIgnoreAsciiCase("vertical");
952 rMap.erase(aFind);
954 return bVertical;
957 bool extractVerticalTabPos(VclBuilder::stringmap &rMap)
959 bool bVertical = false;
960 VclBuilder::stringmap::iterator aFind = rMap.find("tab-pos");
961 if (aFind != rMap.end())
963 bVertical = aFind->second.equalsIgnoreAsciiCase("left") ||
964 aFind->second.equalsIgnoreAsciiCase("right");
965 rMap.erase(aFind);
967 return bVertical;
970 bool extractInconsistent(VclBuilder::stringmap &rMap)
972 bool bInconsistent = false;
973 VclBuilder::stringmap::iterator aFind = rMap.find("inconsistent");
974 if (aFind != rMap.end())
976 bInconsistent = toBool(aFind->second);
977 rMap.erase(aFind);
979 return bInconsistent;
982 OUString extractIconName(VclBuilder::stringmap &rMap)
984 OUString sIconName;
985 VclBuilder::stringmap::iterator aFind = rMap.find(OString("icon-name"));
986 if (aFind != rMap.end())
988 sIconName = aFind->second;
989 rMap.erase(aFind);
991 return sIconName;
994 OUString getStockText(const OUString &rType)
996 if (rType == "gtk-ok")
997 return VclResId(SV_BUTTONTEXT_OK);
998 else if (rType == "gtk-cancel")
999 return VclResId(SV_BUTTONTEXT_CANCEL);
1000 else if (rType == "gtk-help")
1001 return VclResId(SV_BUTTONTEXT_HELP);
1002 else if (rType == "gtk-close")
1003 return VclResId(SV_BUTTONTEXT_CLOSE);
1004 else if (rType == "gtk-revert-to-saved")
1005 return VclResId(SV_BUTTONTEXT_RESET);
1006 else if (rType == "gtk-add")
1007 return VclResId(SV_BUTTONTEXT_ADD);
1008 else if (rType == "gtk-delete")
1009 return VclResId(SV_BUTTONTEXT_DELETE);
1010 else if (rType == "gtk-remove")
1011 return VclResId(SV_BUTTONTEXT_REMOVE);
1012 else if (rType == "gtk-new")
1013 return VclResId(SV_BUTTONTEXT_NEW);
1014 else if (rType == "gtk-edit")
1015 return VclResId(SV_BUTTONTEXT_EDIT);
1016 else if (rType == "gtk-apply")
1017 return VclResId(SV_BUTTONTEXT_APPLY);
1018 else if (rType == "gtk-save")
1019 return VclResId(SV_BUTTONTEXT_SAVE);
1020 else if (rType == "gtk-open")
1021 return VclResId(SV_BUTTONTEXT_OPEN);
1022 else if (rType == "gtk-undo")
1023 return VclResId(SV_BUTTONTEXT_UNDO);
1024 else if (rType == "gtk-paste")
1025 return VclResId(SV_BUTTONTEXT_PASTE);
1026 else if (rType == "gtk-media-next")
1027 return VclResId(SV_BUTTONTEXT_NEXT);
1028 else if (rType == "gtk-media-previous")
1029 return VclResId(SV_BUTTONTEXT_PREV);
1030 else if (rType == "gtk-go-up")
1031 return VclResId(SV_BUTTONTEXT_GO_UP);
1032 else if (rType == "gtk-go-down")
1033 return VclResId(SV_BUTTONTEXT_GO_DOWN);
1034 else if (rType == "gtk-clear")
1035 return VclResId(SV_BUTTONTEXT_CLEAR);
1036 else if (rType == "gtk-media-play")
1037 return VclResId(SV_BUTTONTEXT_PLAY);
1038 else if (rType == "gtk-find")
1039 return VclResId(SV_BUTTONTEXT_FIND);
1040 else if (rType == "gtk-stop")
1041 return VclResId(SV_BUTTONTEXT_STOP);
1042 else if (rType == "gtk-connect")
1043 return VclResId(SV_BUTTONTEXT_CONNECT);
1044 else if (rType == "gtk-yes")
1045 return VclResId(SV_BUTTONTEXT_YES);
1046 else if (rType == "gtk-no")
1047 return VclResId(SV_BUTTONTEXT_NO);
1048 SAL_WARN("vcl.layout", "unknown stock type: " << rType);
1049 return OUString();
1052 bool extractStock(VclBuilder::stringmap &rMap)
1054 bool bIsStock = false;
1055 VclBuilder::stringmap::iterator aFind = rMap.find(OString("use-stock"));
1056 if (aFind != rMap.end())
1058 bIsStock = toBool(aFind->second);
1059 rMap.erase(aFind);
1061 return bIsStock;
1064 WinBits extractRelief(VclBuilder::stringmap &rMap)
1066 WinBits nBits = WB_3DLOOK;
1067 VclBuilder::stringmap::iterator aFind = rMap.find(OString("relief"));
1068 if (aFind != rMap.end())
1070 if (aFind->second == "half")
1071 nBits = WB_FLATBUTTON | WB_BEVELBUTTON;
1072 else if (aFind->second == "none")
1073 nBits = WB_FLATBUTTON;
1074 rMap.erase(aFind);
1076 return nBits;
1079 OUString extractLabel(VclBuilder::stringmap &rMap)
1081 OUString sType;
1082 VclBuilder::stringmap::iterator aFind = rMap.find(OString("label"));
1083 if (aFind != rMap.end())
1085 sType = aFind->second;
1086 rMap.erase(aFind);
1088 return sType;
1091 OUString extractActionName(VclBuilder::stringmap &rMap)
1093 OUString sActionName;
1094 VclBuilder::stringmap::iterator aFind = rMap.find(OString("action-name"));
1095 if (aFind != rMap.end())
1097 sActionName = aFind->second;
1098 rMap.erase(aFind);
1100 return sActionName;
1103 bool extractVisible(VclBuilder::stringmap &rMap)
1105 VclBuilder::stringmap::iterator aFind = rMap.find(OString("visible"));
1106 if (aFind != rMap.end())
1108 return toBool(aFind->second);
1110 return false;
1113 Size extractSizeRequest(VclBuilder::stringmap &rMap)
1115 OUString sWidthRequest("0");
1116 OUString sHeightRequest("0");
1117 VclBuilder::stringmap::iterator aFind = rMap.find(OString("width-request"));
1118 if (aFind != rMap.end())
1120 sWidthRequest = aFind->second;
1121 rMap.erase(aFind);
1123 aFind = rMap.find("height-request");
1124 if (aFind != rMap.end())
1126 sHeightRequest = aFind->second;
1127 rMap.erase(aFind);
1129 return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32());
1132 OUString extractTooltipText(VclBuilder::stringmap &rMap)
1134 OUString sTooltipText;
1135 VclBuilder::stringmap::iterator aFind = rMap.find(OString("tooltip-text"));
1136 if (aFind == rMap.end())
1137 aFind = rMap.find(OString("tooltip-markup"));
1138 if (aFind != rMap.end())
1140 sTooltipText = aFind->second;
1141 rMap.erase(aFind);
1143 return sTooltipText;
1146 float extractAlignment(VclBuilder::stringmap &rMap)
1148 float f = 0.0;
1149 VclBuilder::stringmap::iterator aFind = rMap.find(OString("alignment"));
1150 if (aFind != rMap.end())
1152 f = aFind->second.toFloat();
1153 rMap.erase(aFind);
1155 return f;
1158 OUString extractTitle(VclBuilder::stringmap &rMap)
1160 OUString sTitle;
1161 VclBuilder::stringmap::iterator aFind = rMap.find(OString("title"));
1162 if (aFind != rMap.end())
1164 sTitle = aFind->second;
1165 rMap.erase(aFind);
1167 return sTitle;
1170 bool extractHeadersVisible(VclBuilder::stringmap &rMap)
1172 bool bHeadersVisible = true;
1173 VclBuilder::stringmap::iterator aFind = rMap.find(OString("headers-visible"));
1174 if (aFind != rMap.end())
1176 bHeadersVisible = toBool(aFind->second);
1177 rMap.erase(aFind);
1179 return bHeadersVisible;
1182 bool extractSortIndicator(VclBuilder::stringmap &rMap)
1184 bool bSortIndicator = false;
1185 VclBuilder::stringmap::iterator aFind = rMap.find(OString("sort-indicator"));
1186 if (aFind != rMap.end())
1188 bSortIndicator = toBool(aFind->second);
1189 rMap.erase(aFind);
1191 return bSortIndicator;
1194 bool extractClickable(VclBuilder::stringmap &rMap)
1196 bool bClickable = false;
1197 VclBuilder::stringmap::iterator aFind = rMap.find(OString("clickable"));
1198 if (aFind != rMap.end())
1200 bClickable = toBool(aFind->second);
1201 rMap.erase(aFind);
1203 return bClickable;
1206 void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame)
1208 if (!rFrame.is())
1209 return;
1211 OUString aCommand(extractActionName(rMap));
1212 if (aCommand.isEmpty())
1213 return;
1215 OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
1216 auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCommand, aModuleName);
1217 OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aProperties));
1218 if (!aLabel.isEmpty())
1219 pButton->SetText(aLabel);
1221 OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(aCommand, aProperties, rFrame));
1222 if (!aTooltip.isEmpty())
1223 pButton->SetQuickHelpText(aTooltip);
1225 Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, rFrame));
1226 pButton->SetModeImage(aImage);
1228 pButton->SetCommandHandler(aCommand);
1231 VclPtr<Button> extractStockAndBuildPushButton(vcl::Window *pParent, VclBuilder::stringmap &rMap, bool bToggle, bool bLegacy)
1233 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER;
1234 if (bToggle)
1235 nBits |= WB_TOGGLE;
1237 nBits |= extractRelief(rMap);
1239 VclPtr<Button> xWindow;
1241 if (extractStock(rMap))
1243 OUString sType = extractLabel(rMap);
1244 if (bLegacy)
1246 if (sType == "gtk-ok")
1247 xWindow = VclPtr<OKButton>::Create(pParent, nBits);
1248 else if (sType == "gtk-cancel")
1249 xWindow = VclPtr<CancelButton>::Create(pParent, nBits);
1250 else if (sType == "gtk-close")
1251 xWindow = VclPtr<CloseButton>::Create(pParent, nBits);
1252 else if (sType == "gtk-help")
1253 xWindow = VclPtr<HelpButton>::Create(pParent, nBits);
1255 if (!xWindow)
1257 xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1258 xWindow->SetText(getStockText(sType));
1262 if (!xWindow)
1263 xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1264 return xWindow;
1267 VclPtr<MenuButton> extractStockAndBuildMenuButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1269 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1271 nBits |= extractRelief(rMap);
1273 VclPtr<MenuButton> xWindow = VclPtr<MenuButton>::Create(pParent, nBits);
1275 if (extractStock(rMap))
1277 xWindow->SetText(getStockText(extractLabel(rMap)));
1280 return xWindow;
1283 VclPtr<Button> extractStockAndBuildMenuToggleButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1285 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1287 nBits |= extractRelief(rMap);
1289 VclPtr<Button> xWindow = VclPtr<MenuToggleButton>::Create(pParent, nBits);
1291 if (extractStock(rMap))
1293 xWindow->SetText(getStockText(extractLabel(rMap)));
1296 return xWindow;
1299 OUString extractUnit(const OUString& sPattern)
1301 OUString sUnit(sPattern);
1302 for (sal_Int32 i = 0; i < sPattern.getLength(); ++i)
1304 if (sPattern[i] != '.' && sPattern[i] != ',' && sPattern[i] != '0')
1306 sUnit = sPattern.copy(i);
1307 break;
1310 return sUnit;
1313 int extractDecimalDigits(const OUString& sPattern)
1315 int nDigits = 0;
1316 bool bAfterPoint = false;
1317 for (sal_Int32 i = 0; i < sPattern.getLength(); ++i)
1319 if (sPattern[i] == '.' || sPattern[i] == ',')
1320 bAfterPoint = true;
1321 else if (sPattern[i] == '0')
1323 if (bAfterPoint)
1324 ++nDigits;
1326 else
1327 break;
1329 return nDigits;
1332 FieldUnit detectMetricUnit(const OUString& sUnit)
1334 FieldUnit eUnit = FieldUnit::NONE;
1336 if (sUnit == "mm")
1337 eUnit = FieldUnit::MM;
1338 else if (sUnit == "cm")
1339 eUnit = FieldUnit::CM;
1340 else if (sUnit == "m")
1341 eUnit = FieldUnit::M;
1342 else if (sUnit == "km")
1343 eUnit = FieldUnit::KM;
1344 else if ((sUnit == "twips") || (sUnit == "twip"))
1345 eUnit = FieldUnit::TWIP;
1346 else if (sUnit == "pt")
1347 eUnit = FieldUnit::POINT;
1348 else if (sUnit == "pc")
1349 eUnit = FieldUnit::PICA;
1350 else if (sUnit == "\"" || (sUnit == "in") || (sUnit == "inch"))
1351 eUnit = FieldUnit::INCH;
1352 else if ((sUnit == "'") || (sUnit == "ft") || (sUnit == "foot") || (sUnit == "feet"))
1353 eUnit = FieldUnit::FOOT;
1354 else if (sUnit == "mile" || (sUnit == "miles"))
1355 eUnit = FieldUnit::MILE;
1356 else if (sUnit == "ch")
1357 eUnit = FieldUnit::CHAR;
1358 else if (sUnit == "line")
1359 eUnit = FieldUnit::LINE;
1360 else if (sUnit == "%")
1361 eUnit = FieldUnit::PERCENT;
1362 else if ((sUnit == "pixels") || (sUnit == "pixel") || (sUnit == "px"))
1363 eUnit = FieldUnit::PIXEL;
1364 else if ((sUnit == "degrees") || (sUnit == "degree"))
1365 eUnit = FieldUnit::DEGREE;
1366 else if ((sUnit == "sec") || (sUnit == "seconds") || (sUnit == "second"))
1367 eUnit = FieldUnit::SECOND;
1368 else if ((sUnit == "ms") || (sUnit == "milliseconds") || (sUnit == "millisecond"))
1369 eUnit = FieldUnit::MILLISECOND;
1370 else if (sUnit != "0")
1371 eUnit = FieldUnit::CUSTOM;
1373 return eUnit;
1376 WinBits extractDeferredBits(VclBuilder::stringmap &rMap)
1378 WinBits nBits = WB_3DLOOK|WB_HIDE;
1379 if (extractResizable(rMap))
1380 nBits |= WB_SIZEABLE;
1381 if (extractCloseable(rMap))
1382 nBits |= WB_CLOSEABLE;
1383 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
1384 if (!sBorder.isEmpty())
1385 nBits |= WB_BORDER;
1386 if (!extractDecorated(rMap))
1387 nBits |= WB_OWNERDRAWDECORATION;
1388 OUString sType(extractTypeHint(rMap));
1389 if (sType == "utility")
1390 nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_MOVEABLE;
1391 else if (sType == "popup-menu")
1392 nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_POPUP;
1393 else if (sType == "dock")
1394 nBits |= WB_DOCKABLE | WB_MOVEABLE;
1395 else
1396 nBits |= WB_MOVEABLE;
1397 return nBits;
1401 void VclBuilder::extractGroup(const OString &id, stringmap &rMap)
1403 VclBuilder::stringmap::iterator aFind = rMap.find(OString("group"));
1404 if (aFind != rMap.end())
1406 OUString sID = aFind->second;
1407 sal_Int32 nDelim = sID.indexOf(':');
1408 if (nDelim != -1)
1409 sID = sID.copy(0, nDelim);
1410 m_pParserState->m_aGroupMaps.emplace_back(id, sID.toUtf8());
1411 rMap.erase(aFind);
1415 void VclBuilder::connectNumericFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1417 if (!rAdjustment.isEmpty())
1418 m_pParserState->m_aNumericFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1421 void VclBuilder::connectFormattedFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1423 if (!rAdjustment.isEmpty())
1424 m_pParserState->m_aFormattedFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1427 void VclBuilder::connectTimeFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1429 if (!rAdjustment.isEmpty())
1430 m_pParserState->m_aTimeFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1433 void VclBuilder::connectDateFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1435 if (!rAdjustment.isEmpty())
1436 m_pParserState->m_aDateFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1439 bool VclBuilder::extractAdjustmentToMap(const OString& id, VclBuilder::stringmap& rMap, std::vector<WidgetAdjustmentMap>& rAdjustmentMap)
1441 VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1442 if (aFind != rMap.end())
1444 rAdjustmentMap.emplace_back(id, aFind->second);
1445 rMap.erase(aFind);
1446 return true;
1448 return false;
1451 namespace
1453 sal_Int32 extractActive(VclBuilder::stringmap &rMap)
1455 sal_Int32 nActiveId = 0;
1456 VclBuilder::stringmap::iterator aFind = rMap.find(OString("active"));
1457 if (aFind != rMap.end())
1459 nActiveId = aFind->second.toInt32();
1460 rMap.erase(aFind);
1462 return nActiveId;
1465 bool extractSelectable(VclBuilder::stringmap &rMap)
1467 bool bSelectable = false;
1468 VclBuilder::stringmap::iterator aFind = rMap.find(OString("selectable"));
1469 if (aFind != rMap.end())
1471 bSelectable = toBool(aFind->second);
1472 rMap.erase(aFind);
1474 return bSelectable;
1477 OUString extractAdjustment(VclBuilder::stringmap &rMap)
1479 OUString sAdjustment;
1480 VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1481 if (aFind != rMap.end())
1483 sAdjustment= aFind->second;
1484 rMap.erase(aFind);
1485 return sAdjustment;
1487 return sAdjustment;
1490 bool extractDrawIndicator(VclBuilder::stringmap &rMap)
1492 bool bDrawIndicator = false;
1493 VclBuilder::stringmap::iterator aFind = rMap.find(OString("draw-indicator"));
1494 if (aFind != rMap.end())
1496 bDrawIndicator = toBool(aFind->second);
1497 rMap.erase(aFind);
1499 return bDrawIndicator;
1503 void VclBuilder::extractModel(const OString &id, stringmap &rMap)
1505 VclBuilder::stringmap::iterator aFind = rMap.find(OString("model"));
1506 if (aFind != rMap.end())
1508 m_pParserState->m_aModelMaps.emplace_back(id, aFind->second,
1509 extractActive(rMap));
1510 rMap.erase(aFind);
1514 void VclBuilder::extractBuffer(const OString &id, stringmap &rMap)
1516 VclBuilder::stringmap::iterator aFind = rMap.find(OString("buffer"));
1517 if (aFind != rMap.end())
1519 m_pParserState->m_aTextBufferMaps.emplace_back(id, aFind->second);
1520 rMap.erase(aFind);
1524 void VclBuilder::extractStock(const OString &id, stringmap &rMap)
1526 VclBuilder::stringmap::iterator aFind = rMap.find(OString("stock"));
1527 if (aFind != rMap.end())
1529 stockinfo aInfo;
1530 aInfo.m_sStock = aFind->second;
1531 rMap.erase(aFind);
1532 aFind = rMap.find(OString("icon-size"));
1533 if (aFind != rMap.end())
1535 aInfo.m_nSize = aFind->second.toInt32();
1536 rMap.erase(aFind);
1538 m_pParserState->m_aStockMap[id] = aInfo;
1542 void VclBuilder::extractButtonImage(const OString &id, stringmap &rMap, bool bRadio)
1544 VclBuilder::stringmap::iterator aFind = rMap.find(OString("image"));
1545 if (aFind != rMap.end())
1547 m_pParserState->m_aButtonImageWidgetMaps.emplace_back(id, aFind->second, bRadio);
1548 rMap.erase(aFind);
1552 void VclBuilder::extractMnemonicWidget(const OString &rLabelID, stringmap &rMap)
1554 VclBuilder::stringmap::iterator aFind = rMap.find(OString("mnemonic-widget"));
1555 if (aFind != rMap.end())
1557 OUString sID = aFind->second;
1558 sal_Int32 nDelim = sID.indexOf(':');
1559 if (nDelim != -1)
1560 sID = sID.copy(0, nDelim);
1561 m_pParserState->m_aMnemonicWidgetMaps.emplace_back(rLabelID, sID);
1562 rMap.erase(aFind);
1566 vcl::Window* VclBuilder::prepareWidgetOwnScrolling(vcl::Window *pParent, WinBits &rWinStyle)
1568 //For Widgets that manage their own scrolling, if one appears as a child of
1569 //a scrolling window shoehorn that scrolling settings to this widget and
1570 //return the real parent to use
1571 if (pParent && pParent->GetType() == WindowType::SCROLLWINDOW)
1573 WinBits nScrollBits = pParent->GetStyle();
1574 nScrollBits &= (WB_AUTOHSCROLL|WB_HSCROLL|WB_AUTOVSCROLL|WB_VSCROLL);
1575 rWinStyle |= nScrollBits | WB_BORDER;
1576 pParent = pParent->GetParent();
1579 return pParent;
1582 void VclBuilder::cleanupWidgetOwnScrolling(vcl::Window *pScrollParent, vcl::Window *pWindow, stringmap &rMap)
1584 //remove the redundant scrolling parent
1585 sal_Int32 nWidthReq = pScrollParent->get_width_request();
1586 rMap[OString("width-request")] = OUString::number(nWidthReq);
1587 sal_Int32 nHeightReq = pScrollParent->get_height_request();
1588 rMap[OString("height-request")] = OUString::number(nHeightReq);
1590 m_pParserState->m_aRedundantParentWidgets[pScrollParent] = pWindow;
1593 #ifndef DISABLE_DYNLOADING
1595 extern "C" { static void thisModule() {} }
1597 // Don't unload the module on destruction
1598 class NoAutoUnloadModule : public osl::Module
1600 public:
1601 ~NoAutoUnloadModule() { release(); }
1604 typedef std::map<OUString, std::shared_ptr<NoAutoUnloadModule>> ModuleMap;
1605 static ModuleMap g_aModuleMap;
1607 #if ENABLE_MERGELIBS
1608 static std::shared_ptr<NoAutoUnloadModule> g_pMergedLib = std::make_shared<NoAutoUnloadModule>();
1609 #endif
1611 #ifndef SAL_DLLPREFIX
1612 # define SAL_DLLPREFIX ""
1613 #endif
1615 #endif
1617 void VclBuilder::preload()
1619 #ifndef DISABLE_DYNLOADING
1621 #if ENABLE_MERGELIBS
1622 g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1623 #else
1624 // find -name '*ui*' | xargs grep 'class=".*lo-' |
1625 // sed 's/.*class="//' | sed 's/-.*$//' | sort | uniq
1626 static const char *aWidgetLibs[] = {
1627 "sfxlo", "svtlo", "svxcorelo", "foruilo",
1628 "vcllo", "svxlo", "cuilo", "swlo",
1629 "swuilo", "sclo", "sdlo", "chartcontrollerlo",
1630 "smlo", "scuilo", "basctllo", "sduilo",
1631 "scnlo", "xsltdlglo", "pcrlo" // "dbulo"
1633 for (const auto & lib : aWidgetLibs)
1635 std::unique_ptr<NoAutoUnloadModule> pModule(new NoAutoUnloadModule);
1636 OUString sModule = SAL_DLLPREFIX + OUString::createFromAscii(lib) + SAL_DLLEXTENSION;
1637 if (pModule->loadRelative(&thisModule, sModule))
1638 g_aModuleMap.insert(std::make_pair(sModule, std::move(pModule)));
1640 #endif // ENABLE_MERGELIBS
1641 #endif // DISABLE_DYNLOADING
1644 #if defined DISABLE_DYNLOADING && !HAVE_FEATURE_DESKTOP
1645 extern "C" VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name);
1646 #endif
1648 namespace
1650 // Takes a string like "sfxlo-SidebarToolBox"
1651 VclBuilder::customMakeWidget GetCustomMakeWidget(const OString& name)
1653 VclBuilder::customMakeWidget pFunction = nullptr;
1654 if (sal_Int32 nDelim = name.indexOf('-'); nDelim != -1)
1656 const OUString sFunction("make"
1657 + OStringToOUString(name.copy(nDelim + 1), RTL_TEXTENCODING_UTF8));
1659 #ifndef DISABLE_DYNLOADING
1660 const OUString sModule = SAL_DLLPREFIX
1661 + OStringToOUString(name.copy(0, nDelim), RTL_TEXTENCODING_UTF8)
1662 + SAL_DLLEXTENSION;
1663 ModuleMap::iterator aI = g_aModuleMap.find(sModule);
1664 if (aI == g_aModuleMap.end())
1666 std::shared_ptr<NoAutoUnloadModule> pModule;
1667 #if ENABLE_MERGELIBS
1668 if (!g_pMergedLib->is())
1669 g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1670 if ((pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1671 g_pMergedLib->getFunctionSymbol(sFunction))))
1672 pModule = g_pMergedLib;
1673 #endif
1674 if (!pFunction)
1676 pModule.reset(new NoAutoUnloadModule);
1677 bool ok = pModule->loadRelative(&thisModule, sModule);
1678 assert(ok && "bad module name in .ui");
1679 (void)ok;
1680 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1681 pModule->getFunctionSymbol(sFunction));
1683 g_aModuleMap.insert(std::make_pair(sModule, pModule));
1685 else
1686 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1687 aI->second->getFunctionSymbol(sFunction));
1688 #elif !HAVE_FEATURE_DESKTOP
1689 pFunction = lo_get_custom_widget_func(sFunction.toUtf8().getStr());
1690 SAL_WARN_IF(!pFunction, "vcl.layout", "Could not find " << sFunction);
1691 assert(pFunction);
1692 #else
1693 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1694 osl_getFunctionSymbol((oslModule)RTLD_DEFAULT, sFunction.pData));
1695 #endif
1697 return pFunction;
1701 VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString &name, const OString &id,
1702 stringmap &rMap)
1704 bool bIsPlaceHolder = name.isEmpty();
1705 bool bVertical = false;
1707 if (pParent && (pParent->GetType() == WindowType::TABCONTROL ||
1708 pParent->GetType() == WindowType::VERTICALTABCONTROL))
1710 bool bTopLevel(name == "GtkDialog" || name == "GtkMessageDialog" ||
1711 name == "GtkWindow" || name == "GtkPopover" || name == "GtkAssistant");
1712 if (!bTopLevel)
1714 if (pParent->GetType() == WindowType::TABCONTROL)
1716 //We have to add a page
1717 //make default pageid == position
1718 TabControl *pTabControl = static_cast<TabControl*>(pParent);
1719 sal_uInt16 nNewPageCount = pTabControl->GetPageCount()+1;
1720 sal_uInt16 nNewPageId = nNewPageCount;
1721 pTabControl->InsertPage(nNewPageId, OUString());
1722 pTabControl->SetCurPageId(nNewPageId);
1723 SAL_WARN_IF(bIsPlaceHolder, "vcl.layout", "we should have no placeholders for tabpages");
1724 if (!bIsPlaceHolder)
1726 VclPtrInstance<TabPage> pPage(pTabControl);
1727 pPage->Show();
1729 //Make up a name for it
1730 OString sTabPageId = get_by_window(pParent) +
1731 "-page" +
1732 OString::number(nNewPageCount);
1733 m_aChildren.emplace_back(sTabPageId, pPage, false);
1734 pPage->SetHelpId(m_sHelpRoot + sTabPageId);
1736 pParent = pPage;
1738 pTabControl->SetTabPage(nNewPageId, pPage);
1741 else
1743 VerticalTabControl *pTabControl = static_cast<VerticalTabControl*>(pParent);
1744 SAL_WARN_IF(bIsPlaceHolder, "vcl.layout", "we should have no placeholders for tabpages");
1745 if (!bIsPlaceHolder)
1746 pParent = pTabControl->GetPageParent();
1751 if (bIsPlaceHolder || name == "GtkTreeSelection")
1752 return nullptr;
1754 extractButtonImage(id, rMap, name == "GtkRadioButton");
1756 VclPtr<vcl::Window> xWindow;
1757 if (name == "GtkDialog" || name == "GtkAboutDialog" || name == "GtkAssistant")
1759 // WB_ALLOWMENUBAR because we don't know in advance if we will encounter
1760 // a menubar, and menubars need a BorderWindow in the toplevel, and
1761 // such border windows need to be in created during the dialog ctor
1762 WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_ALLOWMENUBAR;
1763 if (extractResizable(rMap))
1764 nBits |= WB_SIZEABLE;
1765 if (extractCloseable(rMap))
1766 nBits |= WB_CLOSEABLE;
1767 Dialog::InitFlag eInit = !pParent ? Dialog::InitFlag::NoParent : Dialog::InitFlag::Default;
1768 if (name == "GtkAssistant")
1769 xWindow = VclPtr<vcl::RoadmapWizard>::Create(pParent, nBits, eInit);
1770 else if (name == "GtkAboutDialog")
1771 xWindow = VclPtr<vcl::AboutDialog>::Create(pParent, nBits, eInit);
1772 else
1773 xWindow = VclPtr<Dialog>::Create(pParent, nBits, eInit);
1774 #if HAVE_FEATURE_DESKTOP
1775 if (!m_bLegacy && !extractModal(rMap))
1776 xWindow->SetType(WindowType::MODELESSDIALOG);
1777 #endif
1779 else if (name == "GtkMessageDialog")
1781 WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE;
1782 if (extractResizable(rMap))
1783 nBits |= WB_SIZEABLE;
1784 VclPtr<MessageDialog> xDialog(VclPtr<MessageDialog>::Create(pParent, nBits));
1785 m_pParserState->m_aMessageDialogs.push_back(xDialog);
1786 xWindow = xDialog;
1787 #if defined WNT
1788 xWindow->set_border_width(3);
1789 #else
1790 xWindow->set_border_width(12);
1791 #endif
1793 else if (name == "GtkBox" || name == "GtkStatusbar")
1795 bVertical = extractOrientation(rMap);
1796 if (bVertical)
1797 xWindow = VclPtr<VclVBox>::Create(pParent);
1798 else
1799 xWindow = VclPtr<VclHBox>::Create(pParent);
1801 else if (name == "GtkPaned")
1803 bVertical = extractOrientation(rMap);
1804 if (bVertical)
1805 xWindow = VclPtr<VclVPaned>::Create(pParent);
1806 else
1807 xWindow = VclPtr<VclHPaned>::Create(pParent);
1809 else if (name == "GtkHBox")
1810 xWindow = VclPtr<VclHBox>::Create(pParent);
1811 else if (name == "GtkVBox")
1812 xWindow = VclPtr<VclVBox>::Create(pParent);
1813 else if (name == "GtkButtonBox")
1815 bVertical = extractOrientation(rMap);
1816 if (bVertical)
1817 xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1818 else
1819 xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1821 else if (name == "GtkHButtonBox")
1822 xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1823 else if (name == "GtkVButtonBox")
1824 xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1825 else if (name == "GtkGrid")
1826 xWindow = VclPtr<VclGrid>::Create(pParent);
1827 else if (name == "GtkFrame")
1828 xWindow = VclPtr<VclFrame>::Create(pParent);
1829 else if (name == "GtkExpander")
1831 VclPtrInstance<VclExpander> pExpander(pParent);
1832 m_pParserState->m_aExpanderWidgets.push_back(pExpander);
1833 xWindow = pExpander;
1835 else if (name == "GtkAlignment")
1836 xWindow = VclPtr<VclAlignment>::Create(pParent);
1837 else if (name == "GtkButton" || (!m_bLegacy && name == "GtkToggleButton"))
1839 VclPtr<Button> xButton;
1840 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1841 if (sMenu.isEmpty())
1842 xButton = extractStockAndBuildPushButton(pParent, rMap, name == "GtkToggleButton", m_bLegacy);
1843 else
1845 assert(m_bLegacy && "use GtkMenuButton");
1846 xButton = extractStockAndBuildMenuButton(pParent, rMap);
1847 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1849 xButton->SetImageAlign(ImageAlign::Left); //default to left
1850 setupFromActionName(xButton, rMap, m_xFrame);
1851 xWindow = xButton;
1853 else if (name == "GtkMenuButton")
1855 VclPtr<MenuButton> xButton = extractStockAndBuildMenuButton(pParent, rMap);
1856 OUString sMenu = extractPopupMenu(rMap);
1857 if (!sMenu.isEmpty())
1858 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1859 xButton->SetImageAlign(ImageAlign::Left); //default to left
1860 xButton->SetAccessibleRole(css::accessibility::AccessibleRole::BUTTON_MENU);
1862 if (!extractDrawIndicator(rMap))
1863 xButton->SetDropDown(PushButtonDropdownStyle::NONE);
1865 setupFromActionName(xButton, rMap, m_xFrame);
1866 xWindow = xButton;
1868 else if (name == "GtkToggleButton" && m_bLegacy)
1870 VclPtr<Button> xButton;
1871 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1872 assert(sMenu.getLength() && "not implemented yet");
1873 xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
1874 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1875 xButton->SetImageAlign(ImageAlign::Left); //default to left
1876 setupFromActionName(xButton, rMap, m_xFrame);
1877 xWindow = xButton;
1879 else if (name == "GtkRadioButton")
1881 extractGroup(id, rMap);
1882 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1883 OUString sWrap = BuilderUtils::extractCustomProperty(rMap);
1884 if (!sWrap.isEmpty())
1885 nBits |= WB_WORDBREAK;
1886 VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, nBits);
1887 xButton->SetImageAlign(ImageAlign::Left); //default to left
1888 xWindow = xButton;
1890 if (::extractStock(rMap))
1892 xWindow->SetText(getStockText(extractLabel(rMap)));
1895 else if (name == "GtkCheckButton")
1897 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1898 OUString sWrap = BuilderUtils::extractCustomProperty(rMap);
1899 if (!sWrap.isEmpty())
1900 nBits |= WB_WORDBREAK;
1901 //maybe always import as TriStateBox and enable/disable tristate
1902 bool bIsTriState = extractInconsistent(rMap);
1903 VclPtr<CheckBox> xCheckBox;
1904 if (bIsTriState && m_bLegacy)
1905 xCheckBox = VclPtr<TriStateBox>::Create(pParent, nBits);
1906 else
1907 xCheckBox = VclPtr<CheckBox>::Create(pParent, nBits);
1908 if (bIsTriState)
1910 xCheckBox->EnableTriState(true);
1911 xCheckBox->SetState(TRISTATE_INDET);
1913 xCheckBox->SetImageAlign(ImageAlign::Left); //default to left
1915 xWindow = xCheckBox;
1917 if (::extractStock(rMap))
1919 xWindow->SetText(getStockText(extractLabel(rMap)));
1922 else if (name == "GtkSpinButton")
1924 OUString sAdjustment = extractAdjustment(rMap);
1925 OUString sPattern = BuilderUtils::extractCustomProperty(rMap);
1926 OUString sUnit = extractUnit(sPattern);
1928 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_BORDER|WB_3DLOOK;
1929 if (!id.endsWith("-nospin"))
1930 nBits |= WB_SPIN | WB_REPEAT;
1932 if (sPattern.isEmpty())
1934 SAL_INFO("vcl.layout", "making numeric field for " << name << " " << sUnit);
1935 if (m_bLegacy)
1937 connectNumericFormatterAdjustment(id, sAdjustment);
1938 xWindow = VclPtr<NumericField>::Create(pParent, nBits);
1940 else
1942 connectFormattedFormatterAdjustment(id, sAdjustment);
1943 VclPtrInstance<FormattedField> xField(pParent, nBits);
1944 xField->SetMinValue(0);
1945 xWindow = xField;
1948 else
1950 if (sPattern == "hh:mm")
1952 connectTimeFormatterAdjustment(id, sAdjustment);
1953 SAL_INFO("vcl.layout", "making time field for " << name << " " << sUnit);
1954 xWindow = VclPtr<TimeField>::Create(pParent, nBits);
1956 else if (sPattern == "yy:mm:dd")
1958 connectDateFormatterAdjustment(id, sAdjustment);
1959 SAL_INFO("vcl.layout", "making date field for " << name << " " << sUnit);
1960 xWindow = VclPtr<DateField>::Create(pParent, nBits);
1962 else
1964 connectNumericFormatterAdjustment(id, sAdjustment);
1965 FieldUnit eUnit = detectMetricUnit(sUnit);
1966 SAL_INFO("vcl.layout", "making metric field for " << name << " " << sUnit);
1967 VclPtrInstance<MetricField> xField(pParent, nBits);
1968 xField->SetUnit(eUnit);
1969 if (eUnit == FieldUnit::CUSTOM)
1970 xField->SetCustomUnitText(sUnit);
1971 xWindow = xField;
1975 else if (name == "GtkLinkButton")
1976 xWindow = VclPtr<FixedHyperlink>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_NOLABEL);
1977 else if (name == "GtkComboBox" || name == "GtkComboBoxText")
1979 OUString sPattern = BuilderUtils::extractCustomProperty(rMap);
1980 extractModel(id, rMap);
1982 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1984 bool bDropdown = BuilderUtils::extractDropdown(rMap);
1986 if (bDropdown)
1987 nBits |= WB_DROPDOWN;
1989 if (!sPattern.isEmpty())
1991 OUString sAdjustment = extractAdjustment(rMap);
1992 connectNumericFormatterAdjustment(id, sAdjustment);
1993 OUString sUnit = extractUnit(sPattern);
1994 FieldUnit eUnit = detectMetricUnit(sUnit);
1995 SAL_WARN("vcl.layout", "making metric box for type: " << name
1996 << " unit: " << sUnit
1997 << " name: " << id
1998 << " use a VclComboBoxNumeric instead");
1999 VclPtrInstance<MetricBox> xBox(pParent, nBits);
2000 xBox->EnableAutoSize(true);
2001 xBox->SetUnit(eUnit);
2002 xBox->SetDecimalDigits(extractDecimalDigits(sPattern));
2003 if (eUnit == FieldUnit::CUSTOM)
2004 xBox->SetCustomUnitText(sUnit);
2005 xWindow = xBox;
2007 else if (extractEntry(rMap))
2009 VclPtrInstance<ComboBox> xComboBox(pParent, nBits);
2010 xComboBox->EnableAutoSize(true);
2011 xWindow = xComboBox;
2013 else
2015 VclPtrInstance<ListBox> xListBox(pParent, nBits|WB_SIMPLEMODE);
2016 xListBox->EnableAutoSize(true);
2017 xWindow = xListBox;
2020 else if (name == "VclComboBoxNumeric")
2022 OUString sPattern = BuilderUtils::extractCustomProperty(rMap);
2023 OUString sAdjustment = extractAdjustment(rMap);
2024 extractModel(id, rMap);
2026 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
2028 bool bDropdown = BuilderUtils::extractDropdown(rMap);
2030 if (bDropdown)
2031 nBits |= WB_DROPDOWN;
2033 if (!sPattern.isEmpty())
2035 connectNumericFormatterAdjustment(id, sAdjustment);
2036 OUString sUnit = extractUnit(sPattern);
2037 FieldUnit eUnit = detectMetricUnit(sUnit);
2038 SAL_INFO("vcl.layout", "making metric box for " << name << " " << sUnit);
2039 VclPtrInstance<MetricBox> xBox(pParent, nBits);
2040 xBox->EnableAutoSize(true);
2041 xBox->SetUnit(eUnit);
2042 xBox->SetDecimalDigits(extractDecimalDigits(sPattern));
2043 if (eUnit == FieldUnit::CUSTOM)
2044 xBox->SetCustomUnitText(sUnit);
2045 xWindow = xBox;
2047 else
2049 SAL_INFO("vcl.layout", "making numeric box for " << name);
2050 connectNumericFormatterAdjustment(id, sAdjustment);
2051 VclPtrInstance<NumericBox> xBox(pParent, nBits);
2052 if (bDropdown)
2053 xBox->EnableAutoSize(true);
2054 xWindow = xBox;
2057 else if (name == "GtkIconView")
2059 assert(rMap.find(OString("model")) != rMap.end() && "GtkIconView must have a model");
2061 //window we want to apply the packing props for this GtkIconView to
2062 VclPtr<vcl::Window> xWindowForPackingProps;
2063 extractModel(id, rMap);
2064 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
2065 //IconView manages its own scrolling,
2066 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2067 if (pRealParent != pParent)
2068 nWinStyle |= WB_BORDER;
2070 VclPtr<IconView> xBox = VclPtr<IconView>::Create(pRealParent, nWinStyle);
2071 xWindowForPackingProps = xBox;
2073 xWindow = xBox;
2074 xBox->SetNoAutoCurEntry(true);
2075 xBox->SetQuickSearch(true);
2077 if (pRealParent != pParent)
2078 cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
2080 else if (name == "GtkTreeView")
2082 if (!m_bLegacy)
2084 assert(rMap.find(OString("model")) != rMap.end() && "GtkTreeView must have a model");
2087 //window we want to apply the packing props for this GtkTreeView to
2088 VclPtr<vcl::Window> xWindowForPackingProps;
2089 //To-Do
2090 //a) make SvHeaderTabListBox/SvTabListBox the default target for GtkTreeView
2091 //b) remove the non-drop down mode of ListBox and convert
2092 // everything over to SvHeaderTabListBox/SvTabListBox
2093 //c) remove the users of makeSvTabListBox and makeSvTreeListBox
2094 extractModel(id, rMap);
2095 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
2096 if (m_bLegacy)
2098 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2099 if (!sBorder.isEmpty())
2100 nWinStyle |= WB_BORDER;
2102 else
2104 nWinStyle |= WB_HASBUTTONS | WB_HASBUTTONSATROOT;
2106 //ListBox/SvHeaderTabListBox manages its own scrolling,
2107 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2108 if (pRealParent != pParent)
2109 nWinStyle |= WB_BORDER;
2110 if (m_bLegacy)
2112 xWindow = VclPtr<ListBox>::Create(pRealParent, nWinStyle | WB_SIMPLEMODE);
2113 xWindowForPackingProps = xWindow;
2115 else
2117 VclPtr<SvTabListBox> xBox;
2118 bool bHeadersVisible = extractHeadersVisible(rMap);
2119 if (bHeadersVisible)
2121 VclPtr<VclVBox> xContainer = VclPtr<VclVBox>::Create(pRealParent);
2122 OString containerid(id + "-container");
2123 xContainer->SetHelpId(m_sHelpRoot + containerid);
2124 m_aChildren.emplace_back(containerid, xContainer, true);
2126 VclPtrInstance<HeaderBar> xHeader(xContainer, WB_BUTTONSTYLE | WB_BORDER | WB_TABSTOP | WB_3DLOOK);
2127 xHeader->set_width_request(0); // let the headerbar width not affect the size request
2128 OString headerid(id + "-header");
2129 xHeader->SetHelpId(m_sHelpRoot + headerid);
2130 m_aChildren.emplace_back(headerid, xHeader, true);
2132 VclPtr<LclHeaderTabListBox> xHeaderBox = VclPtr<LclHeaderTabListBox>::Create(xContainer, nWinStyle);
2133 xHeaderBox->InitHeaderBar(xHeader);
2134 xContainer->set_expand(true);
2135 xHeader->Show();
2136 xContainer->Show();
2137 xBox = xHeaderBox;
2138 xWindowForPackingProps = xContainer;
2140 else
2142 xBox = VclPtr<LclTabListBox>::Create(pRealParent, nWinStyle);
2143 xWindowForPackingProps = xBox;
2145 xWindow = xBox;
2146 xBox->SetNoAutoCurEntry(true);
2147 xBox->SetQuickSearch(true);
2148 xBox->SetSpaceBetweenEntries(3);
2149 xBox->SetEntryHeight(16);
2150 xBox->SetHighlightRange(); // select over the whole width
2152 if (pRealParent != pParent)
2153 cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
2155 else if (name == "GtkTreeViewColumn")
2157 if (!m_bLegacy)
2159 SvHeaderTabListBox* pTreeView = dynamic_cast<SvHeaderTabListBox*>(pParent);
2160 if (HeaderBar* pHeaderBar = pTreeView ? pTreeView->GetHeaderBar() : nullptr)
2162 HeaderBarItemBits nBits = HeaderBarItemBits::LEFTIMAGE;
2163 if (extractClickable(rMap))
2164 nBits |= HeaderBarItemBits::CLICKABLE;
2165 if (extractSortIndicator(rMap))
2166 nBits |= HeaderBarItemBits::DOWNARROW;
2167 float fAlign = extractAlignment(rMap);
2168 if (fAlign == 0.0)
2169 nBits |= HeaderBarItemBits::LEFT;
2170 else if (fAlign == 1.0)
2171 nBits |= HeaderBarItemBits::RIGHT;
2172 else if (fAlign == 0.5)
2173 nBits |= HeaderBarItemBits::CENTER;
2174 auto nItemId = pHeaderBar->GetItemCount() + 1;
2175 OUString sTitle(extractTitle(rMap));
2176 pHeaderBar->InsertItem(nItemId, sTitle, 100, nBits);
2180 else if (name == "GtkLabel")
2182 WinBits nWinStyle = WB_CENTER|WB_VCENTER|WB_3DLOOK;
2183 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2184 if (!sBorder.isEmpty())
2185 nWinStyle |= WB_BORDER;
2186 extractMnemonicWidget(id, rMap);
2187 if (extractSelectable(rMap))
2188 xWindow = VclPtr<SelectableFixedText>::Create(pParent, nWinStyle);
2189 else
2190 xWindow = VclPtr<FixedText>::Create(pParent, nWinStyle);
2192 else if (name == "GtkImage")
2194 extractStock(id, rMap);
2195 xWindow = VclPtr<FixedImage>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_SCALE);
2196 //such parentless GtkImages are temps used to set icons on buttons
2197 //default them to hidden to stop e.g. insert->index entry flicking temp
2198 //full screen windows
2199 if (!pParent)
2201 rMap["visible"] = "false";
2204 else if (name == "GtkSeparator")
2206 bVertical = extractOrientation(rMap);
2207 xWindow = VclPtr<FixedLine>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2209 else if (name == "GtkScrollbar")
2211 extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2212 bVertical = extractOrientation(rMap);
2213 xWindow = VclPtr<ScrollBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2215 else if (name == "GtkProgressBar")
2217 extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2218 bVertical = extractOrientation(rMap);
2219 xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2221 else if (name == "GtkScrolledWindow")
2223 xWindow = VclPtr<VclScrolledWindow>::Create(pParent);
2225 else if (name == "GtkViewport")
2227 xWindow = VclPtr<VclViewport>::Create(pParent);
2229 else if (name == "GtkEventBox")
2231 xWindow = VclPtr<VclEventBox>::Create(pParent);
2233 else if (name == "GtkEntry")
2235 xWindow = VclPtr<Edit>::Create(pParent, WB_LEFT|WB_VCENTER|WB_BORDER|WB_3DLOOK);
2236 BuilderUtils::ensureDefaultWidthChars(rMap);
2238 else if (name == "GtkNotebook")
2240 if (!extractVerticalTabPos(rMap))
2241 xWindow = VclPtr<TabControl>::Create(pParent, WB_STDTABCONTROL|WB_3DLOOK);
2242 else
2243 xWindow = VclPtr<VerticalTabControl>::Create(pParent);
2245 else if (name == "GtkDrawingArea")
2247 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2248 xWindow = VclPtr<VclDrawingArea>::Create(pParent, sBorder.isEmpty() ? WB_TABSTOP : WB_BORDER | WB_TABSTOP);
2250 else if (name == "GtkTextView")
2252 extractBuffer(id, rMap);
2254 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT;
2255 if (m_bLegacy)
2257 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2258 if (!sBorder.isEmpty())
2259 nWinStyle |= WB_BORDER;
2261 //VclMultiLineEdit manages its own scrolling,
2262 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2263 if (pRealParent != pParent)
2264 nWinStyle |= WB_BORDER;
2265 xWindow = VclPtr<VclMultiLineEdit>::Create(pRealParent, nWinStyle);
2266 if (pRealParent != pParent)
2267 cleanupWidgetOwnScrolling(pParent, xWindow, rMap);
2269 else if (name == "GtkSpinner")
2271 xWindow = VclPtr<Throbber>::Create(pParent, WB_3DLOOK);
2273 else if (name == "GtkScale")
2275 extractAdjustmentToMap(id, rMap, m_pParserState->m_aSliderAdjustmentMaps);
2276 bool bDrawValue = extractDrawValue(rMap);
2277 if (bDrawValue)
2279 OUString sValuePos = extractValuePos(rMap);
2280 (void)sValuePos;
2282 bVertical = extractOrientation(rMap);
2284 WinBits nWinStyle = bVertical ? WB_VERT : WB_HORZ;
2286 xWindow = VclPtr<Slider>::Create(pParent, nWinStyle);
2288 else if (name == "GtkToolbar")
2290 xWindow = VclPtr<ToolBox>::Create(pParent, WB_3DLOOK | WB_TABSTOP);
2292 else if(name == "NotebookBarAddonsToolMergePoint")
2294 customMakeWidget pFunction = GetCustomMakeWidget("sfxlo-NotebookbarToolBox");
2295 if(pFunction != nullptr)
2296 NotebookBarAddonsMerger::MergeNotebookBarAddons(pParent, pFunction, m_xFrame, *m_pNotebookBarAddonsItem, rMap);
2297 return nullptr;
2299 else if (name == "GtkToolButton" || name == "GtkMenuToolButton" ||
2300 name == "GtkToggleToolButton" || name == "GtkRadioToolButton")
2302 ToolBox *pToolBox = dynamic_cast<ToolBox*>(pParent);
2303 if (pToolBox)
2305 OUString aCommand(extractActionName(rMap));
2307 sal_uInt16 nItemId = 0;
2308 ToolBoxItemBits nBits = ToolBoxItemBits::NONE;
2309 if (name == "GtkMenuToolButton")
2310 nBits |= ToolBoxItemBits::DROPDOWN;
2311 else if (name == "GtkToggleToolButton")
2312 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::CHECKABLE;
2313 else if (name == "GtkRadioToolButton")
2314 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::RADIOCHECK;
2316 if (!aCommand.isEmpty() && m_xFrame.is())
2318 pToolBox->InsertItem(aCommand, m_xFrame, nBits, extractSizeRequest(rMap));
2319 nItemId = pToolBox->GetItemId(aCommand);
2321 else
2323 nItemId = pToolBox->GetItemCount() + 1;
2324 //TODO: ImplToolItems::size_type -> sal_uInt16!
2325 pToolBox->InsertItem(nItemId, extractLabel(rMap), nBits);
2326 if (aCommand.isEmpty() && !m_bLegacy)
2327 aCommand = OUString::fromUtf8(id);
2328 pToolBox->SetItemCommand(nItemId, aCommand);
2331 pToolBox->SetHelpId(nItemId, m_sHelpRoot + id);
2332 OUString sTooltip(extractTooltipText(rMap));
2333 if (!sTooltip.isEmpty())
2334 pToolBox->SetQuickHelpText(nItemId, sTooltip);
2336 OUString sIconName(extractIconName(rMap));
2337 if (!sIconName.isEmpty())
2338 pToolBox->SetItemImage(nItemId, FixedImage::loadThemeImage(sIconName));
2340 if (!extractVisible(rMap))
2341 pToolBox->HideItem(nItemId);
2343 m_pParserState->m_nLastToolbarId = nItemId;
2345 return nullptr; // no widget to be created
2348 else if (name == "GtkSeparatorToolItem")
2350 ToolBox *pToolBox = dynamic_cast<ToolBox*>(pParent);
2351 if (pToolBox)
2353 pToolBox->InsertSeparator();
2354 return nullptr; // no widget to be created
2357 else if (name == "GtkWindow")
2359 WinBits nBits = extractDeferredBits(rMap);
2360 if (nBits & WB_DOCKABLE)
2361 xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2362 else
2363 xWindow = VclPtr<FloatingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2365 else if (name == "GtkPopover")
2367 WinBits nBits = extractDeferredBits(rMap);
2368 xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_DOCKABLE|WB_MOVEABLE);
2370 else if (name == "GtkCalendar")
2372 WinBits nBits = extractDeferredBits(rMap);
2373 xWindow = VclPtr<Calendar>::Create(pParent, nBits);
2375 else
2377 if (customMakeWidget pFunction = GetCustomMakeWidget(name))
2379 pFunction(xWindow, pParent, rMap);
2380 if (xWindow->GetType() == WindowType::PUSHBUTTON)
2381 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2382 else if (xWindow->GetType() == WindowType::MENUBUTTON)
2384 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
2385 if (!sMenu.isEmpty())
2386 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
2387 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2391 SAL_INFO_IF(!xWindow, "vcl.layout", "probably need to implement " << name << " or add a make" << name << " function");
2392 if (xWindow)
2394 xWindow->SetHelpId(m_sHelpRoot + id);
2395 SAL_INFO("vcl.layout", "for " << name <<
2396 ", created " << xWindow.get() << " child of " <<
2397 pParent << "(" << xWindow->ImplGetWindowImpl()->mpParent.get() << "/" <<
2398 xWindow->ImplGetWindowImpl()->mpRealParent.get() << "/" <<
2399 xWindow->ImplGetWindowImpl()->mpBorderWindow.get() << ") with helpid " <<
2400 xWindow->GetHelpId());
2401 m_aChildren.emplace_back(id, xWindow, bVertical);
2403 return xWindow;
2406 namespace
2408 //return true for window types which exist in vcl but are not themselves
2409 //represented in the .ui format, i.e. only their children exist.
2410 bool isConsideredGtkPseudo(vcl::Window const *pWindow)
2412 return pWindow->GetType() == WindowType::TABPAGE;
2416 //Any properties from .ui load we couldn't set because of potential virtual methods
2417 //during ctor are applied here
2418 void VclBuilder::setDeferredProperties()
2420 if (!m_bToplevelHasDeferredProperties)
2421 return;
2422 stringmap aDeferredProperties;
2423 aDeferredProperties.swap(m_aDeferredProperties);
2424 m_bToplevelHasDeferredProperties = false;
2425 BuilderUtils::set_properties(m_pParent, aDeferredProperties);
2428 namespace BuilderUtils
2430 void set_properties(vcl::Window *pWindow, const VclBuilder::stringmap &rProps)
2432 for (auto const& prop : rProps)
2434 const OString &rKey = prop.first;
2435 const OUString &rValue = prop.second;
2436 pWindow->set_property(rKey, rValue);
2440 OUString convertMnemonicMarkup(const OUString &rIn)
2442 OUStringBuffer aRet(rIn);
2443 for (sal_Int32 nI = 0; nI < aRet.getLength(); ++nI)
2445 if (aRet[nI] == '_' && nI+1 < aRet.getLength())
2447 if (aRet[nI+1] != '_')
2448 aRet[nI] = MNEMONIC_CHAR;
2449 else
2450 aRet.remove(nI, 1);
2451 ++nI;
2454 return aRet.makeStringAndClear();
2457 OUString extractCustomProperty(VclBuilder::stringmap &rMap)
2459 OUString sCustomProperty;
2460 VclBuilder::stringmap::iterator aFind = rMap.find(OString("customproperty"));
2461 if (aFind != rMap.end())
2463 sCustomProperty = aFind->second;
2464 rMap.erase(aFind);
2466 return sCustomProperty;
2469 FieldUnit detectUnit(OUString const& rString)
2471 OUString const unit(extractUnit(rString));
2472 return detectMetricUnit(unit);
2475 void ensureDefaultWidthChars(VclBuilder::stringmap &rMap)
2477 OString sWidthChars("width-chars");
2478 VclBuilder::stringmap::iterator aFind = rMap.find(sWidthChars);
2479 if (aFind == rMap.end())
2480 rMap[sWidthChars] = "25";
2483 bool extractDropdown(VclBuilder::stringmap &rMap)
2485 bool bDropdown = true;
2486 VclBuilder::stringmap::iterator aFind = rMap.find(OString("dropdown"));
2487 if (aFind != rMap.end())
2489 bDropdown = toBool(aFind->second);
2490 rMap.erase(aFind);
2492 return bDropdown;
2495 void reorderWithinParent(vcl::Window &rWindow, sal_uInt16 nNewPosition)
2497 WindowImpl *pWindowImpl = rWindow.ImplGetWindowImpl();
2498 if (pWindowImpl->mpParent != pWindowImpl->mpRealParent)
2500 assert(pWindowImpl->mpBorderWindow == pWindowImpl->mpParent);
2501 assert(pWindowImpl->mpBorderWindow->ImplGetWindowImpl()->mpParent == pWindowImpl->mpRealParent);
2502 reorderWithinParent(*pWindowImpl->mpBorderWindow, nNewPosition);
2503 return;
2505 rWindow.reorderWithinParent(nNewPosition);
2508 void reorderWithinParent(std::vector<vcl::Window*>& rChilds, bool bIsButtonBox)
2510 for (size_t i = 0; i < rChilds.size(); ++i)
2512 reorderWithinParent(*rChilds[i], i);
2514 if (!bIsButtonBox)
2515 continue;
2517 //The first member of the group for legacy code needs WB_GROUP set and the
2518 //others not
2519 WinBits nBits = rChilds[i]->GetStyle();
2520 nBits &= ~WB_GROUP;
2521 if (i == 0)
2522 nBits |= WB_GROUP;
2523 rChilds[i]->SetStyle(nBits);
2527 sal_Int16 getRoleFromName(const OString& roleName)
2529 using namespace com::sun::star::accessibility;
2531 static const std::unordered_map<OString, sal_Int16> aAtkRoleToAccessibleRole = {
2532 /* This is in atkobject.h's AtkRole order */
2533 { "invalid", AccessibleRole::UNKNOWN },
2534 { "accelerator label", AccessibleRole::UNKNOWN },
2535 { "alert", AccessibleRole::ALERT },
2536 { "animation", AccessibleRole::UNKNOWN },
2537 { "arrow", AccessibleRole::UNKNOWN },
2538 { "calendar", AccessibleRole::UNKNOWN },
2539 { "canvas", AccessibleRole::CANVAS },
2540 { "check box", AccessibleRole::CHECK_BOX },
2541 { "check menu item", AccessibleRole::CHECK_MENU_ITEM },
2542 { "color chooser", AccessibleRole::COLOR_CHOOSER },
2543 { "column header", AccessibleRole::COLUMN_HEADER },
2544 { "combo box", AccessibleRole::COMBO_BOX },
2545 { "date editor", AccessibleRole::DATE_EDITOR },
2546 { "desktop icon", AccessibleRole::DESKTOP_ICON },
2547 { "desktop frame", AccessibleRole::DESKTOP_PANE }, // ?
2548 { "dial", AccessibleRole::UNKNOWN },
2549 { "dialog", AccessibleRole::DIALOG },
2550 { "directory pane", AccessibleRole::DIRECTORY_PANE },
2551 { "drawing area", AccessibleRole::UNKNOWN },
2552 { "file chooser", AccessibleRole::FILE_CHOOSER },
2553 { "filler", AccessibleRole::FILLER },
2554 { "font chooser", AccessibleRole::FONT_CHOOSER },
2555 { "frame", AccessibleRole::FRAME },
2556 { "glass pane", AccessibleRole::GLASS_PANE },
2557 { "html container", AccessibleRole::UNKNOWN },
2558 { "icon", AccessibleRole::ICON },
2559 { "image", AccessibleRole::GRAPHIC },
2560 { "internal frame", AccessibleRole::INTERNAL_FRAME },
2561 { "label", AccessibleRole::LABEL },
2562 { "layered pane", AccessibleRole::LAYERED_PANE },
2563 { "list", AccessibleRole::LIST },
2564 { "list item", AccessibleRole::LIST_ITEM },
2565 { "menu", AccessibleRole::MENU },
2566 { "menu bar", AccessibleRole::MENU_BAR },
2567 { "menu item", AccessibleRole::MENU_ITEM },
2568 { "option pane", AccessibleRole::OPTION_PANE },
2569 { "page tab", AccessibleRole::PAGE_TAB },
2570 { "page tab list", AccessibleRole::PAGE_TAB_LIST },
2571 { "panel", AccessibleRole::PANEL }, // or SHAPE or TEXT_FRAME ?
2572 { "password text", AccessibleRole::PASSWORD_TEXT },
2573 { "popup menu", AccessibleRole::POPUP_MENU },
2574 { "progress bar", AccessibleRole::PROGRESS_BAR },
2575 { "push button", AccessibleRole::PUSH_BUTTON }, // or BUTTON_DROPDOWN or BUTTON_MENU
2576 { "radio button", AccessibleRole::RADIO_BUTTON },
2577 { "radio menu item", AccessibleRole::RADIO_MENU_ITEM },
2578 { "root pane", AccessibleRole::ROOT_PANE },
2579 { "row header", AccessibleRole::ROW_HEADER },
2580 { "scroll bar", AccessibleRole::SCROLL_BAR },
2581 { "scroll pane", AccessibleRole::SCROLL_PANE },
2582 { "separator", AccessibleRole::SEPARATOR },
2583 { "slider", AccessibleRole::SLIDER },
2584 { "split pane", AccessibleRole::SPLIT_PANE },
2585 { "spin button", AccessibleRole::SPIN_BOX }, // ?
2586 { "statusbar", AccessibleRole::STATUS_BAR },
2587 { "table", AccessibleRole::TABLE },
2588 { "table cell", AccessibleRole::TABLE_CELL },
2589 { "table column header", AccessibleRole::COLUMN_HEADER }, // approximate
2590 { "table row header", AccessibleRole::ROW_HEADER }, // approximate
2591 { "tear off menu item", AccessibleRole::UNKNOWN },
2592 { "terminal", AccessibleRole::UNKNOWN },
2593 { "text", AccessibleRole::TEXT },
2594 { "toggle button", AccessibleRole::TOGGLE_BUTTON },
2595 { "tool bar", AccessibleRole::TOOL_BAR },
2596 { "tool tip", AccessibleRole::TOOL_TIP },
2597 { "tree", AccessibleRole::TREE },
2598 { "tree table", AccessibleRole::TREE_TABLE },
2599 { "unknown", AccessibleRole::UNKNOWN },
2600 { "viewport", AccessibleRole::VIEW_PORT },
2601 { "window", AccessibleRole::WINDOW },
2602 { "header", AccessibleRole::HEADER },
2603 { "footer", AccessibleRole::FOOTER },
2604 { "paragraph", AccessibleRole::PARAGRAPH },
2605 { "ruler", AccessibleRole::RULER },
2606 { "application", AccessibleRole::UNKNOWN },
2607 { "autocomplete", AccessibleRole::UNKNOWN },
2608 { "edit bar", AccessibleRole::EDIT_BAR },
2609 { "embedded", AccessibleRole::EMBEDDED_OBJECT },
2610 { "entry", AccessibleRole::UNKNOWN },
2611 { "chart", AccessibleRole::CHART },
2612 { "caption", AccessibleRole::CAPTION },
2613 { "document frame", AccessibleRole::DOCUMENT },
2614 { "heading", AccessibleRole::HEADING },
2615 { "page", AccessibleRole::PAGE },
2616 { "section", AccessibleRole::SECTION },
2617 { "redundant object", AccessibleRole::UNKNOWN },
2618 { "form", AccessibleRole::FORM },
2619 { "link", AccessibleRole::HYPER_LINK },
2620 { "input method window", AccessibleRole::UNKNOWN },
2621 { "table row", AccessibleRole::UNKNOWN },
2622 { "tree item", AccessibleRole::TREE_ITEM },
2623 { "document spreadsheet", AccessibleRole::DOCUMENT_SPREADSHEET },
2624 { "document presentation", AccessibleRole::DOCUMENT_PRESENTATION },
2625 { "document text", AccessibleRole::DOCUMENT_TEXT },
2626 { "document web", AccessibleRole::DOCUMENT }, // approximate
2627 { "document email", AccessibleRole::DOCUMENT }, // approximate
2628 { "comment", AccessibleRole::COMMENT }, // or NOTE or END_NOTE or FOOTNOTE or SCROLL_PANE
2629 { "list box", AccessibleRole::UNKNOWN },
2630 { "grouping", AccessibleRole::GROUP_BOX },
2631 { "image map", AccessibleRole::IMAGE_MAP },
2632 { "notification", AccessibleRole::UNKNOWN },
2633 { "info bar", AccessibleRole::UNKNOWN },
2634 { "level bar", AccessibleRole::UNKNOWN },
2635 { "title bar", AccessibleRole::UNKNOWN },
2636 { "block quote", AccessibleRole::UNKNOWN },
2637 { "audio", AccessibleRole::UNKNOWN },
2638 { "video", AccessibleRole::UNKNOWN },
2639 { "definition", AccessibleRole::UNKNOWN },
2640 { "article", AccessibleRole::UNKNOWN },
2641 { "landmark", AccessibleRole::UNKNOWN },
2642 { "log", AccessibleRole::UNKNOWN },
2643 { "marquee", AccessibleRole::UNKNOWN },
2644 { "math", AccessibleRole::UNKNOWN },
2645 { "rating", AccessibleRole::UNKNOWN },
2646 { "timer", AccessibleRole::UNKNOWN },
2647 { "description list", AccessibleRole::UNKNOWN },
2648 { "description term", AccessibleRole::UNKNOWN },
2649 { "description value", AccessibleRole::UNKNOWN },
2650 { "static", AccessibleRole::STATIC },
2651 { "math fraction", AccessibleRole::UNKNOWN },
2652 { "math root", AccessibleRole::UNKNOWN },
2653 { "subscript", AccessibleRole::UNKNOWN },
2654 { "superscript", AccessibleRole::UNKNOWN },
2655 { "footnote", AccessibleRole::FOOTNOTE },
2658 auto it = aAtkRoleToAccessibleRole.find(roleName);
2659 if (it == aAtkRoleToAccessibleRole.end())
2660 return AccessibleRole::UNKNOWN;
2661 return it->second;
2665 VclPtr<vcl::Window> VclBuilder::insertObject(vcl::Window *pParent, const OString &rClass,
2666 const OString &rID, stringmap &rProps, stringmap &rPango, stringmap &rAtk)
2668 VclPtr<vcl::Window> pCurrentChild;
2670 if (m_pParent && !isConsideredGtkPseudo(m_pParent) && !m_sID.isEmpty() && rID == m_sID)
2672 pCurrentChild = m_pParent;
2674 //toplevels default to resizable and apparently you can't change them
2675 //afterwards, so we need to wait until now before we can truly
2676 //initialize the dialog.
2677 if (pParent && pParent->IsSystemWindow())
2679 SystemWindow *pSysWin = static_cast<SystemWindow*>(pCurrentChild.get());
2680 pSysWin->doDeferredInit(extractDeferredBits(rProps));
2681 m_bToplevelHasDeferredInit = false;
2683 else if (pParent && pParent->IsDockingWindow())
2685 DockingWindow *pDockWin = static_cast<DockingWindow*>(pCurrentChild.get());
2686 pDockWin->doDeferredInit(extractDeferredBits(rProps));
2687 m_bToplevelHasDeferredInit = false;
2690 if (pCurrentChild->GetHelpId().isEmpty())
2692 pCurrentChild->SetHelpId(m_sHelpRoot + m_sID);
2693 SAL_INFO("vcl.layout", "for toplevel dialog " << this << " " <<
2694 rID << ", set helpid " << pCurrentChild->GetHelpId());
2696 m_bToplevelParentFound = true;
2698 else
2700 //if we're being inserting under a toplevel dialog whose init is
2701 //deferred due to waiting to encounter it in this .ui, and it hasn't
2702 //been seen yet, then make unattached widgets parent-less toplevels
2703 if (pParent == m_pParent.get() && m_bToplevelHasDeferredInit)
2704 pParent = nullptr;
2705 pCurrentChild = makeObject(pParent, rClass, rID, rProps);
2708 if (pCurrentChild)
2710 pCurrentChild->set_id(OStringToOUString(rID, RTL_TEXTENCODING_UTF8));
2711 if (pCurrentChild == m_pParent.get() && m_bToplevelHasDeferredProperties)
2712 m_aDeferredProperties = rProps;
2713 else
2714 BuilderUtils::set_properties(pCurrentChild, rProps);
2716 for (auto const& elem : rPango)
2718 const OString &rKey = elem.first;
2719 const OUString &rValue = elem.second;
2720 pCurrentChild->set_font_attribute(rKey, rValue);
2723 m_pParserState->m_aAtkInfo[pCurrentChild] = rAtk;
2726 rProps.clear();
2727 rPango.clear();
2728 rAtk.clear();
2730 if (!pCurrentChild)
2731 pCurrentChild = m_aChildren.empty() ? pParent : m_aChildren.back().m_pWindow.get();
2732 return pCurrentChild;
2735 void VclBuilder::handleTabChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
2737 std::vector<OString> sIDs;
2739 int nLevel = 1;
2740 stringmap aProperties;
2741 std::vector<vcl::EnumContext::Context> context;
2743 while(true)
2745 xmlreader::Span name;
2746 int nsId;
2748 xmlreader::XmlReader::Result res = reader.nextItem(
2749 xmlreader::XmlReader::Text::NONE, &name, &nsId);
2751 if (res == xmlreader::XmlReader::Result::Begin)
2753 ++nLevel;
2754 if (name == "object")
2756 while (reader.nextAttribute(&nsId, &name))
2758 if (name == "id")
2760 name = reader.getAttributeValue(false);
2761 OString sID(name.begin, name.length);
2762 sal_Int32 nDelim = sID.indexOf(':');
2763 if (nDelim != -1)
2765 OString sPattern = sID.copy(nDelim+1);
2766 aProperties[OString("customproperty")] = OUString::fromUtf8(sPattern);
2767 sID = sID.copy(0, nDelim);
2769 sIDs.push_back(sID);
2773 else if (name == "style")
2775 int nPriority = 0;
2776 context = handleStyle(reader, nPriority);
2777 --nLevel;
2779 else if (name == "property")
2780 collectProperty(reader, aProperties);
2783 if (res == xmlreader::XmlReader::Result::End)
2784 --nLevel;
2786 if (!nLevel)
2787 break;
2789 if (res == xmlreader::XmlReader::Result::Done)
2790 break;
2793 if (!pParent)
2794 return;
2796 TabControl *pTabControl = pParent->GetType() == WindowType::TABCONTROL ?
2797 static_cast<TabControl*>(pParent) : nullptr;
2798 VerticalTabControl *pVerticalTabControl = pParent->GetType() == WindowType::VERTICALTABCONTROL ?
2799 static_cast<VerticalTabControl*>(pParent) : nullptr;
2800 assert(pTabControl || pVerticalTabControl);
2801 VclBuilder::stringmap::iterator aFind = aProperties.find(OString("label"));
2802 if (aFind != aProperties.end())
2804 if (pTabControl)
2806 sal_uInt16 nPageId = pTabControl->GetCurPageId();
2807 pTabControl->SetPageText(nPageId, aFind->second);
2808 pTabControl->SetPageName(nPageId, sIDs.back());
2809 if (!context.empty())
2811 TabPage* pPage = pTabControl->GetTabPage(nPageId);
2812 pPage->SetContext(context);
2815 else
2817 OUString sLabel(aFind->second);
2818 OUString sIconName(extractIconName(aProperties));
2819 OUString sTooltip(extractTooltipText(aProperties));
2820 pVerticalTabControl->InsertPage(sIDs.front(), sLabel, FixedImage::loadThemeImage(sIconName), sTooltip,
2821 pVerticalTabControl->GetPageParent()->GetWindow(GetWindowType::LastChild));
2824 else
2826 if (pTabControl)
2827 pTabControl->RemovePage(pTabControl->GetCurPageId());
2831 //so that tabbing between controls goes in a visually sensible sequence
2832 //we sort these into a best-tab-order sequence
2833 bool VclBuilder::sortIntoBestTabTraversalOrder::operator()(const vcl::Window *pA, const vcl::Window *pB) const
2835 //sort child order within parent list by grid position
2836 sal_Int32 nTopA = pA->get_grid_top_attach();
2837 sal_Int32 nTopB = pB->get_grid_top_attach();
2838 if (nTopA < nTopB)
2839 return true;
2840 if (nTopA > nTopB)
2841 return false;
2842 sal_Int32 nLeftA = pA->get_grid_left_attach();
2843 sal_Int32 nLeftB = pB->get_grid_left_attach();
2844 if (nLeftA < nLeftB)
2845 return true;
2846 if (nLeftA > nLeftB)
2847 return false;
2848 //sort into two groups of pack start and pack end
2849 VclPackType ePackA = pA->get_pack_type();
2850 VclPackType ePackB = pB->get_pack_type();
2851 if (ePackA < ePackB)
2852 return true;
2853 if (ePackA > ePackB)
2854 return false;
2855 bool bVerticalContainer = m_pBuilder->get_window_packing_data(pA->GetParent()).m_bVerticalOrient;
2856 bool bPackA = pA->get_secondary();
2857 bool bPackB = pB->get_secondary();
2858 if (!bVerticalContainer)
2860 //for horizontal boxes group secondaries before primaries
2861 if (bPackA > bPackB)
2862 return true;
2863 if (bPackA < bPackB)
2864 return false;
2866 else
2868 //for vertical boxes group secondaries after primaries
2869 if (bPackA < bPackB)
2870 return true;
2871 if (bPackA > bPackB)
2872 return false;
2874 //honour relative box positions with pack group, (numerical order is reversed
2875 //for VclPackType::End, they are packed from the end back, but here we need
2876 //them in visual layout order so that tabbing works as expected)
2877 sal_Int32 nPackA = m_pBuilder->get_window_packing_data(pA).m_nPosition;
2878 sal_Int32 nPackB = m_pBuilder->get_window_packing_data(pB).m_nPosition;
2879 if (nPackA < nPackB)
2880 return ePackA == VclPackType::Start;
2881 if (nPackA > nPackB)
2882 return ePackA != VclPackType::Start;
2883 //sort labels of Frames before body
2884 if (pA->GetParent() == pB->GetParent())
2886 const VclFrame *pFrameParent = dynamic_cast<const VclFrame*>(pA->GetParent());
2887 if (pFrameParent)
2889 const vcl::Window *pLabel = pFrameParent->get_label_widget();
2890 int nFramePosA = (pA == pLabel) ? 0 : 1;
2891 int nFramePosB = (pB == pLabel) ? 0 : 1;
2892 return nFramePosA < nFramePosB;
2895 return false;
2898 void VclBuilder::handleChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
2900 vcl::Window *pCurrentChild = nullptr;
2902 xmlreader::Span name;
2903 int nsId;
2904 OString sType, sInternalChild;
2906 while (reader.nextAttribute(&nsId, &name))
2908 if (name == "type")
2910 name = reader.getAttributeValue(false);
2911 sType = OString(name.begin, name.length);
2913 else if (name == "internal-child")
2915 name = reader.getAttributeValue(false);
2916 sInternalChild = OString(name.begin, name.length);
2920 if (sType == "tab")
2922 handleTabChild(pParent, reader);
2923 return;
2926 int nLevel = 1;
2927 while(true)
2929 xmlreader::XmlReader::Result res = reader.nextItem(
2930 xmlreader::XmlReader::Text::NONE, &name, &nsId);
2932 if (res == xmlreader::XmlReader::Result::Begin)
2934 if (name == "object" || name == "placeholder")
2936 pCurrentChild = handleObject(pParent, reader).get();
2938 bool bObjectInserted = pCurrentChild && pParent != pCurrentChild;
2940 if (bObjectInserted)
2942 //Internal-children default in glade to not having their visible bits set
2943 //even though they are visible (generally anyway)
2944 if (!sInternalChild.isEmpty())
2945 pCurrentChild->Show();
2947 //Select the first page if it's a notebook
2948 if (pCurrentChild->GetType() == WindowType::TABCONTROL)
2950 TabControl *pTabControl = static_cast<TabControl*>(pCurrentChild);
2951 pTabControl->SetCurPageId(pTabControl->GetPageId(0));
2953 //To-Do add reorder capability to the TabControl
2955 else
2957 // We want to sort labels before contents of frames
2958 // for keyboard traversal, especially if there
2959 // are multiple widgets using the same mnemonic
2960 if (sType == "label")
2962 if (VclFrame *pFrameParent = dynamic_cast<VclFrame*>(pParent))
2963 pFrameParent->designate_label(pCurrentChild);
2965 if (sInternalChild.startsWith("vbox") || sInternalChild.startsWith("messagedialog-vbox"))
2967 if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pParent))
2968 pBoxParent->set_content_area(static_cast<VclBox*>(pCurrentChild)); // FIXME-VCLPTR
2970 else if (sInternalChild.startsWith("action_area") || sInternalChild.startsWith("messagedialog-action_area"))
2972 vcl::Window *pContentArea = pCurrentChild->GetParent();
2973 if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pContentArea ? pContentArea->GetParent() : nullptr))
2975 pBoxParent->set_action_area(static_cast<VclButtonBox*>(pCurrentChild)); // FIXME-VCLPTR
2979 bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pCurrentChild) != nullptr;
2981 //To-Do make reorder a virtual in Window, move this foo
2982 //there and see above
2983 std::vector<vcl::Window*> aChilds;
2984 for (vcl::Window* pChild = pCurrentChild->GetWindow(GetWindowType::FirstChild); pChild;
2985 pChild = pChild->GetWindow(GetWindowType::Next))
2987 if (bIsButtonBox)
2989 if (PushButton* pPushButton = dynamic_cast<PushButton*>(pChild))
2990 pPushButton->setAction(true);
2993 aChilds.push_back(pChild);
2996 //sort child order within parent so that tabbing
2997 //between controls goes in a visually sensible sequence
2998 std::stable_sort(aChilds.begin(), aChilds.end(), sortIntoBestTabTraversalOrder(this));
2999 BuilderUtils::reorderWithinParent(aChilds, bIsButtonBox);
3003 else if (name == "packing")
3005 handlePacking(pCurrentChild, pParent, reader);
3007 else if (name == "interface")
3009 while (reader.nextAttribute(&nsId, &name))
3011 if (name == "domain")
3013 name = reader.getAttributeValue(false);
3014 sType = OString(name.begin, name.length);
3015 m_pParserState->m_aResLocale = Translate::Create(sType.getStr());
3018 ++nLevel;
3020 else
3021 ++nLevel;
3024 if (res == xmlreader::XmlReader::Result::End)
3025 --nLevel;
3027 if (!nLevel)
3028 break;
3030 if (res == xmlreader::XmlReader::Result::Done)
3031 break;
3035 void VclBuilder::collectPangoAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3037 xmlreader::Span span;
3038 int nsId;
3040 OString sProperty;
3041 OString sValue;
3043 while (reader.nextAttribute(&nsId, &span))
3045 if (span == "name")
3047 span = reader.getAttributeValue(false);
3048 sProperty = OString(span.begin, span.length);
3050 else if (span == "value")
3052 span = reader.getAttributeValue(false);
3053 sValue = OString(span.begin, span.length);
3057 if (!sProperty.isEmpty())
3058 rMap[sProperty] = OUString::fromUtf8(sValue);
3061 void VclBuilder::collectAtkRelationAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3063 xmlreader::Span span;
3064 int nsId;
3066 OString sProperty;
3067 OString sValue;
3069 while (reader.nextAttribute(&nsId, &span))
3071 if (span == "type")
3073 span = reader.getAttributeValue(false);
3074 sProperty = OString(span.begin, span.length);
3076 else if (span == "target")
3078 span = reader.getAttributeValue(false);
3079 sValue = OString(span.begin, span.length);
3080 sal_Int32 nDelim = sValue.indexOf(':');
3081 if (nDelim != -1)
3082 sValue = sValue.copy(0, nDelim);
3086 if (!sProperty.isEmpty())
3087 rMap[sProperty] = OUString::fromUtf8(sValue);
3090 void VclBuilder::collectAtkRoleAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3092 xmlreader::Span span;
3093 int nsId;
3095 OString sProperty;
3097 while (reader.nextAttribute(&nsId, &span))
3099 if (span == "type")
3101 span = reader.getAttributeValue(false);
3102 sProperty = OString(span.begin, span.length);
3106 if (!sProperty.isEmpty())
3107 rMap["role"] = OUString::fromUtf8(sProperty);
3110 void VclBuilder::handleRow(xmlreader::XmlReader &reader, const OString &rID)
3112 int nLevel = 1;
3114 ListStore::row aRow;
3116 while(true)
3118 xmlreader::Span name;
3119 int nsId;
3121 xmlreader::XmlReader::Result res = reader.nextItem(
3122 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3124 if (res == xmlreader::XmlReader::Result::Done)
3125 break;
3127 if (res == xmlreader::XmlReader::Result::Begin)
3129 ++nLevel;
3130 if (name == "col")
3132 bool bTranslated = false;
3133 sal_uInt32 nId = 0;
3134 OString sContext;
3136 while (reader.nextAttribute(&nsId, &name))
3138 if (name == "id")
3140 name = reader.getAttributeValue(false);
3141 nId = OString(name.begin, name.length).toInt32();
3143 else if (nId == 0 && name == "translatable" && reader.getAttributeValue(false) == "yes")
3145 bTranslated = true;
3147 else if (name == "context")
3149 name = reader.getAttributeValue(false);
3150 sContext = OString(name.begin, name.length);
3154 reader.nextItem(
3155 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3157 OString sValue(name.begin, name.length);
3158 OUString sFinalValue;
3159 if (bTranslated)
3161 if (!sContext.isEmpty())
3162 sValue = sContext + "\004" + sValue;
3163 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3165 else
3166 sFinalValue = OUString::fromUtf8(sValue);
3169 if (aRow.size() < nId+1)
3170 aRow.resize(nId+1);
3171 aRow[nId] = sFinalValue;
3175 if (res == xmlreader::XmlReader::Result::End)
3177 --nLevel;
3180 if (!nLevel)
3181 break;
3184 m_pParserState->m_aModels[rID].m_aEntries.push_back(aRow);
3187 void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OString &rID, const OString &rClass)
3189 int nLevel = 1;
3191 while(true)
3193 xmlreader::Span name;
3194 int nsId;
3196 xmlreader::XmlReader::Result res = reader.nextItem(
3197 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3199 if (res == xmlreader::XmlReader::Result::Done)
3200 break;
3202 if (res == xmlreader::XmlReader::Result::Begin)
3204 if (name == "row")
3206 bool bNotTreeStore = rClass != "GtkTreeStore";
3207 if (bNotTreeStore)
3208 handleRow(reader, rID);
3209 assert(bNotTreeStore && "gtk, as the time of writing, doesn't support data in GtkTreeStore serialization");
3211 else
3212 ++nLevel;
3215 if (res == xmlreader::XmlReader::Result::End)
3217 --nLevel;
3220 if (!nLevel)
3221 break;
3225 void VclBuilder::handleAtkObject(xmlreader::XmlReader &reader, vcl::Window *pWindow)
3227 assert(pWindow);
3229 int nLevel = 1;
3231 stringmap aProperties;
3233 while(true)
3235 xmlreader::Span name;
3236 int nsId;
3238 xmlreader::XmlReader::Result res = reader.nextItem(
3239 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3241 if (res == xmlreader::XmlReader::Result::Done)
3242 break;
3244 if (res == xmlreader::XmlReader::Result::Begin)
3246 ++nLevel;
3247 if (name == "property")
3248 collectProperty(reader, aProperties);
3251 if (res == xmlreader::XmlReader::Result::End)
3253 --nLevel;
3256 if (!nLevel)
3257 break;
3260 for (auto const& prop : aProperties)
3262 const OString &rKey = prop.first;
3263 const OUString &rValue = prop.second;
3265 if (pWindow && rKey.match("AtkObject::"))
3266 pWindow->set_property(rKey.copy(RTL_CONSTASCII_LENGTH("AtkObject::")), rValue);
3267 else
3268 SAL_WARN("vcl.layout", "unhandled atk prop: " << rKey);
3272 std::vector<ComboBoxTextItem> VclBuilder::handleItems(xmlreader::XmlReader &reader) const
3274 int nLevel = 1;
3276 std::vector<ComboBoxTextItem> aItems;
3278 while(true)
3280 xmlreader::Span name;
3281 int nsId;
3283 xmlreader::XmlReader::Result res = reader.nextItem(
3284 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3286 if (res == xmlreader::XmlReader::Result::Done)
3287 break;
3289 if (res == xmlreader::XmlReader::Result::Begin)
3291 ++nLevel;
3292 if (name == "item")
3294 bool bTranslated = false;
3295 OString sContext, sId;
3297 while (reader.nextAttribute(&nsId, &name))
3299 if (name == "translatable" && reader.getAttributeValue(false) == "yes")
3301 bTranslated = true;
3303 else if (name == "context")
3305 name = reader.getAttributeValue(false);
3306 sContext = OString(name.begin, name.length);
3308 else if (name == "id")
3310 name = reader.getAttributeValue(false);
3311 sId = OString(name.begin, name.length);
3315 reader.nextItem(
3316 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3318 OString sValue(name.begin, name.length);
3319 OUString sFinalValue;
3320 if (bTranslated)
3322 if (!sContext.isEmpty())
3323 sValue = sContext + "\004" + sValue;
3324 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3326 else
3327 sFinalValue = OUString::fromUtf8(sValue);
3329 if (m_pStringReplace)
3330 sFinalValue = (*m_pStringReplace)(sFinalValue);
3332 aItems.emplace_back(sFinalValue, sId);
3336 if (res == xmlreader::XmlReader::Result::End)
3338 --nLevel;
3341 if (!nLevel)
3342 break;
3345 return aItems;
3348 VclPtr<Menu> VclBuilder::handleMenu(xmlreader::XmlReader &reader, const OString &rID, bool bMenuBar)
3350 VclPtr<Menu> pCurrentMenu;
3351 if (bMenuBar)
3352 pCurrentMenu = VclPtr<MenuBar>::Create();
3353 else
3354 pCurrentMenu = VclPtr<PopupMenu>::Create();
3356 int nLevel = 1;
3358 stringmap aProperties;
3360 while(true)
3362 xmlreader::Span name;
3363 int nsId;
3365 xmlreader::XmlReader::Result res = reader.nextItem(
3366 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3368 if (res == xmlreader::XmlReader::Result::Done)
3369 break;
3371 if (res == xmlreader::XmlReader::Result::Begin)
3373 if (name == "child")
3375 handleMenuChild(pCurrentMenu, reader);
3377 else
3379 ++nLevel;
3380 if (name == "property")
3381 collectProperty(reader, aProperties);
3385 if (res == xmlreader::XmlReader::Result::End)
3387 --nLevel;
3390 if (!nLevel)
3391 break;
3394 m_aMenus.emplace_back(rID, pCurrentMenu);
3396 return pCurrentMenu;
3399 void VclBuilder::handleMenuChild(Menu *pParent, xmlreader::XmlReader &reader)
3401 xmlreader::Span name;
3402 int nsId;
3404 int nLevel = 1;
3405 while(true)
3407 xmlreader::XmlReader::Result res = reader.nextItem(
3408 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3410 if (res == xmlreader::XmlReader::Result::Begin)
3412 if (name == "object" || name == "placeholder")
3414 handleMenuObject(pParent, reader);
3416 else
3417 ++nLevel;
3420 if (res == xmlreader::XmlReader::Result::End)
3421 --nLevel;
3423 if (!nLevel)
3424 break;
3426 if (res == xmlreader::XmlReader::Result::Done)
3427 break;
3431 void VclBuilder::handleMenuObject(Menu *pParent, xmlreader::XmlReader &reader)
3433 OString sClass;
3434 OString sID;
3435 OUString sCustomProperty;
3436 PopupMenu *pSubMenu = nullptr;
3438 xmlreader::Span name;
3439 int nsId;
3441 while (reader.nextAttribute(&nsId, &name))
3443 if (name == "class")
3445 name = reader.getAttributeValue(false);
3446 sClass = OString(name.begin, name.length);
3448 else if (name == "id")
3450 name = reader.getAttributeValue(false);
3451 sID = OString(name.begin, name.length);
3452 sal_Int32 nDelim = sID.indexOf(':');
3453 if (nDelim != -1)
3455 sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3456 sID = sID.copy(0, nDelim);
3461 int nLevel = 1;
3463 stringmap aProperties;
3464 accelmap aAccelerators;
3466 if (!sCustomProperty.isEmpty())
3467 aProperties[OString("customproperty")] = sCustomProperty;
3469 while(true)
3471 xmlreader::XmlReader::Result res = reader.nextItem(
3472 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3474 if (res == xmlreader::XmlReader::Result::Done)
3475 break;
3477 if (res == xmlreader::XmlReader::Result::Begin)
3479 if (name == "child")
3481 size_t nChildMenuIdx = m_aMenus.size();
3482 handleChild(nullptr, reader);
3483 assert(m_aMenus.size() > nChildMenuIdx && "menu not inserted");
3484 pSubMenu = dynamic_cast<PopupMenu*>(m_aMenus[nChildMenuIdx].m_pMenu.get());
3486 else
3488 ++nLevel;
3489 if (name == "property")
3490 collectProperty(reader, aProperties);
3491 else if (name == "accelerator")
3492 collectAccelerator(reader, aAccelerators);
3496 if (res == xmlreader::XmlReader::Result::End)
3498 --nLevel;
3501 if (!nLevel)
3502 break;
3505 insertMenuObject(pParent, pSubMenu, sClass, sID, aProperties, aAccelerators);
3508 void VclBuilder::handleSizeGroup(xmlreader::XmlReader &reader)
3510 m_pParserState->m_aSizeGroups.emplace_back();
3511 SizeGroup &rSizeGroup = m_pParserState->m_aSizeGroups.back();
3513 int nLevel = 1;
3515 while(true)
3517 xmlreader::Span name;
3518 int nsId;
3520 xmlreader::XmlReader::Result res = reader.nextItem(
3521 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3523 if (res == xmlreader::XmlReader::Result::Done)
3524 break;
3526 if (res == xmlreader::XmlReader::Result::Begin)
3528 ++nLevel;
3529 if (name == "widget")
3531 while (reader.nextAttribute(&nsId, &name))
3533 if (name == "name")
3535 name = reader.getAttributeValue(false);
3536 OString sWidget(name.begin, name.length);
3537 sal_Int32 nDelim = sWidget.indexOf(':');
3538 if (nDelim != -1)
3539 sWidget = sWidget.copy(0, nDelim);
3540 rSizeGroup.m_aWidgets.push_back(sWidget);
3544 else
3546 if (name == "property")
3547 collectProperty(reader, rSizeGroup.m_aProperties);
3551 if (res == xmlreader::XmlReader::Result::End)
3553 --nLevel;
3556 if (!nLevel)
3557 break;
3561 namespace
3563 vcl::KeyCode makeKeyCode(const std::pair<OString,OString> &rKey)
3565 bool bShift = rKey.second.indexOf("GDK_SHIFT_MASK") != -1;
3566 bool bMod1 = rKey.second.indexOf("GDK_CONTROL_MASK") != -1;
3567 bool bMod2 = rKey.second.indexOf("GDK_MOD1_MASK") != -1;
3568 bool bMod3 = rKey.second.indexOf("GDK_MOD2_MASK") != -1;
3570 if (rKey.first == "Insert")
3571 return vcl::KeyCode(KEY_INSERT, bShift, bMod1, bMod2, bMod3);
3572 else if (rKey.first == "Delete")
3573 return vcl::KeyCode(KEY_DELETE, bShift, bMod1, bMod2, bMod3);
3575 assert (rKey.first.getLength() == 1);
3576 sal_Char cChar = rKey.first.toChar();
3578 if (cChar >= 'a' && cChar <= 'z')
3579 return vcl::KeyCode(KEY_A + (cChar - 'a'), bShift, bMod1, bMod2, bMod3);
3580 else if (cChar >= 'A' && cChar <= 'Z')
3581 return vcl::KeyCode(KEY_A + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3582 else if (cChar >= '0' && cChar <= '9')
3583 return vcl::KeyCode(KEY_0 + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3585 return vcl::KeyCode(cChar, bShift, bMod1, bMod2, bMod3);
3589 void VclBuilder::insertMenuObject(Menu *pParent, PopupMenu *pSubMenu, const OString &rClass, const OString &rID,
3590 stringmap &rProps, accelmap &rAccels)
3592 sal_uInt16 nOldCount = pParent->GetItemCount();
3593 sal_uInt16 nNewId = ++m_pParserState->m_nLastMenuItemId;
3595 if(rClass == "NotebookBarAddonsMenuMergePoint")
3597 NotebookBarAddonsMerger::MergeNotebookBarMenuAddons(pParent, nNewId, rID, *m_pNotebookBarAddonsItem);
3598 m_pParserState->m_nLastMenuItemId = pParent->GetItemCount();
3600 else if (rClass == "GtkMenuItem")
3602 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3603 OUString aCommand(extractActionName(rProps));
3604 pParent->InsertItem(nNewId, sLabel, MenuItemBits::NONE , rID);
3605 pParent->SetItemCommand(nNewId, aCommand);
3606 if (pSubMenu)
3607 pParent->SetPopupMenu(nNewId, pSubMenu);
3609 else if (rClass == "GtkCheckMenuItem")
3611 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3612 OUString aCommand(extractActionName(rProps));
3613 pParent->InsertItem(nNewId, sLabel, MenuItemBits::CHECKABLE, rID);
3614 pParent->SetItemCommand(nNewId, aCommand);
3616 else if (rClass == "GtkRadioMenuItem")
3618 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3619 OUString aCommand(extractActionName(rProps));
3620 pParent->InsertItem(nNewId, sLabel, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, rID);
3621 pParent->SetItemCommand(nNewId, aCommand);
3623 else if (rClass == "GtkSeparatorMenuItem")
3625 pParent->InsertSeparator(rID);
3628 SAL_WARN_IF(nOldCount == pParent->GetItemCount(), "vcl.layout", "probably need to implement " << rClass);
3630 if (nOldCount != pParent->GetItemCount())
3632 pParent->SetHelpId(nNewId, m_sHelpRoot + rID);
3634 for (auto const& prop : rProps)
3636 const OString &rKey = prop.first;
3637 const OUString &rValue = prop.second;
3639 if (rKey == "tooltip-markup")
3640 pParent->SetTipHelpText(nNewId, rValue);
3641 else if (rKey == "tooltip-text")
3642 pParent->SetTipHelpText(nNewId, rValue);
3643 else if (rKey == "visible")
3644 pParent->ShowItem(nNewId, toBool(rValue));
3645 else
3646 SAL_INFO("vcl.layout", "unhandled property: " << rKey);
3649 for (auto const& accel : rAccels)
3651 const OString &rSignal = accel.first;
3652 const auto &rValue = accel.second;
3654 if (rSignal == "activate")
3655 pParent->SetAccelKey(nNewId, makeKeyCode(rValue));
3656 else
3657 SAL_INFO("vcl.layout", "unhandled accelerator for: " << rSignal);
3661 rProps.clear();
3664 /// Insert items to a ComboBox or a ListBox.
3665 /// They have no common ancestor that would have 'InsertEntry()', so use a template.
3666 template<typename T> static bool insertItems(vcl::Window *pWindow, VclBuilder::stringmap &rMap,
3667 std::vector<std::unique_ptr<OUString>>& rUserData,
3668 const std::vector<ComboBoxTextItem> &rItems)
3670 T *pContainer = dynamic_cast<T*>(pWindow);
3671 if (!pContainer)
3672 return false;
3674 sal_uInt16 nActiveId = extractActive(rMap);
3675 for (auto const& item : rItems)
3677 sal_Int32 nPos = pContainer->InsertEntry(item.m_sItem);
3678 if (!item.m_sId.isEmpty())
3680 rUserData.emplace_back(std::make_unique<OUString>(OUString::fromUtf8(item.m_sId)));
3681 pContainer->SetEntryData(nPos, rUserData.back().get());
3684 if (nActiveId < rItems.size())
3685 pContainer->SelectEntryPos(nActiveId);
3687 return true;
3690 VclPtr<vcl::Window> VclBuilder::handleObject(vcl::Window *pParent, xmlreader::XmlReader &reader)
3692 OString sClass;
3693 OString sID;
3694 OUString sCustomProperty;
3696 xmlreader::Span name;
3697 int nsId;
3699 while (reader.nextAttribute(&nsId, &name))
3701 if (name == "class")
3703 name = reader.getAttributeValue(false);
3704 sClass = OString(name.begin, name.length);
3706 else if (name == "id")
3708 name = reader.getAttributeValue(false);
3709 sID = OString(name.begin, name.length);
3710 if (m_bLegacy)
3712 sal_Int32 nDelim = sID.indexOf(':');
3713 if (nDelim != -1)
3715 sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3716 sID = sID.copy(0, nDelim);
3722 if (sClass == "GtkListStore" || sClass == "GtkTreeStore")
3724 handleListStore(reader, sID, sClass);
3725 return nullptr;
3727 else if (sClass == "GtkMenu")
3729 handleMenu(reader, sID, false);
3730 return nullptr;
3732 else if (sClass == "GtkMenuBar")
3734 VclPtr<Menu> xMenu = handleMenu(reader, sID, true);
3735 if (SystemWindow* pTopLevel = pParent ? pParent->GetSystemWindow() : nullptr)
3736 pTopLevel->SetMenuBar(dynamic_cast<MenuBar*>(xMenu.get()));
3737 return nullptr;
3739 else if (sClass == "GtkSizeGroup")
3741 handleSizeGroup(reader);
3742 return nullptr;
3744 else if (sClass == "AtkObject")
3746 handleAtkObject(reader, pParent);
3747 return nullptr;
3750 int nLevel = 1;
3752 stringmap aProperties, aPangoAttributes;
3753 stringmap aAtkAttributes;
3754 std::vector<ComboBoxTextItem> aItems;
3756 if (!sCustomProperty.isEmpty())
3757 aProperties[OString("customproperty")] = sCustomProperty;
3759 VclPtr<vcl::Window> pCurrentChild;
3760 while(true)
3762 xmlreader::XmlReader::Result res = reader.nextItem(
3763 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3765 if (res == xmlreader::XmlReader::Result::Done)
3766 break;
3768 if (res == xmlreader::XmlReader::Result::Begin)
3770 if (name == "child")
3772 if (!pCurrentChild)
3774 pCurrentChild = insertObject(pParent, sClass, sID,
3775 aProperties, aPangoAttributes, aAtkAttributes);
3777 handleChild(pCurrentChild, reader);
3779 else if (name == "items")
3780 aItems = handleItems(reader);
3781 else if (name == "style")
3783 int nPriority = 0;
3784 std::vector<vcl::EnumContext::Context> aContext = handleStyle(reader, nPriority);
3785 if (nPriority != 0)
3787 vcl::IPrioritable* pPrioritable = dynamic_cast<vcl::IPrioritable*>(pCurrentChild.get());
3788 SAL_WARN_IF(!pPrioritable, "vcl", "priority set for not supported item");
3789 if (pPrioritable)
3790 pPrioritable->SetPriority(nPriority);
3792 if (!aContext.empty())
3794 vcl::IContext* pContextControl = dynamic_cast<vcl::IContext*>(pCurrentChild.get());
3795 SAL_WARN_IF(!pContextControl, "vcl", "context set for not supported item");
3796 if (pContextControl)
3797 pContextControl->SetContext(aContext);
3800 else
3802 ++nLevel;
3803 if (name == "property")
3804 collectProperty(reader, aProperties);
3805 else if (name == "attribute")
3806 collectPangoAttribute(reader, aPangoAttributes);
3807 else if (name == "relation")
3808 collectAtkRelationAttribute(reader, aAtkAttributes);
3809 else if (name == "role")
3810 collectAtkRoleAttribute(reader, aAtkAttributes);
3811 else if (name == "action-widget")
3812 handleActionWidget(reader);
3816 if (res == xmlreader::XmlReader::Result::End)
3818 --nLevel;
3821 if (!nLevel)
3822 break;
3825 if (sClass == "GtkAdjustment")
3827 m_pParserState->m_aAdjustments[sID] = aProperties;
3828 return nullptr;
3830 else if (sClass == "GtkTextBuffer")
3832 m_pParserState->m_aTextBuffers[sID] = aProperties;
3833 return nullptr;
3836 if (!pCurrentChild)
3838 pCurrentChild = insertObject(pParent, sClass, sID, aProperties,
3839 aPangoAttributes, aAtkAttributes);
3842 if (!aItems.empty())
3844 // try to fill-in the items
3845 if (!insertItems<ComboBox>(pCurrentChild, aProperties, m_aUserData, aItems))
3846 insertItems<ListBox>(pCurrentChild, aProperties, m_aUserData, aItems);
3849 return pCurrentChild;
3852 void VclBuilder::handlePacking(vcl::Window *pCurrent, vcl::Window *pParent, xmlreader::XmlReader &reader)
3854 xmlreader::Span name;
3855 int nsId;
3857 int nLevel = 1;
3859 while(true)
3861 xmlreader::XmlReader::Result res = reader.nextItem(
3862 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3864 if (res == xmlreader::XmlReader::Result::Done)
3865 break;
3867 if (res == xmlreader::XmlReader::Result::Begin)
3869 ++nLevel;
3870 if (name == "property")
3871 applyPackingProperty(pCurrent, pParent, reader);
3874 if (res == xmlreader::XmlReader::Result::End)
3876 --nLevel;
3879 if (!nLevel)
3880 break;
3884 void VclBuilder::applyPackingProperty(vcl::Window *pCurrent,
3885 vcl::Window *pParent,
3886 xmlreader::XmlReader &reader)
3888 if (!pCurrent)
3889 return;
3891 //ToolBoxItems are not true widgets just elements
3892 //of the ToolBox itself
3893 ToolBox *pToolBoxParent = nullptr;
3894 if (pCurrent == pParent)
3895 pToolBoxParent = dynamic_cast<ToolBox*>(pParent);
3897 xmlreader::Span name;
3898 int nsId;
3900 if (pCurrent->GetType() == WindowType::SCROLLWINDOW)
3902 auto aFind = m_pParserState->m_aRedundantParentWidgets.find(VclPtr<vcl::Window>(pCurrent));
3903 if (aFind != m_pParserState->m_aRedundantParentWidgets.end())
3905 pCurrent = aFind->second;
3906 assert(pCurrent);
3910 while (reader.nextAttribute(&nsId, &name))
3912 if (name == "name")
3914 name = reader.getAttributeValue(false);
3915 OString sKey(name.begin, name.length);
3916 sKey = sKey.replace('_', '-');
3917 reader.nextItem(
3918 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3919 OString sValue(name.begin, name.length);
3921 if (sKey == "expand" || sKey == "resize")
3923 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3924 if (pToolBoxParent)
3925 pToolBoxParent->SetItemExpand(m_pParserState->m_nLastToolbarId, bTrue);
3926 else
3927 pCurrent->set_expand(bTrue);
3928 continue;
3931 if (pToolBoxParent)
3932 continue;
3934 if (sKey == "fill")
3936 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3937 pCurrent->set_fill(bTrue);
3939 else if (sKey == "pack-type")
3941 VclPackType ePackType = (!sValue.isEmpty() && (sValue[0] == 'e' || sValue[0] == 'E')) ? VclPackType::End : VclPackType::Start;
3942 pCurrent->set_pack_type(ePackType);
3944 else if (sKey == "left-attach")
3946 pCurrent->set_grid_left_attach(sValue.toInt32());
3948 else if (sKey == "top-attach")
3950 pCurrent->set_grid_top_attach(sValue.toInt32());
3952 else if (sKey == "width")
3954 pCurrent->set_grid_width(sValue.toInt32());
3956 else if (sKey == "height")
3958 pCurrent->set_grid_height(sValue.toInt32());
3960 else if (sKey == "padding")
3962 pCurrent->set_padding(sValue.toInt32());
3964 else if (sKey == "position")
3966 set_window_packing_position(pCurrent, sValue.toInt32());
3968 else if (sKey == "secondary")
3970 pCurrent->set_secondary(toBool(sValue));
3972 else if (sKey == "non-homogeneous")
3974 pCurrent->set_non_homogeneous(toBool(sValue));
3976 else if (sKey == "homogeneous")
3978 pCurrent->set_non_homogeneous(!toBool(sValue));
3980 else
3982 SAL_WARN("vcl.layout", "unknown packing: " << sKey);
3988 std::vector<vcl::EnumContext::Context> VclBuilder::handleStyle(xmlreader::XmlReader &reader, int &nPriority)
3990 std::vector<vcl::EnumContext::Context> aContext;
3992 xmlreader::Span name;
3993 int nsId;
3995 int nLevel = 1;
3997 while(true)
3999 xmlreader::XmlReader::Result res = reader.nextItem(
4000 xmlreader::XmlReader::Text::NONE, &name, &nsId);
4002 if (res == xmlreader::XmlReader::Result::Done)
4003 break;
4005 if (res == xmlreader::XmlReader::Result::Begin)
4007 ++nLevel;
4008 if (name == "class")
4010 OString classStyle = getStyleClass(reader);
4012 if (classStyle.startsWith("context-"))
4014 OString sContext = classStyle.copy(classStyle.indexOf('-') + 1);
4015 OUString sContext2(sContext.getStr(), sContext.getLength(), RTL_TEXTENCODING_UTF8);
4016 aContext.push_back(vcl::EnumContext::GetContextEnum(sContext2));
4018 else if (classStyle.startsWith("priority-"))
4020 OString aPriority = classStyle.copy(classStyle.indexOf('-') + 1);
4021 OUString aPriority2(aPriority.getStr(), aPriority.getLength(), RTL_TEXTENCODING_UTF8);
4022 nPriority = aPriority2.toInt32();
4024 else
4026 SAL_WARN("vcl.layout", "unknown class: " << classStyle);
4031 if (res == xmlreader::XmlReader::Result::End)
4033 --nLevel;
4036 if (!nLevel)
4037 break;
4040 return aContext;
4043 OString VclBuilder::getStyleClass(xmlreader::XmlReader &reader)
4045 xmlreader::Span name;
4046 int nsId;
4047 OString aRet;
4049 while (reader.nextAttribute(&nsId, &name))
4051 if (name == "name")
4053 name = reader.getAttributeValue(false);
4054 aRet = OString (name.begin, name.length);
4058 return aRet;
4061 void VclBuilder::collectProperty(xmlreader::XmlReader &reader, stringmap &rMap) const
4063 xmlreader::Span name;
4064 int nsId;
4066 OString sProperty, sContext;
4068 bool bTranslated = false;
4070 while (reader.nextAttribute(&nsId, &name))
4072 if (name == "name")
4074 name = reader.getAttributeValue(false);
4075 sProperty = OString(name.begin, name.length);
4077 else if (name == "context")
4079 name = reader.getAttributeValue(false);
4080 sContext = OString(name.begin, name.length);
4082 else if (name == "translatable" && reader.getAttributeValue(false) == "yes")
4084 bTranslated = true;
4088 reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4089 OString sValue(name.begin, name.length);
4090 OUString sFinalValue;
4091 if (bTranslated)
4093 if (!sContext.isEmpty())
4094 sValue = sContext + "\004" + sValue;
4095 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
4097 else
4098 sFinalValue = OUString::fromUtf8(sValue);
4100 if (!sProperty.isEmpty())
4102 sProperty = sProperty.replace('_', '-');
4103 if (m_pStringReplace)
4104 sFinalValue = (*m_pStringReplace)(sFinalValue);
4105 rMap[sProperty] = sFinalValue;
4109 void VclBuilder::handleActionWidget(xmlreader::XmlReader &reader)
4111 xmlreader::Span name;
4112 int nsId;
4114 OString sResponse;
4116 while (reader.nextAttribute(&nsId, &name))
4118 if (name == "response")
4120 name = reader.getAttributeValue(false);
4121 sResponse = OString(name.begin, name.length);
4125 reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4126 OString sID(name.begin, name.length);
4127 sal_Int32 nDelim = sID.indexOf(':');
4128 if (nDelim != -1)
4129 sID = sID.copy(0, nDelim);
4130 set_response(sID, sResponse.toInt32());
4133 void VclBuilder::collectAccelerator(xmlreader::XmlReader &reader, accelmap &rMap)
4135 xmlreader::Span name;
4136 int nsId;
4138 OString sProperty;
4139 OString sValue;
4140 OString sModifiers;
4142 while (reader.nextAttribute(&nsId, &name))
4144 if (name == "key")
4146 name = reader.getAttributeValue(false);
4147 sValue = OString(name.begin, name.length);
4149 else if (name == "signal")
4151 name = reader.getAttributeValue(false);
4152 sProperty = OString(name.begin, name.length);
4154 else if (name == "modifiers")
4156 name = reader.getAttributeValue(false);
4157 sModifiers = OString(name.begin, name.length);
4161 if (!sProperty.isEmpty() && !sValue.isEmpty())
4163 rMap[sProperty] = std::make_pair(sValue, sModifiers);
4167 vcl::Window *VclBuilder::get_widget_root()
4169 return m_aChildren.empty() ? nullptr : m_aChildren[0].m_pWindow.get();
4172 vcl::Window *VclBuilder::get_by_name(const OString& sID)
4174 for (auto const& child : m_aChildren)
4176 if (child.m_sID == sID)
4177 return child.m_pWindow;
4180 return nullptr;
4183 PopupMenu *VclBuilder::get_menu(const OString& sID)
4185 for (auto const& menu : m_aMenus)
4187 if (menu.m_sID == sID)
4188 return dynamic_cast<PopupMenu*>(menu.m_pMenu.get());
4191 return nullptr;
4194 void VclBuilder::set_response(const OString& sID, short nResponse)
4196 switch (nResponse)
4198 case -5:
4199 nResponse = RET_OK;
4200 break;
4201 case -6:
4202 nResponse = RET_CANCEL;
4203 break;
4204 case -7:
4205 nResponse = RET_CLOSE;
4206 break;
4207 case -8:
4208 nResponse = RET_YES;
4209 break;
4210 case -9:
4211 nResponse = RET_NO;
4212 break;
4213 case -11:
4214 nResponse = RET_HELP;
4215 break;
4216 default:
4217 assert(nResponse >= 100 && "keep non-canned responses in range 100+ to avoid collision with vcl RET_*");
4218 break;
4221 for (const auto & child : m_aChildren)
4223 if (child.m_sID == sID)
4225 PushButton* pPushButton = dynamic_cast<PushButton*>(child.m_pWindow.get());
4226 assert(pPushButton);
4227 Dialog* pDialog = pPushButton->GetParentDialog();
4228 assert(pDialog);
4229 pDialog->add_button(pPushButton, nResponse, false);
4230 return;
4234 assert(false);
4237 void VclBuilder::delete_by_name(const OString& sID)
4239 auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4240 [&sID](WinAndId& rItem) { return rItem.m_sID == sID; });
4241 if (aI != m_aChildren.end())
4243 aI->m_pWindow.disposeAndClear();
4244 m_aChildren.erase(aI);
4248 void VclBuilder::delete_by_window(vcl::Window *pWindow)
4250 drop_ownership(pWindow);
4251 pWindow->disposeOnce();
4254 void VclBuilder::drop_ownership(const vcl::Window *pWindow)
4256 auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4257 [&pWindow](WinAndId& rItem) { return rItem.m_pWindow == pWindow; });
4258 if (aI != m_aChildren.end())
4259 m_aChildren.erase(aI);
4262 OString VclBuilder::get_by_window(const vcl::Window *pWindow) const
4264 for (auto const& child : m_aChildren)
4266 if (child.m_pWindow == pWindow)
4267 return child.m_sID;
4270 return OString();
4273 VclBuilder::PackingData VclBuilder::get_window_packing_data(const vcl::Window *pWindow) const
4275 //We've stored the return of new Control, some of these get
4276 //border windows placed around them which are what you get
4277 //from GetChild, so scoot up a level if necessary to get the
4278 //window whose position value we have
4279 const vcl::Window *pPropHolder = pWindow->ImplGetWindow();
4281 for (auto const& child : m_aChildren)
4283 if (child.m_pWindow == pPropHolder)
4284 return child.m_aPackingData;
4287 return PackingData();
4290 void VclBuilder::set_window_packing_position(const vcl::Window *pWindow, sal_Int32 nPosition)
4292 for (auto & child : m_aChildren)
4294 if (child.m_pWindow == pWindow)
4295 child.m_aPackingData.m_nPosition = nPosition;
4299 const VclBuilder::ListStore *VclBuilder::get_model_by_name(const OString& sID) const
4301 std::map<OString, ListStore>::const_iterator aI = m_pParserState->m_aModels.find(sID);
4302 if (aI != m_pParserState->m_aModels.end())
4303 return &(aI->second);
4304 return nullptr;
4307 const VclBuilder::TextBuffer *VclBuilder::get_buffer_by_name(const OString& sID) const
4309 std::map<OString, TextBuffer>::const_iterator aI = m_pParserState->m_aTextBuffers.find(sID);
4310 if (aI != m_pParserState->m_aTextBuffers.end())
4311 return &(aI->second);
4312 return nullptr;
4315 const VclBuilder::Adjustment *VclBuilder::get_adjustment_by_name(const OString& sID) const
4317 std::map<OString, Adjustment>::const_iterator aI = m_pParserState->m_aAdjustments.find(sID);
4318 if (aI != m_pParserState->m_aAdjustments.end())
4319 return &(aI->second);
4320 return nullptr;
4323 void VclBuilder::mungeModel(ComboBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4325 for (auto const& entry : rStore.m_aEntries)
4327 const ListStore::row &rRow = entry;
4328 sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4329 if (rRow.size() > 1)
4331 if (m_bLegacy)
4333 sal_IntPtr nValue = rRow[1].toInt32();
4334 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4336 else
4338 if (!rRow[1].isEmpty())
4340 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4341 rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4346 if (nActiveId < rStore.m_aEntries.size())
4347 rTarget.SelectEntryPos(nActiveId);
4350 void VclBuilder::mungeModel(ListBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4352 for (auto const& entry : rStore.m_aEntries)
4354 const ListStore::row &rRow = entry;
4355 sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4356 if (rRow.size() > 1)
4358 if (m_bLegacy)
4360 sal_IntPtr nValue = rRow[1].toInt32();
4361 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4363 else
4365 if (!rRow[1].isEmpty())
4367 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4368 rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4373 if (nActiveId < rStore.m_aEntries.size())
4374 rTarget.SelectEntryPos(nActiveId);
4377 void VclBuilder::mungeModel(SvTabListBox& rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4379 for (auto const& entry : rStore.m_aEntries)
4381 const ListStore::row &rRow = entry;
4382 auto pEntry = rTarget.InsertEntry(rRow[0]);
4383 if (rRow.size() > 1)
4385 if (m_bLegacy)
4387 sal_IntPtr nValue = rRow[1].toInt32();
4388 pEntry->SetUserData(reinterpret_cast<void*>(nValue));
4390 else
4392 if (!rRow[1].isEmpty())
4394 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4395 pEntry->SetUserData(m_aUserData.back().get());
4400 if (nActiveId < rStore.m_aEntries.size())
4402 SvTreeListEntry* pEntry = rTarget.GetEntry(nullptr, nActiveId);
4403 rTarget.Select(pEntry);
4407 void VclBuilder::mungeAdjustment(NumericFormatter &rTarget, const Adjustment &rAdjustment)
4409 int nMul = rtl_math_pow10Exp(1, rTarget.GetDecimalDigits());
4411 for (auto const& elem : rAdjustment)
4413 const OString &rKey = elem.first;
4414 const OUString &rValue = elem.second;
4416 if (rKey == "upper")
4418 sal_Int64 nUpper = rValue.toDouble() * nMul;
4419 rTarget.SetMax(nUpper);
4420 rTarget.SetLast(nUpper);
4422 else if (rKey == "lower")
4424 sal_Int64 nLower = rValue.toDouble() * nMul;
4425 rTarget.SetMin(nLower);
4426 rTarget.SetFirst(nLower);
4428 else if (rKey == "value")
4430 sal_Int64 nValue = rValue.toDouble() * nMul;
4431 rTarget.SetValue(nValue);
4433 else if (rKey == "step-increment")
4435 sal_Int64 nSpinSize = rValue.toDouble() * nMul;
4436 rTarget.SetSpinSize(nSpinSize);
4438 else
4440 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4445 void VclBuilder::mungeAdjustment(FormattedField &rTarget, const Adjustment &rAdjustment)
4447 for (auto const& elem : rAdjustment)
4449 const OString &rKey = elem.first;
4450 const OUString &rValue = elem.second;
4452 if (rKey == "upper")
4454 rTarget.SetMaxValue(rValue.toDouble());
4456 else if (rKey == "lower")
4458 rTarget.SetMinValue(rValue.toDouble());
4460 else if (rKey == "value")
4462 rTarget.SetValue(rValue.toDouble());
4464 else if (rKey == "step-increment")
4466 rTarget.SetSpinSize(rValue.toDouble());
4468 else
4470 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4475 void VclBuilder::mungeAdjustment(TimeField &rTarget, const Adjustment &rAdjustment)
4477 for (auto const& elem : rAdjustment)
4479 const OString &rKey = elem.first;
4480 const OUString &rValue = elem.second;
4482 if (rKey == "upper")
4484 tools::Time aUpper(rValue.toInt32());
4485 rTarget.SetMax(aUpper);
4486 rTarget.SetLast(aUpper);
4488 else if (rKey == "lower")
4490 tools::Time aLower(rValue.toInt32());
4491 rTarget.SetMin(aLower);
4492 rTarget.SetFirst(aLower);
4494 else if (rKey == "value")
4496 tools::Time aValue(rValue.toInt32());
4497 rTarget.SetTime(aValue);
4499 else
4501 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4506 void VclBuilder::mungeAdjustment(DateField &rTarget, const Adjustment &rAdjustment)
4508 for (auto const& elem : rAdjustment)
4510 const OString &rKey = elem.first;
4511 const OUString &rValue = elem.second;
4513 if (rKey == "upper")
4515 Date aUpper(rValue.toInt32());
4516 rTarget.SetMax(aUpper);
4517 rTarget.SetLast(aUpper);
4519 else if (rKey == "lower")
4521 Date aLower(rValue.toInt32());
4522 rTarget.SetMin(aLower);
4523 rTarget.SetFirst(aLower);
4525 else if (rKey == "value")
4527 Date aValue(rValue.toInt32());
4528 rTarget.SetDate(aValue);
4530 else
4532 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4537 void VclBuilder::mungeAdjustment(ScrollBar &rTarget, const Adjustment &rAdjustment)
4539 for (auto const& elem : rAdjustment)
4541 const OString &rKey = elem.first;
4542 const OUString &rValue = elem.second;
4544 if (rKey == "upper")
4545 rTarget.SetRangeMax(rValue.toInt32());
4546 else if (rKey == "lower")
4547 rTarget.SetRangeMin(rValue.toInt32());
4548 else if (rKey == "value")
4549 rTarget.SetThumbPos(rValue.toInt32());
4550 else if (rKey == "step-increment")
4551 rTarget.SetLineSize(rValue.toInt32());
4552 else if (rKey == "page-increment")
4553 rTarget.SetPageSize(rValue.toInt32());
4554 else
4556 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4561 void VclBuilder::mungeAdjustment(Slider& rTarget, const Adjustment& rAdjustment)
4563 for (auto const& elem : rAdjustment)
4565 const OString &rKey = elem.first;
4566 const OUString &rValue = elem.second;
4568 if (rKey == "upper")
4569 rTarget.SetRangeMax(rValue.toInt32());
4570 else if (rKey == "lower")
4571 rTarget.SetRangeMin(rValue.toInt32());
4572 else if (rKey == "value")
4573 rTarget.SetThumbPos(rValue.toInt32());
4574 else if (rKey == "step-increment")
4575 rTarget.SetLineSize(rValue.toInt32());
4576 else if (rKey == "page-increment")
4577 rTarget.SetPageSize(rValue.toInt32());
4578 else
4580 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4585 void VclBuilder::mungeTextBuffer(VclMultiLineEdit &rTarget, const TextBuffer &rTextBuffer)
4587 for (auto const& elem : rTextBuffer)
4589 const OString &rKey = elem.first;
4590 const OUString &rValue = elem.second;
4592 if (rKey == "text")
4593 rTarget.SetText(rValue);
4594 else
4596 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4601 VclBuilder::ParserState::ParserState()
4602 : m_nLastToolbarId(0)
4603 , m_nLastMenuItemId(0)
4606 VclBuilder::MenuAndId::MenuAndId(const OString &rId, Menu *pMenu)
4607 : m_sID(rId)
4608 , m_pMenu(pMenu)
4611 VclBuilder::MenuAndId::~MenuAndId() {}
4613 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */