Coverage Report

Created: 2026-04-09 11:41

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