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 <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>
37 
38 /// set cursor into next/previous cell
GoNextCell(bool bAppendLine)39 bool SwCursorShell::GoNextCell( bool bAppendLine )
40 {
41     bool bRet = false;
42     const SwTableNode* pTableNd = nullptr;
43 
44     if( IsTableMode() || nullptr != ( pTableNd = IsCursorInTable() ))
45     {
46         SwCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
47         SwCallLink aLk( *this ); // watch Cursor-Moves
48         bRet = true;
49 
50         // Check if we have to move the cursor to a covered cell before
51         // proceeding:
52         const SwNode* pTableBoxStartNode = pCursor->GetNode().FindTableBoxStartNode();
53         const SwTableBox* pTableBox = nullptr;
54 
55         if ( pCursor->GetCursorRowSpanOffset() )
56         {
57             pTableBox = pTableBoxStartNode->GetTableBox();
58             if (pTableBox && pTableBox->getRowSpan() > 1)
59             {
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();
66             }
67         }
68 
69         SwNodeIndex  aCellStt( *pTableBoxStartNode->EndOfSectionNode(), 1 );
70 
71         // if there is another StartNode after the EndNode of a cell then
72         // there is another cell
73         if( !aCellStt.GetNode().IsStartNode() )
74         {
75             if( pCursor->HasMark() || !bAppendLine )
76                 bRet = false;
77             else if (pTableNd)
78             {
79                 // if there is no list anymore then create new one
80                 if ( !pTableBox )
81                     pTableBox = pTableNd->GetTable().GetTableBox(
82                                     pCursor->GetPoint()->nNode.GetNode().
83                                     StartOfSectionIndex() );
84 
85                 OSL_ENSURE( pTableBox, "Box is not in this table" );
86                 SwSelBoxes aBoxes;
87 
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();
92             }
93         }
94         bRet = bRet && pCursor->GoNextCell();
95         if( bRet )
96             UpdateCursor();
97     }
98     return bRet;
99 }
100 
GoPrevCell()101 bool SwCursorShell::GoPrevCell()
102 {
103     bool bRet = false;
104     if( IsTableMode() || IsCursorInTable() )
105     {
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
111     }
112     return bRet;
113 }
114 
lcl_FindMostUpperCellFrame(const SwFrame * pFrame)115 static const SwFrame* lcl_FindMostUpperCellFrame( const SwFrame* pFrame )
116 {
117     while ( pFrame &&
118             ( !pFrame->IsCellFrame() ||
119               !pFrame->GetUpper()->GetUpper()->IsTabFrame() ||
120                pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) )
121     {
122         pFrame = pFrame->GetUpper();
123     }
124     return pFrame;
125 }
126 
SelTableRowOrCol(bool bRow,bool bRowSimple)127 bool SwCursorShell::SelTableRowOrCol( bool bRow, bool bRowSimple )
128 {
129     // check if the current cursor's SPoint/Mark are in a table
130     SwFrame *pFrame = GetCurrFrame();
131     if( !pFrame->IsInTab() )
132         return false;
133 
134     const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
135     const SwTabFrame* pMasterTabFrame = pTabFrame->IsFollow() ? pTabFrame->FindMaster( true ) : pTabFrame;
136     const SwTable* pTable = pTabFrame->GetTable();
137 
138     CurrShell aCurr( this );
139 
140     const SwTableBox* pStt = nullptr;
141     const SwTableBox* pEnd = nullptr;
142 
143     // search box based on layout
144     SwSelBoxes aBoxes;
145     SwTableSearchType eType = bRow ? SwTableSearchType::Row : SwTableSearchType::Col;
146     const bool bCheckProtected = !IsReadOnlyAvailable();
147 
148     if( bCheckProtected )
149         eType = static_cast<SwTableSearchType>(eType | SwTableSearchType::Protect);
150 
151     if ( !bRowSimple )
152     {
153         GetTableSel( *this, aBoxes, eType );
154 
155         if( aBoxes.empty() )
156             return false;
157 
158         pStt = aBoxes[0];
159         pEnd = aBoxes.back();
160     }
161     // #i32329# Enhanced table selection
162     else if ( pTable->IsNewModel() )
163     {
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;
169 
170         pStt = aBoxes[0];
171         pEnd = aBoxes.back();
172 
173         m_eEnhancedTableSel = eSearchType;
174     }
175     else
176     {
177         const SwShellCursor *pCursor = GetCursor_();
178         const SwFrame* pStartFrame = pFrame;
179         const SwContentNode *pCNd = pCursor->GetContentNode( false );
180         std::pair<Point, bool> const tmp(pCursor->GetMkPos(), true);
181         const SwFrame* pEndFrame = pCNd
182             ? pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp)
183             : nullptr;
184 
185         if ( bRow )
186         {
187             pStartFrame = lcl_FindMostUpperCellFrame( pStartFrame );
188             pEndFrame   = lcl_FindMostUpperCellFrame( pEndFrame   );
189         }
190 
191         if ( !pStartFrame || !pEndFrame )
192             return false;
193 
194         const bool bVert = pFrame->ImplFindTabFrame()->IsVertical();
195 
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 );
207 
208         if( aBoxes.empty() || ( !bSelectUp && 4 != aCells.size() ) )
209             return false;
210 
211         if ( bSelectUp )
212         {
213             pStt = aBoxes[0];
214             pEnd = aBoxes.back();
215         }
216         else
217         {
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();
222         }
223     }
224 
225     // if no table cursor exists, create one
226     if( !m_pTableCursor )
227     {
228         m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
229         m_pCurrentCursor->DeleteMark();
230         m_pCurrentCursor->SwSelPaintRects::Hide();
231     }
232 
233     m_pTableCursor->DeleteMark();
234 
235     // set start and end of a column
236     m_pTableCursor->GetPoint()->nNode = *pEnd->GetSttNd();
237     m_pTableCursor->Move( fnMoveForward, GoInContent );
238     m_pTableCursor->SetMark();
239     m_pTableCursor->GetPoint()->nNode = *pStt->GetSttNd()->EndOfSectionNode();
240     m_pTableCursor->Move( fnMoveBackward, GoInContent );
241 
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();
248 
249     UpdateCursor();
250     return true;
251 }
252 
SelTable()253 bool SwCursorShell::SelTable()
254 {
255     // check if the current cursor's SPoint/Mark are in a table
256     SwFrame *pFrame = GetCurrFrame();
257     if( !pFrame->IsInTab() )
258         return false;
259 
260     const SwTabFrame *pTableFrame = pFrame->ImplFindTabFrame();
261     const SwTabFrame* pMasterTabFrame = pTableFrame->IsFollow() ? pTableFrame->FindMaster( true ) : pTableFrame;
262     const SwTableNode* pTableNd = pTableFrame->GetTable()->GetTableNode();
263 
264     CurrShell aCurr( this );
265 
266     if( !m_pTableCursor )
267     {
268         m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
269         m_pCurrentCursor->DeleteMark();
270         m_pCurrentCursor->SwSelPaintRects::Hide();
271     }
272 
273     m_pTableCursor->DeleteMark();
274     m_pTableCursor->GetPoint()->nNode = *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()->nNode = *pTableNd->EndOfSectionNode();
281     m_pTableCursor->Move( fnMoveBackward, GoInContent );
282     UpdateCursor();
283     return true;
284 }
285 
SelTableBox()286 bool SwCursorShell::SelTableBox()
287 {
288     // if we're in a table, create a table cursor, and select the cell
289     // that the current cursor's point resides in
290 
291     // search for start node of our table box. If not found, exit really
292     const SwStartNode* pStartNode =
293         m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableBoxStartNode();
294 
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;
305 
306     CurrShell aCurr( this );
307 
308     // create a table cursor, if there isn't one already
309     if( !m_pTableCursor )
310     {
311         m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
312         m_pCurrentCursor->DeleteMark();
313         m_pCurrentCursor->SwSelPaintRects::Hide();
314     }
315 
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()) = SwPosition( *pStartNode );
320     m_pTableCursor->Move( fnMoveForward, GoInNode );
321 
322     // 2. set mark, and move point to last content node in box
323     m_pTableCursor->SetMark();
324     *(m_pTableCursor->GetPoint()) = SwPosition( *(pStartNode->EndOfSectionNode()) );
325     m_pTableCursor->Move( fnMoveBackward, GoInNode );
326 
327     // 3. exchange
328     m_pTableCursor->Exchange();
329 
330     // with some luck, UpdateCursor() will now update everything that
331     // needs updating
332     UpdateCursor();
333 
334     return true;
335 }
336 
337 // TODO: provide documentation
338 /** get the next non-protected cell inside a table
339 
340     @param[in,out] rIdx is on a table node
341     @param bInReadOnly  ???
342 
343     @return <false> if no suitable cell could be found, otherwise <rIdx> points
344             to content in a suitable cell and <true> is returned.
345 */
lcl_FindNextCell(SwNodeIndex & rIdx,bool bInReadOnly)346 static bool lcl_FindNextCell( SwNodeIndex& rIdx, bool bInReadOnly )
347 {
348     // check protected cells
349     SwNodeIndex aTmp( rIdx, 2 ); // TableNode + StartNode
350 
351     // the resulting cell should be in that table:
352     const SwTableNode* pTableNd = rIdx.GetNode().GetTableNode();
353 
354     if ( !pTableNd )
355     {
356         OSL_FAIL( "lcl_FindNextCell not celled with table start node!" );
357         return false;
358     }
359 
360     const SwNode* pTableEndNode = pTableNd->EndOfSectionNode();
361 
362     SwNodes& rNds = aTmp.GetNode().GetNodes();
363     SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
364 
365     // no content node => go to next content node
366     if( !pCNd )
367         pCNd = rNds.GoNext( &aTmp );
368 
369     // robust
370     if ( !pCNd )
371         return false;
372 
373     SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
374 
375     if ( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
376         (!bInReadOnly && pFrame->IsProtected() ) )
377     {
378         // we are not located inside a 'valid' cell. We have to continue searching...
379 
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 );
383 
384         // loop to find a suitable cell...
385         for( ;; )
386         {
387             SwNode* pNd = &aTmp.GetNode();
388 
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;
394 
395             // ok, get the next content node:
396             pCNd = aTmp.GetNode().GetContentNode();
397             if( nullptr == pCNd )
398                 pCNd = rNds.GoNext( &aTmp );
399 
400             // robust:
401             if ( !pCNd )
402                 return false;
403 
404             // check if we have found a suitable table cell:
405             pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
406 
407             if ( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
408                 (bInReadOnly || !pFrame->IsProtected() ) )
409             {
410                 // finally, we have found a suitable table cell => set index and return
411                 rIdx = *pCNd;
412                 return true;
413             }
414 
415             // continue behind the current section:
416             aTmp.Assign( *pCNd->EndOfSectionNode(), +1 );
417         }
418     }
419     rIdx = *pCNd;
420     return true;
421 }
422 
423 /// see lcl_FindNextCell()
lcl_FindPrevCell(SwNodeIndex & rIdx,bool bInReadOnly)424 static bool lcl_FindPrevCell( SwNodeIndex& rIdx, bool bInReadOnly  )
425 {
426     SwNodeIndex aTmp( rIdx, -2 ); // TableNode + EndNode
427 
428     const SwNode* pTableEndNode = &rIdx.GetNode();
429     const SwTableNode* pTableNd = pTableEndNode->StartOfSectionNode()->GetTableNode();
430 
431     if ( !pTableNd )
432     {
433         OSL_FAIL( "lcl_FindPrevCell not celled with table start node!" );
434         return false;
435     }
436 
437     SwContentNode* pCNd = aTmp.GetNode().GetContentNode();
438 
439     if( !pCNd )
440         pCNd = SwNodes::GoPrevious( &aTmp );
441 
442     if ( !pCNd )
443         return false;
444 
445     SwContentFrame* pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
446 
447     if( nullptr == pFrame || pCNd->FindTableNode() != pTableNd ||
448         (!bInReadOnly && pFrame->IsProtected() ))
449     {
450         // skip before current section
451         aTmp.Assign( *pCNd->StartOfSectionNode(), -1 );
452         for( ;; )
453         {
454             SwNode* pNd = &aTmp.GetNode();
455 
456             if( pNd == pTableNd || pNd->GetIndex() < pTableNd->GetIndex() )
457                 return false;
458 
459             pCNd = aTmp.GetNode().GetContentNode();
460             if( nullptr == pCNd )
461                 pCNd = SwNodes::GoPrevious( &aTmp );
462 
463             if ( !pCNd )
464                 return false;
465 
466             pFrame = pCNd->getLayoutFrame( pCNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout() );
467 
468             if( nullptr != pFrame && pCNd->FindTableNode() == pTableNd &&
469                 (bInReadOnly || !pFrame->IsProtected() ) )
470             {
471                 rIdx = *pCNd;
472                 return true; // ok, not protected
473             }
474             aTmp.Assign( *pCNd->StartOfSectionNode(), - 1 );
475         }
476     }
477     rIdx = *pCNd;
478     return true;
479 }
480 
GotoPrevTable(SwPaM & rCurrentCursor,SwMoveFnCollection const & fnPosTable,bool bInReadOnly)481 bool GotoPrevTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
482                     bool bInReadOnly )
483 {
484     SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
485 
486     SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
487 
488     SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
489     if( pTableNd )
490     {
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;
498 
499         if( pInnerTableNd == pTableNd )
500             aIdx.Assign( *pTableNd, - 1 );
501     }
502 
503     SwNodeIndex aOldIdx = aIdx;
504     sal_uLong nLastNd = rCurrentCursor.GetDoc().GetNodes().Count() - 1;
505     do {
506         while( aIdx.GetIndex() &&
507             nullptr == ( pTableNd = aIdx.GetNode().StartOfSectionNode()->GetTableNode()) )
508         {
509             --aIdx;
510             if ( aIdx == aOldIdx )
511             {
512                 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
513                 return false;
514             }
515         }
516 
517         if ( !aIdx.GetIndex() )
518         {
519             SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
520             aIdx = nLastNd;
521             continue;
522         }
523 
524         {
525             if( &fnPosTable == &fnMoveForward ) // at the beginning?
526             {
527                 aIdx = *aIdx.GetNode().StartOfSectionNode();
528                 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
529                 {
530                     // skip table
531                     aIdx.Assign( *pTableNd, -1 );
532                     continue;
533                 }
534             }
535             else
536             {
537                 // check protected cells
538                 if( !lcl_FindNextCell( aIdx, bInReadOnly ))
539                 {
540                     // skip table
541                     aIdx.Assign( *pTableNd, -1 );
542                     continue;
543                 }
544             }
545 
546             SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
547             if ( pTextNode )
548             {
549                 rCurrentCursor.GetPoint()->nNode = *pTextNode;
550                 rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
551                                                       pTextNode->Len() :
552                                                       0 );
553             }
554             return true;
555         }
556     } while( true );
557 
558     return false;
559 }
560 
GotoNextTable(SwPaM & rCurrentCursor,SwMoveFnCollection const & fnPosTable,bool bInReadOnly)561 bool GotoNextTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
562                     bool bInReadOnly )
563 {
564     SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
565 
566     SwNodeIndex aIdx( rCurrentCursor.GetPoint()->nNode );
567     SwTableNode* pTableNd = aIdx.GetNode().FindTableNode();
568 
569     if( pTableNd )
570         aIdx.Assign( *pTableNd->EndOfSectionNode(), 1 );
571 
572     SwNodeIndex aOldIdx = aIdx;
573     sal_uLong nLastNd = rCurrentCursor.GetDoc().GetNodes().Count() - 1;
574     do {
575         while( aIdx.GetIndex() < nLastNd &&
576                 nullptr == ( pTableNd = aIdx.GetNode().GetTableNode()) )
577         {
578             ++aIdx;
579             if ( aIdx == aOldIdx )
580             {
581                 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
582                 return false;
583             }
584         }
585 
586         if ( aIdx.GetIndex() == nLastNd )
587         {
588             SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
589             aIdx = 0;
590             continue;
591         }
592 
593         assert( pTableNd );  // coverity, should never be nullptr
594 
595         if( &fnPosTable == &fnMoveForward ) // at the beginning?
596         {
597             if( !lcl_FindNextCell( aIdx, bInReadOnly ))
598             {
599                 // skip table
600                 aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
601                 continue;
602             }
603         }
604         else
605         {
606             aIdx = *aIdx.GetNode().EndOfSectionNode();
607             // check protected cells
608             if( !lcl_FindNextCell( aIdx, bInReadOnly ))
609             {
610                 // skip table
611                 aIdx.Assign( *pTableNd->EndOfSectionNode(), + 1 );
612                 continue;
613             }
614         }
615 
616         SwTextNode* pTextNode = aIdx.GetNode().GetTextNode();
617         if ( pTextNode )
618         {
619             rCurrentCursor.GetPoint()->nNode = *pTextNode;
620             rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
621                                                   pTextNode->Len() :
622                                                   0 );
623         }
624         return true;
625 
626     } while( true );
627 
628     // the flow is such that it is not possible to get there
629 
630     return false;
631 }
632 
GotoCurrTable(SwPaM & rCurrentCursor,SwMoveFnCollection const & fnPosTable,bool bInReadOnly)633 bool GotoCurrTable( SwPaM& rCurrentCursor, SwMoveFnCollection const & fnPosTable,
634                     bool bInReadOnly )
635 {
636     SwTableNode* pTableNd = rCurrentCursor.GetPoint()->nNode.GetNode().FindTableNode();
637     if( !pTableNd )
638         return false;
639 
640     SwTextNode* pTextNode = nullptr;
641     if( &fnPosTable == &fnMoveBackward ) // to the end of the table
642     {
643         SwNodeIndex aIdx( *pTableNd->EndOfSectionNode() );
644         if( !lcl_FindPrevCell( aIdx, bInReadOnly ))
645             return false;
646         pTextNode = aIdx.GetNode().GetTextNode();
647     }
648     else
649     {
650         SwNodeIndex aIdx( *pTableNd );
651         if( !lcl_FindNextCell( aIdx, bInReadOnly ))
652             return false;
653         pTextNode = aIdx.GetNode().GetTextNode();
654     }
655 
656     if ( pTextNode )
657     {
658         rCurrentCursor.GetPoint()->nNode = *pTextNode;
659         rCurrentCursor.GetPoint()->nContent.Assign( pTextNode, &fnPosTable == &fnMoveBackward ?
660                                                         pTextNode->Len() :
661                                                         0 );
662     }
663 
664     return true;
665 }
666 
MoveTable(SwWhichTable fnWhichTable,SwMoveFnCollection const & fnPosTable)667 bool SwCursor::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
668 {
669     bool bRet = false;
670     SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this);
671 
672     if( pTableCursor || !HasMark() )
673     {
674         SwCursorSaveState aSaveState( *this );
675         bRet = (*fnWhichTable)( *this, fnPosTable, IsReadOnlyAvailable() ) &&
676                 !IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
677                            SwCursorSelOverFlags::Toggle );
678     }
679     return bRet;
680 }
681 
MoveTable(SwWhichTable fnWhichTable,SwMoveFnCollection const & fnPosTable)682 bool SwCursorShell::MoveTable( SwWhichTable fnWhichTable, SwMoveFnCollection const & fnPosTable )
683 {
684     SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed
685 
686     SwShellCursor* pCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor;
687     bool bCheckPos;
688     bool bRet;
689     sal_uLong nPtNd = 0;
690     sal_Int32 nPtCnt = 0;
691 
692     if ( !m_pTableCursor && m_pCurrentCursor->HasMark() )
693     {
694         // switch to table mode
695         m_pTableCursor = new SwShellTableCursor( *this, *m_pCurrentCursor->GetPoint() );
696         m_pCurrentCursor->DeleteMark();
697         m_pCurrentCursor->SwSelPaintRects::Hide();
698         m_pTableCursor->SetMark();
699         pCursor = m_pTableCursor;
700         bCheckPos = false;
701     }
702     else
703     {
704         bCheckPos = true;
705         nPtNd = pCursor->GetPoint()->nNode.GetIndex();
706         nPtCnt = pCursor->GetPoint()->nContent.GetIndex();
707     }
708 
709     bRet = pCursor->MoveTable( fnWhichTable, fnPosTable );
710 
711     if( bRet )
712     {
713         // #i45028# - set "top" position for repeated headline rows
714         pCursor->GetPtPos() = Point();
715 
716         UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
717 
718         if( bCheckPos &&
719             pCursor->GetPoint()->nNode.GetIndex() == nPtNd &&
720             pCursor->GetPoint()->nContent.GetIndex() == nPtCnt )
721             bRet = false;
722     }
723     return bRet;
724 }
725 
IsTableComplexForChart()726 bool SwCursorShell::IsTableComplexForChart()
727 {
728     bool bRet = false;
729 
730     // Here we may trigger table formatting so we better do that inside an action
731     StartAction();
732     const SwTableNode* pTNd = m_pCurrentCursor->GetPoint()->nNode.GetNode().FindTableNode();
733     if( pTNd )
734     {
735         // in a table; check if table or section is balanced
736         OUString sSel;
737         if( m_pTableCursor )
738             sSel = GetBoxNms();
739         bRet = pTNd->GetTable().IsTableComplexForChart( sSel );
740     }
741     EndAction();
742 
743     return bRet;
744 }
745 
GetBoxNms() const746 OUString SwCursorShell::GetBoxNms() const
747 {
748     OUString sNm;
749     const SwPosition* pPos;
750     SwFrame* pFrame;
751 
752     if( IsTableMode() )
753     {
754         SwContentNode *pCNd = m_pTableCursor->Start()->nNode.GetNode().GetContentNode();
755         pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
756         if( !pFrame )
757             return sNm;
758 
759         do {
760             pFrame = pFrame->GetUpper();
761         } while ( pFrame && !pFrame->IsCellFrame() );
762 
763         OSL_ENSURE( pFrame, "no frame for this box" );
764 
765         if( !pFrame )
766             return sNm;
767 
768         sNm = static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName() + ":";
769         pPos = m_pTableCursor->End();
770     }
771     else
772     {
773         const SwTableNode* pTableNd = IsCursorInTable();
774         if( !pTableNd )
775             return sNm;
776         pPos = GetCursor()->GetPoint();
777     }
778 
779     SwContentNode* pCNd = pPos->nNode.GetNode().GetContentNode();
780     pFrame = pCNd ? pCNd->getLayoutFrame( GetLayout() ) : nullptr;
781 
782     if( pFrame )
783     {
784         do {
785             pFrame = pFrame->GetUpper();
786         } while ( pFrame && !pFrame->IsCellFrame() );
787 
788         if( pFrame )
789             sNm += static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetName();
790     }
791     return sNm;
792 }
793 
GotoTable(const OUString & rName)794 bool SwCursorShell::GotoTable( const OUString& rName )
795 {
796     SwCallLink aLk( *this ); // watch Cursor-Moves
797     bool bRet = !m_pTableCursor && m_pCurrentCursor->GotoTable( rName );
798     if( bRet )
799     {
800         m_pCurrentCursor->GetPtPos() = Point();
801         UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
802                     SwCursorShell::READONLY );
803     }
804     return bRet;
805 }
806 
CheckTableBoxContent(const SwPosition * pPos)807 bool SwCursorShell::CheckTableBoxContent( const SwPosition* pPos )
808 {
809     if( !m_pBoxIdx || !m_pBoxPtr || IsSelTableCells() || !IsAutoUpdateCells() )
810         return false;
811 
812     // check if box content is consistent with given box format, reset if not
813     SwTableBox* pChkBox = nullptr;
814     SwStartNode* pSttNd = nullptr;
815     if( !pPos )
816     {
817         // get stored position
818         if (nullptr != (pSttNd = m_pBoxIdx->GetNode().GetStartNode()) &&
819             SwTableBoxStartNode == pSttNd->GetStartNodeType() &&
820             m_pBoxPtr == pSttNd->FindTableNode()->GetTable().
821                         GetTableBox( m_pBoxIdx->GetIndex() ) )
822             pChkBox = m_pBoxPtr;
823     }
824     else
825     {
826         pSttNd = pPos->nNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
827         if( pSttNd)
828             pChkBox = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
829     }
830 
831     // box has more than one paragraph
832     if( pChkBox && pSttNd->GetIndex() + 2 != pSttNd->EndOfSectionIndex() )
833         pChkBox = nullptr;
834 
835     // destroy pointer before next action starts
836     if( !pPos && !pChkBox )
837         ClearTableBoxContent();
838 
839     // cursor not anymore in this section?
840     if( pChkBox && !pPos &&
841         ( m_pCurrentCursor->HasMark() || m_pCurrentCursor->GetNext() != m_pCurrentCursor ||
842           pSttNd->GetIndex() + 1 == m_pCurrentCursor->GetPoint()->nNode.GetIndex() ))
843         pChkBox = nullptr;
844 
845     // Did the content of a box change at all? This is important if e.g. Undo
846     // could not restore the content properly.
847     if( pChkBox )
848     {
849         const SwTextNode* pNd = GetDoc()->GetNodes()[
850                                     pSttNd->GetIndex() + 1 ]->GetTextNode();
851         if( !pNd ||
852             ( pNd->GetText() == SwViewShell::GetShellRes()->aCalc_Error &&
853               SfxItemState::SET == pChkBox->GetFrameFormat()->
854                             GetItemState( RES_BOXATR_FORMULA )) )
855             pChkBox = nullptr;
856     }
857 
858     if( pChkBox )
859     {
860         // destroy pointer before next action starts
861         ClearTableBoxContent();
862         StartAction();
863         GetDoc()->ChkBoxNumFormat( *pChkBox, true );
864         EndAction();
865     }
866 
867     return nullptr != pChkBox;
868 }
869 
SaveTableBoxContent(const SwPosition * pPos)870 void SwCursorShell::SaveTableBoxContent( const SwPosition* pPos )
871 {
872     if( IsSelTableCells() || !IsAutoUpdateCells() )
873         return ;
874 
875     if( !pPos )
876         pPos = m_pCurrentCursor->GetPoint();
877 
878     SwStartNode* pSttNd = pPos->nNode.GetNode().FindSttNodeByType( SwTableBoxStartNode );
879 
880     bool bCheckBox = false;
881     if( pSttNd && m_pBoxIdx )
882     {
883         if( pSttNd == &m_pBoxIdx->GetNode() )
884             pSttNd = nullptr;
885         else
886             bCheckBox = true;
887     }
888     else
889         bCheckBox = nullptr != m_pBoxIdx;
890 
891     if( bCheckBox )
892     {
893         // check m_pBoxIdx
894         SwPosition aPos( *m_pBoxIdx );
895         CheckTableBoxContent( &aPos );
896     }
897 
898     if( pSttNd )
899     {
900         m_pBoxPtr = pSttNd->FindTableNode()->GetTable().GetTableBox( pSttNd->GetIndex() );
901 
902         if( m_pBoxIdx )
903             *m_pBoxIdx = *pSttNd;
904         else
905             m_pBoxIdx = new SwNodeIndex( *pSttNd );
906     }
907 }
908 
ClearTableBoxContent()909 void SwCursorShell::ClearTableBoxContent()
910 {
911     delete m_pBoxIdx;
912     m_pBoxIdx = nullptr;
913     m_pBoxPtr = nullptr;
914 }
915 
EndAllTableBoxEdit()916 bool SwCursorShell::EndAllTableBoxEdit()
917 {
918     bool bRet = false;
919     for(SwViewShell& rSh : GetRingContainer())
920     {
921         if( auto pCursorShell = dynamic_cast<SwCursorShell *>(&rSh) )
922             bRet |= pCursorShell->CheckTableBoxContent(
923                         pCursorShell->m_pCurrentCursor->GetPoint() );
924 
925     }
926     return bRet;
927 }
928 
929 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
930