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