Coverage Report

Created: 2025-12-08 09:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sw/source/uibase/docvw/PostItMgr.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 <config_wasm_strip.h>
21
22
#include <boost/property_tree/json_parser.hpp>
23
24
#include <PostItMgr.hxx>
25
#include <postithelper.hxx>
26
27
#include <AnnotationWin.hxx>
28
#include "frmsidebarwincontainer.hxx"
29
#include <accmap.hxx>
30
31
#include <SidebarWindowsConsts.hxx>
32
#include "AnchorOverlayObject.hxx"
33
#include "ShadowOverlayObject.hxx"
34
35
#include <utility>
36
#include <vcl/svapp.hxx>
37
#include <vcl/outdev.hxx>
38
#include <vcl/settings.hxx>
39
40
#include <chrdlgmodes.hxx>
41
#include <viewopt.hxx>
42
#include <view.hxx>
43
#include <docsh.hxx>
44
#include <wrtsh.hxx>
45
#include <doc.hxx>
46
#include <IDocumentSettingAccess.hxx>
47
#include <IDocumentFieldsAccess.hxx>
48
#include <IDocumentRedlineAccess.hxx>
49
#if ENABLE_YRS
50
#include <IDocumentState.hxx>
51
#endif
52
#include <docstyle.hxx>
53
#include <fldbas.hxx>
54
#include <fmtfld.hxx>
55
#include <docufld.hxx>
56
#include <edtwin.hxx>
57
#include <txtfld.hxx>
58
#include <txtannotationfld.hxx>
59
#include <rootfrm.hxx>
60
#include <SwRewriter.hxx>
61
#include <tools/color.hxx>
62
#include <unotools/datetime.hxx>
63
64
#include <swmodule.hxx>
65
#include <strings.hrc>
66
#include <cmdid.h>
67
68
#include <sfx2/docfile.hxx>
69
#include <sfx2/docfilt.hxx>
70
#include <sfx2/request.hxx>
71
#include <sfx2/event.hxx>
72
#include <svl/srchitem.hxx>
73
74
#include <svl/languageoptions.hxx>
75
#include <svl/hint.hxx>
76
77
#include <svx/svdview.hxx>
78
#include <editeng/eeitem.hxx>
79
#include <editeng/langitem.hxx>
80
#include <editeng/outliner.hxx>
81
#include <editeng/outlobj.hxx>
82
83
#include <comphelper/lok.hxx>
84
#include <comphelper/string.hxx>
85
#include <officecfg/Office/Writer.hxx>
86
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
87
88
#include <annotsh.hxx>
89
#include <swabstdlg.hxx>
90
#include <pagefrm.hxx>
91
#include <officecfg/Office/Common.hxx>
92
93
#include <memory>
94
95
// distance between Anchor Y and initial note position
96
0
#define POSTIT_INITIAL_ANCHOR_DISTANCE      20
97
//distance between two postits
98
0
#define POSTIT_SPACE_BETWEEN                8
99
0
#define POSTIT_MINIMUMSIZE_WITH_META        60
100
0
#define POSTIT_SCROLL_SIDEBAR_HEIGHT        20
101
102
// if we layout more often we stop, this should never happen
103
0
#define MAX_LOOP_COUNT                      50
104
105
using namespace sw::sidebarwindows;
106
using namespace sw::annotation;
107
108
namespace {
109
110
    enum class CommentNotificationType { Add, Remove, Modify, Resolve, SearchHighlight, RedlinedDeletion };
111
112
    bool comp_pos(const std::unique_ptr<SwAnnotationItem>& a, const std::unique_ptr<SwAnnotationItem>& b)
113
0
    {
114
        // sort by anchor position
115
0
        SwPosition aPosAnchorA = a->GetAnchorPosition();
116
0
        SwPosition aPosAnchorB = b->GetAnchorPosition();
117
118
0
        bool aAnchorAInFooter = false;
119
0
        bool aAnchorBInFooter = false;
120
121
        // is the anchor placed in Footnote or the Footer?
122
0
        if( aPosAnchorA.GetNode().FindFootnoteStartNode() || aPosAnchorA.GetNode().FindFooterStartNode() )
123
0
            aAnchorAInFooter = true;
124
0
        if( aPosAnchorB.GetNode().FindFootnoteStartNode() || aPosAnchorB.GetNode().FindFooterStartNode() )
125
0
            aAnchorBInFooter = true;
126
127
        // fdo#34800
128
        // if AnchorA is in footnote, and AnchorB isn't
129
        // we do not want to change over the position
130
0
        if( aAnchorAInFooter && !aAnchorBInFooter )
131
0
            return false;
132
        // if aAnchorA is not placed in a footnote, and aAnchorB is
133
        // force a change over
134
0
        else if( !aAnchorAInFooter && aAnchorBInFooter )
135
0
            return true;
136
        // If neither or both are in the footer, compare the positions.
137
        // Since footnotes are in Inserts section of nodes array and footers
138
        // in Autotext section, all footnotes precede any footers so no need
139
        // to check that.
140
0
        else
141
0
            return aPosAnchorA < aPosAnchorB;
142
0
    }
143
144
    /// Emits LOK notification about one addition/removal/change of a comment
145
    void lcl_CommentNotification(const SwView* pView, const CommentNotificationType nType, const SwAnnotationItem* pItem, const sal_uInt32 nPostItId)
146
0
    {
147
0
        if (!comphelper::LibreOfficeKit::isActive())
148
0
            return;
149
150
0
        boost::property_tree::ptree aAnnotation;
151
0
        aAnnotation.put("action", (nType == CommentNotificationType::Add ? "Add" :
152
0
                                   (nType == CommentNotificationType::Remove ? "Remove" :
153
0
                                    (nType == CommentNotificationType::Modify ? "Modify" :
154
0
                                     (nType == CommentNotificationType::RedlinedDeletion ? "RedlinedDeletion" :
155
0
                                      (nType == CommentNotificationType::SearchHighlight ? "SearchHighlight" :
156
0
                                       (nType == CommentNotificationType::Resolve ? "Resolve" : "???")))))));
157
158
0
        aAnnotation.put("id", nPostItId);
159
0
        if (nType != CommentNotificationType::Remove && pItem != nullptr)
160
0
        {
161
0
            sw::annotation::SwAnnotationWin* pWin = pItem->mpPostIt.get();
162
163
0
            const SwPostItField* pField = pWin->GetPostItField();
164
0
            const SwRect& aRect = pWin->GetAnchorRect();
165
0
            tools::Rectangle aSVRect(aRect.Pos().getX(),
166
0
                                    aRect.Pos().getY(),
167
0
                                    aRect.Pos().getX() + aRect.SSize().Width(),
168
0
                                    aRect.Pos().getY() + aRect.SSize().Height());
169
170
0
            if (!pItem->maLayoutInfo.mPositionFromCommentAnchor)
171
0
            {
172
                // Comments on frames: anchor position is the corner position, not the whole frame.
173
0
                aSVRect.SetSize(Size(0, 0));
174
0
            }
175
176
0
            std::vector<OString> aRects;
177
0
            for (const basegfx::B2DRange& aRange : pWin->GetAnnotationTextRanges())
178
0
            {
179
0
                const SwRect rect(aRange.getMinX(), aRange.getMinY(), aRange.getWidth(), aRange.getHeight());
180
0
                aRects.push_back(rect.SVRect().toString());
181
0
            }
182
0
            const OString sRects = comphelper::string::join("; ", aRects);
183
184
0
            aAnnotation.put("id", pField->GetPostItId());
185
0
            aAnnotation.put("parentId", pField->GetParentPostItId());
186
0
            aAnnotation.put("author", pField->GetPar1().toUtf8().getStr());
187
            // Note, for just plain text we could use "text" populated by pField->GetPar2()
188
0
            aAnnotation.put("html", pWin->GetSimpleHtml());
189
0
            aAnnotation.put("resolved", pField->GetResolved() ? "true" : "false");
190
0
            aAnnotation.put("dateTime", utl::toISO8601(pField->GetDateTime().GetUNODateTime()));
191
0
            aAnnotation.put("anchorPos", aSVRect.toString());
192
0
            aAnnotation.put("textRange", sRects.getStr());
193
0
            aAnnotation.put("layoutStatus", pItem->mLayoutStatus);
194
0
            if (nType == CommentNotificationType::SearchHighlight)
195
0
            {
196
0
                ESelection aSel = pWin->GetOutlinerView()->GetSelection();
197
0
                OString sSelStr = OString::Concat(OString::number(aSel.start.nPara)) + ","
198
0
                                  + OString::number(aSel.start.nIndex) + ","
199
0
                                  + OString::number(aSel.end.nPara) + ","
200
0
                                  + OString::number(aSel.end.nIndex);
201
0
                aAnnotation.put("searchSelection", sSelStr);
202
0
            }
203
0
        }
204
0
        if (nType == CommentNotificationType::Remove && comphelper::LibreOfficeKit::isActive())
205
0
        {
206
            // Redline author is basically the author which has made the modification rather than author of the comments
207
            // This is important to know who removed the comment
208
0
            aAnnotation.put("author", SwModule::get()->GetRedlineAuthor(SwModule::get()->GetRedlineAuthor()));
209
0
        }
210
211
0
        boost::property_tree::ptree aTree;
212
0
        aTree.add_child("comment", aAnnotation);
213
0
        std::stringstream aStream;
214
0
        boost::property_tree::write_json(aStream, aTree);
215
0
        std::string aPayload = aStream.str();
216
217
0
        if (pView)
218
0
        {
219
0
            pView->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, OString(aPayload));
220
0
        }
221
0
    }
222
223
    class FilterFunctor
224
    {
225
    public:
226
        virtual bool operator()(const SwFormatField* pField) const = 0;
227
3
        virtual ~FilterFunctor() {}
228
    };
229
230
    class IsPostitField : public FilterFunctor
231
    {
232
    public:
233
        bool operator()(const SwFormatField* pField) const override
234
3
        {
235
3
            return pField->GetField()->GetTyp()->Which() == SwFieldIds::Postit;
236
3
        }
237
    };
238
239
    class IsPostitFieldWithAuthorOf : public FilterFunctor
240
    {
241
        OUString m_sAuthor;
242
    public:
243
        explicit IsPostitFieldWithAuthorOf(OUString aAuthor)
244
0
            : m_sAuthor(std::move(aAuthor))
245
0
        {
246
0
        }
247
        bool operator()(const SwFormatField* pField) const override
248
0
        {
249
0
            if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
250
0
                return false;
251
0
            return static_cast<const SwPostItField*>(pField->GetField())->GetPar1() == m_sAuthor;
252
0
        }
253
    };
254
255
    class IsPostitFieldWithPostitId : public FilterFunctor
256
    {
257
        sal_uInt32 m_nPostItId;
258
    public:
259
        explicit IsPostitFieldWithPostitId(sal_uInt32 nPostItId)
260
0
            : m_nPostItId(nPostItId)
261
0
            {}
262
263
        bool operator()(const SwFormatField* pField) const override
264
0
        {
265
0
            if (pField->GetField()->GetTyp()->Which() != SwFieldIds::Postit)
266
0
                return false;
267
0
            return static_cast<const SwPostItField*>(pField->GetField())->GetPostItId() == m_nPostItId;
268
0
        }
269
    };
270
271
    class IsFieldNotDeleted : public FilterFunctor
272
    {
273
    private:
274
        IDocumentRedlineAccess const& m_rIDRA;
275
        FilterFunctor const& m_rNext;
276
277
    public:
278
        IsFieldNotDeleted(IDocumentRedlineAccess const& rIDRA,
279
                const FilterFunctor & rNext)
280
0
            : m_rIDRA(rIDRA)
281
0
            , m_rNext(rNext)
282
0
        {
283
0
        }
284
        bool operator()(const SwFormatField* pField) const override
285
0
        {
286
0
            if (!m_rNext(pField))
287
0
                return false;
288
0
            if (!pField->GetTextField())
289
0
                return false;
290
0
            return !sw::IsFieldDeletedInModel(m_rIDRA, *pField->GetTextField());
291
0
        }
292
    };
293
294
    //Manages the passed in vector by automatically removing entries if they are deleted
295
    //and automatically adding entries if they appear in the document and match the
296
    //functor.
297
    //
298
    //This will completely refill in the case of a "anonymous" NULL pField stating
299
    //rather unhelpfully that "something changed" so you may process the same
300
    //Fields more than once.
301
    class FieldDocWatchingStack : public SfxListener
302
    {
303
        std::vector<std::unique_ptr<SwAnnotationItem>>& m_aSidebarItems;
304
        std::vector<const SwFormatField*> m_aFormatFields;
305
        SwDocShell& m_rDocShell;
306
        FilterFunctor& m_rFilter;
307
308
        virtual void Notify(SfxBroadcaster&, const SfxHint& rHint) override
309
0
        {
310
0
            if ( rHint.GetId() != SfxHintId::SwFormatField )
311
0
                return;
312
0
            const SwFormatFieldHint* pHint = static_cast<const SwFormatFieldHint*>(&rHint);
313
314
0
            bool bAllInvalidated = false;
315
0
            if (pHint->Which() == SwFormatFieldHintWhich::REMOVED)
316
0
            {
317
0
                const SwFormatField* pField = pHint->GetField();
318
0
                bAllInvalidated = pField == nullptr;
319
0
                if (!bAllInvalidated && m_rFilter(pField))
320
0
                {
321
0
                    EndListening(const_cast<SwFormatField&>(*pField));
322
0
                    std::erase(m_aFormatFields, pField);
323
0
                }
324
0
            }
325
0
            else if (pHint->Which() == SwFormatFieldHintWhich::INSERTED)
326
0
            {
327
0
                const SwFormatField* pField = pHint->GetField();
328
0
                bAllInvalidated = pField == nullptr;
329
0
                if (!bAllInvalidated && m_rFilter(pField))
330
0
                {
331
0
                    StartListening(const_cast<SwFormatField&>(*pField));
332
0
                    m_aFormatFields.push_back(pField);
333
0
                }
334
0
            }
335
336
0
            if (bAllInvalidated)
337
0
                FillVector();
338
339
0
            return;
340
0
        }
341
342
    public:
343
        FieldDocWatchingStack(std::vector<std::unique_ptr<SwAnnotationItem>>& in, SwDocShell &rDocShell, FilterFunctor& rFilter)
344
0
            : m_aSidebarItems(in)
345
0
            , m_rDocShell(rDocShell)
346
0
            , m_rFilter(rFilter)
347
0
        {
348
0
            FillVector();
349
0
            StartListening(m_rDocShell);
350
0
        }
351
        void FillVector()
352
0
        {
353
0
            EndListeningToAllFields();
354
0
            m_aFormatFields.clear();
355
0
            m_aFormatFields.reserve(m_aSidebarItems.size());
356
0
            for (auto const& p : m_aSidebarItems)
357
0
            {
358
0
                const SwFormatField& rField = p->GetFormatField();
359
0
                if (!m_rFilter(&rField))
360
0
                    continue;
361
0
                StartListening(const_cast<SwFormatField&>(rField));
362
0
                m_aFormatFields.push_back(&rField);
363
0
            }
364
0
        }
365
        void EndListeningToAllFields()
366
0
        {
367
0
            for (auto const& pField : m_aFormatFields)
368
0
            {
369
0
                EndListening(const_cast<SwFormatField&>(*pField));
370
0
            }
371
0
        }
372
        virtual ~FieldDocWatchingStack() override
373
0
        {
374
0
            EndListeningToAllFields();
375
0
            EndListening(m_rDocShell);
376
0
        }
377
        const SwFormatField* pop()
378
0
        {
379
0
            if (m_aFormatFields.empty())
380
0
                return nullptr;
381
0
            const SwFormatField* p = m_aFormatFields.back();
382
0
            EndListening(const_cast<SwFormatField&>(*p));
383
0
            m_aFormatFields.pop_back();
384
0
            return p;
385
0
        }
386
    };
387
388
// a RAII object that sets Ignore redline flag, and restores previous redline flags in dtor
389
class CommentDeleteFlagsRestoreImpl : public SwPostItMgr::CommentDeleteFlagsRestore
390
{
391
public:
392
    CommentDeleteFlagsRestoreImpl(SwWrtShell* shell)
393
0
        : m_pWrtShell(shell)
394
0
        , m_eRestreFlags(m_pWrtShell->GetRedlineFlags())
395
0
    {
396
0
        m_pWrtShell->SetRedlineFlags(m_eRestreFlags | RedlineFlags::Ignore);
397
0
    }
398
0
    ~CommentDeleteFlagsRestoreImpl() { m_pWrtShell->SetRedlineFlags(m_eRestreFlags); }
399
400
private:
401
    SwWrtShell* m_pWrtShell;
402
    RedlineFlags m_eRestreFlags;
403
};
404
405
bool isOwnFileFormat(SfxMedium* pMedium)
406
0
{
407
    // Assume that unsaved documents are own format
408
0
    return !pMedium || !pMedium->GetFilter() || pMedium->GetFilter()->IsOwnFormat();
409
0
}
410
411
} // anonymous namespace
412
413
SwPostItMgr::SwPostItMgr(SwView* pView)
414
4.26k
    : mpView(pView)
415
4.26k
    , mpWrtShell(mpView->GetDocShell()->GetWrtShell())
416
4.26k
    , mpEditWin(&mpView->GetEditWin())
417
4.26k
    , mnEventId(nullptr)
418
4.26k
    , mbWaitingForCalcRects(false)
419
4.26k
    , mpActivePostIt(nullptr)
420
4.26k
    , mbLayout(false)
421
4.26k
    , mbLayoutHeight(0)
422
4.26k
    , mbLayouting(false)
423
4.26k
    , mbReadOnly(mpView->GetDocShell()->IsReadOnly())
424
4.26k
    , mbDeleteNote(true)
425
4.26k
{
426
4.26k
    if(!mpView->GetDrawView() )
427
0
        mpView->GetWrtShell().MakeDrawView();
428
429
    //make sure we get the colour yellow always, even if not the first one of comments or redlining
430
4.26k
    SwModule::get()->GetRedlineAuthor();
431
432
    // collect all PostIts and redline comments that exist after loading the document
433
    // don't check for existence for any of them, don't focus them
434
4.26k
    AddPostIts(false,false);
435
    /*  this code can be used once we want redline comments in the Sidebar
436
    AddRedlineComments(false,false);
437
    */
438
    // we want to receive stuff like SfxHintId::DocChanged
439
4.26k
    StartListening(*mpView->GetDocShell());
440
    // listen to stylesheet pool to update on stylesheet rename,
441
    // as EditTextObject references styles by name.
442
4.26k
    SfxStyleSheetBasePool* pStyleSheetPool = mpView->GetDocShell()->GetStyleSheetPool();
443
4.26k
    if (pStyleSheetPool)
444
4.26k
        StartListening(*static_cast<SwDocStyleSheetPool*>(pStyleSheetPool)->GetEEStyleSheetPool());
445
4.26k
    if (!mvPostItFields.empty())
446
3
    {
447
3
        mbWaitingForCalcRects = true;
448
3
        mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
449
3
    }
450
4.26k
}
451
452
SwPostItMgr::~SwPostItMgr()
453
4.26k
{
454
4.26k
    if ( mnEventId )
455
3
        Application::RemoveUserEvent( mnEventId );
456
    // forget about all our Sidebar windows
457
4.26k
    RemoveSidebarWin();
458
4.26k
    EndListeningAll();
459
460
4.26k
    mPages.clear();
461
4.26k
}
462
463
bool SwPostItMgr::CheckForRemovedPostIts()
464
0
{
465
0
    IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
466
0
    bool bRemoved = false;
467
0
    auto it = mvPostItFields.begin();
468
0
    while(it != mvPostItFields.end())
469
0
    {
470
0
        if (!(*it)->UseElement(*mpWrtShell->GetLayout(), rIDRA))
471
0
        {
472
0
            EndListening(const_cast<SfxBroadcaster&>(*(*it)->GetBroadcaster()));
473
474
0
            if((*it)->mpPostIt && (*it)->mpPostIt->GetPostItField())
475
0
                lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, (*it)->mpPostIt->GetPostItField()->GetPostItId());
476
477
0
            std::unique_ptr<SwAnnotationItem> p = std::move(*it);
478
0
            it = mvPostItFields.erase(it);
479
0
            if (GetActiveSidebarWin() == p->mpPostIt)
480
0
                SetActiveSidebarWin(nullptr);
481
0
            p->mpPostIt.disposeAndClear();
482
483
0
            if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
484
0
            {
485
0
                const SwPostItField* pPostItField = static_cast<const SwPostItField*>(p->GetFormatField().GetField());
486
0
                lcl_CommentNotification(mpView, CommentNotificationType::Remove, nullptr, pPostItField->GetPostItId());
487
0
            }
488
489
0
            bRemoved = true;
490
0
        }
491
0
        else
492
0
            ++it;
493
0
    }
494
495
0
    if ( !bRemoved )
496
0
        return false;
497
498
    // make sure that no deleted items remain in page lists
499
    // todo: only remove deleted ones?!
500
0
    if ( mvPostItFields.empty() )
501
0
    {
502
0
        PreparePageContainer();
503
0
        PrepareView();
504
0
    }
505
0
    else
506
0
    {
507
        // if postits are there make sure that page lists are not empty
508
        // otherwise sudden paints can cause pain (in BorderOverPageBorder)
509
0
        CalcRects();
510
0
    }
511
512
0
    return true;
513
0
}
514
515
SwAnnotationItem* SwPostItMgr::InsertItem(SfxBroadcaster* pItem, bool bCheckExistence, bool bFocus)
516
3
{
517
3
    if (bCheckExistence)
518
0
    {
519
0
        for (auto const& postItField : mvPostItFields)
520
0
        {
521
0
            if ( postItField->GetBroadcaster() == pItem )
522
0
                return nullptr;
523
0
        }
524
0
    }
525
3
    mbLayout = bFocus;
526
527
3
    SwAnnotationItem* pAnnotationItem = nullptr;
528
3
    if (auto pSwFormatField = dynamic_cast< SwFormatField *>( pItem ))
529
3
    {
530
3
        IsPostitField isPostitField;
531
3
        if (!isPostitField(pSwFormatField))
532
0
            return nullptr;
533
3
        mvPostItFields.push_back(std::make_unique<SwAnnotationItem>(*pSwFormatField, bFocus));
534
3
        pAnnotationItem = mvPostItFields.back().get();
535
3
    }
536
3
    assert(dynamic_cast< const SwFormatField *>( pItem ) && "Mgr::InsertItem: seems like new stuff was added");
537
3
    StartListening(*pItem);
538
3
    return pAnnotationItem;
539
3
}
540
541
sw::annotation::SwAnnotationWin* SwPostItMgr::GetRemovedAnnotationWin( const SfxBroadcaster* pBroadcast )
542
0
{
543
0
    auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
544
0
        [&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; });
545
0
    if (i != mvPostItFields.end())
546
0
    {
547
0
        return (*i)->mpPostIt;
548
0
    }
549
0
    return nullptr;
550
0
}
551
552
void SwPostItMgr::RemoveItem( SfxBroadcaster* pBroadcast )
553
0
{
554
0
    EndListening(*pBroadcast);
555
0
    auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
556
0
        [&pBroadcast](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->GetBroadcaster() == pBroadcast; });
557
0
    if (i != mvPostItFields.end())
558
0
    {
559
#if ENABLE_YRS
560
        // note: (*i)->mpPostIt may be null here, if it's in hidden text - see testMissingDefaultLineColor
561
        mpView->GetDocShell()->GetDoc()->getIDocumentState().YrsRemoveComment(
562
            (*i)->GetAnchorPosition());
563
#endif
564
0
        std::unique_ptr<SwAnnotationItem> p = std::move(*i);
565
        // tdf#120487 remove from list before dispose, so comment window
566
        // won't be recreated due to the entry still in the list if focus
567
        // transferring from the pPostIt triggers relayout of postits
568
        // tdf#133348 remove from list before calling SetActiveSidebarWin
569
        // so GetNextPostIt won't deal with mvPostItFields containing empty unique_ptr
570
0
        mvPostItFields.erase(i);
571
0
        if (GetActiveSidebarWin() == p->mpPostIt)
572
0
            SetActiveSidebarWin(nullptr);
573
0
        p->mpPostIt.disposeAndClear();
574
0
    }
575
0
    mbLayout = true;
576
0
    PrepareView();
577
0
}
578
579
void SwPostItMgr::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
580
123k
{
581
123k
    if (rHint.GetId() == SfxHintId::ThisIsAnSfxEventHint)
582
21.3k
    {
583
21.3k
        const SfxEventHint& rSfxEventHint = static_cast<const SfxEventHint&>(rHint);
584
21.3k
        if (rSfxEventHint.GetEventId() == SfxEventHintId::SwEventLayoutFinished)
585
0
        {
586
0
            if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
587
0
            {
588
0
                mbWaitingForCalcRects = true;
589
0
                mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
590
0
            }
591
0
        }
592
21.3k
    }
593
101k
    else if ( rHint.GetId() == SfxHintId::SwFormatField )
594
0
    {
595
0
        const SwFormatFieldHint * pFormatHint = static_cast<const SwFormatFieldHint*>(&rHint);
596
0
        SwFormatField* pField = const_cast <SwFormatField*>( pFormatHint->GetField() );
597
0
        switch ( pFormatHint->Which() )
598
0
        {
599
0
            case SwFormatFieldHintWhich::INSERTED :
600
0
            {
601
0
                if (!pField)
602
0
                {
603
0
                    AddPostIts();
604
0
                    break;
605
0
                }
606
                // get field to be inserted from hint
607
0
                if ( pField->IsFieldInDoc() )
608
0
                {
609
0
                    bool bEmpty = !HasNotes();
610
0
                    SwAnnotationItem* pItem = InsertItem( pField, true, false );
611
612
0
                    if (bEmpty && !mvPostItFields.empty())
613
0
                        PrepareView(true);
614
615
                    // True until the layout of this post it finishes
616
0
                    if (pItem)
617
0
                        pItem->mbPendingLayout = true;
618
0
                }
619
0
                else
620
0
                {
621
0
                    OSL_FAIL("Inserted field not in document!" );
622
0
                }
623
0
                break;
624
0
            }
625
0
            case SwFormatFieldHintWhich::REMOVED:
626
0
            case SwFormatFieldHintWhich::REDLINED_DELETION:
627
0
            {
628
0
                if (mbDeleteNote)
629
0
                {
630
0
                    if (!pField)
631
0
                    {
632
0
                        const bool bWasRemoved = CheckForRemovedPostIts();
633
                        // tdf#143643 ensure relayout on undo of insert comment
634
0
                        if (bWasRemoved)
635
0
                            mbLayout = true;
636
0
                        break;
637
0
                    }
638
0
                    this->Broadcast(rHint);
639
0
                    RemoveItem(pField);
640
641
                    // If LOK has disabled tiled annotations, emit annotation callbacks
642
0
                    if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
643
0
                    {
644
0
                        SwPostItField* pPostItField = static_cast<SwPostItField*>(pField->GetField());
645
0
                        auto type = pFormatHint->Which() == SwFormatFieldHintWhich::REMOVED ? CommentNotificationType::Remove: CommentNotificationType::RedlinedDeletion;
646
0
                        lcl_CommentNotification(mpView, type, nullptr, pPostItField->GetPostItId());
647
0
                    }
648
0
                }
649
0
                break;
650
0
            }
651
0
            case SwFormatFieldHintWhich::FOCUS:
652
0
            {
653
0
                if (pFormatHint->GetView()== mpView)
654
0
                    Focus(rBC);
655
0
                break;
656
0
            }
657
0
            case SwFormatFieldHintWhich::CHANGED:
658
0
            case SwFormatFieldHintWhich::RESOLVED:
659
0
            {
660
0
                SwFormatField* pFormatField = dynamic_cast<SwFormatField*>(&rBC);
661
0
                for (auto const& postItField : mvPostItFields)
662
0
                {
663
0
                    if ( pFormatField == postItField->GetBroadcaster() )
664
0
                    {
665
0
                        if (postItField->mpPostIt)
666
0
                        {
667
0
                            postItField->mpPostIt->SetPostItText();
668
0
                            mbLayout = true;
669
0
                            this->Forward(rBC, rHint);
670
0
                        }
671
672
                        // If LOK has disabled tiled annotations, emit annotation callbacks
673
0
                        if (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations())
674
0
                        {
675
0
                            if(SwFormatFieldHintWhich::CHANGED == pFormatHint->Which())
676
0
                                lcl_CommentNotification(mpView, CommentNotificationType::Modify, postItField.get(), 0);
677
0
                            else
678
0
                                lcl_CommentNotification(mpView, CommentNotificationType::Resolve, postItField.get(), 0);
679
0
                        }
680
0
                        break;
681
0
                    }
682
0
                }
683
0
                break;
684
0
            }
685
0
        }
686
0
    }
687
101k
    else if ( rHint.GetId() == SfxHintId::StyleSheetModifiedExtended )
688
0
    {
689
0
        const SfxStyleSheetModifiedHint * pStyleHint = static_cast<const SfxStyleSheetModifiedHint*>(&rHint);
690
0
        for (const auto& postItField : mvPostItFields)
691
0
        {
692
0
            auto pField = static_cast<SwPostItField*>(postItField->GetFormatField().GetField());
693
0
            pField->ChangeStyleSheetName(pStyleHint->GetOldName(), pStyleHint->GetStyleSheet());
694
0
        }
695
0
    }
696
101k
    else
697
101k
    {
698
101k
        SfxHintId nId = rHint.GetId();
699
101k
        switch ( nId )
700
101k
        {
701
0
            case SfxHintId::ModeChanged:
702
0
            {
703
0
                if ( mbReadOnly != mpView->GetDocShell()->IsReadOnly() )
704
0
                {
705
0
                    mbReadOnly = !mbReadOnly;
706
0
                    SetReadOnlyState();
707
0
                    mbLayout = true;
708
0
                }
709
0
                break;
710
0
            }
711
93.2k
            case SfxHintId::DocChanged:
712
93.2k
            {
713
93.2k
                if ( mpView->GetDocShell() == &rBC )
714
93.2k
                {
715
93.2k
                    if ( !mbWaitingForCalcRects && !mvPostItFields.empty())
716
0
                    {
717
0
                        mbWaitingForCalcRects = true;
718
0
                        mnEventId = Application::PostUserEvent( LINK( this, SwPostItMgr, CalcHdl) );
719
0
                    }
720
93.2k
                }
721
93.2k
                break;
722
0
            }
723
0
            case SfxHintId::LanguageChanged:
724
0
            {
725
0
                SetSpellChecking();
726
0
                break;
727
0
            }
728
0
            case SfxHintId::SwSplitNodeOperation:
729
0
            {
730
                // if we are in a SplitNode/Cut operation, do not delete note and then add again, as this will flicker
731
0
                mbDeleteNote = !mbDeleteNote;
732
0
                break;
733
0
            }
734
0
            case SfxHintId::Dying:
735
0
            {
736
0
                if ( mpView->GetDocShell() != &rBC )
737
0
                {
738
                    // field to be removed is the broadcaster
739
0
                    OSL_FAIL("Notification for removed SwFormatField was not sent!");
740
0
                    RemoveItem(&rBC);
741
0
                }
742
0
                break;
743
0
            }
744
8.52k
            default: break;
745
101k
        }
746
101k
    }
747
123k
}
748
749
void SwPostItMgr::Focus(const SfxBroadcaster& rBC)
750
0
{
751
0
    if (!mpWrtShell->GetViewOptions()->IsPostIts())
752
0
    {
753
0
        SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_NOTES);
754
0
        mpView->ExecViewOptions(aRequest);
755
0
    }
756
757
0
    for (auto const& postItField : mvPostItFields)
758
0
    {
759
        // field to get the focus is the broadcaster
760
0
        if ( &rBC == postItField->GetBroadcaster() )
761
0
        {
762
0
            if (postItField->mpPostIt)
763
0
            {
764
0
                if (postItField->mpPostIt->IsResolved() &&
765
0
                        !mpWrtShell->GetViewOptions()->IsResolvedPostIts())
766
0
                {
767
0
                    SfxRequest aRequest(mpView->GetViewFrame(), SID_TOGGLE_RESOLVED_NOTES);
768
0
                    mpView->ExecViewOptions(aRequest);
769
0
                }
770
0
                postItField->mpPostIt->GrabFocus();
771
0
                MakeVisible(postItField->mpPostIt);
772
0
            }
773
0
            else
774
0
            {
775
                // when the layout algorithm starts, this postit is created and receives focus
776
0
                postItField->mbFocus = true;
777
0
            }
778
0
        }
779
0
    }
780
0
}
781
782
bool SwPostItMgr::CalcRects()
783
154
{
784
154
    if ( mnEventId )
785
0
    {
786
        // if CalcRects() was forced and an event is still pending: remove it
787
        // it is superfluous and also may cause reentrance problems if triggered while layouting
788
0
        Application::RemoveUserEvent( mnEventId );
789
0
        mnEventId = nullptr;
790
0
    }
791
792
154
    bool bChange = false;
793
154
    bool bRepair = false;
794
154
    PreparePageContainer();
795
154
    if ( !mvPostItFields.empty() )
796
0
    {
797
0
        IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
798
0
        for (auto const& pItem : mvPostItFields)
799
0
        {
800
0
            if (!pItem->UseElement(*mpWrtShell->GetLayout(), rIDRA))
801
0
            {
802
0
                OSL_FAIL("PostIt is not in doc or other wrong use");
803
0
                bRepair = true;
804
0
                continue;
805
0
            }
806
0
            const SwRect aOldAnchorRect( pItem->maLayoutInfo.mPosition );
807
0
            const SwPostItHelper::SwLayoutStatus eOldLayoutStatus = pItem->mLayoutStatus;
808
0
            const SwNodeOffset nOldStartNodeIdx( pItem->maLayoutInfo.mnStartNodeIdx );
809
0
            const sal_Int32 nOldStartContent( pItem->maLayoutInfo.mnStartContent );
810
0
            {
811
                // update layout information
812
0
                const SwTextAnnotationField* pTextAnnotationField =
813
0
                    dynamic_cast< const SwTextAnnotationField* >( pItem->GetFormatField().GetTextField() );
814
0
                const ::sw::mark::MarkBase* pAnnotationMark =
815
0
                    pTextAnnotationField != nullptr ? pTextAnnotationField->GetAnnotationMark() : nullptr;
816
0
                if ( pAnnotationMark != nullptr )
817
0
                {
818
0
                    pItem->mLayoutStatus =
819
0
                        SwPostItHelper::getLayoutInfos(
820
0
                            pItem->maLayoutInfo,
821
0
                            pItem->GetAnchorPosition(),
822
0
                            pAnnotationMark );
823
0
                }
824
0
                else
825
0
                {
826
0
                    pItem->mLayoutStatus =
827
0
                        SwPostItHelper::getLayoutInfos( pItem->maLayoutInfo, pItem->GetAnchorPosition() );
828
0
                }
829
0
            }
830
0
            bChange = bChange
831
0
                      || pItem->maLayoutInfo.mPosition != aOldAnchorRect
832
0
                      || pItem->mLayoutStatus != eOldLayoutStatus
833
0
                      || pItem->maLayoutInfo.mnStartNodeIdx != nOldStartNodeIdx
834
0
                      || pItem->maLayoutInfo.mnStartContent != nOldStartContent;
835
0
        }
836
837
        // show notes in right order in navigator
838
        //prevent Anchors during layout to overlap, e.g. when moving a frame
839
0
        if (mvPostItFields.size()>1 )
840
0
            std::stable_sort(mvPostItFields.begin(), mvPostItFields.end(), comp_pos);
841
842
        // sort the items into the right page vector, so layout can be done by page
843
0
        for (auto const& pItem : mvPostItFields)
844
0
        {
845
0
            if( SwPostItHelper::INVISIBLE == pItem->mLayoutStatus )
846
0
            {
847
0
                if (pItem->mpPostIt)
848
0
                    pItem->mpPostIt->HideNote();
849
0
                continue;
850
0
            }
851
852
0
            if( SwPostItHelper::HIDDEN == pItem->mLayoutStatus )
853
0
            {
854
0
                if (!mpWrtShell->GetViewOptions()->IsShowHiddenChar())
855
0
                {
856
0
                    if (pItem->mpPostIt)
857
0
                        pItem->mpPostIt->HideNote();
858
0
                    continue;
859
0
                }
860
0
            }
861
862
0
            const tools::ULong aPageNum = pItem->maLayoutInfo.mnPageNumber;
863
0
            if (aPageNum > mPages.size())
864
0
            {
865
0
                const tools::ULong nNumberOfPages = mPages.size();
866
0
                mPages.reserve(aPageNum);
867
0
                for (tools::ULong j=0; j<aPageNum - nNumberOfPages; ++j)
868
0
                    mPages.emplace_back( new SwPostItPageItem());
869
0
            }
870
0
            mPages[aPageNum-1]->mvSidebarItems.push_back(pItem.get());
871
0
            mPages[aPageNum-1]->mPageRect = pItem->maLayoutInfo.mPageFrame;
872
0
            mPages[aPageNum-1]->eSidebarPosition = pItem->maLayoutInfo.meSidebarPosition;
873
0
        }
874
875
0
        if (!bChange && mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE))
876
0
        {
877
0
            tools::Long nLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
878
0
            if( nLayoutHeight > mbLayoutHeight )
879
0
            {
880
0
                if (mPages[0]->bScrollbar || HasScrollbars())
881
0
                    bChange = true;
882
0
            }
883
0
            else if( nLayoutHeight < mbLayoutHeight )
884
0
            {
885
0
                if (mPages[0]->bScrollbar || !BorderOverPageBorder(1))
886
0
                    bChange = true;
887
0
            }
888
0
        }
889
0
    }
890
891
154
    if ( bRepair )
892
0
        CheckForRemovedPostIts();
893
894
154
    mbLayoutHeight = SwPostItHelper::getLayoutHeight( mpWrtShell->GetLayout() );
895
154
    mbWaitingForCalcRects = false;
896
154
    return bChange;
897
154
}
898
899
bool SwPostItMgr::HasScrollbars() const
900
0
{
901
0
    for (auto const& postItField : mvPostItFields)
902
0
    {
903
0
        if (postItField->mbShow && postItField->mpPostIt && postItField->mpPostIt->HasScrollbar())
904
0
            return true;
905
0
    }
906
0
    return false;
907
0
}
908
909
void SwPostItMgr::PreparePageContainer()
910
4.41k
{
911
    // we do not just delete the SwPostItPageItem, so offset/scrollbar is not lost
912
4.41k
    tools::Long lPageSize = mpWrtShell->GetNumPages();
913
4.41k
    tools::Long lContainerSize = mPages.size();
914
915
4.41k
    if (lContainerSize < lPageSize)
916
4.26k
    {
917
4.26k
        mPages.reserve(lPageSize);
918
104k
        for (tools::Long i=0; i<lPageSize - lContainerSize;i++)
919
99.9k
            mPages.emplace_back( new SwPostItPageItem());
920
4.26k
    }
921
154
    else if (lContainerSize > lPageSize)
922
0
    {
923
0
        for (int i=mPages.size()-1; i >= lPageSize;--i)
924
0
        {
925
0
            mPages.pop_back();
926
0
        }
927
0
    }
928
    // only clear the list, DO NOT delete the objects itself
929
4.41k
    for (auto const& page : mPages)
930
108k
    {
931
108k
        page->mvSidebarItems.clear();
932
108k
        if (mvPostItFields.empty())
933
108k
            page->bScrollbar = false;
934
108k
    }
935
4.41k
}
936
937
VclPtr<SwAnnotationWin> SwPostItMgr::GetOrCreateAnnotationWindow(SwAnnotationItem& rItem, bool& rCreated)
938
0
{
939
0
    VclPtr<SwAnnotationWin> pPostIt = rItem.mpPostIt;
940
0
    if (!pPostIt)
941
0
    {
942
0
        pPostIt = rItem.GetSidebarWindow( mpView->GetEditWin(),
943
0
                                          *this );
944
0
        pPostIt->InitControls();
945
0
        pPostIt->SetReadonly(mbReadOnly);
946
0
        rItem.mpPostIt = pPostIt;
947
#if ENABLE_YRS
948
        SAL_INFO("sw.yrs", "YRS GetOrCreateAnnotationWindow " << rItem.mpPostIt);
949
#endif
950
0
        if (mpAnswer)
951
0
        {
952
0
            if (pPostIt->GetPostItField()->GetParentPostItId() != 0) //do we really have another note in front of this one
953
0
            {
954
0
                pPostIt->InitAnswer(*mpAnswer);
955
0
            }
956
0
            mpAnswer.reset();
957
0
        }
958
959
0
        rCreated = true;
960
0
    }
961
0
    return rItem.mpPostIt;
962
0
}
963
964
void SwPostItMgr::LayoutPostIts()
965
154
{
966
154
    const bool bLoKitActive = comphelper::LibreOfficeKit::isActive();
967
154
    const bool bTiledAnnotations = comphelper::LibreOfficeKit::isTiledAnnotations();
968
154
    const bool bShowNotes = ShowNotes();
969
970
154
    const bool bEnableMapMode = bLoKitActive && !mpEditWin->IsMapModeEnabled();
971
154
    if (bEnableMapMode)
972
0
        mpEditWin->EnableMapMode();
973
974
154
    std::set<VclPtr<SwAnnotationWin>> aCreatedPostIts;
975
154
    if ( !mvPostItFields.empty() && !mbWaitingForCalcRects )
976
0
    {
977
0
        mbLayouting = true;
978
979
        //loop over all pages and do the layout
980
        // - create SwPostIt if necessary
981
        // - place SwPostIts on their initial position
982
        // - calculate necessary height for all PostIts together
983
0
        bool bUpdate = false;
984
0
        for (std::unique_ptr<SwPostItPageItem>& pPage : mPages)
985
0
        {
986
            // only layout if there are notes on this page
987
0
            if (!pPage->mvSidebarItems.empty())
988
0
            {
989
0
                std::vector<SwAnnotationWin*> aVisiblePostItList;
990
0
                tools::ULong                  lNeededHeight = 0;
991
992
0
                for (auto const& pItem : pPage->mvSidebarItems)
993
0
                {
994
0
                    if (pItem->mbShow)
995
0
                    {
996
0
                        bool bCreated = false;
997
0
                        VclPtr<SwAnnotationWin> pPostIt = GetOrCreateAnnotationWindow(*pItem, bCreated);
998
0
                        if (bCreated)
999
0
                        {
1000
                            // The annotation window was created for a previously existing, but not
1001
                            // laid out comment.
1002
0
                            aCreatedPostIts.insert(pPostIt);
1003
0
                        }
1004
1005
0
                        pPostIt->SetChangeTracking(
1006
0
                            pItem->mLayoutStatus,
1007
0
                            GetColorAnchor(pItem->maLayoutInfo.mRedlineAuthor));
1008
0
                        pPostIt->SetSidebarPosition(pPage->eSidebarPosition);
1009
1010
0
                        if (pPostIt->GetPostItField()->GetParentPostItId() != 0)
1011
0
                            pPostIt->SetFollow(true);
1012
1013
0
                        tools::Long aPostItHeight = 0;
1014
0
                        if (bShowNotes)
1015
0
                        {
1016
0
                            tools::Long mlPageBorder = 0;
1017
0
                            tools::Long mlPageEnd = 0;
1018
1019
0
                            if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
1020
0
                            {
1021
                                // x value for notes positioning
1022
0
                                mlPageBorder = mpEditWin->LogicToPixel( Point( pPage->mPageRect.Left(), 0)).X() - GetSidebarWidth(true);// - GetSidebarBorderWidth(true);
1023
                                //bending point
1024
0
                                mlPageEnd =
1025
0
                                    mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
1026
0
                                    ? pItem->maLayoutInfo.mPagePrtArea.Left()
1027
0
                                    : pPage->mPageRect.Left() + 350;
1028
0
                            }
1029
0
                            else if (pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
1030
0
                            {
1031
                                // x value for notes positioning
1032
0
                                mlPageBorder = mpEditWin->LogicToPixel( Point(pPage->mPageRect.Right(), 0)).X() + GetSidebarBorderWidth(true);
1033
                                //bending point
1034
0
                                mlPageEnd =
1035
0
                                    mpWrtShell->getIDocumentSettingAccess().get(DocumentSettingId::BROWSE_MODE)
1036
0
                                    ? pItem->maLayoutInfo.mPagePrtArea.Right() :
1037
0
                                    pPage->mPageRect.Right() - 350;
1038
0
                            }
1039
1040
0
                            tools::Long Y = mpEditWin->LogicToPixel( Point(0,pItem->maLayoutInfo.mPosition.Bottom())).Y();
1041
1042
0
                            tools::Long postItPixelTextHeight
1043
0
                                = (comphelper::LibreOfficeKit::isActive()
1044
0
                                       ? mpEditWin
1045
0
                                             ->LogicToPixel(
1046
0
                                                 Point(0, pPostIt->GetPostItTextHeight()))
1047
0
                                             .Y()
1048
0
                                       : pPostIt->GetPostItTextHeight());
1049
0
                            aPostItHeight
1050
0
                                = (postItPixelTextHeight < pPostIt->GetMinimumSizeWithoutMeta()
1051
0
                                       ? pPostIt->GetMinimumSizeWithoutMeta()
1052
0
                                       : postItPixelTextHeight)
1053
0
                                  + pPostIt->GetMetaHeight();
1054
0
                            pPostIt->SetPosSizePixelRect( mlPageBorder ,
1055
0
                                                          Y - GetInitialAnchorDistance(),
1056
0
                                                          GetSidebarWidth(true),
1057
0
                                                          aPostItHeight,
1058
0
                                                          mlPageEnd );
1059
0
                        }
1060
1061
0
                        pPostIt->SetAnchorRect(pItem->maLayoutInfo.mPosition);
1062
1063
0
                        pPostIt->ChangeSidebarItem( *pItem );
1064
1065
0
                        if (pItem->mbFocus)
1066
0
                        {
1067
0
                            mbLayout = true;
1068
0
                            pPostIt->GrabFocus();
1069
0
                            pItem->mbFocus = false;
1070
0
                        }
1071
                        // only the visible postits are used for the final layout
1072
0
                        aVisiblePostItList.push_back(pPostIt);
1073
0
                        if (bShowNotes)
1074
0
                            lNeededHeight += pPostIt->IsFollow() ? aPostItHeight : aPostItHeight+GetSpaceBetween();
1075
0
                    }
1076
0
                    else // we don't want to see it
1077
0
                    {
1078
0
                        VclPtr<SwAnnotationWin> pPostIt = pItem->mpPostIt;
1079
0
                        if (pPostIt)
1080
0
                            pPostIt->HideNote();
1081
0
                    }
1082
0
                    SwFormatField* pFormatField = &(pItem->GetFormatField());
1083
0
                    SwFormatFieldHintWhich nWhich = SwFormatFieldHintWhich::INSERTED;
1084
0
                    this->Broadcast(SwFormatFieldHint(pFormatField, nWhich, mpView));
1085
0
                }
1086
1087
0
                if (!aVisiblePostItList.empty() && bShowNotes)
1088
0
                {
1089
0
                    bool bOldScrollbar = pPage->bScrollbar;
1090
0
                    pPage->bScrollbar = LayoutByPage(aVisiblePostItList, pPage->mPageRect.SVRect(), lNeededHeight);
1091
0
                    if (!pPage->bScrollbar)
1092
0
                    {
1093
0
                        pPage->lOffset = 0;
1094
0
                    }
1095
0
                    else if (sal_Int32 nScrollSize = GetScrollSize())
1096
0
                    {
1097
                        //when we changed our zoom level, the offset value can be too big, so let's check for the largest possible zoom value
1098
0
                        tools::Long aAvailableHeight = mpEditWin->LogicToPixel(Size(0,pPage->mPageRect.Height())).Height() - 2 * GetSidebarScrollerHeight();
1099
0
                        tools::Long lOffset = -1 * nScrollSize * (aVisiblePostItList.size() - aAvailableHeight / nScrollSize);
1100
0
                        if (pPage->lOffset < lOffset)
1101
0
                            pPage->lOffset = lOffset;
1102
0
                    }
1103
0
                    bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate;
1104
0
                    const tools::Long aSidebarheight = pPage->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
1105
                    /*
1106
                                       TODO
1107
                                       - enlarge all notes till GetNextBorder(), as we resized to average value before
1108
                                       */
1109
                    //let's hide the ones which overlap the page
1110
0
                    for (auto const& visiblePostIt : aVisiblePostItList)
1111
0
                    {
1112
0
                        if (pPage->lOffset != 0)
1113
0
                            visiblePostIt->TranslateTopPosition(pPage->lOffset);
1114
1115
0
                        bool bBottom  = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y()+visiblePostIt->VirtualSize().Height())).Y() <= (pPage->mPageRect.Bottom()-aSidebarheight);
1116
0
                        bool bTop = mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() >= (pPage->mPageRect.Top()+aSidebarheight);
1117
0
                        if ( bBottom && bTop )
1118
0
                        {
1119
                            // When tiled rendering, make sure that only the
1120
                            // view that has the comment focus emits callbacks,
1121
                            // so the editing view jumps to the comment, but
1122
                            // not the others.
1123
0
                            bool bTiledPainting = comphelper::LibreOfficeKit::isTiledPainting();
1124
0
                            if (!bTiledPainting)
1125
                                // No focus -> disable callbacks.
1126
0
                                comphelper::LibreOfficeKit::setTiledPainting(!visiblePostIt->HasChildPathFocus());
1127
0
                            visiblePostIt->ShowNote();
1128
0
                            if (!bTiledPainting)
1129
0
                                comphelper::LibreOfficeKit::setTiledPainting(bTiledPainting);
1130
0
                        }
1131
0
                        else
1132
0
                        {
1133
0
                            if (mpEditWin->PixelToLogic(Point(0,visiblePostIt->VirtualPos().Y())).Y() < (pPage->mPageRect.Top()+aSidebarheight))
1134
0
                            {
1135
0
                                if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
1136
0
                                    visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Left(),
1137
0
                                                                pPage->mPageRect.Top()));
1138
0
                                else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
1139
0
                                    visiblePostIt->ShowAnchorOnly(Point( pPage->mPageRect.Right(),
1140
0
                                                                pPage->mPageRect.Top()));
1141
0
                            }
1142
0
                            else
1143
0
                            {
1144
0
                                if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT )
1145
0
                                    visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Left(),
1146
0
                                                               pPage->mPageRect.Bottom()));
1147
0
                                else if ( pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT )
1148
0
                                    visiblePostIt->ShowAnchorOnly(Point(pPage->mPageRect.Right(),
1149
0
                                                               pPage->mPageRect.Bottom()));
1150
0
                            }
1151
0
                            OSL_ENSURE(pPage->bScrollbar,"SwPostItMgr::LayoutByPage(): note overlaps, but bScrollbar is not true");
1152
0
                        }
1153
0
                    }
1154
0
                }
1155
0
                else
1156
0
                {
1157
0
                    for (auto const& visiblePostIt : aVisiblePostItList)
1158
0
                    {
1159
0
                        visiblePostIt->SetPosAndSize();
1160
0
                    }
1161
1162
0
                    bool bOldScrollbar = pPage->bScrollbar;
1163
0
                    pPage->bScrollbar = false;
1164
0
                    bUpdate = (bOldScrollbar != pPage->bScrollbar) || bUpdate;
1165
0
                }
1166
1167
0
                for (auto const& visiblePostIt : aVisiblePostItList)
1168
0
                {
1169
0
                    if (bLoKitActive && !bTiledAnnotations)
1170
0
                    {
1171
0
                        if (visiblePostIt->GetSidebarItem().mbPendingLayout && visiblePostIt->GetSidebarItem().mLayoutStatus != SwPostItHelper::DELETED)
1172
0
                        {
1173
                            // Notify about a just inserted comment.
1174
0
                            aCreatedPostIts.insert(visiblePostIt);
1175
0
                        }
1176
0
                        else if (visiblePostIt->IsAnchorRectChanged())
1177
0
                        {
1178
0
                            lcl_CommentNotification(mpView, CommentNotificationType::Modify, &visiblePostIt->GetSidebarItem(), 0);
1179
0
                            visiblePostIt->ResetAnchorRectChanged();
1180
0
                        }
1181
0
                    }
1182
1183
                    // Layout for this post it finished now
1184
0
                    visiblePostIt->GetSidebarItem().mbPendingLayout = false;
1185
0
                }
1186
0
            }
1187
0
            else
1188
0
            {
1189
0
                if (pPage->bScrollbar)
1190
0
                    bUpdate = true;
1191
0
                pPage->bScrollbar = false;
1192
0
            }
1193
0
        }
1194
1195
0
        if (!bShowNotes)
1196
0
        {       // we do not want to see the notes anymore -> Options-Writer-View-Notes
1197
0
            IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1198
0
            bool bRepair = false;
1199
0
            for (auto const& postItField : mvPostItFields)
1200
0
            {
1201
0
                if (!postItField->UseElement(*mpWrtShell->GetLayout(), rIDRA))
1202
0
                {
1203
0
                    OSL_FAIL("PostIt is not in doc!");
1204
0
                    bRepair = true;
1205
0
                    continue;
1206
0
                }
1207
1208
0
                if (postItField->mpPostIt)
1209
0
                {
1210
0
                    postItField->mpPostIt->HideNote();
1211
0
                    if (postItField->mpPostIt->HasChildPathFocus())
1212
0
                    {
1213
0
                        SetActiveSidebarWin(nullptr);
1214
0
                        postItField->mpPostIt->GrabFocusToDocument();
1215
0
                    }
1216
0
                }
1217
0
            }
1218
1219
0
            if ( bRepair )
1220
0
                CheckForRemovedPostIts();
1221
0
        }
1222
1223
        // notes scrollbar is otherwise not drawn correctly for some cases
1224
        // scrollbar area is enough
1225
0
        if (bUpdate)
1226
0
            mpEditWin->Invalidate(); /*This is a super expensive relayout and render of the entire page*/
1227
1228
0
        mbLayouting = false;
1229
0
    }
1230
1231
    // Now that comments are laid out, notify about freshly laid out or just inserted comments.
1232
154
    for (const auto& pPostIt : aCreatedPostIts)
1233
0
    {
1234
0
        lcl_CommentNotification(mpView, CommentNotificationType::Add, &pPostIt->GetSidebarItem(), 0);
1235
0
    }
1236
1237
154
    if (bEnableMapMode)
1238
0
        mpEditWin->EnableMapMode(false);
1239
154
}
1240
1241
bool SwPostItMgr::BorderOverPageBorder(tools::ULong aPage) const
1242
0
{
1243
0
    if ( mPages[aPage-1]->mvSidebarItems.empty() )
1244
0
    {
1245
0
        OSL_FAIL("Notes SidePane painted but no rects and page lists calculated!");
1246
0
        return false;
1247
0
    }
1248
1249
0
    auto aItem = mPages[aPage-1]->mvSidebarItems.end();
1250
0
    --aItem;
1251
0
    OSL_ENSURE ((*aItem)->mpPostIt,"BorderOverPageBorder: NULL postIt, should never happen");
1252
0
    if ((*aItem)->mpPostIt)
1253
0
    {
1254
0
        const tools::Long aSidebarheight = mPages[aPage-1]->bScrollbar ? mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height() : 0;
1255
0
        const tools::Long aEndValue = mpEditWin->PixelToLogic(Point(0,(*aItem)->mpPostIt->GetPosPixel().Y()+(*aItem)->mpPostIt->GetSizePixel().Height())).Y();
1256
0
        return aEndValue <= mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight;
1257
0
    }
1258
0
    else
1259
0
        return false;
1260
0
}
1261
1262
void SwPostItMgr::DrawNotesForPage(OutputDevice *pOutDev, sal_uInt32 nPage)
1263
0
{
1264
0
    assert(nPage < mPages.size());
1265
0
    if (nPage >= mPages.size())
1266
0
        return;
1267
0
    const bool bEnableMapMode
1268
0
        = comphelper::LibreOfficeKit::isActive() && !mpEditWin->IsMapModeEnabled();
1269
0
    if (bEnableMapMode)
1270
0
        mpEditWin->EnableMapMode();
1271
0
    for (auto const& pItem : mPages[nPage]->mvSidebarItems)
1272
0
    {
1273
0
        SwAnnotationWin* pPostIt = pItem->mpPostIt;
1274
0
        if (!pPostIt)
1275
0
            continue;
1276
0
        Point aPoint(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
1277
0
        pPostIt->DrawForPage(pOutDev, aPoint);
1278
0
    }
1279
0
    if (bEnableMapMode)
1280
0
        mpEditWin->EnableMapMode(false);
1281
0
}
1282
1283
void SwPostItMgr::PaintTile(OutputDevice& rRenderContext)
1284
0
{
1285
0
    for (const std::unique_ptr<SwAnnotationItem>& pItem : mvPostItFields)
1286
0
    {
1287
0
        SwAnnotationWin* pPostIt = pItem->mpPostIt;
1288
0
        if (!pPostIt)
1289
0
            continue;
1290
1291
0
        bool bEnableMapMode = !mpEditWin->IsMapModeEnabled();
1292
0
        mpEditWin->EnableMapMode();
1293
0
        rRenderContext.Push(vcl::PushFlags::MAPMODE);
1294
0
        Point aOffset(mpEditWin->PixelToLogic(pPostIt->GetPosPixel()));
1295
0
        MapMode aMapMode(rRenderContext.GetMapMode());
1296
0
        aMapMode.SetOrigin(aMapMode.GetOrigin() + aOffset);
1297
0
        rRenderContext.SetMapMode(aMapMode);
1298
0
        Size aSize(rRenderContext.PixelToLogic(pPostIt->GetSizePixel()));
1299
0
        tools::Rectangle aRectangle(Point(0, 0), aSize);
1300
1301
0
        pPostIt->PaintTile(rRenderContext, aRectangle);
1302
1303
0
        rRenderContext.Pop();
1304
0
        if (bEnableMapMode)
1305
0
            mpEditWin->EnableMapMode(false);
1306
0
    }
1307
0
}
1308
1309
void SwPostItMgr::Scroll(const tools::Long lScroll,const tools::ULong aPage)
1310
0
{
1311
0
    OSL_ENSURE((lScroll % GetScrollSize() )==0,"SwPostItMgr::Scroll: scrolling by wrong value");
1312
    // do not scroll more than necessary up or down
1313
0
    if ( ((mPages[aPage-1]->lOffset == 0) && (lScroll>0)) || ( BorderOverPageBorder(aPage) && (lScroll<0)) )
1314
0
        return;
1315
1316
0
    const bool bOldUp = ArrowEnabled(KEY_PAGEUP,aPage);
1317
0
    const bool bOldDown = ArrowEnabled(KEY_PAGEDOWN,aPage);
1318
0
    const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
1319
0
    for (auto const& item : mPages[aPage-1]->mvSidebarItems)
1320
0
    {
1321
0
        SwAnnotationWin* pPostIt = item->mpPostIt;
1322
        // if this is an answer, we should take the normal position and not the real, slightly moved position
1323
0
        pPostIt->SetVirtualPosSize(pPostIt->GetPosPixel(),pPostIt->GetSizePixel());
1324
0
        pPostIt->TranslateTopPosition(lScroll);
1325
1326
0
        if (item->mbShow)
1327
0
        {
1328
0
            bool bBottom  = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y()+pPostIt->VirtualSize().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
1329
0
            bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() >=   (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
1330
0
            if ( bBottom && bTop)
1331
0
            {
1332
0
                    pPostIt->ShowNote();
1333
0
            }
1334
0
            else
1335
0
            {
1336
0
                if ( mpEditWin->PixelToLogic(Point(0,pPostIt->VirtualPos().Y())).Y() < (mPages[aPage-1]->mPageRect.Top()+aSidebarheight))
1337
0
                {
1338
0
                    if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
1339
0
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Top()));
1340
0
                    else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
1341
0
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Top()));
1342
0
                }
1343
0
                else
1344
0
                {
1345
0
                    if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT)
1346
0
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Left(),mPages[aPage-1]->mPageRect.Bottom()));
1347
0
                    else if (mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
1348
0
                        pPostIt->ShowAnchorOnly(Point(mPages[aPage-1]->mPageRect.Right(),mPages[aPage-1]->mPageRect.Bottom()));
1349
0
                }
1350
0
            }
1351
0
        }
1352
0
    }
1353
0
    mPages[aPage-1]->lOffset += lScroll;
1354
0
    if ( (bOldUp != ArrowEnabled(KEY_PAGEUP,aPage)) ||(bOldDown != ArrowEnabled(KEY_PAGEDOWN,aPage)) )
1355
0
    {
1356
0
        mpEditWin->Invalidate(GetBottomScrollRect(aPage));
1357
0
        mpEditWin->Invalidate(GetTopScrollRect(aPage));
1358
0
    }
1359
0
}
1360
1361
void SwPostItMgr::AutoScroll(const SwAnnotationWin* pPostIt,const tools::ULong aPage )
1362
0
{
1363
    // otherwise all notes are visible
1364
0
    if (!mPages[aPage-1]->bScrollbar)
1365
0
        return;
1366
1367
0
    const tools::Long aSidebarheight = mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height();
1368
0
    const bool bBottom  = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height())).Y() <= (mPages[aPage-1]->mPageRect.Bottom()-aSidebarheight);
1369
0
    const bool bTop = mpEditWin->PixelToLogic(Point(0,pPostIt->GetPosPixel().Y())).Y() >= (mPages[aPage-1]->mPageRect.Top()+aSidebarheight);
1370
0
    if ( !(bBottom && bTop))
1371
0
    {
1372
0
        const tools::Long aDiff = bBottom ? mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Top() + aSidebarheight)).Y() - pPostIt->GetPosPixel().Y() :
1373
0
                                        mpEditWin->LogicToPixel(Point(0,mPages[aPage-1]->mPageRect.Bottom() - aSidebarheight)).Y() - (pPostIt->GetPosPixel().Y()+pPostIt->GetSizePixel().Height());
1374
        // this just adds the missing value to get the next a* GetScrollSize() after aDiff
1375
        // e.g aDiff= 61 POSTIT_SCROLL=50 --> lScroll = 100
1376
0
        const auto nScrollSize = GetScrollSize();
1377
0
        assert(nScrollSize);
1378
0
        const tools::Long lScroll = bBottom ? (aDiff + ( nScrollSize - (aDiff % nScrollSize))) : (aDiff - (nScrollSize + (aDiff % nScrollSize)));
1379
0
        Scroll(lScroll, aPage);
1380
0
    }
1381
0
}
1382
1383
void SwPostItMgr::MakeVisible(const SwAnnotationWin* pPostIt )
1384
0
{
1385
0
    tools::Long aPage = -1;
1386
    // we don't know the page yet, let's find it ourselves
1387
0
    std::vector<SwPostItPageItem*>::size_type n=0;
1388
0
    for (auto const& page : mPages)
1389
0
    {
1390
0
        for (auto const& item : page->mvSidebarItems)
1391
0
        {
1392
0
            if (item->mpPostIt==pPostIt)
1393
0
            {
1394
0
                aPage = n+1;
1395
0
                break;
1396
0
            }
1397
0
        }
1398
0
        ++n;
1399
0
    }
1400
0
    if (aPage!=-1)
1401
0
        AutoScroll(pPostIt,aPage);
1402
0
    tools::Rectangle aNoteRect (Point(pPostIt->GetPosPixel().X(),pPostIt->GetPosPixel().Y()-5),pPostIt->GetSizePixel());
1403
0
    if (!aNoteRect.IsEmpty())
1404
0
        mpWrtShell->MakeVisible(SwRect(mpEditWin->PixelToLogic(aNoteRect)));
1405
0
}
1406
1407
bool SwPostItMgr::ArrowEnabled(sal_uInt16 aDirection,tools::ULong aPage) const
1408
0
{
1409
0
    switch (aDirection)
1410
0
    {
1411
0
        case KEY_PAGEUP:
1412
0
            {
1413
0
                return (mPages[aPage-1]->lOffset != 0);
1414
0
            }
1415
0
        case KEY_PAGEDOWN:
1416
0
            {
1417
0
                return (!BorderOverPageBorder(aPage));
1418
0
            }
1419
0
        default: return false;
1420
0
    }
1421
0
}
1422
1423
Color SwPostItMgr::GetArrowColor(sal_uInt16 aDirection,tools::ULong aPage) const
1424
0
{
1425
0
    if (ArrowEnabled(aDirection,aPage))
1426
0
    {
1427
0
        if (Application::GetSettings().GetStyleSettings().GetHighContrastMode())
1428
0
            return COL_WHITE;
1429
0
        else
1430
0
            return COL_NOTES_SIDEPANE_ARROW_ENABLED;
1431
0
    }
1432
0
    else
1433
0
    {
1434
0
        return COL_NOTES_SIDEPANE_ARROW_DISABLED;
1435
0
    }
1436
0
}
1437
1438
bool SwPostItMgr::LayoutByPage(std::vector<SwAnnotationWin*> &aVisiblePostItList, const tools::Rectangle& rBorder, tools::Long lNeededHeight)
1439
0
{
1440
    /*** General layout idea:***/
1441
    //  - if we have space left, we always move the current one up,
1442
    //    otherwise the next one down
1443
    //  - first all notes are resized
1444
    //  - then the real layout starts
1445
1446
    //rBorder is the page rect
1447
0
    const tools::Rectangle aBorder         = mpEditWin->LogicToPixel(rBorder);
1448
0
    tools::Long            lTopBorder      = aBorder.Top() + 5;
1449
0
    tools::Long            lBottomBorder   = aBorder.Bottom() - 5;
1450
0
    const tools::Long      lVisibleHeight  = lBottomBorder - lTopBorder; //aBorder.GetHeight() ;
1451
0
    const size_t    nPostItListSize = aVisiblePostItList.size();
1452
0
    tools::Long            lTranslatePos   = 0;
1453
0
    bool            bScrollbars     = false;
1454
1455
    // do all necessary resizings
1456
0
    if (nPostItListSize > 0 && lVisibleHeight < lNeededHeight)
1457
0
    {
1458
        // ok, now we have to really resize and adding scrollbars
1459
0
        const tools::Long lAverageHeight = (lVisibleHeight - nPostItListSize*GetSpaceBetween()) / nPostItListSize;
1460
0
        if (lAverageHeight<GetMinimumSizeWithMeta())
1461
0
        {
1462
0
            bScrollbars = true;
1463
0
            lTopBorder += GetSidebarScrollerHeight() + 10;
1464
0
            lBottomBorder -= (GetSidebarScrollerHeight() + 10);
1465
0
            for (auto const& visiblePostIt : aVisiblePostItList)
1466
0
                visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),visiblePostIt->GetMinimumSizeWithMeta()));
1467
0
        }
1468
0
        else
1469
0
        {
1470
0
            for (auto const& visiblePostIt : aVisiblePostItList)
1471
0
            {
1472
0
                if ( visiblePostIt->VirtualSize().getHeight() > lAverageHeight)
1473
0
                    visiblePostIt->SetSize(Size(visiblePostIt->VirtualSize().getWidth(),lAverageHeight));
1474
0
            }
1475
0
        }
1476
0
    }
1477
1478
    //start the real layout so nothing overlaps anymore
1479
0
    if (aVisiblePostItList.size()>1)
1480
0
    {
1481
0
        int loop = 0;
1482
0
        bool bDone = false;
1483
        // if no window is moved anymore we are finished
1484
0
        while (!bDone)
1485
0
        {
1486
0
            loop++;
1487
0
            bDone = true;
1488
0
            tools::Long lSpaceUsed = lTopBorder + GetSpaceBetween();
1489
0
            for(auto i = aVisiblePostItList.begin(); i != aVisiblePostItList.end() ; ++i)
1490
0
            {
1491
0
                auto aNextPostIt = i;
1492
0
                ++aNextPostIt;
1493
1494
0
                if (aNextPostIt != aVisiblePostItList.end())
1495
0
                {
1496
0
                    lTranslatePos = ( (*i)->VirtualPos().Y() + (*i)->VirtualSize().Height()) - (*aNextPostIt)->VirtualPos().Y();
1497
0
                    if (lTranslatePos > 0) // note windows overlaps the next one
1498
0
                    {
1499
                        // we are not done yet, loop at least once more
1500
0
                        bDone = false;
1501
                        // if there is space left, move the current note up
1502
                        // it could also happen that there is no space left for the first note due to a scrollbar
1503
                        // then we also jump into, so we move the current one up and the next one down
1504
0
                        if ( (lSpaceUsed <= (*i)->VirtualPos().Y()) || (i==aVisiblePostItList.begin()))
1505
0
                        {
1506
                            // we have space left, so let's move the current one up
1507
0
                            if ( ((*i)->VirtualPos().Y()- lTranslatePos - GetSpaceBetween()) > lTopBorder)
1508
0
                            {
1509
0
                                if ((*aNextPostIt)->IsFollow())
1510
0
                                    (*i)->TranslateTopPosition(-1*(lTranslatePos+ANCHORLINE_WIDTH));
1511
0
                                else
1512
0
                                    (*i)->TranslateTopPosition(-1*(lTranslatePos+GetSpaceBetween()));
1513
0
                            }
1514
0
                            else
1515
0
                            {
1516
0
                                tools::Long lMoveUp = (*i)->VirtualPos().Y() - lTopBorder;
1517
0
                                (*i)->TranslateTopPosition(-1* lMoveUp);
1518
0
                                if ((*aNextPostIt)->IsFollow())
1519
0
                                    (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+ANCHORLINE_WIDTH) - lMoveUp);
1520
0
                                else
1521
0
                                    (*aNextPostIt)->TranslateTopPosition( (lTranslatePos+GetSpaceBetween()) - lMoveUp);
1522
0
                            }
1523
0
                        }
1524
0
                        else
1525
0
                        {
1526
                            // no space left, left move the next one down
1527
0
                            if ((*aNextPostIt)->IsFollow())
1528
0
                                (*aNextPostIt)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH);
1529
0
                            else
1530
0
                                (*aNextPostIt)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
1531
0
                        }
1532
0
                    }
1533
0
                    else
1534
0
                    {
1535
                        // the first one could overlap the topborder instead of a second note
1536
0
                        if (i==aVisiblePostItList.begin())
1537
0
                        {
1538
0
                            tools::Long lMoveDown = lTopBorder - (*i)->VirtualPos().Y();
1539
0
                            if (lMoveDown>0)
1540
0
                            {
1541
0
                                bDone = false;
1542
0
                                (*i)->TranslateTopPosition( lMoveDown);
1543
0
                            }
1544
0
                        }
1545
0
                    }
1546
0
                    if ( (*aNextPostIt)->IsFollow() )
1547
0
                        lSpaceUsed += (*i)->VirtualSize().Height() + ANCHORLINE_WIDTH;
1548
0
                    else
1549
0
                        lSpaceUsed += (*i)->VirtualSize().Height() + GetSpaceBetween();
1550
0
                }
1551
0
                else
1552
0
                {
1553
                    //(*i) is the last visible item
1554
0
                    auto aPrevPostIt = i;
1555
0
                    --aPrevPostIt;
1556
0
                    lTranslatePos = ( (*aPrevPostIt)->VirtualPos().Y() + (*aPrevPostIt)->VirtualSize().Height() ) - (*i)->VirtualPos().Y();
1557
0
                    if (lTranslatePos > 0)
1558
0
                    {
1559
0
                        bDone = false;
1560
0
                        if ( ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()+lTranslatePos) < lBottomBorder)
1561
0
                        {
1562
0
                            if ( (*i)->IsFollow() )
1563
0
                                (*i)->TranslateTopPosition(lTranslatePos+ANCHORLINE_WIDTH);
1564
0
                            else
1565
0
                                (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
1566
0
                        }
1567
0
                        else
1568
0
                        {
1569
0
                            (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()) );
1570
0
                        }
1571
0
                    }
1572
0
                    else
1573
0
                    {
1574
                        // note does not overlap, but we might be over the lower border
1575
                        // only do this if there are no scrollbars, otherwise notes are supposed to overlap the border
1576
0
                        if (!bScrollbars && ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height() > lBottomBorder) )
1577
0
                        {
1578
0
                            bDone = false;
1579
0
                            (*i)->TranslateTopPosition(lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height()));
1580
0
                        }
1581
0
                    }
1582
0
                }
1583
0
            }
1584
            // security check so we don't loop forever
1585
0
            if (loop>MAX_LOOP_COUNT)
1586
0
            {
1587
0
                OSL_FAIL("PostItMgr::Layout(): We are looping forever");
1588
0
                break;
1589
0
            }
1590
0
        }
1591
0
    }
1592
0
    else
1593
0
    {
1594
        // only one left, make sure it is not hidden at the top or bottom
1595
0
        auto i = aVisiblePostItList.begin();
1596
0
        lTranslatePos = lTopBorder - (*i)->VirtualPos().Y();
1597
0
        if (lTranslatePos>0)
1598
0
        {
1599
0
            (*i)->TranslateTopPosition(lTranslatePos+GetSpaceBetween());
1600
0
        }
1601
0
        lTranslatePos = lBottomBorder - ((*i)->VirtualPos().Y()+ (*i)->VirtualSize().Height());
1602
0
        if (lTranslatePos<0)
1603
0
        {
1604
0
            (*i)->TranslateTopPosition(lTranslatePos);
1605
0
        }
1606
0
    }
1607
0
    return bScrollbars;
1608
0
 }
1609
1610
std::vector<SwFormatField*> SwPostItMgr::UpdatePostItsParentInfo()
1611
4.26k
{
1612
4.26k
    IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1613
4.26k
    SwFieldType* pType = mpView->GetDocShell()->GetDoc()->getIDocumentFieldsAccess().GetFieldType(SwFieldIds::Postit, OUString(),false);
1614
4.26k
    std::vector<SwFormatField*> vFormatFields;
1615
4.26k
    pType->CollectPostIts(vFormatFields, rIDRA, mpWrtShell->GetLayout()->IsHideRedlines());
1616
1617
4.26k
    for (std::vector<SwFormatField*>::iterator i = vFormatFields.begin(); i != vFormatFields.end(); i++)
1618
3
    {
1619
3
        SwPostItField *pChildPostIt = static_cast<SwPostItField*>((*i)->GetField());
1620
1621
3
        if (pChildPostIt->GetParentId() != 0 || !pChildPostIt->GetParentName().isEmpty())
1622
0
        {
1623
0
            for (std::vector<SwFormatField*>::iterator j = vFormatFields.begin(); j != vFormatFields.end(); j++)
1624
0
            {
1625
0
                SwPostItField *pParentPostIt = static_cast<SwPostItField*>((*j)->GetField());
1626
0
                if (pChildPostIt->GetParentId() != 0 && pParentPostIt->GetParaId() == pChildPostIt->GetParentId())
1627
0
                {
1628
0
                    pChildPostIt->SetParentPostItId(pParentPostIt->GetPostItId());
1629
0
                    pChildPostIt->SetParentName(pParentPostIt->GetName());
1630
0
                }
1631
0
                else if (!pParentPostIt->GetName().isEmpty() && pParentPostIt->GetName() == pChildPostIt->GetParentName())
1632
0
                {
1633
0
                    pChildPostIt->SetParentPostItId(pParentPostIt->GetPostItId());
1634
0
                    pChildPostIt->SetParentName(pParentPostIt->GetName());
1635
0
                }
1636
0
            }
1637
0
        }
1638
3
    }
1639
4.26k
    return vFormatFields;
1640
4.26k
}
1641
1642
1643
void SwPostItMgr::AddPostIts(const bool bCheckExistence, const bool bFocus)
1644
4.26k
{
1645
4.26k
    const bool bEmpty = mvPostItFields.empty();
1646
4.26k
    std::vector<SwFormatField*> vFormatFields = UpdatePostItsParentInfo();
1647
1648
4.26k
    for(auto pFormatField : vFormatFields)
1649
3
        InsertItem(pFormatField, bCheckExistence, bFocus);
1650
    // if we just added the first one we have to update the view for centering
1651
4.26k
    if (bEmpty && !mvPostItFields.empty())
1652
3
        PrepareView(true);
1653
4.26k
}
1654
1655
void SwPostItMgr::RemoveSidebarWin()
1656
4.26k
{
1657
4.26k
    for (auto& postItField : mvPostItFields)
1658
3
    {
1659
3
        EndListening( *const_cast<SfxBroadcaster*>(postItField->GetBroadcaster()) );
1660
3
        postItField->mpPostIt.disposeAndClear();
1661
3
        postItField.reset();
1662
3
    }
1663
4.26k
    mvPostItFields.clear();
1664
1665
    // all postits removed, no items should be left in pages
1666
4.26k
    PreparePageContainer();
1667
4.26k
}
1668
1669
static bool ConfirmDeleteAll(const SwView& pView, const OUString& sText)
1670
0
{
1671
0
    const bool bAsk = officecfg::Office::Common::Misc::QueryDeleteAllComments::get();
1672
0
    bool bConfirm = true;
1673
0
    if (bAsk)
1674
0
    {
1675
0
        VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create();
1676
0
        auto pDlg
1677
0
            = pFact->CreateQueryDialog(pView.GetFrameWeld(),
1678
0
                                       SwResId(STR_QUERY_DELALLCOMMENTS_TITLE), sText, "", true);
1679
0
        sal_Int32 nResult = pDlg->Execute();
1680
0
        if (pDlg->ShowAgain() == false)
1681
0
        {
1682
0
            std::shared_ptr<comphelper::ConfigurationChanges> xChanges(
1683
0
                comphelper::ConfigurationChanges::create());
1684
0
            officecfg::Office::Common::Misc::QueryDeleteAllComments::set(false, xChanges);
1685
0
            xChanges->commit();
1686
0
        }
1687
0
        bConfirm = (nResult == RET_YES);
1688
0
        pDlg->disposeOnce();
1689
0
    }
1690
0
    return bConfirm;
1691
0
}
1692
1693
std::unique_ptr<SwPostItMgr::CommentDeleteFlagsRestore> SwPostItMgr::ConfigureForCommentDelete()
1694
0
{
1695
0
    if (!mpWrtShell->IsRedlineOn())
1696
0
        return {}; // No track changes - no need to disable it
1697
0
    if (isOwnFileFormat(mpView->GetDocShell()->GetMedium()))
1698
0
        return {}; // Format is smart enough to handle deleted comments in redlines
1699
1700
0
    return std::unique_ptr<CommentDeleteFlagsRestore>(
1701
0
        new CommentDeleteFlagsRestoreImpl(mpWrtShell));
1702
0
}
1703
1704
// copy to new vector, otherwise RemoveItem would operate and delete stuff on mvPostItFields as well
1705
// RemoveItem will clean up the core field and visible postit if necessary
1706
// we cannot just delete everything as before, as postits could move into change tracking
1707
void SwPostItMgr::Delete(const OUString& rAuthor)
1708
0
{
1709
0
    OUString sQuestion = SwResId(STR_QUERY_DELALLCOMMENTSAUTHOR_QUESTION);
1710
0
    sQuestion = sQuestion.replaceAll("%AUTHOR", rAuthor);
1711
0
    if (!ConfirmDeleteAll(mpWrtShell->GetView(), sQuestion))
1712
0
        return;
1713
1714
    // tdf#136540 - prevent scrolling to cursor during deletion of annotations
1715
0
    const bool bUnLockView = !mpWrtShell->IsViewLocked();
1716
0
    mpWrtShell->LockView(true);
1717
1718
0
    mpWrtShell->StartAllAction();
1719
0
    if (HasActiveSidebarWin() && (GetActiveSidebarWin()->GetAuthor() == rAuthor))
1720
0
    {
1721
0
        SetActiveSidebarWin(nullptr);
1722
0
    }
1723
0
    SwRewriter aRewriter;
1724
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_AUTHOR_NOTES) + rAuthor);
1725
0
    mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
1726
1727
0
    IsPostitFieldWithAuthorOf aFilter(rAuthor);
1728
0
    IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1729
0
    IsFieldNotDeleted aFilter2(rIDRA, aFilter);
1730
0
    FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2);
1731
0
    auto restoreGuard = ConfigureForCommentDelete();
1732
0
    while (const SwFormatField* pField = aStack.pop())
1733
0
    {
1734
0
        if (mpWrtShell->GotoField(*pField))
1735
0
            mpWrtShell->DelRight();
1736
0
    }
1737
0
    restoreGuard.reset();
1738
0
    mpWrtShell->EndUndo();
1739
0
    PrepareView();
1740
0
    mpWrtShell->EndAllAction();
1741
0
    mbLayout = true;
1742
0
    CalcRects();
1743
0
    LayoutPostIts();
1744
1745
    // tdf#136540 - prevent scrolling to cursor during deletion of annotations
1746
0
    if (bUnLockView)
1747
0
        mpWrtShell->LockView(false);
1748
0
}
1749
1750
void SwPostItMgr::Delete(sal_uInt32 nPostItId)
1751
0
{
1752
0
    mpWrtShell->StartAllAction();
1753
0
    if (HasActiveSidebarWin() &&
1754
0
        mpActivePostIt->GetPostItField()->GetPostItId() == nPostItId)
1755
0
    {
1756
0
        SetActiveSidebarWin(nullptr);
1757
0
    }
1758
0
    SwRewriter aRewriter;
1759
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1760
0
    mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
1761
1762
0
    IsPostitFieldWithPostitId aFilter(nPostItId);
1763
0
    IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1764
0
    IsFieldNotDeleted aFilter2(rIDRA, aFilter);
1765
0
    FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter2);
1766
0
    const SwFormatField* pField = aStack.pop();
1767
0
    if (pField && mpWrtShell->GotoField(*pField))
1768
0
    {
1769
0
        auto restoreGuard = ConfigureForCommentDelete();
1770
0
        mpWrtShell->DelRight();
1771
0
    }
1772
0
    mpWrtShell->EndUndo();
1773
0
    PrepareView();
1774
0
    mpWrtShell->EndAllAction();
1775
0
    mbLayout = true;
1776
0
    CalcRects();
1777
0
    LayoutPostIts();
1778
0
}
1779
1780
void SwPostItMgr::DeleteCommentThread(sal_uInt32 nPostItId)
1781
0
{
1782
0
    mpWrtShell->StartAllAction();
1783
1784
0
    SwRewriter aRewriter;
1785
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1786
1787
    // We have no undo ID at the moment.
1788
1789
0
    IsPostitFieldWithPostitId aFilter(nPostItId);
1790
0
    FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1791
0
    const SwFormatField* pField = aStack.pop();
1792
    // pField now contains our AnnotationWin object
1793
0
    if (pField) {
1794
0
        SwAnnotationWin* pWin = GetSidebarWin(pField);
1795
0
        pWin->DeleteThread();
1796
0
    }
1797
0
    PrepareView();
1798
0
    mpWrtShell->EndAllAction();
1799
0
    mbLayout = true;
1800
0
    CalcRects();
1801
0
    LayoutPostIts();
1802
0
}
1803
1804
void SwPostItMgr::ToggleResolved(sal_uInt32 nPostItId)
1805
0
{
1806
0
    mpWrtShell->StartAllAction();
1807
1808
0
    SwRewriter aRewriter;
1809
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1810
1811
    // We have no undo ID at the moment.
1812
1813
0
    IsPostitFieldWithPostitId aFilter(nPostItId);
1814
0
    FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1815
0
    const SwFormatField* pField = aStack.pop();
1816
    // pField now contains our AnnotationWin object
1817
0
    if (pField) {
1818
0
        SwAnnotationWin* pWin = GetSidebarWin(pField);
1819
0
        pWin->ToggleResolved();
1820
0
    }
1821
1822
0
    PrepareView();
1823
0
    mpWrtShell->EndAllAction();
1824
0
    mbLayout = true;
1825
0
    CalcRects();
1826
0
    LayoutPostIts();
1827
0
}
1828
1829
void SwPostItMgr::ToggleResolvedForThread(sal_uInt32 nPostItId)
1830
0
{
1831
0
    mpWrtShell->StartAllAction();
1832
1833
0
    SwRewriter aRewriter;
1834
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1835
1836
    // We have no undo ID at the moment.
1837
1838
0
    IsPostitFieldWithPostitId aFilter(nPostItId);
1839
0
    FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1840
0
    const SwFormatField* pField = aStack.pop();
1841
    // pField now contains our AnnotationWin object
1842
0
    if (pField) {
1843
0
        SwAnnotationWin* pWin = GetSidebarWin(pField);
1844
0
        pWin->ToggleResolvedForThread();
1845
0
    }
1846
1847
0
    PrepareView();
1848
0
    mpWrtShell->EndAllAction();
1849
0
    mbLayout = true;
1850
0
    CalcRects();
1851
0
    LayoutPostIts();
1852
0
}
1853
1854
1855
void SwPostItMgr::Delete()
1856
0
{
1857
0
    if (!ConfirmDeleteAll(mpWrtShell->GetView(), SwResId(STR_QUERY_DELALLCOMMENTS_QUESTION)))
1858
0
        return;
1859
1860
0
    mpWrtShell->StartAllAction();
1861
0
    SetActiveSidebarWin(nullptr);
1862
0
    SwRewriter aRewriter;
1863
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_DELETE_ALL_NOTES) );
1864
0
    mpWrtShell->StartUndo( SwUndoId::DELETE, &aRewriter );
1865
1866
0
    IsPostitField aFilter;
1867
0
    IDocumentRedlineAccess const& rIDRA(mpWrtShell->getIDocumentRedlineAccess());
1868
0
    IsFieldNotDeleted aFilter2(rIDRA, aFilter);
1869
0
    FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(),
1870
0
        aFilter2);
1871
0
    auto restoreGuard = ConfigureForCommentDelete();
1872
0
    while (const SwFormatField* pField = aStack.pop())
1873
0
    {
1874
0
        if (mpWrtShell->GotoField(*pField))
1875
0
            mpWrtShell->DelRight();
1876
0
    }
1877
0
    restoreGuard.reset();
1878
1879
0
    mpWrtShell->EndUndo();
1880
0
    PrepareView();
1881
0
    mpWrtShell->EndAllAction();
1882
0
    mbLayout = true;
1883
0
    CalcRects();
1884
0
    LayoutPostIts();
1885
0
}
1886
1887
void SwPostItMgr::PromoteToRoot(sal_uInt32 nPostItId)
1888
0
{
1889
0
    mpWrtShell->StartAllAction();
1890
1891
0
    SwRewriter aRewriter;
1892
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_CONTENT_TYPE_SINGLE_POSTIT));
1893
1894
    // We have no undo ID at the moment.
1895
1896
0
    IsPostitFieldWithPostitId aFilter(nPostItId);
1897
0
    FieldDocWatchingStack aStack(mvPostItFields, *mpView->GetDocShell(), aFilter);
1898
0
    const SwFormatField* pField = aStack.pop();
1899
    // pField now contains our AnnotationWin object
1900
0
    if (pField)
1901
0
    {
1902
0
        SwAnnotationWin* pWin = GetSidebarWin(pField);
1903
0
        pWin->SetAsRoot();
1904
0
    }
1905
0
    PrepareView();
1906
0
    mpWrtShell->EndAllAction();
1907
0
    mbLayout = true;
1908
0
    CalcRects();
1909
0
    LayoutPostIts();
1910
0
}
1911
1912
void SwPostItMgr::MoveSubthreadToRoot(const sw::annotation::SwAnnotationWin* pNewRoot)
1913
0
{
1914
0
    std::vector<std::unique_ptr<SwAnnotationItem>>::iterator first, middle, last;
1915
0
    first = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
1916
0
                         [&pNewRoot](const std::unique_ptr<SwAnnotationItem>& pField) {
1917
0
                             return pField->mpPostIt == pNewRoot;
1918
0
                         });
1919
0
    if (first == mvPostItFields.end())
1920
0
        return;
1921
0
    std::set<int> aPostItIds;
1922
0
    aPostItIds.insert(pNewRoot->GetPostItField()->GetPostItId());
1923
0
    middle = first + 1;
1924
0
    while (middle != mvPostItFields.end()
1925
0
           && aPostItIds.contains((*middle)->mpPostIt->GetPostItField()->GetParentPostItId()))
1926
0
    {
1927
0
        aPostItIds.insert((*middle)->mpPostIt->GetPostItField()->GetPostItId());
1928
0
        ++middle;
1929
0
    }
1930
0
    if (middle == mvPostItFields.end())
1931
0
        return;
1932
0
    last = middle;
1933
0
    while (last != mvPostItFields.end()
1934
0
           && (*last)->mpPostIt->GetPostItField()->GetParentPostItId() != 0)
1935
0
        ++last;
1936
0
    if (last == middle)
1937
0
        return;
1938
0
    std::rotate(first, middle, last);
1939
0
    CalcRects();
1940
0
    LayoutPostIts();
1941
0
}
1942
1943
void SwPostItMgr::ExecuteFormatAllDialog(SwView& rView)
1944
0
{
1945
0
    if (mvPostItFields.empty())
1946
0
        return;
1947
0
    sw::annotation::SwAnnotationWin *pOrigActiveWin = GetActiveSidebarWin();
1948
0
    sw::annotation::SwAnnotationWin *pWin = pOrigActiveWin;
1949
0
    if (!pWin)
1950
0
    {
1951
0
        for (auto const& postItField : mvPostItFields)
1952
0
        {
1953
0
            pWin = postItField->mpPostIt;
1954
0
            if (pWin)
1955
0
                break;
1956
0
        }
1957
0
    }
1958
0
    if (!pWin)
1959
0
        return;
1960
0
    SetActiveSidebarWin(pWin);
1961
0
    OutlinerView* pOLV = pWin->GetOutlinerView();
1962
0
    SfxItemSet aEditAttr(pOLV->GetAttribs());
1963
0
    SfxItemPool* pPool(SwAnnotationShell::GetAnnotationPool(rView));
1964
0
    auto xDlgAttr = std::make_shared<SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLCOLOR, EE_ITEMS_START, EE_ITEMS_END>>(*pPool);
1965
0
    xDlgAttr->Put(aEditAttr);
1966
0
    SwAbstractDialogFactory* pFact = SwAbstractDialogFactory::Create();
1967
0
    VclPtr<SfxAbstractTabDialog> pDlg(pFact->CreateSwCharDlg(rView.GetFrameWeld(), rView, *xDlgAttr, SwCharDlgMode::Ann));
1968
0
    pDlg->StartExecuteAsync(
1969
0
        [this, pDlg, xDlgAttr=std::move(xDlgAttr), pOrigActiveWin] (sal_Int32 nResult)->void
1970
0
        {
1971
0
            if (nResult == RET_OK)
1972
0
            {
1973
0
                auto aNewAttr = *xDlgAttr;
1974
0
                aNewAttr.Put(*pDlg->GetOutputItemSet());
1975
0
                FormatAll(aNewAttr);
1976
0
            }
1977
0
            pDlg->disposeOnce();
1978
0
            SetActiveSidebarWin(pOrigActiveWin);
1979
0
        }
1980
0
    );
1981
0
}
1982
1983
void SwPostItMgr::FormatAll(const SfxItemSet &rNewAttr)
1984
0
{
1985
0
    mpWrtShell->StartAllAction();
1986
0
    SwRewriter aRewriter;
1987
0
    aRewriter.AddRule(UndoArg1, SwResId(STR_FORMAT_ALL_NOTES) );
1988
0
    mpWrtShell->StartUndo( SwUndoId::INSATTR, &aRewriter );
1989
1990
0
    for (auto const& postItField : mvPostItFields)
1991
0
    {
1992
0
        if (!postItField->mpPostIt)
1993
0
            continue;
1994
0
        OutlinerView* pOLV = postItField->mpPostIt->GetOutlinerView();
1995
        //save old selection
1996
0
        ESelection aOrigSel(pOLV->GetSelection());
1997
        //select all
1998
0
        Outliner& rOutliner = pOLV->GetOutliner();
1999
0
        sal_Int32 nParaCount = rOutliner.GetParagraphCount();
2000
0
        if (nParaCount > 0)
2001
0
            pOLV->SelectRange(0, nParaCount);
2002
        //set new char properties
2003
0
        pOLV->SetAttribs(rNewAttr);
2004
        //restore old selection
2005
0
        pOLV->SetSelection(aOrigSel);
2006
        // tdf#91596 store updated formatting in SwField
2007
0
        postItField->mpPostIt->UpdateData();
2008
0
    }
2009
2010
0
    mpWrtShell->EndUndo();
2011
0
    PrepareView();
2012
0
    mpWrtShell->EndAllAction();
2013
0
    mbLayout = true;
2014
0
    CalcRects();
2015
0
    LayoutPostIts();
2016
0
}
2017
2018
void SwPostItMgr::Hide( std::u16string_view rAuthor )
2019
0
{
2020
0
    for (auto const& postItField : mvPostItFields)
2021
0
    {
2022
0
        if ( postItField->mpPostIt && (postItField->mpPostIt->GetAuthor() == rAuthor) )
2023
0
        {
2024
0
            postItField->mbShow  = false;
2025
0
            postItField->mpPostIt->HideNote();
2026
0
        }
2027
0
    }
2028
2029
0
    LayoutPostIts();
2030
0
}
2031
2032
void SwPostItMgr::Hide()
2033
0
{
2034
0
    for (auto const& postItField : mvPostItFields)
2035
0
    {
2036
0
        postItField->mbShow = false;
2037
0
        if (postItField->mpPostIt)
2038
0
            postItField->mpPostIt->HideNote();
2039
0
    }
2040
0
}
2041
2042
SwAnnotationWin* SwPostItMgr::GetSidebarWin( const SfxBroadcaster* pBroadcaster) const
2043
0
{
2044
0
    for (auto const& postItField : mvPostItFields)
2045
0
    {
2046
0
        if ( postItField->GetBroadcaster() == pBroadcaster)
2047
0
            return postItField->mpPostIt;
2048
0
    }
2049
0
    return nullptr;
2050
0
}
2051
2052
sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const SwPostItField* pField) const
2053
4
{
2054
4
    for (auto const& postItField : mvPostItFields)
2055
4
    {
2056
4
        if ( postItField->GetFormatField().GetField() == pField )
2057
4
            return postItField->mpPostIt.get();
2058
4
    }
2059
0
    return nullptr;
2060
4
}
2061
2062
sw::annotation::SwAnnotationWin* SwPostItMgr::GetAnnotationWin(const sal_uInt32 nPostItId) const
2063
0
{
2064
0
    for (auto const& postItField : mvPostItFields)
2065
0
    {
2066
0
        if ( static_cast<const SwPostItField*>(postItField->GetFormatField().GetField())->GetPostItId() == nPostItId )
2067
0
            return postItField->mpPostIt.get();
2068
0
    }
2069
0
    return nullptr;
2070
0
}
2071
2072
SwPostItField* SwPostItMgr::GetLatestPostItField()
2073
0
{
2074
0
    return static_cast<SwPostItField*>(mvPostItFields.back()->GetFormatField().GetField());
2075
0
}
2076
2077
sw::annotation::SwAnnotationWin* SwPostItMgr::GetOrCreateAnnotationWindowForLatestPostItField()
2078
0
{
2079
0
    return GetOrCreateAnnotationWindow(*mvPostItFields.back(), o3tl::temporary(bool()));
2080
0
}
2081
2082
SwAnnotationWin* SwPostItMgr::GetNextPostIt( sal_uInt16 aDirection,
2083
                                          SwAnnotationWin* aPostIt )
2084
0
{
2085
0
    if (mvPostItFields.size()>1)
2086
0
    {
2087
0
        auto i = std::find_if(mvPostItFields.begin(), mvPostItFields.end(),
2088
0
            [&aPostIt](const std::unique_ptr<SwAnnotationItem>& pField) { return pField->mpPostIt == aPostIt; });
2089
0
        if (i == mvPostItFields.end())
2090
0
            return nullptr;
2091
2092
0
        auto iNextPostIt = i;
2093
0
        if (aDirection == KEY_PAGEUP)
2094
0
        {
2095
0
            if ( iNextPostIt == mvPostItFields.begin() )
2096
0
            {
2097
0
                return nullptr;
2098
0
            }
2099
0
            --iNextPostIt;
2100
0
        }
2101
0
        else
2102
0
        {
2103
0
            ++iNextPostIt;
2104
0
            if ( iNextPostIt == mvPostItFields.end() )
2105
0
            {
2106
0
                return nullptr;
2107
0
            }
2108
0
        }
2109
        // let's quit, we are back at the beginning
2110
0
        if ( (*iNextPostIt)->mpPostIt == aPostIt)
2111
0
            return nullptr;
2112
0
        return (*iNextPostIt)->mpPostIt;
2113
0
    }
2114
0
    else
2115
0
        return nullptr;
2116
0
}
2117
2118
tools::Long SwPostItMgr::GetNextBorder()
2119
0
{
2120
0
    for (auto const& pPage : mPages)
2121
0
    {
2122
0
        for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
2123
0
        {
2124
0
            if ((*b)->mpPostIt == mpActivePostIt)
2125
0
            {
2126
0
                auto aNext = b;
2127
0
                ++aNext;
2128
0
                bool bFollow = (aNext != pPage->mvSidebarItems.end()) && (*aNext)->mpPostIt->IsFollow();
2129
0
                if ( pPage->bScrollbar || bFollow )
2130
0
                {
2131
0
                    return -1;
2132
0
                }
2133
0
                else
2134
0
                {
2135
                    //if this is the last item, return the bottom border otherwise the next item
2136
0
                    if (aNext == pPage->mvSidebarItems.end())
2137
0
                        return mpEditWin->LogicToPixel(Point(0,pPage->mPageRect.Bottom())).Y() - GetSpaceBetween();
2138
0
                    else
2139
0
                        return (*aNext)->mpPostIt->GetPosPixel().Y() - GetSpaceBetween();
2140
0
                }
2141
0
            }
2142
0
        }
2143
0
    }
2144
2145
0
    OSL_FAIL("SwPostItMgr::GetNextBorder(): We have to find a next border here");
2146
0
    return -1;
2147
0
}
2148
2149
void SwPostItMgr::SetShadowState(const SwPostItField* pField,bool bCursor)
2150
9.53k
{
2151
9.53k
    if (pField)
2152
4
    {
2153
4
        if (pField !=mShadowState.mpShadowField)
2154
4
        {
2155
4
            if (mShadowState.mpShadowField)
2156
0
            {
2157
                // reset old one if still alive
2158
                // TODO: does not work properly if mouse and cursor was set
2159
0
                sw::annotation::SwAnnotationWin* pOldPostIt =
2160
0
                                    GetAnnotationWin(mShadowState.mpShadowField);
2161
0
                if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT))
2162
0
                    pOldPostIt->SetViewState(ViewState::NORMAL);
2163
0
            }
2164
            //set new one, if it is not currently edited
2165
4
            sw::annotation::SwAnnotationWin* pNewPostIt = GetAnnotationWin(pField);
2166
4
            if (pNewPostIt && pNewPostIt->Shadow() && (pNewPostIt->Shadow()->GetShadowState() != SS_EDIT))
2167
0
            {
2168
0
                pNewPostIt->SetViewState(ViewState::VIEW);
2169
                //remember our new field
2170
0
                mShadowState.mpShadowField = pField;
2171
0
                mShadowState.bCursor = false;
2172
0
                mShadowState.bMouse = false;
2173
0
            }
2174
4
        }
2175
4
        if (bCursor)
2176
4
            mShadowState.bCursor = true;
2177
0
        else
2178
0
            mShadowState.bMouse = true;
2179
4
    }
2180
9.53k
    else
2181
9.53k
    {
2182
9.53k
        if (mShadowState.mpShadowField)
2183
0
        {
2184
0
            if (bCursor)
2185
0
                mShadowState.bCursor = false;
2186
0
            else
2187
0
                mShadowState.bMouse = false;
2188
0
            if (!mShadowState.bCursor && !mShadowState.bMouse)
2189
0
            {
2190
                // reset old one if still alive
2191
0
                sw::annotation::SwAnnotationWin* pOldPostIt = GetAnnotationWin(mShadowState.mpShadowField);
2192
0
                if (pOldPostIt && pOldPostIt->Shadow() && (pOldPostIt->Shadow()->GetShadowState() != SS_EDIT))
2193
0
                {
2194
0
                    pOldPostIt->SetViewState(ViewState::NORMAL);
2195
0
                    mShadowState.mpShadowField = nullptr;
2196
0
                }
2197
0
            }
2198
0
        }
2199
9.53k
    }
2200
9.53k
}
2201
2202
void SwPostItMgr::PrepareView(bool bIgnoreCount)
2203
115
{
2204
115
    if (!HasNotes() || bIgnoreCount)
2205
115
    {
2206
115
        mpWrtShell->StartAllAction();
2207
115
        SwRootFrame* pLayout = mpWrtShell->GetLayout();
2208
115
        if ( pLayout )
2209
115
            SwPostItHelper::setSidebarChanged( pLayout,
2210
115
                mpWrtShell->getIDocumentSettingAccess().get( DocumentSettingId::BROWSE_MODE ) );
2211
115
        mpWrtShell->EndAllAction();
2212
115
    }
2213
115
}
2214
2215
bool SwPostItMgr::ShowScrollbar(const tools::ULong aPage) const
2216
0
{
2217
0
    if (mPages.size() > aPage-1)
2218
0
        return (mPages[aPage-1]->bScrollbar && !mbWaitingForCalcRects);
2219
0
    else
2220
0
        return false;
2221
0
}
2222
2223
bool SwPostItMgr::IsHit(const Point& aPointPixel)
2224
0
{
2225
0
    if (!HasNotes() || !ShowNotes())
2226
0
        return false;
2227
2228
0
    const Point aPoint = mpEditWin->PixelToLogic(aPointPixel);
2229
0
    tools::Rectangle aRect(GetSidebarRect(aPoint));
2230
0
    if (!aRect.Contains(aPoint))
2231
0
        return false;
2232
2233
    // we hit the note's sidebar
2234
    // let's now test for the arrow area
2235
0
    SwRect aPageFrame;
2236
0
    const tools::ULong nPageNum
2237
0
        = SwPostItHelper::getPageInfo(aPageFrame, mpWrtShell->GetLayout(), aPoint);
2238
0
    if (!nPageNum)
2239
0
        return false;
2240
0
    if (mPages[nPageNum - 1]->bScrollbar)
2241
0
        return ScrollbarHit(nPageNum, aPoint);
2242
0
    return false;
2243
0
}
2244
2245
vcl::Window* SwPostItMgr::IsHitSidebarWindow(const Point& rPointLogic)
2246
0
{
2247
0
    vcl::Window* pRet = nullptr;
2248
2249
0
    if (HasNotes() && ShowNotes())
2250
0
    {
2251
0
        bool bEnableMapMode = !mpEditWin->IsMapModeEnabled();
2252
0
        if (bEnableMapMode)
2253
0
            mpEditWin->EnableMapMode();
2254
2255
0
        for (const std::unique_ptr<SwAnnotationItem>& pItem : mvPostItFields)
2256
0
        {
2257
0
            SwAnnotationWin* pPostIt = pItem->mpPostIt;
2258
0
            if (!pPostIt)
2259
0
                continue;
2260
2261
0
            if (pPostIt->IsHitWindow(rPointLogic))
2262
0
            {
2263
0
                pRet = pPostIt;
2264
0
                break;
2265
0
            }
2266
0
        }
2267
2268
0
        if (bEnableMapMode)
2269
0
            mpEditWin->EnableMapMode(false);
2270
0
    }
2271
2272
0
    return pRet;
2273
0
}
2274
2275
tools::Rectangle SwPostItMgr::GetSidebarRect(const Point& rPointLogic)
2276
0
{
2277
0
    const SwRootFrame* pLayout = mpWrtShell->GetLayout();
2278
0
    SwRect aPageFrame;
2279
0
    const tools::ULong nPageNum = SwPostItHelper::getPageInfo(aPageFrame, pLayout, rPointLogic);
2280
0
    if (!nPageNum)
2281
0
        return tools::Rectangle();
2282
2283
0
    return GetSidebarPos(rPointLogic) == sw::sidebarwindows::SidebarPosition::LEFT
2284
0
               ? tools::Rectangle(
2285
0
                     Point(aPageFrame.Left() - GetSidebarWidth() - GetSidebarBorderWidth(),
2286
0
                           aPageFrame.Top()),
2287
0
                     Size(GetSidebarWidth(), aPageFrame.Height()))
2288
0
               : tools::Rectangle(
2289
0
                     Point(aPageFrame.Right() + GetSidebarBorderWidth(), aPageFrame.Top()),
2290
0
                     Size(GetSidebarWidth(), aPageFrame.Height()));
2291
0
}
2292
2293
bool SwPostItMgr::IsHitSidebarDragArea(const Point& rPointPx)
2294
0
{
2295
0
    if (!HasNotes() || !ShowNotes())
2296
0
        return false;
2297
2298
0
    const Point aPointLogic = mpEditWin->PixelToLogic(rPointPx);
2299
0
    sw::sidebarwindows::SidebarPosition eSidebarPosition = GetSidebarPos(aPointLogic);
2300
0
    if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::NONE)
2301
0
        return false;
2302
2303
0
    tools::Rectangle aDragArea(GetSidebarRect(aPointLogic));
2304
0
    aDragArea.SetTop(aPointLogic.Y());
2305
0
    if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
2306
0
        aDragArea.SetPos(Point(aDragArea.Right() - 50, aDragArea.Top()));
2307
0
    else
2308
0
        aDragArea.SetPos(Point(aDragArea.Left() - 50, aDragArea.Top()));
2309
2310
0
    Size aS(aDragArea.GetSize());
2311
0
    aS.setWidth(100);
2312
0
    aDragArea.SetSize(aS);
2313
0
    return aDragArea.Contains(aPointLogic);
2314
0
}
2315
2316
tools::Rectangle SwPostItMgr::GetBottomScrollRect(const tools::ULong aPage) const
2317
0
{
2318
0
    SwRect aPageRect = mPages[aPage-1]->mPageRect;
2319
0
    Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2320
0
                         ? Point(aPageRect.Left() - GetSidebarWidth() - GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
2321
0
                         : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
2322
0
    Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
2323
0
    return tools::Rectangle(aPointBottom,aSize);
2324
0
}
2325
2326
tools::Rectangle SwPostItMgr::GetTopScrollRect(const tools::ULong aPage) const
2327
0
{
2328
0
    SwRect aPageRect = mPages[aPage-1]->mPageRect;
2329
0
    Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2330
0
                      ? Point(aPageRect.Left() - GetSidebarWidth() -GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height())
2331
0
                      : Point(aPageRect.Right() + GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height());
2332
0
    Size aSize(GetSidebarWidth() - mpEditWin->PixelToLogic(Size(4,0)).Width(), mpEditWin->PixelToLogic(Size(0,GetSidebarScrollerHeight())).Height()) ;
2333
0
    return tools::Rectangle(aPointTop,aSize);
2334
0
}
2335
2336
//IMPORTANT: if you change the rects here, also change SwPageFrame::PaintNotesSidebar()
2337
bool SwPostItMgr::ScrollbarHit(const tools::ULong aPage,const Point &aPoint)
2338
0
{
2339
0
    SwRect aPageRect = mPages[aPage-1]->mPageRect;
2340
0
    Point aPointBottom = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2341
0
                         ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth() + mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height())
2342
0
                         : Point(aPageRect.Right() + GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Bottom()- mpEditWin->PixelToLogic(Size(0,2+GetSidebarScrollerHeight())).Height());
2343
2344
0
    Point aPointTop = mPages[aPage-1]->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2345
0
                      ? Point(aPageRect.Left() - GetSidebarWidth()-GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height())
2346
0
                      : Point(aPageRect.Right()+GetSidebarBorderWidth()+ mpEditWin->PixelToLogic(Size(2,0)).Width(),aPageRect.Top() + mpEditWin->PixelToLogic(Size(0,2)).Height());
2347
2348
0
    tools::Rectangle aRectBottom(GetBottomScrollRect(aPage));
2349
0
    tools::Rectangle aRectTop(GetTopScrollRect(aPage));
2350
2351
0
    if (aRectBottom.Contains(aPoint))
2352
0
    {
2353
0
        if (aPoint.X() < tools::Long((aPointBottom.X() + GetSidebarWidth()/3)))
2354
0
            Scroll( GetScrollSize(),aPage);
2355
0
        else
2356
0
            Scroll( -1*GetScrollSize(), aPage);
2357
0
        return true;
2358
0
    }
2359
0
    else if (aRectTop.Contains(aPoint))
2360
0
    {
2361
0
        if (aPoint.X() < tools::Long((aPointTop.X() + GetSidebarWidth()/3*2)))
2362
0
            Scroll(GetScrollSize(), aPage);
2363
0
        else
2364
0
            Scroll(-1*GetScrollSize(), aPage);
2365
0
        return true;
2366
0
    }
2367
0
    return false;
2368
0
}
2369
2370
void SwPostItMgr::CorrectPositions()
2371
0
{
2372
0
    if ( mbWaitingForCalcRects || mbLayouting || mvPostItFields.empty() )
2373
0
        return;
2374
2375
    // find first valid note
2376
0
    SwAnnotationWin *pFirstPostIt = nullptr;
2377
0
    for (auto const& postItField : mvPostItFields)
2378
0
    {
2379
0
        pFirstPostIt = postItField->mpPostIt;
2380
0
        if (pFirstPostIt)
2381
0
            break;
2382
0
    }
2383
2384
    //if we have not found a valid note, forget about it and leave
2385
0
    if (!pFirstPostIt)
2386
0
        return;
2387
2388
    // yeah, I know,    if this is a left page it could be wrong, but finding the page and the note is probably not even faster than just doing it
2389
    // check, if anchor overlay object exists.
2390
0
    const tools::Long aAnchorX = pFirstPostIt->Anchor()
2391
0
                          ? mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(pFirstPostIt->Anchor()->GetSixthPosition().getX()),0)).X()
2392
0
                          : 0;
2393
0
    const tools::Long aAnchorY = pFirstPostIt->Anchor()
2394
0
                          ? mpEditWin->LogicToPixel( Point(0,static_cast<tools::Long>(pFirstPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1
2395
0
                          : 0;
2396
0
    if (Point(aAnchorX,aAnchorY) == pFirstPostIt->GetPosPixel())
2397
0
        return;
2398
2399
0
    tools::Long aAnchorPosX = 0;
2400
0
    tools::Long aAnchorPosY = 0;
2401
0
    for (const std::unique_ptr<SwPostItPageItem>& pPage : mPages)
2402
0
    {
2403
0
        for (auto const& item : pPage->mvSidebarItems)
2404
0
        {
2405
            // check, if anchor overlay object exists.
2406
0
            if ( item->mbShow && item->mpPostIt && item->mpPostIt->Anchor() )
2407
0
            {
2408
0
                aAnchorPosX = pPage->eSidebarPosition == sw::sidebarwindows::SidebarPosition::LEFT
2409
0
                    ? mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSeventhPosition().getX()),0)).X()
2410
0
                    : mpEditWin->LogicToPixel( Point(static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSixthPosition().getX()),0)).X();
2411
0
                aAnchorPosY = mpEditWin->LogicToPixel( Point(0,static_cast<tools::Long>(item->mpPostIt->Anchor()->GetSixthPosition().getY()))).Y() + 1;
2412
0
                item->mpPostIt->SetPosPixel(Point(aAnchorPosX,aAnchorPosY));
2413
0
            }
2414
0
        }
2415
0
    }
2416
0
}
2417
2418
bool SwPostItMgr::ShowNotes() const
2419
9.12M
{
2420
    // we only want to see notes if Options - Writer - View - Notes is ticked
2421
9.12M
    return mbForceShow || mpWrtShell->GetViewOptions()->IsPostIts();
2422
9.12M
}
2423
2424
bool SwPostItMgr::HasNotes() const
2425
9.22M
{
2426
9.22M
    return !mvPostItFields.empty();
2427
9.22M
}
2428
2429
void SwPostItMgr::SetSidebarWidth(const Point& rPointLogic)
2430
0
{
2431
0
    tools::Rectangle nSidebarRect = GetSidebarRect(rPointLogic);
2432
0
    if (nSidebarRect.IsEmpty())
2433
0
        return;
2434
2435
0
    sw::sidebarwindows::SidebarPosition eSidebarPosition = GetSidebarPos(rPointLogic);
2436
0
    if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::NONE)
2437
0
        return;
2438
2439
    // Calculate the width to be applied in logic units
2440
0
    tools::Long nLogicWidth;
2441
0
    if (eSidebarPosition == sw::sidebarwindows::SidebarPosition::RIGHT)
2442
0
        nLogicWidth = rPointLogic.X() - nSidebarRect.Left();
2443
0
    else
2444
0
        nLogicWidth = nSidebarRect.Right() - rPointLogic.X();
2445
2446
    // The zoom level is conveniently used as reference to define the minimum width
2447
0
    const sal_uInt16 nZoom = mpWrtShell->GetViewOptions()->GetZoom();
2448
0
    double nFactor = static_cast<double>(mpEditWin->LogicToPixel(Point(nLogicWidth, 0)).X())
2449
0
                     / static_cast<double>(nZoom);
2450
    // The width may vary from 1x to 8x the zoom factor
2451
0
    nFactor = std::clamp(nFactor, 1.0, 8.0);
2452
0
    std::shared_ptr<comphelper::ConfigurationChanges> xChanges(
2453
0
        comphelper::ConfigurationChanges::create());
2454
0
    officecfg::Office::Writer::Notes::DisplayWidthFactor::set(nFactor, xChanges);
2455
0
    xChanges->commit();
2456
2457
    // tdf#159146 After resizing the sidebar the layout and the ruler needs to be updated
2458
0
    mpWrtShell->InvalidateLayout(true);
2459
0
    mpView->GetHRuler().Invalidate();
2460
0
    mpView->InvalidateRulerPos();
2461
2462
0
    LayoutPostIts();
2463
0
}
2464
2465
tools::ULong SwPostItMgr::GetSidebarWidth(bool bPx) const
2466
1.00k
{
2467
1.00k
    bool bEnableMapMode = !mpWrtShell->GetOut()->IsMapModeEnabled();
2468
1.00k
    sal_uInt16 nZoom = mpWrtShell->GetViewOptions()->GetZoom();
2469
1.00k
    if (comphelper::LibreOfficeKit::isActive() && !bEnableMapMode)
2470
0
    {
2471
        // The output device is the tile and contains the real wanted scale factor.
2472
0
        double fScaleX = double(mpWrtShell->GetOut()->GetMapMode().GetScaleX());
2473
0
        nZoom = fScaleX * 100;
2474
0
    }
2475
1.00k
    tools::ULong aWidth = static_cast<tools::ULong>(
2476
1.00k
        nZoom * officecfg::Office::Writer::Notes::DisplayWidthFactor::get());
2477
2478
1.00k
    if (bPx)
2479
1.00k
        return aWidth;
2480
0
    else
2481
0
    {
2482
0
        if (bEnableMapMode)
2483
            // The output device is the window.
2484
0
            mpWrtShell->GetOut()->EnableMapMode();
2485
0
        tools::Long nRet = mpWrtShell->GetOut()->PixelToLogic(Size(aWidth, 0)).Width();
2486
0
        if (bEnableMapMode)
2487
0
            mpWrtShell->GetOut()->EnableMapMode(false);
2488
0
        return nRet;
2489
0
    }
2490
1.00k
}
2491
2492
tools::ULong SwPostItMgr::GetSidebarBorderWidth(bool bPx) const
2493
1.00k
{
2494
1.00k
    if (bPx)
2495
1.00k
        return 2;
2496
0
    else
2497
0
        return mpWrtShell->GetOut()->PixelToLogic(Size(2,0)).Width();
2498
1.00k
}
2499
2500
Color SwPostItMgr::GetColorDark(std::size_t aAuthorIndex)
2501
0
{
2502
0
    Color aColor = GetColorAnchor(aAuthorIndex);
2503
0
    svtools::ColorConfig aColorConfig;
2504
0
    const Color aBgColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
2505
0
    if (aBgColor.IsDark())
2506
0
        aColor.DecreaseLuminance(80);
2507
0
    else
2508
0
        aColor.IncreaseLuminance(150);
2509
0
    return aColor;
2510
0
}
2511
2512
Color SwPostItMgr::GetColorLight(std::size_t aAuthorIndex)
2513
0
{
2514
0
    Color aColor = GetColorAnchor(aAuthorIndex);
2515
0
    svtools::ColorConfig aColorConfig;
2516
0
    const Color aBgColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
2517
0
    if (aBgColor.IsDark())
2518
0
        aColor.DecreaseLuminance(130);
2519
0
    else
2520
0
        aColor.IncreaseLuminance(200);
2521
0
    return aColor;
2522
0
}
2523
2524
Color SwPostItMgr::GetColorAnchor(std::size_t aAuthorIndex)
2525
0
{
2526
0
    if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode())
2527
0
    {
2528
0
        svtools::ColorConfig aColorConfig;
2529
0
        switch (aAuthorIndex % 9)
2530
0
        {
2531
0
            case 0: return aColorConfig.GetColorValue(svtools::AUTHOR1).nColor;
2532
0
            case 1: return aColorConfig.GetColorValue(svtools::AUTHOR2).nColor;
2533
0
            case 2: return aColorConfig.GetColorValue(svtools::AUTHOR3).nColor;
2534
0
            case 3: return aColorConfig.GetColorValue(svtools::AUTHOR4).nColor;
2535
0
            case 4: return aColorConfig.GetColorValue(svtools::AUTHOR5).nColor;
2536
0
            case 5: return aColorConfig.GetColorValue(svtools::AUTHOR6).nColor;
2537
0
            case 6: return aColorConfig.GetColorValue(svtools::AUTHOR7).nColor;
2538
0
            case 7: return aColorConfig.GetColorValue(svtools::AUTHOR8).nColor;
2539
0
            case 8: return aColorConfig.GetColorValue(svtools::AUTHOR9).nColor;
2540
0
        }
2541
0
    }
2542
2543
0
    return COL_WHITE;
2544
0
}
2545
2546
void SwPostItMgr::SetActiveSidebarWin( SwAnnotationWin* p)
2547
0
{
2548
0
    if ( p == mpActivePostIt )
2549
0
        return;
2550
2551
    // we need the temp variable so we can set mpActivePostIt before we call DeactivatePostIt
2552
    // therefore we get a new layout in DOCCHANGED when switching from postit to document,
2553
    // otherwise, GetActivePostIt() would still hold our old postit
2554
0
    SwAnnotationWin* pActive = mpActivePostIt;
2555
0
    mpActivePostIt = p;
2556
0
    if (pActive)
2557
0
    {
2558
0
        pActive->DeactivatePostIt();
2559
0
        mShadowState.mpShadowField = nullptr;
2560
0
    }
2561
0
    if (mpActivePostIt)
2562
0
    {
2563
0
        mpActivePostIt->GotoPos();
2564
0
        mpView->AttrChangedNotify(nullptr);
2565
0
        mpActivePostIt->ActivatePostIt();
2566
0
    }
2567
0
}
2568
2569
IMPL_LINK_NOARG( SwPostItMgr, CalcHdl, void*, void )
2570
0
{
2571
0
    mnEventId = nullptr;
2572
0
    if ( mbLayouting )
2573
0
    {
2574
0
        OSL_FAIL("Reentrance problem in Layout Manager!");
2575
0
        mbWaitingForCalcRects = false;
2576
0
        return;
2577
0
    }
2578
2579
    // do not change order, even if it would seem so in the first place, we need the calcrects always
2580
0
    if (CalcRects() || mbLayout)
2581
0
    {
2582
0
        mbLayout = false;
2583
0
        LayoutPostIts();
2584
0
    }
2585
0
}
2586
2587
void SwPostItMgr::Rescale()
2588
154
{
2589
154
    for (auto const& postItField : mvPostItFields)
2590
0
        if ( postItField->mpPostIt )
2591
0
            postItField->mpPostIt->Rescale();
2592
154
}
2593
2594
sal_Int32 SwPostItMgr::GetInitialAnchorDistance() const
2595
0
{
2596
0
    const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2597
0
    return sal_Int32(POSTIT_INITIAL_ANCHOR_DISTANCE * f);
2598
0
}
2599
2600
sal_Int32 SwPostItMgr::GetSpaceBetween() const
2601
0
{
2602
0
    const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2603
0
    return sal_Int32(POSTIT_SPACE_BETWEEN * f);
2604
0
}
2605
2606
sal_Int32 SwPostItMgr::GetScrollSize() const
2607
0
{
2608
0
    const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2609
0
    return sal_Int32((POSTIT_SPACE_BETWEEN + POSTIT_MINIMUMSIZE_WITH_META) * f);
2610
0
}
2611
2612
sal_Int32 SwPostItMgr::GetMinimumSizeWithMeta() const
2613
0
{
2614
0
    const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2615
0
    return sal_Int32(POSTIT_MINIMUMSIZE_WITH_META * f);
2616
0
}
2617
2618
sal_Int32 SwPostItMgr::GetSidebarScrollerHeight() const
2619
0
{
2620
0
    const Fraction& f( mpEditWin->GetMapMode().GetScaleY() );
2621
0
    return sal_Int32(POSTIT_SCROLL_SIDEBAR_HEIGHT * f);
2622
0
}
2623
2624
void SwPostItMgr::SetSpellChecking()
2625
0
{
2626
0
    for (auto const& postItField : mvPostItFields)
2627
0
        if ( postItField->mpPostIt )
2628
0
            postItField->mpPostIt->SetSpellChecking();
2629
0
}
2630
2631
void SwPostItMgr::SetReadOnlyState()
2632
0
{
2633
0
    for (auto const& postItField : mvPostItFields)
2634
0
        if ( postItField->mpPostIt )
2635
0
            postItField->mpPostIt->SetReadonly( mbReadOnly );
2636
0
}
2637
2638
void SwPostItMgr::CheckMetaText()
2639
0
{
2640
0
    for (auto const& postItField : mvPostItFields)
2641
0
        if ( postItField->mpPostIt )
2642
0
            postItField->mpPostIt->CheckMetaText();
2643
0
}
2644
2645
void SwPostItMgr::UpdateColors()
2646
0
{
2647
0
    for (auto const& postItField : mvPostItFields)
2648
0
        if ( postItField->mpPostIt )
2649
0
        {
2650
0
            postItField->mpPostIt->UpdateColors();
2651
0
            postItField->mpPostIt->Invalidate();
2652
0
        }
2653
0
}
2654
2655
sal_uInt16 SwPostItMgr::Replace(SvxSearchItem const * pItem)
2656
0
{
2657
0
    SwAnnotationWin* pWin = GetActiveSidebarWin();
2658
0
    sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( *pItem );
2659
0
    if (!aResult)
2660
0
        SetActiveSidebarWin(nullptr);
2661
0
    return aResult;
2662
0
}
2663
2664
sal_uInt16 SwPostItMgr::FinishSearchReplace(const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward)
2665
0
{
2666
0
    SwAnnotationWin* pWin = GetActiveSidebarWin();
2667
0
    SvxSearchItem aItem(SID_SEARCH_ITEM );
2668
0
    aItem.SetSearchOptions(rSearchOptions);
2669
0
    aItem.SetBackward(!bSrchForward);
2670
0
    sal_uInt16 aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem );
2671
0
    if (!aResult)
2672
0
        SetActiveSidebarWin(nullptr);
2673
0
    else
2674
0
        lcl_CommentNotification(mpView, CommentNotificationType::SearchHighlight, &pWin->GetSidebarItem(), 0);
2675
0
    return aResult;
2676
0
}
2677
2678
sal_uInt16 SwPostItMgr::SearchReplace(const SwFormatField &pField, const i18nutil::SearchOptions2& rSearchOptions, bool bSrchForward)
2679
0
{
2680
0
    sal_uInt16 aResult = 0;
2681
0
    SwAnnotationWin* pWin = GetSidebarWin(&pField);
2682
0
    if (pWin)
2683
0
    {
2684
0
        ESelection aOldSelection = pWin->GetOutlinerView()->GetSelection();
2685
0
        if (bSrchForward)
2686
0
            pWin->GetOutlinerView()->SetSelection(ESelection(0, 0));
2687
0
        else
2688
0
            pWin->GetOutlinerView()->SetSelection(ESelection::AtEnd());
2689
0
        SvxSearchItem aItem(SID_SEARCH_ITEM );
2690
0
        aItem.SetSearchOptions(rSearchOptions);
2691
0
        aItem.SetBackward(!bSrchForward);
2692
0
        aResult = pWin->GetOutlinerView()->StartSearchAndReplace( aItem );
2693
0
        if (!aResult)
2694
0
            pWin->GetOutlinerView()->SetSelection(aOldSelection);
2695
0
        else
2696
0
        {
2697
0
            SetActiveSidebarWin(pWin);
2698
0
            MakeVisible(pWin);
2699
0
            lcl_CommentNotification(mpView, CommentNotificationType::SearchHighlight, &pWin->GetSidebarItem(), 0);
2700
0
        }
2701
0
    }
2702
0
    return aResult;
2703
0
}
2704
2705
void SwPostItMgr::AssureStdModeAtShell()
2706
0
{
2707
0
    mpWrtShell->AssureStdMode();
2708
0
}
2709
2710
bool SwPostItMgr::HasActiveSidebarWin() const
2711
13.7k
{
2712
13.7k
    return mpActivePostIt != nullptr;
2713
13.7k
}
2714
2715
bool SwPostItMgr::HasActiveAnnotationWin() const
2716
0
{
2717
0
    return HasActiveSidebarWin() &&
2718
0
           mpActivePostIt != nullptr;
2719
0
}
2720
2721
void SwPostItMgr::GrabFocusOnActiveSidebarWin()
2722
0
{
2723
0
    if ( HasActiveSidebarWin() )
2724
0
    {
2725
0
        mpActivePostIt->GrabFocus();
2726
0
    }
2727
0
}
2728
2729
void SwPostItMgr::UpdateDataOnActiveSidebarWin()
2730
0
{
2731
0
    if ( HasActiveSidebarWin() )
2732
0
    {
2733
0
        mpActivePostIt->UpdateData();
2734
0
    }
2735
0
}
2736
2737
void SwPostItMgr::DeleteActiveSidebarWin()
2738
0
{
2739
0
    if ( HasActiveSidebarWin() )
2740
0
    {
2741
0
        mpActivePostIt->Delete();
2742
0
    }
2743
0
}
2744
2745
void SwPostItMgr::HideActiveSidebarWin()
2746
0
{
2747
0
    if ( HasActiveSidebarWin() )
2748
0
    {
2749
0
        mpActivePostIt->Hide();
2750
0
    }
2751
0
}
2752
2753
void SwPostItMgr::ToggleInsModeOnActiveSidebarWin()
2754
0
{
2755
0
    if ( HasActiveSidebarWin() )
2756
0
    {
2757
0
        mpActivePostIt->ToggleInsMode();
2758
0
    }
2759
0
}
2760
2761
#if !ENABLE_WASM_STRIP_ACCESSIBILITY
2762
void SwPostItMgr::ConnectSidebarWinToFrame( const SwFrame& rFrame,
2763
                                          const SwFormatField& rFormatField,
2764
                                          SwAnnotationWin& rSidebarWin )
2765
0
{
2766
0
    if ( mpFrameSidebarWinContainer == nullptr )
2767
0
    {
2768
0
        mpFrameSidebarWinContainer.reset(new SwFrameSidebarWinContainer());
2769
0
    }
2770
2771
0
    const bool bInserted = mpFrameSidebarWinContainer->insert( rFrame, rFormatField, rSidebarWin );
2772
0
    if ( bInserted &&
2773
0
         mpWrtShell->GetAccessibleMap() )
2774
0
    {
2775
0
        mpWrtShell->GetAccessibleMap()->InvalidatePosOrSize( nullptr, nullptr, &rSidebarWin, SwRect() );
2776
0
    }
2777
0
}
2778
2779
void SwPostItMgr::DisconnectSidebarWinFromFrame( const SwFrame& rFrame,
2780
                                               SwAnnotationWin& rSidebarWin )
2781
0
{
2782
0
    if ( mpFrameSidebarWinContainer != nullptr )
2783
0
    {
2784
0
        const bool bRemoved = mpFrameSidebarWinContainer->remove( rFrame, rSidebarWin );
2785
0
        if ( bRemoved &&
2786
0
             mpWrtShell->GetAccessibleMap() )
2787
0
        {
2788
0
            mpWrtShell->GetAccessibleMap()->A11yDispose( nullptr, nullptr, &rSidebarWin );
2789
0
        }
2790
0
    }
2791
0
}
2792
#endif // ENABLE_WASM_STRIP_ACCESSIBILITY
2793
2794
bool SwPostItMgr::HasFrameConnectedSidebarWins( const SwFrame& rFrame )
2795
0
{
2796
0
    bool bRet( false );
2797
2798
0
    if ( mpFrameSidebarWinContainer != nullptr )
2799
0
    {
2800
0
        bRet = !mpFrameSidebarWinContainer->empty( rFrame );
2801
0
    }
2802
2803
0
    return bRet;
2804
0
}
2805
2806
vcl::Window* SwPostItMgr::GetSidebarWinForFrameByIndex( const SwFrame& rFrame,
2807
                                                 const sal_Int32 nIndex )
2808
0
{
2809
0
    vcl::Window* pSidebarWin( nullptr );
2810
2811
0
    if ( mpFrameSidebarWinContainer != nullptr )
2812
0
    {
2813
0
        pSidebarWin = mpFrameSidebarWinContainer->get( rFrame, nIndex );
2814
0
    }
2815
2816
0
    return pSidebarWin;
2817
0
}
2818
2819
std::vector<vcl::Window*> SwPostItMgr::GetAllSidebarWinForFrame(const SwFrame& rFrame)
2820
0
{
2821
0
    if ( mpFrameSidebarWinContainer != nullptr )
2822
0
        return mpFrameSidebarWinContainer->getAll(rFrame);
2823
2824
0
    return {};
2825
0
}
2826
2827
0
void SwPostItMgr::ShowHideResolvedNotes(bool visible) {
2828
0
    for (auto const& pPage : mPages)
2829
0
    {
2830
0
        for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
2831
0
        {
2832
0
            if ((*b)->mpPostIt->IsResolved())
2833
0
            {
2834
0
                (*b)->mpPostIt->SetResolved(true);
2835
0
                (*b)->mpPostIt->GetSidebarItem().mbShow = visible;
2836
0
            }
2837
0
        }
2838
0
    }
2839
0
    LayoutPostIts();
2840
0
}
2841
2842
0
void SwPostItMgr::UpdateResolvedStatus(const sw::annotation::SwAnnotationWin* topNote) {
2843
    // Given the topmost note as an argument, scans over all notes and sets the
2844
    // 'resolved' state of each descendant of the top notes to the resolved state
2845
    // of the top note.
2846
0
    bool resolved = topNote->IsResolved();
2847
0
    for (auto const& pPage : mPages)
2848
0
    {
2849
0
        for(auto b = pPage->mvSidebarItems.begin(); b!= pPage->mvSidebarItems.end(); ++b)
2850
0
        {
2851
0
            if((*b)->mpPostIt->GetTopReplyNote() == topNote) {
2852
0
               (*b)->mpPostIt->SetResolved(resolved);
2853
0
            }
2854
0
        }
2855
0
    }
2856
0
}
2857
2858
sw::sidebarwindows::SidebarPosition SwPostItMgr::GetSidebarPos(const Point& rPointLogic)
2859
992
{
2860
992
    if (const SwRootFrame* pLayout = mpWrtShell->GetLayout())
2861
992
    {
2862
992
        const SwPageFrame* pPageFrame = pLayout->GetPageAtPos(rPointLogic, nullptr, true);
2863
992
        if (pPageFrame)
2864
948
            return pPageFrame->SidebarPosition();
2865
992
    }
2866
44
    return sw::sidebarwindows::SidebarPosition::NONE;
2867
992
}
2868
2869
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */