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 <hintids.hxx>
21 #include <editeng/protitem.hxx>
22 #include <com/sun/star/i18n/WordType.hpp>
23 #include <com/sun/star/i18n/XBreakIterator.hpp>
24 #include <unotools/charclass.hxx>
25 #include <svl/ctloptions.hxx>
26 #include <swmodule.hxx>
27 #include <fmtcntnt.hxx>
28 #include <swtblfmt.hxx>
29 #include <swcrsr.hxx>
30 #include <unocrsr.hxx>
31 #include <bookmrk.hxx>
32 #include <doc.hxx>
33 #include <IDocumentUndoRedo.hxx>
34 #include <IDocumentRedlineAccess.hxx>
35 #include <IDocumentLayoutAccess.hxx>
36 #include <docary.hxx>
37 #include <ndtxt.hxx>
38 #include <section.hxx>
39 #include <swtable.hxx>
40 #include <cntfrm.hxx>
41 #include <rootfrm.hxx>
42 #include <txtfrm.hxx>
43 #include <notxtfrm.hxx>
44 #include <scriptinfo.hxx>
45 #include <crstate.hxx>
46 #include <docsh.hxx>
47 #include <viewsh.hxx>
48 #include <frmatr.hxx>
49 #include <breakit.hxx>
50 #include <mdiexp.hxx>
51 #include <strings.hrc>
52 #include <redline.hxx>
53 #include <txatbase.hxx>
54 #include <IDocumentMarkAccess.hxx>
55 #include <memory>
56 #include <comphelper/lok.hxx>
57
58 using namespace ::com::sun::star::i18n;
59
60 static const sal_uInt16 coSrchRplcThreshold = 60000;
61
62 struct PercentHdl
63 {
64 SwDocShell* const pDSh;
65 sal_uLong nActPos;
66 bool bBack, bNodeIdx;
67
PercentHdlPercentHdl68 PercentHdl( sal_uLong nStt, sal_uLong nEnd, SwDocShell* pSh )
69 : pDSh(pSh), bBack(false), bNodeIdx(false)
70 {
71 nActPos = nStt;
72 if( ( bBack = (nStt > nEnd )) )
73 {
74 sal_uLong n = nStt; nStt = nEnd; nEnd = n;
75 }
76 ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd );
77 }
78
PercentHdlPercentHdl79 explicit PercentHdl( const SwPaM& rPam )
80 : pDSh( rPam.GetDoc()->GetDocShell() )
81 {
82 sal_uLong nStt, nEnd;
83 if( rPam.GetPoint()->nNode == rPam.GetMark()->nNode )
84 {
85 bNodeIdx = false;
86 nStt = rPam.GetMark()->nContent.GetIndex();
87 nEnd = rPam.GetPoint()->nContent.GetIndex();
88 }
89 else
90 {
91 bNodeIdx = true;
92 nStt = rPam.GetMark()->nNode.GetIndex();
93 nEnd = rPam.GetPoint()->nNode.GetIndex();
94 }
95 nActPos = nStt;
96 if( ( bBack = (nStt > nEnd )) )
97 {
98 sal_uLong n = nStt; nStt = nEnd; nEnd = n;
99 }
100 ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd, pDSh );
101 }
102
~PercentHdlPercentHdl103 ~PercentHdl() { ::EndProgress( pDSh ); }
104
NextPosPercentHdl105 void NextPos( sal_uLong nPos ) const
106 { ::SetProgressState( bBack ? nActPos - nPos : nPos, pDSh ); }
107
NextPosPercentHdl108 void NextPos( SwPosition const & rPos ) const
109 {
110 sal_uLong nPos;
111 if( bNodeIdx )
112 nPos = rPos.nNode.GetIndex();
113 else
114 nPos = rPos.nContent.GetIndex();
115 ::SetProgressState( bBack ? nActPos - nPos : nPos, pDSh );
116 }
117 };
118
SwCursor(const SwPosition & rPos,SwPaM * pRing)119 SwCursor::SwCursor( const SwPosition &rPos, SwPaM* pRing )
120 : SwPaM( rPos, pRing )
121 , m_nRowSpanOffset(0)
122 , m_nCursorBidiLevel(0)
123 , m_bColumnSelection(false)
124 {
125 }
126
127 // @@@ semantic: no copy ctor.
SwCursor(SwCursor const & rCpy,SwPaM * const pRing)128 SwCursor::SwCursor(SwCursor const& rCpy, SwPaM *const pRing)
129 : SwPaM( rCpy, pRing )
130 , m_nRowSpanOffset(rCpy.m_nRowSpanOffset)
131 , m_nCursorBidiLevel(rCpy.m_nCursorBidiLevel)
132 , m_bColumnSelection(rCpy.m_bColumnSelection)
133 {
134 }
135
~SwCursor()136 SwCursor::~SwCursor()
137 {
138 }
139
Create(SwPaM * pRing) const140 SwCursor* SwCursor::Create( SwPaM* pRing ) const
141 {
142 return new SwCursor( *GetPoint(), pRing );
143 }
144
IsReadOnlyAvailable() const145 bool SwCursor::IsReadOnlyAvailable() const
146 {
147 return false;
148 }
149
IsSkipOverHiddenSections() const150 bool SwCursor::IsSkipOverHiddenSections() const
151 {
152 return true;
153 }
154
IsSkipOverProtectSections() const155 bool SwCursor::IsSkipOverProtectSections() const
156 {
157 return !IsReadOnlyAvailable();
158 }
159
160 // CreateNewSavePos is virtual so that derived classes of cursor can implement
161 // own SaveObjects if needed and validate them in the virtual check routines.
SaveState()162 void SwCursor::SaveState()
163 {
164 m_vSavePos.emplace_back( *this );
165 }
166
RestoreState()167 void SwCursor::RestoreState()
168 {
169 if (!m_vSavePos.empty()) // Robust
170 {
171 m_vSavePos.pop_back();
172 }
173 }
174
175 /// determine if point is outside of the node-array's content area
IsNoContent() const176 bool SwCursor::IsNoContent() const
177 {
178 return GetPoint()->nNode.GetIndex() <
179 GetDoc()->GetNodes().GetEndOfExtras().GetIndex();
180 }
181
IsSelOvrCheck(SwCursorSelOverFlags)182 bool SwCursor::IsSelOvrCheck(SwCursorSelOverFlags)
183 {
184 return false;
185 }
186
187 // extracted from IsSelOvr()
IsSelOvrCheck(SwCursorSelOverFlags eFlags)188 bool SwTableCursor::IsSelOvrCheck(SwCursorSelOverFlags eFlags)
189 {
190 SwNodes& rNds = GetDoc()->GetNodes();
191 // check sections of nodes array
192 if( (SwCursorSelOverFlags::CheckNodeSection & eFlags)
193 && HasMark() )
194 {
195 SwNodeIndex aOldPos( rNds, GetSavePos()->nNode );
196 if( !CheckNodesRange( aOldPos, GetPoint()->nNode, true ))
197 {
198 GetPoint()->nNode = aOldPos;
199 GetPoint()->nContent.Assign( GetContentNode(), GetSavePos()->nContent );
200 return true;
201 }
202 }
203 return SwCursor::IsSelOvrCheck(eFlags);
204 }
205
206 namespace
207 {
InputFieldAtPos(SwPosition const * pPos)208 const SwTextAttr* InputFieldAtPos(SwPosition const *pPos)
209 {
210 SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode();
211 if (!pTextNd)
212 return nullptr;
213 return pTextNd->GetTextAttrAt(pPos->nContent.GetIndex(), RES_TXTATR_INPUTFIELD, SwTextNode::PARENT);
214 }
215 }
216
IsSelOvr(SwCursorSelOverFlags eFlags)217 bool SwCursor::IsSelOvr( SwCursorSelOverFlags eFlags )
218 {
219 SwDoc* pDoc = GetDoc();
220 SwNodes& rNds = pDoc->GetNodes();
221
222 bool bSkipOverHiddenSections = IsSkipOverHiddenSections();
223 bool bSkipOverProtectSections = IsSkipOverProtectSections();
224
225 if ( IsSelOvrCheck( eFlags ) )
226 {
227 return true;
228 }
229
230 if (m_vSavePos.back().nNode != GetPoint()->nNode.GetIndex() &&
231 // (1997) in UI-ReadOnly everything is allowed
232 ( !pDoc->GetDocShell() || !pDoc->GetDocShell()->IsReadOnlyUI() ))
233 {
234 // check new sections
235 SwNodeIndex& rPtIdx = GetPoint()->nNode;
236 const SwSectionNode* pSectNd = rPtIdx.GetNode().FindSectionNode();
237 if( pSectNd &&
238 ((bSkipOverHiddenSections && pSectNd->GetSection().IsHiddenFlag() ) ||
239 (bSkipOverProtectSections && pSectNd->GetSection().IsProtectFlag() )))
240 {
241 if( !( SwCursorSelOverFlags::ChangePos & eFlags ) )
242 {
243 // then we're already done
244 RestoreSavePos();
245 return true;
246 }
247
248 // set cursor to new position:
249 SwNodeIndex aIdx( rPtIdx );
250 sal_Int32 nContentPos = m_vSavePos.back().nContent;
251 bool bGoNxt = m_vSavePos.back().nNode < rPtIdx.GetIndex();
252 SwContentNode* pCNd = bGoNxt
253 ? rNds.GoNextSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections)
254 : SwNodes::GoPrevSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections);
255 if( !pCNd && ( SwCursorSelOverFlags::EnableRevDirection & eFlags ))
256 {
257 bGoNxt = !bGoNxt;
258 pCNd = bGoNxt ? rNds.GoNextSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections)
259 : SwNodes::GoPrevSection( &rPtIdx, bSkipOverHiddenSections, bSkipOverProtectSections);
260 }
261
262 bool bIsValidPos = nullptr != pCNd;
263 const bool bValidNodesRange = bIsValidPos &&
264 ::CheckNodesRange( rPtIdx, aIdx, true );
265 if( !bValidNodesRange )
266 {
267 rPtIdx = m_vSavePos.back().nNode;
268 if( nullptr == ( pCNd = rPtIdx.GetNode().GetContentNode() ) )
269 {
270 bIsValidPos = false;
271 nContentPos = 0;
272 rPtIdx = aIdx;
273 if( nullptr == ( pCNd = rPtIdx.GetNode().GetContentNode() ) )
274 {
275 // then to the beginning of the document
276 rPtIdx = rNds.GetEndOfExtras();
277 pCNd = rNds.GoNext( &rPtIdx );
278 }
279 }
280 }
281
282 // register ContentIndex:
283 const sal_Int32 nTmpPos = bIsValidPos ? (bGoNxt ? 0 : pCNd->Len()) : nContentPos;
284 GetPoint()->nContent.Assign( pCNd, nTmpPos );
285 if( !bIsValidPos || !bValidNodesRange ||
286 IsInProtectTable( true ) )
287 return true;
288 }
289
290 // is there a protected section in the section?
291 if( HasMark() && bSkipOverProtectSections)
292 {
293 sal_uLong nSttIdx = GetMark()->nNode.GetIndex(),
294 nEndIdx = GetPoint()->nNode.GetIndex();
295 if( nEndIdx <= nSttIdx )
296 {
297 sal_uLong nTmp = nSttIdx;
298 nSttIdx = nEndIdx;
299 nEndIdx = nTmp;
300 }
301
302 const SwSectionFormats& rFormats = pDoc->GetSections();
303 for( SwSectionFormats::size_type n = 0; n < rFormats.size(); ++n )
304 {
305 const SwSectionFormat* pFormat = rFormats[n];
306 const SvxProtectItem& rProtect = pFormat->GetProtect();
307 if( rProtect.IsContentProtected() )
308 {
309 const SwFormatContent& rContent = pFormat->GetContent(false);
310 OSL_ENSURE( rContent.GetContentIdx(), "No SectionNode?" );
311 sal_uLong nIdx = rContent.GetContentIdx()->GetIndex();
312 if( nSttIdx <= nIdx && nEndIdx >= nIdx )
313 {
314 // if it is no linked section then we cannot select it
315 const SwSection& rSect = *pFormat->GetSection();
316 if( CONTENT_SECTION == rSect.GetType() )
317 {
318 RestoreSavePos();
319 return true;
320 }
321 }
322 }
323 }
324 }
325 }
326
327 const SwNode* pNd = &GetPoint()->nNode.GetNode();
328 if( pNd->IsContentNode() && !dynamic_cast<SwUnoCursor*>(this) )
329 {
330 const SwContentFrame* pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() );
331 if ( (SwCursorSelOverFlags::ChangePos & eFlags) //allowed to change position if it's a bad one
332 && pFrame && pFrame->isFrameAreaDefinitionValid()
333 && !pFrame->getFrameArea().Height() //a bad zero height position
334 && !InputFieldAtPos(GetPoint()) ) //unless it's a (vertical) input field
335 {
336 // skip to the next/prev valid paragraph with a layout
337 SwNodeIndex& rPtIdx = GetPoint()->nNode;
338 bool bGoNxt = m_vSavePos.back().nNode < rPtIdx.GetIndex();
339 while( nullptr != ( pFrame = ( bGoNxt ? pFrame->GetNextContentFrame() : pFrame->GetPrevContentFrame() ))
340 && 0 == pFrame->getFrameArea().Height() )
341 ;
342
343 // #i72394# skip to prev/next valid paragraph with a layout in case
344 // the first search did not succeed:
345 if( !pFrame )
346 {
347 bGoNxt = !bGoNxt;
348 pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() );
349 while ( pFrame && 0 == pFrame->getFrameArea().Height() )
350 {
351 pFrame = bGoNxt ? pFrame->GetNextContentFrame()
352 : pFrame->GetPrevContentFrame();
353 }
354 }
355
356 if (pFrame != nullptr)
357 {
358 if (pFrame->IsTextFrame())
359 {
360 SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pFrame));
361 *GetPoint() = pTextFrame->MapViewToModelPos(TextFrameIndex(
362 bGoNxt ? 0 : pTextFrame->GetText().getLength()));
363 }
364 else
365 {
366 assert(pFrame->IsNoTextFrame());
367 SwContentNode *const pCNd = const_cast<SwContentNode*>(
368 static_cast<SwNoTextFrame const*>(pFrame)->GetNode());
369 assert(pCNd);
370
371 // set this ContentNode as new position
372 rPtIdx = *pCNd;
373 // assign corresponding ContentIndex
374 const sal_Int32 nTmpPos = bGoNxt ? 0 : pCNd->Len();
375 GetPoint()->nContent.Assign( pCNd, nTmpPos );
376 }
377
378
379 if (rPtIdx.GetIndex() == m_vSavePos.back().nNode
380 && GetPoint()->nContent.GetIndex() == m_vSavePos.back().nContent)
381 {
382 // new position equals saved one
383 // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
384 pFrame = nullptr;
385 }
386
387 if ( IsInProtectTable( true ) )
388 {
389 // new position in protected table
390 // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
391 pFrame = nullptr;
392 }
393 }
394 }
395
396 if( !pFrame )
397 {
398 DeleteMark();
399 RestoreSavePos();
400 return true; // we need a frame
401 }
402 }
403
404 // is the cursor allowed to be in a protected node?
405 if( !( SwCursorSelOverFlags::ChangePos & eFlags ) && !IsAtValidPos() )
406 {
407 DeleteMark();
408 RestoreSavePos();
409 return true;
410 }
411
412 if( !HasMark() )
413 return false;
414
415 // check for invalid sections
416 if( !::CheckNodesRange( GetMark()->nNode, GetPoint()->nNode, true ))
417 {
418 DeleteMark();
419 RestoreSavePos();
420 return true; // we need a frame
421 }
422
423 if( (pNd = &GetMark()->nNode.GetNode())->IsContentNode()
424 && !static_cast<const SwContentNode*>(pNd)->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() )
425 && !dynamic_cast<SwUnoCursor*>(this) )
426 {
427 DeleteMark();
428 RestoreSavePos();
429 return true; // we need a frame
430 }
431
432 // ensure that selection is only inside an InputField or contains the InputField completely
433 {
434 const SwTextAttr* pInputFieldTextAttrAtPoint = InputFieldAtPos(GetPoint());
435 const SwTextAttr* pInputFieldTextAttrAtMark = InputFieldAtPos(GetMark());
436
437 if ( pInputFieldTextAttrAtPoint != pInputFieldTextAttrAtMark )
438 {
439 const sal_uLong nRefNodeIdx =
440 ( SwCursorSelOverFlags::Toggle & eFlags )
441 ? m_vSavePos.back().nNode
442 : GetMark()->nNode.GetIndex();
443 const sal_Int32 nRefContentIdx =
444 ( SwCursorSelOverFlags::Toggle & eFlags )
445 ? m_vSavePos.back().nContent
446 : GetMark()->nContent.GetIndex();
447 const bool bIsForwardSelection =
448 nRefNodeIdx < GetPoint()->nNode.GetIndex()
449 || ( nRefNodeIdx == GetPoint()->nNode.GetIndex()
450 && nRefContentIdx < GetPoint()->nContent.GetIndex() );
451
452 if ( pInputFieldTextAttrAtPoint != nullptr )
453 {
454 const sal_Int32 nNewPointPos =
455 bIsForwardSelection ? *(pInputFieldTextAttrAtPoint->End()) : pInputFieldTextAttrAtPoint->GetStart();
456 SwTextNode* pTextNdAtPoint = GetPoint()->nNode.GetNode().GetTextNode();
457 GetPoint()->nContent.Assign( pTextNdAtPoint, nNewPointPos );
458 }
459
460 if ( pInputFieldTextAttrAtMark != nullptr )
461 {
462 const sal_Int32 nNewMarkPos =
463 bIsForwardSelection ? pInputFieldTextAttrAtMark->GetStart() : *(pInputFieldTextAttrAtMark->End());
464 SwTextNode* pTextNdAtMark = GetMark()->nNode.GetNode().GetTextNode();
465 GetMark()->nContent.Assign( pTextNdAtMark, nNewMarkPos );
466 }
467 }
468 }
469
470 const SwTableNode* pPtNd = GetPoint()->nNode.GetNode().FindTableNode();
471 const SwTableNode* pMrkNd = GetMark()->nNode.GetNode().FindTableNode();
472 // both in no or in same table node
473 if( ( !pMrkNd && !pPtNd ) || pPtNd == pMrkNd )
474 return false;
475
476 // in different tables or only mark in table
477 if( pMrkNd )
478 {
479 // not allowed, so go back to old position
480 RestoreSavePos();
481 // Cursor stays at old position
482 return true;
483 }
484
485 // Note: this cannot happen in TableMode
486 // Only Point in Table then go behind/in front of table
487 if (SwCursorSelOverFlags::ChangePos & eFlags)
488 {
489 bool bSelTop = GetPoint()->nNode.GetIndex() <
490 ((SwCursorSelOverFlags::Toggle & eFlags)
491 ? m_vSavePos.back().nNode : GetMark()->nNode.GetIndex());
492
493 do { // loop for table after table
494 sal_uLong nSEIdx = pPtNd->EndOfSectionIndex();
495 sal_uLong nSttEndTable = nSEIdx + 1;
496
497 if( bSelTop )
498 nSttEndTable = rNds[ nSEIdx ]->StartOfSectionIndex() - 1;
499
500 GetPoint()->nNode = nSttEndTable;
501 const SwNode* pMyNd = &(GetNode());
502
503 if( pMyNd->IsSectionNode() || ( pMyNd->IsEndNode() &&
504 pMyNd->StartOfSectionNode()->IsSectionNode() ) )
505 {
506 pMyNd = bSelTop
507 ? SwNodes::GoPrevSection( &GetPoint()->nNode,true,false )
508 : rNds.GoNextSection( &GetPoint()->nNode,true,false );
509
510 /* #i12312# Handle failure of Go{Prev|Next}Section */
511 if ( nullptr == pMyNd)
512 break;
513
514 if( nullptr != ( pPtNd = pMyNd->FindTableNode() ))
515 continue;
516 }
517
518 // we permit these
519 if( pMyNd->IsContentNode() &&
520 ::CheckNodesRange( GetMark()->nNode,
521 GetPoint()->nNode, true ))
522 {
523 // table in table
524 const SwTableNode* pOuterTableNd = pMyNd->FindTableNode();
525 if ( pOuterTableNd )
526 pMyNd = pOuterTableNd;
527 else
528 {
529 SwContentNode* pCNd = const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pMyNd));
530 GetPoint()->nContent.Assign( pCNd, bSelTop ? pCNd->Len() : 0 );
531 return false;
532 }
533 }
534 if( bSelTop
535 ? ( !pMyNd->IsEndNode() || nullptr == ( pPtNd = pMyNd->FindTableNode() ))
536 : nullptr == ( pPtNd = pMyNd->GetTableNode() ))
537 break;
538 } while( true );
539 }
540
541 // stay on old position
542 RestoreSavePos();
543 return true;
544 }
545
IsInProtectTable(bool bMove,bool bChgCursor)546 bool SwCursor::IsInProtectTable( bool bMove, bool bChgCursor )
547 {
548 SwContentNode* pCNd = GetContentNode();
549 if( !pCNd )
550 return false;
551
552 // No table, no protected cell:
553 const SwTableNode* pTableNode = pCNd->FindTableNode();
554 if ( !pTableNode )
555 return false;
556
557 // Current position == last save position?
558 if (m_vSavePos.back().nNode == GetPoint()->nNode.GetIndex())
559 return false;
560
561 // Check for covered cell:
562 bool bInCoveredCell = false;
563 const SwStartNode* pTmpSttNode = pCNd->FindTableBoxStartNode();
564 OSL_ENSURE( pTmpSttNode, "In table, therefore I expect to get a SwTableBoxStartNode" );
565 const SwTableBox* pBox = pTmpSttNode ? pTableNode->GetTable().GetTableBox( pTmpSttNode->GetIndex() ) : nullptr; //Robust #151355
566 if ( pBox && pBox->getRowSpan() < 1 ) // Robust #151270
567 bInCoveredCell = true;
568
569 // Positions of covered cells are not acceptable:
570 if ( !bInCoveredCell )
571 {
572 // Position not protected?
573 if ( !pCNd->IsProtect() )
574 return false;
575
576 // Cursor in protected cells allowed?
577 if ( IsReadOnlyAvailable() )
578 return false;
579 }
580
581 // If we reach this point, we are in a protected or covered table cell!
582
583 if( !bMove )
584 {
585 if( bChgCursor )
586 // restore the last save position
587 RestoreSavePos();
588
589 return true; // Cursor stays at old position
590 }
591
592 // We are in a protected table cell. Traverse top to bottom?
593 if (m_vSavePos.back().nNode < GetPoint()->nNode.GetIndex())
594 {
595 // search next valid box
596 // if there is another StartNode after the EndNode of a cell then
597 // there is another cell
598 SwNodeIndex aCellStt( *GetNode().FindTableBoxStartNode()->EndOfSectionNode(), 1 );
599 bool bProt = true;
600 GoNextCell:
601 for (;;) {
602 if( !aCellStt.GetNode().IsStartNode() )
603 break;
604 ++aCellStt;
605 if( nullptr == ( pCNd = aCellStt.GetNode().GetContentNode() ))
606 pCNd = aCellStt.GetNodes().GoNext( &aCellStt );
607 bProt = pCNd->IsProtect();
608 if( !bProt )
609 break;
610 aCellStt.Assign( *pCNd->FindTableBoxStartNode()->EndOfSectionNode(), 1 );
611 }
612
613 SetNextCursor:
614 if( !bProt ) // found free cell
615 {
616 GetPoint()->nNode = aCellStt;
617 SwContentNode* pTmpCNd = GetContentNode();
618 if( pTmpCNd )
619 {
620 GetPoint()->nContent.Assign( pTmpCNd, 0 );
621 return false;
622 }
623 return IsSelOvr( SwCursorSelOverFlags::Toggle |
624 SwCursorSelOverFlags::ChangePos );
625 }
626 // end of table, so go to next node
627 ++aCellStt;
628 SwNode* pNd;
629 if( ( pNd = &aCellStt.GetNode())->IsEndNode() || HasMark())
630 {
631 // if only table in FlyFrame or SSelection then stay on old position
632 if( bChgCursor )
633 RestoreSavePos();
634 return true;
635 }
636 else if( pNd->IsTableNode() && aCellStt++ )
637 goto GoNextCell;
638
639 bProt = false; // index is now on a content node
640 goto SetNextCursor;
641 }
642
643 // search for the previous valid box
644 {
645 // if there is another EndNode in front of the StartNode than there
646 // exists a previous cell
647 SwNodeIndex aCellStt( *GetNode().FindTableBoxStartNode(), -1 );
648 SwNode* pNd;
649 bool bProt = true;
650 GoPrevCell:
651 for (;;) {
652 if( !( pNd = &aCellStt.GetNode())->IsEndNode() )
653 break;
654 aCellStt.Assign( *pNd->StartOfSectionNode(), +1 );
655 if( nullptr == ( pCNd = aCellStt.GetNode().GetContentNode() ))
656 pCNd = pNd->GetNodes().GoNext( &aCellStt );
657 bProt = pCNd->IsProtect();
658 if( !bProt )
659 break;
660 aCellStt.Assign( *pNd->FindTableBoxStartNode(), -1 );
661 }
662
663 SetPrevCursor:
664 if( !bProt ) // found free cell
665 {
666 GetPoint()->nNode = aCellStt;
667 SwContentNode* pTmpCNd = GetContentNode();
668 if( pTmpCNd )
669 {
670 GetPoint()->nContent.Assign( pTmpCNd, 0 );
671 return false;
672 }
673 return IsSelOvr( SwCursorSelOverFlags::Toggle |
674 SwCursorSelOverFlags::ChangePos );
675 }
676 // at the beginning of a table, so go to next node
677 --aCellStt;
678 if( ( pNd = &aCellStt.GetNode())->IsStartNode() || HasMark() )
679 {
680 // if only table in FlyFrame or SSelection then stay on old position
681 if( bChgCursor )
682 RestoreSavePos();
683 return true;
684 }
685 else if( pNd->StartOfSectionNode()->IsTableNode() && aCellStt-- )
686 goto GoPrevCell;
687
688 bProt = false; // index is now on a content node
689 goto SetPrevCursor;
690 }
691 }
692
693 /// Return <true> if cursor can be set to this position
IsAtValidPos(bool bPoint) const694 bool SwCursor::IsAtValidPos( bool bPoint ) const
695 {
696 const SwDoc* pDoc = GetDoc();
697 const SwPosition* pPos = bPoint ? GetPoint() : GetMark();
698 const SwNode* pNd = &pPos->nNode.GetNode();
699
700 if( pNd->IsContentNode() && !static_cast<const SwContentNode*>(pNd)->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout() ) &&
701 !dynamic_cast<const SwUnoCursor*>(this) )
702 {
703 return false;
704 }
705
706 // #i45129# - in UI-ReadOnly everything is allowed
707 if( !pDoc->GetDocShell() || !pDoc->GetDocShell()->IsReadOnlyUI() )
708 return true;
709
710 const bool bCursorInReadOnly = IsReadOnlyAvailable();
711 if( !bCursorInReadOnly && pNd->IsProtect() )
712 return false;
713
714 const SwSectionNode* pSectNd = pNd->FindSectionNode();
715 return !pSectNd
716 || !(pSectNd->GetSection().IsHiddenFlag() ||
717 ( !bCursorInReadOnly && pSectNd->GetSection().IsProtectFlag() ));
718 }
719
SaveTableBoxContent(const SwPosition *)720 void SwCursor::SaveTableBoxContent( const SwPosition* ) {}
721
722 /// set range for search in document
MakeFindRange(SwDocPositions nStart,SwDocPositions nEnd,SwPaM * pRange) const723 SwMoveFnCollection const & SwCursor::MakeFindRange( SwDocPositions nStart,
724 SwDocPositions nEnd, SwPaM* pRange ) const
725 {
726 pRange->SetMark();
727 FillFindPos( nStart, *pRange->GetMark() );
728 FillFindPos( nEnd, *pRange->GetPoint() );
729
730 // determine direction of search
731 return ( SwDocPositions::Start == nStart || SwDocPositions::OtherStart == nStart ||
732 (SwDocPositions::Curr == nStart &&
733 (SwDocPositions::End == nEnd || SwDocPositions::OtherEnd == nEnd ) ))
734 ? fnMoveForward : fnMoveBackward;
735 }
736
lcl_FindSelection(SwFindParas & rParas,SwCursor * pCurrentCursor,SwMoveFnCollection const & fnMove,SwCursor * & pFndRing,SwPaM & aRegion,FindRanges eFndRngs,bool bInReadOnly,bool & bCancel)737 static sal_uLong lcl_FindSelection( SwFindParas& rParas, SwCursor* pCurrentCursor,
738 SwMoveFnCollection const & fnMove, SwCursor*& pFndRing,
739 SwPaM& aRegion, FindRanges eFndRngs,
740 bool bInReadOnly, bool& bCancel )
741 {
742 SwDoc* pDoc = pCurrentCursor->GetDoc();
743 bool const bDoesUndo = pDoc->GetIDocumentUndoRedo().DoesUndo();
744 int nFndRet = 0;
745 sal_uLong nFound = 0;
746 const bool bSrchBkwrd = &fnMove == &fnMoveBackward;
747 SwPaM *pTmpCursor = pCurrentCursor, *pSaveCursor = pCurrentCursor;
748
749 // only create progress bar for ShellCursor
750 bool bIsUnoCursor = dynamic_cast<SwUnoCursor*>(pCurrentCursor) != nullptr;
751 std::unique_ptr<PercentHdl> pPHdl;
752 sal_uInt16 nCursorCnt = 0;
753 if( FindRanges::InSel & eFndRngs )
754 {
755 while( pCurrentCursor != ( pTmpCursor = pTmpCursor->GetNext() ))
756 ++nCursorCnt;
757 if( nCursorCnt && !bIsUnoCursor )
758 pPHdl.reset(new PercentHdl( 0, nCursorCnt, pDoc->GetDocShell() ));
759 }
760 else
761 pSaveCursor = pSaveCursor->GetPrev();
762
763 bool bEnd = false;
764 do {
765 aRegion.SetMark();
766 // independent from search direction: SPoint is always bigger than mark
767 // if the search area is valid
768 SwPosition *pSttPos = aRegion.GetMark(),
769 *pEndPos = aRegion.GetPoint();
770 *pSttPos = *pTmpCursor->Start();
771 *pEndPos = *pTmpCursor->End();
772 if( bSrchBkwrd )
773 aRegion.Exchange();
774
775 if( !nCursorCnt && !pPHdl && !bIsUnoCursor )
776 pPHdl.reset(new PercentHdl( aRegion ));
777
778 // as long as found and not at same position
779 while( *pSttPos <= *pEndPos &&
780 0 != ( nFndRet = rParas.DoFind(*pCurrentCursor, fnMove,
781 aRegion, bInReadOnly)) &&
782 ( !pFndRing ||
783 *pFndRing->GetPoint() != *pCurrentCursor->GetPoint() ||
784 *pFndRing->GetMark() != *pCurrentCursor->GetMark() ))
785 {
786 if( !( FIND_NO_RING & nFndRet ))
787 {
788 // #i24084# - create ring similar to the one in CreateCursor
789 SwCursor* pNew = pCurrentCursor->Create( pFndRing );
790 if( !pFndRing )
791 pFndRing = pNew;
792
793 pNew->SetMark();
794 *pNew->GetMark() = *pCurrentCursor->GetMark();
795 }
796
797 ++nFound;
798
799 if( !( eFndRngs & FindRanges::InSelAll) )
800 {
801 bEnd = true;
802 break;
803 }
804
805 if ((coSrchRplcThreshold == nFound)
806 && pDoc->GetIDocumentUndoRedo().DoesUndo()
807 && rParas.IsReplaceMode())
808 {
809 short nRet = pCurrentCursor->MaxReplaceArived();
810 if( RET_YES == nRet )
811 {
812 pDoc->GetIDocumentUndoRedo().DelAllUndoObj();
813 pDoc->GetIDocumentUndoRedo().DoUndo(false);
814 }
815 else
816 {
817 bEnd = true;
818 if(RET_CANCEL == nRet)
819 {
820 bCancel = true;
821 }
822 break;
823 }
824 }
825
826 if( bSrchBkwrd )
827 // move pEndPos in front of the found area
828 *pEndPos = *pCurrentCursor->Start();
829 else
830 // move pSttPos behind the found area
831 *pSttPos = *pCurrentCursor->End();
832
833 if( *pSttPos == *pEndPos )
834 // in area but at the end => done
835 break;
836
837 if( !nCursorCnt && pPHdl )
838 {
839 pPHdl->NextPos( *aRegion.GetMark() );
840 }
841 }
842
843 if( bEnd || !( eFndRngs & ( FindRanges::InSelAll | FindRanges::InSel )) )
844 break;
845
846 pTmpCursor = pTmpCursor->GetNext();
847 if( nCursorCnt && pPHdl )
848 {
849 pPHdl->NextPos( ++pPHdl->nActPos );
850 }
851
852 } while( pTmpCursor != pSaveCursor && pTmpCursor->GetNext() != pTmpCursor);
853
854 if( nFound && !pFndRing ) // if no ring should be created
855 pFndRing = pCurrentCursor->Create();
856
857 pDoc->GetIDocumentUndoRedo().DoUndo(bDoesUndo);
858 return nFound;
859 }
860
lcl_MakeSelFwrd(const SwNode & rSttNd,const SwNode & rEndNd,SwPaM & rPam,bool bFirst)861 static bool lcl_MakeSelFwrd( const SwNode& rSttNd, const SwNode& rEndNd,
862 SwPaM& rPam, bool bFirst )
863 {
864 if( rSttNd.GetIndex() + 1 == rEndNd.GetIndex() )
865 return false;
866
867 SwNodes& rNds = rPam.GetDoc()->GetNodes();
868 rPam.DeleteMark();
869 SwContentNode* pCNd;
870 if( !bFirst )
871 {
872 rPam.GetPoint()->nNode = rSttNd;
873 pCNd = rNds.GoNext( &rPam.GetPoint()->nNode );
874 if( !pCNd )
875 return false;
876 pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
877 }
878 else if( rSttNd.GetIndex() > rPam.GetPoint()->nNode.GetIndex() ||
879 rPam.GetPoint()->nNode.GetIndex() >= rEndNd.GetIndex() )
880 // not in this section
881 return false;
882
883 rPam.SetMark();
884 rPam.GetPoint()->nNode = rEndNd;
885 pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode );
886 if( !pCNd )
887 return false;
888 pCNd->MakeEndIndex( &rPam.GetPoint()->nContent );
889
890 return *rPam.GetMark() < *rPam.GetPoint();
891 }
892
lcl_MakeSelBkwrd(const SwNode & rSttNd,const SwNode & rEndNd,SwPaM & rPam,bool bFirst)893 static bool lcl_MakeSelBkwrd( const SwNode& rSttNd, const SwNode& rEndNd,
894 SwPaM& rPam, bool bFirst )
895 {
896 if( rEndNd.GetIndex() + 1 == rSttNd.GetIndex() )
897 return false;
898
899 SwNodes& rNds = rPam.GetDoc()->GetNodes();
900 rPam.DeleteMark();
901 SwContentNode* pCNd;
902 if( !bFirst )
903 {
904 rPam.GetPoint()->nNode = rSttNd;
905 pCNd = SwNodes::GoPrevious( &rPam.GetPoint()->nNode );
906 if( !pCNd )
907 return false;
908 pCNd->MakeEndIndex( &rPam.GetPoint()->nContent );
909 }
910 else if( rEndNd.GetIndex() > rPam.GetPoint()->nNode.GetIndex() ||
911 rPam.GetPoint()->nNode.GetIndex() >= rSttNd.GetIndex() )
912 return false; // not in this section
913
914 rPam.SetMark();
915 rPam.GetPoint()->nNode = rEndNd;
916 pCNd = rNds.GoNext( &rPam.GetPoint()->nNode );
917 if( !pCNd )
918 return false;
919 pCNd->MakeStartIndex( &rPam.GetPoint()->nContent );
920
921 return *rPam.GetPoint() < *rPam.GetMark();
922 }
923
924 // this method "searches" for all use cases because in SwFindParas is always the
925 // correct parameters and respective search method
FindAll(SwFindParas & rParas,SwDocPositions nStart,SwDocPositions nEnde,FindRanges eFndRngs,bool & bCancel)926 sal_uLong SwCursor::FindAll( SwFindParas& rParas,
927 SwDocPositions nStart, SwDocPositions nEnde,
928 FindRanges eFndRngs, bool& bCancel )
929 {
930 bCancel = false;
931 SwCursorSaveState aSaveState( *this );
932
933 // create region without adding it to the ring
934 SwPaM aRegion( *GetPoint() );
935 SwMoveFnCollection const & fnMove = MakeFindRange( nStart, nEnde, &aRegion );
936
937 sal_uLong nFound = 0;
938 const bool bMvBkwrd = &fnMove == &fnMoveBackward;
939 bool bInReadOnly = IsReadOnlyAvailable();
940
941 SwCursor* pFndRing = nullptr;
942 SwNodes& rNds = GetDoc()->GetNodes();
943
944 // search in sections?
945 if( FindRanges::InSel & eFndRngs )
946 {
947 // if string was not found in region then get all sections (cursors
948 // stays unchanged)
949 if( 0 == ( nFound = lcl_FindSelection( rParas, this, fnMove,
950 pFndRing, aRegion, eFndRngs,
951 bInReadOnly, bCancel ) ))
952 return nFound;
953
954 // found string at least once; it's all in new Cursor ring thus delete old one
955 while( GetNext() != this )
956 delete GetNext();
957
958 *GetPoint() = *pFndRing->GetPoint();
959 SetMark();
960 *GetMark() = *pFndRing->GetMark();
961 pFndRing->GetRingContainer().merge( GetRingContainer() );
962 delete pFndRing;
963 }
964 else if( FindRanges::InOther & eFndRngs )
965 {
966 // put cursor as copy of current into ring
967 // chaining points always to first created, so forward
968 SwCursor* pSav = Create( this ); // save the current cursor
969
970 // if already outside of body text search from this position or start at
971 // 1. base section
972 if( bMvBkwrd
973 ? lcl_MakeSelBkwrd( rNds.GetEndOfExtras(),
974 *rNds.GetEndOfPostIts().StartOfSectionNode(),
975 *this, rNds.GetEndOfExtras().GetIndex() >=
976 GetPoint()->nNode.GetIndex() )
977 : lcl_MakeSelFwrd( *rNds.GetEndOfPostIts().StartOfSectionNode(),
978 rNds.GetEndOfExtras(), *this,
979 rNds.GetEndOfExtras().GetIndex() >=
980 GetPoint()->nNode.GetIndex() ))
981 {
982 nFound = lcl_FindSelection( rParas, this, fnMove, pFndRing,
983 aRegion, eFndRngs, bInReadOnly, bCancel );
984 }
985
986 if( !nFound )
987 {
988 // put back the old one
989 *GetPoint() = *pSav->GetPoint();
990 if( pSav->HasMark() )
991 {
992 SetMark();
993 *GetMark() = *pSav->GetMark();
994 }
995 else
996 DeleteMark();
997 return 0;
998 }
999
1000 if( !( FindRanges::InSelAll & eFndRngs ))
1001 {
1002 // there should only be a single one, thus add it
1003 // independent from search direction: SPoint is always bigger than
1004 // mark if the search area is valid
1005 *GetPoint() = *pFndRing->GetPoint();
1006 SetMark();
1007 *GetMark() = *pFndRing->GetMark();
1008 }
1009 else
1010 {
1011 // found string at least once; it's all in new Cursor ring thus delete old one
1012 while( GetNext() != this )
1013 delete GetNext();
1014
1015 *GetPoint() = *pFndRing->GetPoint();
1016 SetMark();
1017 *GetMark() = *pFndRing->GetMark();
1018 pFndRing->GetRingContainer().merge( GetRingContainer() );
1019 }
1020 delete pFndRing;
1021 }
1022 else if( FindRanges::InSelAll & eFndRngs )
1023 {
1024 SwCursor* pSav = Create( this ); // save the current cursor
1025
1026 const SwNode* pSttNd = ( FindRanges::InBodyOnly & eFndRngs )
1027 ? rNds.GetEndOfContent().StartOfSectionNode()
1028 : rNds.GetEndOfPostIts().StartOfSectionNode();
1029
1030 if( bMvBkwrd
1031 ? lcl_MakeSelBkwrd( rNds.GetEndOfContent(), *pSttNd, *this, false )
1032 : lcl_MakeSelFwrd( *pSttNd, rNds.GetEndOfContent(), *this, false ))
1033 {
1034 nFound = lcl_FindSelection( rParas, this, fnMove, pFndRing,
1035 aRegion, eFndRngs, bInReadOnly, bCancel );
1036 }
1037
1038 if( !nFound )
1039 {
1040 // put back the old one
1041 *GetPoint() = *pSav->GetPoint();
1042 if( pSav->HasMark() )
1043 {
1044 SetMark();
1045 *GetMark() = *pSav->GetMark();
1046 }
1047 else
1048 DeleteMark();
1049 return 0;
1050 }
1051 while( GetNext() != this )
1052 delete GetNext();
1053
1054 *GetPoint() = *pFndRing->GetPoint();
1055 SetMark();
1056 *GetMark() = *pFndRing->GetMark();
1057 pFndRing->GetRingContainer().merge( GetRingContainer() );
1058 delete pFndRing;
1059 }
1060 else
1061 {
1062 // if a GetMark is set then keep the GetMark of the found object
1063 // This allows spanning an area with this search.
1064 SwPosition aMarkPos( *GetMark() );
1065 const bool bMarkPos = HasMark() && (eFndRngs == FindRanges::InBody);
1066
1067 nFound = rParas.DoFind(*this, fnMove, aRegion, bInReadOnly) ? 1 : 0;
1068 if (0 != nFound && bMarkPos)
1069 *GetMark() = aMarkPos;
1070 }
1071
1072 if( nFound && SwCursor::IsSelOvr( SwCursorSelOverFlags::Toggle ) )
1073 nFound = 0;
1074 return nFound;
1075 }
1076
FillFindPos(SwDocPositions ePos,SwPosition & rPos) const1077 void SwCursor::FillFindPos( SwDocPositions ePos, SwPosition& rPos ) const
1078 {
1079 bool bIsStart = true;
1080 SwContentNode* pCNd = nullptr;
1081 SwNodes& rNds = GetDoc()->GetNodes();
1082
1083 switch( ePos )
1084 {
1085 case SwDocPositions::Start:
1086 rPos.nNode = *rNds.GetEndOfContent().StartOfSectionNode();
1087 pCNd = rNds.GoNext( &rPos.nNode );
1088 break;
1089 case SwDocPositions::End:
1090 rPos.nNode = rNds.GetEndOfContent();
1091 pCNd = SwNodes::GoPrevious( &rPos.nNode );
1092 bIsStart = false;
1093 break;
1094 case SwDocPositions::OtherStart:
1095 rPos.nNode = *rNds[ sal_uLong(0) ];
1096 pCNd = rNds.GoNext( &rPos.nNode );
1097 break;
1098 case SwDocPositions::OtherEnd:
1099 rPos.nNode = *rNds.GetEndOfContent().StartOfSectionNode();
1100 pCNd = SwNodes::GoPrevious( &rPos.nNode );
1101 bIsStart = false;
1102 break;
1103 default:
1104 rPos = *GetPoint();
1105 }
1106
1107 if( pCNd )
1108 {
1109 rPos.nContent.Assign( pCNd, bIsStart ? 0 : pCNd->Len() );
1110 }
1111 }
1112
MaxReplaceArived()1113 short SwCursor::MaxReplaceArived()
1114 {
1115 return RET_YES;
1116 }
1117
1118 namespace {
1119
1120 struct HideWrapper
1121 {
1122 // either the frame's text or the node's text (possibly pre-filtered)
1123 OUString const* m_pText;
1124 // this is actually a TextFrameIndex but all of the i18n code uses sal_Int32
1125 sal_Int32 m_nPtIndex;
1126 // if mapping is needed, use this frame
1127 SwTextFrame * m_pFrame;
1128 // input in the constructor, output (via mapping) in the destructor
1129 SwTextNode *& m_rpTextNode;
1130 sal_Int32 & m_rPtPos;
1131
HideWrapper__anonf41b6c240211::HideWrapper1132 HideWrapper(SwRootFrame const*const pLayout,
1133 SwTextNode *& rpTextNode, sal_Int32 & rPtPos,
1134 OUString const*const pFilteredNodeText = nullptr)
1135 : m_pText(pFilteredNodeText)
1136 , m_pFrame(nullptr)
1137 , m_rpTextNode(rpTextNode)
1138 , m_rPtPos(rPtPos)
1139 {
1140 if (pLayout && pLayout->IsHideRedlines())
1141 {
1142 m_pFrame = static_cast<SwTextFrame*>(rpTextNode->getLayoutFrame(pLayout));
1143 m_pText = &m_pFrame->GetText();
1144 m_nPtIndex = sal_Int32(m_pFrame->MapModelToView(rpTextNode, rPtPos));
1145 }
1146 else
1147 {
1148 if (!m_pText)
1149 {
1150 m_pText = &rpTextNode->GetText();
1151 }
1152 m_nPtIndex = rPtPos;
1153 }
1154 }
~HideWrapper__anonf41b6c240211::HideWrapper1155 ~HideWrapper()
1156 {
1157 AssignBack(m_rpTextNode, m_rPtPos);
1158 }
AssignBack__anonf41b6c240211::HideWrapper1159 void AssignBack(SwTextNode *& rpTextNode, sal_Int32 & rPtPos)
1160 {
1161 if (0 <= m_nPtIndex && m_pFrame)
1162 {
1163 std::pair<SwTextNode*, sal_Int32> const pos(
1164 m_pFrame->MapViewToModel(TextFrameIndex(m_nPtIndex)));
1165 rpTextNode = pos.first;
1166 rPtPos = pos.second;
1167 }
1168 else
1169 {
1170 rPtPos = m_nPtIndex;
1171 }
1172 }
1173 };
1174
1175 } // namespace
1176
SelectWord(SwViewShell const * pViewShell,const Point * pPt)1177 bool SwCursor::SelectWord( SwViewShell const * pViewShell, const Point* pPt )
1178 {
1179 return SelectWordWT( pViewShell, WordType::ANYWORD_IGNOREWHITESPACES, pPt );
1180 }
1181
IsStartWordWT(sal_Int16 nWordType,SwRootFrame const * const pLayout) const1182 bool SwCursor::IsStartWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
1183 {
1184 bool bRet = false;
1185 SwTextNode* pTextNd = GetNode().GetTextNode();
1186 if (pTextNd)
1187 {
1188 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1189
1190 HideWrapper w(pLayout, pTextNd, nPtPos);
1191
1192 bRet = g_pBreakIt->GetBreakIter()->isBeginWord(
1193 *w.m_pText, w.m_nPtIndex,
1194 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos )),
1195 nWordType );
1196 }
1197 return bRet;
1198 }
1199
IsEndWordWT(sal_Int16 nWordType,SwRootFrame const * const pLayout) const1200 bool SwCursor::IsEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
1201 {
1202 bool bRet = false;
1203 SwTextNode* pTextNd = GetNode().GetTextNode();
1204 if (pTextNd)
1205 {
1206 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1207
1208 HideWrapper w(pLayout, pTextNd, nPtPos);
1209
1210 bRet = g_pBreakIt->GetBreakIter()->isEndWord(
1211 *w.m_pText, w.m_nPtIndex,
1212 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1213 nWordType );
1214
1215 }
1216 return bRet;
1217 }
1218
IsInWordWT(sal_Int16 nWordType,SwRootFrame const * const pLayout) const1219 bool SwCursor::IsInWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
1220 {
1221 bool bRet = false;
1222 SwTextNode* pTextNd = GetNode().GetTextNode();
1223 if (pTextNd)
1224 {
1225 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1226
1227 {
1228 HideWrapper w(pLayout, pTextNd, nPtPos);
1229
1230 Boundary aBoundary = g_pBreakIt->GetBreakIter()->getWordBoundary(
1231 *w.m_pText, w.m_nPtIndex,
1232 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1233 nWordType,
1234 true );
1235
1236 bRet = aBoundary.startPos != aBoundary.endPos &&
1237 aBoundary.startPos <= w.m_nPtIndex &&
1238 w.m_nPtIndex <= aBoundary.endPos;
1239 w.m_nPtIndex = aBoundary.startPos; // hack: convert startPos back...
1240 }
1241 if(bRet)
1242 {
1243 const CharClass& rCC = GetAppCharClass();
1244 bRet = rCC.isLetterNumeric(pTextNd->GetText(), nPtPos);
1245 }
1246 }
1247 return bRet;
1248 }
1249
IsStartEndSentence(bool bEnd,SwRootFrame const * const pLayout) const1250 bool SwCursor::IsStartEndSentence(bool bEnd, SwRootFrame const*const pLayout) const
1251 {
1252 bool bRet = bEnd ?
1253 GetContentNode() && GetPoint()->nContent == GetContentNode()->Len() :
1254 GetPoint()->nContent.GetIndex() == 0;
1255
1256 if ((pLayout != nullptr && pLayout->IsHideRedlines()) || !bRet)
1257 {
1258 SwCursor aCursor(*GetPoint(), nullptr);
1259 SwPosition aOrigPos = *aCursor.GetPoint();
1260 aCursor.GoSentence(bEnd ? SwCursor::END_SENT : SwCursor::START_SENT, pLayout);
1261 bRet = aOrigPos == *aCursor.GetPoint();
1262 }
1263 return bRet;
1264 }
1265
GoStartWordWT(sal_Int16 nWordType,SwRootFrame const * const pLayout)1266 bool SwCursor::GoStartWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1267 {
1268 bool bRet = false;
1269 SwTextNode* pTextNd = GetNode().GetTextNode();
1270 if (pTextNd)
1271 {
1272 SwCursorSaveState aSave( *this );
1273 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1274
1275 {
1276 HideWrapper w(pLayout, pTextNd, nPtPos);
1277
1278 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->getWordBoundary(
1279 *w.m_pText, w.m_nPtIndex,
1280 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1281 nWordType,
1282 false ).startPos;
1283 }
1284
1285 if (nPtPos < pTextNd->GetText().getLength() && nPtPos >= 0)
1286 {
1287 *GetPoint() = SwPosition(*pTextNd, nPtPos);
1288 if( !IsSelOvr() )
1289 bRet = true;
1290 }
1291 }
1292 return bRet;
1293 }
1294
GoEndWordWT(sal_Int16 nWordType,SwRootFrame const * const pLayout)1295 bool SwCursor::GoEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1296 {
1297 bool bRet = false;
1298 SwTextNode* pTextNd = GetNode().GetTextNode();
1299 if (pTextNd)
1300 {
1301 SwCursorSaveState aSave( *this );
1302 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1303
1304 {
1305 HideWrapper w(pLayout, pTextNd, nPtPos);
1306
1307 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->getWordBoundary(
1308 *w.m_pText, w.m_nPtIndex,
1309 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1310 nWordType,
1311 true ).endPos;
1312 }
1313
1314 if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0 &&
1315 GetPoint()->nContent.GetIndex() != nPtPos )
1316 {
1317 *GetPoint() = SwPosition(*pTextNd, nPtPos);
1318 if( !IsSelOvr() )
1319 bRet = true;
1320 }
1321 }
1322 return bRet;
1323 }
1324
GoNextWordWT(sal_Int16 nWordType,SwRootFrame const * const pLayout)1325 bool SwCursor::GoNextWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1326 {
1327 bool bRet = false;
1328 SwTextNode* pTextNd = GetNode().GetTextNode();
1329 if (pTextNd)
1330 {
1331 SwCursorSaveState aSave( *this );
1332 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1333
1334 {
1335 HideWrapper w(pLayout, pTextNd, nPtPos);
1336
1337 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->nextWord(
1338 *w.m_pText, w.m_nPtIndex,
1339 g_pBreakIt->GetLocale( pTextNd->GetLang(nPtPos, 1) ),
1340 nWordType ).startPos;
1341 }
1342
1343 if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0)
1344 {
1345 *GetPoint() = SwPosition(*pTextNd, nPtPos);
1346 if( !IsSelOvr() )
1347 bRet = true;
1348 }
1349 }
1350 return bRet;
1351 }
1352
GoPrevWordWT(sal_Int16 nWordType,SwRootFrame const * const pLayout)1353 bool SwCursor::GoPrevWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1354 {
1355 bool bRet = false;
1356 SwTextNode* pTextNd = GetNode().GetTextNode();
1357 if (pTextNd)
1358 {
1359 SwCursorSaveState aSave( *this );
1360 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1361
1362 {
1363 HideWrapper w(pLayout, pTextNd, nPtPos);
1364
1365 const sal_Int32 nPtStart = w.m_nPtIndex;
1366 if (w.m_nPtIndex)
1367 {
1368 --w.m_nPtIndex;
1369 w.AssignBack(pTextNd, nPtPos);
1370 }
1371
1372 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->previousWord(
1373 *w.m_pText, nPtStart,
1374 g_pBreakIt->GetLocale( pTextNd->GetLang(nPtPos, 1) ),
1375 nWordType ).startPos;
1376 }
1377
1378 if (nPtPos < pTextNd->GetText().getLength() && nPtPos >= 0)
1379 {
1380 *GetPoint() = SwPosition(*pTextNd, nPtPos);
1381 if( !IsSelOvr() )
1382 bRet = true;
1383 }
1384 }
1385 return bRet;
1386 }
1387
SelectWordWT(SwViewShell const * pViewShell,sal_Int16 nWordType,const Point * pPt)1388 bool SwCursor::SelectWordWT( SwViewShell const * pViewShell, sal_Int16 nWordType, const Point* pPt )
1389 {
1390 SwCursorSaveState aSave( *this );
1391
1392 bool bRet = false;
1393 DeleteMark();
1394 const SwRootFrame* pLayout = pViewShell->GetLayout();
1395 if( pPt && nullptr != pLayout )
1396 {
1397 // set the cursor to the layout position
1398 Point aPt( *pPt );
1399 pLayout->GetCursorOfst( GetPoint(), aPt );
1400 }
1401
1402 SwTextNode* pTextNd = GetNode().GetTextNode();
1403 if (pTextNd)
1404 {
1405 // Should we select the whole fieldmark?
1406 const IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess( );
1407 sw::mark::IFieldmark const*const pMark(pMarksAccess->getFieldmarkFor(*GetPoint()));
1408 if (pMark && (IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
1409 || IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::DATE_FIELDMARK))
1410 {
1411 *GetPoint() = sw::mark::FindFieldSep(*pMark);
1412 ++GetPoint()->nContent; // Don't select the separator
1413
1414 const SwPosition& rEnd = pMark->GetMarkEnd();
1415
1416 assert(pMark->GetMarkEnd() != *GetPoint());
1417 SetMark();
1418 GetMark()->nNode = rEnd.nNode;
1419 GetMark()->nContent = rEnd.nContent;
1420 --GetMark()->nContent; // Don't select the end delimiter
1421
1422 bRet = true;
1423 }
1424 else
1425 {
1426 bool bForward = true;
1427 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1428
1429 HideWrapper w(pViewShell->GetLayout(), pTextNd, nPtPos);
1430
1431 Boundary aBndry( g_pBreakIt->GetBreakIter()->getWordBoundary(
1432 *w.m_pText, w.m_nPtIndex,
1433 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1434 nWordType,
1435 bForward ));
1436
1437 if (comphelper::LibreOfficeKit::isActive() && aBndry.startPos == aBndry.endPos && w.m_nPtIndex > 0)
1438 {
1439 // nPtPos is the end of the paragraph, select the last word then.
1440 --w.m_nPtIndex;
1441 w.AssignBack(pTextNd, nPtPos);
1442
1443 aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary(
1444 *w.m_pText, w.m_nPtIndex,
1445 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ),
1446 nWordType,
1447 bForward );
1448
1449 }
1450
1451 SwTextNode * pStartNode(pTextNd);
1452 sal_Int32 nStartIndex;
1453 w.m_nPtIndex = aBndry.startPos;
1454 w.AssignBack(pStartNode, nStartIndex);
1455
1456 SwTextNode * pEndNode(pTextNd);
1457 sal_Int32 nEndIndex;
1458 w.m_nPtIndex = aBndry.endPos;
1459 w.AssignBack(pEndNode, nEndIndex);
1460
1461 if( aBndry.startPos != aBndry.endPos )
1462 {
1463 *GetPoint() = SwPosition(*pEndNode, nEndIndex);
1464 if( !IsSelOvr() )
1465 {
1466 SetMark();
1467 *GetMark() = SwPosition(*pStartNode, nStartIndex);
1468 if (sw::mark::IMark* pAnnotationMark = pMarksAccess->getAnnotationMarkFor(*GetPoint()))
1469 {
1470 // An annotation mark covers the selected word. Check
1471 // if it covers only the word: in that case we select
1472 // the comment anchor as well.
1473 bool bStartMatch = GetMark()->nNode == pAnnotationMark->GetMarkStart().nNode &&
1474 GetMark()->nContent == pAnnotationMark->GetMarkStart().nContent;
1475 bool bEndMatch = GetPoint()->nNode == pAnnotationMark->GetMarkEnd().nNode &&
1476 GetPoint()->nContent.GetIndex() + 1 == pAnnotationMark->GetMarkEnd().nContent.GetIndex();
1477 if (bStartMatch && bEndMatch)
1478 ++GetPoint()->nContent;
1479 }
1480 if( !IsSelOvr() )
1481 bRet = true;
1482 }
1483 }
1484 }
1485 }
1486
1487 if( !bRet )
1488 {
1489 DeleteMark();
1490 RestoreSavePos();
1491 }
1492 return bRet;
1493 }
1494
lcl_MaskDeletedRedlines(const SwTextNode * pTextNd)1495 static OUString lcl_MaskDeletedRedlines( const SwTextNode* pTextNd )
1496 {
1497 OUString aRes;
1498 if (pTextNd)
1499 {
1500 //mask deleted redlines
1501 OUString sNodeText(pTextNd->GetText());
1502 const SwDoc& rDoc = *pTextNd->GetDoc();
1503 const bool bShowChg = IDocumentRedlineAccess::IsShowChanges( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() );
1504 if ( bShowChg )
1505 {
1506 SwRedlineTable::size_type nAct = rDoc.getIDocumentRedlineAccess().GetRedlinePos( *pTextNd, RedlineType::Any );
1507 for ( ; nAct < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); nAct++ )
1508 {
1509 const SwRangeRedline* pRed = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nAct ];
1510 if ( pRed->Start()->nNode > pTextNd->GetIndex() )
1511 break;
1512
1513 if( RedlineType::Delete == pRed->GetType() )
1514 {
1515 sal_Int32 nStart, nEnd;
1516 pRed->CalcStartEnd( pTextNd->GetIndex(), nStart, nEnd );
1517
1518 while ( nStart < nEnd && nStart < sNodeText.getLength() )
1519 sNodeText = sNodeText.replaceAt( nStart++, 1, OUString(CH_TXTATR_INWORD) );
1520 }
1521 }
1522 }
1523 aRes = sNodeText;
1524 }
1525 return aRes;
1526 }
1527
GoSentence(SentenceMoveType eMoveType,SwRootFrame const * const pLayout)1528 bool SwCursor::GoSentence(SentenceMoveType eMoveType, SwRootFrame const*const pLayout)
1529 {
1530 bool bRet = false;
1531 SwTextNode* pTextNd = GetNode().GetTextNode();
1532 if (pTextNd)
1533 {
1534 OUString const sNodeText(lcl_MaskDeletedRedlines(pTextNd));
1535
1536 SwCursorSaveState aSave( *this );
1537 sal_Int32 nPtPos = GetPoint()->nContent.GetIndex();
1538
1539 {
1540 HideWrapper w(pLayout, pTextNd, nPtPos, &sNodeText);
1541
1542 switch ( eMoveType )
1543 {
1544 case START_SENT: /* when modifying: see also ExpandToSentenceBorders below! */
1545 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
1546 *w.m_pText, w.m_nPtIndex,
1547 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
1548 break;
1549 case END_SENT: /* when modifying: see also ExpandToSentenceBorders below! */
1550 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
1551 *w.m_pText, w.m_nPtIndex,
1552 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
1553 break;
1554 case NEXT_SENT:
1555 {
1556 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
1557 *w.m_pText, w.m_nPtIndex,
1558 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
1559 if (w.m_nPtIndex >= 0 && w.m_nPtIndex < w.m_pText->getLength())
1560 {
1561 do
1562 {
1563 ++w.m_nPtIndex;
1564 }
1565 while (w.m_nPtIndex < w.m_pText->getLength()
1566 && (*w.m_pText)[w.m_nPtIndex] == ' ');
1567 }
1568 break;
1569 }
1570 case PREV_SENT:
1571 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
1572 *w.m_pText, w.m_nPtIndex,
1573 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
1574
1575 if (w.m_nPtIndex == 0)
1576 return false; // the previous sentence is not in this paragraph
1577 if (w.m_nPtIndex > 0)
1578 {
1579 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
1580 *w.m_pText, w.m_nPtIndex - 1,
1581 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos)));
1582 }
1583 break;
1584 }
1585 }
1586
1587 // it is allowed to place the PaM just behind the last
1588 // character in the text thus <= ...Len
1589 if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0)
1590 {
1591 *GetPoint() = SwPosition(*pTextNd, nPtPos);
1592 if( !IsSelOvr() )
1593 bRet = true;
1594 }
1595 }
1596 return bRet;
1597 }
1598
ExpandToSentenceBorders(SwRootFrame const * const pLayout)1599 bool SwCursor::ExpandToSentenceBorders(SwRootFrame const*const pLayout)
1600 {
1601 bool bRes = false;
1602 SwTextNode* pStartNd = Start()->nNode.GetNode().GetTextNode();
1603 SwTextNode* pEndNd = End()->nNode.GetNode().GetTextNode();
1604 if (pStartNd && pEndNd)
1605 {
1606 if (!HasMark())
1607 SetMark();
1608
1609 OUString sStartText( lcl_MaskDeletedRedlines( pStartNd ) );
1610 OUString sEndText( pStartNd == pEndNd? sStartText : lcl_MaskDeletedRedlines( pEndNd ) );
1611
1612 SwCursorSaveState aSave( *this );
1613 sal_Int32 nStartPos = Start()->nContent.GetIndex();
1614 sal_Int32 nEndPos = End()->nContent.GetIndex();
1615
1616 {
1617 HideWrapper w(pLayout, pStartNd, nStartPos, &sStartText);
1618
1619 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence(
1620 *w.m_pText, w.m_nPtIndex,
1621 g_pBreakIt->GetLocale( pStartNd->GetLang( nStartPos ) ) );
1622 }
1623 {
1624 HideWrapper w(pLayout, pEndNd, nEndPos, &sEndText);
1625
1626 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence(
1627 *w.m_pText, w.m_nPtIndex,
1628 g_pBreakIt->GetLocale( pEndNd->GetLang( nEndPos ) ) );
1629 }
1630
1631 // it is allowed to place the PaM just behind the last
1632 // character in the text thus <= ...Len
1633 bool bChanged = false;
1634 if (nStartPos <= pStartNd->GetText().getLength() && nStartPos >= 0)
1635 {
1636 *GetMark() = SwPosition(*pStartNd, nStartPos);
1637 bChanged = true;
1638 }
1639 if (nEndPos <= pEndNd->GetText().getLength() && nEndPos >= 0)
1640 {
1641 *GetPoint() = SwPosition(*pEndNd, nEndPos);
1642 bChanged = true;
1643 }
1644 if (bChanged && !IsSelOvr())
1645 bRes = true;
1646 }
1647 return bRes;
1648 }
1649
LeftRight(bool bLeft,sal_uInt16 nCnt,sal_uInt16,bool,bool,bool,SwRootFrame const *)1650 bool SwTableCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 /*nMode*/,
1651 bool /*bVisualAllowed*/, bool /*bSkipHidden*/, bool /*bInsertCursor*/,
1652 SwRootFrame const*)
1653 {
1654 return bLeft ? GoPrevCell( nCnt )
1655 : GoNextCell( nCnt );
1656 }
1657
1658 // calculate cursor bidi level: extracted from LeftRight()
1659 const SwContentFrame*
DoSetBidiLevelLeftRight(bool & io_rbLeft,bool bVisualAllowed,bool bInsertCursor)1660 SwCursor::DoSetBidiLevelLeftRight(
1661 bool & io_rbLeft, bool bVisualAllowed, bool bInsertCursor)
1662 {
1663 // calculate cursor bidi level
1664 const SwContentFrame* pSttFrame = nullptr;
1665 SwNode& rNode = GetPoint()->nNode.GetNode();
1666
1667 if( rNode.IsTextNode() )
1668 {
1669 const SwTextNode& rTNd = *rNode.GetTextNode();
1670 SwIndex& rIdx = GetPoint()->nContent;
1671 sal_Int32 nPos = rIdx.GetIndex();
1672
1673 const SvtCTLOptions& rCTLOptions = SW_MOD()->GetCTLOptions();
1674 if ( bVisualAllowed && rCTLOptions.IsCTLFontEnabled() &&
1675 SvtCTLOptions::MOVEMENT_VISUAL ==
1676 rCTLOptions.GetCTLCursorMovement() )
1677 {
1678 // for visual cursor travelling (used in bidi layout)
1679 // we first have to convert the logic to a visual position
1680 Point aPt;
1681 std::pair<Point, bool> const tmp(aPt, true);
1682 pSttFrame = rTNd.getLayoutFrame(
1683 GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(),
1684 GetPoint(), &tmp);
1685 if( pSttFrame )
1686 {
1687 sal_uInt8 nCursorLevel = GetCursorBidiLevel();
1688 bool bForward = ! io_rbLeft;
1689 SwTextFrame *const pTF(const_cast<SwTextFrame*>(
1690 static_cast<const SwTextFrame*>(pSttFrame)));
1691 TextFrameIndex nTFIndex(pTF->MapModelToViewPos(*GetPoint()));
1692 pTF->PrepareVisualMove( nTFIndex, nCursorLevel,
1693 bForward, bInsertCursor );
1694 *GetPoint() = pTF->MapViewToModelPos(nTFIndex);
1695 SetCursorBidiLevel( nCursorLevel );
1696 io_rbLeft = ! bForward;
1697 }
1698 }
1699 else
1700 {
1701 SwTextFrame const* pFrame;
1702 const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo(rTNd, &pFrame);
1703 if ( pSI )
1704 {
1705 const sal_Int32 nMoveOverPos = io_rbLeft ?
1706 ( nPos ? nPos - 1 : 0 ) :
1707 nPos;
1708 TextFrameIndex nIndex(pFrame->MapModelToView(&rTNd, nMoveOverPos));
1709 SetCursorBidiLevel( pSI->DirType(nIndex) );
1710 }
1711 }
1712 }
1713 return pSttFrame;
1714 }
1715
LeftRight(bool bLeft,sal_uInt16 nCnt,sal_uInt16 nMode,bool bVisualAllowed,bool bSkipHidden,bool bInsertCursor,SwRootFrame const * const pLayout)1716 bool SwCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode,
1717 bool bVisualAllowed,bool bSkipHidden, bool bInsertCursor,
1718 SwRootFrame const*const pLayout)
1719 {
1720 // calculate cursor bidi level
1721 SwNode& rNode = GetPoint()->nNode.GetNode();
1722 const SwContentFrame* pSttFrame = // may side-effect bLeft!
1723 DoSetBidiLevelLeftRight(bLeft, bVisualAllowed, bInsertCursor);
1724
1725 // can the cursor be moved n times?
1726 SwCursorSaveState aSave( *this );
1727 SwMoveFnCollection const & fnMove = bLeft ? fnMoveBackward : fnMoveForward;
1728
1729 SwGoInDoc fnGo;
1730 if ( bSkipHidden )
1731 fnGo = CRSR_SKIP_CELLS == nMode ? GoInContentCellsSkipHidden : GoInContentSkipHidden;
1732 else
1733 fnGo = CRSR_SKIP_CELLS == nMode ? GoInContentCells : GoInContent;
1734
1735 SwTextFrame const* pFrame(nullptr);
1736 if (pLayout)
1737 {
1738 pFrame = static_cast<SwTextFrame*>(rNode.GetContentNode()->getLayoutFrame(pLayout));
1739 if (pFrame)
1740 {
1741 while (pFrame->GetPrecede())
1742 {
1743 pFrame = static_cast<SwTextFrame const*>(pFrame->GetPrecede());
1744 }
1745 }
1746 }
1747
1748 while( nCnt )
1749 {
1750 SwNodeIndex aOldNodeIdx( GetPoint()->nNode );
1751
1752 TextFrameIndex beforeIndex(-1);
1753 if (pFrame)
1754 {
1755 beforeIndex = pFrame->MapModelToViewPos(*GetPoint());
1756 }
1757
1758 if ( !Move( fnMove, fnGo ) )
1759 break;
1760
1761 if (pFrame)
1762 {
1763 SwTextFrame const* pNewFrame(static_cast<SwTextFrame const*>(
1764 GetPoint()->nNode.GetNode().GetContentNode()->getLayoutFrame(pLayout)));
1765 if (pNewFrame)
1766 {
1767 while (pNewFrame->GetPrecede())
1768 {
1769 pNewFrame = static_cast<SwTextFrame const*>(pNewFrame->GetPrecede());
1770 }
1771 }
1772 // sw_redlinehide: fully redline-deleted nodes don't have frames...
1773 if (pFrame == pNewFrame || !pNewFrame)
1774 {
1775 if (!pNewFrame || beforeIndex == pFrame->MapModelToViewPos(*GetPoint()))
1776 {
1777 continue; // moving inside delete redline, doesn't count...
1778 }
1779 }
1780 else
1781 {
1782 // assume iteration is stable & returns the same frame
1783 assert(!pFrame->IsAnFollow(pNewFrame) && !pNewFrame->IsAnFollow(pFrame));
1784 pFrame = pNewFrame;
1785 }
1786 }
1787
1788 // If we were located inside a covered cell but our position has been
1789 // corrected, we check if the last move has moved the cursor to a
1790 // different table cell. In this case we set the cursor to the stored
1791 // covered position and redo the move:
1792 if (m_nRowSpanOffset)
1793 {
1794 const SwNode* pOldTabBoxSttNode = aOldNodeIdx.GetNode().FindTableBoxStartNode();
1795 const SwTableNode* pOldTabSttNode = pOldTabBoxSttNode ? pOldTabBoxSttNode->FindTableNode() : nullptr;
1796 const SwNode* pNewTabBoxSttNode = GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1797 const SwTableNode* pNewTabSttNode = pNewTabBoxSttNode ? pNewTabBoxSttNode->FindTableNode() : nullptr;
1798
1799 const bool bCellChanged = pOldTabSttNode && pNewTabSttNode &&
1800 pOldTabSttNode == pNewTabSttNode &&
1801 pOldTabBoxSttNode && pNewTabBoxSttNode &&
1802 pOldTabBoxSttNode != pNewTabBoxSttNode;
1803
1804 if ( bCellChanged )
1805 {
1806 // Set cursor to start/end of covered cell:
1807 SwTableBox* pTableBox = pOldTabBoxSttNode->GetTableBox();
1808 if ( pTableBox && pTableBox->getRowSpan() > 1 )
1809 {
1810 pTableBox = & pTableBox->FindEndOfRowSpan(
1811 pOldTabSttNode->GetTable(),
1812 static_cast<sal_uInt16>(pTableBox->getRowSpan() + m_nRowSpanOffset));
1813 SwNodeIndex& rPtIdx = GetPoint()->nNode;
1814 SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
1815 rPtIdx = aNewIdx;
1816
1817 GetDoc()->GetNodes().GoNextSection( &rPtIdx, false, false );
1818 SwContentNode* pContentNode = GetContentNode();
1819 if ( pContentNode )
1820 {
1821 GetPoint()->nContent.Assign( pContentNode, bLeft ? pContentNode->Len() : 0 );
1822
1823 // Redo the move:
1824 if ( !Move( fnMove, fnGo ) )
1825 break;
1826 }
1827 }
1828 m_nRowSpanOffset = 0;
1829 }
1830 }
1831
1832 // Check if I'm inside a covered cell. Correct cursor if necessary and
1833 // store covered cell:
1834 const SwNode* pTableBoxStartNode = GetPoint()->nNode.GetNode().FindTableBoxStartNode();
1835 if ( pTableBoxStartNode )
1836 {
1837 const SwTableBox* pTableBox = pTableBoxStartNode->GetTableBox();
1838 if ( pTableBox && pTableBox->getRowSpan() < 1 )
1839 {
1840 // Store the row span offset:
1841 m_nRowSpanOffset = pTableBox->getRowSpan();
1842
1843 // Move cursor to non-covered cell:
1844 const SwTableNode* pTableNd = pTableBoxStartNode->FindTableNode();
1845 pTableBox = & pTableBox->FindStartOfRowSpan( pTableNd->GetTable() );
1846 SwNodeIndex& rPtIdx = GetPoint()->nNode;
1847 SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
1848 rPtIdx = aNewIdx;
1849
1850 GetDoc()->GetNodes().GoNextSection( &rPtIdx, false, false );
1851 SwContentNode* pContentNode = GetContentNode();
1852 if ( pContentNode )
1853 {
1854 GetPoint()->nContent.Assign( pContentNode, bLeft ? pContentNode->Len() : 0 );
1855 }
1856 }
1857 }
1858 --nCnt;
1859 }
1860
1861 // here come some special rules for visual cursor travelling
1862 if ( pSttFrame )
1863 {
1864 SwNode& rTmpNode = GetPoint()->nNode.GetNode();
1865 if ( &rTmpNode != &rNode && rTmpNode.IsTextNode() )
1866 {
1867 Point aPt;
1868 std::pair<Point, bool> const tmp(aPt, true);
1869 const SwContentFrame* pEndFrame = rTmpNode.GetTextNode()->getLayoutFrame(
1870 GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(),
1871 GetPoint(), &tmp);
1872 if ( pEndFrame )
1873 {
1874 if ( ! pEndFrame->IsRightToLeft() != ! pSttFrame->IsRightToLeft() )
1875 {
1876 if ( ! bLeft )
1877 pEndFrame->RightMargin( this );
1878 else
1879 pEndFrame->LeftMargin( this );
1880 }
1881 }
1882 }
1883 }
1884
1885 return 0 == nCnt && !IsInProtectTable( true ) &&
1886 !IsSelOvr( SwCursorSelOverFlags::Toggle |
1887 SwCursorSelOverFlags::ChangePos );
1888 }
1889
1890 // calculate cursor bidi level: extracted from UpDown()
DoSetBidiLevelUpDown()1891 void SwCursor::DoSetBidiLevelUpDown()
1892 {
1893 SwNode& rNode = GetPoint()->nNode.GetNode();
1894 if ( rNode.IsTextNode() )
1895 {
1896 SwTextFrame const* pFrame;
1897 const SwScriptInfo* pSI =
1898 SwScriptInfo::GetScriptInfo( *rNode.GetTextNode(), &pFrame );
1899 if ( pSI )
1900 {
1901 SwIndex& rIdx = GetPoint()->nContent;
1902 const sal_Int32 nPos = rIdx.GetIndex();
1903
1904 if (nPos && nPos < rNode.GetTextNode()->GetText().getLength())
1905 {
1906 TextFrameIndex const nIndex(pFrame->MapModelToView(rNode.GetTextNode(), nPos));
1907 const sal_uInt8 nCurrLevel = pSI->DirType( nIndex );
1908 const sal_uInt8 nPrevLevel = pSI->DirType( nIndex - TextFrameIndex(1) );
1909
1910 if ( nCurrLevel % 2 != nPrevLevel % 2 )
1911 {
1912 // set cursor level to the lower of the two levels
1913 SetCursorBidiLevel( std::min( nCurrLevel, nPrevLevel ) );
1914 }
1915 else
1916 SetCursorBidiLevel( nCurrLevel );
1917 }
1918 }
1919 }
1920 }
1921
UpDown(bool bUp,sal_uInt16 nCnt,Point const * pPt,long nUpDownX,SwRootFrame & rLayout)1922 bool SwCursor::UpDown( bool bUp, sal_uInt16 nCnt,
1923 Point const * pPt, long nUpDownX,
1924 SwRootFrame & rLayout)
1925 {
1926 SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this);
1927 bool bAdjustTableCursor = false;
1928
1929 // If the point/mark of the table cursor in the same box then set cursor to
1930 // beginning of the box
1931 if( pTableCursor && GetNode().StartOfSectionNode() ==
1932 GetNode( false ).StartOfSectionNode() )
1933 {
1934 if ( End() != GetPoint() )
1935 Exchange();
1936 bAdjustTableCursor = true;
1937 }
1938
1939 bool bRet = false;
1940 Point aPt;
1941 if( pPt )
1942 aPt = *pPt;
1943 std::pair<Point, bool> const temp(aPt, true);
1944 SwContentFrame* pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &temp);
1945
1946 if( pFrame )
1947 {
1948 SwCursorSaveState aSave( *this );
1949
1950 if( !pPt )
1951 {
1952 SwRect aTmpRect;
1953 pFrame->GetCharRect( aTmpRect, *GetPoint() );
1954 aPt = aTmpRect.Pos();
1955
1956 nUpDownX = pFrame->IsVertical() ?
1957 aPt.getY() - pFrame->getFrameArea().Top() :
1958 aPt.getX() - pFrame->getFrameArea().Left();
1959 }
1960
1961 // It is allowed to move footnotes in other footnotes but not sections
1962 const bool bChkRange = !pFrame->IsInFootnote() || HasMark();
1963 const SwPosition aOldPos( *GetPoint() );
1964 const bool bInReadOnly = IsReadOnlyAvailable();
1965
1966 if ( bAdjustTableCursor && !bUp )
1967 {
1968 // Special case: We have a table cursor but the start box has more
1969 // than one paragraph. If we want to go down, we have to set the
1970 // point to the last frame in the table box. This is only necessary
1971 // if we do not already have a table selection
1972 const SwStartNode* pTableNd = GetNode().FindTableBoxStartNode();
1973 OSL_ENSURE( pTableNd, "pTableCursor without SwTableNode?" );
1974
1975 if ( pTableNd ) // safety first
1976 {
1977 const SwNode* pEndNd = pTableNd->EndOfSectionNode();
1978 GetPoint()->nNode = *pEndNd;
1979 pTableCursor->Move( fnMoveBackward, GoInNode );
1980 std::pair<Point, bool> const tmp(aPt, true);
1981 pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp);
1982 }
1983 }
1984
1985 while( nCnt &&
1986 (bUp ? pFrame->UnitUp( this, nUpDownX, bInReadOnly )
1987 : pFrame->UnitDown( this, nUpDownX, bInReadOnly ) ) &&
1988 CheckNodesRange( aOldPos.nNode, GetPoint()->nNode, bChkRange ))
1989 {
1990 std::pair<Point, bool> const tmp(aPt, true);
1991 pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp);
1992 --nCnt;
1993 }
1994
1995 // iterate over whole number of items?
1996 if( !nCnt && !IsSelOvr( SwCursorSelOverFlags::Toggle |
1997 SwCursorSelOverFlags::ChangePos ) )
1998 {
1999 if( !pTableCursor )
2000 {
2001 // try to position the cursor at half of the char-rect's height
2002 DisableCallbackAction a(rLayout);
2003 std::pair<Point, bool> const tmp(aPt, true);
2004 pFrame = GetContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp);
2005 SwCursorMoveState eTmpState( MV_UPDOWN );
2006 eTmpState.m_bSetInReadOnly = bInReadOnly;
2007 SwRect aTmpRect;
2008 pFrame->GetCharRect( aTmpRect, *GetPoint(), &eTmpState );
2009 if ( pFrame->IsVertical() )
2010 {
2011 aPt.setX(aTmpRect.Center().getX());
2012 pFrame->Calc(rLayout.GetCurrShell()->GetOut());
2013 aPt.setY(pFrame->getFrameArea().Top() + nUpDownX);
2014 }
2015 else
2016 {
2017 aPt.setY(aTmpRect.Center().getY());
2018 pFrame->Calc(rLayout.GetCurrShell()->GetOut());
2019 aPt.setX(pFrame->getFrameArea().Left() + nUpDownX);
2020 }
2021 pFrame->GetCursorOfst( GetPoint(), aPt, &eTmpState );
2022 }
2023 bRet = !IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos );
2024 }
2025 else
2026 *GetPoint() = aOldPos;
2027
2028 DoSetBidiLevelUpDown(); // calculate cursor bidi level
2029 }
2030 return bRet;
2031 }
2032
LeftRightMargin(SwRootFrame const & rLayout,bool bLeft,bool bAPI)2033 bool SwCursor::LeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI)
2034 {
2035 Point aPt;
2036 std::pair<Point, bool> const tmp(aPt, true);
2037 SwContentFrame const*const pFrame = GetContentNode()->getLayoutFrame(
2038 &rLayout, GetPoint(), &tmp);
2039
2040 // calculate cursor bidi level
2041 if ( pFrame )
2042 SetCursorBidiLevel( pFrame->IsRightToLeft() ? 1 : 0 );
2043
2044 SwCursorSaveState aSave( *this );
2045 return pFrame
2046 && (bLeft ? pFrame->LeftMargin( this ) : pFrame->RightMargin( this, bAPI ) )
2047 && !IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos );
2048 }
2049
IsAtLeftRightMargin(SwRootFrame const & rLayout,bool bLeft,bool bAPI) const2050 bool SwCursor::IsAtLeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI) const
2051 {
2052 bool bRet = false;
2053 Point aPt;
2054 std::pair<Point, bool> const tmp(aPt, true);
2055 SwContentFrame const*const pFrame = GetContentNode()->getLayoutFrame(
2056 &rLayout, GetPoint(), &tmp);
2057 if( pFrame )
2058 {
2059 SwPaM aPam( *GetPoint() );
2060 if( !bLeft && aPam.GetPoint()->nContent.GetIndex() )
2061 --aPam.GetPoint()->nContent;
2062 bRet = (bLeft ? pFrame->LeftMargin( &aPam )
2063 : pFrame->RightMargin( &aPam, bAPI ))
2064 && (!pFrame->IsTextFrame()
2065 || static_cast<SwTextFrame const*>(pFrame)->MapModelToViewPos(*aPam.GetPoint())
2066 == static_cast<SwTextFrame const*>(pFrame)->MapModelToViewPos(*GetPoint()));
2067 }
2068 return bRet;
2069 }
2070
SttEndDoc(bool bStt)2071 bool SwCursor::SttEndDoc( bool bStt )
2072 {
2073 SwCursorSaveState aSave( *this );
2074 // Never jump over section boundaries during selection!
2075 // Can the cursor still moved on?
2076 SwMoveFnCollection const & fnMove = bStt ? fnMoveBackward : fnMoveForward;
2077 bool bRet = (!HasMark() || !IsNoContent() ) &&
2078 Move( fnMove, GoInDoc ) &&
2079 !IsInProtectTable( true ) &&
2080 !IsSelOvr( SwCursorSelOverFlags::Toggle |
2081 SwCursorSelOverFlags::ChangePos |
2082 SwCursorSelOverFlags::EnableRevDirection );
2083 return bRet;
2084 }
2085
GoPrevNextCell(bool bNext,sal_uInt16 nCnt)2086 bool SwCursor::GoPrevNextCell( bool bNext, sal_uInt16 nCnt )
2087 {
2088 const SwTableNode* pTableNd = GetPoint()->nNode.GetNode().FindTableNode();
2089 if( !pTableNd )
2090 return false;
2091
2092 // If there is another EndNode in front of the cell's StartNode then there
2093 // exists a previous cell
2094 SwCursorSaveState aSave( *this );
2095 SwNodeIndex& rPtIdx = GetPoint()->nNode;
2096
2097 while( nCnt-- )
2098 {
2099 const SwNode* pTableBoxStartNode = rPtIdx.GetNode().FindTableBoxStartNode();
2100 const SwTableBox* pTableBox = pTableBoxStartNode->GetTableBox();
2101
2102 // Check if we have to move the cursor to a covered cell before
2103 // proceeding:
2104 if (m_nRowSpanOffset)
2105 {
2106 if ( pTableBox && pTableBox->getRowSpan() > 1 )
2107 {
2108 pTableBox = & pTableBox->FindEndOfRowSpan( pTableNd->GetTable(),
2109 static_cast<sal_uInt16>(pTableBox->getRowSpan() + m_nRowSpanOffset));
2110 SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
2111 rPtIdx = aNewIdx;
2112 pTableBoxStartNode = rPtIdx.GetNode().FindTableBoxStartNode();
2113 }
2114 m_nRowSpanOffset = 0;
2115 }
2116
2117 const SwNode* pTmpNode = bNext ?
2118 pTableBoxStartNode->EndOfSectionNode() :
2119 pTableBoxStartNode;
2120
2121 SwNodeIndex aCellIdx( *pTmpNode, bNext ? 1 : -1 );
2122 if( (bNext && !aCellIdx.GetNode().IsStartNode()) ||
2123 (!bNext && !aCellIdx.GetNode().IsEndNode()) )
2124 return false;
2125
2126 if (bNext)
2127 rPtIdx = aCellIdx;
2128 else
2129 rPtIdx.Assign(*aCellIdx.GetNode().StartOfSectionNode());
2130
2131 pTableBoxStartNode = rPtIdx.GetNode().FindTableBoxStartNode();
2132 pTableBox = pTableBoxStartNode->GetTableBox();
2133 if ( pTableBox && pTableBox->getRowSpan() < 1 )
2134 {
2135 m_nRowSpanOffset = pTableBox->getRowSpan();
2136 // move cursor to non-covered cell:
2137 pTableBox = & pTableBox->FindStartOfRowSpan( pTableNd->GetTable() );
2138 SwNodeIndex aNewIdx( *pTableBox->GetSttNd() );
2139 rPtIdx = aNewIdx;
2140 }
2141 }
2142
2143 ++rPtIdx;
2144 if( !rPtIdx.GetNode().IsContentNode() )
2145 GetDoc()->GetNodes().GoNextSection( &rPtIdx, true, false );
2146 GetPoint()->nContent.Assign( GetContentNode(), 0 );
2147
2148 return !IsInProtectTable( true );
2149 }
2150
GotoTable(const OUString &)2151 bool SwTableCursor::GotoTable( const OUString& )
2152 {
2153 return false; // invalid action
2154 }
2155
GotoTable(const OUString & rName)2156 bool SwCursor::GotoTable( const OUString& rName )
2157 {
2158 bool bRet = false;
2159 if ( !HasMark() )
2160 {
2161 SwTable* pTmpTable = SwTable::FindTable( GetDoc()->FindTableFormatByName( rName ) );
2162 if( pTmpTable )
2163 {
2164 // a table in a normal nodes array
2165 SwCursorSaveState aSave( *this );
2166 GetPoint()->nNode = *pTmpTable->GetTabSortBoxes()[ 0 ]->
2167 GetSttNd()->FindTableNode();
2168 Move( fnMoveForward, GoInContent );
2169 bRet = !IsSelOvr();
2170 }
2171 }
2172 return bRet;
2173 }
2174
GotoTableBox(const OUString & rName)2175 bool SwCursor::GotoTableBox( const OUString& rName )
2176 {
2177 bool bRet = false;
2178 const SwTableNode* pTableNd = GetPoint()->nNode.GetNode().FindTableNode();
2179 if( pTableNd )
2180 {
2181 // retrieve box by name
2182 const SwTableBox* pTableBox = pTableNd->GetTable().GetTableBox( rName );
2183 if( pTableBox && pTableBox->GetSttNd() &&
2184 ( !pTableBox->GetFrameFormat()->GetProtect().IsContentProtected() ||
2185 IsReadOnlyAvailable() ) )
2186 {
2187 SwCursorSaveState aSave( *this );
2188 GetPoint()->nNode = *pTableBox->GetSttNd();
2189 Move( fnMoveForward, GoInContent );
2190 bRet = !IsSelOvr();
2191 }
2192 }
2193 return bRet;
2194 }
2195
MovePara(SwWhichPara fnWhichPara,SwMoveFnCollection const & fnPosPara)2196 bool SwCursor::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & fnPosPara )
2197 {
2198 // for optimization test something before
2199 const SwNode* pNd = &GetPoint()->nNode.GetNode();
2200 bool bShortCut = false;
2201 if ( fnWhichPara == GoCurrPara )
2202 {
2203 // #i41048#
2204 // If fnWhichPara == GoCurrPara then (*fnWhichPara)( *this, fnPosPara )
2205 // can already move the cursor to a different text node. In this case
2206 // we better check if IsSelOvr().
2207 const SwContentNode* pContentNd = pNd->GetContentNode();
2208 if ( pContentNd )
2209 {
2210 const sal_Int32 nSttEnd = &fnPosPara == &fnMoveForward ? 0 : pContentNd->Len();
2211 if ( GetPoint()->nContent.GetIndex() != nSttEnd )
2212 bShortCut = true;
2213 }
2214 }
2215 else
2216 {
2217 if ( pNd->IsTextNode() &&
2218 pNd->GetNodes()[ pNd->GetIndex() +
2219 (fnWhichPara == GoNextPara ? 1 : -1 ) ]->IsTextNode() )
2220 bShortCut = true;
2221 }
2222
2223 if ( bShortCut )
2224 return (*fnWhichPara)( *this, fnPosPara );
2225
2226 // else we must use the SaveStructure, because the next/prev is not
2227 // a same node type.
2228 SwCursorSaveState aSave( *this );
2229 return (*fnWhichPara)( *this, fnPosPara ) &&
2230 !IsInProtectTable( true ) &&
2231 !IsSelOvr( SwCursorSelOverFlags::Toggle |
2232 SwCursorSelOverFlags::ChangePos );
2233 }
2234
MoveSection(SwWhichSection fnWhichSect,SwMoveFnCollection const & fnPosSect)2235 bool SwCursor::MoveSection( SwWhichSection fnWhichSect,
2236 SwMoveFnCollection const & fnPosSect)
2237 {
2238 SwCursorSaveState aSave( *this );
2239 return (*fnWhichSect)( *this, fnPosSect ) &&
2240 !IsInProtectTable( true ) &&
2241 !IsSelOvr( SwCursorSelOverFlags::Toggle |
2242 SwCursorSelOverFlags::ChangePos );
2243 }
2244
RestoreSavePos()2245 void SwCursor::RestoreSavePos()
2246 {
2247 // This method is not supposed to be used in cases when nodes may be
2248 // deleted; detect such cases, but do not crash (example: fdo#40831).
2249 sal_uLong uNodeCount = GetPoint()->nNode.GetNodes().Count();
2250 OSL_ENSURE(m_vSavePos.empty() || m_vSavePos.back().nNode < uNodeCount,
2251 "SwCursor::RestoreSavePos: invalid node: "
2252 "probably something was deleted; consider using SwUnoCursor instead");
2253 if (!m_vSavePos.empty() && m_vSavePos.back().nNode < uNodeCount)
2254 {
2255 GetPoint()->nNode = m_vSavePos.back().nNode;
2256
2257 sal_Int32 nIdx = 0;
2258 if ( GetContentNode() )
2259 {
2260 if (m_vSavePos.back().nContent <= GetContentNode()->Len())
2261 nIdx = m_vSavePos.back().nContent;
2262 else
2263 {
2264 nIdx = GetContentNode()->Len();
2265 OSL_FAIL("SwCursor::RestoreSavePos: invalid content index");
2266 }
2267 }
2268 GetPoint()->nContent.Assign( GetContentNode(), nIdx );
2269 }
2270 }
2271
SwTableCursor(const SwPosition & rPos)2272 SwTableCursor::SwTableCursor( const SwPosition &rPos )
2273 : SwCursor( rPos, nullptr )
2274 {
2275 m_bParked = false;
2276 m_bChanged = false;
2277 m_nTablePtNd = 0;
2278 m_nTableMkNd = 0;
2279 m_nTablePtCnt = 0;
2280 m_nTableMkCnt = 0;
2281 }
2282
~SwTableCursor()2283 SwTableCursor::~SwTableCursor() {}
2284
2285 static bool
lcl_SeekEntry(const SwSelBoxes & rTmp,SwStartNode const * const pSrch,size_t & o_rFndPos)2286 lcl_SeekEntry(const SwSelBoxes& rTmp, SwStartNode const*const pSrch,
2287 size_t & o_rFndPos)
2288 {
2289 sal_uLong nIdx = pSrch->GetIndex();
2290
2291 size_t nO = rTmp.size();
2292 if( nO > 0 )
2293 {
2294 nO--;
2295 size_t nU = 0;
2296 while( nU <= nO )
2297 {
2298 size_t nM = nU + ( nO - nU ) / 2;
2299 if( rTmp[ nM ]->GetSttNd() == pSrch )
2300 {
2301 o_rFndPos = nM;
2302 return true;
2303 }
2304 else if( rTmp[ nM ]->GetSttIdx() < nIdx )
2305 nU = nM + 1;
2306 else if( nM == 0 )
2307 return false;
2308 else
2309 nO = nM - 1;
2310 }
2311 }
2312 return false;
2313 }
2314
MakeBoxSels(SwCursor * pCurrentCursor)2315 SwCursor* SwTableCursor::MakeBoxSels( SwCursor* pCurrentCursor )
2316 {
2317 if (m_bChanged)
2318 {
2319 if (m_bParked)
2320 {
2321 // move back into content
2322 Exchange();
2323 Move( fnMoveForward );
2324 Exchange();
2325 Move( fnMoveForward );
2326 m_bParked = false;
2327 }
2328
2329 m_bChanged = false;
2330
2331 // create temporary copies so that all boxes that
2332 // have already cursors can be removed
2333 SwSelBoxes aTmp(m_SelectedBoxes);
2334
2335 // compare old and new ones
2336 SwNodes& rNds = pCurrentCursor->GetDoc()->GetNodes();
2337 const SwStartNode* pSttNd;
2338 SwPaM* pCur = pCurrentCursor;
2339 do {
2340 size_t nPos;
2341 bool bDel = false;
2342 pSttNd = pCur->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
2343 if( !pCur->HasMark() || !pSttNd ||
2344 pSttNd != pCur->GetMark()->nNode.GetNode().FindTableBoxStartNode() )
2345 bDel = true;
2346
2347 else if( lcl_SeekEntry( aTmp, pSttNd, nPos ))
2348 {
2349 SwNodeIndex aIdx( *pSttNd, 1 );
2350 const SwNode* pNd = &aIdx.GetNode();
2351 if( !pNd->IsContentNode() )
2352 pNd = rNds.GoNextSection( &aIdx, true, false );
2353
2354 SwPosition* pPos = pCur->GetMark();
2355 if( pNd != &pPos->nNode.GetNode() )
2356 pPos->nNode = *pNd;
2357 pPos->nContent.Assign( const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd)), 0 );
2358
2359 aIdx.Assign( *pSttNd->EndOfSectionNode(), - 1 );
2360 if( !( pNd = &aIdx.GetNode())->IsContentNode() )
2361 pNd = SwNodes::GoPrevSection( &aIdx, true, false );
2362
2363 pPos = pCur->GetPoint();
2364 if (pNd && pNd != &pPos->nNode.GetNode())
2365 pPos->nNode = *pNd;
2366 pPos->nContent.Assign(const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pNd)), pNd ? static_cast<const SwContentNode*>(pNd)->Len() : 0);
2367
2368 aTmp.erase( aTmp.begin() + nPos );
2369 }
2370 else
2371 bDel = true;
2372
2373 pCur = pCur->GetNext();
2374 if( bDel )
2375 {
2376 SwPaM* pDel = pCur->GetPrev();
2377
2378 if( pDel == pCurrentCursor )
2379 pCurrentCursor->DeleteMark();
2380 else
2381 delete pDel;
2382 }
2383 } while ( pCurrentCursor != pCur );
2384
2385 for (size_t nPos = 0; nPos < aTmp.size(); ++nPos)
2386 {
2387 pSttNd = aTmp[ nPos ]->GetSttNd();
2388
2389 SwNodeIndex aIdx( *pSttNd, 1 );
2390 if( &aIdx.GetNodes() != &rNds )
2391 break;
2392 SwNode* pNd = &aIdx.GetNode();
2393 if( !pNd->IsContentNode() )
2394 pNd = rNds.GoNextSection( &aIdx, true, false );
2395
2396 SwPaM *const pNew = (!pCurrentCursor->IsMultiSelection() && !pCurrentCursor->HasMark())
2397 ? pCurrentCursor
2398 : pCurrentCursor->Create( pCurrentCursor );
2399 pNew->GetPoint()->nNode = *pNd;
2400 pNew->GetPoint()->nContent.Assign( static_cast<SwContentNode*>(pNd), 0 );
2401 pNew->SetMark();
2402
2403 SwPosition* pPos = pNew->GetPoint();
2404 pPos->nNode.Assign( *pSttNd->EndOfSectionNode(), - 1 );
2405 if( !( pNd = &pPos->nNode.GetNode())->IsContentNode() )
2406 pNd = SwNodes::GoPrevSection( &pPos->nNode, true, false );
2407
2408 pPos->nContent.Assign(static_cast<SwContentNode*>(pNd), pNd ? static_cast<SwContentNode*>(pNd)->Len() : 0);
2409 }
2410 }
2411 return pCurrentCursor;
2412 }
2413
InsertBox(const SwTableBox & rTableBox)2414 void SwTableCursor::InsertBox( const SwTableBox& rTableBox )
2415 {
2416 SwTableBox* pBox = const_cast<SwTableBox*>(&rTableBox);
2417 m_SelectedBoxes.insert(pBox);
2418 m_bChanged = true;
2419 }
2420
DeleteBox(size_t const nPos)2421 void SwTableCursor::DeleteBox(size_t const nPos)
2422 {
2423 m_SelectedBoxes.erase(m_SelectedBoxes.begin() + nPos);
2424 m_bChanged = true;
2425 }
2426
NewTableSelection()2427 bool SwTableCursor::NewTableSelection()
2428 {
2429 bool bRet = false;
2430 const SwNode *pStart = GetNode().FindTableBoxStartNode();
2431 const SwNode *pEnd = GetNode(false).FindTableBoxStartNode();
2432 if( pStart && pEnd )
2433 {
2434 const SwTableNode *pTableNode = pStart->FindTableNode();
2435 if( pTableNode == pEnd->FindTableNode() &&
2436 pTableNode->GetTable().IsNewModel() )
2437 {
2438 bRet = true;
2439 SwSelBoxes aNew(m_SelectedBoxes);
2440 pTableNode->GetTable().CreateSelection( pStart, pEnd, aNew,
2441 SwTable::SEARCH_NONE, false );
2442 ActualizeSelection( aNew );
2443 }
2444 }
2445 return bRet;
2446 }
2447
ActualizeSelection(const SwSelBoxes & rNew)2448 void SwTableCursor::ActualizeSelection( const SwSelBoxes &rNew )
2449 {
2450 size_t nOld = 0, nNew = 0;
2451 while (nOld < m_SelectedBoxes.size() && nNew < rNew.size())
2452 {
2453 SwTableBox const*const pPOld = m_SelectedBoxes[ nOld ];
2454 const SwTableBox* pPNew = rNew[ nNew ];
2455 if( pPOld == pPNew )
2456 { // this box will stay
2457 ++nOld;
2458 ++nNew;
2459 }
2460 else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() )
2461 {
2462 DeleteBox( nOld ); // this box has to go
2463 }
2464 else
2465 {
2466 InsertBox( *pPNew ); // this is a new one
2467 ++nOld;
2468 ++nNew;
2469 }
2470 }
2471
2472 while (nOld < m_SelectedBoxes.size())
2473 {
2474 DeleteBox( nOld ); // some more to delete
2475 }
2476
2477 for ( ; nNew < rNew.size(); ++nNew ) // some more to insert
2478 {
2479 InsertBox( *rNew[ nNew ] );
2480 }
2481 }
2482
IsCursorMovedUpdate()2483 bool SwTableCursor::IsCursorMovedUpdate()
2484 {
2485 if( !IsCursorMoved() )
2486 return false;
2487
2488 m_nTableMkNd = GetMark()->nNode.GetIndex();
2489 m_nTablePtNd = GetPoint()->nNode.GetIndex();
2490 m_nTableMkCnt = GetMark()->nContent.GetIndex();
2491 m_nTablePtCnt = GetPoint()->nContent.GetIndex();
2492 return true;
2493 }
2494
2495 /// park table cursor on the boxes' start node
ParkCursor()2496 void SwTableCursor::ParkCursor()
2497 {
2498 // de-register index from text node
2499 SwNode* pNd = &GetPoint()->nNode.GetNode();
2500 if( !pNd->IsStartNode() )
2501 pNd = pNd->StartOfSectionNode();
2502 GetPoint()->nNode = *pNd;
2503 GetPoint()->nContent.Assign( nullptr, 0 );
2504
2505 pNd = &GetMark()->nNode.GetNode();
2506 if( !pNd->IsStartNode() )
2507 pNd = pNd->StartOfSectionNode();
2508 GetMark()->nNode = *pNd;
2509 GetMark()->nContent.Assign( nullptr, 0 );
2510
2511 m_bChanged = true;
2512 m_bParked = true;
2513 }
2514
HasReadOnlyBoxSel() const2515 bool SwTableCursor::HasReadOnlyBoxSel() const
2516 {
2517 bool bRet = false;
2518 for (size_t n = m_SelectedBoxes.size(); n; )
2519 {
2520 if (m_SelectedBoxes[--n]->GetFrameFormat()->GetProtect().IsContentProtected())
2521 {
2522 bRet = true;
2523 break;
2524 }
2525 }
2526 return bRet;
2527 }
2528
2529 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2530