ITEM: Refactor ItemType
[LibreOffice.git] / sw / source / core / frmedt / fetab.cxx
blob8981a6ac3edd8a99be39337eaab5910275190c05
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 <memory>
21 #include <hintids.hxx>
23 #include <vcl/errinf.hxx>
24 #include <basegfx/vector/b2dvector.hxx>
25 #include <editeng/protitem.hxx>
26 #include <editeng/brushitem.hxx>
27 #include <editeng/frmdiritem.hxx>
28 #include <svtools/ruler.hxx>
29 #include <osl/diagnose.h>
30 #include <swwait.hxx>
31 #include <fmtfsize.hxx>
32 #include <fmtornt.hxx>
33 #include <frmatr.hxx>
34 #include <fesh.hxx>
35 #include <wrtsh.hxx>
36 #include <doc.hxx>
37 #include <docsh.hxx>
38 #include <svx/svdview.hxx>
39 #include <IDocumentState.hxx>
40 #include <IDocumentLayoutAccess.hxx>
41 #include <IDocumentRedlineAccess.hxx>
42 #include <IDocumentUndoRedo.hxx>
43 #include <cntfrm.hxx>
44 #include <txtfrm.hxx>
45 #include <notxtfrm.hxx>
46 #include <rootfrm.hxx>
47 #include <pagefrm.hxx>
48 #include <tabfrm.hxx>
49 #include <rowfrm.hxx>
50 #include <cellfrm.hxx>
51 #include <flyfrm.hxx>
52 #include <swtable.hxx>
53 #include <swddetbl.hxx>
54 #include <ndtxt.hxx>
55 #include <calc.hxx>
56 #include <dialoghelp.hxx>
57 #include <tabcol.hxx>
58 #include <tblafmt.hxx>
59 #include <cellatr.hxx>
60 #include <pam.hxx>
61 #include <viscrs.hxx>
62 #include <tblsel.hxx>
63 #include <swerror.h>
64 #include <swundo.hxx>
65 #include <frmtool.hxx>
66 #include <fmtrowsplt.hxx>
67 #include <node.hxx>
68 #include <sortedobjs.hxx>
69 #include <shellres.hxx>
71 using namespace ::com::sun::star;
73 // also see swtable.cxx
74 #define COLFUZZY 20L
76 static bool IsSame( SwDoc & rDoc, tools::Long nA, tools::Long nB )
78 const SwViewShell *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell();
79 if( !pVSh )
80 return std::abs(nA-nB) <= COLFUZZY;
82 SdrView* pDrawView = const_cast<SdrView*>(pVSh->GetDrawView());
83 const auto nOld = pDrawView->GetHitTolerancePixel();
84 pDrawView->SetHitTolerancePixel( COLFUZZY/4 );
85 bool bRet = std::abs(nA-nB) <= pDrawView->getHitTolLog();
86 pDrawView->SetHitTolerancePixel( nOld );
87 return bRet;
90 namespace {
92 class TableWait
94 const std::unique_ptr<SwWait> m_pWait;
95 // this seems really fishy: do some locking, if an arbitrary number of lines is exceeded
96 static const size_t our_kLineLimit = 20;
97 static bool ShouldWait(size_t nCnt, SwFrame *pFrame, size_t nCnt2)
98 { return our_kLineLimit < nCnt || our_kLineLimit < nCnt2 || (pFrame && our_kLineLimit < pFrame->ImplFindTabFrame()->GetTable()->GetTabLines().size()); }
99 public:
100 TableWait(size_t nCnt, SwFrame *pFrame, SwDocShell &rDocShell, size_t nCnt2 = 0)
101 : m_pWait( ShouldWait(nCnt, pFrame, nCnt2) ? std::make_unique<SwWait>( rDocShell, true ) : nullptr )
107 void SwFEShell::ParkCursorInTab()
109 SwCursor * pSwCursor = GetCursor();
111 OSL_ENSURE(pSwCursor, "no SwCursor");
113 SwPosition aStartPos = *pSwCursor->GetPoint(), aEndPos = aStartPos;
115 /* Search least and greatest position in current cursor ring.
117 for(SwPaM& rTmpCursor : pSwCursor->GetRingContainer())
119 SwCursor* pTmpCursor = static_cast<SwCursor *>(&rTmpCursor);
120 const SwPosition * pPt = pTmpCursor->GetPoint(),
121 * pMk = pTmpCursor->GetMark();
123 if (*pPt < aStartPos)
124 aStartPos = *pPt;
126 if (*pPt > aEndPos)
127 aEndPos = *pPt;
129 if (*pMk < aStartPos)
130 aStartPos = *pMk;
132 if (*pMk > aEndPos)
133 aEndPos = *pMk;
137 KillPams();
139 /* @@@ semantic: SwCursor::operator=() is not implemented @@@ */
141 /* Set cursor to end of selection to ensure IsLastCellInRow works
142 properly. */
144 SwCursor aTmpCursor( aEndPos, nullptr );
145 *pSwCursor = aTmpCursor;
148 /* Move the cursor out of the columns to delete and stay in the
149 same row. If the table has only one column the cursor will
150 stay in the row and the shell will take care of it. */
151 if (IsLastCellInRow())
153 /* If the cursor is in the last row of the table, first
154 try to move it to the previous cell. If that fails move
155 it to the next cell. */
158 SwCursor aTmpCursor( aStartPos, nullptr );
159 *pSwCursor = aTmpCursor;
162 if (! pSwCursor->GoPrevCell())
164 SwCursor aTmpCursor( aEndPos, nullptr );
165 *pSwCursor = aTmpCursor;
166 pSwCursor->GoNextCell();
169 else
171 /* If the cursor is not in the last row of the table, first
172 try to move it to the next cell. If that fails move it
173 to the previous cell. */
176 SwCursor aTmpCursor( aEndPos, nullptr );
177 *pSwCursor = aTmpCursor;
180 if (! pSwCursor->GoNextCell())
182 SwCursor aTmpCursor( aStartPos, nullptr );
183 *pSwCursor = aTmpCursor;
184 pSwCursor->GoPrevCell();
189 void SwFEShell::InsertRow( sal_uInt16 nCnt, bool bBehind )
191 // check if Point/Mark of current cursor are in a table
192 SwFrame *pFrame = GetCurrFrame();
193 if( !pFrame || !pFrame->IsInTab() )
194 return;
196 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
198 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
199 DialogMask::MessageInfo | DialogMask::ButtonsOk );
200 return;
203 // pending drag & drop?
204 bool bAction = ActionPend();
206 CurrShell aCurr( this );
207 StartAllAction();
209 // search boxes via the layout
210 SwSelBoxes aBoxes;
211 bool bSelectAll = StartsWith_() == StartsWith::Table && ExtendedSelectedAll();
212 if (bSelectAll)
214 // Set the end of the selection to the last paragraph of the last cell of the table.
215 SwPaM* pPaM = getShellCursor(false);
216 SwNode* pNode = pPaM->Start()->GetNode().FindTableNode()->EndOfSectionNode();
217 // pNode is the end node of the table, we want the last node before the end node of the last cell.
218 pPaM->End()->Assign( pNode->GetIndex() - 2 );
220 GetTableSel( *this, aBoxes, SwTableSearchType::Row );
222 TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
224 if ( !aBoxes.empty() )
225 GetDoc()->InsertRow( aBoxes, nCnt, bBehind, /*bInsertDummy=*/!bAction );
227 EndAllActionAndCall();
230 void SwFEShell::InsertCol( sal_uInt16 nCnt, bool bBehind )
232 // check if Point/Mark of current cursor are in a table
233 SwFrame *pFrame = GetCurrFrame();
234 if( !pFrame || !pFrame->IsInTab() )
235 return;
237 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
239 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
240 DialogMask::MessageInfo | DialogMask::ButtonsOk );
241 return;
244 CurrShell aCurr( this );
246 if( !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::Col ) )
248 ErrorHandler::HandleError( ERR_TBLINSCOL_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
249 DialogMask::MessageInfo | DialogMask::ButtonsOk );
250 return;
253 // pending drag & drop?
254 bool bAction = ActionPend();
256 StartAllAction();
257 // search boxes via the layout
258 SwSelBoxes aBoxes;
259 GetTableSel( *this, aBoxes, SwTableSearchType::Col );
261 TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
263 if( !aBoxes.empty() )
264 GetDoc()->InsertCol( aBoxes, nCnt, bBehind, /*bInsertDummy=*/!bAction );
266 EndAllActionAndCall();
269 // Determines if the current cursor is in the last row of the table.
270 bool SwFEShell::IsLastCellInRow() const
272 SwTabCols aTabCols;
273 GetTabCols( aTabCols );
274 bool bResult = false;
276 if (IsTableRightToLeft())
277 /* If the table is right-to-left the last row is the most left one. */
278 bResult = 0 == GetCurTabColNum();
279 else
280 /* If the table is left-to-right the last row is the most right one. */
281 bResult = aTabCols.Count() == GetCurTabColNum();
283 return bResult;
286 bool SwFEShell::DeleteCol()
288 // check if Point/Mark of current cursor are in a table
289 SwFrame *pFrame = GetCurrFrame();
290 if( !pFrame || !pFrame->IsInTab() )
291 return false;
293 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
295 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
296 DialogMask::MessageInfo | DialogMask::ButtonsOk );
297 return false;
300 CurrShell aCurr( this );
302 bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording();
303 bool bRecordAndHideChanges = bRecordChanges &&
304 GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
306 // tracked deletion: remove only textbox content,
307 // and set IsNoTracked table box property to false
308 if ( bRecordChanges )
310 StartUndo(SwUndoId::COL_DELETE);
311 StartAllAction();
313 if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
314 pWrtShell->SelectTableCol();
316 // search boxes via the layout
317 SwSelBoxes aBoxes;
318 GetTableSel( *this, aBoxes, SwTableSearchType::Col );
320 TableWait aWait( 20, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
322 SwTableNode* pTableNd = pFrame->IsTextFrame()
323 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode()
324 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode();
326 for (size_t i = 0; i < aBoxes.size(); ++i)
328 SwTableBox *pBox = aBoxes[i];
329 if ( pBox->GetSttNd() )
331 SwNodeIndex aIdx( *pBox->GetSttNd(), 1 );
332 SwCursor aCursor( SwPosition(aIdx), nullptr );
333 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
334 GetDoc()->SetBoxAttr( aCursor, aHasTextChangesOnly );
336 // add dummy text content to the empty box for change tracking
337 if ( pBox->IsEmpty() )
339 IDocumentContentOperations& rIDCO = GetDoc()->getIDocumentContentOperations();
340 IDocumentRedlineAccess& rIDRA = GetDoc()->getIDocumentRedlineAccess();
341 RedlineFlags eOld = rIDRA.GetRedlineFlags();
342 rIDRA.SetRedlineFlags_intern(RedlineFlags::NONE);
343 rIDCO.InsertString( aCursor, OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
344 aCursor.SetMark();
345 aCursor.GetMark()->SetContent(0);
346 rIDRA.SetRedlineFlags_intern( eOld );
347 rIDCO.DeleteAndJoin( aCursor );
353 SwEditShell* pEditShell = GetDoc()->GetEditShell();
354 pEditShell->Delete();
356 // remove cell frames in Hide Changes mode (and table frames, if needed)
357 if ( bRecordAndHideChanges )
359 // remove all frames of the table, and make them again without the deleted ones
360 // TODO remove only the deleted frames
361 pTableNd->DelFrames();
363 if ( !pTableNd->GetTable().IsDeleted() )
365 pTableNd->MakeOwnFrames();
369 EndAllActionAndCall();
370 EndUndo(SwUndoId::COL_DELETE);
371 return true;
374 StartAllAction();
376 // search boxes via the layout
377 bool bRet;
378 SwSelBoxes aBoxes;
379 SwTableSearchType eSearchType = SwTableSearchType::Col;
381 // NewModel tables already ExpandColumnSelection, so don't do it here also.
382 const SwContentNode* pContentNd = getShellCursor(false)->GetPointNode().GetContentNode();
383 const SwTableNode* pTableNd = pContentNd ? pContentNd->FindTableNode() : nullptr;
384 if (pTableNd && pTableNd->GetTable().IsNewModel())
385 eSearchType = SwTableSearchType::NONE;
387 GetTableSel(*this, aBoxes, eSearchType);
388 if ( !aBoxes.empty() )
390 TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
392 // remove crsr from the deletion area.
393 // Put them behind/on the table; via the
394 // document position they will be put to the old position
395 while( !pFrame->IsCellFrame() )
396 pFrame = pFrame->GetUpper();
398 ParkCursorInTab();
400 // then delete the column
401 StartUndo(SwUndoId::COL_DELETE);
402 bRet = GetDoc()->DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
403 EndUndo(SwUndoId::COL_DELETE);
405 else
406 bRet = false;
408 EndAllActionAndCall();
409 return bRet;
412 void SwFEShell::DeleteTable()
414 DeleteRow(true);
417 bool SwFEShell::DeleteRow(bool bCompleteTable)
419 // check if Point/Mark of current cursor are in a table
420 SwFrame *pFrame = GetCurrFrame();
421 if( !pFrame || !pFrame->IsInTab() )
422 return false;
424 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
426 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
427 DialogMask::MessageInfo | DialogMask::ButtonsOk );
428 return false;
431 CurrShell aCurr( this );
433 bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording();
434 bool bRecordAndHideChanges = bRecordChanges &&
435 GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
437 // tracked deletion: all rows have already had tracked row change in the table selection
438 if ( bRecordChanges && !SwDoc::HasRowNotTracked( *getShellCursor( false ) ) )
439 return false;
441 if ( bRecordChanges )
442 StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
444 StartAllAction();
446 // tracked deletion: remove only textbox content,
447 // and set HasTextChangesOnly table line property to false
448 SwEditShell* pEditShell = nullptr;
449 if ( bRecordChanges )
451 pEditShell = GetDoc()->GetEditShell();
452 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
453 GetDoc()->SetRowNotTracked( *getShellCursor( false ), aHasTextChangesOnly );
455 if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
456 pWrtShell->SelectTableRow();
458 // don't need to remove the row frames in Show Changes mode
459 if ( !bRecordAndHideChanges )
461 if ( pEditShell )
462 pEditShell->Delete(false);
464 EndAllActionAndCall();
465 EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
467 return true;
471 // search for boxes via the layout
472 bool bRet;
473 SwSelBoxes aBoxes;
474 GetTableSel( *this, aBoxes, SwTableSearchType::Row );
476 if( !aBoxes.empty() )
478 TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
480 // Delete cursors from the deletion area.
481 // Then the cursor is:
482 // 1. the following row, if there is another row after this
483 // 2. the preceding row, if there is another row before this
484 // 3. otherwise below the table
486 SwTableNode* pTableNd = pFrame->IsTextFrame()
487 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode()
488 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode();
490 // search all boxes / lines
491 FndBox_ aFndBox( nullptr, nullptr );
493 FndPara aPara( aBoxes, &aFndBox );
494 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
497 if( aFndBox.GetLines().empty() )
499 EndAllActionAndCall();
500 return false;
503 KillPams();
505 FndBox_* pFndBox = &aFndBox;
506 while( 1 == pFndBox->GetLines().size() &&
507 1 == pFndBox->GetLines().front()->GetBoxes().size())
509 FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
510 if( pTmp->GetBox()->GetSttNd() )
511 break; // otherwise too far
512 pFndBox = pTmp;
515 SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
516 SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
517 while( !pDelBox->GetSttNd() )
519 SwTableLine* pLn = pDelBox->GetTabLines().back();
520 pDelBox = pLn->GetTabBoxes().back();
522 SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
523 pDelBox );
524 // skip deleted lines in Hide Changes mode with enabled change tracking
525 if ( bRecordAndHideChanges )
527 SwRedlineTable::size_type nRedlinePos = 0;
528 while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) )
529 pNextBox = pNextBox->GetUpper()->FindNextBox( pTableNd->GetTable(),
530 pNextBox->GetUpper()->GetTabBoxes().back() );
533 // skip protected cells
534 while( pNextBox &&
535 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
536 pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
538 if( !pNextBox ) // no next? then the previous
540 pDelLine = pFndBox->GetLines().front()->GetLine();
541 pDelBox = pDelLine->GetTabBoxes()[ 0 ];
542 while( !pDelBox->GetSttNd() )
543 pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
544 pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
545 pDelBox );
546 // skip previous deleted lines in Hide Changes mode with enabled change tracking
547 if ( bRecordAndHideChanges )
549 SwRedlineTable::size_type nRedlinePos = 0;
550 while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) )
552 pNextBox = pNextBox->GetUpper()->FindPreviousBox( pTableNd->GetTable(),
553 pNextBox->GetUpper()->GetTabBoxes()[0] );
554 nRedlinePos = 0;
558 // skip previous protected cells
559 while( pNextBox &&
560 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
561 pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
564 // delete row content in Hide Changes mode
565 if ( pEditShell && bRecordAndHideChanges )
567 // select the row deleted with change tracking cell by cell to handle
568 // the already deleted cells
569 SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this);
570 for (SwSelBoxes::size_type nBox = 0; pWrtShell && nBox < aBoxes.size(); ++nBox)
572 pWrtShell->SelectTableRow();
573 SwCursor* pTableCursor = static_cast<SwCursor*>(GetTableCursor());
574 auto pStart = aBoxes[nBox];
575 if ( !pTableCursor )
576 pTableCursor = GetCursor(true);
578 if ( pTableCursor )
580 // set start and end of the selection
581 pTableCursor->DeleteMark();
582 pTableCursor->GetPoint()->Assign( *pStart->GetSttNd()->EndOfSectionNode() );
583 pTableCursor->Move( fnMoveBackward, GoInContent );
584 pWrtShell->UpdateCursor();
587 pEditShell->Delete(false);
591 SwNodeOffset nIdx;
592 if( pNextBox ) // put cursor here
593 nIdx = pNextBox->GetSttIdx() + 1;
594 else // otherwise below the table
595 nIdx = pTableNd->EndOfSectionIndex() + 1;
597 SwNodeIndex aIdx( GetDoc()->GetNodes(), nIdx );
598 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
599 if( !pCNd )
600 pCNd = SwNodes::GoNext(&aIdx);
602 // remove row frames in Hide Changes mode (and table frames, if needed)
603 if ( bRecordAndHideChanges )
605 // remove all frames of the table, and make them again without the deleted ones
606 // TODO remove only the deleted frames
607 pTableNd->DelFrames();
608 if ( !pTableNd->GetTable().IsDeleted() )
610 pTableNd->MakeOwnFrames();
613 EndAllActionAndCall();
615 // put cursor
616 SwPaM* pPam = GetCursor();
617 pPam->GetPoint()->Assign( *pCNd, 0 );
618 pPam->SetMark(); // both want something
619 pPam->DeleteMark();
620 if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
622 pWrtShell->UpdateCursor();
623 // tdf#150578 enable the disabled table toolbar by (zero) cursor moving
624 pWrtShell->Right( SwCursorSkipMode::Chars, false, 0, false );
627 EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
628 return true;
630 else if( pCNd )
632 // put cursor
633 SwPaM* pPam = GetCursor();
634 pPam->GetPoint()->Assign( *pCNd, 0 );
635 pPam->SetMark(); // both want something
636 pPam->DeleteMark();
640 // now delete the lines
641 StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
642 bRet = GetDoc()->DeleteRowCol( aBoxes );
643 EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
645 else
646 bRet = false;
648 EndAllActionAndCall();
649 return bRet;
652 TableMergeErr SwFEShell::MergeTab()
654 // check if Point/Mark of current cursor are in a table
655 TableMergeErr nRet = TableMergeErr::NoSelection;
656 if( IsTableMode() )
658 SwShellTableCursor* pTableCursor = GetTableCursor();
659 const SwTableNode* pTableNd = pTableCursor->GetPointNode().FindTableNode();
660 if( dynamic_cast< const SwDDETable* >(&pTableNd->GetTable()) != nullptr )
662 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
663 DialogMask::MessageInfo | DialogMask::ButtonsOk );
665 else
667 CurrShell aCurr( this );
668 StartAllAction();
670 TableWait aWait(pTableCursor->GetSelectedBoxesCount(), nullptr,
671 *GetDoc()->GetDocShell(),
672 pTableNd->GetTable().GetTabLines().size() );
674 nRet = GetDoc()->MergeTable( *pTableCursor );
676 KillPams();
678 EndAllActionAndCall();
681 return nRet;
684 void SwFEShell::SplitTab( bool bVert, sal_uInt16 nCnt, bool bSameHeight )
686 // check if Point/Mark of current cursor are in a table
687 SwFrame *pFrame = GetCurrFrame();
688 if( !pFrame || !pFrame->IsInTab() )
689 return;
691 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
693 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
694 DialogMask::MessageInfo | DialogMask::ButtonsOk );
695 return;
698 CurrShell aCurr( this );
700 if( bVert && !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::NONE ) )
702 ErrorHandler::HandleError( ERR_TBLSPLIT_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
703 DialogMask::MessageInfo | DialogMask::ButtonsOk );
704 return;
706 StartAllAction();
707 // search boxes via the layout
708 SwSelBoxes aBoxes;
709 GetTableSel( *this, aBoxes );
710 if( !aBoxes.empty() )
712 TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
714 // now delete the columns
715 GetDoc()->SplitTable( aBoxes, bVert, nCnt, bSameHeight );
717 ClearFEShellTabCols(*GetDoc(), nullptr);
719 EndAllActionAndCall();
722 void SwFEShell::GetTabCols_(SwTabCols &rToFill, const SwFrame *pBox) const
724 const SwTabFrame *pTab = pBox->FindTabFrame();
725 if (m_pColumnCache)
727 bool bDel = true;
728 if (m_pColumnCache->pLastTable == pTab->GetTable())
730 bDel = false;
731 SwRectFnSet aRectFnSet(pTab);
733 const SwPageFrame* pPage = pTab->FindPageFrame();
734 const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
735 aRectFnSet.GetLeft(pPage->getFrameArea());
736 const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
737 aRectFnSet.GetLeft(pPage->getFrameArea());
739 if (m_pColumnCache->pLastTabFrame != pTab)
741 // if TabFrame was changed, we only shift a little bit
742 // as the width is the same
743 SwRectFnSet fnRectX(m_pColumnCache->pLastTabFrame);
744 if (fnRectX.GetWidth(m_pColumnCache->pLastTabFrame->getFrameArea()) ==
745 aRectFnSet.GetWidth(pTab->getFrameArea()) )
747 m_pColumnCache->pLastCols->SetLeftMin( nLeftMin );
749 m_pColumnCache->pLastTabFrame = pTab;
751 else
752 bDel = true;
755 if ( !bDel &&
756 m_pColumnCache->pLastCols->GetLeftMin () == o3tl::narrowing<sal_uInt16>(nLeftMin) &&
757 m_pColumnCache->pLastCols->GetLeft () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetLeft(pTab->getFramePrintArea())) &&
758 m_pColumnCache->pLastCols->GetRight () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetRight(pTab->getFramePrintArea()))&&
759 m_pColumnCache->pLastCols->GetRightMax() == o3tl::narrowing<sal_uInt16>(nRightMax) - m_pColumnCache->pLastCols->GetLeftMin() )
761 if (m_pColumnCache->pLastCellFrame != pBox)
763 pTab->GetTable()->GetTabCols( *m_pColumnCache->pLastCols,
764 static_cast<const SwCellFrame*>(pBox)->GetTabBox(), true);
765 m_pColumnCache->pLastCellFrame = pBox;
767 rToFill = *m_pColumnCache->pLastCols;
769 else
770 bDel = true;
772 if ( bDel )
773 m_pColumnCache.reset();
775 if (!m_pColumnCache)
777 SwDoc::GetTabCols( rToFill, static_cast<const SwCellFrame*>(pBox) );
779 m_pColumnCache.reset(new SwColCache);
780 m_pColumnCache->pLastCols.reset(new SwTabCols(rToFill));
781 m_pColumnCache->pLastTable = pTab->GetTable();
782 m_pColumnCache->pLastTabFrame = pTab;
783 m_pColumnCache->pLastCellFrame = pBox;
787 void SwFEShell::GetTabRows_(SwTabCols &rToFill, const SwFrame *pBox) const
789 const SwTabFrame *pTab = pBox->FindTabFrame();
790 if (m_pRowCache)
792 bool bDel = true;
793 if (m_pRowCache->pLastTable == pTab->GetTable())
795 bDel = false;
796 SwRectFnSet aRectFnSet(pTab);
797 const SwPageFrame* pPage = pTab->FindPageFrame();
798 const tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
799 pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
800 pTab->GetPrtTop() - pPage->getFrameArea().Top() );
801 const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
802 const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
803 const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
805 if (m_pRowCache->pLastTabFrame != pTab || m_pRowCache->pLastCellFrame != pBox)
806 bDel = true;
808 if ( !bDel &&
809 m_pRowCache->pLastCols->GetLeftMin () == nLeftMin &&
810 m_pRowCache->pLastCols->GetLeft () == nLeft &&
811 m_pRowCache->pLastCols->GetRight () == nRight &&
812 m_pRowCache->pLastCols->GetRightMax() == nRightMax )
814 rToFill = *m_pRowCache->pLastCols;
816 else
817 bDel = true;
819 if ( bDel )
820 m_pRowCache.reset();
822 if (!m_pRowCache)
824 SwDoc::GetTabRows( rToFill, static_cast<const SwCellFrame*>(pBox) );
826 m_pRowCache.reset(new SwColCache);
827 m_pRowCache->pLastCols.reset(new SwTabCols(rToFill));
828 m_pRowCache->pLastTable = pTab->GetTable();
829 m_pRowCache->pLastTabFrame = pTab;
830 m_pRowCache->pLastCellFrame = pBox;
834 void SwFEShell::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly )
836 SwFrame *pBox = GetCurrFrame();
837 if( !pBox || !pBox->IsInTab() )
838 return;
840 CurrShell aCurr( this );
841 StartAllAction();
845 pBox = pBox->GetUpper();
846 } while (pBox && !pBox->IsCellFrame());
848 GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<SwCellFrame*>(pBox) );
849 EndAllActionAndCall();
852 void SwFEShell::GetTabCols( SwTabCols &rToFill ) const
854 const SwFrame *pFrame = GetCurrFrame();
855 if( !pFrame || !pFrame->IsInTab() )
856 return;
859 pFrame = pFrame->GetUpper();
861 while (pFrame && !pFrame->IsCellFrame());
863 if (!pFrame)
864 return;
866 GetTabCols_( rToFill, pFrame );
869 void SwFEShell::GetTabRows( SwTabCols &rToFill ) const
871 const SwFrame *pFrame = GetCurrFrame();
872 if( !pFrame || !pFrame->IsInTab() )
873 return;
876 pFrame = pFrame->GetUpper();
877 } while (pFrame && !pFrame->IsCellFrame());
879 if (!pFrame)
880 return;
882 GetTabRows_( rToFill, pFrame );
885 void SwFEShell::SetTabRows( const SwTabCols &rNew, bool bCurColOnly )
887 SwFrame *pBox = GetCurrFrame();
888 if( !pBox || !pBox->IsInTab() )
889 return;
891 CurrShell aCurr( this );
892 StartAllAction();
896 pBox = pBox->GetUpper();
897 } while (pBox && !pBox->IsCellFrame());
899 GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<SwCellFrame*>(pBox) );
900 EndAllActionAndCall();
903 void SwFEShell::GetMouseTabRows( SwTabCols &rToFill, const Point &rPt ) const
905 const SwFrame *pBox = GetBox( rPt );
906 if ( pBox )
907 GetTabRows_( rToFill, pBox );
910 void SwFEShell::SetMouseTabRows( const SwTabCols &rNew, bool bCurColOnly, const Point &rPt )
912 const SwFrame *pBox = GetBox( rPt );
913 if( pBox )
915 CurrShell aCurr( this );
916 StartAllAction();
917 GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<const SwCellFrame*>(pBox) );
918 EndAllActionAndCall();
922 void SwFEShell::SetRowSplit( const SwFormatRowSplit& rNew )
924 CurrShell aCurr( this );
925 StartAllAction();
926 GetDoc()->SetRowSplit( *getShellCursor( false ), rNew );
927 EndAllActionAndCall();
930 std::unique_ptr<SwFormatRowSplit> SwFEShell::GetRowSplit() const
932 return SwDoc::GetRowSplit( *getShellCursor( false ) );
935 void SwFEShell::SetRowHeight( const SwFormatFrameSize &rNew )
937 CurrShell aCurr( this );
938 StartAllAction();
939 GetDoc()->SetRowHeight( *getShellCursor( false ), rNew );
940 EndAllActionAndCall();
943 std::unique_ptr<SwFormatFrameSize> SwFEShell::GetRowHeight() const
945 return SwDoc::GetRowHeight( *getShellCursor( false ) );
948 bool SwFEShell::BalanceRowHeight( bool bTstOnly, const bool bOptimize )
950 CurrShell aCurr( this );
951 if( !bTstOnly )
952 StartAllAction();
953 bool bRet = GetDoc()->BalanceRowHeight( *getShellCursor( false ), bTstOnly, bOptimize );
954 if( !bTstOnly )
955 EndAllActionAndCall();
956 return bRet;
959 void SwFEShell::SetRowBackground( const SvxBrushItem &rNew )
961 CurrShell aCurr( this );
962 StartAllAction();
963 GetDoc()->SetRowBackground( *getShellCursor( false ), rNew );
964 EndAllActionAndCall();
967 bool SwFEShell::GetRowBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
969 return SwDoc::GetRowBackground( *getShellCursor( false ), rToFill );
972 void SwFEShell::SetTabBorders( const SfxItemSet& rSet )
974 CurrShell aCurr( this );
975 StartAllAction();
976 GetDoc()->SetTabBorders( *getShellCursor( false ), rSet );
977 EndAllActionAndCall();
980 void SwFEShell::SetTabLineStyle( const Color* pColor, bool bSetLine,
981 const editeng::SvxBorderLine* pBorderLine )
983 CurrShell aCurr( this );
984 StartAllAction();
985 GetDoc()->SetTabLineStyle( *getShellCursor( false ),
986 pColor, bSetLine, pBorderLine );
987 EndAllActionAndCall();
990 void SwFEShell::GetTabBorders( SfxItemSet& rSet ) const
992 SwDoc::GetTabBorders( *getShellCursor( false ), rSet );
995 void SwFEShell::SetBoxBackground( const SvxBrushItem &rNew )
997 CurrShell aCurr( this );
998 StartAllAction();
999 GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew );
1000 EndAllActionAndCall();
1003 bool SwFEShell::GetBoxBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
1005 std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill);
1006 bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp));
1007 rToFill.reset(static_cast<SvxBrushItem*>(aTemp.release()));
1008 return bRetval;
1011 void SwFEShell::SetBoxDirection( const SvxFrameDirectionItem& rNew )
1013 CurrShell aCurr( this );
1014 StartAllAction();
1015 GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew );
1016 EndAllActionAndCall();
1019 bool SwFEShell::GetBoxDirection( std::unique_ptr<SvxFrameDirectionItem>& rToFill ) const
1021 std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill);
1022 bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp));
1023 rToFill.reset(static_cast<SvxFrameDirectionItem*>(aTemp.release()));
1024 return bRetval;
1027 void SwFEShell::SetBoxAlign( sal_uInt16 nAlign )
1029 CurrShell aCurr( this );
1030 StartAllAction();
1031 GetDoc()->SetBoxAlign( *getShellCursor( false ), nAlign );
1032 EndAllActionAndCall();
1035 sal_uInt16 SwFEShell::GetBoxAlign() const
1037 return SwDoc::GetBoxAlign( *getShellCursor( false ) );
1040 void SwFEShell::SetTabBackground( const SvxBrushItem &rNew )
1042 SwFrame *pFrame = GetCurrFrame();
1043 if( !pFrame || !pFrame->IsInTab() )
1044 return;
1046 CurrShell aCurr( this );
1047 StartAllAction();
1048 GetDoc()->SetAttr( rNew, *pFrame->ImplFindTabFrame()->GetFormat() );
1049 EndAllAction(); // no call, nothing changes!
1050 GetDoc()->getIDocumentState().SetModified();
1053 void SwFEShell::GetTabBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
1055 SwFrame *pFrame = GetCurrFrame();
1056 if( pFrame && pFrame->IsInTab() )
1057 rToFill = pFrame->ImplFindTabFrame()->GetFormat()->makeBackgroundBrushItem();
1060 bool SwFEShell::HasWholeTabSelection() const
1062 // whole table selected?
1063 if ( IsTableMode() )
1065 SwSelBoxes aBoxes;
1066 ::GetTableSelCrs( *this, aBoxes );
1067 if( !aBoxes.empty() )
1069 const SwTableNode *pTableNd = IsCursorInTable();
1070 return pTableNd &&
1071 aBoxes[0]->GetSttIdx() - 1 == pTableNd->EndOfSectionNode()->StartOfSectionIndex() &&
1072 aBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1 == pTableNd->EndOfSectionIndex();
1075 return false;
1078 bool SwFEShell::HasBoxSelection() const
1080 if(!IsCursorInTable())
1081 return false;
1082 // whole table selected?
1083 if( IsTableMode() )
1084 return true;
1085 SwPaM* pPam = GetCursor();
1086 // empty boxes are also selected as the absence of selection
1087 bool bChg = false;
1088 if( pPam->GetPoint() == pPam->End())
1090 bChg = true;
1091 pPam->Exchange();
1093 SwNode* pNd;
1094 if( pPam->GetPoint()->GetNodeIndex() -1 ==
1095 ( pNd = &pPam->GetPointNode())->StartOfSectionIndex() &&
1096 !pPam->GetPoint()->GetContentIndex() &&
1097 pPam->GetMark()->GetNodeIndex() + 1 ==
1098 pNd->EndOfSectionIndex())
1100 SwNodeIndex aIdx( *pNd->EndOfSectionNode(), -1 );
1101 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1102 if( !pCNd )
1104 pCNd = SwNodes::GoPrevious( &aIdx );
1105 assert(pCNd && "no ContentNode in box ??");
1107 if( pPam->GetMark()->GetContentIndex() == pCNd->Len() )
1109 if( bChg )
1110 pPam->Exchange();
1111 return true;
1114 if( bChg )
1115 pPam->Exchange();
1116 return false;
1119 void SwFEShell::ProtectCells()
1121 SvxProtectItem aProt( RES_PROTECT );
1122 aProt.SetContentProtect( true );
1124 CurrShell aCurr( this );
1125 StartAllAction();
1127 GetDoc()->SetBoxAttr( *getShellCursor( false ), aProt );
1129 if( !IsCursorReadonly() )
1131 if( IsTableMode() )
1132 ClearMark();
1133 ParkCursorInTab();
1135 EndAllActionAndCall();
1138 // cancel table selection
1139 void SwFEShell::UnProtectCells()
1141 CurrShell aCurr( this );
1142 StartAllAction();
1144 SwSelBoxes aBoxes;
1145 if( IsTableMode() )
1146 ::GetTableSelCrs( *this, aBoxes );
1147 else
1149 SwFrame *pFrame = GetCurrFrame();
1150 do {
1151 pFrame = pFrame->GetUpper();
1152 } while ( pFrame && !pFrame->IsCellFrame() );
1153 if( pFrame )
1155 SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1156 aBoxes.insert( pBox );
1160 if( !aBoxes.empty() )
1161 GetDoc()->UnProtectCells( aBoxes );
1163 EndAllActionAndCall();
1166 void SwFEShell::UnProtectTables()
1168 CurrShell aCurr( this );
1169 StartAllAction();
1170 GetDoc()->UnProtectTables( *GetCursor() );
1171 EndAllActionAndCall();
1174 bool SwFEShell::HasTableAnyProtection( const OUString* pTableName,
1175 bool* pFullTableProtection )
1177 return GetDoc()->HasTableAnyProtection( GetCursor()->GetPoint(), pTableName,
1178 pFullTableProtection );
1181 bool SwFEShell::CanUnProtectCells() const
1183 bool bUnProtectAvailable = false;
1184 const SwTableNode *pTableNd = IsCursorInTable();
1185 if( pTableNd && !pTableNd->IsProtect() )
1187 SwSelBoxes aBoxes;
1188 if( IsTableMode() )
1189 ::GetTableSelCrs( *this, aBoxes );
1190 else
1192 SwFrame *pFrame = GetCurrFrame();
1193 do {
1194 if ( pFrame )
1195 pFrame = pFrame->GetUpper();
1196 } while ( pFrame && !pFrame->IsCellFrame() );
1197 if( pFrame )
1199 SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1200 aBoxes.insert( pBox );
1203 if( !aBoxes.empty() )
1204 bUnProtectAvailable = ::HasProtectedCells( aBoxes );
1206 return bUnProtectAvailable;
1209 sal_uInt16 SwFEShell::GetRowsToRepeat() const
1211 const SwFrame *pFrame = GetCurrFrame();
1212 const SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr;
1213 if( pTab )
1214 return pTab->GetTable()->GetRowsToRepeat();
1215 return 0;
1218 void SwFEShell::SetRowsToRepeat( sal_uInt16 nSet )
1220 SwFrame *pFrame = GetCurrFrame();
1221 SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr;
1222 if( pTab && pTab->GetTable()->GetRowsToRepeat() != nSet )
1224 SwWait aWait( *GetDoc()->GetDocShell(), true );
1225 CurrShell aCurr( this );
1226 StartAllAction();
1227 GetDoc()->SetRowsToRepeat( *pTab->GetTable(), nSet );
1228 EndAllActionAndCall();
1232 // returns the number of rows consecutively selected from top
1233 static sal_uInt16 lcl_GetRowNumber( const SwPosition& rPos )
1235 Point aTmpPt;
1236 const SwContentNode *pNd;
1237 const SwContentFrame *pFrame;
1239 std::pair<Point, bool> const tmp(aTmpPt, false);
1240 pNd = rPos.GetNode().GetContentNode();
1241 if( nullptr != pNd )
1242 pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp);
1243 else
1244 pFrame = nullptr;
1246 const SwFrame* pRow = (pFrame && pFrame->IsInTab()) ? pFrame->GetUpper() : nullptr;
1248 while (pRow && (!pRow->GetUpper() || !pRow->GetUpper()->IsTabFrame()))
1249 pRow = pRow->GetUpper();
1251 if (!pRow)
1252 return USHRT_MAX;
1254 const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper());
1255 const SwTableLine* pTabLine = static_cast<const SwRowFrame*>(pRow)->GetTabLine();
1256 sal_uInt16 nRet = USHRT_MAX;
1257 sal_uInt16 nI = 0;
1258 while ( sal::static_int_cast<SwTableLines::size_type>(nI) < pTabFrame->GetTable()->GetTabLines().size() )
1260 if ( pTabFrame->GetTable()->GetTabLines()[ nI ] == pTabLine )
1262 nRet = nI;
1263 break;
1265 ++nI;
1268 return nRet;
1271 sal_uInt16 SwFEShell::GetRowSelectionFromTop() const
1273 sal_uInt16 nRet = 0;
1274 const SwPaM* pPaM = IsTableMode() ? GetTableCursor() : GetCursor_();
1275 const sal_uInt16 nPtLine = lcl_GetRowNumber( *pPaM->GetPoint() );
1277 if ( !IsTableMode() )
1279 nRet = 0 == nPtLine ? 1 : 0;
1281 else
1283 const sal_uInt16 nMkLine = lcl_GetRowNumber( *pPaM->GetMark() );
1285 if ( ( nPtLine == 0 && nMkLine != USHRT_MAX ) ||
1286 ( nMkLine == 0 && nPtLine != USHRT_MAX ) )
1288 nRet = std::max( nPtLine, nMkLine ) + 1;
1292 return nRet;
1296 * 1. case: bRepeat = true
1297 * returns true if the current frame is located inside a table headline in
1298 * a follow frame
1300 * 2. case: bRepeat = false
1301 * returns true if the current frame is located inside a table headline OR
1302 * inside the first line of a table!!!
1304 bool SwFEShell::CheckHeadline( bool bRepeat ) const
1306 bool bRet = false;
1307 if ( !IsTableMode() )
1309 SwFrame *pFrame = GetCurrFrame(); // DONE MULTIIHEADER
1310 SwTabFrame* pTab = (pFrame && pFrame->IsInTab()) ? pFrame->FindTabFrame() : nullptr;
1311 if (pTab)
1313 if ( bRepeat )
1315 bRet = pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
1317 else
1319 if (SwFrame* pLower = pTab->Lower())
1321 bRet = static_cast<SwLayoutFrame*>(pLower)->IsAnLower( pFrame ) ||
1322 pTab->IsInHeadline( *pFrame );
1327 return bRet;
1330 void SwFEShell::AdjustCellWidth( const bool bBalance, const bool bNoShrink )
1332 CurrShell aCurr( this );
1333 StartAllAction();
1335 // switch on wait-cursor, as we do not know how
1336 // much content is affected
1337 TableWait aWait(std::numeric_limits<size_t>::max(), nullptr,
1338 *GetDoc()->GetDocShell());
1340 GetDoc()->AdjustCellWidth( *getShellCursor( false ), bBalance, bNoShrink );
1341 EndAllActionAndCall();
1344 bool SwFEShell::IsAdjustCellWidthAllowed( bool bBalance ) const
1346 // at least one row with content should be contained in the selection
1348 SwFrame *pFrame = GetCurrFrame();
1349 if( !pFrame || !pFrame->IsInTab() )
1350 return false;
1352 SwSelBoxes aBoxes;
1353 ::GetTableSelCrs( *this, aBoxes );
1355 if ( bBalance )
1356 return aBoxes.size() > 1;
1358 if ( aBoxes.empty() )
1362 pFrame = pFrame->GetUpper();
1364 while (pFrame && !pFrame->IsCellFrame());
1366 if (!pFrame)
1367 return false;
1369 SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1370 aBoxes.insert( pBox );
1373 for (size_t i = 0; i < aBoxes.size(); ++i)
1375 SwTableBox *pBox = aBoxes[i];
1376 if ( pBox->GetSttNd() )
1378 SwNodeIndex aIdx( *pBox->GetSttNd(), 1 );
1379 SwTextNode* pCNd = aIdx.GetNode().GetTextNode();
1380 if( !pCNd )
1381 pCNd = static_cast<SwTextNode*>(SwNodes::GoNext(&aIdx));
1383 while ( pCNd )
1385 if (!pCNd->GetText().isEmpty())
1386 return true;
1387 ++aIdx;
1388 pCNd = aIdx.GetNode().GetTextNode();
1392 return false;
1395 void SwFEShell::SetTableStyle(const OUString& rStyleName)
1397 // make sure SwDoc has the style
1398 SwTableAutoFormat *pTableFormat = GetDoc()->GetTableStyles().FindAutoFormat(rStyleName);
1399 if (!pTableFormat)
1400 return;
1402 SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
1403 if (!pTableNode)
1404 return;
1406 // set the name & update
1407 UpdateTableStyleFormatting(pTableNode, false, &rStyleName);
1410 bool SwFEShell::ResetTableStyle()
1412 SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
1413 if (!pTableNode)
1414 return false;
1416 OUString takingAddressOfRValue;
1417 return UpdateTableStyleFormatting(pTableNode, false, &takingAddressOfRValue);
1420 // AutoFormat for the table/table selection
1421 bool SwFEShell::SetTableStyle(const SwTableAutoFormat& rStyle)
1423 // make sure SwDoc has the style
1424 GetDoc()->GetTableStyles().AddAutoFormat(rStyle);
1426 SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
1427 if (!pTableNode)
1428 return false;
1430 // set the name & update
1431 return UpdateTableStyleFormatting(pTableNode, false, &rStyle.GetName());
1434 bool SwFEShell::UpdateTableStyleFormatting(SwTableNode *pTableNode,
1435 bool bResetDirect, OUString const*const pStyleName)
1437 if (!pTableNode)
1439 pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
1440 if (!pTableNode || pTableNode->GetTable().IsTableComplex())
1441 return false;
1444 OUString const aTableStyleName(pStyleName
1445 ? *pStyleName
1446 : pTableNode->GetTable().GetTableStyleName());
1448 std::unique_ptr<SwTableAutoFormat> pNone;
1449 SwTableAutoFormat* pTableStyle;
1450 if (pStyleName && pStyleName->isEmpty())
1452 pNone.reset(new SwTableAutoFormat(SwViewShell::GetShellRes()->aStrNone));
1453 pNone->DisableAll();
1454 pTableStyle = pNone.get();
1456 else
1458 pTableStyle = GetDoc()->GetTableStyles().FindAutoFormat(aTableStyleName);
1460 if (!pTableStyle)
1461 return false;
1463 SwSelBoxes aBoxes;
1465 // whole table or only current selection
1466 if( IsTableMode() )
1467 ::GetTableSelCrs( *this, aBoxes );
1468 else
1470 const SwTableSortBoxes& rTBoxes = pTableNode->GetTable().GetTabSortBoxes();
1471 for (size_t n = 0; n < rTBoxes.size(); ++n)
1473 SwTableBox* pBox = rTBoxes[ n ];
1474 aBoxes.insert( pBox );
1478 bool bRet;
1479 if( !aBoxes.empty() )
1481 CurrShell aCurr( this );
1482 StartAllAction();
1483 bRet = GetDoc()->SetTableAutoFormat(
1484 aBoxes, *pTableStyle, bResetDirect, pStyleName);
1485 ClearFEShellTabCols(*GetDoc(), nullptr);
1486 EndAllActionAndCall();
1488 else
1489 bRet = false;
1490 return bRet;
1493 bool SwFEShell::GetTableAutoFormat( SwTableAutoFormat& rGet )
1495 const SwTableNode *pTableNd = IsCursorInTable();
1496 if( !pTableNd || pTableNd->GetTable().IsTableComplex() )
1497 return false;
1499 SwSelBoxes aBoxes;
1501 if ( !IsTableMode() ) // if cursor are not current
1502 GetCursor();
1504 // whole table or only current selection
1505 if( IsTableMode() )
1506 ::GetTableSelCrs( *this, aBoxes );
1507 else
1509 const SwTableSortBoxes& rTBoxes = pTableNd->GetTable().GetTabSortBoxes();
1510 for (size_t n = 0; n < rTBoxes.size(); ++n)
1512 SwTableBox* pBox = rTBoxes[ n ];
1513 aBoxes.insert( pBox );
1517 return GetDoc()->GetTableAutoFormat( aBoxes, rGet );
1520 bool SwFEShell::DeleteTableSel()
1522 // check if SPoint/Mark of current cursor are in a table
1523 SwFrame *pFrame = GetCurrFrame();
1524 if( !pFrame || !pFrame->IsInTab() )
1525 return false;
1527 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
1529 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
1530 DialogMask::MessageInfo | DialogMask::ButtonsOk );
1531 return false;
1534 CurrShell aCurr( this );
1535 StartAllAction();
1537 // search boxes via the layout
1538 bool bRet;
1539 SwSelBoxes aBoxes;
1540 GetTableSelCrs( *this, aBoxes );
1541 if( !aBoxes.empty() )
1543 TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
1545 // cursor should be removed from deletion area.
1546 // Put them behind/on the table; via the document
1547 // position they'll be set to the old position
1548 while( !pFrame->IsCellFrame() )
1549 pFrame = pFrame->GetUpper();
1550 ParkCursor( *static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetSttNd() );
1552 bRet = GetDoc()->DeleteRowCol( aBoxes );
1554 ClearFEShellTabCols(*GetDoc(), nullptr);
1556 else
1557 bRet = false;
1558 EndAllActionAndCall();
1559 return bRet;
1562 size_t SwFEShell::GetCurTabColNum() const
1564 //!!!GetCurMouseTabColNum() mitpflegen!!!!
1565 SwFrame *pFrame = GetCurrFrame();
1566 OSL_ENSURE( pFrame, "Cursor parked?" );
1568 // check if SPoint/Mark of current cursor are in a table
1569 if (!pFrame || !pFrame->IsInTab())
1570 return 0;
1574 // JP 26.09.95: why compare with ContentFrame
1575 // and not with CellFrame ????
1576 pFrame = pFrame->GetUpper();
1577 } while (pFrame && !pFrame->IsCellFrame());
1579 if (!pFrame)
1580 return 0;
1582 size_t nRet = 0;
1584 SwRectFnSet aRectFnSet(pFrame);
1586 const SwPageFrame* pPage = pFrame->FindPageFrame();
1588 // get TabCols, as only via these we get to the position
1589 SwTabCols aTabCols;
1590 GetTabCols( aTabCols );
1592 if( pFrame->FindTabFrame()->IsRightToLeft() )
1594 tools::Long nX = aRectFnSet.GetRight(pFrame->getFrameArea()) - aRectFnSet.GetLeft(pPage->getFrameArea());
1596 const tools::Long nRight = aTabCols.GetLeftMin() + aTabCols.GetRight();
1598 if ( !::IsSame( *GetDoc(), nX, nRight ) )
1600 nX = nRight - nX + aTabCols.GetLeft();
1601 for ( size_t i = 0; i < aTabCols.Count(); ++i )
1602 if ( ::IsSame( *GetDoc(), nX, aTabCols[i] ) )
1604 nRet = i + 1;
1605 break;
1609 else
1611 const tools::Long nX = aRectFnSet.GetLeft(pFrame->getFrameArea()) -
1612 aRectFnSet.GetLeft(pPage->getFrameArea());
1614 const tools::Long nLeft = aTabCols.GetLeftMin();
1616 if ( !::IsSame( *GetDoc(), nX, nLeft + aTabCols.GetLeft() ) )
1618 for ( size_t i = 0; i < aTabCols.Count(); ++i )
1619 if ( ::IsSame( *GetDoc(), nX, nLeft + aTabCols[i] ) )
1621 nRet = i + 1;
1622 break;
1626 return nRet;
1629 static const SwFrame *lcl_FindFrameInTab( const SwLayoutFrame *pLay, const Point &rPt, SwTwips nFuzzy )
1631 const SwFrame *pFrame = pLay->Lower();
1633 while( pFrame && pLay->IsAnLower( pFrame ) )
1635 if ( pFrame->getFrameArea().IsNear( rPt, nFuzzy ) )
1637 if ( pFrame->IsLayoutFrame() )
1639 const SwFrame *pTmp = ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), rPt, nFuzzy );
1640 if ( pTmp )
1641 return pTmp;
1644 return pFrame;
1647 pFrame = pFrame->FindNext();
1650 return nullptr;
1653 static const SwCellFrame *lcl_FindFrame( SwDoc & rDoc, const SwLayoutFrame *pLay, const Point &rPt,
1654 SwTwips nFuzzy, bool* pbRow, bool* pbCol )
1656 // bMouseMoveRowCols :
1657 // Method is called for
1658 // - Moving columns/rows with the mouse or
1659 // - Enhanced table selection
1660 const bool bMouseMoveRowCols = nullptr == pbCol;
1662 bool bCloseToRow = false;
1663 bool bCloseToCol = false;
1665 const SwFrame *pFrame = pLay->ContainsContent();
1666 const SwFrame* pRet = nullptr;
1668 if ( pFrame )
1672 if ( pFrame->IsInTab() )
1673 pFrame = const_cast<SwFrame*>(pFrame)->ImplFindTabFrame();
1675 if (!pFrame)
1676 break;
1678 if ( pFrame->IsTabFrame() )
1680 Point aPt( rPt );
1681 bool bSearchForFrameInTab = true;
1682 SwTwips nTmpFuzzy = nFuzzy;
1684 if ( !bMouseMoveRowCols )
1686 // We ignore nested tables for the enhanced table selection:
1687 while ( pFrame->GetUpper()->IsInTab() )
1688 pFrame = pFrame->GetUpper()->FindTabFrame();
1690 // We first check if the given point is 'close' to the left or top
1691 // border of the table frame:
1692 OSL_ENSURE( pFrame, "Nested table frame without outer table" );
1693 SwRectFnSet aRectFnSet(pFrame);
1694 const bool bRTL = pFrame->IsRightToLeft();
1696 SwRect aTabRect = pFrame->getFramePrintArea();
1697 aTabRect.Pos() += pFrame->getFrameArea().Pos();
1699 const SwTwips nLeft = bRTL ?
1700 aRectFnSet.GetRight(aTabRect) :
1701 aRectFnSet.GetLeft(aTabRect);
1702 const SwTwips nTop = aRectFnSet.GetTop(aTabRect);
1704 SwTwips const rPointX = aRectFnSet.IsVert() ? aPt.Y() : aPt.X();
1705 SwTwips const rPointY = aRectFnSet.IsVert() ? aPt.X() : aPt.Y();
1707 const SwTwips nXDiff = aRectFnSet.XDiff( nLeft, rPointX ) * ( bRTL ? -1 : 1 );
1708 const SwTwips nYDiff = aRectFnSet.YDiff( nTop, rPointY );
1710 bCloseToRow = nXDiff >= 0 && nXDiff < nFuzzy;
1711 bCloseToCol = nYDiff >= 0 && nYDiff < nFuzzy;
1713 if ( bCloseToCol && 2 * nYDiff > nFuzzy )
1715 const SwFrame* pPrev = pFrame->GetPrev();
1716 if ( pPrev )
1718 SwRect aPrevRect = pPrev->getFramePrintArea();
1719 aPrevRect.Pos() += pPrev->getFrameArea().Pos();
1721 if( aPrevRect.Contains( rPt ) )
1723 bCloseToCol = false;
1729 // If we found the point to be 'close' to the left or top border
1730 // of the table frame, we adjust the point to be on that border:
1731 if ( bCloseToRow && bCloseToCol )
1732 aPt = bRTL ? aTabRect.TopRight() : aRectFnSet.GetPos(aTabRect);
1733 else if ( bCloseToRow )
1734 aRectFnSet.IsVert() ? aPt.setY(nLeft) : aPt.setX(nLeft);
1735 else if ( bCloseToCol )
1736 aRectFnSet.IsVert() ? aPt.setX(nTop) : aPt.setY(nTop);
1738 if ( !bCloseToRow && !bCloseToCol )
1739 bSearchForFrameInTab = false;
1741 // Since the point has been adjusted, we call lcl_FindFrameInTab()
1742 // with a fuzzy value of 1:
1743 nTmpFuzzy = 1;
1746 const SwFrame* pTmp = bSearchForFrameInTab ?
1747 ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), aPt, nTmpFuzzy ) :
1748 nullptr;
1750 if ( pTmp )
1752 pFrame = pTmp;
1753 break;
1756 pFrame = pFrame->FindNextCnt();
1758 } while ( pFrame && pLay->IsAnLower( pFrame ) );
1761 if ( pFrame && pFrame->IsInTab() && pLay->IsAnLower( pFrame ) )
1765 // We allow mouse drag of table borders within nested tables,
1766 // but disallow hotspot selection of nested tables.
1767 if ( bMouseMoveRowCols )
1769 // find the next cell frame
1770 while ( pFrame && !pFrame->IsCellFrame() )
1771 pFrame = pFrame->GetUpper();
1773 else
1775 // find the most upper cell frame:
1776 while ( pFrame &&
1777 ( !pFrame->IsCellFrame() ||
1778 !pFrame->GetUpper()->GetUpper()->IsTabFrame() ||
1779 pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) )
1780 pFrame = pFrame->GetUpper();
1783 if ( pFrame ) // Note: this condition should be the same like the while condition!!!
1785 // #i32329# Enhanced table selection
1786 // used for hotspot selection of tab/cols/rows
1787 if ( !bMouseMoveRowCols )
1790 assert(pbCol && pbRow && "pbCol or pbRow missing");
1792 if ( bCloseToRow || bCloseToCol )
1794 *pbRow = bCloseToRow;
1795 *pbCol = bCloseToCol;
1796 pRet = pFrame;
1797 break;
1800 else
1802 // used for mouse move of columns/rows
1803 const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
1804 SwRect aTabRect = pTabFrame->getFramePrintArea();
1805 aTabRect.Pos() += pTabFrame->getFrameArea().Pos();
1807 SwRectFnSet aRectFnSet(pTabFrame);
1809 const SwTwips nTabTop = aRectFnSet.GetTop(aTabRect);
1810 const SwTwips nMouseTop = aRectFnSet.IsVert() ? rPt.X() : rPt.Y();
1812 // Do not allow to drag upper table border:
1813 if ( !::IsSame( rDoc, nTabTop, nMouseTop ) )
1815 if ( ::IsSame( rDoc, pFrame->getFrameArea().Left(), rPt.X() ) ||
1816 ::IsSame( rDoc, pFrame->getFrameArea().Right(),rPt.X() ) )
1818 if ( pbRow ) *pbRow = false;
1819 pRet = pFrame;
1820 break;
1822 if ( ::IsSame( rDoc, pFrame->getFrameArea().Top(), rPt.Y() ) ||
1823 ::IsSame( rDoc, pFrame->getFrameArea().Bottom(),rPt.Y() ) )
1825 if ( pbRow ) *pbRow = true;
1826 pRet = pFrame;
1827 break;
1832 pFrame = pFrame->GetUpper();
1834 } while ( pFrame );
1837 // robust:
1838 OSL_ENSURE( !pRet || pRet->IsCellFrame(), "lcl_FindFrame() is supposed to find a cell frame!" );
1839 return pRet && pRet->IsCellFrame() ? static_cast<const SwCellFrame*>(pRet) : nullptr;
1842 // pbCol = 0 => Used for moving table rows/cols with mouse
1843 // pbCol != 0 => Used for selecting table/rows/cols
1845 #define ENHANCED_TABLE_SELECTION_FUZZY 10
1847 const SwFrame* SwFEShell::GetBox( const Point &rPt, bool* pbRow, bool* pbCol ) const
1849 const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower());
1850 vcl::Window* pOutWin = GetWin();
1851 SwTwips nFuzzy = COLFUZZY;
1852 if( pOutWin )
1854 // #i32329# Enhanced table selection
1855 SwTwips nSize = pbCol ? ENHANCED_TABLE_SELECTION_FUZZY : RULER_MOUSE_MARGINWIDTH;
1856 Size aTmp( nSize, nSize );
1857 aTmp = pOutWin->PixelToLogic( aTmp );
1858 nFuzzy = aTmp.Width();
1861 while ( pPage && !pPage->getFrameArea().IsNear( rPt, nFuzzy ) )
1862 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
1864 const SwCellFrame *pFrame = nullptr;
1865 if ( pPage )
1867 // We cannot search the box by GetModelPositionForViewPoint or GetContentPos.
1868 // This would lead to a performance collapse for documents
1869 // with a lot of paragraphs/tables on one page
1870 //(BrowseMode!)
1872 // check flys first
1873 if ( pPage->GetSortedObjs() )
1875 for ( size_t i = 0; !pFrame && i < pPage->GetSortedObjs()->size(); ++i )
1877 SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i];
1878 if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
1880 pFrame = lcl_FindFrame( *GetDoc(), pFlyFrame, rPt, nFuzzy, pbRow, pbCol );
1884 const SwLayoutFrame *pLay = static_cast<const SwLayoutFrame*>(pPage->Lower());
1885 while ( pLay && !pFrame )
1887 pFrame = lcl_FindFrame( *GetDoc(), pLay, rPt, nFuzzy, pbRow, pbCol );
1888 pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
1891 return pFrame;
1894 /* Helper function*/
1895 /* calculated the distance between Point rC and Line Segment (rA, rB) */
1896 static double lcl_DistancePoint2Segment( const Point& rA, const Point& rB, const Point& rC )
1898 double nRet = 0;
1900 const basegfx::B2DVector aBC( rC.X() - rB.X(), rC.Y() - rB.Y() );
1901 const basegfx::B2DVector aAB( rB.X() - rA.X(), rB.Y() - rA.Y() );
1902 const double nDot1 = aBC.scalar( aAB );
1904 if ( nDot1 > 0 ) // check outside case 1
1905 nRet = aBC.getLength();
1906 else
1908 const basegfx::B2DVector aAC( rC.X() - rA.X(), rC.Y() - rA.Y() );
1909 const basegfx::B2DVector aBA( rA.X() - rB.X(), rA.Y() - rB.Y() );
1910 const double nDot2 = aAC.scalar( aBA );
1912 if ( nDot2 > 0 ) // check outside case 2
1913 nRet = aAC.getLength();
1914 else
1916 const double nDiv = aAB.getLength();
1917 nRet = nDiv ? aAB.cross( aAC ) / nDiv : 0;
1921 return std::abs(nRet);
1924 /* Helper function*/
1925 static Point lcl_ProjectOntoClosestTableFrame( const SwTabFrame& rTab, const Point& rPoint, bool bRowDrag )
1927 Point aRet( rPoint );
1928 const SwTabFrame* pCurrentTab = &rTab;
1929 const bool bVert = pCurrentTab->IsVertical();
1930 const bool bRTL = pCurrentTab->IsRightToLeft();
1932 // Western Layout:
1933 // bRowDrag = true => compare to left border of table
1934 // bRowDrag = false => compare to top border of table
1936 // Asian Layout:
1937 // bRowDrag = true => compare to right border of table
1938 // bRowDrag = false => compare to top border of table
1940 // RTL Layout:
1941 // bRowDrag = true => compare to right border of table
1942 // bRowDrag = false => compare to top border of table
1943 bool bLeft = false;
1944 bool bRight = false;
1946 if ( bRowDrag )
1948 if ( bVert || bRTL )
1949 bRight = true;
1950 else
1951 bLeft = true;
1954 // used to find the minimal distance
1955 double nMin = -1;
1956 Point aMin1;
1957 Point aMin2;
1959 Point aS1;
1960 Point aS2;
1962 while ( pCurrentTab )
1964 SwRect aTabRect( pCurrentTab->getFramePrintArea() );
1965 aTabRect += pCurrentTab->getFrameArea().Pos();
1967 if ( bLeft )
1969 // distance to left table border
1970 aS1 = aTabRect.TopLeft();
1971 aS2 = aTabRect.BottomLeft();
1973 else if ( bRight )
1975 // distance to right table border
1976 aS1 = aTabRect.TopRight();
1977 aS2 = aTabRect.BottomRight();
1979 else //if ( bTop )
1981 // distance to top table border
1982 aS1 = aTabRect.TopLeft();
1983 aS2 = aTabRect.TopRight();
1986 const double nDist = lcl_DistancePoint2Segment( aS1, aS2, rPoint );
1988 if ( nDist < nMin || -1 == nMin )
1990 aMin1 = aS1;
1991 aMin2 = aS2;
1992 nMin = nDist;
1995 pCurrentTab = pCurrentTab->GetFollow();
1998 // project onto closest line:
1999 if ( bLeft || bRight )
2001 aRet.setX(aMin1.getX());
2002 if ( aRet.getY() > aMin2.getY() )
2003 aRet.setY(aMin2.getY());
2004 else if ( aRet.getY() < aMin1.getY() )
2005 aRet.setY(aMin1.getY());
2007 else
2009 aRet.setY(aMin1.getY());
2010 if ( aRet.getX() > aMin2.getX() )
2011 aRet.setX(aMin2.getX());
2012 else if ( aRet.getX() < aMin1.getX() )
2013 aRet.setX(aMin1.getX());
2016 return aRet;
2019 // #i32329# Enhanced table selection
2020 bool SwFEShell::SelTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag )
2022 bool bRet = false;
2023 Point aEndPt;
2024 if ( pEnd )
2025 aEndPt = *pEnd;
2027 SwPosition* ppPos[2] = { nullptr, nullptr };
2028 Point paPt [2] = { rPt, aEndPt };
2029 bool pbRow[2] = { false, false };
2030 bool pbCol[2] = { false, false };
2032 // pEnd is set during dragging.
2033 for ( sal_uInt16 i = 0; i < ( pEnd ? 2 : 1 ); ++i )
2035 const SwCellFrame* pFrame =
2036 static_cast<const SwCellFrame*>(GetBox( paPt[i], &pbRow[i], &pbCol[i] ) );
2038 if( pFrame )
2040 const SwFrame* pLower = pFrame->Lower();
2041 while( pLower && pLower->IsRowFrame() )
2043 pFrame = static_cast<const SwCellFrame*>( static_cast<const SwLayoutFrame*>( pLower )->Lower() );
2044 pLower = pFrame ? pFrame->Lower() : nullptr;
2046 if( pFrame && pFrame->GetTabBox()->GetSttNd() &&
2047 pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() )
2048 pFrame = nullptr;
2051 if ( pFrame )
2053 const SwContentFrame* pContent = ::GetCellContent( *pFrame );
2055 if ( pContent && pContent->IsTextFrame() )
2058 ppPos[i] = new SwPosition(static_cast<SwTextFrame const*>(pContent)->MapViewToModelPos(TextFrameIndex(0)));
2060 // paPt[i] will not be used any longer, now we use it to store
2061 // a position inside the content frame
2062 paPt[i] = pContent->getFrameArea().Center();
2066 // no calculation of end frame if start frame has not been found.
2067 if ( 1 == i || !ppPos[0] || !pEnd || !pFrame )
2068 break;
2070 // find 'closest' table frame to pEnd:
2071 const SwTabFrame* pCurrentTab = pFrame->FindTabFrame();
2072 if ( pCurrentTab->IsFollow() )
2073 pCurrentTab = pCurrentTab->FindMaster( true );
2075 const Point aProjection = lcl_ProjectOntoClosestTableFrame( *pCurrentTab, *pEnd, bRowDrag );
2076 paPt[1] = aProjection;
2079 if ( ppPos[0] )
2081 SwShellCursor* pCursor = GetCursor_();
2082 SwCursorSaveState aSaveState( *pCursor );
2083 SwPosition aOldPos( *pCursor->GetPoint() );
2085 pCursor->DeleteMark();
2086 *pCursor->GetPoint() = *ppPos[0];
2087 pCursor->GetPtPos() = paPt[0];
2089 if ( !pCursor->IsInProtectTable() )
2091 bool bNewSelection = true;
2093 if ( ppPos[1] )
2095 if ( ppPos[1]->GetNode().StartOfSectionNode() !=
2096 aOldPos.GetNode().StartOfSectionNode() )
2098 pCursor->SetMark();
2099 SwCursorSaveState aSaveState2( *pCursor );
2100 *pCursor->GetPoint() = *ppPos[1];
2101 pCursor->GetPtPos() = paPt[1];
2103 if ( pCursor->IsInProtectTable( false, false ) )
2105 pCursor->RestoreSavePos();
2106 bNewSelection = false;
2109 else
2111 pCursor->RestoreSavePos();
2112 bNewSelection = false;
2116 if ( bNewSelection )
2118 // #i35543# SelTableRowCol should remove any existing
2119 // table cursor:
2120 if ( IsTableMode() )
2121 TableCursorToCursor();
2123 if ( pbRow[0] && pbCol[0] )
2124 bRet = SwCursorShell::SelTable();
2125 else if ( pbRow[0] )
2126 bRet = SwCursorShell::SelTableRowOrCol( true, true );
2127 else if ( pbCol[0] )
2128 bRet = SwCursorShell::SelTableRowOrCol( false, true );
2130 else
2131 bRet = true;
2134 delete ppPos[0];
2135 delete ppPos[1];
2138 return bRet;
2141 SwTab SwFEShell::WhichMouseTabCol( const Point &rPt ) const
2143 SwTab nRet = SwTab::COL_NONE;
2144 bool bRow = false;
2145 bool bCol = false;
2146 bool bSelect = false;
2148 // First try: Do we get the row/col move cursor?
2149 const SwCellFrame* pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow ));
2151 if ( !pFrame )
2153 // Second try: Do we get the row/col/tab selection cursor?
2154 pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow, &bCol ));
2155 bSelect = true;
2158 if( pFrame )
2160 const SwFrame* pLower = pFrame->Lower();
2161 while( pLower && pLower->IsRowFrame() )
2163 pFrame = static_cast<const SwCellFrame*>(static_cast<const SwLayoutFrame*>(pLower)->Lower());
2164 pLower = pFrame ? pFrame->Lower() : nullptr;
2166 if( pFrame && ((pFrame->GetTabBox()->GetSttNd() &&
2167 pFrame->GetTabBox()->GetSttNd()->IsInProtectSect()) || (pFrame->GetTabBox()->getRowSpan() < 0)))
2168 pFrame = nullptr;
2171 if( pFrame )
2173 if ( !bSelect )
2175 if ( pFrame->IsVertical() )
2176 nRet = bRow ? SwTab::COL_VERT : SwTab::ROW_VERT;
2177 else
2178 nRet = bRow ? SwTab::ROW_HORI : SwTab::COL_HORI;
2180 else
2182 const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
2183 if ( pTabFrame->IsVertical() )
2185 if ( bRow && bCol )
2187 nRet = SwTab::SEL_VERT;
2189 else if ( bRow )
2191 nRet = SwTab::ROWSEL_VERT;
2193 else if ( bCol )
2195 nRet = SwTab::COLSEL_VERT;
2198 else
2200 if ( bRow && bCol )
2202 nRet = pTabFrame->IsRightToLeft() ?
2203 SwTab::SEL_HORI_RTL :
2204 SwTab::SEL_HORI;
2206 else if ( bRow )
2208 nRet = pTabFrame->IsRightToLeft() ?
2209 SwTab::ROWSEL_HORI_RTL :
2210 SwTab::ROWSEL_HORI;
2212 else if ( bCol )
2214 nRet = SwTab::COLSEL_HORI;
2220 return nRet;
2223 // -> #i23726#
2224 SwTextNode * SwFEShell::GetNumRuleNodeAtPos( const Point &rPt)
2226 SwTextNode * pResult = nullptr;
2228 SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel);
2230 if( GetContentAtPos(rPt, aContentAtPos) && aContentAtPos.aFnd.pNode)
2231 pResult = aContentAtPos.aFnd.pNode->GetTextNode();
2233 return pResult;
2236 bool SwFEShell::IsNumLabel( const Point &rPt, int nMaxOffset )
2238 bool bResult = false;
2240 SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel);
2242 if( GetContentAtPos(rPt, aContentAtPos))
2244 if ((nMaxOffset >= 0 && aContentAtPos.nDist <= nMaxOffset) ||
2245 (nMaxOffset < 0))
2246 bResult = true;
2249 return bResult;
2251 // <- #i23726#
2253 // #i42921#
2254 bool SwFEShell::IsVerticalModeAtNdAndPos( const SwTextNode& _rTextNode,
2255 const Point& _rDocPos )
2257 bool bRet( false );
2259 const SvxFrameDirection nTextDir =
2260 _rTextNode.GetTextDirection( SwPosition(_rTextNode), &_rDocPos );
2261 switch ( nTextDir )
2263 case SvxFrameDirection::Unknown:
2264 case SvxFrameDirection::Horizontal_RL_TB:
2265 case SvxFrameDirection::Horizontal_LR_TB:
2267 bRet = false;
2269 break;
2270 case SvxFrameDirection::Vertical_LR_TB:
2271 case SvxFrameDirection::Vertical_RL_TB:
2273 bRet = true;
2275 break;
2276 default: break;
2279 return bRet;
2282 void SwFEShell::GetMouseTabCols( SwTabCols &rToFill, const Point &rPt ) const
2284 const SwFrame *pBox = GetBox( rPt );
2285 if ( pBox )
2286 GetTabCols_( rToFill, pBox );
2289 void SwFEShell::SetMouseTabCols( const SwTabCols &rNew, bool bCurRowOnly,
2290 const Point &rPt )
2292 const SwFrame *pBox = GetBox( rPt );
2293 if( pBox )
2295 CurrShell aCurr( this );
2296 StartAllAction();
2297 GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<const SwCellFrame*>(pBox) );
2298 EndAllActionAndCall();
2302 sal_uInt16 SwFEShell::GetCurMouseColNum( const Point &rPt ) const
2304 return GetCurColNum_( GetBox( rPt ), nullptr );
2307 size_t SwFEShell::GetCurMouseTabColNum( const Point &rPt ) const
2309 //!!!GetCurTabColNum() mitpflegen!!!!
2310 size_t nRet = 0;
2312 const SwFrame *pFrame = GetBox( rPt );
2313 OSL_ENSURE( pFrame, "Table not found" );
2314 if( pFrame )
2316 const tools::Long nX = pFrame->getFrameArea().Left();
2318 // get TabCols, only via these we get the position
2319 SwTabCols aTabCols;
2320 GetMouseTabCols( aTabCols, rPt );
2322 const tools::Long nLeft = aTabCols.GetLeftMin();
2324 if ( !::IsSame( *GetDoc(), nX, nLeft + aTabCols.GetLeft() ) )
2326 for ( size_t i = 0; i < aTabCols.Count(); ++i )
2327 if ( ::IsSame( *GetDoc(), nX, nLeft + aTabCols[i] ) )
2329 nRet = i + 1;
2330 break;
2334 return nRet;
2337 void ClearFEShellTabCols(SwDoc & rDoc, SwTabFrame const*const pFrame)
2339 auto const pShell(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
2340 if (pShell)
2342 for (SwViewShell& rCurrentShell : pShell->GetRingContainer())
2344 if (auto const pFE = dynamic_cast<SwFEShell *>(&rCurrentShell))
2346 pFE->ClearColumnRowCache(pFrame);
2352 void SwFEShell::ClearColumnRowCache(SwTabFrame const*const pFrame)
2354 if (m_pColumnCache)
2356 if (pFrame == nullptr || pFrame == m_pColumnCache->pLastTabFrame)
2358 m_pColumnCache.reset();
2361 if (m_pRowCache)
2363 if (pFrame == nullptr || pFrame == m_pRowCache->pLastTabFrame)
2365 m_pRowCache.reset();
2370 void SwFEShell::GetTableAttr( SfxItemSet &rSet ) const
2372 SwFrame *pFrame = GetCurrFrame();
2373 if( pFrame && pFrame->IsInTab() )
2374 rSet.Put( pFrame->ImplFindTabFrame()->GetFormat()->GetAttrSet() );
2377 void SwFEShell::SetTableAttr( const SfxItemSet &rNew )
2379 SwFrame *pFrame = GetCurrFrame();
2380 if( pFrame && pFrame->IsInTab() )
2382 CurrShell aCurr( this );
2383 StartAllAction();
2384 SwTabFrame *pTab = pFrame->FindTabFrame();
2385 pTab->GetTable()->SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
2386 GetDoc()->SetAttr( rNew, *pTab->GetFormat() );
2387 GetDoc()->getIDocumentState().SetModified();
2388 EndAllActionAndCall();
2392 // change a cell width/cell height/column width/row height
2393 void SwFEShell::SetColRowWidthHeight( TableChgWidthHeightType eType, sal_uInt16 nDiff )
2395 SwFrame *pFrame = GetCurrFrame();
2396 if( !pFrame || !pFrame->IsInTab() )
2397 return;
2399 CurrShell aCurr( this );
2400 StartAllAction();
2402 do {
2403 pFrame = pFrame->GetUpper();
2404 } while( !pFrame->IsCellFrame() );
2406 SwTabFrame *pTab = pFrame->ImplFindTabFrame();
2408 // if the table is in relative values (USHRT_MAX)
2409 // then it should be recalculated to absolute values now
2410 const SwFormatFrameSize& rTableFrameSz = pTab->GetFormat()->GetFrameSize();
2411 SwRectFnSet aRectFnSet(pTab);
2412 tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
2413 TableChgWidthHeightType eTypePos = extractPosition(eType);
2414 if( TableChgMode::VarWidthChangeAbs == pTab->GetTable()->GetTableChgMode() &&
2415 ( eTypePos == TableChgWidthHeightType::ColLeft || eTypePos == TableChgWidthHeightType::ColRight ) &&
2416 text::HoriOrientation::NONE == pTab->GetFormat()->GetHoriOrient().GetHoriOrient() &&
2417 nPrtWidth != rTableFrameSz.GetWidth() )
2419 SwFormatFrameSize aSz( rTableFrameSz );
2420 aSz.SetWidth( pTab->getFramePrintArea().Width() );
2421 pTab->GetFormat()->SetFormatAttr( aSz );
2424 SwTwips nLogDiff = nDiff;
2425 nLogDiff *= pTab->GetFormat()->GetFrameSize().GetWidth();
2426 nLogDiff /= nPrtWidth;
2428 /** The cells are destroyed in here */
2429 GetDoc()->SetColRowWidthHeight(
2430 *const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()),
2431 eType, nDiff, nLogDiff );
2433 ClearFEShellTabCols(*GetDoc(), nullptr);
2434 EndAllActionAndCall();
2437 static bool lcl_IsFormulaSelBoxes( const SwTable& rTable, const SwTableBoxFormula& rFormula,
2438 SwCellFrames& rCells )
2440 SwTableBoxFormula aTmp( rFormula );
2441 SwSelBoxes aBoxes;
2442 aTmp.GetBoxesOfFormula(rTable, aBoxes);
2443 for (size_t nSelBoxes = aBoxes.size(); nSelBoxes; )
2445 SwTableBox* pBox = aBoxes[ --nSelBoxes ];
2447 if( std::none_of(rCells.begin(), rCells.end(), [&pBox](SwCellFrame* pFrame) { return pFrame->GetTabBox() == pBox; }) )
2448 return false;
2451 return true;
2454 // ask formula for auto-sum
2455 void SwFEShell::GetAutoSum( OUString& rFormula ) const
2457 SwFrame *pFrame = GetCurrFrame();
2458 SwTabFrame *pTab = pFrame ? pFrame->ImplFindTabFrame() : nullptr;
2459 if( !pTab )
2460 return;
2462 SwCellFrames aCells;
2463 OUString sFields;
2464 if( ::GetAutoSumSel( *this, aCells ))
2466 sal_uInt16 nW = 0;
2467 for( size_t n = aCells.size(); n; )
2469 SwCellFrame* pCFrame = aCells[ --n ];
2470 sal_uInt16 nBoxW = pCFrame->GetTabBox()->IsFormulaOrValueBox();
2471 if( !nBoxW )
2472 break;
2474 if( !nW )
2476 if( USHRT_MAX == nBoxW )
2477 continue; // skip space at beginning
2479 // formula only if box is contained
2480 if( RES_BOXATR_FORMULA == nBoxW &&
2481 !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
2482 GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells))
2484 nW = RES_BOXATR_VALUE;
2485 // restore previous spaces!
2486 for( size_t i = aCells.size(); n+1 < i; )
2488 sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">"
2489 + sFields;
2492 else
2493 nW = nBoxW;
2495 else if( RES_BOXATR_VALUE == nW )
2497 // search for values, Value/Formula/Text found -> include
2498 if( RES_BOXATR_FORMULA == nBoxW &&
2499 ::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
2500 GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells ))
2501 break;
2502 else if( USHRT_MAX != nBoxW )
2503 sFields = OUStringChar(cListDelim) + sFields;
2504 else
2505 break;
2507 else if( RES_BOXATR_FORMULA == nW )
2509 // only continue search when the current formula points to
2510 // all boxes contained in the selection
2511 if( RES_BOXATR_FORMULA == nBoxW )
2513 if( !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
2514 GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells ))
2516 // redo only for values!
2518 nW = RES_BOXATR_VALUE;
2519 sFields.clear();
2520 // restore previous spaces!
2521 for( size_t i = aCells.size(); n+1 < i; )
2523 sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">"
2524 + sFields;
2527 else
2528 sFields = OUStringChar(cListDelim) + sFields;
2530 else if( USHRT_MAX == nBoxW )
2531 break;
2532 else
2533 continue; // ignore this box
2535 else
2536 // all other stuff terminates the loop
2537 // possibly allow texts??
2538 break;
2540 sFields = "<" + pCFrame->GetTabBox()->GetName() + ">" + sFields;
2544 rFormula = sCalc_Sum;
2545 if (!sFields.isEmpty())
2547 rFormula += "(" + sFields + ")";
2551 bool SwFEShell::IsTableRightToLeft() const
2553 SwFrame *pFrame = GetCurrFrame();
2554 SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr;
2555 if (!pTab)
2556 return false;
2557 return pTab->IsRightToLeft();
2560 bool SwFEShell::IsMouseTableRightToLeft(const Point &rPt) const
2562 SwFrame *pFrame = const_cast<SwFrame *>(GetBox( rPt ));
2563 const SwTabFrame* pTabFrame = pFrame ? pFrame->ImplFindTabFrame() : nullptr;
2564 OSL_ENSURE( pTabFrame, "Table not found" );
2565 return pTabFrame && pTabFrame->IsRightToLeft();
2568 bool SwFEShell::IsTableVertical() const
2570 SwFrame *pFrame = GetCurrFrame();
2571 SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr;
2572 if (!pTab)
2573 return false;
2574 return pTab->IsVertical();
2577 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */