Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sd/source/ui/sidebar/MasterPageObserver.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 <MasterPageObserver.hxx>
21
22
#include <algorithm>
23
#include <iterator>
24
#include <drawdoc.hxx>
25
#include <sdpage.hxx>
26
#include <set>
27
#include <unordered_map>
28
#include <memory>
29
#include <vector>
30
#include <svl/lstner.hxx>
31
#include <osl/doublecheckedlocking.h>
32
#include <osl/getglobalmutex.hxx>
33
#include <tools/debug.hxx>
34
35
namespace sd {
36
37
class MasterPageObserver::Implementation
38
    : public SfxListener
39
{
40
public:
41
    /** The master page observer will listen to events of this document and
42
        detect changes of the use of master pages.
43
    */
44
    void RegisterDocument (SdDrawDocument& rDocument);
45
46
    /** The master page observer will stop to listen to events of this
47
        document.
48
    */
49
    void UnregisterDocument (SdDrawDocument& rDocument);
50
51
    /** Add a listener that is informed of master pages that are newly
52
        assigned to slides or become unassigned.
53
        @param rEventListener
54
            The event listener to call for future events.  Call
55
            RemoveEventListener() before the listener is destroyed.
56
    */
57
    void AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener);
58
59
    /** Remove the given listener from the list of listeners.
60
        @param rEventListener
61
            After this method returns the given listener is not called back
62
            from this object.  Passing a listener that has not
63
            been registered before is safe and is silently ignored.
64
    */
65
    void RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener);
66
67
private:
68
    ::std::vector<Link<MasterPageObserverEvent&,void>> maListeners;
69
70
    struct DrawDocHash {
71
        size_t operator()(SdDrawDocument* argument) const
72
0
        { return reinterpret_cast<sal_uIntPtr>(argument); }
73
    };
74
    typedef std::unordered_map<SdDrawDocument*,
75
                            MasterPageObserver::MasterPageNameSet,
76
                            DrawDocHash>
77
        MasterPageContainer;
78
    MasterPageContainer maUsedMasterPages;
79
80
    virtual void Notify(
81
        SfxBroadcaster& rBroadcaster,
82
        const SfxHint& rHint) override;
83
84
    void AnalyzeUsedMasterPages (SdDrawDocument& rDocument);
85
86
    void SendEvent (MasterPageObserverEvent& rEvent);
87
};
88
89
//===== MasterPageObserver ====================================================
90
91
MasterPageObserver&  MasterPageObserver::Instance()
92
0
{
93
0
    static MasterPageObserver* gInstance = []()
94
0
    {
95
0
        MasterPageObserver* pInstance = new MasterPageObserver ();
96
0
        SdGlobalResourceContainer::Instance().AddResource (
97
0
            ::std::unique_ptr<SdGlobalResource>(pInstance));
98
0
        return pInstance;
99
0
    }();
100
0
    return *gInstance;
101
0
}
102
103
void MasterPageObserver::RegisterDocument (SdDrawDocument& rDocument)
104
0
{
105
0
    mpImpl->RegisterDocument (rDocument);
106
0
}
107
108
void MasterPageObserver::UnregisterDocument (SdDrawDocument& rDocument)
109
0
{
110
0
    mpImpl->UnregisterDocument (rDocument);
111
0
}
112
113
void MasterPageObserver::AddEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener)
114
0
{
115
116
0
    mpImpl->AddEventListener (rEventListener);
117
0
}
118
119
void MasterPageObserver::RemoveEventListener (const Link<MasterPageObserverEvent&,void>& rEventListener)
120
0
{
121
0
    mpImpl->RemoveEventListener (rEventListener);
122
0
}
123
124
MasterPageObserver::MasterPageObserver()
125
0
    : mpImpl (new Implementation)
126
0
{}
127
128
MasterPageObserver::~MasterPageObserver()
129
0
{}
130
131
//===== MasterPageObserver::Implementation ====================================
132
133
void MasterPageObserver::Implementation::RegisterDocument (
134
    SdDrawDocument& rDocument)
135
0
{
136
    // Gather the names of all the master pages in the given document.
137
0
    MasterPageContainer::mapped_type aMasterPageSet;
138
0
    sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard);
139
0
    for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++)
140
0
    {
141
0
        SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard);
142
0
        if (pMasterPage != nullptr)
143
0
            aMasterPageSet.insert (pMasterPage->GetName());
144
0
    }
145
146
0
    bool bAlreadyExists = maUsedMasterPages.find(&rDocument) != maUsedMasterPages.end();
147
0
    maUsedMasterPages[&rDocument] = std::move(aMasterPageSet);
148
149
0
    if (!bAlreadyExists)
150
0
        StartListening (rDocument);
151
0
}
152
153
void MasterPageObserver::Implementation::UnregisterDocument (
154
    SdDrawDocument& rDocument)
155
0
{
156
0
    EndListening (rDocument);
157
158
0
    MasterPageContainer::iterator aMasterPageDescriptor(maUsedMasterPages.find(&rDocument));
159
0
    if(aMasterPageDescriptor != maUsedMasterPages.end())
160
0
        maUsedMasterPages.erase(aMasterPageDescriptor);
161
0
}
162
163
void MasterPageObserver::Implementation::AddEventListener (
164
    const Link<MasterPageObserverEvent&,void>& rEventListener)
165
0
{
166
0
    if (::std::find (
167
0
        maListeners.begin(),
168
0
        maListeners.end(),
169
0
        rEventListener) != maListeners.end())
170
0
        return;
171
172
0
    maListeners.push_back (rEventListener);
173
174
    // Tell the new listener about all the master pages that are
175
    // currently in use.
176
0
    for (const auto& rDocument : maUsedMasterPages)
177
0
    {
178
0
        ::std::set<OUString>::reverse_iterator aNameIterator;
179
0
        for (aNameIterator=rDocument.second.rbegin();
180
0
             aNameIterator!=rDocument.second.rend();
181
0
             ++aNameIterator)
182
0
        {
183
0
          MasterPageObserverEvent aEvent (
184
0
              MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS,
185
0
              *aNameIterator);
186
0
          SendEvent (aEvent);
187
0
        }
188
0
    }
189
0
}
190
191
void MasterPageObserver::Implementation::RemoveEventListener (
192
    const Link<MasterPageObserverEvent&,void>& rEventListener)
193
0
{
194
0
    maListeners.erase (
195
0
        ::std::find (
196
0
            maListeners.begin(),
197
0
            maListeners.end(),
198
0
            rEventListener));
199
0
}
200
201
void MasterPageObserver::Implementation::Notify(
202
    SfxBroadcaster& rBroadcaster,
203
    const SfxHint& rHint)
204
0
{
205
0
    if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint)
206
0
        return;
207
0
    const SdrHint* pSdrHint = static_cast<const SdrHint*>(&rHint);
208
209
0
    switch (pSdrHint->GetKind())
210
0
    {
211
0
        case SdrHintKind::PageOrderChange:
212
            // Process the modified set of pages only when the number of
213
            // standard and notes master pages are equal.  This test
214
            // filters out events that are sent in between the insertion
215
            // of a new standard master page and a new notes master
216
            // page.
217
0
            if (auto pDrawDocument = dynamic_cast<SdDrawDocument *>( &rBroadcaster ))
218
0
            {
219
0
                if (pDrawDocument->GetMasterSdPageCount(PageKind::Standard)
220
0
                    == pDrawDocument->GetMasterSdPageCount(PageKind::Notes))
221
0
                {
222
0
                    AnalyzeUsedMasterPages (*pDrawDocument);
223
0
                }
224
0
            }
225
0
            break;
226
227
0
        default:
228
0
            break;
229
0
    }
230
0
}
231
232
void MasterPageObserver::Implementation::AnalyzeUsedMasterPages (
233
    SdDrawDocument& rDocument)
234
0
{
235
    // Create a set of names of the master pages used by the given document.
236
0
    sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard);
237
0
    ::std::set<OUString> aCurrentMasterPages;
238
0
    for (sal_uInt16 nIndex=0; nIndex<nMasterPageCount; nIndex++)
239
0
    {
240
0
        SdPage* pMasterPage = rDocument.GetMasterSdPage (nIndex, PageKind::Standard);
241
0
        if (pMasterPage != nullptr)
242
0
            aCurrentMasterPages.insert (pMasterPage->GetName());
243
0
    }
244
245
0
    std::vector<OUString> aNewMasterPages;
246
0
    std::vector<OUString> aRemovedMasterPages;
247
0
    MasterPageContainer::iterator aOldMasterPagesDescriptor (
248
0
        maUsedMasterPages.find(&rDocument));
249
0
    if (aOldMasterPagesDescriptor == maUsedMasterPages.end())
250
0
        return;
251
252
    // Send events about the newly used master pages.
253
0
    ::std::set_difference (
254
0
        aCurrentMasterPages.begin(),
255
0
        aCurrentMasterPages.end(),
256
0
        aOldMasterPagesDescriptor->second.begin(),
257
0
        aOldMasterPagesDescriptor->second.end(),
258
0
        std::back_inserter(aNewMasterPages));
259
0
    for (const auto& aNewMasterPage : aNewMasterPages)
260
0
    {
261
0
        MasterPageObserverEvent aEvent (
262
0
            MasterPageObserverEvent::ET_MASTER_PAGE_ADDED,
263
0
            aNewMasterPage);
264
0
        SendEvent (aEvent);
265
0
    }
266
267
    // Send events about master pages that are not used any longer.
268
0
    ::std::set_difference (
269
0
        aOldMasterPagesDescriptor->second.begin(),
270
0
        aOldMasterPagesDescriptor->second.end(),
271
0
        aCurrentMasterPages.begin(),
272
0
        aCurrentMasterPages.end(),
273
0
        std::back_inserter(aRemovedMasterPages));
274
0
    for (const auto& aRemovedMasterPage : aRemovedMasterPages)
275
0
    {
276
0
        MasterPageObserverEvent aEvent (
277
0
            MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED,
278
0
            aRemovedMasterPage);
279
0
        SendEvent (aEvent);
280
0
    }
281
282
    // Store the new list of master pages.
283
0
    aOldMasterPagesDescriptor->second = std::move(aCurrentMasterPages);
284
0
}
285
286
void MasterPageObserver::Implementation::SendEvent (
287
    MasterPageObserverEvent& rEvent)
288
0
{
289
0
    for (const auto& aLink : maListeners)
290
0
    {
291
0
        aLink.Call(rEvent);
292
0
    }
293
0
}
294
295
} // end of namespace sd
296
297
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */