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