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