Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / source / window / builder.cxx
blob39ac93e3c8201d8dd7a6f2a64197508556d96710
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 <i18nutil/unicode.hxx>
17 #include <osl/module.hxx>
18 #include <sal/log.hxx>
19 #include <unotools/localedatawrapper.hxx>
20 #include <unotools/resmgr.hxx>
21 #include <vcl/builder.hxx>
22 #include <vcl/button.hxx>
23 #include <vcl/calendar.hxx>
24 #include <vcl/dialog.hxx>
25 #include <vcl/edit.hxx>
26 #include <vcl/field.hxx>
27 #include <vcl/fmtfield.hxx>
28 #include <vcl/fixed.hxx>
29 #include <vcl/toolkit/fixedhyper.hxx>
30 #include <vcl/headbar.hxx>
31 #include <vcl/IPrioritable.hxx>
32 #include <vcl/ivctrl.hxx>
33 #include <vcl/layout.hxx>
34 #include <vcl/lstbox.hxx>
35 #include <vcl/menubtn.hxx>
36 #include <vcl/mnemonic.hxx>
37 #include <vcl/toolkit/prgsbar.hxx>
38 #include <vcl/scrbar.hxx>
39 #include <vcl/svapp.hxx>
40 #include <vcl/svtabbx.hxx>
41 #include <vcl/tabctrl.hxx>
42 #include <vcl/tabpage.hxx>
43 #include <vcl/toolkit/throbber.hxx>
44 #include <vcl/toolbox.hxx>
45 #include <vcl/treelistentry.hxx>
46 #include <vcl/vclmedit.hxx>
47 #include <vcl/settings.hxx>
48 #include <vcl/slider.hxx>
49 #include <vcl/weld.hxx>
50 #include <vcl/commandinfoprovider.hxx>
51 #include <iconview.hxx>
52 #include <svdata.hxx>
53 #include <bitmaps.hlst>
54 #include <messagedialog.hxx>
55 #include <window.h>
56 #include <xmlreader/xmlreader.hxx>
57 #include <desktop/crashreport.hxx>
58 #include <salinst.hxx>
59 #include <strings.hrc>
60 #include <aboutdialog.hxx>
61 #include <treeglue.hxx>
62 #include <tools/diagnose_ex.h>
63 #include <wizdlg.hxx>
64 #include <tools/svlibrary.h>
66 #ifdef DISABLE_DYNLOADING
67 #include <dlfcn.h>
68 #endif
70 static bool toBool(const OString &rValue)
72 return (!rValue.isEmpty() && (rValue[0] == 't' || rValue[0] == 'T' || rValue[0] == '1'));
75 namespace
77 OUString mapStockToImageResource(const OUString& sType)
79 if (sType == "gtk-index")
80 return SV_RESID_BITMAP_INDEX;
81 else if (sType == "gtk-refresh")
82 return SV_RESID_BITMAP_REFRESH;
83 else if (sType == "gtk-apply")
84 return IMG_APPLY;
85 else if (sType == "gtk-dialog-error")
86 return IMG_ERROR;
87 else if (sType == "gtk-add")
88 return IMG_ADD;
89 else if (sType == "gtk-remove")
90 return IMG_REMOVE;
91 return OUString();
94 SymbolType mapStockToSymbol(const OUString& sType)
96 SymbolType eRet = SymbolType::DONTKNOW;
97 if (sType == "gtk-media-next")
98 eRet = SymbolType::NEXT;
99 else if (sType == "gtk-media-previous")
100 eRet = SymbolType::PREV;
101 else if (sType == "gtk-media-play")
102 eRet = SymbolType::PLAY;
103 else if (sType == "gtk-media-stop")
104 eRet = SymbolType::STOP;
105 else if (sType == "gtk-goto-first")
106 eRet = SymbolType::FIRST;
107 else if (sType == "gtk-goto-last")
108 eRet = SymbolType::LAST;
109 else if (sType == "gtk-go-back")
110 eRet = SymbolType::ARROW_LEFT;
111 else if (sType == "gtk-go-forward")
112 eRet = SymbolType::ARROW_RIGHT;
113 else if (sType == "gtk-go-up")
114 eRet = SymbolType::ARROW_UP;
115 else if (sType == "gtk-go-down")
116 eRet = SymbolType::ARROW_DOWN;
117 else if (sType == "gtk-missing-image")
118 eRet = SymbolType::IMAGE;
119 else if (sType == "gtk-help")
120 eRet = SymbolType::HELP;
121 else if (sType == "gtk-close")
122 eRet = SymbolType::CLOSE;
123 else if (!mapStockToImageResource(sType).isEmpty())
124 eRet = SymbolType::IMAGE;
125 return eRet;
128 void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame);
131 #if defined SAL_LOG_WARN
132 namespace
134 bool isButtonType(WindowType nType)
136 return nType == WindowType::PUSHBUTTON ||
137 nType == WindowType::OKBUTTON ||
138 nType == WindowType::CANCELBUTTON ||
139 nType == WindowType::HELPBUTTON ||
140 nType == WindowType::IMAGEBUTTON ||
141 nType == WindowType::MENUBUTTON ||
142 nType == WindowType::MOREBUTTON ||
143 nType == WindowType::SPINBUTTON;
146 #endif
148 weld::Builder* Application::CreateBuilder(weld::Widget* pParent, const OUString &rUIFile)
150 return ImplGetSVData()->mpDefInst->CreateBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile);
153 weld::Builder* Application::CreateInterimBuilder(vcl::Window* pParent, const OUString &rUIFile)
155 return SalInstance::CreateInterimBuilder(pParent, VclBuilderContainer::getUIRootDir(), rUIFile);
158 weld::MessageDialog* Application::CreateMessageDialog(weld::Widget* pParent, VclMessageType eMessageType,
159 VclButtonsType eButtonType, const OUString& rPrimaryMessage)
161 return ImplGetSVData()->mpDefInst->CreateMessageDialog(pParent, eMessageType, eButtonType, rPrimaryMessage);
164 weld::Window* Application::GetFrameWeld(const css::uno::Reference<css::awt::XWindow>& rWindow)
166 return ImplGetSVData()->mpDefInst->GetFrameWeld(rWindow);
169 namespace weld
171 OUString MetricSpinButton::MetricToString(FieldUnit rUnit)
173 const FieldUnitStringList& rList = ImplGetFieldUnits();
174 // return unit's default string (ie, the first one )
175 auto it = std::find_if(
176 rList.begin(), rList.end(),
177 [&rUnit](const std::pair<OUString, FieldUnit>& rItem) { return rItem.second == rUnit; });
178 if (it != rList.end())
179 return it->first;
181 return OUString();
184 IMPL_LINK_NOARG(MetricSpinButton, spin_button_value_changed, SpinButton&, void)
186 signal_value_changed();
189 IMPL_LINK(MetricSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
191 OUString sNewText(format_number(rSpinButton.get_value()));
192 if (sNewText != rSpinButton.get_text())
193 rSpinButton.set_text(sNewText);
196 void MetricSpinButton::update_width_chars()
198 int min, max;
199 m_xSpinButton->get_range(min, max);
200 auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
201 m_xSpinButton->get_pixel_size(format_number(max)).Width());
202 int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
203 m_xSpinButton->set_width_chars(chars);
206 unsigned int SpinButton::Power10(unsigned int n)
208 unsigned int nValue = 1;
209 for (unsigned int i = 0; i < n; ++i)
210 nValue *= 10;
211 return nValue;
214 int SpinButton::denormalize(int nValue) const
216 const int nFactor = Power10(get_digits());
218 if ((nValue < (SAL_MIN_INT32 + nFactor)) || (nValue > (SAL_MAX_INT32 - nFactor)))
220 return nValue / nFactor;
223 const int nHalf = nFactor / 2;
225 if (nValue < 0)
226 return (nValue - nHalf) / nFactor;
227 return (nValue + nHalf) / nFactor;
230 OUString MetricSpinButton::format_number(int nValue) const
232 OUString aStr;
234 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
236 unsigned int nDecimalDigits = m_xSpinButton->get_digits();
237 //pawn percent off to icu to decide whether percent is separated from its number for this locale
238 if (m_eSrcUnit == FieldUnit::PERCENT)
240 double fValue = nValue;
241 fValue /= SpinButton::Power10(nDecimalDigits);
242 aStr = unicode::formatPercent(fValue, rLocaleData.getLanguageTag());
244 else
246 aStr = rLocaleData.getNum(nValue, nDecimalDigits, true, true);
247 OUString aSuffix = MetricToString(m_eSrcUnit);
248 if (m_eSrcUnit != FieldUnit::NONE && m_eSrcUnit != FieldUnit::DEGREE && m_eSrcUnit != FieldUnit::INCH)
249 aStr += " ";
250 if (m_eSrcUnit == FieldUnit::INCH)
252 OUString sDoublePrime = u"\u2033";
253 if (aSuffix != "\"" && aSuffix != sDoublePrime)
254 aStr += " ";
255 else
256 aSuffix = sDoublePrime;
258 assert(m_eSrcUnit != FieldUnit::PERCENT);
259 aStr += aSuffix;
262 return aStr;
265 void MetricSpinButton::set_digits(unsigned int digits)
267 int step, page;
268 get_increments(step, page, m_eSrcUnit);
269 int value = get_value(m_eSrcUnit);
270 m_xSpinButton->set_digits(digits);
271 set_increments(step, page, m_eSrcUnit);
272 set_value(value, m_eSrcUnit);
273 update_width_chars();
276 void MetricSpinButton::set_unit(FieldUnit eUnit)
278 if (eUnit != m_eSrcUnit)
280 int step, page;
281 get_increments(step, page, m_eSrcUnit);
282 int value = get_value(m_eSrcUnit);
283 m_eSrcUnit = eUnit;
284 set_increments(step, page, m_eSrcUnit);
285 set_value(value, m_eSrcUnit);
286 spin_button_output(*m_xSpinButton);
287 update_width_chars();
291 int MetricSpinButton::ConvertValue(int nValue, FieldUnit eInUnit, FieldUnit eOutUnit) const
293 return MetricField::ConvertValue(nValue, 0, m_xSpinButton->get_digits(), eInUnit, eOutUnit);
296 IMPL_LINK(MetricSpinButton, spin_button_input, int*, result, bool)
298 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
299 double fResult(0.0);
300 bool bRet = MetricFormatter::TextToValue(get_text(), fResult, 0, m_xSpinButton->get_digits(), rLocaleData, m_eSrcUnit);
301 if (bRet)
303 if (fResult > SAL_MAX_INT32)
304 fResult = SAL_MAX_INT32;
305 else if (fResult < SAL_MIN_INT32)
306 fResult = SAL_MIN_INT32;
307 *result = fResult;
309 return bRet;
312 IMPL_LINK_NOARG(TimeSpinButton, spin_button_cursor_position, Entry&, void)
314 int nStartPos, nEndPos;
315 m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);
317 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
318 const int nTimeArea = TimeFormatter::GetTimeArea(m_eFormat, m_xSpinButton->get_text(), nEndPos,
319 rLocaleData);
321 int nIncrements = 1;
323 if (nTimeArea == 1)
324 nIncrements = 1000 * 60 * 60;
325 else if (nTimeArea == 2)
326 nIncrements = 1000 * 60;
327 else if (nTimeArea == 3)
328 nIncrements = 1000;
330 m_xSpinButton->set_increments(nIncrements, nIncrements * 10);
333 IMPL_LINK_NOARG(TimeSpinButton, spin_button_value_changed, SpinButton&, void)
335 signal_value_changed();
338 IMPL_LINK(TimeSpinButton, spin_button_output, SpinButton&, rSpinButton, void)
340 int nStartPos, nEndPos;
341 rSpinButton.get_selection_bounds(nStartPos, nEndPos);
342 rSpinButton.set_text(format_number(rSpinButton.get_value()));
343 rSpinButton.set_position(nEndPos);
346 IMPL_LINK(TimeSpinButton, spin_button_input, int*, result, bool)
348 int nStartPos, nEndPos;
349 m_xSpinButton->get_selection_bounds(nStartPos, nEndPos);
351 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
352 tools::Time aResult(0);
353 bool bRet = TimeFormatter::TextToTime(m_xSpinButton->get_text(), aResult, m_eFormat, true, rLocaleData);
354 if (bRet)
355 *result = ConvertValue(aResult);
356 return bRet;
359 void TimeSpinButton::update_width_chars()
361 int min, max;
362 m_xSpinButton->get_range(min, max);
363 auto width = std::max(m_xSpinButton->get_pixel_size(format_number(min)).Width(),
364 m_xSpinButton->get_pixel_size(format_number(max)).Width());
365 int chars = ceil(width / m_xSpinButton->get_approximate_digit_width());
366 m_xSpinButton->set_width_chars(chars);
369 tools::Time TimeSpinButton::ConvertValue(int nValue)
371 tools::Time aTime(0);
372 aTime.MakeTimeFromMS(nValue);
373 return aTime;
376 int TimeSpinButton::ConvertValue(const tools::Time& rTime)
378 return rTime.GetMSFromTime();
381 OUString TimeSpinButton::format_number(int nValue) const
383 const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper();
384 return TimeFormatter::FormatTime(ConvertValue(nValue), m_eFormat, TimeFormat::Hour24, true, rLocaleData);
387 EntryTreeView::EntryTreeView(std::unique_ptr<Entry> xEntry, std::unique_ptr<TreeView> xTreeView)
388 : m_xEntry(std::move(xEntry))
389 , m_xTreeView(std::move(xTreeView))
391 m_xTreeView->connect_changed(LINK(this, EntryTreeView, ClickHdl));
392 m_xEntry->connect_changed(LINK(this, EntryTreeView, ModifyHdl));
395 IMPL_LINK(EntryTreeView, ClickHdl, weld::TreeView&, rView, void)
397 m_xEntry->set_text(rView.get_selected_text());
398 m_aChangeHdl.Call(*this);
401 IMPL_LINK_NOARG(EntryTreeView, ModifyHdl, weld::Entry&, void)
403 m_aChangeHdl.Call(*this);
406 void EntryTreeView::set_height_request_by_rows(int nRows)
408 int nHeight = nRows == -1 ? -1 : m_xTreeView->get_height_rows(nRows);
409 m_xTreeView->set_size_request(m_xTreeView->get_size_request().Width(), nHeight);
413 VclBuilder::VclBuilder(vcl::Window* pParent, const OUString& sUIDir, const OUString& sUIFile,
414 const OString& sID, const css::uno::Reference<css::frame::XFrame>& rFrame,
415 bool bLegacy, const NotebookBarAddonsItem* pNotebookBarAddonsItem)
416 : m_pNotebookBarAddonsItem(pNotebookBarAddonsItem
417 ? new NotebookBarAddonsItem(*pNotebookBarAddonsItem)
418 : new NotebookBarAddonsItem{})
419 , m_sID(sID)
420 , m_sHelpRoot(OUStringToOString(sUIFile, RTL_TEXTENCODING_UTF8))
421 , m_pStringReplace(Translate::GetReadStringHook())
422 , m_pParent(pParent)
423 , m_bToplevelParentFound(false)
424 , m_bLegacy(bLegacy)
425 , m_pParserState(new ParserState)
426 , m_xFrame(rFrame)
428 m_bToplevelHasDeferredInit = pParent &&
429 ((pParent->IsSystemWindow() && static_cast<SystemWindow*>(pParent)->isDeferredInit()) ||
430 (pParent->IsDockingWindow() && static_cast<DockingWindow*>(pParent)->isDeferredInit()));
431 m_bToplevelHasDeferredProperties = m_bToplevelHasDeferredInit;
433 sal_Int32 nIdx = m_sHelpRoot.lastIndexOf('.');
434 if (nIdx != -1)
435 m_sHelpRoot = m_sHelpRoot.copy(0, nIdx);
436 m_sHelpRoot += OString('/');
438 OUString sUri = sUIDir + sUIFile;
442 xmlreader::XmlReader reader(sUri);
444 handleChild(pParent, reader);
446 catch (const css::uno::Exception &rExcept)
448 DBG_UNHANDLED_EXCEPTION("vcl.layout", "Unable to read .ui file");
449 CrashReporter::addKeyValue("VclBuilderException", "Unable to read .ui file: " + rExcept.Message, CrashReporter::Write);
450 throw;
453 //Set Mnemonic widgets when everything has been imported
454 for (auto const& mnemonicWidget : m_pParserState->m_aMnemonicWidgetMaps)
456 FixedText *pOne = get<FixedText>(mnemonicWidget.m_sID);
457 vcl::Window *pOther = get<vcl::Window>(mnemonicWidget.m_sValue.toUtf8());
458 SAL_WARN_IF(!pOne || !pOther, "vcl", "missing either source " << mnemonicWidget.m_sID
459 << " or target " << mnemonicWidget.m_sValue << " member of Mnemonic Widget Mapping");
460 if (pOne && pOther)
461 pOne->set_mnemonic_widget(pOther);
464 //Set a11y relations and role when everything has been imported
465 for (auto const& elemAtk : m_pParserState->m_aAtkInfo)
467 vcl::Window *pSource = elemAtk.first;
468 const stringmap &rMap = elemAtk.second;
470 for (auto const& elemMap : rMap)
472 const OString &rType = elemMap.first;
473 const OUString &rParam = elemMap.second;
474 if (rType == "role")
476 sal_Int16 role = BuilderUtils::getRoleFromName(rParam.toUtf8());
477 if (role != com::sun::star::accessibility::AccessibleRole::UNKNOWN)
478 pSource->SetAccessibleRole(role);
480 else
482 vcl::Window *pTarget = get<vcl::Window>(rParam.toUtf8());
483 SAL_WARN_IF(!pTarget, "vcl", "missing parameter of a11y relation: " << rParam);
484 if (!pTarget)
485 continue;
486 if (rType == "labelled-by")
487 pSource->SetAccessibleRelationLabeledBy(pTarget);
488 else if (rType == "label-for")
489 pSource->SetAccessibleRelationLabelFor(pTarget);
490 else if (rType == "member-of")
491 pSource->SetAccessibleRelationMemberOf(pTarget);
492 else
494 SAL_WARN("vcl.layout", "unhandled a11y relation :" << rType);
500 //Set radiobutton groups when everything has been imported
501 for (auto const& elem : m_pParserState->m_aGroupMaps)
503 RadioButton *pOne = get<RadioButton>(elem.m_sID);
504 RadioButton *pOther = get<RadioButton>(elem.m_sValue);
505 SAL_WARN_IF(!pOne || !pOther, "vcl", "missing member of radiobutton group");
506 if (pOne && pOther)
508 if (m_bLegacy)
509 pOne->group(*pOther);
510 else
512 pOther->group(*pOne);
513 std::stable_sort(pOther->m_xGroup->begin(), pOther->m_xGroup->end(), sortIntoBestTabTraversalOrder(this));
518 //Set ComboBox models when everything has been imported
519 for (auto const& elem : m_pParserState->m_aModelMaps)
521 vcl::Window* pTarget = get<vcl::Window>(elem.m_sID);
522 ListBox *pListBoxTarget = dynamic_cast<ListBox*>(pTarget);
523 ComboBox *pComboBoxTarget = dynamic_cast<ComboBox*>(pTarget);
524 SvTabListBox *pTreeBoxTarget = dynamic_cast<SvTabListBox*>(pTarget);
525 // pStore may be empty
526 const ListStore *pStore = get_model_by_name(elem.m_sValue.toUtf8());
527 SAL_WARN_IF(!pListBoxTarget && !pComboBoxTarget && !pTreeBoxTarget, "vcl", "missing elements of combobox");
528 if (pListBoxTarget && pStore)
529 mungeModel(*pListBoxTarget, *pStore, elem.m_nActiveId);
530 else if (pComboBoxTarget && pStore)
531 mungeModel(*pComboBoxTarget, *pStore, elem.m_nActiveId);
532 else if (pTreeBoxTarget && pStore)
533 mungeModel(*pTreeBoxTarget, *pStore, elem.m_nActiveId);
536 //Set TextView buffers when everything has been imported
537 for (auto const& elem : m_pParserState->m_aTextBufferMaps)
539 VclMultiLineEdit *pTarget = get<VclMultiLineEdit>(elem.m_sID);
540 const TextBuffer *pBuffer = get_buffer_by_name(elem.m_sValue.toUtf8());
541 SAL_WARN_IF(!pTarget || !pBuffer, "vcl", "missing elements of textview/textbuffer");
542 if (pTarget && pBuffer)
543 mungeTextBuffer(*pTarget, *pBuffer);
546 //Set SpinButton adjustments when everything has been imported
547 for (auto const& elem : m_pParserState->m_aNumericFormatterAdjustmentMaps)
549 NumericFormatter *pTarget = dynamic_cast<NumericFormatter*>(get<vcl::Window>(elem.m_sID));
550 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
551 SAL_WARN_IF(!pTarget, "vcl", "missing NumericFormatter element of spinbutton/adjustment");
552 SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
553 if (pTarget && pAdjustment)
554 mungeAdjustment(*pTarget, *pAdjustment);
557 for (auto const& elem : m_pParserState->m_aFormattedFormatterAdjustmentMaps)
559 FormattedField *pTarget = dynamic_cast<FormattedField*>(get<vcl::Window>(elem.m_sID));
560 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
561 SAL_WARN_IF(!pTarget, "vcl", "missing FormattedField element of spinbutton/adjustment");
562 SAL_WARN_IF(!pAdjustment, "vcl", "missing Adjustment element of spinbutton/adjustment");
563 if (pTarget && pAdjustment)
564 mungeAdjustment(*pTarget, *pAdjustment);
567 for (auto const& elem : m_pParserState->m_aTimeFormatterAdjustmentMaps)
569 TimeField *pTarget = dynamic_cast<TimeField*>(get<vcl::Window>(elem.m_sID));
570 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
571 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of spinbutton/adjustment");
572 if (pTarget && pAdjustment)
573 mungeAdjustment(*pTarget, *pAdjustment);
576 for (auto const& elem : m_pParserState->m_aDateFormatterAdjustmentMaps)
578 DateField *pTarget = dynamic_cast<DateField*>(get<vcl::Window>(elem.m_sID));
579 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
580 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of spinbutton/adjustment");
581 if (pTarget && pAdjustment)
582 mungeAdjustment(*pTarget, *pAdjustment);
585 //Set ScrollBar adjustments when everything has been imported
586 for (auto const& elem : m_pParserState->m_aScrollAdjustmentMaps)
588 ScrollBar *pTarget = get<ScrollBar>(elem.m_sID);
589 const Adjustment *pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
590 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scrollbar/adjustment");
591 if (pTarget && pAdjustment)
592 mungeAdjustment(*pTarget, *pAdjustment);
595 //Set Scale(Slider) adjustments
596 for (auto const& elem : m_pParserState->m_aSliderAdjustmentMaps)
598 Slider* pTarget = dynamic_cast<Slider*>(get<vcl::Window>(elem.m_sID));
599 const Adjustment* pAdjustment = get_adjustment_by_name(elem.m_sValue.toUtf8());
600 SAL_WARN_IF(!pTarget || !pAdjustment, "vcl", "missing elements of scale(slider)/adjustment");
601 if (pTarget && pAdjustment)
603 mungeAdjustment(*pTarget, *pAdjustment);
607 //Set size-groups when all widgets have been imported
608 for (auto const& sizeGroup : m_pParserState->m_aSizeGroups)
610 std::shared_ptr<VclSizeGroup> xGroup(std::make_shared<VclSizeGroup>());
612 for (auto const& elem : sizeGroup.m_aProperties)
614 const OString &rKey = elem.first;
615 const OUString &rValue = elem.second;
616 xGroup->set_property(rKey, rValue);
619 for (auto const& elem : sizeGroup.m_aWidgets)
621 vcl::Window* pWindow = get<vcl::Window>(elem.getStr());
622 pWindow->add_to_size_group(xGroup);
626 //Set button images when everything has been imported
627 std::set<OUString> aImagesToBeRemoved;
628 for (auto const& elem : m_pParserState->m_aButtonImageWidgetMaps)
630 PushButton *pTargetButton = nullptr;
631 RadioButton *pTargetRadio = nullptr;
632 Button *pTarget = nullptr;
634 if (!elem.m_bRadio)
636 pTargetButton = get<PushButton>(elem.m_sID);
637 pTarget = pTargetButton;
639 else
641 pTargetRadio = get<RadioButton>(elem.m_sID);
642 pTarget = pTargetRadio;
645 FixedImage *pImage = get<FixedImage>(elem.m_sValue.toUtf8());
646 SAL_WARN_IF(!pTarget || !pImage,
647 "vcl", "missing elements of button/image/stock");
648 if (!pTarget || !pImage)
649 continue;
650 aImagesToBeRemoved.insert(elem.m_sValue);
652 VclBuilder::StockMap::iterator aFind = m_pParserState->m_aStockMap.find(elem.m_sValue.toUtf8());
653 if (aFind == m_pParserState->m_aStockMap.end())
655 if (!elem.m_bRadio)
656 pTargetButton->SetModeImage(pImage->GetImage());
657 else
658 pTargetRadio->SetModeRadioImage(pImage->GetImage());
660 else
662 const stockinfo &rImageInfo = aFind->second;
663 SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
664 SAL_WARN_IF(eType == SymbolType::DONTKNOW, "vcl", "missing stock image element for button");
665 if (eType == SymbolType::DONTKNOW)
666 continue;
667 if (!elem.m_bRadio)
669 pTargetButton->SetSymbol(eType);
670 //fdo#76457 keep symbol images small e.g. tools->customize->menu
671 //but images the right size. Really the PushButton::CalcMinimumSize
672 //and PushButton::ImplDrawPushButton are the better place to handle
673 //this, but its such a train-wreck
674 if (eType != SymbolType::IMAGE)
675 pTargetButton->SetStyle(pTargetButton->GetStyle() | WB_SMALLSTYLE);
677 else
678 SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl.layout", "unimplemented symbol type for radiobuttons");
679 if (eType == SymbolType::IMAGE)
681 Image const aImage(StockImage::Yes,
682 mapStockToImageResource(rImageInfo.m_sStock));
683 if (!elem.m_bRadio)
684 pTargetButton->SetModeImage(aImage);
685 else
686 pTargetRadio->SetModeRadioImage(aImage);
688 switch (rImageInfo.m_nSize)
690 case 1:
691 pTarget->SetSmallSymbol();
692 break;
693 case 3:
694 // large toolbar, make bigger than normal (4)
695 pTarget->set_width_request(pTarget->GetOptimalSize().Width() * 1.5);
696 pTarget->set_height_request(pTarget->GetOptimalSize().Height() * 1.5);
697 break;
698 case 4:
699 break;
700 default:
701 SAL_WARN("vcl.layout", "unsupported image size " << rImageInfo.m_nSize);
702 break;
707 //There may be duplicate use of an Image, so we used a set to collect and
708 //now we can remove them from the tree after their final munge
709 for (auto const& elem : aImagesToBeRemoved)
711 delete_by_name(elem.toUtf8());
714 //fill in any stock icons in surviving images
715 for (auto const& elem : m_pParserState->m_aStockMap)
717 FixedImage *pImage = get<FixedImage>(elem.first);
718 SAL_WARN_IF(!pImage, "vcl", "missing elements of image/stock: " << elem.first);
719 if (!pImage)
720 continue;
722 const stockinfo &rImageInfo = elem.second;
723 if (rImageInfo.m_sStock == "gtk-missing-image")
724 continue;
726 SymbolType eType = mapStockToSymbol(rImageInfo.m_sStock);
727 SAL_WARN_IF(eType != SymbolType::IMAGE, "vcl", "unimplemented symbol type for images");
728 if (eType != SymbolType::IMAGE)
729 continue;
731 Image const aImage(StockImage::Yes,
732 mapStockToImageResource(rImageInfo.m_sStock));
733 pImage->SetImage(aImage);
736 //Set button menus when everything has been imported
737 for (auto const& elem : m_pParserState->m_aButtonMenuMaps)
739 MenuButton *pTarget = get<MenuButton>(elem.m_sID);
740 PopupMenu *pMenu = get_menu(elem.m_sValue.toUtf8());
741 SAL_WARN_IF(!pTarget || !pMenu,
742 "vcl", "missing elements of button/menu");
743 if (!pTarget || !pMenu)
744 continue;
745 pTarget->SetPopupMenu(pMenu);
748 //Remove ScrollWindow parent widgets whose children in vcl implement scrolling
749 //internally.
750 for (auto const& elem : m_pParserState->m_aRedundantParentWidgets)
752 delete_by_window(elem.first);
755 //fdo#67378 merge the label into the disclosure button
756 for (auto const& elem : m_pParserState->m_aExpanderWidgets)
758 vcl::Window *pChild = elem->get_child();
759 vcl::Window* pLabel = elem->GetWindow(GetWindowType::LastChild);
760 if (pLabel && pLabel != pChild && pLabel->GetType() == WindowType::FIXEDTEXT)
762 FixedText *pLabelWidget = static_cast<FixedText*>(pLabel);
763 elem->set_label(pLabelWidget->GetText());
764 delete_by_window(pLabel);
768 // create message dialog message area now
769 for (auto const& elem : m_pParserState->m_aMessageDialogs)
770 elem->create_message_area();
772 //drop maps, etc. that we don't need again
773 m_pParserState.reset();
775 SAL_WARN_IF(!m_sID.isEmpty() && (!m_bToplevelParentFound && !get_by_name(m_sID)), "vcl.layout",
776 "Requested top level widget \"" << m_sID << "\" not found in " << sUIFile);
778 #if defined SAL_LOG_WARN
779 if (m_bToplevelParentFound && m_pParent->IsDialog())
781 int nButtons = 0;
782 bool bHasDefButton = false;
783 for (auto const& child : m_aChildren)
785 if (isButtonType(child.m_pWindow->GetType()))
787 ++nButtons;
788 if (child.m_pWindow->GetStyle() & WB_DEFBUTTON)
790 bHasDefButton = true;
791 break;
795 SAL_WARN_IF(nButtons && !bHasDefButton, "vcl.layout", "No default button defined in " << sUIFile);
797 #endif
800 VclBuilder::~VclBuilder()
802 disposeBuilder();
805 void VclBuilder::disposeBuilder()
807 for (std::vector<WinAndId>::reverse_iterator aI = m_aChildren.rbegin(),
808 aEnd = m_aChildren.rend(); aI != aEnd; ++aI)
810 aI->m_pWindow.disposeAndClear();
812 m_aChildren.clear();
814 for (std::vector<MenuAndId>::reverse_iterator aI = m_aMenus.rbegin(),
815 aEnd = m_aMenus.rend(); aI != aEnd; ++aI)
817 aI->m_pMenu.disposeAndClear();
819 m_aMenus.clear();
820 m_pParent.clear();
823 namespace
825 bool extractDrawValue(VclBuilder::stringmap& rMap)
827 bool bDrawValue = true;
828 VclBuilder::stringmap::iterator aFind = rMap.find("draw_value");
829 if (aFind != rMap.end())
831 bDrawValue = toBool(aFind->second);
832 rMap.erase(aFind);
834 return bDrawValue;
837 OUString extractPopupMenu(VclBuilder::stringmap& rMap)
839 OUString sRet;
840 VclBuilder::stringmap::iterator aFind = rMap.find("popup");
841 if (aFind != rMap.end())
843 sRet = aFind->second;
844 rMap.erase(aFind);
846 return sRet;
849 OUString extractValuePos(VclBuilder::stringmap& rMap)
851 OUString sRet("top");
852 VclBuilder::stringmap::iterator aFind = rMap.find("value_pos");
853 if (aFind != rMap.end())
855 sRet = aFind->second;
856 rMap.erase(aFind);
858 return sRet;
861 OUString extractTypeHint(VclBuilder::stringmap &rMap)
863 OUString sRet("normal");
864 VclBuilder::stringmap::iterator aFind = rMap.find("type-hint");
865 if (aFind != rMap.end())
867 sRet = aFind->second;
868 rMap.erase(aFind);
870 return sRet;
873 bool extractResizable(VclBuilder::stringmap &rMap)
875 bool bResizable = true;
876 VclBuilder::stringmap::iterator aFind = rMap.find("resizable");
877 if (aFind != rMap.end())
879 bResizable = toBool(aFind->second);
880 rMap.erase(aFind);
882 return bResizable;
885 #if HAVE_FEATURE_DESKTOP
886 bool extractModal(VclBuilder::stringmap &rMap)
888 bool bModal = false;
889 VclBuilder::stringmap::iterator aFind = rMap.find("modal");
890 if (aFind != rMap.end())
892 bModal = toBool(aFind->second);
893 rMap.erase(aFind);
895 return bModal;
897 #endif
899 bool extractDecorated(VclBuilder::stringmap &rMap)
901 bool bDecorated = true;
902 VclBuilder::stringmap::iterator aFind = rMap.find("decorated");
903 if (aFind != rMap.end())
905 bDecorated = toBool(aFind->second);
906 rMap.erase(aFind);
908 return bDecorated;
911 bool extractCloseable(VclBuilder::stringmap &rMap)
913 bool bCloseable = true;
914 VclBuilder::stringmap::iterator aFind = rMap.find("deletable");
915 if (aFind != rMap.end())
917 bCloseable = toBool(aFind->second);
918 rMap.erase(aFind);
920 return bCloseable;
923 bool extractEntry(VclBuilder::stringmap &rMap)
925 bool bHasEntry = false;
926 VclBuilder::stringmap::iterator aFind = rMap.find("has-entry");
927 if (aFind != rMap.end())
929 bHasEntry = toBool(aFind->second);
930 rMap.erase(aFind);
932 return bHasEntry;
935 bool extractOrientation(VclBuilder::stringmap &rMap)
937 bool bVertical = false;
938 VclBuilder::stringmap::iterator aFind = rMap.find("orientation");
939 if (aFind != rMap.end())
941 bVertical = aFind->second.equalsIgnoreAsciiCase("vertical");
942 rMap.erase(aFind);
944 return bVertical;
947 bool extractVerticalTabPos(VclBuilder::stringmap &rMap)
949 bool bVertical = false;
950 VclBuilder::stringmap::iterator aFind = rMap.find("tab-pos");
951 if (aFind != rMap.end())
953 bVertical = aFind->second.equalsIgnoreAsciiCase("left") ||
954 aFind->second.equalsIgnoreAsciiCase("right");
955 rMap.erase(aFind);
957 return bVertical;
960 bool extractInconsistent(VclBuilder::stringmap &rMap)
962 bool bInconsistent = false;
963 VclBuilder::stringmap::iterator aFind = rMap.find("inconsistent");
964 if (aFind != rMap.end())
966 bInconsistent = toBool(aFind->second);
967 rMap.erase(aFind);
969 return bInconsistent;
972 OUString extractIconName(VclBuilder::stringmap &rMap)
974 OUString sIconName;
975 VclBuilder::stringmap::iterator aFind = rMap.find(OString("icon-name"));
976 if (aFind != rMap.end())
978 sIconName = aFind->second;
979 rMap.erase(aFind);
981 return sIconName;
984 OUString getStockText(const OUString &rType)
986 if (rType == "gtk-ok")
987 return VclResId(SV_BUTTONTEXT_OK);
988 else if (rType == "gtk-cancel")
989 return VclResId(SV_BUTTONTEXT_CANCEL);
990 else if (rType == "gtk-help")
991 return VclResId(SV_BUTTONTEXT_HELP);
992 else if (rType == "gtk-close")
993 return VclResId(SV_BUTTONTEXT_CLOSE);
994 else if (rType == "gtk-revert-to-saved")
995 return VclResId(SV_BUTTONTEXT_RESET);
996 else if (rType == "gtk-add")
997 return VclResId(SV_BUTTONTEXT_ADD);
998 else if (rType == "gtk-delete")
999 return VclResId(SV_BUTTONTEXT_DELETE);
1000 else if (rType == "gtk-remove")
1001 return VclResId(SV_BUTTONTEXT_REMOVE);
1002 else if (rType == "gtk-new")
1003 return VclResId(SV_BUTTONTEXT_NEW);
1004 else if (rType == "gtk-edit")
1005 return VclResId(SV_BUTTONTEXT_EDIT);
1006 else if (rType == "gtk-apply")
1007 return VclResId(SV_BUTTONTEXT_APPLY);
1008 else if (rType == "gtk-save")
1009 return VclResId(SV_BUTTONTEXT_SAVE);
1010 else if (rType == "gtk-open")
1011 return VclResId(SV_BUTTONTEXT_OPEN);
1012 else if (rType == "gtk-undo")
1013 return VclResId(SV_BUTTONTEXT_UNDO);
1014 else if (rType == "gtk-paste")
1015 return VclResId(SV_BUTTONTEXT_PASTE);
1016 else if (rType == "gtk-media-next")
1017 return VclResId(SV_BUTTONTEXT_NEXT);
1018 else if (rType == "gtk-media-previous")
1019 return VclResId(SV_BUTTONTEXT_PREV);
1020 else if (rType == "gtk-go-up")
1021 return VclResId(SV_BUTTONTEXT_GO_UP);
1022 else if (rType == "gtk-go-down")
1023 return VclResId(SV_BUTTONTEXT_GO_DOWN);
1024 else if (rType == "gtk-clear")
1025 return VclResId(SV_BUTTONTEXT_CLEAR);
1026 else if (rType == "gtk-media-play")
1027 return VclResId(SV_BUTTONTEXT_PLAY);
1028 else if (rType == "gtk-find")
1029 return VclResId(SV_BUTTONTEXT_FIND);
1030 else if (rType == "gtk-stop")
1031 return VclResId(SV_BUTTONTEXT_STOP);
1032 else if (rType == "gtk-connect")
1033 return VclResId(SV_BUTTONTEXT_CONNECT);
1034 else if (rType == "gtk-yes")
1035 return VclResId(SV_BUTTONTEXT_YES);
1036 else if (rType == "gtk-no")
1037 return VclResId(SV_BUTTONTEXT_NO);
1038 SAL_WARN("vcl.layout", "unknown stock type: " << rType);
1039 return OUString();
1042 bool extractStock(VclBuilder::stringmap &rMap)
1044 bool bIsStock = false;
1045 VclBuilder::stringmap::iterator aFind = rMap.find(OString("use-stock"));
1046 if (aFind != rMap.end())
1048 bIsStock = toBool(aFind->second);
1049 rMap.erase(aFind);
1051 return bIsStock;
1054 WinBits extractRelief(VclBuilder::stringmap &rMap)
1056 WinBits nBits = WB_3DLOOK;
1057 VclBuilder::stringmap::iterator aFind = rMap.find(OString("relief"));
1058 if (aFind != rMap.end())
1060 if (aFind->second == "half")
1061 nBits = WB_FLATBUTTON | WB_BEVELBUTTON;
1062 else if (aFind->second == "none")
1063 nBits = WB_FLATBUTTON;
1064 rMap.erase(aFind);
1066 return nBits;
1069 OUString extractLabel(VclBuilder::stringmap &rMap)
1071 OUString sType;
1072 VclBuilder::stringmap::iterator aFind = rMap.find(OString("label"));
1073 if (aFind != rMap.end())
1075 sType = aFind->second;
1076 rMap.erase(aFind);
1078 return sType;
1081 OUString extractActionName(VclBuilder::stringmap &rMap)
1083 OUString sActionName;
1084 VclBuilder::stringmap::iterator aFind = rMap.find(OString("action-name"));
1085 if (aFind != rMap.end())
1087 sActionName = aFind->second;
1088 rMap.erase(aFind);
1090 return sActionName;
1093 bool extractVisible(VclBuilder::stringmap &rMap)
1095 VclBuilder::stringmap::iterator aFind = rMap.find(OString("visible"));
1096 if (aFind != rMap.end())
1098 return toBool(aFind->second);
1100 return false;
1103 Size extractSizeRequest(VclBuilder::stringmap &rMap)
1105 OUString sWidthRequest("0");
1106 OUString sHeightRequest("0");
1107 VclBuilder::stringmap::iterator aFind = rMap.find(OString("width-request"));
1108 if (aFind != rMap.end())
1110 sWidthRequest = aFind->second;
1111 rMap.erase(aFind);
1113 aFind = rMap.find("height-request");
1114 if (aFind != rMap.end())
1116 sHeightRequest = aFind->second;
1117 rMap.erase(aFind);
1119 return Size(sWidthRequest.toInt32(), sHeightRequest.toInt32());
1122 OUString extractTooltipText(VclBuilder::stringmap &rMap)
1124 OUString sTooltipText;
1125 VclBuilder::stringmap::iterator aFind = rMap.find(OString("tooltip-text"));
1126 if (aFind == rMap.end())
1127 aFind = rMap.find(OString("tooltip-markup"));
1128 if (aFind != rMap.end())
1130 sTooltipText = aFind->second;
1131 rMap.erase(aFind);
1133 return sTooltipText;
1136 float extractAlignment(VclBuilder::stringmap &rMap)
1138 float f = 0.0;
1139 VclBuilder::stringmap::iterator aFind = rMap.find(OString("alignment"));
1140 if (aFind != rMap.end())
1142 f = aFind->second.toFloat();
1143 rMap.erase(aFind);
1145 return f;
1148 OUString extractTitle(VclBuilder::stringmap &rMap)
1150 OUString sTitle;
1151 VclBuilder::stringmap::iterator aFind = rMap.find(OString("title"));
1152 if (aFind != rMap.end())
1154 sTitle = aFind->second;
1155 rMap.erase(aFind);
1157 return sTitle;
1160 bool extractHeadersVisible(VclBuilder::stringmap &rMap)
1162 bool bHeadersVisible = true;
1163 VclBuilder::stringmap::iterator aFind = rMap.find(OString("headers-visible"));
1164 if (aFind != rMap.end())
1166 bHeadersVisible = toBool(aFind->second);
1167 rMap.erase(aFind);
1169 return bHeadersVisible;
1172 bool extractSortIndicator(VclBuilder::stringmap &rMap)
1174 bool bSortIndicator = false;
1175 VclBuilder::stringmap::iterator aFind = rMap.find(OString("sort-indicator"));
1176 if (aFind != rMap.end())
1178 bSortIndicator = toBool(aFind->second);
1179 rMap.erase(aFind);
1181 return bSortIndicator;
1184 bool extractClickable(VclBuilder::stringmap &rMap)
1186 bool bClickable = false;
1187 VclBuilder::stringmap::iterator aFind = rMap.find(OString("clickable"));
1188 if (aFind != rMap.end())
1190 bClickable = toBool(aFind->second);
1191 rMap.erase(aFind);
1193 return bClickable;
1196 void setupFromActionName(Button *pButton, VclBuilder::stringmap &rMap, const css::uno::Reference<css::frame::XFrame>& rFrame)
1198 if (!rFrame.is())
1199 return;
1201 OUString aCommand(extractActionName(rMap));
1202 if (aCommand.isEmpty())
1203 return;
1205 OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(rFrame));
1206 OUString aLabel(vcl::CommandInfoProvider::GetLabelForCommand(aCommand, aModuleName));
1207 if (!aLabel.isEmpty())
1208 pButton->SetText(aLabel);
1210 OUString aTooltip(vcl::CommandInfoProvider::GetTooltipForCommand(aCommand, rFrame));
1211 if (!aTooltip.isEmpty())
1212 pButton->SetQuickHelpText(aTooltip);
1214 Image aImage(vcl::CommandInfoProvider::GetImageForCommand(aCommand, rFrame));
1215 pButton->SetModeImage(aImage);
1217 pButton->SetCommandHandler(aCommand);
1220 VclPtr<Button> extractStockAndBuildPushButton(vcl::Window *pParent, VclBuilder::stringmap &rMap, bool bToggle, bool bLegacy)
1222 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER;
1223 if (bToggle)
1224 nBits |= WB_TOGGLE;
1226 nBits |= extractRelief(rMap);
1228 VclPtr<Button> xWindow;
1230 if (extractStock(rMap))
1232 OUString sType = extractLabel(rMap);
1233 if (bLegacy)
1235 if (sType == "gtk-ok")
1236 xWindow = VclPtr<OKButton>::Create(pParent, nBits);
1237 else if (sType == "gtk-cancel")
1238 xWindow = VclPtr<CancelButton>::Create(pParent, nBits);
1239 else if (sType == "gtk-close")
1240 xWindow = VclPtr<CloseButton>::Create(pParent, nBits);
1241 else if (sType == "gtk-help")
1242 xWindow = VclPtr<HelpButton>::Create(pParent, nBits);
1244 if (!xWindow)
1246 xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1247 xWindow->SetText(getStockText(sType));
1251 if (!xWindow)
1252 xWindow = VclPtr<PushButton>::Create(pParent, nBits);
1253 return xWindow;
1256 VclPtr<MenuButton> extractStockAndBuildMenuButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1258 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1260 nBits |= extractRelief(rMap);
1262 VclPtr<MenuButton> xWindow = VclPtr<MenuButton>::Create(pParent, nBits);
1264 if (extractStock(rMap))
1266 xWindow->SetText(getStockText(extractLabel(rMap)));
1269 return xWindow;
1272 VclPtr<Button> extractStockAndBuildMenuToggleButton(vcl::Window *pParent, VclBuilder::stringmap &rMap)
1274 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1276 nBits |= extractRelief(rMap);
1278 VclPtr<Button> xWindow = VclPtr<MenuToggleButton>::Create(pParent, nBits);
1280 if (extractStock(rMap))
1282 xWindow->SetText(getStockText(extractLabel(rMap)));
1285 return xWindow;
1288 OUString extractUnit(const OUString& sPattern)
1290 OUString sUnit(sPattern);
1291 for (sal_Int32 i = 0; i < sPattern.getLength(); ++i)
1293 if (sPattern[i] != '.' && sPattern[i] != ',' && sPattern[i] != '0')
1295 sUnit = sPattern.copy(i);
1296 break;
1299 return sUnit;
1302 int extractDecimalDigits(const OUString& sPattern)
1304 int nDigits = 0;
1305 bool bAfterPoint = false;
1306 for (sal_Int32 i = 0; i < sPattern.getLength(); ++i)
1308 if (sPattern[i] == '.' || sPattern[i] == ',')
1309 bAfterPoint = true;
1310 else if (sPattern[i] == '0')
1312 if (bAfterPoint)
1313 ++nDigits;
1315 else
1316 break;
1318 return nDigits;
1321 FieldUnit detectMetricUnit(const OUString& sUnit)
1323 FieldUnit eUnit = FieldUnit::NONE;
1325 if (sUnit == "mm")
1326 eUnit = FieldUnit::MM;
1327 else if (sUnit == "cm")
1328 eUnit = FieldUnit::CM;
1329 else if (sUnit == "m")
1330 eUnit = FieldUnit::M;
1331 else if (sUnit == "km")
1332 eUnit = FieldUnit::KM;
1333 else if ((sUnit == "twips") || (sUnit == "twip"))
1334 eUnit = FieldUnit::TWIP;
1335 else if (sUnit == "pt")
1336 eUnit = FieldUnit::POINT;
1337 else if (sUnit == "pc")
1338 eUnit = FieldUnit::PICA;
1339 else if (sUnit == "\"" || (sUnit == "in") || (sUnit == "inch"))
1340 eUnit = FieldUnit::INCH;
1341 else if ((sUnit == "'") || (sUnit == "ft") || (sUnit == "foot") || (sUnit == "feet"))
1342 eUnit = FieldUnit::FOOT;
1343 else if (sUnit == "mile" || (sUnit == "miles"))
1344 eUnit = FieldUnit::MILE;
1345 else if (sUnit == "ch")
1346 eUnit = FieldUnit::CHAR;
1347 else if (sUnit == "line")
1348 eUnit = FieldUnit::LINE;
1349 else if (sUnit == "%")
1350 eUnit = FieldUnit::PERCENT;
1351 else if ((sUnit == "pixels") || (sUnit == "pixel") || (sUnit == "px"))
1352 eUnit = FieldUnit::PIXEL;
1353 else if ((sUnit == "degrees") || (sUnit == "degree"))
1354 eUnit = FieldUnit::DEGREE;
1355 else if ((sUnit == "sec") || (sUnit == "seconds") || (sUnit == "second"))
1356 eUnit = FieldUnit::SECOND;
1357 else if ((sUnit == "ms") || (sUnit == "milliseconds") || (sUnit == "millisecond"))
1358 eUnit = FieldUnit::MILLISECOND;
1359 else if (sUnit != "0")
1360 eUnit = FieldUnit::CUSTOM;
1362 return eUnit;
1365 WinBits extractDeferredBits(VclBuilder::stringmap &rMap)
1367 WinBits nBits = WB_3DLOOK|WB_HIDE;
1368 if (extractResizable(rMap))
1369 nBits |= WB_SIZEABLE;
1370 if (extractCloseable(rMap))
1371 nBits |= WB_CLOSEABLE;
1372 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
1373 if (!sBorder.isEmpty())
1374 nBits |= WB_BORDER;
1375 if (!extractDecorated(rMap))
1376 nBits |= WB_OWNERDRAWDECORATION;
1377 OUString sType(extractTypeHint(rMap));
1378 if (sType == "utility")
1379 nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_MOVEABLE;
1380 else if (sType == "popup-menu")
1381 nBits |= WB_SYSTEMWINDOW | WB_DIALOGCONTROL | WB_POPUP;
1382 else if (sType == "dock")
1383 nBits |= WB_DOCKABLE | WB_MOVEABLE;
1384 else
1385 nBits |= WB_MOVEABLE;
1386 return nBits;
1390 void VclBuilder::extractGroup(const OString &id, stringmap &rMap)
1392 VclBuilder::stringmap::iterator aFind = rMap.find(OString("group"));
1393 if (aFind != rMap.end())
1395 OUString sID = aFind->second;
1396 sal_Int32 nDelim = sID.indexOf(':');
1397 if (nDelim != -1)
1398 sID = sID.copy(0, nDelim);
1399 m_pParserState->m_aGroupMaps.emplace_back(id, sID.toUtf8());
1400 rMap.erase(aFind);
1404 void VclBuilder::connectNumericFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1406 if (!rAdjustment.isEmpty())
1407 m_pParserState->m_aNumericFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1410 void VclBuilder::connectFormattedFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1412 if (!rAdjustment.isEmpty())
1413 m_pParserState->m_aFormattedFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1416 void VclBuilder::connectTimeFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1418 if (!rAdjustment.isEmpty())
1419 m_pParserState->m_aTimeFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1422 void VclBuilder::connectDateFormatterAdjustment(const OString &id, const OUString &rAdjustment)
1424 if (!rAdjustment.isEmpty())
1425 m_pParserState->m_aDateFormatterAdjustmentMaps.emplace_back(id, rAdjustment);
1428 bool VclBuilder::extractAdjustmentToMap(const OString& id, VclBuilder::stringmap& rMap, std::vector<WidgetAdjustmentMap>& rAdjustmentMap)
1430 VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1431 if (aFind != rMap.end())
1433 rAdjustmentMap.emplace_back(id, aFind->second);
1434 rMap.erase(aFind);
1435 return true;
1437 return false;
1440 namespace
1442 sal_Int32 extractActive(VclBuilder::stringmap &rMap)
1444 sal_Int32 nActiveId = 0;
1445 VclBuilder::stringmap::iterator aFind = rMap.find(OString("active"));
1446 if (aFind != rMap.end())
1448 nActiveId = aFind->second.toInt32();
1449 rMap.erase(aFind);
1451 return nActiveId;
1454 bool extractSelectable(VclBuilder::stringmap &rMap)
1456 bool bSelectable = false;
1457 VclBuilder::stringmap::iterator aFind = rMap.find(OString("selectable"));
1458 if (aFind != rMap.end())
1460 bSelectable = toBool(aFind->second);
1461 rMap.erase(aFind);
1463 return bSelectable;
1466 OUString extractAdjustment(VclBuilder::stringmap &rMap)
1468 OUString sAdjustment;
1469 VclBuilder::stringmap::iterator aFind = rMap.find(OString("adjustment"));
1470 if (aFind != rMap.end())
1472 sAdjustment= aFind->second;
1473 rMap.erase(aFind);
1474 return sAdjustment;
1476 return sAdjustment;
1479 bool extractDrawIndicator(VclBuilder::stringmap &rMap)
1481 bool bDrawIndicator = false;
1482 VclBuilder::stringmap::iterator aFind = rMap.find(OString("draw-indicator"));
1483 if (aFind != rMap.end())
1485 bDrawIndicator = toBool(aFind->second);
1486 rMap.erase(aFind);
1488 return bDrawIndicator;
1492 void VclBuilder::extractModel(const OString &id, stringmap &rMap)
1494 VclBuilder::stringmap::iterator aFind = rMap.find(OString("model"));
1495 if (aFind != rMap.end())
1497 m_pParserState->m_aModelMaps.emplace_back(id, aFind->second,
1498 extractActive(rMap));
1499 rMap.erase(aFind);
1503 void VclBuilder::extractBuffer(const OString &id, stringmap &rMap)
1505 VclBuilder::stringmap::iterator aFind = rMap.find(OString("buffer"));
1506 if (aFind != rMap.end())
1508 m_pParserState->m_aTextBufferMaps.emplace_back(id, aFind->second);
1509 rMap.erase(aFind);
1513 void VclBuilder::extractStock(const OString &id, stringmap &rMap)
1515 VclBuilder::stringmap::iterator aFind = rMap.find(OString("stock"));
1516 if (aFind != rMap.end())
1518 stockinfo aInfo;
1519 aInfo.m_sStock = aFind->second;
1520 rMap.erase(aFind);
1521 aFind = rMap.find(OString("icon-size"));
1522 if (aFind != rMap.end())
1524 aInfo.m_nSize = aFind->second.toInt32();
1525 rMap.erase(aFind);
1527 m_pParserState->m_aStockMap[id] = aInfo;
1531 void VclBuilder::extractButtonImage(const OString &id, stringmap &rMap, bool bRadio)
1533 VclBuilder::stringmap::iterator aFind = rMap.find(OString("image"));
1534 if (aFind != rMap.end())
1536 m_pParserState->m_aButtonImageWidgetMaps.emplace_back(id, aFind->second, bRadio);
1537 rMap.erase(aFind);
1541 void VclBuilder::extractMnemonicWidget(const OString &rLabelID, stringmap &rMap)
1543 VclBuilder::stringmap::iterator aFind = rMap.find(OString("mnemonic-widget"));
1544 if (aFind != rMap.end())
1546 OUString sID = aFind->second;
1547 sal_Int32 nDelim = sID.indexOf(':');
1548 if (nDelim != -1)
1549 sID = sID.copy(0, nDelim);
1550 m_pParserState->m_aMnemonicWidgetMaps.emplace_back(rLabelID, sID);
1551 rMap.erase(aFind);
1555 vcl::Window* VclBuilder::prepareWidgetOwnScrolling(vcl::Window *pParent, WinBits &rWinStyle)
1557 //For Widgets that manage their own scrolling, if one appears as a child of
1558 //a scrolling window shoehorn that scrolling settings to this widget and
1559 //return the real parent to use
1560 if (pParent && pParent->GetType() == WindowType::SCROLLWINDOW)
1562 WinBits nScrollBits = pParent->GetStyle();
1563 nScrollBits &= (WB_AUTOHSCROLL|WB_HSCROLL|WB_AUTOVSCROLL|WB_VSCROLL);
1564 rWinStyle |= nScrollBits | WB_BORDER;
1565 pParent = pParent->GetParent();
1568 return pParent;
1571 void VclBuilder::cleanupWidgetOwnScrolling(vcl::Window *pScrollParent, vcl::Window *pWindow, stringmap &rMap)
1573 //remove the redundant scrolling parent
1574 sal_Int32 nWidthReq = pScrollParent->get_width_request();
1575 rMap[OString("width-request")] = OUString::number(nWidthReq);
1576 sal_Int32 nHeightReq = pScrollParent->get_height_request();
1577 rMap[OString("height-request")] = OUString::number(nHeightReq);
1579 m_pParserState->m_aRedundantParentWidgets[pScrollParent] = pWindow;
1582 #ifndef DISABLE_DYNLOADING
1584 extern "C" { static void thisModule() {} }
1586 // Don't unload the module on destruction
1587 class NoAutoUnloadModule : public osl::Module
1589 public:
1590 ~NoAutoUnloadModule() { release(); }
1593 typedef std::map<OUString, std::shared_ptr<NoAutoUnloadModule>> ModuleMap;
1594 static ModuleMap g_aModuleMap;
1596 #if ENABLE_MERGELIBS
1597 static std::shared_ptr<NoAutoUnloadModule> g_pMergedLib = std::make_shared<NoAutoUnloadModule>();
1598 #endif
1600 #ifndef SAL_DLLPREFIX
1601 # define SAL_DLLPREFIX ""
1602 #endif
1604 #endif
1606 void VclBuilder::preload()
1608 #ifndef DISABLE_DYNLOADING
1610 #if ENABLE_MERGELIBS
1611 g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1612 #else
1613 // find -name '*ui*' | xargs grep 'class=".*lo-' |
1614 // sed 's/.*class="//' | sed 's/-.*$//' | sort | uniq
1615 static const char *aWidgetLibs[] = {
1616 "sfxlo", "svtlo", "svxcorelo", "foruilo",
1617 "vcllo", "svxlo", "cuilo", "swlo",
1618 "swuilo", "sclo", "sdlo", "chartcontrollerlo",
1619 "smlo", "scuilo", "basctllo", "sduilo",
1620 "scnlo", "xsltdlglo", "pcrlo" // "dbulo"
1622 for (const auto & lib : aWidgetLibs)
1624 std::unique_ptr<NoAutoUnloadModule> pModule(new NoAutoUnloadModule);
1625 OUString sModule = SAL_DLLPREFIX + OUString::createFromAscii(lib) + SAL_DLLEXTENSION;
1626 if (pModule->loadRelative(&thisModule, sModule))
1627 g_aModuleMap.insert(std::make_pair(sModule, std::move(pModule)));
1629 #endif // ENABLE_MERGELIBS
1630 #endif // DISABLE_DYNLOADING
1633 #if defined DISABLE_DYNLOADING && !HAVE_FEATURE_DESKTOP
1634 extern "C" VclBuilder::customMakeWidget lo_get_custom_widget_func(const char* name);
1635 #endif
1637 namespace
1639 // Takes a string like "sfxlo-SidebarToolBox"
1640 VclBuilder::customMakeWidget GetCustomMakeWidget(const OString& name)
1642 VclBuilder::customMakeWidget pFunction = nullptr;
1643 if (sal_Int32 nDelim = name.indexOf('-'); nDelim != -1)
1645 const OUString sFunction("make"
1646 + OStringToOUString(name.copy(nDelim + 1), RTL_TEXTENCODING_UTF8));
1648 #ifndef DISABLE_DYNLOADING
1649 const OUString sModule = SAL_DLLPREFIX
1650 + OStringToOUString(name.copy(0, nDelim), RTL_TEXTENCODING_UTF8)
1651 + SAL_DLLEXTENSION;
1652 ModuleMap::iterator aI = g_aModuleMap.find(sModule);
1653 if (aI == g_aModuleMap.end())
1655 std::shared_ptr<NoAutoUnloadModule> pModule;
1656 #if ENABLE_MERGELIBS
1657 if (!g_pMergedLib->is())
1658 g_pMergedLib->loadRelative(&thisModule, SVLIBRARY("merged"));
1659 if ((pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1660 g_pMergedLib->getFunctionSymbol(sFunction))))
1661 pModule = g_pMergedLib;
1662 #endif
1663 if (!pFunction)
1665 pModule.reset(new NoAutoUnloadModule);
1666 bool ok = pModule->loadRelative(&thisModule, sModule);
1667 assert(ok && "bad module name in .ui");
1668 (void)ok;
1669 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1670 pModule->getFunctionSymbol(sFunction));
1672 g_aModuleMap.insert(std::make_pair(sModule, pModule));
1674 else
1675 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1676 aI->second->getFunctionSymbol(sFunction));
1677 #elif !HAVE_FEATURE_DESKTOP
1678 pFunction = lo_get_custom_widget_func(sFunction.toUtf8().getStr());
1679 SAL_WARN_IF(!pFunction, "vcl.layout", "Could not find " << sFunction);
1680 assert(pFunction);
1681 #else
1682 pFunction = reinterpret_cast<VclBuilder::customMakeWidget>(
1683 osl_getFunctionSymbol((oslModule)RTLD_DEFAULT, sFunction.pData));
1684 #endif
1686 return pFunction;
1690 VclPtr<vcl::Window> VclBuilder::makeObject(vcl::Window *pParent, const OString &name, const OString &id,
1691 stringmap &rMap)
1693 bool bIsPlaceHolder = name.isEmpty();
1694 bool bVertical = false;
1696 if (pParent && (pParent->GetType() == WindowType::TABCONTROL ||
1697 pParent->GetType() == WindowType::VERTICALTABCONTROL))
1699 bool bTopLevel(name == "GtkDialog" || name == "GtkMessageDialog" ||
1700 name == "GtkWindow" || name == "GtkPopover" || name == "GtkAssistant");
1701 if (!bTopLevel)
1703 if (pParent->GetType() == WindowType::TABCONTROL)
1705 //We have to add a page
1706 //make default pageid == position
1707 TabControl *pTabControl = static_cast<TabControl*>(pParent);
1708 sal_uInt16 nNewPageCount = pTabControl->GetPageCount()+1;
1709 sal_uInt16 nNewPageId = nNewPageCount;
1710 pTabControl->InsertPage(nNewPageId, OUString());
1711 pTabControl->SetCurPageId(nNewPageId);
1712 SAL_WARN_IF(bIsPlaceHolder, "vcl.layout", "we should have no placeholders for tabpages");
1713 if (!bIsPlaceHolder)
1715 VclPtrInstance<TabPage> pPage(pTabControl);
1716 pPage->Show();
1718 //Make up a name for it
1719 OString sTabPageId = get_by_window(pParent) +
1720 "-page" +
1721 OString::number(nNewPageCount);
1722 m_aChildren.emplace_back(sTabPageId, pPage, false);
1723 pPage->SetHelpId(m_sHelpRoot + sTabPageId);
1725 pParent = pPage;
1727 pTabControl->SetTabPage(nNewPageId, pPage);
1730 else
1732 VerticalTabControl *pTabControl = static_cast<VerticalTabControl*>(pParent);
1733 SAL_WARN_IF(bIsPlaceHolder, "vcl.layout", "we should have no placeholders for tabpages");
1734 if (!bIsPlaceHolder)
1735 pParent = pTabControl->GetPageParent();
1740 if (bIsPlaceHolder || name == "GtkTreeSelection")
1741 return nullptr;
1743 extractButtonImage(id, rMap, name == "GtkRadioButton");
1745 VclPtr<vcl::Window> xWindow;
1746 if (name == "GtkDialog" || name == "GtkAboutDialog" || name == "GtkAssistant")
1748 // WB_ALLOWMENUBAR because we don't know in advance if we will encounter
1749 // a menubar, and menubars need a BorderWindow in the toplevel, and
1750 // such border windows need to be in created during the dialog ctor
1751 WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_ALLOWMENUBAR;
1752 if (extractResizable(rMap))
1753 nBits |= WB_SIZEABLE;
1754 if (extractCloseable(rMap))
1755 nBits |= WB_CLOSEABLE;
1756 Dialog::InitFlag eInit = !pParent ? Dialog::InitFlag::NoParent : Dialog::InitFlag::Default;
1757 if (name == "GtkAssistant")
1758 xWindow = VclPtr<vcl::RoadmapWizard>::Create(pParent, nBits, eInit);
1759 else if (name == "GtkAboutDialog")
1760 xWindow = VclPtr<vcl::AboutDialog>::Create(pParent, nBits, eInit);
1761 else
1762 xWindow = VclPtr<Dialog>::Create(pParent, nBits, eInit);
1763 #if HAVE_FEATURE_DESKTOP
1764 if (!m_bLegacy && !extractModal(rMap))
1765 xWindow->SetType(WindowType::MODELESSDIALOG);
1766 #endif
1768 else if (name == "GtkMessageDialog")
1770 WinBits nBits = WB_MOVEABLE|WB_3DLOOK|WB_CLOSEABLE;
1771 if (extractResizable(rMap))
1772 nBits |= WB_SIZEABLE;
1773 VclPtr<MessageDialog> xDialog(VclPtr<MessageDialog>::Create(pParent, nBits));
1774 m_pParserState->m_aMessageDialogs.push_back(xDialog);
1775 xWindow = xDialog;
1776 #if defined WNT
1777 xWindow->set_border_width(3);
1778 #else
1779 xWindow->set_border_width(12);
1780 #endif
1782 else if (name == "GtkBox" || name == "GtkStatusbar")
1784 bVertical = extractOrientation(rMap);
1785 if (bVertical)
1786 xWindow = VclPtr<VclVBox>::Create(pParent);
1787 else
1788 xWindow = VclPtr<VclHBox>::Create(pParent);
1790 else if (name == "GtkPaned")
1792 bVertical = extractOrientation(rMap);
1793 if (bVertical)
1794 xWindow = VclPtr<VclVPaned>::Create(pParent);
1795 else
1796 xWindow = VclPtr<VclHPaned>::Create(pParent);
1798 else if (name == "GtkHBox")
1799 xWindow = VclPtr<VclHBox>::Create(pParent);
1800 else if (name == "GtkVBox")
1801 xWindow = VclPtr<VclVBox>::Create(pParent);
1802 else if (name == "GtkButtonBox")
1804 bVertical = extractOrientation(rMap);
1805 if (bVertical)
1806 xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1807 else
1808 xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1810 else if (name == "GtkHButtonBox")
1811 xWindow = VclPtr<VclHButtonBox>::Create(pParent);
1812 else if (name == "GtkVButtonBox")
1813 xWindow = VclPtr<VclVButtonBox>::Create(pParent);
1814 else if (name == "GtkGrid")
1815 xWindow = VclPtr<VclGrid>::Create(pParent);
1816 else if (name == "GtkFrame")
1817 xWindow = VclPtr<VclFrame>::Create(pParent);
1818 else if (name == "GtkExpander")
1820 VclPtrInstance<VclExpander> pExpander(pParent);
1821 m_pParserState->m_aExpanderWidgets.push_back(pExpander);
1822 xWindow = pExpander;
1824 else if (name == "GtkAlignment")
1825 xWindow = VclPtr<VclAlignment>::Create(pParent);
1826 else if (name == "GtkButton" || (!m_bLegacy && name == "GtkToggleButton"))
1828 VclPtr<Button> xButton;
1829 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1830 if (sMenu.isEmpty())
1831 xButton = extractStockAndBuildPushButton(pParent, rMap, name == "GtkToggleButton", m_bLegacy);
1832 else
1834 assert(m_bLegacy && "use GtkMenuButton");
1835 xButton = extractStockAndBuildMenuButton(pParent, rMap);
1836 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1838 xButton->SetImageAlign(ImageAlign::Left); //default to left
1839 setupFromActionName(xButton, rMap, m_xFrame);
1840 xWindow = xButton;
1842 else if (name == "GtkMenuButton")
1844 VclPtr<MenuButton> xButton = extractStockAndBuildMenuButton(pParent, rMap);
1845 OUString sMenu = extractPopupMenu(rMap);
1846 if (!sMenu.isEmpty())
1847 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1848 xButton->SetImageAlign(ImageAlign::Left); //default to left
1849 xButton->SetAccessibleRole(css::accessibility::AccessibleRole::BUTTON_MENU);
1851 if (!extractDrawIndicator(rMap))
1852 xButton->SetDropDown(PushButtonDropdownStyle::NONE);
1854 setupFromActionName(xButton, rMap, m_xFrame);
1855 xWindow = xButton;
1857 else if (name == "GtkToggleButton" && m_bLegacy)
1859 VclPtr<Button> xButton;
1860 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
1861 assert(sMenu.getLength() && "not implemented yet");
1862 xButton = extractStockAndBuildMenuToggleButton(pParent, rMap);
1863 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
1864 xButton->SetImageAlign(ImageAlign::Left); //default to left
1865 setupFromActionName(xButton, rMap, m_xFrame);
1866 xWindow = xButton;
1868 else if (name == "GtkRadioButton")
1870 extractGroup(id, rMap);
1871 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1872 OUString sWrap = BuilderUtils::extractCustomProperty(rMap);
1873 if (!sWrap.isEmpty())
1874 nBits |= WB_WORDBREAK;
1875 VclPtr<RadioButton> xButton = VclPtr<RadioButton>::Create(pParent, nBits);
1876 xButton->SetImageAlign(ImageAlign::Left); //default to left
1877 xWindow = xButton;
1879 if (::extractStock(rMap))
1881 xWindow->SetText(getStockText(extractLabel(rMap)));
1884 else if (name == "GtkCheckButton")
1886 WinBits nBits = WB_CLIPCHILDREN|WB_CENTER|WB_VCENTER|WB_3DLOOK;
1887 OUString sWrap = BuilderUtils::extractCustomProperty(rMap);
1888 if (!sWrap.isEmpty())
1889 nBits |= WB_WORDBREAK;
1890 //maybe always import as TriStateBox and enable/disable tristate
1891 bool bIsTriState = extractInconsistent(rMap);
1892 VclPtr<CheckBox> xCheckBox;
1893 if (bIsTriState && m_bLegacy)
1894 xCheckBox = VclPtr<TriStateBox>::Create(pParent, nBits);
1895 else
1896 xCheckBox = VclPtr<CheckBox>::Create(pParent, nBits);
1897 if (bIsTriState)
1899 xCheckBox->EnableTriState(true);
1900 xCheckBox->SetState(TRISTATE_INDET);
1902 xCheckBox->SetImageAlign(ImageAlign::Left); //default to left
1904 xWindow = xCheckBox;
1906 if (::extractStock(rMap))
1908 xWindow->SetText(getStockText(extractLabel(rMap)));
1911 else if (name == "GtkSpinButton")
1913 OUString sAdjustment = extractAdjustment(rMap);
1914 OUString sPattern = BuilderUtils::extractCustomProperty(rMap);
1915 OUString sUnit = extractUnit(sPattern);
1917 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_BORDER|WB_3DLOOK;
1918 if (!id.endsWith("-nospin"))
1919 nBits |= WB_SPIN | WB_REPEAT;
1921 if (sPattern.isEmpty())
1923 SAL_INFO("vcl.layout", "making numeric field for " << name << " " << sUnit);
1924 if (m_bLegacy)
1926 connectNumericFormatterAdjustment(id, sAdjustment);
1927 xWindow = VclPtr<NumericField>::Create(pParent, nBits);
1929 else
1931 connectFormattedFormatterAdjustment(id, sAdjustment);
1932 VclPtrInstance<FormattedField> xField(pParent, nBits);
1933 xField->SetMinValue(0);
1934 xWindow = xField;
1937 else
1939 if (sPattern == "hh:mm")
1941 connectTimeFormatterAdjustment(id, sAdjustment);
1942 SAL_INFO("vcl.layout", "making time field for " << name << " " << sUnit);
1943 xWindow = VclPtr<TimeField>::Create(pParent, nBits);
1945 else if (sPattern == "yy:mm:dd")
1947 connectDateFormatterAdjustment(id, sAdjustment);
1948 SAL_INFO("vcl.layout", "making date field for " << name << " " << sUnit);
1949 xWindow = VclPtr<DateField>::Create(pParent, nBits);
1951 else
1953 connectNumericFormatterAdjustment(id, sAdjustment);
1954 FieldUnit eUnit = detectMetricUnit(sUnit);
1955 SAL_INFO("vcl.layout", "making metric field for " << name << " " << sUnit);
1956 VclPtrInstance<MetricField> xField(pParent, nBits);
1957 xField->SetUnit(eUnit);
1958 if (eUnit == FieldUnit::CUSTOM)
1959 xField->SetCustomUnitText(sUnit);
1960 xWindow = xField;
1964 else if (name == "GtkLinkButton")
1965 xWindow = VclPtr<FixedHyperlink>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_NOLABEL);
1966 else if (name == "GtkComboBox" || name == "GtkComboBoxText")
1968 OUString sPattern = BuilderUtils::extractCustomProperty(rMap);
1969 extractModel(id, rMap);
1971 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
1973 bool bDropdown = BuilderUtils::extractDropdown(rMap);
1975 if (bDropdown)
1976 nBits |= WB_DROPDOWN;
1978 if (!sPattern.isEmpty())
1980 OUString sAdjustment = extractAdjustment(rMap);
1981 connectNumericFormatterAdjustment(id, sAdjustment);
1982 OUString sUnit = extractUnit(sPattern);
1983 FieldUnit eUnit = detectMetricUnit(sUnit);
1984 SAL_WARN("vcl.layout", "making metric box for type: " << name
1985 << " unit: " << sUnit
1986 << " name: " << id
1987 << " use a VclComboBoxNumeric instead");
1988 VclPtrInstance<MetricBox> xBox(pParent, nBits);
1989 xBox->EnableAutoSize(true);
1990 xBox->SetUnit(eUnit);
1991 xBox->SetDecimalDigits(extractDecimalDigits(sPattern));
1992 if (eUnit == FieldUnit::CUSTOM)
1993 xBox->SetCustomUnitText(sUnit);
1994 xWindow = xBox;
1996 else if (extractEntry(rMap))
1998 VclPtrInstance<ComboBox> xComboBox(pParent, nBits);
1999 xComboBox->EnableAutoSize(true);
2000 xWindow = xComboBox;
2002 else
2004 VclPtrInstance<ListBox> xListBox(pParent, nBits|WB_SIMPLEMODE);
2005 xListBox->EnableAutoSize(true);
2006 xWindow = xListBox;
2009 else if (name == "VclComboBoxNumeric")
2011 OUString sPattern = BuilderUtils::extractCustomProperty(rMap);
2012 OUString sAdjustment = extractAdjustment(rMap);
2013 extractModel(id, rMap);
2015 WinBits nBits = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
2017 bool bDropdown = BuilderUtils::extractDropdown(rMap);
2019 if (bDropdown)
2020 nBits |= WB_DROPDOWN;
2022 if (!sPattern.isEmpty())
2024 connectNumericFormatterAdjustment(id, sAdjustment);
2025 OUString sUnit = extractUnit(sPattern);
2026 FieldUnit eUnit = detectMetricUnit(sUnit);
2027 SAL_INFO("vcl.layout", "making metric box for " << name << " " << sUnit);
2028 VclPtrInstance<MetricBox> xBox(pParent, nBits);
2029 xBox->EnableAutoSize(true);
2030 xBox->SetUnit(eUnit);
2031 xBox->SetDecimalDigits(extractDecimalDigits(sPattern));
2032 if (eUnit == FieldUnit::CUSTOM)
2033 xBox->SetCustomUnitText(sUnit);
2034 xWindow = xBox;
2036 else
2038 SAL_INFO("vcl.layout", "making numeric box for " << name);
2039 connectNumericFormatterAdjustment(id, sAdjustment);
2040 VclPtrInstance<NumericBox> xBox(pParent, nBits);
2041 if (bDropdown)
2042 xBox->EnableAutoSize(true);
2043 xWindow = xBox;
2046 else if (name == "GtkIconView")
2048 assert(rMap.find(OString("model")) != rMap.end() && "GtkIconView must have a model");
2050 //window we want to apply the packing props for this GtkIconView to
2051 VclPtr<vcl::Window> xWindowForPackingProps;
2052 extractModel(id, rMap);
2053 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
2054 //IconView manages its own scrolling,
2055 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2056 if (pRealParent != pParent)
2057 nWinStyle |= WB_BORDER;
2059 VclPtr<IconView> xBox = VclPtr<IconView>::Create(pRealParent, nWinStyle);
2060 xWindowForPackingProps = xBox;
2062 xWindow = xBox;
2063 xBox->SetNoAutoCurEntry(true);
2064 xBox->SetQuickSearch(true);
2066 if (pRealParent != pParent)
2067 cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
2069 else if (name == "GtkTreeView")
2071 if (!m_bLegacy)
2073 assert(rMap.find(OString("model")) != rMap.end() && "GtkTreeView must have a model");
2076 //window we want to apply the packing props for this GtkTreeView to
2077 VclPtr<vcl::Window> xWindowForPackingProps;
2078 //To-Do
2079 //a) make SvHeaderTabListBox/SvTabListBox the default target for GtkTreeView
2080 //b) remove the non-drop down mode of ListBox and convert
2081 // everything over to SvHeaderTabListBox/SvTabListBox
2082 //c) remove the users of makeSvTabListBox and makeSvTreeListBox
2083 extractModel(id, rMap);
2084 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT|WB_VCENTER|WB_3DLOOK;
2085 if (m_bLegacy)
2087 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2088 if (!sBorder.isEmpty())
2089 nWinStyle |= WB_BORDER;
2091 else
2093 nWinStyle |= WB_HASBUTTONS | WB_HASBUTTONSATROOT;
2095 //ListBox/SvHeaderTabListBox manages its own scrolling,
2096 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2097 if (pRealParent != pParent)
2098 nWinStyle |= WB_BORDER;
2099 if (m_bLegacy)
2101 xWindow = VclPtr<ListBox>::Create(pRealParent, nWinStyle | WB_SIMPLEMODE);
2102 xWindowForPackingProps = xWindow;
2104 else
2106 VclPtr<SvTabListBox> xBox;
2107 bool bHeadersVisible = extractHeadersVisible(rMap);
2108 if (bHeadersVisible)
2110 VclPtr<VclVBox> xContainer = VclPtr<VclVBox>::Create(pRealParent);
2111 OString containerid(id + "-container");
2112 xContainer->SetHelpId(m_sHelpRoot + containerid);
2113 m_aChildren.emplace_back(containerid, xContainer, true);
2115 VclPtrInstance<HeaderBar> xHeader(xContainer, WB_BUTTONSTYLE | WB_BORDER | WB_TABSTOP | WB_3DLOOK);
2116 xHeader->set_width_request(0); // let the headerbar width not affect the size request
2117 OString headerid(id + "-header");
2118 xHeader->SetHelpId(m_sHelpRoot + headerid);
2119 m_aChildren.emplace_back(headerid, xHeader, true);
2121 VclPtr<LclHeaderTabListBox> xHeaderBox = VclPtr<LclHeaderTabListBox>::Create(xContainer, nWinStyle);
2122 xHeaderBox->InitHeaderBar(xHeader);
2123 xContainer->set_expand(true);
2124 xHeader->Show();
2125 xContainer->Show();
2126 xBox = xHeaderBox;
2127 xWindowForPackingProps = xContainer;
2129 else
2131 xBox = VclPtr<LclTabListBox>::Create(pRealParent, nWinStyle);
2132 xWindowForPackingProps = xBox;
2134 xWindow = xBox;
2135 xBox->SetNoAutoCurEntry(true);
2136 xBox->SetQuickSearch(true);
2137 xBox->SetSpaceBetweenEntries(3);
2138 xBox->SetEntryHeight(16);
2139 xBox->SetHighlightRange(); // select over the whole width
2141 if (pRealParent != pParent)
2142 cleanupWidgetOwnScrolling(pParent, xWindowForPackingProps, rMap);
2144 else if (name == "GtkTreeViewColumn")
2146 if (!m_bLegacy)
2148 SvHeaderTabListBox* pTreeView = dynamic_cast<SvHeaderTabListBox*>(pParent);
2149 if (HeaderBar* pHeaderBar = pTreeView ? pTreeView->GetHeaderBar() : nullptr)
2151 HeaderBarItemBits nBits = HeaderBarItemBits::LEFTIMAGE;
2152 if (extractClickable(rMap))
2153 nBits |= HeaderBarItemBits::CLICKABLE;
2154 if (extractSortIndicator(rMap))
2155 nBits |= HeaderBarItemBits::DOWNARROW;
2156 float fAlign = extractAlignment(rMap);
2157 if (fAlign == 0.0)
2158 nBits |= HeaderBarItemBits::LEFT;
2159 else if (fAlign == 1.0)
2160 nBits |= HeaderBarItemBits::RIGHT;
2161 else if (fAlign == 0.5)
2162 nBits |= HeaderBarItemBits::CENTER;
2163 auto nItemId = pHeaderBar->GetItemCount() + 1;
2164 OUString sTitle(extractTitle(rMap));
2165 pHeaderBar->InsertItem(nItemId, sTitle, 100, nBits);
2169 else if (name == "GtkLabel")
2171 WinBits nWinStyle = WB_CENTER|WB_VCENTER|WB_3DLOOK;
2172 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2173 if (!sBorder.isEmpty())
2174 nWinStyle |= WB_BORDER;
2175 extractMnemonicWidget(id, rMap);
2176 if (extractSelectable(rMap))
2177 xWindow = VclPtr<SelectableFixedText>::Create(pParent, nWinStyle);
2178 else
2179 xWindow = VclPtr<FixedText>::Create(pParent, nWinStyle);
2181 else if (name == "GtkImage")
2183 extractStock(id, rMap);
2184 xWindow = VclPtr<FixedImage>::Create(pParent, WB_CENTER|WB_VCENTER|WB_3DLOOK|WB_SCALE);
2185 //such parentless GtkImages are temps used to set icons on buttons
2186 //default them to hidden to stop e.g. insert->index entry flicking temp
2187 //full screen windows
2188 if (!pParent)
2190 rMap["visible"] = "false";
2193 else if (name == "GtkSeparator")
2195 bVertical = extractOrientation(rMap);
2196 xWindow = VclPtr<FixedLine>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2198 else if (name == "GtkScrollbar")
2200 extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2201 bVertical = extractOrientation(rMap);
2202 xWindow = VclPtr<ScrollBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2204 else if (name == "GtkProgressBar")
2206 extractAdjustmentToMap(id, rMap, m_pParserState->m_aScrollAdjustmentMaps);
2207 bVertical = extractOrientation(rMap);
2208 xWindow = VclPtr<ProgressBar>::Create(pParent, bVertical ? WB_VERT : WB_HORZ);
2210 else if (name == "GtkScrolledWindow")
2212 xWindow = VclPtr<VclScrolledWindow>::Create(pParent);
2214 else if (name == "GtkViewport")
2216 xWindow = VclPtr<VclViewport>::Create(pParent);
2218 else if (name == "GtkEventBox")
2220 xWindow = VclPtr<VclEventBox>::Create(pParent);
2222 else if (name == "GtkEntry")
2224 xWindow = VclPtr<Edit>::Create(pParent, WB_LEFT|WB_VCENTER|WB_BORDER|WB_3DLOOK);
2225 BuilderUtils::ensureDefaultWidthChars(rMap);
2227 else if (name == "GtkNotebook")
2229 if (!extractVerticalTabPos(rMap))
2230 xWindow = VclPtr<TabControl>::Create(pParent, WB_STDTABCONTROL|WB_3DLOOK);
2231 else
2232 xWindow = VclPtr<VerticalTabControl>::Create(pParent);
2234 else if (name == "GtkDrawingArea")
2236 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2237 xWindow = VclPtr<VclDrawingArea>::Create(pParent, sBorder.isEmpty() ? WB_TABSTOP : WB_BORDER | WB_TABSTOP);
2239 else if (name == "GtkTextView")
2241 extractBuffer(id, rMap);
2243 WinBits nWinStyle = WB_CLIPCHILDREN|WB_LEFT;
2244 if (m_bLegacy)
2246 OUString sBorder = BuilderUtils::extractCustomProperty(rMap);
2247 if (!sBorder.isEmpty())
2248 nWinStyle |= WB_BORDER;
2250 //VclMultiLineEdit manages its own scrolling,
2251 vcl::Window *pRealParent = prepareWidgetOwnScrolling(pParent, nWinStyle);
2252 if (pRealParent != pParent)
2253 nWinStyle |= WB_BORDER;
2254 xWindow = VclPtr<VclMultiLineEdit>::Create(pRealParent, nWinStyle);
2255 if (pRealParent != pParent)
2256 cleanupWidgetOwnScrolling(pParent, xWindow, rMap);
2258 else if (name == "GtkSpinner")
2260 xWindow = VclPtr<Throbber>::Create(pParent, WB_3DLOOK);
2262 else if (name == "GtkScale")
2264 extractAdjustmentToMap(id, rMap, m_pParserState->m_aSliderAdjustmentMaps);
2265 bool bDrawValue = extractDrawValue(rMap);
2266 if (bDrawValue)
2268 OUString sValuePos = extractValuePos(rMap);
2269 (void)sValuePos;
2271 bVertical = extractOrientation(rMap);
2273 WinBits nWinStyle = bVertical ? WB_VERT : WB_HORZ;
2275 xWindow = VclPtr<Slider>::Create(pParent, nWinStyle);
2277 else if (name == "GtkToolbar")
2279 xWindow = VclPtr<ToolBox>::Create(pParent, WB_3DLOOK | WB_TABSTOP);
2281 else if(name == "NotebookBarAddonsToolMergePoint")
2283 customMakeWidget pFunction = GetCustomMakeWidget("sfxlo-NotebookbarToolBox");
2284 if(pFunction != nullptr)
2285 NotebookBarAddonsMerger::MergeNotebookBarAddons(pParent, pFunction, m_xFrame, *m_pNotebookBarAddonsItem, rMap);
2286 return nullptr;
2288 else if (name == "GtkToolButton" || name == "GtkMenuToolButton" ||
2289 name == "GtkToggleToolButton" || name == "GtkRadioToolButton")
2291 ToolBox *pToolBox = dynamic_cast<ToolBox*>(pParent);
2292 if (pToolBox)
2294 OUString aCommand(extractActionName(rMap));
2296 sal_uInt16 nItemId = 0;
2297 ToolBoxItemBits nBits = ToolBoxItemBits::NONE;
2298 if (name == "GtkMenuToolButton")
2299 nBits |= ToolBoxItemBits::DROPDOWN;
2300 else if (name == "GtkToggleToolButton")
2301 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::CHECKABLE;
2302 else if (name == "GtkRadioToolButton")
2303 nBits |= ToolBoxItemBits::AUTOCHECK | ToolBoxItemBits::RADIOCHECK;
2305 if (!aCommand.isEmpty() && m_xFrame.is())
2307 pToolBox->InsertItem(aCommand, m_xFrame, nBits, extractSizeRequest(rMap));
2308 nItemId = pToolBox->GetItemId(aCommand);
2310 else
2312 nItemId = pToolBox->GetItemCount() + 1;
2313 //TODO: ImplToolItems::size_type -> sal_uInt16!
2314 pToolBox->InsertItem(nItemId, extractLabel(rMap), nBits);
2315 if (aCommand.isEmpty() && !m_bLegacy)
2316 aCommand = OUString::fromUtf8(id);
2317 pToolBox->SetItemCommand(nItemId, aCommand);
2320 pToolBox->SetHelpId(nItemId, m_sHelpRoot + id);
2321 OUString sTooltip(extractTooltipText(rMap));
2322 if (!sTooltip.isEmpty())
2323 pToolBox->SetQuickHelpText(nItemId, sTooltip);
2325 OUString sIconName(extractIconName(rMap));
2326 if (!sIconName.isEmpty())
2327 pToolBox->SetItemImage(nItemId, FixedImage::loadThemeImage(sIconName));
2329 if (!extractVisible(rMap))
2330 pToolBox->HideItem(nItemId);
2332 m_pParserState->m_nLastToolbarId = nItemId;
2334 return nullptr; // no widget to be created
2337 else if (name == "GtkSeparatorToolItem")
2339 ToolBox *pToolBox = dynamic_cast<ToolBox*>(pParent);
2340 if (pToolBox)
2342 pToolBox->InsertSeparator();
2343 return nullptr; // no widget to be created
2346 else if (name == "GtkWindow")
2348 WinBits nBits = extractDeferredBits(rMap);
2349 if (nBits & WB_DOCKABLE)
2350 xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2351 else
2352 xWindow = VclPtr<FloatingWindow>::Create(pParent, nBits|WB_MOVEABLE);
2354 else if (name == "GtkPopover")
2356 WinBits nBits = extractDeferredBits(rMap);
2357 xWindow = VclPtr<DockingWindow>::Create(pParent, nBits|WB_DOCKABLE|WB_MOVEABLE);
2359 else if (name == "GtkCalendar")
2361 WinBits nBits = extractDeferredBits(rMap);
2362 xWindow = VclPtr<Calendar>::Create(pParent, nBits);
2364 else
2366 if (customMakeWidget pFunction = GetCustomMakeWidget(name))
2368 pFunction(xWindow, pParent, rMap);
2369 if (xWindow->GetType() == WindowType::PUSHBUTTON)
2370 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2371 else if (xWindow->GetType() == WindowType::MENUBUTTON)
2373 OUString sMenu = BuilderUtils::extractCustomProperty(rMap);
2374 if (!sMenu.isEmpty())
2375 m_pParserState->m_aButtonMenuMaps.emplace_back(id, sMenu);
2376 setupFromActionName(static_cast<Button*>(xWindow.get()), rMap, m_xFrame);
2380 SAL_INFO_IF(!xWindow, "vcl.layout", "probably need to implement " << name << " or add a make" << name << " function");
2381 if (xWindow)
2383 xWindow->SetHelpId(m_sHelpRoot + id);
2384 SAL_INFO("vcl.layout", "for " << name <<
2385 ", created " << xWindow.get() << " child of " <<
2386 pParent << "(" << xWindow->ImplGetWindowImpl()->mpParent.get() << "/" <<
2387 xWindow->ImplGetWindowImpl()->mpRealParent.get() << "/" <<
2388 xWindow->ImplGetWindowImpl()->mpBorderWindow.get() << ") with helpid " <<
2389 xWindow->GetHelpId());
2390 m_aChildren.emplace_back(id, xWindow, bVertical);
2392 return xWindow;
2395 namespace
2397 //return true for window types which exist in vcl but are not themselves
2398 //represented in the .ui format, i.e. only their children exist.
2399 bool isConsideredGtkPseudo(vcl::Window const *pWindow)
2401 return pWindow->GetType() == WindowType::TABPAGE;
2405 //Any properties from .ui load we couldn't set because of potential virtual methods
2406 //during ctor are applied here
2407 void VclBuilder::setDeferredProperties()
2409 if (!m_bToplevelHasDeferredProperties)
2410 return;
2411 stringmap aDeferredProperties;
2412 aDeferredProperties.swap(m_aDeferredProperties);
2413 m_bToplevelHasDeferredProperties = false;
2414 BuilderUtils::set_properties(m_pParent, aDeferredProperties);
2417 namespace BuilderUtils
2419 void set_properties(vcl::Window *pWindow, const VclBuilder::stringmap &rProps)
2421 for (auto const& prop : rProps)
2423 const OString &rKey = prop.first;
2424 const OUString &rValue = prop.second;
2425 pWindow->set_property(rKey, rValue);
2429 OUString convertMnemonicMarkup(const OUString &rIn)
2431 OUStringBuffer aRet(rIn);
2432 for (sal_Int32 nI = 0; nI < aRet.getLength(); ++nI)
2434 if (aRet[nI] == '_' && nI+1 < aRet.getLength())
2436 if (aRet[nI+1] != '_')
2437 aRet[nI] = MNEMONIC_CHAR;
2438 else
2439 aRet.remove(nI, 1);
2440 ++nI;
2443 return aRet.makeStringAndClear();
2446 OUString extractCustomProperty(VclBuilder::stringmap &rMap)
2448 OUString sCustomProperty;
2449 VclBuilder::stringmap::iterator aFind = rMap.find(OString("customproperty"));
2450 if (aFind != rMap.end())
2452 sCustomProperty = aFind->second;
2453 rMap.erase(aFind);
2455 return sCustomProperty;
2458 FieldUnit detectUnit(OUString const& rString)
2460 OUString const unit(extractUnit(rString));
2461 return detectMetricUnit(unit);
2464 void ensureDefaultWidthChars(VclBuilder::stringmap &rMap)
2466 OString sWidthChars("width-chars");
2467 VclBuilder::stringmap::iterator aFind = rMap.find(sWidthChars);
2468 if (aFind == rMap.end())
2469 rMap[sWidthChars] = "25";
2472 bool extractDropdown(VclBuilder::stringmap &rMap)
2474 bool bDropdown = true;
2475 VclBuilder::stringmap::iterator aFind = rMap.find(OString("dropdown"));
2476 if (aFind != rMap.end())
2478 bDropdown = toBool(aFind->second);
2479 rMap.erase(aFind);
2481 return bDropdown;
2484 void reorderWithinParent(vcl::Window &rWindow, sal_uInt16 nNewPosition)
2486 WindowImpl *pWindowImpl = rWindow.ImplGetWindowImpl();
2487 if (pWindowImpl->mpParent != pWindowImpl->mpRealParent)
2489 assert(pWindowImpl->mpBorderWindow == pWindowImpl->mpParent);
2490 assert(pWindowImpl->mpBorderWindow->ImplGetWindowImpl()->mpParent == pWindowImpl->mpRealParent);
2491 reorderWithinParent(*pWindowImpl->mpBorderWindow, nNewPosition);
2492 return;
2494 rWindow.reorderWithinParent(nNewPosition);
2497 void reorderWithinParent(std::vector<vcl::Window*>& rChilds, bool bIsButtonBox)
2499 for (size_t i = 0; i < rChilds.size(); ++i)
2501 reorderWithinParent(*rChilds[i], i);
2503 if (!bIsButtonBox)
2504 continue;
2506 //The first member of the group for legacy code needs WB_GROUP set and the
2507 //others not
2508 WinBits nBits = rChilds[i]->GetStyle();
2509 nBits &= ~WB_GROUP;
2510 if (i == 0)
2511 nBits |= WB_GROUP;
2512 rChilds[i]->SetStyle(nBits);
2516 sal_Int16 getRoleFromName(const OString& roleName)
2518 using namespace com::sun::star::accessibility;
2520 static const std::unordered_map<OString, sal_Int16> aAtkRoleToAccessibleRole = {
2521 /* This is in atkobject.h's AtkRole order */
2522 { "invalid", AccessibleRole::UNKNOWN },
2523 { "accelerator label", AccessibleRole::UNKNOWN },
2524 { "alert", AccessibleRole::ALERT },
2525 { "animation", AccessibleRole::UNKNOWN },
2526 { "arrow", AccessibleRole::UNKNOWN },
2527 { "calendar", AccessibleRole::UNKNOWN },
2528 { "canvas", AccessibleRole::CANVAS },
2529 { "check box", AccessibleRole::CHECK_BOX },
2530 { "check menu item", AccessibleRole::CHECK_MENU_ITEM },
2531 { "color chooser", AccessibleRole::COLOR_CHOOSER },
2532 { "column header", AccessibleRole::COLUMN_HEADER },
2533 { "combo box", AccessibleRole::COMBO_BOX },
2534 { "date editor", AccessibleRole::DATE_EDITOR },
2535 { "desktop icon", AccessibleRole::DESKTOP_ICON },
2536 { "desktop frame", AccessibleRole::DESKTOP_PANE }, // ?
2537 { "dial", AccessibleRole::UNKNOWN },
2538 { "dialog", AccessibleRole::DIALOG },
2539 { "directory pane", AccessibleRole::DIRECTORY_PANE },
2540 { "drawing area", AccessibleRole::UNKNOWN },
2541 { "file chooser", AccessibleRole::FILE_CHOOSER },
2542 { "filler", AccessibleRole::FILLER },
2543 { "font chooser", AccessibleRole::FONT_CHOOSER },
2544 { "frame", AccessibleRole::FRAME },
2545 { "glass pane", AccessibleRole::GLASS_PANE },
2546 { "html container", AccessibleRole::UNKNOWN },
2547 { "icon", AccessibleRole::ICON },
2548 { "image", AccessibleRole::GRAPHIC },
2549 { "internal frame", AccessibleRole::INTERNAL_FRAME },
2550 { "label", AccessibleRole::LABEL },
2551 { "layered pane", AccessibleRole::LAYERED_PANE },
2552 { "list", AccessibleRole::LIST },
2553 { "list item", AccessibleRole::LIST_ITEM },
2554 { "menu", AccessibleRole::MENU },
2555 { "menu bar", AccessibleRole::MENU_BAR },
2556 { "menu item", AccessibleRole::MENU_ITEM },
2557 { "option pane", AccessibleRole::OPTION_PANE },
2558 { "page tab", AccessibleRole::PAGE_TAB },
2559 { "page tab list", AccessibleRole::PAGE_TAB_LIST },
2560 { "panel", AccessibleRole::PANEL }, // or SHAPE or TEXT_FRAME ?
2561 { "password text", AccessibleRole::PASSWORD_TEXT },
2562 { "popup menu", AccessibleRole::POPUP_MENU },
2563 { "progress bar", AccessibleRole::PROGRESS_BAR },
2564 { "push button", AccessibleRole::PUSH_BUTTON }, // or BUTTON_DROPDOWN or BUTTON_MENU
2565 { "radio button", AccessibleRole::RADIO_BUTTON },
2566 { "radio menu item", AccessibleRole::RADIO_MENU_ITEM },
2567 { "root pane", AccessibleRole::ROOT_PANE },
2568 { "row header", AccessibleRole::ROW_HEADER },
2569 { "scroll bar", AccessibleRole::SCROLL_BAR },
2570 { "scroll pane", AccessibleRole::SCROLL_PANE },
2571 { "separator", AccessibleRole::SEPARATOR },
2572 { "slider", AccessibleRole::SLIDER },
2573 { "split pane", AccessibleRole::SPLIT_PANE },
2574 { "spin button", AccessibleRole::SPIN_BOX }, // ?
2575 { "statusbar", AccessibleRole::STATUS_BAR },
2576 { "table", AccessibleRole::TABLE },
2577 { "table cell", AccessibleRole::TABLE_CELL },
2578 { "table column header", AccessibleRole::COLUMN_HEADER }, // approximate
2579 { "table row header", AccessibleRole::ROW_HEADER }, // approximate
2580 { "tear off menu item", AccessibleRole::UNKNOWN },
2581 { "terminal", AccessibleRole::UNKNOWN },
2582 { "text", AccessibleRole::TEXT },
2583 { "toggle button", AccessibleRole::TOGGLE_BUTTON },
2584 { "tool bar", AccessibleRole::TOOL_BAR },
2585 { "tool tip", AccessibleRole::TOOL_TIP },
2586 { "tree", AccessibleRole::TREE },
2587 { "tree table", AccessibleRole::TREE_TABLE },
2588 { "unknown", AccessibleRole::UNKNOWN },
2589 { "viewport", AccessibleRole::VIEW_PORT },
2590 { "window", AccessibleRole::WINDOW },
2591 { "header", AccessibleRole::HEADER },
2592 { "footer", AccessibleRole::FOOTER },
2593 { "paragraph", AccessibleRole::PARAGRAPH },
2594 { "ruler", AccessibleRole::RULER },
2595 { "application", AccessibleRole::UNKNOWN },
2596 { "autocomplete", AccessibleRole::UNKNOWN },
2597 { "edit bar", AccessibleRole::EDIT_BAR },
2598 { "embedded", AccessibleRole::EMBEDDED_OBJECT },
2599 { "entry", AccessibleRole::UNKNOWN },
2600 { "chart", AccessibleRole::CHART },
2601 { "caption", AccessibleRole::CAPTION },
2602 { "document frame", AccessibleRole::DOCUMENT },
2603 { "heading", AccessibleRole::HEADING },
2604 { "page", AccessibleRole::PAGE },
2605 { "section", AccessibleRole::SECTION },
2606 { "redundant object", AccessibleRole::UNKNOWN },
2607 { "form", AccessibleRole::FORM },
2608 { "link", AccessibleRole::HYPER_LINK },
2609 { "input method window", AccessibleRole::UNKNOWN },
2610 { "table row", AccessibleRole::UNKNOWN },
2611 { "tree item", AccessibleRole::TREE_ITEM },
2612 { "document spreadsheet", AccessibleRole::DOCUMENT_SPREADSHEET },
2613 { "document presentation", AccessibleRole::DOCUMENT_PRESENTATION },
2614 { "document text", AccessibleRole::DOCUMENT_TEXT },
2615 { "document web", AccessibleRole::DOCUMENT }, // approximate
2616 { "document email", AccessibleRole::DOCUMENT }, // approximate
2617 { "comment", AccessibleRole::COMMENT }, // or NOTE or END_NOTE or FOOTNOTE or SCROLL_PANE
2618 { "list box", AccessibleRole::UNKNOWN },
2619 { "grouping", AccessibleRole::GROUP_BOX },
2620 { "image map", AccessibleRole::IMAGE_MAP },
2621 { "notification", AccessibleRole::UNKNOWN },
2622 { "info bar", AccessibleRole::UNKNOWN },
2623 { "level bar", AccessibleRole::UNKNOWN },
2624 { "title bar", AccessibleRole::UNKNOWN },
2625 { "block quote", AccessibleRole::UNKNOWN },
2626 { "audio", AccessibleRole::UNKNOWN },
2627 { "video", AccessibleRole::UNKNOWN },
2628 { "definition", AccessibleRole::UNKNOWN },
2629 { "article", AccessibleRole::UNKNOWN },
2630 { "landmark", AccessibleRole::UNKNOWN },
2631 { "log", AccessibleRole::UNKNOWN },
2632 { "marquee", AccessibleRole::UNKNOWN },
2633 { "math", AccessibleRole::UNKNOWN },
2634 { "rating", AccessibleRole::UNKNOWN },
2635 { "timer", AccessibleRole::UNKNOWN },
2636 { "description list", AccessibleRole::UNKNOWN },
2637 { "description term", AccessibleRole::UNKNOWN },
2638 { "description value", AccessibleRole::UNKNOWN },
2639 { "static", AccessibleRole::STATIC },
2640 { "math fraction", AccessibleRole::UNKNOWN },
2641 { "math root", AccessibleRole::UNKNOWN },
2642 { "subscript", AccessibleRole::UNKNOWN },
2643 { "superscript", AccessibleRole::UNKNOWN },
2644 { "footnote", AccessibleRole::FOOTNOTE },
2647 auto it = aAtkRoleToAccessibleRole.find(roleName);
2648 if (it == aAtkRoleToAccessibleRole.end())
2649 return AccessibleRole::UNKNOWN;
2650 return it->second;
2654 VclPtr<vcl::Window> VclBuilder::insertObject(vcl::Window *pParent, const OString &rClass,
2655 const OString &rID, stringmap &rProps, stringmap &rPango, stringmap &rAtk)
2657 VclPtr<vcl::Window> pCurrentChild;
2659 if (m_pParent && !isConsideredGtkPseudo(m_pParent) && !m_sID.isEmpty() && rID == m_sID)
2661 pCurrentChild = m_pParent;
2663 //toplevels default to resizable and apparently you can't change them
2664 //afterwards, so we need to wait until now before we can truly
2665 //initialize the dialog.
2666 if (pParent && pParent->IsSystemWindow())
2668 SystemWindow *pSysWin = static_cast<SystemWindow*>(pCurrentChild.get());
2669 pSysWin->doDeferredInit(extractDeferredBits(rProps));
2670 m_bToplevelHasDeferredInit = false;
2672 else if (pParent && pParent->IsDockingWindow())
2674 DockingWindow *pDockWin = static_cast<DockingWindow*>(pCurrentChild.get());
2675 pDockWin->doDeferredInit(extractDeferredBits(rProps));
2676 m_bToplevelHasDeferredInit = false;
2679 if (pCurrentChild->GetHelpId().isEmpty())
2681 pCurrentChild->SetHelpId(m_sHelpRoot + m_sID);
2682 SAL_INFO("vcl.layout", "for toplevel dialog " << this << " " <<
2683 rID << ", set helpid " << pCurrentChild->GetHelpId());
2685 m_bToplevelParentFound = true;
2687 else
2689 //if we're being inserting under a toplevel dialog whose init is
2690 //deferred due to waiting to encounter it in this .ui, and it hasn't
2691 //been seen yet, then make unattached widgets parent-less toplevels
2692 if (pParent == m_pParent.get() && m_bToplevelHasDeferredInit)
2693 pParent = nullptr;
2694 pCurrentChild = makeObject(pParent, rClass, rID, rProps);
2697 if (pCurrentChild)
2699 pCurrentChild->set_id(OStringToOUString(rID, RTL_TEXTENCODING_UTF8));
2700 if (pCurrentChild == m_pParent.get() && m_bToplevelHasDeferredProperties)
2701 m_aDeferredProperties = rProps;
2702 else
2703 BuilderUtils::set_properties(pCurrentChild, rProps);
2705 for (auto const& elem : rPango)
2707 const OString &rKey = elem.first;
2708 const OUString &rValue = elem.second;
2709 pCurrentChild->set_font_attribute(rKey, rValue);
2712 m_pParserState->m_aAtkInfo[pCurrentChild] = rAtk;
2715 rProps.clear();
2716 rPango.clear();
2717 rAtk.clear();
2719 if (!pCurrentChild)
2720 pCurrentChild = m_aChildren.empty() ? pParent : m_aChildren.back().m_pWindow.get();
2721 return pCurrentChild;
2724 void VclBuilder::handleTabChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
2726 std::vector<OString> sIDs;
2728 int nLevel = 1;
2729 stringmap aProperties;
2730 std::vector<vcl::EnumContext::Context> context;
2732 while(true)
2734 xmlreader::Span name;
2735 int nsId;
2737 xmlreader::XmlReader::Result res = reader.nextItem(
2738 xmlreader::XmlReader::Text::NONE, &name, &nsId);
2740 if (res == xmlreader::XmlReader::Result::Begin)
2742 ++nLevel;
2743 if (name == "object")
2745 while (reader.nextAttribute(&nsId, &name))
2747 if (name == "id")
2749 name = reader.getAttributeValue(false);
2750 OString sID(name.begin, name.length);
2751 sal_Int32 nDelim = sID.indexOf(':');
2752 if (nDelim != -1)
2754 OString sPattern = sID.copy(nDelim+1);
2755 aProperties[OString("customproperty")] = OUString::fromUtf8(sPattern);
2756 sID = sID.copy(0, nDelim);
2758 sIDs.push_back(sID);
2762 else if (name == "style")
2764 int nPriority = 0;
2765 context = handleStyle(reader, nPriority);
2766 --nLevel;
2768 else if (name == "property")
2769 collectProperty(reader, aProperties);
2772 if (res == xmlreader::XmlReader::Result::End)
2773 --nLevel;
2775 if (!nLevel)
2776 break;
2778 if (res == xmlreader::XmlReader::Result::Done)
2779 break;
2782 if (!pParent)
2783 return;
2785 TabControl *pTabControl = pParent->GetType() == WindowType::TABCONTROL ?
2786 static_cast<TabControl*>(pParent) : nullptr;
2787 VerticalTabControl *pVerticalTabControl = pParent->GetType() == WindowType::VERTICALTABCONTROL ?
2788 static_cast<VerticalTabControl*>(pParent) : nullptr;
2789 assert(pTabControl || pVerticalTabControl);
2790 VclBuilder::stringmap::iterator aFind = aProperties.find(OString("label"));
2791 if (aFind != aProperties.end())
2793 if (pTabControl)
2795 sal_uInt16 nPageId = pTabControl->GetCurPageId();
2796 pTabControl->SetPageText(nPageId, aFind->second);
2797 pTabControl->SetPageName(nPageId, sIDs.back());
2798 if (!context.empty())
2800 TabPage* pPage = pTabControl->GetTabPage(nPageId);
2801 pPage->SetContext(context);
2804 else
2806 OUString sLabel(aFind->second);
2807 OUString sIconName(extractIconName(aProperties));
2808 OUString sTooltip(extractTooltipText(aProperties));
2809 pVerticalTabControl->InsertPage(sIDs.front(), sLabel, FixedImage::loadThemeImage(sIconName), sTooltip,
2810 pVerticalTabControl->GetPageParent()->GetWindow(GetWindowType::LastChild));
2813 else
2815 if (pTabControl)
2816 pTabControl->RemovePage(pTabControl->GetCurPageId());
2820 //so that tabbing between controls goes in a visually sensible sequence
2821 //we sort these into a best-tab-order sequence
2822 bool VclBuilder::sortIntoBestTabTraversalOrder::operator()(const vcl::Window *pA, const vcl::Window *pB) const
2824 //sort child order within parent list by grid position
2825 sal_Int32 nTopA = pA->get_grid_top_attach();
2826 sal_Int32 nTopB = pB->get_grid_top_attach();
2827 if (nTopA < nTopB)
2828 return true;
2829 if (nTopA > nTopB)
2830 return false;
2831 sal_Int32 nLeftA = pA->get_grid_left_attach();
2832 sal_Int32 nLeftB = pB->get_grid_left_attach();
2833 if (nLeftA < nLeftB)
2834 return true;
2835 if (nLeftA > nLeftB)
2836 return false;
2837 //sort into two groups of pack start and pack end
2838 VclPackType ePackA = pA->get_pack_type();
2839 VclPackType ePackB = pB->get_pack_type();
2840 if (ePackA < ePackB)
2841 return true;
2842 if (ePackA > ePackB)
2843 return false;
2844 bool bVerticalContainer = m_pBuilder->get_window_packing_data(pA->GetParent()).m_bVerticalOrient;
2845 bool bPackA = pA->get_secondary();
2846 bool bPackB = pB->get_secondary();
2847 if (!bVerticalContainer)
2849 //for horizontal boxes group secondaries before primaries
2850 if (bPackA > bPackB)
2851 return true;
2852 if (bPackA < bPackB)
2853 return false;
2855 else
2857 //for vertical boxes group secondaries after primaries
2858 if (bPackA < bPackB)
2859 return true;
2860 if (bPackA > bPackB)
2861 return false;
2863 //honour relative box positions with pack group, (numerical order is reversed
2864 //for VclPackType::End, they are packed from the end back, but here we need
2865 //them in visual layout order so that tabbing works as expected)
2866 sal_Int32 nPackA = m_pBuilder->get_window_packing_data(pA).m_nPosition;
2867 sal_Int32 nPackB = m_pBuilder->get_window_packing_data(pB).m_nPosition;
2868 if (nPackA < nPackB)
2869 return ePackA == VclPackType::Start;
2870 if (nPackA > nPackB)
2871 return ePackA != VclPackType::Start;
2872 //sort labels of Frames before body
2873 if (pA->GetParent() == pB->GetParent())
2875 const VclFrame *pFrameParent = dynamic_cast<const VclFrame*>(pA->GetParent());
2876 if (pFrameParent)
2878 const vcl::Window *pLabel = pFrameParent->get_label_widget();
2879 int nFramePosA = (pA == pLabel) ? 0 : 1;
2880 int nFramePosB = (pB == pLabel) ? 0 : 1;
2881 return nFramePosA < nFramePosB;
2884 return false;
2887 void VclBuilder::handleChild(vcl::Window *pParent, xmlreader::XmlReader &reader)
2889 vcl::Window *pCurrentChild = nullptr;
2891 xmlreader::Span name;
2892 int nsId;
2893 OString sType, sInternalChild;
2895 while (reader.nextAttribute(&nsId, &name))
2897 if (name == "type")
2899 name = reader.getAttributeValue(false);
2900 sType = OString(name.begin, name.length);
2902 else if (name == "internal-child")
2904 name = reader.getAttributeValue(false);
2905 sInternalChild = OString(name.begin, name.length);
2909 if (sType == "tab")
2911 handleTabChild(pParent, reader);
2912 return;
2915 int nLevel = 1;
2916 while(true)
2918 xmlreader::XmlReader::Result res = reader.nextItem(
2919 xmlreader::XmlReader::Text::NONE, &name, &nsId);
2921 if (res == xmlreader::XmlReader::Result::Begin)
2923 if (name == "object" || name == "placeholder")
2925 pCurrentChild = handleObject(pParent, reader).get();
2927 bool bObjectInserted = pCurrentChild && pParent != pCurrentChild;
2929 if (bObjectInserted)
2931 //Internal-children default in glade to not having their visible bits set
2932 //even though they are visible (generally anyway)
2933 if (!sInternalChild.isEmpty())
2934 pCurrentChild->Show();
2936 //Select the first page if it's a notebook
2937 if (pCurrentChild->GetType() == WindowType::TABCONTROL)
2939 TabControl *pTabControl = static_cast<TabControl*>(pCurrentChild);
2940 pTabControl->SetCurPageId(pTabControl->GetPageId(0));
2942 //To-Do add reorder capability to the TabControl
2944 else
2946 // We want to sort labels before contents of frames
2947 // for keyboard traversal, especially if there
2948 // are multiple widgets using the same mnemonic
2949 if (sType == "label")
2951 if (VclFrame *pFrameParent = dynamic_cast<VclFrame*>(pParent))
2952 pFrameParent->designate_label(pCurrentChild);
2954 if (sInternalChild.startsWith("vbox") || sInternalChild.startsWith("messagedialog-vbox"))
2956 if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pParent))
2957 pBoxParent->set_content_area(static_cast<VclBox*>(pCurrentChild)); // FIXME-VCLPTR
2959 else if (sInternalChild.startsWith("action_area") || sInternalChild.startsWith("messagedialog-action_area"))
2961 vcl::Window *pContentArea = pCurrentChild->GetParent();
2962 if (Dialog *pBoxParent = dynamic_cast<Dialog*>(pContentArea ? pContentArea->GetParent() : nullptr))
2964 pBoxParent->set_action_area(static_cast<VclButtonBox*>(pCurrentChild)); // FIXME-VCLPTR
2968 bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pCurrentChild) != nullptr;
2970 //To-Do make reorder a virtual in Window, move this foo
2971 //there and see above
2972 std::vector<vcl::Window*> aChilds;
2973 for (vcl::Window* pChild = pCurrentChild->GetWindow(GetWindowType::FirstChild); pChild;
2974 pChild = pChild->GetWindow(GetWindowType::Next))
2976 if (bIsButtonBox)
2978 if (PushButton* pPushButton = dynamic_cast<PushButton*>(pChild))
2979 pPushButton->setAction(true);
2982 aChilds.push_back(pChild);
2985 //sort child order within parent so that tabbing
2986 //between controls goes in a visually sensible sequence
2987 std::stable_sort(aChilds.begin(), aChilds.end(), sortIntoBestTabTraversalOrder(this));
2988 BuilderUtils::reorderWithinParent(aChilds, bIsButtonBox);
2992 else if (name == "packing")
2994 handlePacking(pCurrentChild, pParent, reader);
2996 else if (name == "interface")
2998 while (reader.nextAttribute(&nsId, &name))
3000 if (name == "domain")
3002 name = reader.getAttributeValue(false);
3003 sType = OString(name.begin, name.length);
3004 m_pParserState->m_aResLocale = Translate::Create(sType.getStr());
3007 ++nLevel;
3009 else
3010 ++nLevel;
3013 if (res == xmlreader::XmlReader::Result::End)
3014 --nLevel;
3016 if (!nLevel)
3017 break;
3019 if (res == xmlreader::XmlReader::Result::Done)
3020 break;
3024 void VclBuilder::collectPangoAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3026 xmlreader::Span span;
3027 int nsId;
3029 OString sProperty;
3030 OString sValue;
3032 while (reader.nextAttribute(&nsId, &span))
3034 if (span == "name")
3036 span = reader.getAttributeValue(false);
3037 sProperty = OString(span.begin, span.length);
3039 else if (span == "value")
3041 span = reader.getAttributeValue(false);
3042 sValue = OString(span.begin, span.length);
3046 if (!sProperty.isEmpty())
3047 rMap[sProperty] = OUString::fromUtf8(sValue);
3050 void VclBuilder::collectAtkRelationAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3052 xmlreader::Span span;
3053 int nsId;
3055 OString sProperty;
3056 OString sValue;
3058 while (reader.nextAttribute(&nsId, &span))
3060 if (span == "type")
3062 span = reader.getAttributeValue(false);
3063 sProperty = OString(span.begin, span.length);
3065 else if (span == "target")
3067 span = reader.getAttributeValue(false);
3068 sValue = OString(span.begin, span.length);
3069 sal_Int32 nDelim = sValue.indexOf(':');
3070 if (nDelim != -1)
3071 sValue = sValue.copy(0, nDelim);
3075 if (!sProperty.isEmpty())
3076 rMap[sProperty] = OUString::fromUtf8(sValue);
3079 void VclBuilder::collectAtkRoleAttribute(xmlreader::XmlReader &reader, stringmap &rMap)
3081 xmlreader::Span span;
3082 int nsId;
3084 OString sProperty;
3086 while (reader.nextAttribute(&nsId, &span))
3088 if (span == "type")
3090 span = reader.getAttributeValue(false);
3091 sProperty = OString(span.begin, span.length);
3095 if (!sProperty.isEmpty())
3096 rMap["role"] = OUString::fromUtf8(sProperty);
3099 void VclBuilder::handleRow(xmlreader::XmlReader &reader, const OString &rID)
3101 int nLevel = 1;
3103 ListStore::row aRow;
3105 while(true)
3107 xmlreader::Span name;
3108 int nsId;
3110 xmlreader::XmlReader::Result res = reader.nextItem(
3111 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3113 if (res == xmlreader::XmlReader::Result::Done)
3114 break;
3116 if (res == xmlreader::XmlReader::Result::Begin)
3118 ++nLevel;
3119 if (name == "col")
3121 bool bTranslated = false;
3122 sal_uInt32 nId = 0;
3123 OString sContext;
3125 while (reader.nextAttribute(&nsId, &name))
3127 if (name == "id")
3129 name = reader.getAttributeValue(false);
3130 nId = OString(name.begin, name.length).toInt32();
3132 else if (nId == 0 && name == "translatable" && reader.getAttributeValue(false) == "yes")
3134 bTranslated = true;
3136 else if (name == "context")
3138 name = reader.getAttributeValue(false);
3139 sContext = OString(name.begin, name.length);
3143 reader.nextItem(
3144 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3146 OString sValue(name.begin, name.length);
3147 OUString sFinalValue;
3148 if (bTranslated)
3150 if (!sContext.isEmpty())
3151 sValue = sContext + "\004" + sValue;
3152 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3154 else
3155 sFinalValue = OUString::fromUtf8(sValue);
3158 if (aRow.size() < nId+1)
3159 aRow.resize(nId+1);
3160 aRow[nId] = sFinalValue;
3164 if (res == xmlreader::XmlReader::Result::End)
3166 --nLevel;
3169 if (!nLevel)
3170 break;
3173 m_pParserState->m_aModels[rID].m_aEntries.push_back(aRow);
3176 void VclBuilder::handleListStore(xmlreader::XmlReader &reader, const OString &rID, const OString &rClass)
3178 int nLevel = 1;
3180 while(true)
3182 xmlreader::Span name;
3183 int nsId;
3185 xmlreader::XmlReader::Result res = reader.nextItem(
3186 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3188 if (res == xmlreader::XmlReader::Result::Done)
3189 break;
3191 if (res == xmlreader::XmlReader::Result::Begin)
3193 if (name == "row")
3195 bool bNotTreeStore = rClass != "GtkTreeStore";
3196 if (bNotTreeStore)
3197 handleRow(reader, rID);
3198 assert(bNotTreeStore && "gtk, as the time of writing, doesn't support data in GtkTreeStore serialization");
3200 else
3201 ++nLevel;
3204 if (res == xmlreader::XmlReader::Result::End)
3206 --nLevel;
3209 if (!nLevel)
3210 break;
3214 void VclBuilder::handleAtkObject(xmlreader::XmlReader &reader, vcl::Window *pWindow)
3216 assert(pWindow);
3218 int nLevel = 1;
3220 stringmap aProperties;
3222 while(true)
3224 xmlreader::Span name;
3225 int nsId;
3227 xmlreader::XmlReader::Result res = reader.nextItem(
3228 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3230 if (res == xmlreader::XmlReader::Result::Done)
3231 break;
3233 if (res == xmlreader::XmlReader::Result::Begin)
3235 ++nLevel;
3236 if (name == "property")
3237 collectProperty(reader, aProperties);
3240 if (res == xmlreader::XmlReader::Result::End)
3242 --nLevel;
3245 if (!nLevel)
3246 break;
3249 for (auto const& prop : aProperties)
3251 const OString &rKey = prop.first;
3252 const OUString &rValue = prop.second;
3254 if (pWindow && rKey.match("AtkObject::"))
3255 pWindow->set_property(rKey.copy(RTL_CONSTASCII_LENGTH("AtkObject::")), rValue);
3256 else
3257 SAL_WARN("vcl.layout", "unhandled atk prop: " << rKey);
3261 std::vector<ComboBoxTextItem> VclBuilder::handleItems(xmlreader::XmlReader &reader) const
3263 int nLevel = 1;
3265 std::vector<ComboBoxTextItem> aItems;
3267 while(true)
3269 xmlreader::Span name;
3270 int nsId;
3272 xmlreader::XmlReader::Result res = reader.nextItem(
3273 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3275 if (res == xmlreader::XmlReader::Result::Done)
3276 break;
3278 if (res == xmlreader::XmlReader::Result::Begin)
3280 ++nLevel;
3281 if (name == "item")
3283 bool bTranslated = false;
3284 OString sContext, sId;
3286 while (reader.nextAttribute(&nsId, &name))
3288 if (name == "translatable" && reader.getAttributeValue(false) == "yes")
3290 bTranslated = true;
3292 else if (name == "context")
3294 name = reader.getAttributeValue(false);
3295 sContext = OString(name.begin, name.length);
3297 else if (name == "id")
3299 name = reader.getAttributeValue(false);
3300 sId = OString(name.begin, name.length);
3304 reader.nextItem(
3305 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3307 OString sValue(name.begin, name.length);
3308 OUString sFinalValue;
3309 if (bTranslated)
3311 if (!sContext.isEmpty())
3312 sValue = sContext + "\004" + sValue;
3313 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
3315 else
3316 sFinalValue = OUString::fromUtf8(sValue);
3318 if (m_pStringReplace)
3319 sFinalValue = (*m_pStringReplace)(sFinalValue);
3321 aItems.emplace_back(sFinalValue, sId);
3325 if (res == xmlreader::XmlReader::Result::End)
3327 --nLevel;
3330 if (!nLevel)
3331 break;
3334 return aItems;
3337 VclPtr<Menu> VclBuilder::handleMenu(xmlreader::XmlReader &reader, const OString &rID, bool bMenuBar)
3339 VclPtr<Menu> pCurrentMenu;
3340 if (bMenuBar)
3341 pCurrentMenu = VclPtr<MenuBar>::Create();
3342 else
3343 pCurrentMenu = VclPtr<PopupMenu>::Create();
3345 int nLevel = 1;
3347 stringmap aProperties;
3349 while(true)
3351 xmlreader::Span name;
3352 int nsId;
3354 xmlreader::XmlReader::Result res = reader.nextItem(
3355 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3357 if (res == xmlreader::XmlReader::Result::Done)
3358 break;
3360 if (res == xmlreader::XmlReader::Result::Begin)
3362 if (name == "child")
3364 handleMenuChild(pCurrentMenu, reader);
3366 else
3368 ++nLevel;
3369 if (name == "property")
3370 collectProperty(reader, aProperties);
3374 if (res == xmlreader::XmlReader::Result::End)
3376 --nLevel;
3379 if (!nLevel)
3380 break;
3383 m_aMenus.emplace_back(rID, pCurrentMenu);
3385 return pCurrentMenu;
3388 void VclBuilder::handleMenuChild(Menu *pParent, xmlreader::XmlReader &reader)
3390 xmlreader::Span name;
3391 int nsId;
3393 int nLevel = 1;
3394 while(true)
3396 xmlreader::XmlReader::Result res = reader.nextItem(
3397 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3399 if (res == xmlreader::XmlReader::Result::Begin)
3401 if (name == "object" || name == "placeholder")
3403 handleMenuObject(pParent, reader);
3405 else
3406 ++nLevel;
3409 if (res == xmlreader::XmlReader::Result::End)
3410 --nLevel;
3412 if (!nLevel)
3413 break;
3415 if (res == xmlreader::XmlReader::Result::Done)
3416 break;
3420 void VclBuilder::handleMenuObject(Menu *pParent, xmlreader::XmlReader &reader)
3422 OString sClass;
3423 OString sID;
3424 OUString sCustomProperty;
3425 PopupMenu *pSubMenu = nullptr;
3427 xmlreader::Span name;
3428 int nsId;
3430 while (reader.nextAttribute(&nsId, &name))
3432 if (name == "class")
3434 name = reader.getAttributeValue(false);
3435 sClass = OString(name.begin, name.length);
3437 else if (name == "id")
3439 name = reader.getAttributeValue(false);
3440 sID = OString(name.begin, name.length);
3441 sal_Int32 nDelim = sID.indexOf(':');
3442 if (nDelim != -1)
3444 sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3445 sID = sID.copy(0, nDelim);
3450 int nLevel = 1;
3452 stringmap aProperties;
3453 accelmap aAccelerators;
3455 if (!sCustomProperty.isEmpty())
3456 aProperties[OString("customproperty")] = sCustomProperty;
3458 while(true)
3460 xmlreader::XmlReader::Result res = reader.nextItem(
3461 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3463 if (res == xmlreader::XmlReader::Result::Done)
3464 break;
3466 if (res == xmlreader::XmlReader::Result::Begin)
3468 if (name == "child")
3470 size_t nChildMenuIdx = m_aMenus.size();
3471 handleChild(nullptr, reader);
3472 assert(m_aMenus.size() > nChildMenuIdx && "menu not inserted");
3473 pSubMenu = dynamic_cast<PopupMenu*>(m_aMenus[nChildMenuIdx].m_pMenu.get());
3475 else
3477 ++nLevel;
3478 if (name == "property")
3479 collectProperty(reader, aProperties);
3480 else if (name == "accelerator")
3481 collectAccelerator(reader, aAccelerators);
3485 if (res == xmlreader::XmlReader::Result::End)
3487 --nLevel;
3490 if (!nLevel)
3491 break;
3494 insertMenuObject(pParent, pSubMenu, sClass, sID, aProperties, aAccelerators);
3497 void VclBuilder::handleSizeGroup(xmlreader::XmlReader &reader)
3499 m_pParserState->m_aSizeGroups.emplace_back();
3500 SizeGroup &rSizeGroup = m_pParserState->m_aSizeGroups.back();
3502 int nLevel = 1;
3504 while(true)
3506 xmlreader::Span name;
3507 int nsId;
3509 xmlreader::XmlReader::Result res = reader.nextItem(
3510 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3512 if (res == xmlreader::XmlReader::Result::Done)
3513 break;
3515 if (res == xmlreader::XmlReader::Result::Begin)
3517 ++nLevel;
3518 if (name == "widget")
3520 while (reader.nextAttribute(&nsId, &name))
3522 if (name == "name")
3524 name = reader.getAttributeValue(false);
3525 OString sWidget(name.begin, name.length);
3526 sal_Int32 nDelim = sWidget.indexOf(':');
3527 if (nDelim != -1)
3528 sWidget = sWidget.copy(0, nDelim);
3529 rSizeGroup.m_aWidgets.push_back(sWidget);
3533 else
3535 if (name == "property")
3536 collectProperty(reader, rSizeGroup.m_aProperties);
3540 if (res == xmlreader::XmlReader::Result::End)
3542 --nLevel;
3545 if (!nLevel)
3546 break;
3550 namespace
3552 vcl::KeyCode makeKeyCode(const std::pair<OString,OString> &rKey)
3554 bool bShift = rKey.second.indexOf("GDK_SHIFT_MASK") != -1;
3555 bool bMod1 = rKey.second.indexOf("GDK_CONTROL_MASK") != -1;
3556 bool bMod2 = rKey.second.indexOf("GDK_MOD1_MASK") != -1;
3557 bool bMod3 = rKey.second.indexOf("GDK_MOD2_MASK") != -1;
3559 if (rKey.first == "Insert")
3560 return vcl::KeyCode(KEY_INSERT, bShift, bMod1, bMod2, bMod3);
3561 else if (rKey.first == "Delete")
3562 return vcl::KeyCode(KEY_DELETE, bShift, bMod1, bMod2, bMod3);
3564 assert (rKey.first.getLength() == 1);
3565 sal_Char cChar = rKey.first.toChar();
3567 if (cChar >= 'a' && cChar <= 'z')
3568 return vcl::KeyCode(KEY_A + (cChar - 'a'), bShift, bMod1, bMod2, bMod3);
3569 else if (cChar >= 'A' && cChar <= 'Z')
3570 return vcl::KeyCode(KEY_A + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3571 else if (cChar >= '0' && cChar <= '9')
3572 return vcl::KeyCode(KEY_0 + (cChar - 'A'), bShift, bMod1, bMod2, bMod3);
3574 return vcl::KeyCode(cChar, bShift, bMod1, bMod2, bMod3);
3578 void VclBuilder::insertMenuObject(Menu *pParent, PopupMenu *pSubMenu, const OString &rClass, const OString &rID,
3579 stringmap &rProps, accelmap &rAccels)
3581 sal_uInt16 nOldCount = pParent->GetItemCount();
3582 sal_uInt16 nNewId = ++m_pParserState->m_nLastMenuItemId;
3584 if(rClass == "NotebookBarAddonsMenuMergePoint")
3586 NotebookBarAddonsMerger::MergeNotebookBarMenuAddons(pParent, nNewId, rID, *m_pNotebookBarAddonsItem);
3587 m_pParserState->m_nLastMenuItemId = pParent->GetItemCount();
3589 else if (rClass == "GtkMenuItem")
3591 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3592 OUString aCommand(extractActionName(rProps));
3593 pParent->InsertItem(nNewId, sLabel, MenuItemBits::NONE , rID);
3594 pParent->SetItemCommand(nNewId, aCommand);
3595 if (pSubMenu)
3596 pParent->SetPopupMenu(nNewId, pSubMenu);
3598 else if (rClass == "GtkCheckMenuItem")
3600 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3601 OUString aCommand(extractActionName(rProps));
3602 pParent->InsertItem(nNewId, sLabel, MenuItemBits::CHECKABLE, rID);
3603 pParent->SetItemCommand(nNewId, aCommand);
3605 else if (rClass == "GtkRadioMenuItem")
3607 OUString sLabel(BuilderUtils::convertMnemonicMarkup(extractLabel(rProps)));
3608 OUString aCommand(extractActionName(rProps));
3609 pParent->InsertItem(nNewId, sLabel, MenuItemBits::AUTOCHECK | MenuItemBits::RADIOCHECK, rID);
3610 pParent->SetItemCommand(nNewId, aCommand);
3612 else if (rClass == "GtkSeparatorMenuItem")
3614 pParent->InsertSeparator(rID);
3617 SAL_WARN_IF(nOldCount == pParent->GetItemCount(), "vcl.layout", "probably need to implement " << rClass);
3619 if (nOldCount != pParent->GetItemCount())
3621 pParent->SetHelpId(nNewId, m_sHelpRoot + rID);
3623 for (auto const& prop : rProps)
3625 const OString &rKey = prop.first;
3626 const OUString &rValue = prop.second;
3628 if (rKey == "tooltip-markup")
3629 pParent->SetTipHelpText(nNewId, rValue);
3630 else if (rKey == "tooltip-text")
3631 pParent->SetTipHelpText(nNewId, rValue);
3632 else if (rKey == "visible")
3633 pParent->ShowItem(nNewId, toBool(rValue));
3634 else
3635 SAL_INFO("vcl.layout", "unhandled property: " << rKey);
3638 for (auto const& accel : rAccels)
3640 const OString &rSignal = accel.first;
3641 const auto &rValue = accel.second;
3643 if (rSignal == "activate")
3644 pParent->SetAccelKey(nNewId, makeKeyCode(rValue));
3645 else
3646 SAL_INFO("vcl.layout", "unhandled accelerator for: " << rSignal);
3650 rProps.clear();
3653 /// Insert items to a ComboBox or a ListBox.
3654 /// They have no common ancestor that would have 'InsertEntry()', so use a template.
3655 template<typename T> static bool insertItems(vcl::Window *pWindow, VclBuilder::stringmap &rMap,
3656 std::vector<std::unique_ptr<OUString>>& rUserData,
3657 const std::vector<ComboBoxTextItem> &rItems)
3659 T *pContainer = dynamic_cast<T*>(pWindow);
3660 if (!pContainer)
3661 return false;
3663 sal_uInt16 nActiveId = extractActive(rMap);
3664 for (auto const& item : rItems)
3666 sal_Int32 nPos = pContainer->InsertEntry(item.m_sItem);
3667 if (!item.m_sId.isEmpty())
3669 rUserData.emplace_back(std::make_unique<OUString>(OUString::fromUtf8(item.m_sId)));
3670 pContainer->SetEntryData(nPos, rUserData.back().get());
3673 if (nActiveId < rItems.size())
3674 pContainer->SelectEntryPos(nActiveId);
3676 return true;
3679 VclPtr<vcl::Window> VclBuilder::handleObject(vcl::Window *pParent, xmlreader::XmlReader &reader)
3681 OString sClass;
3682 OString sID;
3683 OUString sCustomProperty;
3685 xmlreader::Span name;
3686 int nsId;
3688 while (reader.nextAttribute(&nsId, &name))
3690 if (name == "class")
3692 name = reader.getAttributeValue(false);
3693 sClass = OString(name.begin, name.length);
3695 else if (name == "id")
3697 name = reader.getAttributeValue(false);
3698 sID = OString(name.begin, name.length);
3699 if (m_bLegacy)
3701 sal_Int32 nDelim = sID.indexOf(':');
3702 if (nDelim != -1)
3704 sCustomProperty = OUString::fromUtf8(sID.copy(nDelim+1));
3705 sID = sID.copy(0, nDelim);
3711 if (sClass == "GtkListStore" || sClass == "GtkTreeStore")
3713 handleListStore(reader, sID, sClass);
3714 return nullptr;
3716 else if (sClass == "GtkMenu")
3718 handleMenu(reader, sID, false);
3719 return nullptr;
3721 else if (sClass == "GtkMenuBar")
3723 VclPtr<Menu> xMenu = handleMenu(reader, sID, true);
3724 if (SystemWindow* pTopLevel = pParent ? pParent->GetSystemWindow() : nullptr)
3725 pTopLevel->SetMenuBar(dynamic_cast<MenuBar*>(xMenu.get()));
3726 return nullptr;
3728 else if (sClass == "GtkSizeGroup")
3730 handleSizeGroup(reader);
3731 return nullptr;
3733 else if (sClass == "AtkObject")
3735 handleAtkObject(reader, pParent);
3736 return nullptr;
3739 int nLevel = 1;
3741 stringmap aProperties, aPangoAttributes;
3742 stringmap aAtkAttributes;
3743 std::vector<ComboBoxTextItem> aItems;
3745 if (!sCustomProperty.isEmpty())
3746 aProperties[OString("customproperty")] = sCustomProperty;
3748 VclPtr<vcl::Window> pCurrentChild;
3749 while(true)
3751 xmlreader::XmlReader::Result res = reader.nextItem(
3752 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3754 if (res == xmlreader::XmlReader::Result::Done)
3755 break;
3757 if (res == xmlreader::XmlReader::Result::Begin)
3759 if (name == "child")
3761 if (!pCurrentChild)
3763 pCurrentChild = insertObject(pParent, sClass, sID,
3764 aProperties, aPangoAttributes, aAtkAttributes);
3766 handleChild(pCurrentChild, reader);
3768 else if (name == "items")
3769 aItems = handleItems(reader);
3770 else if (name == "style")
3772 int nPriority = 0;
3773 std::vector<vcl::EnumContext::Context> aContext = handleStyle(reader, nPriority);
3774 if (nPriority != 0)
3776 vcl::IPrioritable* pPrioritable = dynamic_cast<vcl::IPrioritable*>(pCurrentChild.get());
3777 SAL_WARN_IF(!pPrioritable, "vcl", "priority set for not supported item");
3778 if (pPrioritable)
3779 pPrioritable->SetPriority(nPriority);
3781 if (!aContext.empty())
3783 vcl::IContext* pContextControl = dynamic_cast<vcl::IContext*>(pCurrentChild.get());
3784 SAL_WARN_IF(!pContextControl, "vcl", "context set for not supported item");
3785 if (pContextControl)
3786 pContextControl->SetContext(aContext);
3789 else
3791 ++nLevel;
3792 if (name == "property")
3793 collectProperty(reader, aProperties);
3794 else if (name == "attribute")
3795 collectPangoAttribute(reader, aPangoAttributes);
3796 else if (name == "relation")
3797 collectAtkRelationAttribute(reader, aAtkAttributes);
3798 else if (name == "role")
3799 collectAtkRoleAttribute(reader, aAtkAttributes);
3800 else if (name == "action-widget")
3801 handleActionWidget(reader);
3805 if (res == xmlreader::XmlReader::Result::End)
3807 --nLevel;
3810 if (!nLevel)
3811 break;
3814 if (sClass == "GtkAdjustment")
3816 m_pParserState->m_aAdjustments[sID] = aProperties;
3817 return nullptr;
3819 else if (sClass == "GtkTextBuffer")
3821 m_pParserState->m_aTextBuffers[sID] = aProperties;
3822 return nullptr;
3825 if (!pCurrentChild)
3827 pCurrentChild = insertObject(pParent, sClass, sID, aProperties,
3828 aPangoAttributes, aAtkAttributes);
3831 if (!aItems.empty())
3833 // try to fill-in the items
3834 if (!insertItems<ComboBox>(pCurrentChild, aProperties, m_aUserData, aItems))
3835 insertItems<ListBox>(pCurrentChild, aProperties, m_aUserData, aItems);
3838 return pCurrentChild;
3841 void VclBuilder::handlePacking(vcl::Window *pCurrent, vcl::Window *pParent, xmlreader::XmlReader &reader)
3843 xmlreader::Span name;
3844 int nsId;
3846 int nLevel = 1;
3848 while(true)
3850 xmlreader::XmlReader::Result res = reader.nextItem(
3851 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3853 if (res == xmlreader::XmlReader::Result::Done)
3854 break;
3856 if (res == xmlreader::XmlReader::Result::Begin)
3858 ++nLevel;
3859 if (name == "property")
3860 applyPackingProperty(pCurrent, pParent, reader);
3863 if (res == xmlreader::XmlReader::Result::End)
3865 --nLevel;
3868 if (!nLevel)
3869 break;
3873 void VclBuilder::applyPackingProperty(vcl::Window *pCurrent,
3874 vcl::Window *pParent,
3875 xmlreader::XmlReader &reader)
3877 if (!pCurrent)
3878 return;
3880 //ToolBoxItems are not true widgets just elements
3881 //of the ToolBox itself
3882 ToolBox *pToolBoxParent = nullptr;
3883 if (pCurrent == pParent)
3884 pToolBoxParent = dynamic_cast<ToolBox*>(pParent);
3886 xmlreader::Span name;
3887 int nsId;
3889 if (pCurrent->GetType() == WindowType::SCROLLWINDOW)
3891 auto aFind = m_pParserState->m_aRedundantParentWidgets.find(VclPtr<vcl::Window>(pCurrent));
3892 if (aFind != m_pParserState->m_aRedundantParentWidgets.end())
3894 pCurrent = aFind->second;
3895 assert(pCurrent);
3899 while (reader.nextAttribute(&nsId, &name))
3901 if (name == "name")
3903 name = reader.getAttributeValue(false);
3904 OString sKey(name.begin, name.length);
3905 sKey = sKey.replace('_', '-');
3906 reader.nextItem(
3907 xmlreader::XmlReader::Text::Raw, &name, &nsId);
3908 OString sValue(name.begin, name.length);
3910 if (sKey == "expand" || sKey == "resize")
3912 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3913 if (pToolBoxParent)
3914 pToolBoxParent->SetItemExpand(m_pParserState->m_nLastToolbarId, bTrue);
3915 else
3916 pCurrent->set_expand(bTrue);
3917 continue;
3920 if (pToolBoxParent)
3921 continue;
3923 if (sKey == "fill")
3925 bool bTrue = (!sValue.isEmpty() && (sValue[0] == 't' || sValue[0] == 'T' || sValue[0] == '1'));
3926 pCurrent->set_fill(bTrue);
3928 else if (sKey == "pack-type")
3930 VclPackType ePackType = (!sValue.isEmpty() && (sValue[0] == 'e' || sValue[0] == 'E')) ? VclPackType::End : VclPackType::Start;
3931 pCurrent->set_pack_type(ePackType);
3933 else if (sKey == "left-attach")
3935 pCurrent->set_grid_left_attach(sValue.toInt32());
3937 else if (sKey == "top-attach")
3939 pCurrent->set_grid_top_attach(sValue.toInt32());
3941 else if (sKey == "width")
3943 pCurrent->set_grid_width(sValue.toInt32());
3945 else if (sKey == "height")
3947 pCurrent->set_grid_height(sValue.toInt32());
3949 else if (sKey == "padding")
3951 pCurrent->set_padding(sValue.toInt32());
3953 else if (sKey == "position")
3955 set_window_packing_position(pCurrent, sValue.toInt32());
3957 else if (sKey == "secondary")
3959 pCurrent->set_secondary(toBool(sValue));
3961 else if (sKey == "non-homogeneous")
3963 pCurrent->set_non_homogeneous(toBool(sValue));
3965 else if (sKey == "homogeneous")
3967 pCurrent->set_non_homogeneous(!toBool(sValue));
3969 else
3971 SAL_WARN("vcl.layout", "unknown packing: " << sKey);
3977 std::vector<vcl::EnumContext::Context> VclBuilder::handleStyle(xmlreader::XmlReader &reader, int &nPriority)
3979 std::vector<vcl::EnumContext::Context> aContext;
3981 xmlreader::Span name;
3982 int nsId;
3984 int nLevel = 1;
3986 while(true)
3988 xmlreader::XmlReader::Result res = reader.nextItem(
3989 xmlreader::XmlReader::Text::NONE, &name, &nsId);
3991 if (res == xmlreader::XmlReader::Result::Done)
3992 break;
3994 if (res == xmlreader::XmlReader::Result::Begin)
3996 ++nLevel;
3997 if (name == "class")
3999 OString classStyle = getStyleClass(reader);
4001 if (classStyle.startsWith("context-"))
4003 OString sContext = classStyle.copy(classStyle.indexOf('-') + 1);
4004 OUString sContext2(sContext.getStr(), sContext.getLength(), RTL_TEXTENCODING_UTF8);
4005 aContext.push_back(vcl::EnumContext::GetContextEnum(sContext2));
4007 else if (classStyle.startsWith("priority-"))
4009 OString aPriority = classStyle.copy(classStyle.indexOf('-') + 1);
4010 OUString aPriority2(aPriority.getStr(), aPriority.getLength(), RTL_TEXTENCODING_UTF8);
4011 nPriority = aPriority2.toInt32();
4013 else
4015 SAL_WARN("vcl.layout", "unknown class: " << classStyle);
4020 if (res == xmlreader::XmlReader::Result::End)
4022 --nLevel;
4025 if (!nLevel)
4026 break;
4029 return aContext;
4032 OString VclBuilder::getStyleClass(xmlreader::XmlReader &reader)
4034 xmlreader::Span name;
4035 int nsId;
4036 OString aRet;
4038 while (reader.nextAttribute(&nsId, &name))
4040 if (name == "name")
4042 name = reader.getAttributeValue(false);
4043 aRet = OString (name.begin, name.length);
4047 return aRet;
4050 void VclBuilder::collectProperty(xmlreader::XmlReader &reader, stringmap &rMap) const
4052 xmlreader::Span name;
4053 int nsId;
4055 OString sProperty, sContext;
4057 bool bTranslated = false;
4059 while (reader.nextAttribute(&nsId, &name))
4061 if (name == "name")
4063 name = reader.getAttributeValue(false);
4064 sProperty = OString(name.begin, name.length);
4066 else if (name == "context")
4068 name = reader.getAttributeValue(false);
4069 sContext = OString(name.begin, name.length);
4071 else if (name == "translatable" && reader.getAttributeValue(false) == "yes")
4073 bTranslated = true;
4077 reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4078 OString sValue(name.begin, name.length);
4079 OUString sFinalValue;
4080 if (bTranslated)
4082 if (!sContext.isEmpty())
4083 sValue = sContext + "\004" + sValue;
4084 sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
4086 else
4087 sFinalValue = OUString::fromUtf8(sValue);
4089 if (!sProperty.isEmpty())
4091 sProperty = sProperty.replace('_', '-');
4092 if (m_pStringReplace)
4093 sFinalValue = (*m_pStringReplace)(sFinalValue);
4094 rMap[sProperty] = sFinalValue;
4098 void VclBuilder::handleActionWidget(xmlreader::XmlReader &reader)
4100 xmlreader::Span name;
4101 int nsId;
4103 OString sResponse;
4105 while (reader.nextAttribute(&nsId, &name))
4107 if (name == "response")
4109 name = reader.getAttributeValue(false);
4110 sResponse = OString(name.begin, name.length);
4114 reader.nextItem(xmlreader::XmlReader::Text::Raw, &name, &nsId);
4115 OString sID(name.begin, name.length);
4116 sal_Int32 nDelim = sID.indexOf(':');
4117 if (nDelim != -1)
4118 sID = sID.copy(0, nDelim);
4119 set_response(sID, sResponse.toInt32());
4122 void VclBuilder::collectAccelerator(xmlreader::XmlReader &reader, accelmap &rMap)
4124 xmlreader::Span name;
4125 int nsId;
4127 OString sProperty;
4128 OString sValue;
4129 OString sModifiers;
4131 while (reader.nextAttribute(&nsId, &name))
4133 if (name == "key")
4135 name = reader.getAttributeValue(false);
4136 sValue = OString(name.begin, name.length);
4138 else if (name == "signal")
4140 name = reader.getAttributeValue(false);
4141 sProperty = OString(name.begin, name.length);
4143 else if (name == "modifiers")
4145 name = reader.getAttributeValue(false);
4146 sModifiers = OString(name.begin, name.length);
4150 if (!sProperty.isEmpty() && !sValue.isEmpty())
4152 rMap[sProperty] = std::make_pair(sValue, sModifiers);
4156 vcl::Window *VclBuilder::get_widget_root()
4158 return m_aChildren.empty() ? nullptr : m_aChildren[0].m_pWindow.get();
4161 vcl::Window *VclBuilder::get_by_name(const OString& sID)
4163 for (auto const& child : m_aChildren)
4165 if (child.m_sID == sID)
4166 return child.m_pWindow;
4169 return nullptr;
4172 PopupMenu *VclBuilder::get_menu(const OString& sID)
4174 for (auto const& menu : m_aMenus)
4176 if (menu.m_sID == sID)
4177 return dynamic_cast<PopupMenu*>(menu.m_pMenu.get());
4180 return nullptr;
4183 void VclBuilder::set_response(const OString& sID, short nResponse)
4185 switch (nResponse)
4187 case -5:
4188 nResponse = RET_OK;
4189 break;
4190 case -6:
4191 nResponse = RET_CANCEL;
4192 break;
4193 case -7:
4194 nResponse = RET_CLOSE;
4195 break;
4196 case -8:
4197 nResponse = RET_YES;
4198 break;
4199 case -9:
4200 nResponse = RET_NO;
4201 break;
4202 case -11:
4203 nResponse = RET_HELP;
4204 break;
4205 default:
4206 assert(nResponse >= 100 && "keep non-canned responses in range 100+ to avoid collision with vcl RET_*");
4207 break;
4210 for (const auto & child : m_aChildren)
4212 if (child.m_sID == sID)
4214 PushButton* pPushButton = dynamic_cast<PushButton*>(child.m_pWindow.get());
4215 assert(pPushButton);
4216 Dialog* pDialog = pPushButton->GetParentDialog();
4217 assert(pDialog);
4218 pDialog->add_button(pPushButton, nResponse, false);
4219 return;
4223 assert(false);
4226 void VclBuilder::delete_by_name(const OString& sID)
4228 auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4229 [&sID](WinAndId& rItem) { return rItem.m_sID == sID; });
4230 if (aI != m_aChildren.end())
4232 aI->m_pWindow.disposeAndClear();
4233 m_aChildren.erase(aI);
4237 void VclBuilder::delete_by_window(vcl::Window *pWindow)
4239 drop_ownership(pWindow);
4240 pWindow->disposeOnce();
4243 void VclBuilder::drop_ownership(const vcl::Window *pWindow)
4245 auto aI = std::find_if(m_aChildren.begin(), m_aChildren.end(),
4246 [&pWindow](WinAndId& rItem) { return rItem.m_pWindow == pWindow; });
4247 if (aI != m_aChildren.end())
4248 m_aChildren.erase(aI);
4251 OString VclBuilder::get_by_window(const vcl::Window *pWindow) const
4253 for (auto const& child : m_aChildren)
4255 if (child.m_pWindow == pWindow)
4256 return child.m_sID;
4259 return OString();
4262 VclBuilder::PackingData VclBuilder::get_window_packing_data(const vcl::Window *pWindow) const
4264 //We've stored the return of new Control, some of these get
4265 //border windows placed around them which are what you get
4266 //from GetChild, so scoot up a level if necessary to get the
4267 //window whose position value we have
4268 const vcl::Window *pPropHolder = pWindow->ImplGetWindow();
4270 for (auto const& child : m_aChildren)
4272 if (child.m_pWindow == pPropHolder)
4273 return child.m_aPackingData;
4276 return PackingData();
4279 void VclBuilder::set_window_packing_position(const vcl::Window *pWindow, sal_Int32 nPosition)
4281 for (auto & child : m_aChildren)
4283 if (child.m_pWindow == pWindow)
4284 child.m_aPackingData.m_nPosition = nPosition;
4288 const VclBuilder::ListStore *VclBuilder::get_model_by_name(const OString& sID) const
4290 std::map<OString, ListStore>::const_iterator aI = m_pParserState->m_aModels.find(sID);
4291 if (aI != m_pParserState->m_aModels.end())
4292 return &(aI->second);
4293 return nullptr;
4296 const VclBuilder::TextBuffer *VclBuilder::get_buffer_by_name(const OString& sID) const
4298 std::map<OString, TextBuffer>::const_iterator aI = m_pParserState->m_aTextBuffers.find(sID);
4299 if (aI != m_pParserState->m_aTextBuffers.end())
4300 return &(aI->second);
4301 return nullptr;
4304 const VclBuilder::Adjustment *VclBuilder::get_adjustment_by_name(const OString& sID) const
4306 std::map<OString, Adjustment>::const_iterator aI = m_pParserState->m_aAdjustments.find(sID);
4307 if (aI != m_pParserState->m_aAdjustments.end())
4308 return &(aI->second);
4309 return nullptr;
4312 void VclBuilder::mungeModel(ComboBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4314 for (auto const& entry : rStore.m_aEntries)
4316 const ListStore::row &rRow = entry;
4317 sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4318 if (rRow.size() > 1)
4320 if (m_bLegacy)
4322 sal_IntPtr nValue = rRow[1].toInt32();
4323 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4325 else
4327 if (!rRow[1].isEmpty())
4329 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4330 rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4335 if (nActiveId < rStore.m_aEntries.size())
4336 rTarget.SelectEntryPos(nActiveId);
4339 void VclBuilder::mungeModel(ListBox &rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4341 for (auto const& entry : rStore.m_aEntries)
4343 const ListStore::row &rRow = entry;
4344 sal_uInt16 nEntry = rTarget.InsertEntry(rRow[0]);
4345 if (rRow.size() > 1)
4347 if (m_bLegacy)
4349 sal_IntPtr nValue = rRow[1].toInt32();
4350 rTarget.SetEntryData(nEntry, reinterpret_cast<void*>(nValue));
4352 else
4354 if (!rRow[1].isEmpty())
4356 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4357 rTarget.SetEntryData(nEntry, m_aUserData.back().get());
4362 if (nActiveId < rStore.m_aEntries.size())
4363 rTarget.SelectEntryPos(nActiveId);
4366 void VclBuilder::mungeModel(SvTabListBox& rTarget, const ListStore &rStore, sal_uInt16 nActiveId)
4368 for (auto const& entry : rStore.m_aEntries)
4370 const ListStore::row &rRow = entry;
4371 auto pEntry = rTarget.InsertEntry(rRow[0]);
4372 if (rRow.size() > 1)
4374 if (m_bLegacy)
4376 sal_IntPtr nValue = rRow[1].toInt32();
4377 pEntry->SetUserData(reinterpret_cast<void*>(nValue));
4379 else
4381 if (!rRow[1].isEmpty())
4383 m_aUserData.emplace_back(std::make_unique<OUString>(rRow[1]));
4384 pEntry->SetUserData(m_aUserData.back().get());
4389 if (nActiveId < rStore.m_aEntries.size())
4391 SvTreeListEntry* pEntry = rTarget.GetEntry(nullptr, nActiveId);
4392 rTarget.Select(pEntry);
4396 void VclBuilder::mungeAdjustment(NumericFormatter &rTarget, const Adjustment &rAdjustment)
4398 int nMul = rtl_math_pow10Exp(1, rTarget.GetDecimalDigits());
4400 for (auto const& elem : rAdjustment)
4402 const OString &rKey = elem.first;
4403 const OUString &rValue = elem.second;
4405 if (rKey == "upper")
4407 sal_Int64 nUpper = rValue.toDouble() * nMul;
4408 rTarget.SetMax(nUpper);
4409 rTarget.SetLast(nUpper);
4411 else if (rKey == "lower")
4413 sal_Int64 nLower = rValue.toDouble() * nMul;
4414 rTarget.SetMin(nLower);
4415 rTarget.SetFirst(nLower);
4417 else if (rKey == "value")
4419 sal_Int64 nValue = rValue.toDouble() * nMul;
4420 rTarget.SetValue(nValue);
4422 else if (rKey == "step-increment")
4424 sal_Int64 nSpinSize = rValue.toDouble() * nMul;
4425 rTarget.SetSpinSize(nSpinSize);
4427 else
4429 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4434 void VclBuilder::mungeAdjustment(FormattedField &rTarget, const Adjustment &rAdjustment)
4436 for (auto const& elem : rAdjustment)
4438 const OString &rKey = elem.first;
4439 const OUString &rValue = elem.second;
4441 if (rKey == "upper")
4443 rTarget.SetMaxValue(rValue.toDouble());
4445 else if (rKey == "lower")
4447 rTarget.SetMinValue(rValue.toDouble());
4449 else if (rKey == "value")
4451 rTarget.SetValue(rValue.toDouble());
4453 else if (rKey == "step-increment")
4455 rTarget.SetSpinSize(rValue.toDouble());
4457 else
4459 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4464 void VclBuilder::mungeAdjustment(TimeField &rTarget, const Adjustment &rAdjustment)
4466 for (auto const& elem : rAdjustment)
4468 const OString &rKey = elem.first;
4469 const OUString &rValue = elem.second;
4471 if (rKey == "upper")
4473 tools::Time aUpper(rValue.toInt32());
4474 rTarget.SetMax(aUpper);
4475 rTarget.SetLast(aUpper);
4477 else if (rKey == "lower")
4479 tools::Time aLower(rValue.toInt32());
4480 rTarget.SetMin(aLower);
4481 rTarget.SetFirst(aLower);
4483 else if (rKey == "value")
4485 tools::Time aValue(rValue.toInt32());
4486 rTarget.SetTime(aValue);
4488 else
4490 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4495 void VclBuilder::mungeAdjustment(DateField &rTarget, const Adjustment &rAdjustment)
4497 for (auto const& elem : rAdjustment)
4499 const OString &rKey = elem.first;
4500 const OUString &rValue = elem.second;
4502 if (rKey == "upper")
4504 Date aUpper(rValue.toInt32());
4505 rTarget.SetMax(aUpper);
4506 rTarget.SetLast(aUpper);
4508 else if (rKey == "lower")
4510 Date aLower(rValue.toInt32());
4511 rTarget.SetMin(aLower);
4512 rTarget.SetFirst(aLower);
4514 else if (rKey == "value")
4516 Date aValue(rValue.toInt32());
4517 rTarget.SetDate(aValue);
4519 else
4521 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4526 void VclBuilder::mungeAdjustment(ScrollBar &rTarget, const Adjustment &rAdjustment)
4528 for (auto const& elem : rAdjustment)
4530 const OString &rKey = elem.first;
4531 const OUString &rValue = elem.second;
4533 if (rKey == "upper")
4534 rTarget.SetRangeMax(rValue.toInt32());
4535 else if (rKey == "lower")
4536 rTarget.SetRangeMin(rValue.toInt32());
4537 else if (rKey == "value")
4538 rTarget.SetThumbPos(rValue.toInt32());
4539 else if (rKey == "step-increment")
4540 rTarget.SetLineSize(rValue.toInt32());
4541 else if (rKey == "page-increment")
4542 rTarget.SetPageSize(rValue.toInt32());
4543 else
4545 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4550 void VclBuilder::mungeAdjustment(Slider& rTarget, const Adjustment& rAdjustment)
4552 for (auto const& elem : rAdjustment)
4554 const OString &rKey = elem.first;
4555 const OUString &rValue = elem.second;
4557 if (rKey == "upper")
4558 rTarget.SetRangeMax(rValue.toInt32());
4559 else if (rKey == "lower")
4560 rTarget.SetRangeMin(rValue.toInt32());
4561 else if (rKey == "value")
4562 rTarget.SetThumbPos(rValue.toInt32());
4563 else if (rKey == "step-increment")
4564 rTarget.SetLineSize(rValue.toInt32());
4565 else if (rKey == "page-increment")
4566 rTarget.SetPageSize(rValue.toInt32());
4567 else
4569 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4574 void VclBuilder::mungeTextBuffer(VclMultiLineEdit &rTarget, const TextBuffer &rTextBuffer)
4576 for (auto const& elem : rTextBuffer)
4578 const OString &rKey = elem.first;
4579 const OUString &rValue = elem.second;
4581 if (rKey == "text")
4582 rTarget.SetText(rValue);
4583 else
4585 SAL_INFO("vcl.layout", "unhandled property :" << rKey);
4590 VclBuilder::ParserState::ParserState()
4591 : m_nLastToolbarId(0)
4592 , m_nLastMenuItemId(0)
4595 VclBuilder::MenuAndId::MenuAndId(const OString &rId, Menu *pMenu)
4596 : m_sID(rId)
4597 , m_pMenu(pMenu)
4600 VclBuilder::MenuAndId::~MenuAndId() {}
4602 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */