Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / crsr / trvltbl.cxx
blob689457d028636df524292ffbb84edd62da118f28
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <hintids.hxx>
21 #include <crsrsh.hxx>
22 #include <doc.hxx>
23 #include <cntfrm.hxx>
24 #include <editsh.hxx>
25 #include <pam.hxx>
26 #include <swtable.hxx>
27 #include <frmfmt.hxx>
28 #include <viscrs.hxx>
29 #include "callnk.hxx"
30 #include <tabfrm.hxx>
31 #include <ndtxt.hxx>
32 #include <shellres.hxx>
33 #include <cellfrm.hxx>
34 #include <IDocumentLayoutAccess.hxx>
35 #include <osl/diagnose.h>
36 #include <svx/srchdlg.hxx>
38 /// set cursor into next/previous cell
39 bool SwCursorShell::GoNextCell( bool bAppendLine )
41 bool bRet = false;
42 const SwTableNode* pTableNd = nullptr;
44 if( IsTableMode() || nullptr != ( pTableNd = IsCursorInTable() ))
46 SwCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
47 SwCallLink aLk( *this ); // watch Cursor-Moves
48 bRet = true;
50 // Check if we have to move the cursor to a covered cell before
51 // proceeding:
52 const SwNode* pTableBoxStartNode = pCursor->GetPointNode().FindTableBoxStartNode();
53 const SwTableBox* pTableBox = nullptr;
55 if ( pCursor->GetCursorRowSpanOffset() )
57 pTableBox = pTableBoxStartNode->GetTableBox();
58 if (pTableBox && pTableBox->getRowSpan() > 1)
60 if ( !pTableNd )
61 pTableNd = IsCursorInTable();
62 assert (pTableNd);
63 pTableBox = & pTableBox->FindEndOfRowSpan( pTableNd->GetTable(),
64 o3tl::narrowing<sal_uInt16>(pTableBox->getRowSpan() + pCursor->GetCursorRowSpanOffset() ) );
65 pTableBoxStartNode = pTableBox->GetSttNd();
69 SwNodeIndex aCellStt( *pTableBoxStartNode->EndOfSectionNode(), 1 );
71 // if there is another StartNode after the EndNode of a cell then
72 // there is another cell
73 if( !aCellStt.GetNode().IsStartNode() )
75 if( pCursor->HasMark() || !bAppendLine )
76 bRet = false;
77 else if (pTableNd)
79 // if there is no list anymore then create new one
80 if ( !pTableBox )
81 pTableBox = pTableNd->GetTable().GetTableBox(
82 pCursor->GetPoint()->GetNode().
83 StartOfSectionIndex() );
85 OSL_ENSURE( pTableBox, "Box is not in this table" );
86 SwSelBoxes aBoxes;
88 // the document might change; w/o Action views would not be notified
89 static_cast<SwEditShell*>(this)->StartAllAction();
90 bRet = mxDoc->InsertRow( SwTable::SelLineFromBox( pTableBox, aBoxes, false ));
91 static_cast<SwEditShell*>(this)->EndAllAction();
94 bRet = bRet && pCursor->GoNextCell();
95 if( bRet )
96 UpdateCursor();
98 return bRet;
101 bool SwCursorShell::GoPrevCell()
103 bool bRet = false;
104 if( IsTableMode() || IsCursorInTable() )
106 SwCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
107 SwCallLink aLk( *this ); // watch Cursor-Moves
108 bRet = pCursor->GoPrevCell();
109 if( bRet )
110 UpdateCursor(); // update current cursor
112 return bRet;
115 static const SwFrame* lcl_FindMostUpperCellFrame( const SwFrame* pFrame )
117 while ( pFrame &&
118 ( !pFrame->IsCellFrame() ||
119 !pFrame->GetUpper()->GetUpper()->IsTabFrame() ||
120 pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) )
122 pFrame = pFrame->GetUpper();
124 return pFrame;
127 bool SwCursorShell::SelTableRowOrCol( bool bRow, bool bRowSimple )
129 // check if the current cursor's SPoint/Mark are in a table
130 SwFrame *pFrame = GetCurrFrame();
131 if( !pFrame || !pFrame->IsInTab() )
132 return false;
134 const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
135 const SwTabFrame* pMasterTabFrame = pTabFrame->IsFollow() ? pTabFrame->FindMaster( true ) : pTabFrame;
136 const SwTable* pTable = pTabFrame->GetTable();
138 CurrShell aCurr( this );
140 const SwTableBox* pStt = nullptr;
141 const SwTableBox* pEnd = nullptr;
143 // search box based on layout
144 SwSelBoxes aBoxes;
145 SwTableSearchType eType = bRow ? SwTableSearchType::Row : SwTableSearchType::Col;
146 const bool bCheckProtected = !IsReadOnlyAvailable();
148 if( bCheckProtected )
149 eType = static_cast<SwTableSearchType>(eType | SwTableSearchType::Protect);
151 if ( !bRowSimple )
153 GetTableSel( *this, aBoxes, eType );
155 if( aBoxes.empty() )
156 return false;
158 pStt = aBoxes[0];
159 pEnd = aBoxes.back();
161 // #i32329# Enhanced table selection
162 else if ( pTable->IsNewModel() )
164 const SwShellCursor *pCursor = GetCursor_();
165 SwTable::SearchType eSearchType = bRow ? SwTable::SEARCH_ROW : SwTable::SEARCH_COL;
166 pTable->CreateSelection( *pCursor, aBoxes, eSearchType, bCheckProtected );
167 if( aBoxes.empty() )
168 return false;
170 pStt = aBoxes[0];
171 pEnd = aBoxes.back();
173 m_eEnhancedTableSel = eSearchType;
175 else
177 const SwShellCursor *pCursor = GetCursor_();
178 const SwFrame* pStartFrame = pFrame;
179 const SwContentNode *pCNd = pCursor->GetMarkContentNode();
180 std::pair<Point, bool> const tmp(pCursor->GetMkPos(), true);
181 const SwFrame* pEndFrame = pCNd
182 ? pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp)
183 : nullptr;
185 if ( bRow )
187 pStartFrame = lcl_FindMostUpperCellFrame( pStartFrame );
188 pEndFrame = lcl_FindMostUpperCellFrame( pEndFrame );
191 if ( !pStartFrame || !pEndFrame )
192 return false;
194 const bool bVert = pFrame->ImplFindTabFrame()->IsVertical();
196 // If we select upwards it is sufficient to set pStt and pEnd
197 // to the first resp. last box of the selection obtained from
198 // GetTableSel. However, selecting downwards requires the frames
199 // located at the corners of the selection. This does not work
200 // for column selections in vertical tables:
201 const bool bSelectUp = ( bVert && !bRow ) ||
202 *pCursor->GetPoint() <= *pCursor->GetMark();
203 SwCellFrames aCells;
204 GetTableSel( static_cast<const SwCellFrame*>(pStartFrame),
205 static_cast<const SwCellFrame*>(pEndFrame),
206 aBoxes, bSelectUp ? nullptr : &aCells, eType );
208 if( aBoxes.empty() || ( !bSelectUp && 4 != aCells.size() ) )
209 return false;
211 if ( bSelectUp )
213 pStt = aBoxes[0];
214 pEnd = aBoxes.back();
216 else
218 // will become point of table cursor
219 pStt = aCells[bVert ? 0 : (bRow ? 2 : 1)]->GetTabBox();
220 // will become mark of table cursor
221 pEnd = aCells[bVert ? 3 : (bRow ? 1 : 2)]->GetTabBox();
225 // if no table cursor exists, create one
226 if( !m_pTableCursor )
228 m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
229 m_pCurrentCursor->DeleteMark();
230 m_pCurrentCursor->SwSelPaintRects::Hide();
233 m_pTableCursor->DeleteMark();
235 // set start and end of a column
236 m_pTableCursor->GetPoint()->Assign( *pEnd->GetSttNd()->EndOfSectionNode() );
237 m_pTableCursor->Move( fnMoveBackward, GoInContent );
238 m_pTableCursor->SetMark();
239 m_pTableCursor->GetPoint()->Assign( *pStt->GetSttNd()->EndOfSectionNode() );
240 m_pTableCursor->Move( fnMoveBackward, GoInContent );
242 // set PtPos 'close' to the reference table, otherwise we might get problems
243 // with the repeated headlines check in UpdateCursor():
244 if ( !bRow )
245 m_pTableCursor->GetPtPos() = pMasterTabFrame->IsVertical()
246 ? pMasterTabFrame->getFrameArea().TopRight()
247 : pMasterTabFrame->getFrameArea().TopLeft();
249 UpdateCursor();
250 return true;
253 bool SwCursorShell::SelTable()
255 // check if the current cursor's SPoint/Mark are in a table
256 SwFrame *pFrame = GetCurrFrame();
257 if( !pFrame->IsInTab() )
258 return false;
260 const SwTabFrame *pTableFrame = pFrame->ImplFindTabFrame();
261 const SwTabFrame* pMasterTabFrame = pTableFrame->IsFollow() ? pTableFrame->FindMaster( true ) : pTableFrame;
262 const SwTableNode* pTableNd = pTableFrame->GetTable()->GetTableNode();
264 CurrShell aCurr( this );
266 if( !m_pTableCursor )
268 m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
269 m_pCurrentCursor->DeleteMark();
270 m_pCurrentCursor->SwSelPaintRects::Hide();
273 m_pTableCursor->DeleteMark();
274 m_pTableCursor->GetPoint()->Assign( *pTableNd );
275 m_pTableCursor->Move( fnMoveForward, GoInContent );
276 m_pTableCursor->SetMark();
277 // set MkPos 'close' to the master table, otherwise we might get problems
278 // with the repeated headlines check in UpdateCursor():
279 m_pTableCursor->GetMkPos() = pMasterTabFrame->IsVertical() ? pMasterTabFrame->getFrameArea().TopRight() : pMasterTabFrame->getFrameArea().TopLeft();
280 m_pTableCursor->GetPoint()->Assign( *pTableNd->EndOfSectionNode() );
281 m_pTableCursor->Move( fnMoveBackward, GoInContent );
282 UpdateCursor();
283 return true;
286 bool SwCursorShell::SelTableBox()
288 // if we're in a table, create a table cursor, and select the cell
289 // that the current cursor's point resides in
291 // search for start node of our table box. If not found, exit really
292 const SwStartNode* pStartNode =
293 m_pCurrentCursor->GetPoint()->GetNode().FindTableBoxStartNode();
295 #if OSL_DEBUG_LEVEL > 0
296 // the old code checks whether we're in a table by asking the
297 // frame. This should yield the same result as searching for the
298 // table box start node, right?
299 SwFrame *pFrame = GetCurrFrame();
300 OSL_ENSURE( !pFrame->IsInTab() == !(pStartNode != nullptr),
301 "Schroedinger's table: We're in a box, and also we aren't." );
302 #endif
303 if( pStartNode == nullptr )
304 return false;
306 CurrShell aCurr( this );
308 // create a table cursor, if there isn't one already
309 if( !m_pTableCursor )
311 m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
312 m_pCurrentCursor->DeleteMark();
313 m_pCurrentCursor->SwSelPaintRects::Hide();
316 // select the complete box with our shiny new m_pTableCursor
317 // 1. delete mark, and move point to first content node in box
318 m_pTableCursor->DeleteMark();
319 m_pTableCursor->GetPoint()->Assign( *pStartNode );
320 m_pTableCursor->Move( fnMoveForward, GoInNode );
322 // 2. set mark, and move point to last content node in box
323 m_pTableCursor->SetMark();
324 m_pTableCursor->GetPoint()->Assign( *(pStartNode->EndOfSectionNode()) );
325 m_pTableCursor->Move( fnMoveBackward, GoInNode );
327 // 3. exchange
328 m_pTableCursor->Exchange();
330 // with some luck, UpdateCursor() will now update everything that
331 // needs updating
332 UpdateCursor();
334 return true;
337 // TODO: provide documentation
338 /** get the next non-protected cell inside a table
340 @param[in,out] rIdx is on a table node
341 @param bInReadOnly ???
343 @return <false> if no suitable cell could be found, otherwise <rIdx> points
344 to content in a suitable cell and <true> is returned.
346 static bool lcl_FindNextCell( SwNodeIndex& rIdx, bool bInReadOnly )
348 // check protected cells
349 SwNodeIndex aTmp( rIdx, 2 ); // TableNode + StartNode
351 // the resulting cell should be in that table:
352 const SwTableNode* pTableNd = rIdx.GetNode().GetTableNode();
354 if ( !pTableNd )
356 OSL_FAIL( "lcl_FindNextCell not celled with table start node!" );
357 return false;
360 const SwNode* pTableEndNode = pTableNd->EndOfSectionNode();
362 SwNodes& rNds = aTmp.GetNode().GetNodes();
363 SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
365 // no content node => go to next content node
366 if( !pCNd )
367 pCNd = rNds.GoNext( &aTmp );
369 // robust
370 if ( !pCNd )
371 return false;
373 SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
375 if ( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
376 (!bInReadOnly && pFrame->IsProtected() ) )
378 // we are not located inside a 'valid' cell. We have to continue searching...
380 // skip behind current section. This might be the end of the table cell
381 // or behind an inner section or...
382 aTmp.Assign( *pCNd->EndOfSectionNode(), 1 );
384 // loop to find a suitable cell...
385 for( ;; )
387 SwNode* pNd = &aTmp.GetNode();
389 // we break this loop if we reached the end of the table.
390 // to make this code even more robust, we also break if we are
391 // already behind the table end node:
392 if( pNd == pTableEndNode || /*robust: */ pNd->GetIndex() > pTableEndNode->GetIndex() )
393 return false;
395 // ok, get the next content node:
396 pCNd = aTmp.GetNode().GetContentNode();
397 if( nullptr == pCNd )
398 pCNd = rNds.GoNext( &aTmp );
400 // robust:
401 if ( !pCNd )
402 return false;
404 // check if we have found a suitable table cell:
405 pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
407 if ( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
408 (bInReadOnly || !pFrame->IsProtected() ) )
410 // finally, we have found a suitable table cell => set index and return
411 rIdx = *pCNd;
412 return true;
415 // continue behind the current section:
416 aTmp.Assign( *pCNd->EndOfSectionNode(), +1 );
419 rIdx = *pCNd;
420 return true;
423 /// see lcl_FindNextCell()
424 static bool lcl_FindPrevCell( SwNodeIndex& rIdx, bool bInReadOnly )
426 SwNodeIndex aTmp( rIdx, -2 ); // TableNode + EndNode
428 const SwNode* pTableEndNode = &rIdx.GetNode();
429 const SwTableNode* pTableNd = pTableEndNode->StartOfSectionNode()->GetTableNode();
431 if ( !pTableNd )
433 OSL_FAIL( "lcl_FindPrevCell not celled with table start node!" );
434 return false;
437 SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
439 if( !pCNd )
440 pCNd = SwNodes::GoPrevious( &aTmp );
442 if ( !pCNd )
443 return false;
445 SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
447 if( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
448 (!bInReadOnly && pFrame->IsProtected() ))
450 // skip before current section
451 aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
452 for( ;; )
454 SwNode* pNd = &aTmp.GetNode();
456 if( pNd == pTableNd || pNd->GetIndex() < pTableNd->GetIndex() )
457 return false;
459 pCNd = aTmp.GetNode().GetContentNode();
460 if( nullptr == pCNd )
461 pCNd = SwNodes::GoPrevious( &aTmp );
463 if ( !pCNd )
464 return false;
466 pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
468 if( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
469 (bInReadOnly || !pFrame->IsProtected() ) )
471 rIdx = *pCNd;
472 return true; // ok, not protected
474 aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
477 rIdx = *pCNd;
478 return true;
481 bool GotoPrevTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
482 bool bInReadOnly )
484 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
486 SwNodeIndex aIdx( rCurrentCursor.GetPoint()->GetNode() );
488 SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
489 if( pTableNd )
491 // #i26532#: If we are inside a table, we may not go backward to the
492 // table start node, because we would miss any tables inside this table.
493 SwTableNode* pInnerTableNd = nullptr;
494 SwNodeIndex aTmpIdx( aIdx );
495 while( aTmpIdx.GetIndex() &&
496 nullptr == ( pInnerTableNd = aTmpIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
497 --aTmpIdx;
499 if( pInnerTableNd == pTableNd )
500 aIdx.Assign( *pTableNd, -1 );
503 SwNodeIndex aOldIdx = aIdx;
504 SwNodeOffset nLastNd(rCurrentCursor.GetDoc().GetNodes().Count() - 1);
505 do {
506 while( aIdx.GetIndex() &&
507 nullptr == ( pTableNd = aIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
509 --aIdx;
510 if ( aIdx == aOldIdx )
512 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
513 return false;
517 if ( !aIdx.GetIndex() )
519 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
520 aIdx = nLastNd;
521 continue;
525 if( &fnPosTable == &fnMoveForward ) // at the beginning?
527 aIdx = *aIdx.GetNode().StartOfSectionNode();
528 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
530 // skip table
531 aIdx.Assign( *pTableNd, -1 );
532 continue;
535 else
537 // check protected cells
538 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
540 // skip table
541 aIdx.Assign( *pTableNd, -1 );
542 continue;
546 SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
547 if ( pTextNode )
549 rCurrentCursor.GetPoint()->Assign(*pTextNode, &fnPosTable == &fnMoveBackward ?
550 pTextNode->Len() :
551 0 );
553 return true;
555 } while( true );
557 return false;
560 bool GotoNextTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
561 bool bInReadOnly )
563 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
565 SwNodeIndex aIdx( rCurrentCursor.GetPoint()->GetNode() );
566 SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
568 if( pTableNd )
569 aIdx.Assign( *pTableNd->EndOfSectionNode(), 1 );
571 SwNodeIndex aOldIdx = aIdx;
572 SwNodeOffset nLastNd(rCurrentCursor.GetDoc().GetNodes().Count() - 1);
573 do {
574 while( aIdx.GetIndex() < nLastNd &&
575 nullptr == ( pTableNd = aIdx.GetNode().GetTableNode()) )
577 ++aIdx;
578 if ( aIdx == aOldIdx )
580 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
581 return false;
585 if ( aIdx.GetIndex() == nLastNd )
587 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
588 aIdx = SwNodeOffset(0);
589 continue;
592 assert( pTableNd ); // coverity, should never be nullptr
594 if( &fnPosTable == &fnMoveForward ) // at the beginning?
596 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
598 // skip table
599 aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
600 continue;
603 else
605 aIdx = *aIdx.GetNode().EndOfSectionNode();
606 // check protected cells
607 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
609 // skip table
610 aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
611 continue;
615 SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
616 if ( pTextNode )
618 rCurrentCursor.GetPoint()->Assign(*pTextNode, &fnPosTable == &fnMoveBackward ?
619 pTextNode->Len() :
620 0 );
622 return true;
624 } while( true );
626 // the flow is such that it is not possible to get there
628 return false;
631 bool GotoCurrTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
632 bool bInReadOnly )
634 SwTableNode* pTableNd = rCurrentCursor.GetPoint()->GetNode().FindTableNode();
635 if( !pTableNd )
636 return false;
638 SwTextNode* pTextNode = nullptr;
639 if( &fnPosTable == &fnMoveBackward ) // to the end of the table
641 SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() );
642 if( !lcl_FindPrevCell( aIdx, bInReadOnly ))
643 return false;
644 pTextNode = aIdx.GetNode().GetTextNode();
646 else
648 SwNodeIndex aIdx( *pTableNd );
649 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
650 return false;
651 pTextNode = aIdx.GetNode().GetTextNode();
654 if ( pTextNode )
656 rCurrentCursor.GetPoint()->Assign(*pTextNode, &fnPosTable == &fnMoveBackward ?
657 pTextNode->Len() :
658 0 );
661 return true;
664 bool SwCursor::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
666 bool bRet = false;
667 SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this);
669 if( pTableCursor || !HasMark() )
671 SwCursorSaveState aSaveState( *this );
672 bRet = (*fnWhichTable)( *this, fnPosTable, IsReadOnlyAvailable() ) &&
673 !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
674 SwCursorSelOverFlags::Toggle );
676 return bRet;
679 bool SwCursorShell::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
681 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
683 SwShellCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
684 bool bCheckPos;
685 bool bRet;
686 SwNodeOffset nPtNd(0);
687 sal_Int32 nPtCnt = 0;
689 if ( !m_pTableCursor && m_pCurrentCursor->HasMark() )
691 // switch to table mode
692 m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
693 m_pCurrentCursor->DeleteMark();
694 m_pCurrentCursor->SwSelPaintRects::Hide();
695 m_pTableCursor->SetMark();
696 pCursor = m_pTableCursor;
697 bCheckPos = false;
699 else
701 bCheckPos = true;
702 nPtNd = pCursor->GetPoint()->GetNodeIndex();
703 nPtCnt = pCursor->GetPoint()->GetContentIndex();
706 bRet = pCursor->MoveTable( fnWhichTable, fnPosTable );
708 if( bRet )
710 // #i45028# - set "top" position for repeated headline rows
711 pCursor->GetPtPos() = Point();
713 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
715 if( bCheckPos &&
716 pCursor->GetPoint()->GetNodeIndex() == nPtNd &&
717 pCursor->GetPoint()->GetContentIndex() == nPtCnt )
718 bRet = false;
720 return bRet;
723 bool SwCursorShell::IsTableComplexForChart()
725 bool bRet = false;
727 // Here we may trigger table formatting so we better do that inside an action
728 StartAction();
729 const SwTableNode* pTNd = m_pCurrentCursor->GetPoint()->GetNode().FindTableNode();
730 if( pTNd )
732 // in a table; check if table or section is balanced
733 OUString sSel;
734 if( m_pTableCursor )
735 sSel = GetBoxNms();
736 bRet = pTNd->GetTable().IsTableComplexForChart( sSel );
738 EndAction();
740 return bRet;
743 OUString SwCursorShell::GetBoxNms() const
745 OUString sNm;
746 const SwPosition* pPos;
747 SwFrame* pFrame;
749 if( IsTableMode() )
751 SwContentNode *pCNd = m_pTableCursor->Start()->GetNode().GetContentNode();
752 pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
753 if( !pFrame )
754 return sNm;
756 do {
757 pFrame = pFrame->GetUpper();
758 } while ( pFrame && !pFrame->IsCellFrame() );
760 OSL_ENSURE( pFrame, "no frame for this box" );
762 if( !pFrame )
763 return sNm;
765 sNm = static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName() + ":";
766 pPos = m_pTableCursor->End();
768 else
770 const SwTableNode* pTableNd = IsCursorInTable();
771 if( !pTableNd )
772 return sNm;
773 pPos = GetCursor()->GetPoint();
776 SwContentNode* pCNd = pPos->GetNode().GetContentNode();
777 pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
779 if( pFrame )
781 do {
782 pFrame = pFrame->GetUpper();
783 } while ( pFrame && !pFrame->IsCellFrame() );
785 if( pFrame )
786 sNm += static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName();
788 return sNm;
791 bool SwCursorShell::GotoTable( const OUString& rName )
793 SwCallLink aLk( *this ); // watch Cursor-Moves
794 bool bRet = !m_pTableCursor && m_pCurrentCursor->GotoTable( rName );
795 if( bRet )
797 m_pCurrentCursor->GetPtPos() = Point();
798 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
799 SwCursorShell::READONLY );
801 return bRet;
804 bool SwCursorShell::CheckTableBoxContent( const SwPosition* pPos )
806 if( !m_pBoxIdx || !m_pBoxPtr || IsSelTableCells() || !IsAutoUpdateCells() )
807 return false;
809 // check if box content is consistent with given box format, reset if not
810 SwTableBox* pChkBox = nullptr;
811 SwStartNode* pSttNd = nullptr;
812 if( !pPos )
814 // get stored position
815 if (nullptr != (pSttNd = m_pBoxIdx->GetNode().GetStartNode()) &&
816 SwTableBoxStartNode == pSttNd->GetStartNodeType() &&
817 m_pBoxPtr == pSttNd->FindTableNode()->GetTable().
818 GetTableBox( m_pBoxIdx->GetIndex() ) )
819 pChkBox = m_pBoxPtr;
821 else
823 pSttNd = pPos->GetNode().FindSttNodeByType( SwTableBoxStartNode );
824 if( pSttNd)
825 pChkBox = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
828 // box has more than one paragraph
829 if( pChkBox && pSttNd->GetIndex() + SwNodeOffset(2) != pSttNd->EndOfSectionIndex() )
830 pChkBox = nullptr;
832 // destroy pointer before next action starts
833 if( !pPos && !pChkBox )
834 ClearTableBoxContent();
836 // cursor not anymore in this section?
837 if( pChkBox && !pPos &&
838 ( m_pCurrentCursor->HasMark() || m_pCurrentCursor->GetNext() != m_pCurrentCursor ||
839 pSttNd->GetIndex() + 1 == m_pCurrentCursor->GetPoint()->GetNodeIndex() ))
840 pChkBox = nullptr;
842 // Did the content of a box change at all? This is important if e.g. Undo
843 // could not restore the content properly.
844 if( pChkBox )
846 const SwTextNode* pNd = GetDoc()->GetNodes()[
847 pSttNd->GetIndex() + 1 ]->GetTextNode();
848 if( !pNd ||
849 ( pNd->GetText() == SwViewShell::GetShellRes()->aCalc_Error &&
850 SfxItemState::SET == pChkBox->GetFrameFormat()->
851 GetItemState( RES_BOXATR_FORMULA )) )
852 pChkBox = nullptr;
855 if( pChkBox )
857 // destroy pointer before next action starts
858 ClearTableBoxContent();
859 StartAction();
860 GetDoc()->ChkBoxNumFormat( *pChkBox, true );
861 EndAction();
864 return nullptr != pChkBox;
867 void SwCursorShell::SaveTableBoxContent( const SwPosition* pPos )
869 if( IsSelTableCells() || !IsAutoUpdateCells() )
870 return ;
872 if( !pPos )
873 pPos = m_pCurrentCursor->GetPoint();
875 SwStartNode* pSttNd = pPos->GetNode().FindSttNodeByType( SwTableBoxStartNode );
877 bool bCheckBox = false;
878 if( pSttNd && m_pBoxIdx )
880 if( pSttNd == &m_pBoxIdx->GetNode() )
881 pSttNd = nullptr;
882 else
883 bCheckBox = true;
885 else
886 bCheckBox = nullptr != m_pBoxIdx;
888 if( bCheckBox )
890 // check m_pBoxIdx
891 SwPosition aPos( *m_pBoxIdx );
892 CheckTableBoxContent( &aPos );
895 if( pSttNd )
897 m_pBoxPtr = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
899 if( m_pBoxIdx )
900 *m_pBoxIdx = *pSttNd;
901 else
902 m_pBoxIdx = new SwNodeIndex( *pSttNd );
906 void SwCursorShell::ClearTableBoxContent()
908 delete m_pBoxIdx;
909 m_pBoxIdx = nullptr;
910 m_pBoxPtr = nullptr;
913 bool SwCursorShell::EndAllTableBoxEdit()
915 bool bRet = false;
916 for(SwViewShell& rSh : GetRingContainer())
918 if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rSh) )
919 bRet |= pCursorShell->CheckTableBoxContent(
920 pCursorShell->m_pCurrentCursor->GetPoint() );
923 return bRet;
926 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */