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 
21 #include <svx/svdmrkv.hxx>
22 #include <svx/svdetc.hxx>
23 #include <svx/svdoedge.hxx>
24 #include <svx/svdpagv.hxx>
25 #include <svx/svdpage.hxx>
26 #include "svddrgm1.hxx"
27 
28 
29 // Point Selection
30 
31 
HasMarkablePoints() const32 bool SdrMarkView::HasMarkablePoints() const
33 {
34     ForceUndirtyMrkPnt();
35     bool bRet=false;
36     if (!ImpIsFrameHandles()) {
37         const size_t nMarkCount=GetMarkedObjectCount();
38         if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
39             for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
40                 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
41                 const SdrObject* pObj=pM->GetMarkedSdrObj();
42                 bRet=pObj->IsPolyObj();
43             }
44         }
45     }
46     return bRet;
47 }
48 
GetMarkablePointCount() const49 sal_Int32 SdrMarkView::GetMarkablePointCount() const
50 {
51     ForceUndirtyMrkPnt();
52     sal_Int32 nCount=0;
53     if (!ImpIsFrameHandles()) {
54         const size_t nMarkCount=GetMarkedObjectCount();
55         if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
56             for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
57                 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
58                 const SdrObject* pObj=pM->GetMarkedSdrObj();
59                 if (pObj->IsPolyObj()) {
60                     nCount+=pObj->GetPointCount();
61                 }
62             }
63         }
64     }
65     return nCount;
66 }
67 
HasMarkedPoints() const68 bool SdrMarkView::HasMarkedPoints() const
69 {
70     ForceUndirtyMrkPnt();
71     bool bRet=false;
72     if (!ImpIsFrameHandles()) {
73         const size_t nMarkCount=GetMarkedObjectCount();
74         if (nMarkCount<=static_cast<size_t>(mnFrameHandlesLimit)) {
75             for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
76                 const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
77                 const SdrUShortCont& rPts = pM->GetMarkedPoints();
78                 bRet = !rPts.empty();
79             }
80         }
81     }
82     return bRet;
83 }
84 
IsPointMarkable(const SdrHdl & rHdl) const85 bool SdrMarkView::IsPointMarkable(const SdrHdl& rHdl) const
86 {
87     return !ImpIsFrameHandles() && !rHdl.IsPlusHdl() && rHdl.GetKind()!=SdrHdlKind::Glue && rHdl.GetKind()!=SdrHdlKind::SmartTag && rHdl.GetObj()!=nullptr && rHdl.GetObj()->IsPolyObj();
88 }
89 
MarkPointHelper(SdrHdl * pHdl,SdrMark * pMark,bool bUnmark)90 bool SdrMarkView::MarkPointHelper(SdrHdl* pHdl, SdrMark* pMark, bool bUnmark)
91 {
92     return ImpMarkPoint( pHdl, pMark, bUnmark );
93 }
94 
ImpMarkPoint(SdrHdl * pHdl,SdrMark * pMark,bool bUnmark)95 bool SdrMarkView::ImpMarkPoint(SdrHdl* pHdl, SdrMark* pMark, bool bUnmark)
96 {
97     if (pHdl==nullptr || pHdl->IsPlusHdl() || pHdl->GetKind()==SdrHdlKind::Glue)
98         return false;
99 
100     if (pHdl->IsSelected() != bUnmark)
101         return false;
102 
103     SdrObject* pObj=pHdl->GetObj();
104     if (pObj==nullptr || !pObj->IsPolyObj())
105         return false;
106 
107     if (pMark==nullptr)
108     {
109         const size_t nMarkNum=TryToFindMarkedObject(pObj);
110         if (nMarkNum==SAL_MAX_SIZE)
111             return false;
112         pMark=GetSdrMarkByIndex(nMarkNum);
113     }
114     const sal_uInt32 nHdlNum(pHdl->GetObjHdlNum());
115     SdrUShortCont& rPts=pMark->GetMarkedPoints();
116     if (!bUnmark)
117     {
118         rPts.insert(static_cast<sal_uInt16>(nHdlNum));
119     }
120     else
121     {
122         SdrUShortCont::const_iterator it = rPts.find( static_cast<sal_uInt16>(nHdlNum) );
123         if (it != rPts.end())
124         {
125             rPts.erase(it);
126         }
127         else
128         {
129             return false; // error case!
130         }
131     }
132 
133     pHdl->SetSelected(!bUnmark);
134     if (!mbPlusHdlAlways)
135     {
136         if (!bUnmark)
137         {
138             SdrHdlList plusList(nullptr);
139             pObj->AddToPlusHdlList(plusList, *pHdl);
140             sal_uInt32 nCount(plusList.GetHdlCount());
141             for (sal_uInt32 i=0; i<nCount; i++)
142             {
143                 SdrHdl* pPlusHdl=plusList.GetHdl(i);
144                 pPlusHdl->SetObj(pObj);
145                 pPlusHdl->SetPageView(pMark->GetPageView());
146                 pPlusHdl->SetPlusHdl(true);
147             }
148             plusList.MoveTo(maHdlList);
149         }
150         else
151         {
152             for (size_t i = maHdlList.GetHdlCount(); i>0;)
153             {
154                 --i;
155                 SdrHdl* pPlusHdl=maHdlList.GetHdl(i);
156                 if (pPlusHdl->IsPlusHdl() && pPlusHdl->GetSourceHdlNum()==nHdlNum)
157                 {
158                     maHdlList.RemoveHdl(i);
159                 }
160             }
161         }
162     }
163 
164     maHdlList.Sort();
165 
166     return true;
167 }
168 
169 
MarkPoint(SdrHdl & rHdl,bool bUnmark)170 bool SdrMarkView::MarkPoint(SdrHdl& rHdl, bool bUnmark)
171 {
172     ForceUndirtyMrkPnt();
173     bool bRet=false;
174     const SdrObject* pObj=rHdl.GetObj();
175     if (IsPointMarkable(rHdl) && rHdl.IsSelected()==bUnmark) {
176         const size_t nMarkNum=TryToFindMarkedObject(pObj);
177         if (nMarkNum!=SAL_MAX_SIZE) {
178             SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
179             if (ImpMarkPoint(&rHdl,pM,bUnmark)) {
180                 MarkListHasChanged();
181                 bRet=true;
182             }
183         }
184     }
185 
186     return bRet;
187 }
188 
MarkPoints(const tools::Rectangle * pRect,bool bUnmark)189 bool SdrMarkView::MarkPoints(const tools::Rectangle* pRect, bool bUnmark)
190 {
191     ForceUndirtyMrkPnt();
192     bool bChgd=false;
193     SortMarkedObjects();
194     const SdrObject* pObj0=nullptr;
195     const SdrPageView* pPV0=nullptr;
196     SdrMark* pM=nullptr;
197     maHdlList.Sort();
198     const size_t nHdlCnt=maHdlList.GetHdlCount();
199     for (size_t nHdlNum=nHdlCnt; nHdlNum>0;) {
200         --nHdlNum;
201         SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
202         if (IsPointMarkable(*pHdl) && pHdl->IsSelected()==bUnmark) {
203             const SdrObject* pObj=pHdl->GetObj();
204             const SdrPageView* pPV=pHdl->GetPageView();
205             if (pObj!=pObj0 || pPV!=pPV0 || pM==nullptr) { // This section is for optimization,
206                 const size_t nMarkNum=TryToFindMarkedObject(pObj);  // so ImpMarkPoint() doesn't always
207                 if (nMarkNum!=SAL_MAX_SIZE) { // have to search the object in the MarkList.
208                     pM=GetSdrMarkByIndex(nMarkNum);
209                     pObj0=pObj;
210                     pPV0=pPV;
211                 } else {
212 #ifdef DBG_UTIL
213                     if (pObj->IsInserted()) {
214                         OSL_FAIL("SdrMarkView::MarkPoints(const Rectangle* pRect): Selected object not found.");
215                     }
216 #endif
217                     pM=nullptr;
218                 }
219             }
220             Point aPos(pHdl->GetPos());
221             if (pM!=nullptr && (pRect==nullptr || pRect->IsInside(aPos))) {
222                 if (ImpMarkPoint(pHdl,pM,bUnmark)) bChgd=true;
223             }
224         }
225     }
226     if (bChgd) {
227         MarkListHasChanged();
228     }
229 
230     return bChgd;
231 }
232 
MarkNextPoint()233 void SdrMarkView::MarkNextPoint()
234 {
235     ForceUndirtyMrkPnt();
236     SortMarkedObjects();
237 }
238 
GetMarkedPointsRect() const239 const tools::Rectangle& SdrMarkView::GetMarkedPointsRect() const
240 {
241     ForceUndirtyMrkPnt();
242     if (mbMarkedPointsRectsDirty) ImpSetPointsRects();
243     return maMarkedPointsRect;
244 }
245 
SetPlusHandlesAlwaysVisible(bool bOn)246 void SdrMarkView::SetPlusHandlesAlwaysVisible(bool bOn)
247 { // TODO: Optimize HandlePaint!
248     ForceUndirtyMrkPnt();
249     if (bOn!=mbPlusHdlAlways) {
250         mbPlusHdlAlways=bOn;
251         SetMarkHandles(nullptr);
252         MarkListHasChanged();
253     }
254 }
255 
256 
257 // ImpSetPointsRects() is for PolyPoints and GluePoints!
258 
259 
ImpSetPointsRects() const260 void SdrMarkView::ImpSetPointsRects() const
261 {
262     tools::Rectangle aPnts;
263     tools::Rectangle aGlue;
264     const size_t nHdlCnt=maHdlList.GetHdlCount();
265     for (size_t nHdlNum=0; nHdlNum<nHdlCnt; ++nHdlNum) {
266         const SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
267         SdrHdlKind eKind=pHdl->GetKind();
268         if ((eKind==SdrHdlKind::Poly && pHdl->IsSelected()) || eKind==SdrHdlKind::Glue) {
269             Point aPt(pHdl->GetPos());
270             tools::Rectangle& rR=eKind==SdrHdlKind::Glue ? aGlue : aPnts;
271             if (rR.IsEmpty()) {
272                 rR=tools::Rectangle(aPt,aPt);
273             } else {
274                 if (aPt.X()<rR.Left  ()) rR.SetLeft(aPt.X() );
275                 if (aPt.X()>rR.Right ()) rR.SetRight(aPt.X() );
276                 if (aPt.Y()<rR.Top   ()) rR.SetTop(aPt.Y() );
277                 if (aPt.Y()>rR.Bottom()) rR.SetBottom(aPt.Y() );
278             }
279         }
280     }
281     const_cast<SdrMarkView*>(this)->maMarkedPointsRect=aPnts;
282     const_cast<SdrMarkView*>(this)->maMarkedGluePointsRect=aGlue;
283     const_cast<SdrMarkView*>(this)->mbMarkedPointsRectsDirty=false;
284 }
285 
286 
287 // UndirtyMrkPnt() is for PolyPoints and GluePoints!
288 
289 
UndirtyMrkPnt() const290 void SdrMarkView::UndirtyMrkPnt() const
291 {
292     bool bChg=false;
293     const size_t nMarkCount=GetMarkedObjectCount();
294     for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
295         SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
296         const SdrObject* pObj=pM->GetMarkedSdrObj();
297         // PolyPoints
298         {
299             SdrUShortCont& rPts = pM->GetMarkedPoints();
300             if (pObj->IsPolyObj()) {
301                 // Remove invalid selected points, that is, all
302                 // entries above the number of points in the object.
303                 sal_uInt32 nMax(pObj->GetPointCount());
304 
305                 SdrUShortCont::const_iterator it = rPts.lower_bound(nMax);
306                 if( it != rPts.end() )
307                 {
308                     rPts.erase(it, rPts.end());
309                     bChg = true;
310                 }
311             }
312             else
313             {
314                 OSL_FAIL("SdrMarkView::UndirtyMrkPnt(): Selected points on an object that is not a PolyObj!");
315                 if (!rPts.empty())
316                 {
317                     rPts.clear();
318                     bChg = true;
319                 }
320             }
321         }
322 
323         // GluePoints
324         {
325             SdrUShortCont& rPts = pM->GetMarkedGluePoints();
326             const SdrGluePointList* pGPL=pObj->GetGluePointList();
327             if (pGPL!=nullptr) {
328                 // Remove invalid selected glue points, that is, all entries
329                 // (IDs) that aren't contained in the GluePointList of the
330                 // object
331                 for(SdrUShortCont::const_iterator it = rPts.begin(); it != rPts.end(); )
332                 {
333                     sal_uInt16 nId=*it;
334                     if (pGPL->FindGluePoint(nId)==SDRGLUEPOINT_NOTFOUND) {
335                         it = rPts.erase(it);
336                         bChg=true;
337                     }
338                     else
339                         ++it;
340                 }
341             } else {
342                 if (!rPts.empty()) {
343                     rPts.clear(); // object doesn't have any glue points (any more)
344                     bChg=true;
345                 }
346             }
347         }
348     }
349     if (bChg) const_cast<SdrMarkView*>(this)->mbMarkedPointsRectsDirty=true;
350     const_cast<SdrMarkView*>(this)->mbMrkPntDirty=false;
351 }
352 
353 
HasMarkableGluePoints() const354 bool SdrMarkView::HasMarkableGluePoints() const
355 {
356     bool bRet=false;
357     if (IsGluePointEditMode()) {
358         ForceUndirtyMrkPnt();
359         const size_t nMarkCount=GetMarkedObjectCount();
360         for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
361             const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
362             const SdrObject* pObj=pM->GetMarkedSdrObj();
363             const SdrGluePointList* pGPL=pObj->GetGluePointList();
364 
365             // #i38892#
366             if(pGPL && pGPL->GetCount())
367             {
368                 for(sal_uInt16 a(0); !bRet && a < pGPL->GetCount(); a++)
369                 {
370                     if((*pGPL)[a].IsUserDefined())
371                     {
372                         bRet = true;
373                     }
374                 }
375             }
376         }
377     }
378     return bRet;
379 }
380 
HasMarkedGluePoints() const381 bool SdrMarkView::HasMarkedGluePoints() const
382 {
383     ForceUndirtyMrkPnt();
384     bool bRet=false;
385     const size_t nMarkCount=GetMarkedObjectCount();
386     for (size_t nMarkNum=0; nMarkNum<nMarkCount && !bRet; ++nMarkNum) {
387         const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
388         const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
389         bRet = !rPts.empty();
390     }
391     return bRet;
392 }
393 
MarkGluePoints(const tools::Rectangle * pRect,bool bUnmark)394 bool SdrMarkView::MarkGluePoints(const tools::Rectangle* pRect, bool bUnmark)
395 {
396     if (!IsGluePointEditMode() && !bUnmark) return false;
397     ForceUndirtyMrkPnt();
398     bool bChgd=false;
399     SortMarkedObjects();
400     const size_t nMarkCount=GetMarkedObjectCount();
401     for (size_t nMarkNum=0; nMarkNum<nMarkCount; ++nMarkNum) {
402         SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
403         const SdrObject* pObj=pM->GetMarkedSdrObj();
404         const SdrGluePointList* pGPL=pObj->GetGluePointList();
405         SdrUShortCont& rPts = pM->GetMarkedGluePoints();
406         if (bUnmark && pRect==nullptr) { // UnmarkAll
407             if (!rPts.empty()) {
408                 rPts.clear();
409                 bChgd=true;
410             }
411         } else {
412             if (pGPL!=nullptr) {
413                 sal_uInt16 nGluePointCnt=pGPL->GetCount();
414                 for (sal_uInt16 nGPNum=0; nGPNum<nGluePointCnt; nGPNum++) {
415                     const SdrGluePoint& rGP=(*pGPL)[nGPNum];
416 
417                     // #i38892#
418                     if(rGP.IsUserDefined())
419                     {
420                         Point aPos(rGP.GetAbsolutePos(*pObj));
421                         if (pRect==nullptr || pRect->IsInside(aPos)) {
422                             bool bContains = rPts.find( rGP.GetId() ) != rPts.end();
423                             if (!bUnmark && !bContains) {
424                                 bChgd=true;
425                                 rPts.insert(rGP.GetId());
426                             }
427                             if (bUnmark && bContains) {
428                                 bChgd=true;
429                                 rPts.erase(rGP.GetId());
430                             }
431                         }
432                     }
433                 }
434             }
435         }
436     }
437     if (bChgd) {
438         AdjustMarkHdl();
439         MarkListHasChanged();
440     }
441     return bChgd;
442 }
443 
PickGluePoint(const Point & rPnt,SdrObject * & rpObj,sal_uInt16 & rnId,SdrPageView * & rpPV) const444 bool SdrMarkView::PickGluePoint(const Point& rPnt, SdrObject*& rpObj, sal_uInt16& rnId, SdrPageView*& rpPV) const
445 {
446     rpObj=nullptr; rpPV=nullptr; rnId=0;
447     if (!IsGluePointEditMode()) return false;
448     OutputDevice* pOut=mpActualOutDev.get();
449     if (pOut==nullptr) pOut=GetFirstOutputDevice();
450     if (pOut==nullptr) return false;
451     SortMarkedObjects();
452     const size_t nMarkCount=GetMarkedObjectCount();
453     size_t nMarkNum=nMarkCount;
454     while (nMarkNum>0) {
455         nMarkNum--;
456         const SdrMark* pM=GetSdrMarkByIndex(nMarkNum);
457         SdrObject* pObj=pM->GetMarkedSdrObj();
458         SdrPageView* pPV=pM->GetPageView();
459         const SdrGluePointList* pGPL=pObj->GetGluePointList();
460         if (pGPL!=nullptr) {
461             sal_uInt16 nNum=pGPL->HitTest(rPnt,*pOut,pObj);
462             if (nNum!=SDRGLUEPOINT_NOTFOUND)
463             {
464                 // #i38892#
465                 const SdrGluePoint& rCandidate = (*pGPL)[nNum];
466 
467                 if(rCandidate.IsUserDefined())
468                 {
469                     rpObj=pObj;
470                     rnId=(*pGPL)[nNum].GetId();
471                     rpPV=pPV;
472                     return true;
473                 }
474             }
475         }
476     }
477     return false;
478 }
479 
MarkGluePoint(const SdrObject * pObj,sal_uInt16 nId,bool bUnmark)480 bool SdrMarkView::MarkGluePoint(const SdrObject* pObj, sal_uInt16 nId, bool bUnmark)
481 {
482     if (!IsGluePointEditMode()) return false;
483     ForceUndirtyMrkPnt();
484     bool bChgd=false;
485     if (pObj!=nullptr) {
486         const size_t nMarkPos=TryToFindMarkedObject(pObj);
487         if (nMarkPos!=SAL_MAX_SIZE) {
488             SdrMark* pM=GetSdrMarkByIndex(nMarkPos);
489             SdrUShortCont& rPts = pM->GetMarkedGluePoints();
490             bool bContains = rPts.find( nId ) != rPts.end();
491             if (!bUnmark && !bContains) {
492                 bChgd=true;
493                 rPts.insert(nId);
494             }
495             if (bUnmark && bContains) {
496                 bChgd=true;
497                 rPts.erase(nId);
498             }
499         } else {
500             // TODO: implement implicit selection of objects
501         }
502     }
503     if (bChgd) {
504         AdjustMarkHdl();
505         MarkListHasChanged();
506     }
507     return bChgd;
508 }
509 
IsGluePointMarked(const SdrObject * pObj,sal_uInt16 nId) const510 bool SdrMarkView::IsGluePointMarked(const SdrObject* pObj, sal_uInt16 nId) const
511 {
512     ForceUndirtyMrkPnt();
513     bool bRet=false;
514     const size_t nPos=TryToFindMarkedObject(pObj); // casting to NonConst
515     if (nPos!=SAL_MAX_SIZE) {
516         const SdrMark* pM=GetSdrMarkByIndex(nPos);
517         const SdrUShortCont& rPts = pM->GetMarkedGluePoints();
518         bRet = rPts.find( nId ) != rPts.end();
519     }
520     return bRet;
521 }
522 
GetGluePointHdl(const SdrObject * pObj,sal_uInt16 nId) const523 SdrHdl* SdrMarkView::GetGluePointHdl(const SdrObject* pObj, sal_uInt16 nId) const
524 {
525     ForceUndirtyMrkPnt();
526     const size_t nHdlCnt=maHdlList.GetHdlCount();
527     for (size_t nHdlNum=0; nHdlNum<nHdlCnt; ++nHdlNum) {
528         SdrHdl* pHdl=maHdlList.GetHdl(nHdlNum);
529         if (pHdl->GetObj()==pObj &&
530             pHdl->GetKind()==SdrHdlKind::Glue &&
531             pHdl->GetObjHdlNum()==nId ) return pHdl;
532     }
533     return nullptr;
534 }
535 
MarkNextGluePoint()536 void SdrMarkView::MarkNextGluePoint()
537 {
538     ForceUndirtyMrkPnt();
539     SortMarkedObjects();
540 }
541 
GetMarkedGluePointsRect() const542 const tools::Rectangle& SdrMarkView::GetMarkedGluePointsRect() const
543 {
544     ForceUndirtyMrkPnt();
545     if (mbMarkedPointsRectsDirty) ImpSetPointsRects();
546     return maMarkedGluePointsRect;
547 }
548 
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
550