Coverage Report

Created: 2025-07-07 10:01

/src/libreoffice/sfx2/source/sidebar/ResourceManager.cxx
Line
Count
Source (jump to first uncovered line)
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
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sidebar/DeckDescriptor.hxx>
21
#include <sidebar/PanelDescriptor.hxx>
22
#include <sfx2/sidebar/ResourceManager.hxx>
23
#include <sidebar/Tools.hxx>
24
25
#include <officecfg/Office/Common.hxx>
26
#include <officecfg/Office/UI/Sidebar.hxx>
27
#include <unotools/confignode.hxx>
28
#include <comphelper/lok.hxx>
29
#include <comphelper/processfactory.hxx>
30
#include <comphelper/namedvaluecollection.hxx>
31
#include <comphelper/sequence.hxx>
32
#include <comphelper/types.hxx>
33
34
#include <comphelper/diagnose_ex.hxx>
35
#include <sal/log.hxx>
36
#include <o3tl/string_view.hxx>
37
38
#include <com/sun/star/frame/ModuleManager.hpp>
39
#include <com/sun/star/ui/XSidebarPanel.hpp>
40
#include <com/sun/star/ui/XUpdateModel.hpp>
41
42
#include <map>
43
44
using namespace css;
45
using namespace css::uno;
46
47
namespace sfx2::sidebar {
48
49
namespace
50
{
51
52
OUString getString(utl::OConfigurationNode const & aNode, const OUString& rNodeName)
53
0
{
54
0
    return comphelper::getString(aNode.getNodeValue(rNodeName));
55
0
}
56
sal_Int32 getInt32(utl::OConfigurationNode const & aNode, const OUString& rNodeName)
57
0
{
58
0
    return comphelper::getINT32(aNode.getNodeValue(rNodeName));
59
0
}
60
bool getBool(utl::OConfigurationNode const & aNode, const OUString& rNodeName)
61
0
{
62
0
    return comphelper::getBOOL(aNode.getNodeValue(rNodeName));
63
0
}
64
65
css::uno::Sequence<OUString> BuildContextList (const ContextList& rContextList)
66
0
{
67
0
    const ::std::vector<ContextList::Entry>& entries = rContextList.GetEntries();
68
69
0
    css::uno::Sequence<OUString> result(entries.size());
70
0
    auto resultRange = asNonConstRange(result);
71
0
    tools::Long i = 0;
72
73
0
    for (auto const& entry : entries)
74
0
    {
75
0
        OUString appName = entry.maContext.msApplication;
76
0
        OUString contextName = entry.maContext.msContext;
77
0
        OUString menuCommand = entry.msMenuCommand;
78
79
0
        OUString visibility;
80
0
        if (entry.mbIsInitiallyVisible)
81
0
            visibility = "visible";
82
0
        else
83
0
            visibility = "hidden";
84
85
0
        OUString element = appName + ", " + contextName +", " + visibility;
86
87
0
        if (!menuCommand.isEmpty())
88
0
            element += ", "+menuCommand;
89
90
0
        resultRange[i] = element;
91
92
0
        ++i;
93
0
    }
94
95
0
    return result;
96
97
0
}
98
99
} //end anonymous namespace
100
101
ResourceManager::ResourceManager()
102
0
{
103
0
    ReadDeckList();
104
0
    ReadPanelList();
105
0
    ReadLastActive();
106
0
}
107
108
ResourceManager::~ResourceManager()
109
0
{
110
0
}
111
112
void ResourceManager::InitDeckContext(const Context& rContext)
113
0
{
114
0
    for (auto const& deck : maDecks)
115
0
    {
116
0
        const ContextList::Entry* pMatchingEntry = deck->maContextList.GetMatch(rContext);
117
118
0
        bool bIsEnabled;
119
0
        if (pMatchingEntry)
120
0
            bIsEnabled = pMatchingEntry->mbIsInitiallyVisible;
121
0
        else
122
0
            bIsEnabled = false;
123
124
0
        deck->mbIsEnabled = bIsEnabled;
125
0
    }
126
0
}
127
128
std::shared_ptr<DeckDescriptor> ResourceManager::ImplGetDeckDescriptor(std::u16string_view rsDeckId) const
129
0
{
130
0
    for (auto const& deck : maDecks)
131
0
    {
132
0
        if (deck->mbHiddenInViewerMode && officecfg::Office::Common::Misc::ViewerAppMode::get())
133
0
            continue;
134
0
        if (deck->mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get())
135
0
            continue;
136
0
        if (deck->msId == rsDeckId)
137
0
            return deck;
138
0
    }
139
0
    return nullptr;
140
0
}
141
142
std::shared_ptr<DeckDescriptor> ResourceManager::GetDeckDescriptor(std::u16string_view rsDeckId) const
143
0
{
144
0
    return ImplGetDeckDescriptor( rsDeckId );
145
0
}
146
147
std::shared_ptr<PanelDescriptor> ResourceManager::ImplGetPanelDescriptor(std::u16string_view rsPanelId) const
148
0
{
149
0
    for (auto const& panel : maPanels)
150
0
    {
151
0
        if (panel->msId == rsPanelId)
152
0
            return panel;
153
0
    }
154
0
    return nullptr;
155
0
}
156
157
std::shared_ptr<PanelDescriptor> ResourceManager::GetPanelDescriptor(std::u16string_view rsPanelId) const
158
0
{
159
0
    return ImplGetPanelDescriptor( rsPanelId );
160
0
}
161
162
const ResourceManager::DeckContextDescriptorContainer& ResourceManager::GetMatchingDecks (
163
                                                            DeckContextDescriptorContainer& rDecks,
164
                                                            const Context& rContext,
165
                                                            const bool bIsDocumentReadOnly,
166
                                                            const Reference<frame::XController>& rxController)
167
0
{
168
0
    ReadLegacyAddons(rxController);
169
170
0
    std::multimap<sal_Int32,DeckContextDescriptor> aOrderedIds;
171
0
    for (auto const& deck : maDecks)
172
0
    {
173
0
        if (deck->mbHiddenInViewerMode && officecfg::Office::Common::Misc::ViewerAppMode::get())
174
0
            continue;
175
0
        if (deck->mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get())
176
0
            continue;
177
178
0
        const DeckDescriptor& rDeckDescriptor (*deck);
179
0
        if (rDeckDescriptor.maContextList.GetMatch(rContext) == nullptr)
180
0
            continue;
181
182
0
        DeckContextDescriptor aDeckContextDescriptor;
183
0
        aDeckContextDescriptor.msId = rDeckDescriptor.msId;
184
185
0
        aDeckContextDescriptor.mbIsEnabled = (! bIsDocumentReadOnly || IsDeckEnabled(rDeckDescriptor.msId, rContext, rxController) )
186
0
                                             && rDeckDescriptor.mbIsEnabled;
187
188
189
0
        aOrderedIds.emplace(rDeckDescriptor.mnOrderIndex, aDeckContextDescriptor);
190
0
    }
191
192
0
    for (auto const& orderId : aOrderedIds)
193
0
    {
194
0
        rDecks.push_back(orderId.second);
195
0
    }
196
197
0
    return rDecks;
198
0
}
199
200
const ResourceManager::PanelContextDescriptorContainer& ResourceManager::GetMatchingPanels (
201
                                                            PanelContextDescriptorContainer& rPanelIds,
202
                                                            const Context& rContext,
203
                                                            std::u16string_view sDeckId,
204
                                                            const Reference<frame::XController>& rxController)
205
0
{
206
0
    ReadLegacyAddons(rxController);
207
208
0
    std::multimap<sal_Int32, PanelContextDescriptor> aOrderedIds;
209
0
    for (auto const& panel : maPanels)
210
0
    {
211
0
        const PanelDescriptor& rPanelDescriptor (*panel);
212
0
        if (rPanelDescriptor.mbExperimental && !officecfg::Office::Common::Misc::ExperimentalMode::get())
213
0
            continue;
214
0
        if ( rPanelDescriptor.msDeckId != sDeckId )
215
0
            continue;
216
217
0
        const ContextList::Entry* pEntry = rPanelDescriptor.maContextList.GetMatch(rContext);
218
0
        if (pEntry == nullptr)
219
0
            continue;
220
221
0
        PanelContextDescriptor aPanelContextDescriptor;
222
0
        aPanelContextDescriptor.msId = rPanelDescriptor.msId;
223
0
        aPanelContextDescriptor.msMenuCommand = pEntry->msMenuCommand;
224
0
        aPanelContextDescriptor.mbIsInitiallyVisible = pEntry->mbIsInitiallyVisible;
225
0
        aPanelContextDescriptor.mbShowForReadOnlyDocuments = rPanelDescriptor.mbShowForReadOnlyDocuments;
226
0
        aOrderedIds.emplace(rPanelDescriptor.mnOrderIndex, aPanelContextDescriptor);
227
0
    }
228
229
0
    for (auto const& orderId : aOrderedIds)
230
0
    {
231
0
        rPanelIds.push_back(orderId.second);
232
0
    }
233
234
0
    return rPanelIds;
235
0
}
236
237
const OUString& ResourceManager::GetLastActiveDeck( const Context& rContext )
238
0
{
239
0
    assert(!comphelper::LibreOfficeKit::isActive());
240
0
    if( maLastActiveDecks.find( rContext.msApplication ) == maLastActiveDecks.end())
241
0
        return maLastActiveDecks[u"any"_ustr];
242
0
    else
243
0
        return maLastActiveDecks[rContext.msApplication];
244
0
}
245
246
void ResourceManager::SetLastActiveDeck( const Context& rContext, const OUString &rsDeckId )
247
0
{
248
0
    assert(!comphelper::LibreOfficeKit::isActive());
249
0
    maLastActiveDecks[rContext.msApplication] = rsDeckId;
250
0
}
251
252
void ResourceManager::ReadDeckList()
253
0
{
254
0
    const utl::OConfigurationTreeRoot aDeckRootNode(
255
0
                                        comphelper::getProcessComponentContext(),
256
0
                                        u"org.openoffice.Office.UI.Sidebar/Content/DeckList"_ustr,
257
0
                                        false);
258
0
    if (!aDeckRootNode.isValid())
259
0
        return;
260
261
0
    const Sequence<OUString> aDeckNodeNames (aDeckRootNode.getNodeNames());
262
0
    maDecks.clear();
263
0
    for (const OUString& aDeckName : aDeckNodeNames)
264
0
    {
265
0
        if (comphelper::LibreOfficeKit::isActive())
266
0
        {
267
            // Hide these decks in LOK as they aren't fully functional.
268
0
            if (aDeckName == "GalleryDeck")
269
0
                continue;
270
0
        }
271
272
0
        const utl::OConfigurationNode aDeckNode(aDeckRootNode.openNode(aDeckName));
273
0
        if (!aDeckNode.isValid())
274
0
            continue;
275
276
0
        maDecks.push_back(std::make_shared<DeckDescriptor>());
277
0
        DeckDescriptor& rDeckDescriptor (*maDecks.back());
278
279
0
        rDeckDescriptor.msTitle = getString(aDeckNode, u"Title"_ustr);
280
0
        rDeckDescriptor.msId = getString(aDeckNode, u"Id"_ustr);
281
0
        rDeckDescriptor.msIconURL = getString(aDeckNode, u"IconURL"_ustr);
282
0
        rDeckDescriptor.msHighContrastIconURL = getString(aDeckNode, u"HighContrastIconURL"_ustr);
283
0
        rDeckDescriptor.msTitleBarIconURL = getString(aDeckNode, u"TitleBarIconURL"_ustr);
284
0
        rDeckDescriptor.msHighContrastTitleBarIconURL = getString(aDeckNode, u"HighContrastTitleBarIconURL"_ustr);
285
0
        rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
286
0
        rDeckDescriptor.msHelpId = "SIDEBAR_" + rDeckDescriptor.msId.toAsciiUpperCase();
287
0
        rDeckDescriptor.mnOrderIndex = getInt32(aDeckNode, u"OrderIndex"_ustr);
288
0
        rDeckDescriptor.mbExperimental = getBool(aDeckNode, u"IsExperimental"_ustr);
289
0
        rDeckDescriptor.mbHiddenInViewerMode = getBool(aDeckNode, u"HiddenInViewer"_ustr);
290
291
0
        rDeckDescriptor.msNodeName = aDeckName;
292
293
0
        ReadContextList(
294
0
            aDeckNode,
295
0
            rDeckDescriptor.maContextList,
296
0
            OUString());
297
298
0
    }
299
0
}
300
301
void ResourceManager::SaveDecksSettings(const Context& rContext)
302
0
{
303
0
    if (comphelper::LibreOfficeKit::isActive())
304
0
        return;
305
306
0
    for (auto const& deck : maDecks)
307
0
    {
308
0
       const ContextList::Entry* pMatchingEntry = deck->maContextList.GetMatch(rContext);
309
0
       if (pMatchingEntry)
310
0
       {
311
0
            std::shared_ptr<DeckDescriptor> xDeckDesc = GetDeckDescriptor(deck->msId);
312
0
            if (xDeckDesc)
313
0
                SaveDeckSettings(xDeckDesc.get());
314
0
       }
315
316
0
    }
317
0
}
318
319
void ResourceManager::SaveDeckSettings(const DeckDescriptor* pDeckDesc)
320
0
{
321
0
    const utl::OConfigurationTreeRoot aDeckRootNode(
322
0
                                    comphelper::getProcessComponentContext(),
323
0
                                    u"org.openoffice.Office.UI.Sidebar/Content/DeckList"_ustr,
324
0
                                    true);
325
0
    if (!aDeckRootNode.isValid())
326
0
        return;
327
328
    // save deck settings
329
330
0
    ::uno::Sequence< OUString > sContextList = BuildContextList(pDeckDesc->maContextList);
331
332
0
    utl::OConfigurationNode aDeckNode (aDeckRootNode.openNode(pDeckDesc->msNodeName));
333
334
0
    css::uno::Any aTitle(Any(pDeckDesc->msTitle));
335
0
    css::uno::Any aOrder(Any(pDeckDesc->mnOrderIndex));
336
0
    css::uno::Any aContextList(sContextList);
337
338
0
    bool bChanged = false;
339
0
    if (aTitle != aDeckNode.getNodeValue(u"Title"_ustr))
340
0
    {
341
0
        aDeckNode.setNodeValue(u"Title"_ustr, aTitle);
342
0
        bChanged = true;
343
0
    }
344
0
    if (aOrder != aDeckNode.getNodeValue(u"OrderIndex"_ustr))
345
0
    {
346
0
        aDeckNode.setNodeValue(u"OrderIndex"_ustr, aOrder);
347
0
        bChanged = true;
348
0
    }
349
0
    if (aContextList != aDeckNode.getNodeValue(u"ContextList"_ustr))
350
0
    {
351
0
        aDeckNode.setNodeValue(u"ContextList"_ustr, aContextList);
352
0
        bChanged = true;
353
0
    }
354
355
0
    if (bChanged)
356
0
        aDeckRootNode.commit();
357
358
    // save panel settings
359
360
0
    const utl::OConfigurationTreeRoot aPanelRootNode(
361
0
                                    comphelper::getProcessComponentContext(),
362
0
                                    u"org.openoffice.Office.UI.Sidebar/Content/PanelList"_ustr,
363
0
                                    true);
364
365
0
    if (!aPanelRootNode.isValid())
366
0
        return;
367
368
0
    if (!pDeckDesc->mpDeck) // the deck has not been edited
369
0
        return;
370
371
0
    SharedPanelContainer rPanels = pDeckDesc->mpDeck->GetPanels();
372
373
0
    bChanged = false;
374
0
    for (auto const& panel : rPanels)
375
0
    {
376
0
        OUString panelId = panel->GetId();
377
0
        std::shared_ptr<PanelDescriptor> xPanelDesc = GetPanelDescriptor(panelId);
378
379
0
        ::uno::Sequence< OUString > sPanelContextList = BuildContextList(xPanelDesc->maContextList);
380
381
0
        utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(xPanelDesc->msNodeName));
382
383
0
        aTitle <<= xPanelDesc->msTitle;
384
0
        aOrder <<= xPanelDesc->mnOrderIndex;
385
0
        aContextList <<= sPanelContextList;
386
387
0
        if (aTitle != aPanelNode.getNodeValue(u"Title"_ustr))
388
0
        {
389
0
            aPanelNode.setNodeValue(u"Title"_ustr, aTitle);
390
0
            bChanged = true;
391
0
        }
392
0
        if (aOrder != aPanelNode.getNodeValue(u"OrderIndex"_ustr))
393
0
        {
394
0
            aPanelNode.setNodeValue(u"OrderIndex"_ustr, aOrder);
395
0
            bChanged = true;
396
0
        }
397
0
        if (aContextList != aPanelNode.getNodeValue(u"ContextList"_ustr))
398
0
        {
399
0
            aPanelNode.setNodeValue(u"ContextList"_ustr, aContextList);
400
0
            bChanged = true;
401
0
        }
402
0
    }
403
404
0
    if (bChanged)
405
0
        aPanelRootNode.commit();
406
0
}
407
408
void ResourceManager::SaveLastActiveDeck(const Context& rContext, const OUString& rActiveDeck)
409
0
{
410
0
    if (comphelper::LibreOfficeKit::isActive())
411
0
        return;
412
413
0
    maLastActiveDecks[rContext.msApplication] = rActiveDeck;
414
415
0
    std::set<OUString> aLastActiveDecks;
416
0
    for ( auto const & rEntry : maLastActiveDecks )
417
0
        aLastActiveDecks.insert( rEntry.first + "," +  rEntry.second);
418
419
0
    std::shared_ptr<comphelper::ConfigurationChanges> cfgWriter( comphelper::ConfigurationChanges::create() );
420
421
0
    officecfg::Office::UI::Sidebar::Content::LastActiveDeck::set(comphelper::containerToSequence(aLastActiveDecks), cfgWriter);
422
0
    cfgWriter->commit();
423
424
0
}
425
426
void ResourceManager::ReadPanelList()
427
0
{
428
0
    const utl::OConfigurationTreeRoot aPanelRootNode(
429
0
                                        comphelper::getProcessComponentContext(),
430
0
                                        u"org.openoffice.Office.UI.Sidebar/Content/PanelList"_ustr,
431
0
                                        false);
432
0
    if (!aPanelRootNode.isValid())
433
0
        return;
434
435
0
    const Sequence<OUString> aPanelNodeNames (aPanelRootNode.getNodeNames());
436
0
    maPanels.clear();
437
0
    for (const auto& rPanelNodeName : aPanelNodeNames)
438
0
    {
439
0
        const utl::OConfigurationNode aPanelNode (aPanelRootNode.openNode(rPanelNodeName));
440
0
        if (!aPanelNode.isValid())
441
0
            continue;
442
443
0
        if (comphelper::LibreOfficeKit::isActive())
444
0
        {
445
            // Hide these panels in LOK as they aren't fully functional.
446
0
            OUString aPanelId = getString(aPanelNode, u"Id"_ustr);
447
0
            if (aPanelId == "PageStylesPanel" || aPanelId == "PageHeaderPanel"
448
0
                || aPanelId == "PageFooterPanel")
449
0
                continue;
450
0
        }
451
452
0
        maPanels.push_back(std::make_shared<PanelDescriptor>());
453
0
        PanelDescriptor& rPanelDescriptor(*maPanels.back());
454
455
0
        rPanelDescriptor.msTitle = getString(aPanelNode, u"Title"_ustr);
456
0
        rPanelDescriptor.mbIsTitleBarOptional = getBool(aPanelNode, u"TitleBarIsOptional"_ustr);
457
0
        rPanelDescriptor.msId = getString(aPanelNode, u"Id"_ustr);
458
0
        rPanelDescriptor.msDeckId = getString(aPanelNode, u"DeckId"_ustr);
459
0
        rPanelDescriptor.msTitleBarIconURL = getString(aPanelNode, u"TitleBarIconURL"_ustr);
460
0
        rPanelDescriptor.msHighContrastTitleBarIconURL = getString(aPanelNode, u"HighContrastTitleBarIconURL"_ustr);
461
0
        rPanelDescriptor.msImplementationURL = getString(aPanelNode, u"ImplementationURL"_ustr);
462
0
        rPanelDescriptor.mnOrderIndex = getInt32(aPanelNode, u"OrderIndex"_ustr);
463
0
        rPanelDescriptor.mbShowForReadOnlyDocuments = getBool(aPanelNode, u"ShowForReadOnlyDocument"_ustr);
464
0
        rPanelDescriptor.mbWantsCanvas = getBool(aPanelNode, u"WantsCanvas"_ustr);
465
0
        rPanelDescriptor.mbWantsAWT = getBool(aPanelNode, u"WantsAWT"_ustr);
466
0
        rPanelDescriptor.mbExperimental = getBool(aPanelNode, u"IsExperimental"_ustr);
467
0
        const OUString sDefaultMenuCommand(getString(aPanelNode, u"DefaultMenuCommand"_ustr));
468
469
0
        rPanelDescriptor.msNodeName = rPanelNodeName;
470
471
0
        ReadContextList(aPanelNode, rPanelDescriptor.maContextList, sDefaultMenuCommand);
472
0
    }
473
0
}
474
475
void ResourceManager::ReadLastActive()
476
0
{
477
0
    if (comphelper::LibreOfficeKit::isActive())
478
0
        return;
479
480
0
    const Sequence <OUString> aLastActive (officecfg::Office::UI::Sidebar::Content::LastActiveDeck::get());
481
482
0
    for (const auto& rDeckInfo : aLastActive)
483
0
    {
484
0
        sal_Int32 nCharIdx = rDeckInfo.lastIndexOf(',');
485
0
        if ( nCharIdx <= 0 || (nCharIdx == rDeckInfo.getLength() - 1) )
486
0
        {
487
0
            SAL_WARN("sfx.sidebar", "Expecting 2 values separated by comma");
488
0
            continue;
489
0
        }
490
491
0
        const OUString sApplicationName = rDeckInfo.copy( 0, nCharIdx );
492
0
        vcl::EnumContext::Application eApplication (vcl::EnumContext::GetApplicationEnum(sApplicationName));
493
0
        const OUString sLastUsed = rDeckInfo.copy( nCharIdx + 1 );
494
495
        // guard against garbage in place of application
496
0
        if (eApplication != vcl::EnumContext::Application::NONE)
497
0
            maLastActiveDecks.insert( std::make_pair(sApplicationName, sLastUsed ) );
498
0
    }
499
500
    // Set up a default for Math - will do nothing if already set
501
0
    for (const auto& aOverrideDeck : GetDeckOverrides())
502
0
        maLastActiveDecks.emplace(aOverrideDeck.first, aOverrideDeck.second);
503
0
}
504
505
void ResourceManager::SetupOverrides()
506
0
{
507
0
    maApplicationDeckOverrides = {
508
0
        { vcl::EnumContext::GetApplicationName(vcl::EnumContext::Application::Formula), "ElementsDeck" }
509
0
    };
510
0
}
511
512
void ResourceManager::ReadContextList (
513
                        const utl::OConfigurationNode& rParentNode,
514
                        ContextList& rContextList,
515
                        const OUString& rsDefaultMenuCommand)
516
0
{
517
0
    const Any aValue = rParentNode.getNodeValue(u"ContextList"_ustr);
518
0
    Sequence<OUString> aValues;
519
0
    if (!(aValue >>= aValues))
520
0
        return;
521
522
0
    for (const OUString& sValue : aValues)
523
0
    {
524
0
        sal_Int32 nCharacterIndex (0);
525
0
        const OUString sApplicationName (o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex)));
526
0
        if (nCharacterIndex < 0)
527
0
        {
528
0
            if (sApplicationName.getLength() == 0)
529
0
            {
530
                // This is a valid case: in the XML file the separator
531
                // was used as terminator.  Using it in the last line
532
                // creates an additional but empty entry.
533
0
                break;
534
0
            }
535
0
            else
536
0
            {
537
0
                SAL_WARN("sfx.sidebar", "expecting three or four values per ContextList entry, separated by comma, entries: " << aValues);
538
0
                continue;
539
0
            }
540
0
        }
541
542
0
        const OUString sContextName(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex)));
543
0
        if (nCharacterIndex < 0)
544
0
        {
545
0
            SAL_WARN("sfx.sidebar", "expecting three or four values per ContextList entry, separated by comma");
546
0
            continue;
547
0
        }
548
549
0
        const std::u16string_view sInitialState(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex)));
550
551
        // The fourth argument is optional.
552
0
        const OUString sMenuCommandOverride(
553
0
            nCharacterIndex < 0
554
0
                ? OUString()
555
0
                : OUString(o3tl::trim(o3tl::getToken(sValue, 0, ',', nCharacterIndex))));
556
557
0
        const OUString sMenuCommand(
558
0
            sMenuCommandOverride.getLength() > 0
559
0
                ? (sMenuCommandOverride == "none"
560
0
                    ? OUString()
561
0
                    : sMenuCommandOverride)
562
0
                : rsDefaultMenuCommand);
563
564
        // Setup a list of application enums.  Note that the
565
        // application name may result in more than one value (eg
566
        // DrawImpress will result in two enums, one for Draw and one
567
        // for Impress).
568
0
        std::vector<vcl::EnumContext::Application> aApplications;
569
0
        vcl::EnumContext::Application eApplication (vcl::EnumContext::GetApplicationEnum(sApplicationName));
570
571
0
        if (eApplication == vcl::EnumContext::Application::NONE
572
0
            && sApplicationName != vcl::EnumContext::GetApplicationName(vcl::EnumContext::Application::NONE))
573
0
        {
574
            // Handle some special names: abbreviations that make
575
            // context descriptions more readable.
576
0
            if (sApplicationName == "Writer")
577
0
                aApplications.push_back(vcl::EnumContext::Application::Writer);
578
0
            else if (sApplicationName == "Calc")
579
0
                aApplications.push_back(vcl::EnumContext::Application::Calc);
580
0
            else if (sApplicationName == "Draw")
581
0
                aApplications.push_back(vcl::EnumContext::Application::Draw);
582
0
            else if (sApplicationName == "Impress")
583
0
                aApplications.push_back(vcl::EnumContext::Application::Impress);
584
0
            else if (sApplicationName == "Chart")
585
0
                aApplications.push_back(vcl::EnumContext::Application::Chart);
586
0
            else if (sApplicationName == "Math")
587
0
                aApplications.push_back(vcl::EnumContext::Application::Formula);
588
0
            else if (sApplicationName == "DrawImpress")
589
0
            {
590
                // A special case among the special names:  it is
591
                // common to use the same context descriptions for
592
                // both Draw and Impress.  This special case helps to
593
                // avoid duplication in the .xcu file.
594
0
                aApplications.push_back(vcl::EnumContext::Application::Draw);
595
0
                aApplications.push_back(vcl::EnumContext::Application::Impress);
596
0
            }
597
0
            else if (sApplicationName == "WriterVariants")
598
0
            {
599
                // Another special case for all Writer variants.
600
0
                aApplications.push_back(vcl::EnumContext::Application::Writer);
601
0
                aApplications.push_back(vcl::EnumContext::Application::WriterGlobal);
602
0
                aApplications.push_back(vcl::EnumContext::Application::WriterWeb);
603
0
                aApplications.push_back(vcl::EnumContext::Application::WriterXML);
604
0
                aApplications.push_back(vcl::EnumContext::Application::WriterForm);
605
0
                aApplications.push_back(vcl::EnumContext::Application::WriterReport);
606
0
            }
607
0
            else
608
0
            {
609
0
                SAL_WARN("sfx.sidebar", "application name " << sApplicationName << " not recognized");
610
0
                continue;
611
0
            }
612
0
        }
613
0
        else
614
0
        {
615
            // No conversion of the application name necessary.
616
0
            aApplications.push_back(eApplication);
617
0
        }
618
619
        // Setup the actual context enum.
620
0
        const vcl::EnumContext::Context eContext (vcl::EnumContext::GetContextEnum(sContextName));
621
0
        if (eContext == vcl::EnumContext::Context::Unknown)
622
0
        {
623
0
            SAL_WARN("sfx.sidebar", "context name " << sContextName << " not recognized");
624
0
            continue;
625
0
        }
626
627
        // Setup the flag that controls whether a deck/pane is
628
        // initially visible/expanded.
629
0
        bool bIsInitiallyVisible;
630
0
        if (sInitialState == u"visible")
631
0
            bIsInitiallyVisible = true;
632
0
        else if (sInitialState == u"hidden")
633
0
            bIsInitiallyVisible = false;
634
0
        else
635
0
        {
636
0
            SAL_WARN("sfx.sidebar", "unrecognized state");
637
0
            continue;
638
0
        }
639
640
641
        // Add context descriptors.
642
0
        for (auto const& application : aApplications)
643
0
        {
644
0
            if (application != vcl::EnumContext::Application::NONE)
645
0
            {
646
0
                rContextList.AddContextDescription(
647
0
                    Context(
648
0
                        vcl::EnumContext::GetApplicationName(application),
649
0
                        vcl::EnumContext::GetContextName(eContext)),
650
0
                    bIsInitiallyVisible,
651
0
                    sMenuCommand);
652
0
            }
653
0
        }
