Add a comment to clarify what kind of inputs the class handles
[LibreOffice.git] / sw / source / core / crsr / trvltbl.cxx
blobc7390b7de1b84ea4ad5f7ee024f65c074780f9bb
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* pStart = 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 pStart = 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 pStart = 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 pStart 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 pStart = aBoxes[0];
214 pEnd = aBoxes.back();
216 else
218 // will become point of table cursor
219 pStart = 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( *pStart->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 SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
364 // no content node => go to next content node
365 if( !pCNd )
366 pCNd = SwNodes::GoNext(&aTmp);
368 // robust
369 if ( !pCNd )
370 return false;
372 SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
374 if ( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
375 (!bInReadOnly && pFrame->IsProtected() ) )
377 // we are not located inside a 'valid' cell. We have to continue searching...
379 // skip behind current section. This might be the end of the table cell
380 // or behind an inner section or...
381 aTmp.Assign( *pCNd->EndOfSectionNode(), 1 );
383 // loop to find a suitable cell...
384 for( ;; )
386 SwNode* pNd = &aTmp.GetNode();
388 // we break this loop if we reached the end of the table.
389 // to make this code even more robust, we also break if we are
390 // already behind the table end node:
391 if( pNd == pTableEndNode || /*robust: */ pNd->GetIndex() > pTableEndNode->GetIndex() )
392 return false;
394 // ok, get the next content node:
395 pCNd = aTmp.GetNode().GetContentNode();
396 if( nullptr == pCNd )
397 pCNd = SwNodes::GoNext(&aTmp);
399 // robust:
400 if ( !pCNd )
401 return false;
403 // check if we have found a suitable table cell:
404 pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
406 if ( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
407 (bInReadOnly || !pFrame->IsProtected() ) )
409 // finally, we have found a suitable table cell => set index and return
410 rIdx = *pCNd;
411 return true;
414 // continue behind the current section:
415 aTmp.Assign( *pCNd->EndOfSectionNode(), +1 );
418 rIdx = *pCNd;
419 return true;
422 /// see lcl_FindNextCell()
423 static bool lcl_FindPrevCell( SwNodeIndex& rIdx, bool bInReadOnly )
425 SwNodeIndex aTmp( rIdx, -2 ); // TableNode + EndNode
427 const SwNode* pTableEndNode = &rIdx.GetNode();
428 const SwTableNode* pTableNd = pTableEndNode->StartOfSectionNode()->GetTableNode();
430 if ( !pTableNd )
432 OSL_FAIL( "lcl_FindPrevCell not celled with table start node!" );
433 return false;
436 SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
438 if( !pCNd )
439 pCNd = SwNodes::GoPrevious( &aTmp );
441 if ( !pCNd )
442 return false;
444 SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
446 if( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
447 (!bInReadOnly && pFrame->IsProtected() ))
449 // skip before current section
450 aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
451 for( ;; )
453 SwNode* pNd = &aTmp.GetNode();
455 if( pNd == pTableNd || pNd->GetIndex() < pTableNd->GetIndex() )
456 return false;
458 pCNd = aTmp.GetNode().GetContentNode();
459 if( nullptr == pCNd )
460 pCNd = SwNodes::GoPrevious( &aTmp );
462 if ( !pCNd )
463 return false;
465 pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
467 if( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
468 (bInReadOnly || !pFrame->IsProtected() ) )
470 rIdx = *pCNd;
471 return true; // ok, not protected
473 aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
476 rIdx = *pCNd;
477 return true;
480 bool GotoPrevTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
481 bool bInReadOnly )
483 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
485 SwNodeIndex aIdx( rCurrentCursor.GetPoint()->GetNode() );
487 SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
488 if( pTableNd )
490 // #i26532#: If we are inside a table, we may not go backward to the
491 // table start node, because we would miss any tables inside this table.
492 SwTableNode* pInnerTableNd = nullptr;
493 SwNodeIndex aTmpIdx( aIdx );
494 while( aTmpIdx.GetIndex() &&
495 nullptr == ( pInnerTableNd = aTmpIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
496 --aTmpIdx;
498 if( pInnerTableNd == pTableNd )
499 aIdx.Assign( *pTableNd, -1 );
502 SwNodeIndex aOldIdx = aIdx;
503 SwNodeOffset nLastNd(rCurrentCursor.GetDoc().GetNodes().Count() - 1);
504 do {
505 while( aIdx.GetIndex() &&
506 nullptr == ( pTableNd = aIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
508 --aIdx;
509 if ( aIdx == aOldIdx )
511 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
512 return false;
516 if ( !aIdx.GetIndex() )
518 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
519 aIdx = nLastNd;
520 continue;
524 if( &fnPosTable == &fnMoveForward ) // at the beginning?
526 aIdx = *aIdx.GetNode().StartOfSectionNode();
527 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
529 // skip table
530 aIdx.Assign( *pTableNd, -1 );
531 continue;
534 else
536 // check protected cells
537 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
539 // skip table
540 aIdx.Assign( *pTableNd, -1 );
541 continue;
545 SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
546 if ( pTextNode )
548 rCurrentCursor.GetPoint()->Assign(*pTextNode, &fnPosTable == &fnMoveBackward ?
549 pTextNode->Len() :
550 0 );
552 return true;
554 } while( true );
556 return false;
559 bool GotoNextTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
560 bool bInReadOnly )
562 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
564 SwNodeIndex aIdx( rCurrentCursor.GetPoint()->GetNode() );
565 SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
567 if( pTableNd )
568 aIdx.Assign( *pTableNd->EndOfSectionNode(), 1 );
570 SwNodeIndex aOldIdx = aIdx;
571 SwNodeOffset nLastNd(rCurrentCursor.GetDoc().GetNodes().Count() - 1);
572 do {
573 while( aIdx.GetIndex() < nLastNd &&
574 nullptr == ( pTableNd = aIdx.GetNode().GetTableNode()) )
576 ++aIdx;
577 if ( aIdx == aOldIdx )
579 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
580 return false;
584 if ( aIdx.GetIndex() == nLastNd )
586 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
587 aIdx = SwNodeOffset(0);
588 continue;
591 assert( pTableNd ); // coverity, should never be nullptr
593 if( &fnPosTable == &fnMoveForward ) // at the beginning?
595 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
597 // skip table
598 aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
599 continue;
602 else
604 aIdx = *aIdx.GetNode().EndOfSectionNode();
605 // check protected cells
606 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
608 // skip table
609 aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
610 continue;
614 SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
615 if ( pTextNode )
617 rCurrentCursor.GetPoint()->Assign(*pTextNode, &fnPosTable == &fnMoveBackward ?
618 pTextNode->Len() :
619 0 );
621 return true;
623 } while( true );
625 // the flow is such that it is not possible to get there
627 return false;
630 bool GotoCurrTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
631 bool bInReadOnly )
633 SwTableNode* pTableNd = rCurrentCursor.GetPoint()->GetNode().FindTableNode();
634 if( !pTableNd )
635 return false;
637 SwTextNode* pTextNode = nullptr;
638 if( &fnPosTable == &fnMoveBackward ) // to the end of the table
640 SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() );
641 if( !lcl_FindPrevCell( aIdx, bInReadOnly ))
642 return false;
643 pTextNode = aIdx.GetNode().GetTextNode();
645 else
647 SwNodeIndex aIdx( *pTableNd );
648 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
649 return false;
650 pTextNode = aIdx.GetNode().GetTextNode();
653 if ( pTextNode )
655 rCurrentCursor.GetPoint()->Assign(*pTextNode, &fnPosTable == &fnMoveBackward ?
656 pTextNode->Len() :
657 0 );
660 return true;
663 bool SwCursor::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
665 bool bRet = false;
666 SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this);
668 if( pTableCursor || !HasMark() )
670 SwCursorSaveState aSaveState( *this );
671 bRet = (*fnWhichTable)( *this, fnPosTable, IsReadOnlyAvailable() ) &&
672 !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
673 SwCursorSelOverFlags::Toggle );
675 return bRet;
678 bool SwCursorShell::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
680 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
682 SwShellCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
683 bool bCheckPos;
684 bool bRet;
685 SwNodeOffset nPtNd(0);
686 sal_Int32 nPtCnt = 0;
688 if ( !m_pTableCursor && m_pCurrentCursor->HasMark() )
690 // switch to table mode
691 m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
692 m_pCurrentCursor->DeleteMark();
693 m_pCurrentCursor->SwSelPaintRects::Hide();
694 m_pTableCursor->SetMark();
695 pCursor = m_pTableCursor;
696 bCheckPos = false;
698 else
700 bCheckPos = true;
701 nPtNd = pCursor->GetPoint()->GetNodeIndex();
702 nPtCnt = pCursor->GetPoint()->GetContentIndex();
705 bRet = pCursor->MoveTable( fnWhichTable, fnPosTable );
707 if( bRet )
709 // #i45028# - set "top" position for repeated headline rows
710 pCursor->GetPtPos() = Point();
712 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
714 if( bCheckPos &&
715 pCursor->GetPoint()->GetNodeIndex() == nPtNd &&
716 pCursor->GetPoint()->GetContentIndex() == nPtCnt )
717 bRet = false;
719 return bRet;
722 bool SwCursorShell::IsTableComplexForChart()
724 bool bRet = false;
726 // Here we may trigger table formatting so we better do that inside an action
727 StartAction();
728 const SwTableNode* pTNd = m_pCurrentCursor->GetPoint()->GetNode().FindTableNode();
729 if( pTNd )
731 // in a table; check if table or section is balanced
732 OUString sSel;
733 if( m_pTableCursor )
734 sSel = GetBoxNms();
735 bRet = pTNd->GetTable().IsTableComplexForChart( sSel );
737 EndAction();
739 return bRet;
742 OUString SwCursorShell::GetBoxNms() const
744 OUString sNm;
745 const SwPosition* pPos;
746 SwFrame* pFrame;
748 if( IsTableMode() )
750 SwContentNode *pCNd = m_pTableCursor->Start()->GetNode().GetContentNode();
751 pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
752 if( !pFrame )
753 return sNm;
755 do {
756 pFrame = pFrame->GetUpper();
757 } while ( pFrame && !pFrame->IsCellFrame() );
759 OSL_ENSURE( pFrame, "no frame for this box" );
761 if( !pFrame )
762 return sNm;
764 sNm = static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName() + ":";
765 pPos = m_pTableCursor->End();
767 else
769 const SwTableNode* pTableNd = IsCursorInTable();
770 if( !pTableNd )
771 return sNm;
772 pPos = GetCursor()->GetPoint();
775 SwContentNode* pCNd = pPos->GetNode().GetContentNode();
776 pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
778 if( pFrame )
780 do {
781 pFrame = pFrame->GetUpper();
782 } while ( pFrame && !pFrame->IsCellFrame() );
784 if( pFrame )
785 sNm += static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName();
787 return sNm;
790 bool SwCursorShell::GotoTable( const OUString& rName )
792 SwCallLink aLk( *this ); // watch Cursor-Moves
793 bool bRet = !m_pTableCursor && m_pCurrentCursor->GotoTable( rName );
794 if( bRet )
796 m_pCurrentCursor->GetPtPos() = Point();
797 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
798 SwCursorShell::READONLY );
800 return bRet;
803 bool SwCursorShell::CheckTableBoxContent( const SwPosition* pPos )
805 if( !m_pBoxIdx || !m_pBoxPtr || IsSelTableCells() || !IsAutoUpdateCells() )
806 return false;
808 // check if box content is consistent with given box format, reset if not
809 SwTableBox* pChkBox = nullptr;
810 SwStartNode* pSttNd = nullptr;
811 if( !pPos )
813 // get stored position
814 if (nullptr != (pSttNd = m_pBoxIdx->GetNode().GetStartNode()) &&
815 SwTableBoxStartNode == pSttNd->GetStartNodeType() &&
816 m_pBoxPtr == pSttNd->FindTableNode()->GetTable().
817 GetTableBox( m_pBoxIdx->GetIndex() ) )
818 pChkBox = m_pBoxPtr;
820 else
822 pSttNd = pPos->GetNode().FindStartNodeByType( SwTableBoxStartNode );
823 if( pSttNd)
824 pChkBox = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
827 // box has more than one paragraph
828 if( pChkBox && pSttNd->GetIndex() + SwNodeOffset(2) != pSttNd->EndOfSectionIndex() )
829 pChkBox = nullptr;
831 // destroy pointer before next action starts
832 if( !pPos && !pChkBox )
833 ClearTableBoxContent();
835 // cursor not anymore in this section?
836 if( pChkBox && !pPos &&
837 ( m_pCurrentCursor->HasMark() || m_pCurrentCursor->GetNext() != m_pCurrentCursor ||
838 pSttNd->GetIndex() + 1 == m_pCurrentCursor->GetPoint()->GetNodeIndex() ))
839 pChkBox = nullptr;
841 // Did the content of a box change at all? This is important if e.g. Undo
842 // could not restore the content properly.
843 if( pChkBox )
845 const SwTextNode* pNd = GetDoc()->GetNodes()[
846 pSttNd->GetIndex() + 1 ]->GetTextNode();
847 if( !pNd ||
848 ( pNd->GetText() == SwViewShell::GetShellRes()->aCalc_Error &&
849 SfxItemState::SET == pChkBox->GetFrameFormat()->
850 GetItemState( RES_BOXATR_FORMULA )) )
851 pChkBox = nullptr;
854 if( pChkBox )
856 // destroy pointer before next action starts
857 ClearTableBoxContent();
858 StartAction();
859 GetDoc()->ChkBoxNumFormat( *pChkBox, true );
860 EndAction();
863 return nullptr != pChkBox;
866 void SwCursorShell::SaveTableBoxContent( const SwPosition* pPos )
868 if( IsSelTableCells() || !IsAutoUpdateCells() )
869 return ;
871 if( !pPos )
872 pPos = m_pCurrentCursor->GetPoint();
874 SwStartNode* pSttNd = pPos->GetNode().FindStartNodeByType( SwTableBoxStartNode );
876 bool bCheckBox = false;
877 if( pSttNd && m_pBoxIdx )
879 if( pSttNd == &m_pBoxIdx->GetNode() )
880 pSttNd = nullptr;
881 else
882 bCheckBox = true;
884 else
885 bCheckBox = nullptr != m_pBoxIdx;
887 if( bCheckBox )
889 // check m_pBoxIdx
890 SwPosition aPos( *m_pBoxIdx );
891 CheckTableBoxContent( &aPos );
894 if( pSttNd )
896 m_pBoxPtr = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
898 if( m_pBoxIdx )
899 *m_pBoxIdx = *pSttNd;
900 else
901 m_pBoxIdx = new SwNodeIndex( *pSttNd );
905 void SwCursorShell::ClearTableBoxContent()
907 delete m_pBoxIdx;
908 m_pBoxIdx = nullptr;
909 m_pBoxPtr = nullptr;
912 bool SwCursorShell::EndAllTableBoxEdit()
914 bool bRet = false;
915 for(SwViewShell& rSh : GetRingContainer())
917 if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rSh) )
918 bRet |= pCursorShell->CheckTableBoxContent(
919 pCursorShell->m_pCurrentCursor->GetPoint() );
922 return bRet;
925 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */