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 <postithelper.hxx>
21 #include <PostItMgr.hxx>
22 #include <AnnotationWin.hxx>
23 
24 #include <fmtfld.hxx>
25 #include <txtfld.hxx>
26 #include <ndtxt.hxx>
27 #include <pagefrm.hxx>
28 #include <rootfrm.hxx>
29 #include <txtfrm.hxx>
30 #include <IDocumentRedlineAccess.hxx>
31 #include <IDocumentFieldsAccess.hxx>
32 #include <IDocumentMarkAccess.hxx>
33 #include <redline.hxx>
34 #include <scriptinfo.hxx>
35 #include <calbck.hxx>
36 #include <IMark.hxx>
37 #include <sortedobjs.hxx>
38 #include <anchoredobject.hxx>
39 #include <fmtanchr.hxx>
40 
41 class Point;
42 
43 namespace
44 {
45 /// Checks if pAnnotationMark covers exactly rAnchorPos (the comment anchor).
AnnotationMarkCoversCommentAnchor(const sw::mark::IMark * pAnnotationMark,const SwPosition & rAnchorPos)46 bool AnnotationMarkCoversCommentAnchor(const sw::mark::IMark* pAnnotationMark,
47                                        const SwPosition& rAnchorPos)
48 {
49     if (!pAnnotationMark)
50     {
51         return false;
52     }
53 
54     const SwPosition& rMarkStart = pAnnotationMark->GetMarkStart();
55     const SwPosition& rMarkEnd = pAnnotationMark->GetMarkEnd();
56 
57     if (rMarkStart != rAnchorPos)
58     {
59         // This can be the as-char case: the comment placeholder character is exactly between the
60         // annotation mark start and end.
61         SwPosition aPosition(rMarkStart);
62         ++aPosition.nContent;
63         if (aPosition != rAnchorPos)
64         {
65             return false;
66         }
67 
68         ++aPosition.nContent;
69         if (aPosition != rMarkEnd)
70         {
71             return false;
72         }
73 
74         return true;
75     }
76 
77     if (rMarkStart.nNode != rMarkEnd.nNode)
78     {
79         return false;
80     }
81 
82     return rMarkEnd.nContent.GetIndex() == rMarkStart.nContent.GetIndex() + 1;
83 }
84 
85 /**
86  * Finds the first draw object of rTextFrame which has the same anchor position as the start of
87  * rAnnotationMark.
88  */
GetAnchoredObjectOfAnnotationMark(const sw::mark::IMark & rAnnotationMark,const SwTextFrame & rTextFrame)89 SwAnchoredObject* GetAnchoredObjectOfAnnotationMark(const sw::mark::IMark& rAnnotationMark,
90                                                     const SwTextFrame& rTextFrame)
91 {
92     const SwSortedObjs* pAnchored = rTextFrame.GetDrawObjs();
93     if (!pAnchored)
94     {
95         return nullptr;
96     }
97 
98     for (SwAnchoredObject* pObject : *pAnchored)
99     {
100         SwFrameFormat& rFrameFormat = pObject->GetFrameFormat();
101         const SwPosition* pFrameAnchor = rFrameFormat.GetAnchor().GetContentAnchor();
102         if (!pFrameAnchor)
103         {
104             continue;
105         }
106 
107         if (rAnnotationMark.GetMarkStart() == *pFrameAnchor)
108         {
109             return pObject;
110         }
111     }
112 
113     return nullptr;
114 }
115 }
116 
SwSidebarItem(const bool aFocus)117 SwSidebarItem::SwSidebarItem(const bool aFocus)
118     : mpPostIt(nullptr)
119     , mbShow(true)
120     , mbFocus(aFocus)
121     , mbPendingLayout(false)
122     , mLayoutStatus(SwPostItHelper::INVISIBLE)
123     , maLayoutInfo()
124 {
125 }
126 
~SwSidebarItem()127 SwSidebarItem::~SwSidebarItem() {}
128 
getLayoutInfos(SwLayoutInfo & o_rInfo,const SwPosition & rAnchorPos,const sw::mark::IMark * pAnnotationMark)129 SwPostItHelper::SwLayoutStatus SwPostItHelper::getLayoutInfos(
130     SwLayoutInfo& o_rInfo,
131     const SwPosition& rAnchorPos,
132     const sw::mark::IMark* pAnnotationMark )
133 {
134     SwLayoutStatus aRet = INVISIBLE;
135     SwTextNode* pTextNode = rAnchorPos.nNode.GetNode().GetTextNode();
136     if ( pTextNode == nullptr )
137         return aRet;
138 
139     SwIterator<SwTextFrame, SwContentNode, sw::IteratorMode::UnwrapMulti> aIter(*pTextNode);
140     for( SwTextFrame* pTextFrame = aIter.First(); pTextFrame != nullptr; pTextFrame = aIter.Next() )
141     {
142         if( !pTextFrame->IsFollow() )
143         {
144             pTextFrame = pTextFrame->GetFrameAtPos( rAnchorPos );
145             SwPageFrame *pPage = pTextFrame ? pTextFrame->FindPageFrame() : nullptr;
146             if ( pPage != nullptr && !pPage->IsInvalid() && !pPage->IsInvalidFly() )
147             {
148                 aRet = VISIBLE;
149 
150                 o_rInfo.mpAnchorFrame = pTextFrame;
151                 {
152                     DisableCallbackAction a(*pTextFrame->getRootFrame());
153                     bool bPositionFromCommentAnchor = true;
154                     if (AnnotationMarkCoversCommentAnchor(pAnnotationMark, rAnchorPos))
155                     {
156                         SwAnchoredObject* pFrame
157                             = GetAnchoredObjectOfAnnotationMark(*pAnnotationMark, *pTextFrame);
158                         if (pFrame)
159                         {
160                             o_rInfo.mPosition = pFrame->GetObjRect();
161                             bPositionFromCommentAnchor = false;
162                         }
163                     }
164                     if (bPositionFromCommentAnchor)
165                     {
166                         pTextFrame->GetCharRect(o_rInfo.mPosition, rAnchorPos, nullptr, false);
167                     }
168                     o_rInfo.mPositionFromCommentAnchor = bPositionFromCommentAnchor;
169                 }
170                 if (pAnnotationMark != nullptr)
171                 {
172                     const SwPosition& rAnnotationStartPos = pAnnotationMark->GetMarkStart();
173                     o_rInfo.mnStartNodeIdx = rAnnotationStartPos.nNode.GetIndex();
174                     o_rInfo.mnStartContent = rAnnotationStartPos.nContent.GetIndex();
175                 }
176                 else
177                 {
178                     o_rInfo.mnStartNodeIdx = 0;
179                     o_rInfo.mnStartContent = -1;
180                 }
181                 o_rInfo.mPageFrame = pPage->getFrameArea();
182                 o_rInfo.mPagePrtArea = pPage->getFramePrintArea();
183                 o_rInfo.mPagePrtArea.Pos() += o_rInfo.mPageFrame.Pos();
184                 o_rInfo.mnPageNumber = pPage->GetPhyPageNum();
185                 o_rInfo.meSidebarPosition = pPage->SidebarPosition();
186                 o_rInfo.mRedlineAuthor = 0;
187 
188                 const IDocumentRedlineAccess& rIDRA = pTextNode->getIDocumentRedlineAccess();
189                 if( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) )
190                 {
191                     const SwRangeRedline* pRedline = rIDRA.GetRedline( rAnchorPos, nullptr );
192                     if( pRedline )
193                     {
194                         if( RedlineType::Insert == pRedline->GetType() )
195                             aRet = INSERTED;
196                         else if( RedlineType::Delete == pRedline->GetType() )
197                         {
198                             bool bDeleted = pAnnotationMark == nullptr;
199                             if( !bDeleted )
200                             {
201                                 IDocumentMarkAccess& rDMA(*pTextNode->GetDoc().getIDocumentMarkAccess());
202                                 IDocumentMarkAccess::const_iterator_t pAnnotationBookmark =
203                                     rDMA.findAnnotationBookmark(pAnnotationMark->GetName());
204                                 // tdf#140980 only really deleted, if there is no helper bookmark
205                                 // in ChangesInMargin mode
206                                 if ( pAnnotationBookmark == rDMA.getBookmarksEnd() )
207                                     bDeleted = true;
208                             }
209                             if ( bDeleted )
210                                 aRet = DELETED;
211                         }
212                         o_rInfo.mRedlineAuthor = pRedline->GetAuthor();
213                     }
214                 }
215             }
216         }
217     }
218 
219     return ( (aRet==VISIBLE) && SwScriptInfo::IsInHiddenRange( *pTextNode , rAnchorPos.nContent.GetIndex()) )
220              ? HIDDEN
221              : aRet;
222 }
223 
getLayoutHeight(const SwRootFrame * pRoot)224 tools::Long SwPostItHelper::getLayoutHeight( const SwRootFrame* pRoot )
225 {
226     tools::Long nRet = pRoot ? pRoot->getFrameArea().Height() : 0;
227     return nRet;
228 }
229 
setSidebarChanged(SwRootFrame * pRoot,bool bBrowseMode)230 void SwPostItHelper::setSidebarChanged( SwRootFrame* pRoot, bool bBrowseMode )
231 {
232     if( pRoot )
233     {
234         pRoot->SetSidebarChanged();
235         if( bBrowseMode )
236             pRoot->InvalidateBrowseWidth();
237     }
238 }
239 
getPageInfo(SwRect & rPageFrame,const SwRootFrame * pRoot,const Point & rPoint)240 tools::ULong SwPostItHelper::getPageInfo( SwRect& rPageFrame, const SwRootFrame* pRoot, const Point& rPoint )
241 {
242     tools::ULong nRet = 0;
243     const SwFrame* pPage = pRoot->GetPageAtPos( rPoint, nullptr, true );
244     if( pPage )
245     {
246         nRet = pPage->GetPhyPageNum();
247         rPageFrame = pPage->getFrameArea();
248     }
249     return nRet;
250 }
251 
GetAnchorPosition() const252 SwPosition SwAnnotationItem::GetAnchorPosition() const
253 {
254     SwTextField* pTextField = mrFormatField.GetTextField();
255     SwTextNode* pTextNode = pTextField->GetpTextNode();
256 
257     SwPosition aPos( *pTextNode );
258     aPos.nContent.Assign( pTextNode, pTextField->GetStart() );
259     return aPos;
260 }
261 
UseElement(SwRootFrame const & rLayout,IDocumentRedlineAccess const & rIDRA)262 bool SwAnnotationItem::UseElement(SwRootFrame const& rLayout,
263         IDocumentRedlineAccess const& rIDRA)
264 {
265     return mrFormatField.IsFieldInDoc()
266         && (!rLayout.IsHideRedlines()
267             || !sw::IsFieldDeletedInModel(rIDRA, *mrFormatField.GetTextField()));
268 }
269 
GetSidebarWindow(SwEditWin & rEditWin,SwPostItMgr & aMgr)270 VclPtr<sw::annotation::SwAnnotationWin> SwAnnotationItem::GetSidebarWindow(
271                                                             SwEditWin& rEditWin,
272                                                             SwPostItMgr& aMgr)
273 {
274     return VclPtr<sw::annotation::SwAnnotationWin>::Create( rEditWin,
275                                                 aMgr,
276                                                 *this,
277                                                 &mrFormatField );
278 }
279 
280 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
281