654
0
    }
655
0
}
656
657
void ResourceManager::ReadLegacyAddons (const Reference<frame::XController>& rxController)
658
0
{
659
    // Get module name for given frame.
660
0
    OUString sModuleName (Tools::GetModuleName(rxController));
661
0
    if (sModuleName.getLength() == 0)
662
0
        return;
663
0
    if (maProcessedApplications.find(sModuleName) != maProcessedApplications.end())
664
0
    {
665
        // Addons for this application have already been read.
666
        // There is nothing more to do.
667
0
        return;
668
0
    }
669
670
    // Mark module as processed.  Even when there is an error that
671
    // prevents the configuration data from being read, this error
672
    // will not be triggered a second time.
673
0
    maProcessedApplications.insert(sModuleName);
674
675
    // Get access to the configuration root node for the application.
676
0
    utl::OConfigurationTreeRoot aLegacyRootNode (GetLegacyAddonRootNode(sModuleName));
677
0
    if (!aLegacyRootNode.isValid())
678
0
        return;
679
680
    // Process child nodes.
681
0
    std::vector<OUString> aMatchingNodeNames;
682
0
    GetToolPanelNodeNames(aMatchingNodeNames, aLegacyRootNode);
683
0
    const sal_Int32 nCount (aMatchingNodeNames.size());
684
0
    for (sal_Int32 nReadIndex(0); nReadIndex<nCount; ++nReadIndex)
685
0
    {
686
0
        const OUString& rsNodeName (aMatchingNodeNames[nReadIndex]);
687
0
        const utl::OConfigurationNode aChildNode (aLegacyRootNode.openNode(rsNodeName));
688
0
        if (!aChildNode.isValid())
689
0
            continue;
690
691
0
        if ( rsNodeName == "private:resource/toolpanel/DrawingFramework/CustomAnimations" ||
692
0
             rsNodeName == "private:resource/toolpanel/DrawingFramework/Layouts" ||
693
0
             rsNodeName == "private:resource/toolpanel/DrawingFramework/MasterPages" ||
694
0
             rsNodeName == "private:resource/toolpanel/DrawingFramework/SlideTransitions" ||
695
0
             rsNodeName == "private:resource/toolpanel/DrawingFramework/TableDesign" )
696
0
          continue;
697
698
0
        maDecks.push_back(std::make_shared<DeckDescriptor>());
699
0
        DeckDescriptor& rDeckDescriptor(*maDecks.back());
700
0
        rDeckDescriptor.msTitle = getString(aChildNode, u"UIName"_ustr);
701
0
        rDeckDescriptor.msId = rsNodeName;
702
0
        rDeckDescriptor.msIconURL = getString(aChildNode, u"ImageURL"_ustr);
703
0
        rDeckDescriptor.msHighContrastIconURL = rDeckDescriptor.msIconURL;
704
0
        rDeckDescriptor.msTitleBarIconURL.clear();
705
0
        rDeckDescriptor.msHighContrastTitleBarIconURL.clear();
706
0
        rDeckDescriptor.msHelpText = rDeckDescriptor.msTitle;
707
0
        rDeckDescriptor.mbIsEnabled = true;
708
0
        rDeckDescriptor.mnOrderIndex = 100000 + nReadIndex;
709
0
        rDeckDescriptor.maContextList.AddContextDescription(Context(sModuleName, u"any"_ustr), true, OUString());
710
711
0
        maPanels.push_back(std::make_shared<PanelDescriptor>());
712
0
        PanelDescriptor& rPanelDescriptor(*maPanels.back());
713
0
        rPanelDescriptor.msTitle = getString(aChildNode, u"UIName"_ustr);
714
0
        rPanelDescriptor.mbIsTitleBarOptional = true;
715
0
        rPanelDescriptor.msId = rsNodeName;
716
0
        rPanelDescriptor.msDeckId = rsNodeName;
717
0
        rPanelDescriptor.msTitleBarIconURL.clear();
718
0
        rPanelDescriptor.msHighContrastTitleBarIconURL.clear();
719
0
        rPanelDescriptor.msImplementationURL = rsNodeName;
720
0
        rPanelDescriptor.mnOrderIndex = 100000 + nReadIndex;
721
0
        rPanelDescriptor.mbShowForReadOnlyDocuments = false;
722
0
        rPanelDescriptor.mbWantsCanvas = false;
723
0
        rPanelDescriptor.mbWantsAWT = true;
724
0
        rPanelDescriptor.maContextList.AddContextDescription(Context(sModuleName, u"any"_ustr), true, OUString());
725
0
    }
726
0
}
727
728
void ResourceManager::StorePanelExpansionState (
729
                        std::u16string_view rsPanelId,
730
                        const bool bExpansionState,
731
                        const Context& rContext)
732
0
{
733
0
    for (auto const& panel : maPanels)
734
0
    {
735
0
        if (panel->msId == rsPanelId)
736
0
        {
737
0
            ContextList::Entry* pEntry(panel->maContextList.GetMatch(rContext));
738
0
            if (pEntry != nullptr)
739
0
                pEntry->mbIsInitiallyVisible = bExpansionState;
740
0
        }
741
0
    }
742
0
}
743
744
utl::OConfigurationTreeRoot ResourceManager::GetLegacyAddonRootNode (const OUString& rsModuleName)
745
0
{
746
0
    try
747
0
    {
748
0
        const Reference<XComponentContext>& xContext(comphelper::getProcessComponentContext());
749
0
        const Reference<frame::XModuleManager2> xModuleAccess = frame::ModuleManager::create(xContext);
750
0
        const comphelper::NamedValueCollection aModuleProperties(xModuleAccess->getByName(rsModuleName));
751
0
        const OUString sWindowStateRef(aModuleProperties.getOrDefault(
752
0
                                       u"ooSetupFactoryWindowStateConfigRef"_ustr,
753
0
                                       OUString()));
754
755
0
        OUString aPathComposer = "org.openoffice.Office.UI." + sWindowStateRef +
756
0
            "/UIElements/States";
757
758
0
        return utl::OConfigurationTreeRoot(xContext, aPathComposer, false);
759
0
    }
760
0
    catch (const Exception&)
761
0
    {
762
0
        DBG_UNHANDLED_EXCEPTION("sfx.sidebar");
763
0
    }
764
765
0
    return utl::OConfigurationTreeRoot();
766
0
}
767
768
void ResourceManager::GetToolPanelNodeNames (
769
                        std::vector<OUString>& rMatchingNames,
770
                        const utl::OConfigurationTreeRoot& aRoot)
771
0
{
772
0
    const Sequence<OUString> aChildNodeNames (aRoot.getNodeNames());
773
0
    std::copy_if(aChildNodeNames.begin(), aChildNodeNames.end(), std::back_inserter(rMatchingNames),
774
0
        [](const OUString& rChildNodeName) { return rChildNodeName.startsWith( "private:resource/toolpanel/" ); });
775
0
}
776
777
bool ResourceManager::IsDeckEnabled (
778
                        std::u16string_view rsDeckId,
779
                        const Context& rContext,
780
                        const Reference<frame::XController>& rxController)
781
0
{
782
783
    // Check if any panel that matches the current context can be
784
    // displayed.
785
0
    PanelContextDescriptorContainer aPanelContextDescriptors;
786
787
0
    GetMatchingPanels(aPanelContextDescriptors, rContext, rsDeckId, rxController);
788
789
0
    for (auto const& panelContextDescriptor : aPanelContextDescriptors)
790
0
    {
791
0
        if (panelContextDescriptor.mbShowForReadOnlyDocuments)
792
0
            return true;
793
0
    }
794
0
    return false;
795
0
}
796
797
void ResourceManager::UpdateModel(const css::uno::Reference<css::frame::XModel>& xModel)
798
0
{
799
0
    for (auto const& deck : maDecks)
800
0
    {
801
0
        if (!deck->mpDeck)
802
0
            continue;
803
804
0
        const SharedPanelContainer& rContainer = deck->mpDeck->GetPanels();
805
806
0
        for (auto const& elem : rContainer)
807
0
        {
808
0
            css::uno::Reference<css::ui::XUpdateModel> xPanel(elem->GetPanelComponent(), css::uno::UNO_QUERY);
809
0
            if (xPanel.is()) // tdf#108814 interface is optional
810
0
            {
811
0
                xPanel->updateModel(xModel);
812
0
            }
813
0
        }
814
0
    }
815
0
}
816
817
void ResourceManager::disposeDecks()
818
0
{
819
0
    for (auto const& deck : maDecks)
820
0
    {
821
0
        deck->mpDeck.disposeAndClear();
822
0
    }
823
0
}
824
825
} // end of namespace sfx2::sidebar
826
827
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */