Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sd/source/ui/sidebar/RecentlyUsedMasterPages.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
 * 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 "RecentlyUsedMasterPages.hxx"
21
#include "MasterPageContainerProviders.hxx"
22
#include <MasterPageObserver.hxx>
23
#include "MasterPageDescriptor.hxx"
24
#include <tools/ConfigurationAccess.hxx>
25
26
#include <algorithm>
27
#include <memory>
28
#include <vector>
29
30
#include <com/sun/star/container/XNameAccess.hpp>
31
#include <com/sun/star/container/XNameContainer.hpp>
32
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
33
#include <osl/doublecheckedlocking.h>
34
#include <osl/getglobalmutex.hxx>
35
36
using namespace ::com::sun::star;
37
using namespace ::com::sun::star::uno;
38
39
namespace {
40
41
OUString GetPathToImpressConfigurationRoot()
42
0
{
43
0
    return u"/org.openoffice.Office.Impress/"_ustr;
44
0
}
45
OUString GetPathToSetNode()
46
0
{
47
0
    return u"MultiPaneGUI/ToolPanel/RecentlyUsedMasterPages"_ustr;
48
0
}
49
50
} // end of anonymous namespace
51
52
namespace sd::sidebar {
53
54
RecentlyUsedMasterPages* RecentlyUsedMasterPages::mpInstance = nullptr;
55
56
RecentlyUsedMasterPages&  RecentlyUsedMasterPages::Instance()
57
0
{
58
0
    if (mpInstance == nullptr)
59
0
    {
60
0
        ::osl::GetGlobalMutex aMutexFunctor;
61
0
        ::osl::MutexGuard aGuard (aMutexFunctor());
62
0
        if (mpInstance == nullptr)
63
0
        {
64
0
            RecentlyUsedMasterPages* pInstance = new RecentlyUsedMasterPages();
65
0
            pInstance->LateInit();
66
0
            SdGlobalResourceContainer::Instance().AddResource (
67
0
                ::std::unique_ptr<SdGlobalResource>(pInstance));
68
0
            OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
69
0
            mpInstance = pInstance;
70
0
        }
71
0
    }
72
0
    else {
73
0
        OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
74
0
    }
75
76
0
    return *mpInstance;
77
0
}
78
79
constexpr size_t gnMaxListSize(8);
80
81
RecentlyUsedMasterPages::RecentlyUsedMasterPages()
82
0
    : mpContainer(std::make_shared<MasterPageContainer>())
83
0
{
84
0
}
85
86
void RecentlyUsedMasterPages::ImplDestroy()
87
0
{
88
0
    Link<MasterPageContainerChangeEvent&,void> aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener));
89
0
    mpContainer->RemoveChangeListener(aLink);
90
91
0
    MasterPageObserver::Instance().RemoveEventListener(
92
0
        LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener));
93
0
}
94
95
RecentlyUsedMasterPages::~RecentlyUsedMasterPages()
96
0
{
97
0
    suppress_fun_call_w_exception(ImplDestroy());
98
0
}
99
100
void RecentlyUsedMasterPages::LateInit()
101
0
{
102
0
    Link<MasterPageContainerChangeEvent&,void> aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener));
103
0
    mpContainer->AddChangeListener(aLink);
104
105
0
    LoadPersistentValues ();
106
0
    MasterPageObserver::Instance().AddEventListener(
107
0
        LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener));
108
0
}
109
110
void RecentlyUsedMasterPages::LoadPersistentValues()
111
0
{
112
0
    try
113
0
    {
114
0
        sdtools::ConfigurationAccess aConfiguration (
115
0
            GetPathToImpressConfigurationRoot(),
116
0
            sdtools::ConfigurationAccess::READ_ONLY);
117
0
        Reference<container::XNameAccess> xSet (
118
0
            aConfiguration.GetConfigurationNode(GetPathToSetNode()),
119
0
            UNO_QUERY);
120
0
        if ( ! xSet.is())
121
0
            return;
122
123
0
        static constexpr OUStringLiteral sURLMemberName(u"URL");
124
0
        static constexpr OUStringLiteral sNameMemberName(u"Name");
125
0
        OUString sURL;
126
0
        OUString sName;
127
128
        // Read the names and URLs of the master pages.
129
0
        const Sequence<OUString> aKeys (xSet->getElementNames());
130
0
        mvMasterPages.clear();
131
0
        mvMasterPages.reserve(aKeys.getLength());
132
0
        for (const auto& rKey : aKeys)
133
0
        {
134
0
            Reference<container::XNameAccess> xSetItem (
135
0
                xSet->getByName(rKey), UNO_QUERY);
136
0
            if (xSetItem.is())
137
0
            {
138
0
                Any aURL (xSetItem->getByName(sURLMemberName));
139
0
                Any aName (xSetItem->getByName(sNameMemberName));
140
0
                aURL >>= sURL;
141
0
                aName >>= sName;
142
0
                SharedMasterPageDescriptor pDescriptor = std::make_shared<MasterPageDescriptor>(
143
0
                    MasterPageContainer::TEMPLATE,
144
0
                    -1,
145
0
                    sURL,
146
0
                    OUString(),
147
0
                    sName,
148
0
                    false,
149
0
                    std::make_shared<TemplatePageObjectProvider>(sURL),
150
0
                    std::make_shared<TemplatePreviewProvider>(sURL));
151
                // For user supplied templates we use a different
152
                // preview provider: The preview in the document shows
153
                // not only shapes on the master page but also shapes on
154
                // the foreground.  This is misleading and therefore
155
                // these previews are discarded and created directly
156
                // from the page objects.
157
0
                if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER)
158
0
                    pDescriptor->mpPreviewProvider = std::make_shared<PagePreviewProvider>();
159
0
                MasterPageContainer::Token aToken (mpContainer->PutMasterPage(pDescriptor));
160
0
                mvMasterPages.emplace_back(aToken,sURL,sName);
161
0
            }
162
0
        }
163
164
0
        ResolveList();
165
0
    }
166
0
    catch (Exception&)
167
0
    {
168
        // Ignore exception.
169
0
    }
170
0
}
171
172
void RecentlyUsedMasterPages::SavePersistentValues()
173
0
{
174
0
    try
175
0
    {
176
0
        sdtools::ConfigurationAccess aConfiguration (
177
0
            GetPathToImpressConfigurationRoot(),
178
0
            sdtools::ConfigurationAccess::READ_WRITE);
179
0
        Reference<container::XNameContainer> xSet (
180
0
            aConfiguration.GetConfigurationNode(GetPathToSetNode()),
181
0
            UNO_QUERY);
182
0
        if ( ! xSet.is())
183
0
            return;
184
185
        // Clear the set.
186
0
        const Sequence<OUString> aKeys (xSet->getElementNames());
187
0
        for (const auto& rKey : aKeys)
188
0
            xSet->removeByName (rKey);
189
190
        // Fill it with the URLs of this object.
191
0
        static constexpr OUStringLiteral sURLMemberName(u"URL");
192
0
        static constexpr OUStringLiteral sNameMemberName(u"Name");
193
0
        Any aValue;
194
0
        Reference<lang::XSingleServiceFactory> xChildFactory (
195
0
            xSet, UNO_QUERY);
196
0
        if ( ! xChildFactory.is())
197
0
            return;
198
0
        sal_Int32 nIndex(0);
199
0
        for (const auto& rDescriptor : mvMasterPages)
200
0
        {
201
            // Create new child.
202
0
            OUString sKey = "index_" + OUString::number(nIndex);
203
0
            Reference<container::XNameReplace> xChild(
204
0
                xChildFactory->createInstance(), UNO_QUERY);
205
0
            if (xChild.is())
206
0
            {
207
0
                xSet->insertByName (sKey, Any(xChild));
208
209
0
                aValue <<= rDescriptor.msURL;
210
0
                xChild->replaceByName (sURLMemberName, aValue);
211
212
0
                aValue <<= rDescriptor.msName;
213
0
                xChild->replaceByName (sNameMemberName, aValue);
214
0
            }
215
0
            ++nIndex;
216
0
        }
217
218
        // Write the data back to disk.
219
0
        aConfiguration.CommitChanges();
220
0
    }
221
0
    catch (Exception&)
222
0
    {
223
        // Ignore exception.
224
0
    }
225
0
}
226
227
void RecentlyUsedMasterPages::AddEventListener (const Link<LinkParamNone*,void>& rEventListener)
228
0
{
229
0
    if (::std::find (
230
0
        maListeners.begin(),
231
0
        maListeners.end(),
232
0
        rEventListener) == maListeners.end())
233
0
    {
234
0
        maListeners.push_back (rEventListener);
235
0
    }
236
0
}
237
238
void RecentlyUsedMasterPages::RemoveEventListener (const Link<LinkParamNone*,void>& rEventListener)
239
0
{
240
0
    maListeners.erase (
241
0
        ::std::find (
242
0
            maListeners.begin(),
243
0
            maListeners.end(),
244
0
            rEventListener));
245
0
}
246
247
int RecentlyUsedMasterPages::GetMasterPageCount() const
248
0
{
249
0
    return mvMasterPages.size();
250
0
}
251
252
MasterPageContainer::Token RecentlyUsedMasterPages::GetTokenForIndex (sal_uInt32 nIndex) const
253
0
{
254
0
    if(nIndex<mvMasterPages.size())
255
0
        return mvMasterPages[nIndex].maToken;
256
0
    else
257
0
        return MasterPageContainer::NIL_TOKEN;
258
0
}
259
260
void RecentlyUsedMasterPages::SendEvent()
261
0
{
262
0
    for (const auto& aLink : maListeners)
263
0
    {
264
0
        aLink.Call(nullptr);
265
0
    }
266
0
}
267
268
IMPL_LINK(RecentlyUsedMasterPages, MasterPageChangeListener,
269
    MasterPageObserverEvent&, rEvent, void)
270
0
{
271
0
    switch (rEvent.meType)
272
0
    {
273
0
        case MasterPageObserverEvent::ET_MASTER_PAGE_ADDED:
274
0
        case MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS:
275
0
            AddMasterPage(
276
0
                mpContainer->GetTokenForStyleName(rEvent.mrMasterPageName));
277
0
            break;
278
279
0
        case MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED:
280
            // Do not change the list of recently master pages (the deleted
281
            // page was recently used) but tell the listeners.  They may want
282
            // to update their lists.
283
0
            SendEvent();
284
0
            break;
285
0
    }
286
0
}
287
288
IMPL_LINK(RecentlyUsedMasterPages, MasterPageContainerChangeListener,
289
    MasterPageContainerChangeEvent&, rEvent, void)
290
0
{
291
0
    switch (rEvent.meEventType)
292
0
    {
293
0
        case MasterPageContainerChangeEvent::EventType::CHILD_ADDED:
294
0
        case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED:
295
0
        case MasterPageContainerChangeEvent::EventType::INDEX_CHANGED:
296
0
            ResolveList();
297
0
            break;
298
299
0
        default:
300
            // Ignored.
301
0
            break;
302
0
    }
303
0
}
304
305
void RecentlyUsedMasterPages::AddMasterPage (
306
    MasterPageContainer::Token aToken)
307
0
{
308
    // For the page to be inserted the token has to be valid and the page
309
    // has to have a valid URL.  This excludes master pages that do not come
310
    // from template files.
311
0
    if (aToken == MasterPageContainer::NIL_TOKEN
312
0
        || mpContainer->GetURLForToken(aToken).isEmpty())
313
0
        return;
314
315
0
    MasterPageList::iterator aIterator (
316
0
        ::std::find_if(mvMasterPages.begin(),mvMasterPages.end(),
317
0
            Descriptor::TokenComparator(aToken)));
318
0
    if (aIterator != mvMasterPages.end())
319
0
    {
320
        // When an entry for the given token already exists then remove
321
        // it now and insert it later at the head of the list.
322
0
        mvMasterPages.erase (aIterator);
323
0
    }
324
325
0
    mvMasterPages.insert(mvMasterPages.begin(),
326
0
        Descriptor(
327
0
            aToken,
328
0
            mpContainer->GetURLForToken(aToken),
329
0
            mpContainer->GetStyleNameForToken(aToken)));
330
331
    // Shorten list to maximal size.
332
0
    while (mvMasterPages.size() > gnMaxListSize)
333
0
    {
334
0
        mvMasterPages.pop_back ();
335
0
    }
336
337
0
    SavePersistentValues ();
338
0
    SendEvent();
339
0
}
340
341
void RecentlyUsedMasterPages::ResolveList()
342
0
{
343
0
    bool bNotify (false);
344
345
0
    for (auto& rDescriptor : mvMasterPages)
346
0
    {
347
0
        if (rDescriptor.maToken == MasterPageContainer::NIL_TOKEN)
348
0
        {
349
0
            MasterPageContainer::Token aToken (mpContainer->GetTokenForURL(rDescriptor.msURL));
350
0
            rDescriptor.maToken = aToken;
351
0
            if (aToken != MasterPageContainer::NIL_TOKEN)
352
0
                bNotify = true;
353
0
        }
354
0
        else
355
0
        {
356
0
            if ( ! mpContainer->HasToken(rDescriptor.maToken))
357
0
            {
358
0
                rDescriptor.maToken = MasterPageContainer::NIL_TOKEN;
359
0
                bNotify = true;
360
0
            }
361
0
        }
362
0
    }
363
364
0
    if (bNotify)
365
0
        SendEvent();
366
0
}
367
368
} // end of namespace sd::sidebar
369
370
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */