Coverage Report

Created: 2025-11-16 09:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/sdr/contact/viewcontact.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 <svx/sdr/contact/viewcontact.hxx>
21
#include <svx/sdr/contact/viewobjectcontact.hxx>
22
#include <svx/sdr/contact/objectcontact.hxx>
23
#include <basegfx/polygon/b2dpolygon.hxx>
24
#include <basegfx/polygon/b2dpolygontools.hxx>
25
#include <basegfx/color/bcolor.hxx>
26
#include <drawinglayer/primitive2d/PolygonHairlinePrimitive2D.hxx>
27
#include <osl/diagnose.h>
28
#include <tools/debug.hxx>
29
30
namespace sdr::contact
31
{
32
// Create an Object-Specific ViewObjectContact, set ViewContact and
33
// ObjectContact. Always needs to return something. Default is to create
34
// a standard ViewObjectContact containing the given ObjectContact and *this
35
ViewObjectContact& ViewContact::CreateObjectSpecificViewObjectContact(ObjectContact& rObjectContact)
36
0
{
37
0
    return *(new ViewObjectContact(rObjectContact, *this));
38
0
}
39
40
11.2M
ViewContact::ViewContact() {}
41
42
11.2M
ViewContact::~ViewContact() { deleteAllVOCs(); }
43
44
void ViewContact::deleteAllVOCs()
45
20.2M
{
46
    // get rid of all VOCs
47
    // #i84257# To avoid that each 'delete pCandidate' again uses
48
    // the local RemoveViewObjectContact with a search and removal in the
49
    // vector, simply copy and clear local vector.
50
20.2M
    std::vector<ViewObjectContact*> aLocalVOCList;
51
20.2M
    aLocalVOCList.swap(maViewObjectContactVector);
52
53
20.2M
    for (const auto& pCandidate : aLocalVOCList)
54
        // ViewObjectContacts only make sense with View and Object contacts.
55
        // When the contact to the SdrObject is deleted like in this case,
56
        // all ViewObjectContacts can be deleted, too.
57
0
        delete pCandidate;
58
59
    // assert when there were new entries added during deletion
60
20.2M
    DBG_ASSERT(maViewObjectContactVector.empty(), "Corrupted ViewObjectContactList in VC (!)");
61
62
20.2M
    mxViewIndependentPrimitive2DSequence = drawinglayer::primitive2d::Primitive2DContainer();
63
20.2M
}
64
65
// get an Object-specific ViewObjectContact for a specific
66
// ObjectContact (->View). Always needs to return something.
67
ViewObjectContact& ViewContact::GetViewObjectContact(ObjectContact& rObjectContact)
68
269k
{
69
269k
    ViewObjectContact* pRetval = nullptr;
70
269k
    const sal_uInt32 nCount(maViewObjectContactVector.size());
71
72
    // first search if there exists a VOC for the given OC
73
449k
    for (sal_uInt32 a(0); !pRetval && a < nCount; a++)
74
179k
    {
75
179k
        ViewObjectContact* pCandidate = maViewObjectContactVector[a];
76
179k
        assert(pCandidate && "Corrupted ViewObjectContactList (!)");
77
78
179k
        if (&(pCandidate->GetObjectContact()) == &rObjectContact)
79
179k
        {
80
179k
            pRetval = pCandidate;
81
179k
        }
82
179k
    }
83
84
269k
    if (!pRetval)
85
90.2k
    {
86
        // create a new one. It's inserted to the local list from the
87
        // ViewObjectContact constructor via AddViewObjectContact()
88
90.2k
        pRetval = &CreateObjectSpecificViewObjectContact(rObjectContact);
89
90.2k
    }
90
91
269k
    return *pRetval;
92
269k
}
93
94
// A new ViewObjectContact was created and shall be remembered.
95
void ViewContact::AddViewObjectContact(ViewObjectContact& rVOContact)
96
90.2k
{
97
90.2k
    maViewObjectContactVector.push_back(&rVOContact);
98
90.2k
}
99
100
// A ViewObjectContact was deleted and shall be forgotten.
101
void ViewContact::RemoveViewObjectContact(ViewObjectContact& rVOContact)
102
90.2k
{
103
90.2k
    std::vector<ViewObjectContact*>::iterator aFindResult = std::find(
104
90.2k
        maViewObjectContactVector.begin(), maViewObjectContactVector.end(), &rVOContact);
105
106
90.2k
    if (aFindResult != maViewObjectContactVector.end())
107
90.2k
    {
108
90.2k
        maViewObjectContactVector.erase(aFindResult);
109
90.2k
    }
110
90.2k
}
111
112
// Test if this ViewContact has ViewObjectContacts at all. This can
113
// be used to test if this ViewContact is visualized ATM or not
114
bool ViewContact::HasViewObjectContacts() const
115
0
{
116
0
    const sal_uInt32 nCount(maViewObjectContactVector.size());
117
118
0
    for (sal_uInt32 a(0); a < nCount; a++)
119
0
    {
120
0
        if (!maViewObjectContactVector[a]->GetObjectContact().IsPreviewRenderer())
121
0
        {
122
0
            return true;
123
0
        }
124
0
    }
125
0
    return false;
126
0
}
127
128
// Test if this ViewContact has ViewObjectContacts at all. This can
129
// be used to test if this ViewContact is visualized ATM or not
130
bool ViewContact::isAnimatedInAnyViewObjectContact() const
131
0
{
132
0
    const sal_uInt32 nCount(maViewObjectContactVector.size());
133
134
0
    for (sal_uInt32 a(0); a < nCount; a++)
135
0
    {
136
0
        if (maViewObjectContactVector[a]->isAnimated())
137
0
        {
138
0
            return true;
139
0
        }
140
0
    }
141
142
0
    return false;
143
0
}
144
145
// Access to possible sub-hierarchy and parent. GetObjectCount() default is 0L
146
// and GetViewContact default pops up an assert since it's an error if
147
// GetObjectCount has a result != 0 and it's not overridden.
148
sal_uInt32 ViewContact::GetObjectCount() const
149
4.76M
{
150
    // no sub-objects
151
4.76M
    return 0;
152
4.76M
}
153
154
ViewContact& ViewContact::GetViewContact(sal_uInt32 /*nIndex*/) const
155
0
{
156
    // This is the default implementation; call would be an error
157
0
    OSL_FAIL("ViewContact::GetViewContact: This call needs to be overridden when GetObjectCount() "
158
0
             "can return results != 0 (!)");
159
0
    return const_cast<ViewContact&>(*this);
160
0
}
161
162
ViewContact* ViewContact::GetParentContact() const
163
0
{
164
    // default has no parent
165
0
    return nullptr;
166
0
}
167
168
void ViewContact::ActionChildInserted(ViewContact& rChild)
169
2.14M
{
170
    // propagate change to all existing visualisations which
171
    // will force a VOC for the new child and invalidate its range
172
2.14M
    const sal_uInt32 nCount(maViewObjectContactVector.size());
173
174
2.14M
    for (sal_uInt32 a(0); a < nCount; a++)
175
0
    {
176
0
        ViewObjectContact* pCandidate = maViewObjectContactVector[a];
177
0
        DBG_ASSERT(pCandidate,
178
0
                   "ViewContact::GetViewObjectContact() invalid ViewObjectContactList (!)");
179
180
        // take action at all VOCs. At the VOCs ObjectContact the initial
181
        // rectangle will be invalidated at the associated OutputDevice.
182
0
        pCandidate->ActionChildInserted(rChild);
183
0
    }
184
2.14M
}
185
186
// React on changes of the object of this ViewContact
187
void ViewContact::ActionChanged()
188
55.8M
{
189
    // propagate change to all existing VOCs. This will invalidate
190
    // all drawn visualisations in all known views
191
55.8M
    const sal_uInt32 nCount(maViewObjectContactVector.size());
192
193
55.8M
    for (sal_uInt32 a(0); a < nCount; a++)
194
0
    {
195
0
        ViewObjectContact* pCandidate = maViewObjectContactVector[a];
196
0
        DBG_ASSERT(pCandidate,
197
0
                   "ViewContact::GetViewObjectContact() invalid ViewObjectContactList (!)");
198
199
0
        if (pCandidate)
200
0
        {
201
0
            pCandidate->ActionChanged();
202
0
        }
203
0
    }
204
55.8M
}
205
206
// IASS: helper for IASS invalidates
207
void ViewContact::ActionChangedIfDifferentPageView(const SdrPageView& rSdrPageView)
208
0
{
209
0
    const sal_uInt32 nCount(maViewObjectContactVector.size());
210
211
0
    for (sal_uInt32 a(0); a < nCount; a++)
212
0
    {
213
0
        ViewObjectContact* pCandidate = maViewObjectContactVector[a];
214
0
        DBG_ASSERT(pCandidate,
215
0
                   "ViewContact::GetViewObjectContact() invalid ViewObjectContactList (!)");
216
217
0
        if (pCandidate)
218
0
        {
219
0
            pCandidate->ActionChangedIfDifferentPageView(rSdrPageView);
220
0
        }
221
0
    }
222
0
}
223
224
bool ViewContact::hasMultipleViewObjectContacts() const
225
0
{
226
0
    return maViewObjectContactVector.size() > 1;
227
0
}
228
229
// access to SdrObject and/or SdrPage. May return 0L like the default
230
// implementations do. Override as needed.
231
316
SdrObject* ViewContact::TryToGetSdrObject() const { return nullptr; }
232
233
// primitive stuff
234
235
void ViewContact::createViewIndependentPrimitive2DSequence(
236
    drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
237
0
{
238
    // This is the default implementation and should never be called (see header). If this is called,
239
    // someone implemented a ViewContact (VC) visualisation object without defining the visualisation by
240
    // providing a sequence of primitives -> which cannot be correct.
241
    // Since we have no access to any known model data here, the default implementation creates a yellow placeholder
242
    // hairline polygon with a default size of (1000, 1000, 5000, 3000)
243
0
    OSL_FAIL("ViewContact::createViewIndependentPrimitive2DSequence(): Never call the fallback "
244
0
             "base implementation, this is always an error (!)");
245
0
    basegfx::B2DPolygon aOutline(
246
0
        basegfx::utils::createPolygonFromRect(basegfx::B2DRange(1000.0, 1000.0, 5000.0, 3000.0)));
247
0
    const basegfx::BColor aYellow(1.0, 1.0, 0.0);
248
0
    const drawinglayer::primitive2d::Primitive2DReference xReference(
249
0
        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(std::move(aOutline), aYellow));
250
251
0
    rVisitor.visit(xReference);
252
0
}
253
254
void ViewContact::getViewIndependentPrimitive2DContainer(
255
    drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) const
256
2.82k
{
257
    /* Local up-to-date checks. Create new list and compare.
258
        We cannot just always use the new data because the old data has cached bitmaps in it e.g. see the document in tdf#146108.
259
    */
260
2.82k
    drawinglayer::primitive2d::Primitive2DContainer xNew;
261
2.82k
    createViewIndependentPrimitive2DSequence(xNew);
262
263
2.82k
    if (!xNew.empty())
264
2.24k
    {
265
        // allow evtl. embedding in object-specific infos, e.g. Name, Title, Description
266
2.24k
        xNew = embedToObjectSpecificInformation(std::move(xNew));
267
2.24k
    }
268
269
2.82k
    if (mxViewIndependentPrimitive2DSequence != xNew)
270
1.57k
    {
271
        // has changed, copy content
272
1.57k
        const_cast<ViewContact*>(this)->mxViewIndependentPrimitive2DSequence = std::move(xNew);
273
1.57k
    }
274
275
    // return current Primitive2DContainer
276
2.82k
    rVisitor.visit(mxViewIndependentPrimitive2DSequence);
277
2.82k
}
278
279
// add Gluepoints (if available)
280
drawinglayer::primitive2d::Primitive2DContainer
281
ViewContact::createGluePointPrimitive2DSequence() const
282
0
{
283
    // default returns empty reference
284
0
    return drawinglayer::primitive2d::Primitive2DContainer();
285
0
}
286
287
drawinglayer::primitive2d::Primitive2DContainer ViewContact::embedToObjectSpecificInformation(
288
    drawinglayer::primitive2d::Primitive2DContainer aSource) const
289
0
{
290
    // nothing to do for default
291
0
    return aSource;
292
0
}
293
294
basegfx::B2DRange
295
ViewContact::getRange(const drawinglayer::geometry::ViewInformation2D& /*rViewInfo2D*/) const
296
2.82k
{
297
    // Return empty range.
298
2.82k
    return basegfx::B2DRange();
299
2.82k
}
300
301
void ViewContact::flushViewObjectContacts(bool bWithHierarchy)
302
9.05M
{
303
9.05M
    if (bWithHierarchy)
304
9.05M
    {
305
        // flush DrawingLayer hierarchy
306
9.05M
        const sal_uInt32 nCount(GetObjectCount());
307
308
17.0M
        for (sal_uInt32 a(0); a < nCount; a++)
309
7.97M
        {
310
7.97M
            ViewContact& rChild = GetViewContact(a);
311
7.97M
            rChild.flushViewObjectContacts(bWithHierarchy);
312
7.97M
        }
313
9.05M
    }
314
315
    // delete local VOCs
316
9.05M
    deleteAllVOCs();
317
9.05M
}
318
319
void ViewContact::getPrimitive2DSequenceHierarchyOfIndex(
320
    sal_uInt32 a, DisplayInfo& rDisplayInfo, ObjectContact& rObjectContact,
321
    drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor)
322
86.5k
{
323
86.5k
    const ViewObjectContact& rCandidate(GetViewContact(a).GetViewObjectContact(rObjectContact));
324
86.5k
    rCandidate.getPrimitive2DSequenceHierarchy(rDisplayInfo, rVisitor);
325
86.5k
}
326
}
327
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */