Coverage Report

Created: 2025-12-08 09:28

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