android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / frmedt / fetab.cxx
blob93e20fab036e0012c49e7a2606dced5fb2ffa1a9
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 <IDocumentState.hxx>
39 #include <IDocumentLayoutAccess.hxx>
40 #include <IDocumentRedlineAccess.hxx>
41 #include <IDocumentUndoRedo.hxx>
42 #include <cntfrm.hxx>
43 #include <txtfrm.hxx>
44 #include <notxtfrm.hxx>
45 #include <rootfrm.hxx>
46 #include <pagefrm.hxx>
47 #include <tabfrm.hxx>
48 #include <rowfrm.hxx>
49 #include <cellfrm.hxx>
50 #include <flyfrm.hxx>
51 #include <swtable.hxx>
52 #include <swddetbl.hxx>
53 #include <ndtxt.hxx>
54 #include <calc.hxx>
55 #include <dialoghelp.hxx>
56 #include <tabcol.hxx>
57 #include <tblafmt.hxx>
58 #include <cellatr.hxx>
59 #include <pam.hxx>
60 #include <viscrs.hxx>
61 #include <tblsel.hxx>
62 #include <swerror.h>
63 #include <swundo.hxx>
64 #include <frmtool.hxx>
65 #include <fmtrowsplt.hxx>
66 #include <node.hxx>
67 #include <sortedobjs.hxx>
69 using namespace ::com::sun::star;
71 // also see swtable.cxx
72 #define COLFUZZY 20L
74 static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; }
76 namespace {
78 class TableWait
80 const std::unique_ptr<SwWait> m_pWait;
81 // this seems really fishy: do some locking, if an arbitrary number of lines is exceeded
82 static const size_t our_kLineLimit = 20;
83 static bool ShouldWait(size_t nCnt, SwFrame *pFrame, size_t nCnt2)
84 { return our_kLineLimit < nCnt || our_kLineLimit < nCnt2 || (pFrame && our_kLineLimit < pFrame->ImplFindTabFrame()->GetTable()->GetTabLines().size()); }
85 public:
86 TableWait(size_t nCnt, SwFrame *pFrame, SwDocShell &rDocShell, size_t nCnt2 = 0)
87 : m_pWait( ShouldWait(nCnt, pFrame, nCnt2) ? std::make_unique<SwWait>( rDocShell, true ) : nullptr )
88 { }
93 void SwFEShell::ParkCursorInTab()
95 SwCursor * pSwCursor = GetCursor();
97 OSL_ENSURE(pSwCursor, "no SwCursor");
99 SwPosition aStartPos = *pSwCursor->GetPoint(), aEndPos = aStartPos;
101 /* Search least and greatest position in current cursor ring.
103 for(SwPaM& rTmpCursor : pSwCursor->GetRingContainer())
105 SwCursor* pTmpCursor = static_cast<SwCursor *>(&rTmpCursor);
106 const SwPosition * pPt = pTmpCursor->GetPoint(),
107 * pMk = pTmpCursor->GetMark();
109 if (*pPt < aStartPos)
110 aStartPos = *pPt;
112 if (*pPt > aEndPos)
113 aEndPos = *pPt;
115 if (*pMk < aStartPos)
116 aStartPos = *pMk;
118 if (*pMk > aEndPos)
119 aEndPos = *pMk;
123 KillPams();
125 /* @@@ semantic: SwCursor::operator=() is not implemented @@@ */
127 /* Set cursor to end of selection to ensure IsLastCellInRow works
128 properly. */
130 SwCursor aTmpCursor( aEndPos, nullptr );
131 *pSwCursor = aTmpCursor;
134 /* Move the cursor out of the columns to delete and stay in the
135 same row. If the table has only one column the cursor will
136 stay in the row and the shell will take care of it. */
137 if (IsLastCellInRow())
139 /* If the cursor is in the last row of the table, first
140 try to move it to the previous cell. If that fails move
141 it to the next cell. */
144 SwCursor aTmpCursor( aStartPos, nullptr );
145 *pSwCursor = aTmpCursor;
148 if (! pSwCursor->GoPrevCell())
150 SwCursor aTmpCursor( aEndPos, nullptr );
151 *pSwCursor = aTmpCursor;
152 pSwCursor->GoNextCell();
155 else
157 /* If the cursor is not in the last row of the table, first
158 try to move it to the next cell. If that fails move it
159 to the previous cell. */
162 SwCursor aTmpCursor( aEndPos, nullptr );
163 *pSwCursor = aTmpCursor;
166 if (! pSwCursor->GoNextCell())
168 SwCursor aTmpCursor( aStartPos, nullptr );
169 *pSwCursor = aTmpCursor;
170 pSwCursor->GoPrevCell();
175 void SwFEShell::InsertRow( sal_uInt16 nCnt, bool bBehind )
177 // check if Point/Mark of current cursor are in a table
178 SwFrame *pFrame = GetCurrFrame();
179 if( !pFrame || !pFrame->IsInTab() )
180 return;
182 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
184 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
185 DialogMask::MessageInfo | DialogMask::ButtonsOk );
186 return;
189 // pending drag & drop?
190 bool bAction = ActionPend();
192 CurrShell aCurr( this );
193 StartAllAction();
195 // search boxes via the layout
196 SwSelBoxes aBoxes;
197 bool bSelectAll = StartsWith_() == StartsWith::Table && ExtendedSelectedAll();
198 if (bSelectAll)
200 // Set the end of the selection to the last paragraph of the last cell of the table.
201 SwPaM* pPaM = getShellCursor(false);
202 SwNode* pNode = pPaM->Start()->GetNode().FindTableNode()->EndOfSectionNode();
203 // pNode is the end node of the table, we want the last node before the end node of the last cell.
204 pPaM->End()->Assign( pNode->GetIndex() - 2 );
206 GetTableSel( *this, aBoxes, SwTableSearchType::Row );
208 TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
210 if ( !aBoxes.empty() )
211 GetDoc()->InsertRow( aBoxes, nCnt, bBehind, /*bInsertDummy=*/!bAction );
213 EndAllActionAndCall();
216 void SwFEShell::InsertCol( sal_uInt16 nCnt, bool bBehind )
218 // check if Point/Mark of current cursor are in a table
219 SwFrame *pFrame = GetCurrFrame();
220 if( !pFrame || !pFrame->IsInTab() )
221 return;
223 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
225 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
226 DialogMask::MessageInfo | DialogMask::ButtonsOk );
227 return;
230 CurrShell aCurr( this );
232 if( !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::Col ) )
234 ErrorHandler::HandleError( ERR_TBLINSCOL_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
235 DialogMask::MessageInfo | DialogMask::ButtonsOk );
236 return;
239 // pending drag & drop?
240 bool bAction = ActionPend();
242 StartAllAction();
243 // search boxes via the layout
244 SwSelBoxes aBoxes;
245 GetTableSel( *this, aBoxes, SwTableSearchType::Col );
247 TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
249 if( !aBoxes.empty() )
250 GetDoc()->InsertCol( aBoxes, nCnt, bBehind, /*bInsertDummy=*/!bAction );
252 EndAllActionAndCall();
255 // Determines if the current cursor is in the last row of the table.
256 bool SwFEShell::IsLastCellInRow() const
258 SwTabCols aTabCols;
259 GetTabCols( aTabCols );
260 bool bResult = false;
262 if (IsTableRightToLeft())
263 /* If the table is right-to-left the last row is the most left one. */
264 bResult = 0 == GetCurTabColNum();
265 else
266 /* If the table is left-to-right the last row is the most right one. */
267 bResult = aTabCols.Count() == GetCurTabColNum();
269 return bResult;
272 bool SwFEShell::DeleteCol()
274 // check if Point/Mark of current cursor are in a table
275 SwFrame *pFrame = GetCurrFrame();
276 if( !pFrame || !pFrame->IsInTab() )
277 return false;
279 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
281 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
282 DialogMask::MessageInfo | DialogMask::ButtonsOk );
283 return false;
286 CurrShell aCurr( this );
288 bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording();
289 bool bRecordAndHideChanges = bRecordChanges &&
290 GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
292 // tracked deletion: remove only textbox content,
293 // and set IsNoTracked table box property to false
294 if ( bRecordChanges )
296 StartUndo(SwUndoId::COL_DELETE);
297 StartAllAction();
299 if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
300 pWrtShell->SelectTableCol();
302 // search boxes via the layout
303 SwSelBoxes aBoxes;
304 GetTableSel( *this, aBoxes, SwTableSearchType::Col );
306 TableWait aWait( 20, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
308 SwTableNode* pTableNd = pFrame->IsTextFrame()
309 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode()
310 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode();
312 for (size_t i = 0; i < aBoxes.size(); ++i)
314 SwTableBox *pBox = aBoxes[i];
315 if ( pBox->GetSttNd() )
317 SwNodeIndex aIdx( *pBox->GetSttNd(), 1 );
318 SwCursor aCursor( SwPosition(aIdx), nullptr );
319 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
320 GetDoc()->SetBoxAttr( aCursor, aHasTextChangesOnly );
322 // add dummy text content to the empty box for change tracking
323 if ( pBox->IsEmpty() )
325 IDocumentContentOperations& rIDCO = GetDoc()->getIDocumentContentOperations();
326 IDocumentRedlineAccess& rIDRA = GetDoc()->getIDocumentRedlineAccess();
327 RedlineFlags eOld = rIDRA.GetRedlineFlags();
328 rIDRA.SetRedlineFlags_intern(RedlineFlags::NONE);
329 rIDCO.InsertString( aCursor, OUStringChar(CH_TXT_TRACKED_DUMMY_CHAR) );
330 aCursor.SetMark();
331 aCursor.GetMark()->SetContent(0);
332 rIDRA.SetRedlineFlags_intern( eOld );
333 rIDCO.DeleteAndJoin( aCursor );
339 SwEditShell* pEditShell = GetDoc()->GetEditShell();
340 pEditShell->Delete();
342 // remove cell frames in Hide Changes mode (and table frames, if needed)
343 if ( bRecordAndHideChanges )
345 // remove all frames of the table, and make them again without the deleted ones
346 // TODO remove only the deleted frames
347 pTableNd->DelFrames();
349 if ( !pTableNd->GetTable().IsDeleted() )
351 pTableNd->MakeOwnFrames();
355 EndAllActionAndCall();
356 EndUndo(SwUndoId::COL_DELETE);
357 return true;
360 StartAllAction();
362 // search boxes via the layout
363 bool bRet;
364 SwSelBoxes aBoxes;
365 SwTableSearchType eSearchType = SwTableSearchType::Col;
367 // NewModel tables already ExpandColumnSelection, so don't do it here also.
368 const SwContentNode* pContentNd = getShellCursor(false)->GetPointNode().GetContentNode();
369 const SwTableNode* pTableNd = pContentNd ? pContentNd->FindTableNode() : nullptr;
370 if (pTableNd && pTableNd->GetTable().IsNewModel())
371 eSearchType = SwTableSearchType::NONE;
373 GetTableSel(*this, aBoxes, eSearchType);
374 if ( !aBoxes.empty() )
376 TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
378 // remove crsr from the deletion area.
379 // Put them behind/on the table; via the
380 // document position they will be put to the old position
381 while( !pFrame->IsCellFrame() )
382 pFrame = pFrame->GetUpper();
384 ParkCursorInTab();
386 // then delete the column
387 StartUndo(SwUndoId::COL_DELETE);
388 bRet = GetDoc()->DeleteRowCol(aBoxes, SwDoc::RowColMode::DeleteColumn);
389 EndUndo(SwUndoId::COL_DELETE);
391 else
392 bRet = false;
394 EndAllActionAndCall();
395 return bRet;
398 void SwFEShell::DeleteTable()
400 DeleteRow(true);
403 bool SwFEShell::DeleteRow(bool bCompleteTable)
405 // check if Point/Mark of current cursor are in a table
406 SwFrame *pFrame = GetCurrFrame();
407 if( !pFrame || !pFrame->IsInTab() )
408 return false;
410 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
412 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
413 DialogMask::MessageInfo | DialogMask::ButtonsOk );
414 return false;
417 CurrShell aCurr( this );
419 bool bRecordChanges = GetDoc()->GetDocShell()->IsChangeRecording();
420 bool bRecordAndHideChanges = bRecordChanges &&
421 GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout()->IsHideRedlines();
423 // tracked deletion: all rows have already had tracked row change in the table selection
424 if ( bRecordChanges && !SwDoc::HasRowNotTracked( *getShellCursor( false ) ) )
425 return false;
427 if ( bRecordChanges )
428 StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
430 StartAllAction();
432 // tracked deletion: remove only textbox content,
433 // and set HasTextChangesOnly table line property to false
434 if ( bRecordChanges )
436 SvxPrintItem aHasTextChangesOnly(RES_PRINT, false);
437 GetDoc()->SetRowNotTracked( *getShellCursor( false ), aHasTextChangesOnly );
439 if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
440 pWrtShell->SelectTableRow();
442 // don't need to remove the row frames in Show Changes mode
443 if ( !bRecordAndHideChanges )
445 if (SwEditShell* pEditShell = GetDoc()->GetEditShell())
446 pEditShell->Delete(false);
448 EndAllActionAndCall();
449 EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
451 return true;
455 // search for boxes via the layout
456 bool bRet;
457 SwSelBoxes aBoxes;
458 GetTableSel( *this, aBoxes, SwTableSearchType::Row );
460 if( !aBoxes.empty() )
462 TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
464 // Delete cursors from the deletion area.
465 // Then the cursor is:
466 // 1. the following row, if there is another row after this
467 // 2. the preceding row, if there is another row before this
468 // 3. otherwise below the table
470 SwTableNode* pTableNd = pFrame->IsTextFrame()
471 ? static_cast<SwTextFrame*>(pFrame)->GetTextNodeFirst()->FindTableNode()
472 : static_cast<SwNoTextFrame*>(pFrame)->GetNode()->FindTableNode();
474 // search all boxes / lines
475 FndBox_ aFndBox( nullptr, nullptr );
477 FndPara aPara( aBoxes, &aFndBox );
478 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
481 if( aFndBox.GetLines().empty() )
483 EndAllActionAndCall();
484 return false;
487 KillPams();
489 FndBox_* pFndBox = &aFndBox;
490 while( 1 == pFndBox->GetLines().size() &&
491 1 == pFndBox->GetLines().front()->GetBoxes().size())
493 FndBox_ *const pTmp = pFndBox->GetLines().front()->GetBoxes()[0].get();
494 if( pTmp->GetBox()->GetSttNd() )
495 break; // otherwise too far
496 pFndBox = pTmp;
499 SwTableLine* pDelLine = pFndBox->GetLines().back()->GetLine();
500 SwTableBox* pDelBox = pDelLine->GetTabBoxes().back();
501 while( !pDelBox->GetSttNd() )
503 SwTableLine* pLn = pDelBox->GetTabLines().back();
504 pDelBox = pLn->GetTabBoxes().back();
506 SwTableBox* pNextBox = pDelLine->FindNextBox( pTableNd->GetTable(),
507 pDelBox );
508 // skip deleted lines in Hide Changes mode with enabled change tracking
509 if ( bRecordAndHideChanges )
511 SwRedlineTable::size_type nRedlinePos = 0;
512 while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) )
513 pNextBox = pNextBox->GetUpper()->FindNextBox( pTableNd->GetTable(),
514 pNextBox->GetUpper()->GetTabBoxes().back() );
517 // skip protected cells
518 while( pNextBox &&
519 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
520 pNextBox = pNextBox->FindNextBox( pTableNd->GetTable(), pNextBox );
522 if( !pNextBox ) // no next? then the previous
524 pDelLine = pFndBox->GetLines().front()->GetLine();
525 pDelBox = pDelLine->GetTabBoxes()[ 0 ];
526 while( !pDelBox->GetSttNd() )
527 pDelBox = pDelBox->GetTabLines()[0]->GetTabBoxes()[0];
528 pNextBox = pDelLine->FindPreviousBox( pTableNd->GetTable(),
529 pDelBox );
530 // skip previous deleted lines in Hide Changes mode with enabled change tracking
531 if ( bRecordAndHideChanges )
533 SwRedlineTable::size_type nRedlinePos = 0;
534 while( pNextBox && pNextBox->GetUpper()->IsDeleted(nRedlinePos) )
536 pNextBox = pNextBox->GetUpper()->FindPreviousBox( pTableNd->GetTable(),
537 pNextBox->GetUpper()->GetTabBoxes()[0] );
538 nRedlinePos = 0;
542 // skip previous protected cells
543 while( pNextBox &&
544 pNextBox->GetFrameFormat()->GetProtect().IsContentProtected() )
545 pNextBox = pNextBox->FindPreviousBox( pTableNd->GetTable(), pNextBox );
548 // delete row content in Hide Changes mode
549 if ( bRecordAndHideChanges )
551 SwEditShell* pEditShell = GetDoc()->GetEditShell();
553 // select the rows deleted with change tracking
554 if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
556 pWrtShell->SelectTableRow();
557 SwCursor* pTableCursor = static_cast<SwCursor*>(GetTableCursor());
558 auto pStt = aBoxes[0];
559 auto pEnd = aBoxes.back();
560 if ( pTableCursor )
561 pTableCursor->DeleteMark();
562 else
563 pTableCursor = GetCursor(true);
565 if ( pTableCursor )
567 // set start and end of the selection
568 pTableCursor->GetPoint()->Assign( *pEnd->GetSttNd()->EndOfSectionNode() );
569 pTableCursor->Move( fnMoveBackward, GoInContent );
570 pTableCursor->SetMark();
571 pTableCursor->GetPoint()->Assign( *pStt->GetSttNd()->EndOfSectionNode() );
572 pTableCursor->Move( fnMoveBackward, GoInContent );
573 pWrtShell->UpdateCursor();
577 if (pEditShell)
578 pEditShell->Delete(false);
581 SwNodeOffset nIdx;
582 if( pNextBox ) // put cursor here
583 nIdx = pNextBox->GetSttIdx() + 1;
584 else // otherwise below the table
585 nIdx = pTableNd->EndOfSectionIndex() + 1;
587 SwNodeIndex aIdx( GetDoc()->GetNodes(), nIdx );
588 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
589 if( !pCNd )
590 pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
592 // remove row frames in Hide Changes mode (and table frames, if needed)
593 if ( bRecordAndHideChanges )
595 // remove all frames of the table, and make them again without the deleted ones
596 // TODO remove only the deleted frames
597 pTableNd->DelFrames();
598 if ( !pTableNd->GetTable().IsDeleted() )
600 pTableNd->MakeOwnFrames();
603 EndAllActionAndCall();
605 // put cursor
606 SwPaM* pPam = GetCursor();
607 pPam->GetPoint()->Assign( *pCNd, 0 );
608 pPam->SetMark(); // both want something
609 pPam->DeleteMark();
610 if ( SwWrtShell* pWrtShell = dynamic_cast<SwWrtShell*>(this) )
612 pWrtShell->UpdateCursor();
613 // tdf#150578 enable the disabled table toolbar by (zero) cursor moving
614 pWrtShell->Right( SwCursorSkipMode::Chars, false, 0, false );
617 EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
618 return true;
620 else if( pCNd )
622 // put cursor
623 SwPaM* pPam = GetCursor();
624 pPam->GetPoint()->Assign( *pCNd, 0 );
625 pPam->SetMark(); // both want something
626 pPam->DeleteMark();
630 // now delete the lines
631 StartUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
632 bRet = GetDoc()->DeleteRowCol( aBoxes );
633 EndUndo(bCompleteTable ? SwUndoId::UI_TABLE_DELETE : SwUndoId::ROW_DELETE);
635 else
636 bRet = false;
638 EndAllActionAndCall();
639 return bRet;
642 TableMergeErr SwFEShell::MergeTab()
644 // check if Point/Mark of current cursor are in a table
645 TableMergeErr nRet = TableMergeErr::NoSelection;
646 if( IsTableMode() )
648 SwShellTableCursor* pTableCursor = GetTableCursor();
649 const SwTableNode* pTableNd = pTableCursor->GetPointNode().FindTableNode();
650 if( dynamic_cast< const SwDDETable* >(&pTableNd->GetTable()) != nullptr )
652 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
653 DialogMask::MessageInfo | DialogMask::ButtonsOk );
655 else
657 CurrShell aCurr( this );
658 StartAllAction();
660 TableWait aWait(pTableCursor->GetSelectedBoxesCount(), nullptr,
661 *GetDoc()->GetDocShell(),
662 pTableNd->GetTable().GetTabLines().size() );
664 nRet = GetDoc()->MergeTable( *pTableCursor );
666 KillPams();
668 EndAllActionAndCall();
671 return nRet;
674 void SwFEShell::SplitTab( bool bVert, sal_uInt16 nCnt, bool bSameHeight )
676 // check if Point/Mark of current cursor are in a table
677 SwFrame *pFrame = GetCurrFrame();
678 if( !pFrame || !pFrame->IsInTab() )
679 return;
681 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
683 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
684 DialogMask::MessageInfo | DialogMask::ButtonsOk );
685 return;
688 CurrShell aCurr( this );
690 if( bVert && !CheckSplitCells( *this, nCnt + 1, SwTableSearchType::NONE ) )
692 ErrorHandler::HandleError( ERR_TBLSPLIT_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
693 DialogMask::MessageInfo | DialogMask::ButtonsOk );
694 return;
696 StartAllAction();
697 // search boxes via the layout
698 SwSelBoxes aBoxes;
699 GetTableSel( *this, aBoxes );
700 if( !aBoxes.empty() )
702 TableWait aWait( nCnt, pFrame, *GetDoc()->GetDocShell(), aBoxes.size() );
704 // now delete the columns
705 GetDoc()->SplitTable( aBoxes, bVert, nCnt, bSameHeight );
707 ClearFEShellTabCols(*GetDoc(), nullptr);
709 EndAllActionAndCall();
712 void SwFEShell::GetTabCols_(SwTabCols &rToFill, const SwFrame *pBox) const
714 const SwTabFrame *pTab = pBox->FindTabFrame();
715 if (m_pColumnCache)
717 bool bDel = true;
718 if (m_pColumnCache->pLastTable == pTab->GetTable())
720 bDel = false;
721 SwRectFnSet aRectFnSet(pTab);
723 const SwPageFrame* pPage = pTab->FindPageFrame();
724 const sal_uLong nLeftMin = aRectFnSet.GetLeft(pTab->getFrameArea()) -
725 aRectFnSet.GetLeft(pPage->getFrameArea());
726 const sal_uLong nRightMax = aRectFnSet.GetRight(pTab->getFrameArea()) -
727 aRectFnSet.GetLeft(pPage->getFrameArea());
729 if (m_pColumnCache->pLastTabFrame != pTab)
731 // if TabFrame was changed, we only shift a little bit
732 // as the width is the same
733 SwRectFnSet fnRectX(m_pColumnCache->pLastTabFrame);
734 if (fnRectX.GetWidth(m_pColumnCache->pLastTabFrame->getFrameArea()) ==
735 aRectFnSet.GetWidth(pTab->getFrameArea()) )
737 m_pColumnCache->pLastCols->SetLeftMin( nLeftMin );
739 m_pColumnCache->pLastTabFrame = pTab;
741 else
742 bDel = true;
745 if ( !bDel &&
746 m_pColumnCache->pLastCols->GetLeftMin () == o3tl::narrowing<sal_uInt16>(nLeftMin) &&
747 m_pColumnCache->pLastCols->GetLeft () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetLeft(pTab->getFramePrintArea())) &&
748 m_pColumnCache->pLastCols->GetRight () == o3tl::narrowing<sal_uInt16>(aRectFnSet.GetRight(pTab->getFramePrintArea()))&&
749 m_pColumnCache->pLastCols->GetRightMax() == o3tl::narrowing<sal_uInt16>(nRightMax) - m_pColumnCache->pLastCols->GetLeftMin() )
751 if (m_pColumnCache->pLastCellFrame != pBox)
753 pTab->GetTable()->GetTabCols( *m_pColumnCache->pLastCols,
754 static_cast<const SwCellFrame*>(pBox)->GetTabBox(), true);
755 m_pColumnCache->pLastCellFrame = pBox;
757 rToFill = *m_pColumnCache->pLastCols;
759 else
760 bDel = true;
762 if ( bDel )
763 m_pColumnCache.reset();
765 if (!m_pColumnCache)
767 SwDoc::GetTabCols( rToFill, static_cast<const SwCellFrame*>(pBox) );
769 m_pColumnCache.reset(new SwColCache);
770 m_pColumnCache->pLastCols.reset(new SwTabCols(rToFill));
771 m_pColumnCache->pLastTable = pTab->GetTable();
772 m_pColumnCache->pLastTabFrame = pTab;
773 m_pColumnCache->pLastCellFrame = pBox;
777 void SwFEShell::GetTabRows_(SwTabCols &rToFill, const SwFrame *pBox) const
779 const SwTabFrame *pTab = pBox->FindTabFrame();
780 if (m_pRowCache)
782 bool bDel = true;
783 if (m_pRowCache->pLastTable == pTab->GetTable())
785 bDel = false;
786 SwRectFnSet aRectFnSet(pTab);
787 const SwPageFrame* pPage = pTab->FindPageFrame();
788 const tools::Long nLeftMin = ( aRectFnSet.IsVert() ?
789 pTab->GetPrtLeft() - pPage->getFrameArea().Left() :
790 pTab->GetPrtTop() - pPage->getFrameArea().Top() );
791 const tools::Long nLeft = aRectFnSet.IsVert() ? LONG_MAX : 0;
792 const tools::Long nRight = aRectFnSet.GetHeight(pTab->getFramePrintArea());
793 const tools::Long nRightMax = aRectFnSet.IsVert() ? nRight : LONG_MAX;
795 if (m_pRowCache->pLastTabFrame != pTab || m_pRowCache->pLastCellFrame != pBox)
796 bDel = true;
798 if ( !bDel &&
799 m_pRowCache->pLastCols->GetLeftMin () == nLeftMin &&
800 m_pRowCache->pLastCols->GetLeft () == nLeft &&
801 m_pRowCache->pLastCols->GetRight () == nRight &&
802 m_pRowCache->pLastCols->GetRightMax() == nRightMax )
804 rToFill = *m_pRowCache->pLastCols;
806 else
807 bDel = true;
809 if ( bDel )
810 m_pRowCache.reset();
812 if (!m_pRowCache)
814 SwDoc::GetTabRows( rToFill, static_cast<const SwCellFrame*>(pBox) );
816 m_pRowCache.reset(new SwColCache);
817 m_pRowCache->pLastCols.reset(new SwTabCols(rToFill));
818 m_pRowCache->pLastTable = pTab->GetTable();
819 m_pRowCache->pLastTabFrame = pTab;
820 m_pRowCache->pLastCellFrame = pBox;
824 void SwFEShell::SetTabCols( const SwTabCols &rNew, bool bCurRowOnly )
826 SwFrame *pBox = GetCurrFrame();
827 if( !pBox || !pBox->IsInTab() )
828 return;
830 CurrShell aCurr( this );
831 StartAllAction();
835 pBox = pBox->GetUpper();
836 } while (pBox && !pBox->IsCellFrame());
838 GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<SwCellFrame*>(pBox) );
839 EndAllActionAndCall();
842 void SwFEShell::GetTabCols( SwTabCols &rToFill ) const
844 const SwFrame *pFrame = GetCurrFrame();
845 if( !pFrame || !pFrame->IsInTab() )
846 return;
849 pFrame = pFrame->GetUpper();
851 while (pFrame && !pFrame->IsCellFrame());
853 if (!pFrame)
854 return;
856 GetTabCols_( rToFill, pFrame );
859 void SwFEShell::GetTabRows( SwTabCols &rToFill ) const
861 const SwFrame *pFrame = GetCurrFrame();
862 if( !pFrame || !pFrame->IsInTab() )
863 return;
866 pFrame = pFrame->GetUpper();
867 } while (pFrame && !pFrame->IsCellFrame());
869 if (!pFrame)
870 return;
872 GetTabRows_( rToFill, pFrame );
875 void SwFEShell::SetTabRows( const SwTabCols &rNew, bool bCurColOnly )
877 SwFrame *pBox = GetCurrFrame();
878 if( !pBox || !pBox->IsInTab() )
879 return;
881 CurrShell aCurr( this );
882 StartAllAction();
886 pBox = pBox->GetUpper();
887 } while (pBox && !pBox->IsCellFrame());
889 GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<SwCellFrame*>(pBox) );
890 EndAllActionAndCall();
893 void SwFEShell::GetMouseTabRows( SwTabCols &rToFill, const Point &rPt ) const
895 const SwFrame *pBox = GetBox( rPt );
896 if ( pBox )
897 GetTabRows_( rToFill, pBox );
900 void SwFEShell::SetMouseTabRows( const SwTabCols &rNew, bool bCurColOnly, const Point &rPt )
902 const SwFrame *pBox = GetBox( rPt );
903 if( pBox )
905 CurrShell aCurr( this );
906 StartAllAction();
907 GetDoc()->SetTabRows( rNew, bCurColOnly, static_cast<const SwCellFrame*>(pBox) );
908 EndAllActionAndCall();
912 void SwFEShell::SetRowSplit( const SwFormatRowSplit& rNew )
914 CurrShell aCurr( this );
915 StartAllAction();
916 GetDoc()->SetRowSplit( *getShellCursor( false ), rNew );
917 EndAllActionAndCall();
920 std::unique_ptr<SwFormatRowSplit> SwFEShell::GetRowSplit() const
922 return SwDoc::GetRowSplit( *getShellCursor( false ) );
925 void SwFEShell::SetRowHeight( const SwFormatFrameSize &rNew )
927 CurrShell aCurr( this );
928 StartAllAction();
929 GetDoc()->SetRowHeight( *getShellCursor( false ), rNew );
930 EndAllActionAndCall();
933 std::unique_ptr<SwFormatFrameSize> SwFEShell::GetRowHeight() const
935 return SwDoc::GetRowHeight( *getShellCursor( false ) );
938 bool SwFEShell::BalanceRowHeight( bool bTstOnly, const bool bOptimize )
940 CurrShell aCurr( this );
941 if( !bTstOnly )
942 StartAllAction();
943 bool bRet = GetDoc()->BalanceRowHeight( *getShellCursor( false ), bTstOnly, bOptimize );
944 if( !bTstOnly )
945 EndAllActionAndCall();
946 return bRet;
949 void SwFEShell::SetRowBackground( const SvxBrushItem &rNew )
951 CurrShell aCurr( this );
952 StartAllAction();
953 GetDoc()->SetRowBackground( *getShellCursor( false ), rNew );
954 EndAllActionAndCall();
957 bool SwFEShell::GetRowBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
959 return SwDoc::GetRowBackground( *getShellCursor( false ), rToFill );
962 void SwFEShell::SetTabBorders( const SfxItemSet& rSet )
964 CurrShell aCurr( this );
965 StartAllAction();
966 GetDoc()->SetTabBorders( *getShellCursor( false ), rSet );
967 EndAllActionAndCall();
970 void SwFEShell::SetTabLineStyle( const Color* pColor, bool bSetLine,
971 const editeng::SvxBorderLine* pBorderLine )
973 CurrShell aCurr( this );
974 StartAllAction();
975 GetDoc()->SetTabLineStyle( *getShellCursor( false ),
976 pColor, bSetLine, pBorderLine );
977 EndAllActionAndCall();
980 void SwFEShell::GetTabBorders( SfxItemSet& rSet ) const
982 SwDoc::GetTabBorders( *getShellCursor( false ), rSet );
985 void SwFEShell::SetBoxBackground( const SvxBrushItem &rNew )
987 CurrShell aCurr( this );
988 StartAllAction();
989 GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew );
990 EndAllActionAndCall();
993 bool SwFEShell::GetBoxBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
995 std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill);
996 bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp));
997 rToFill.reset(static_cast<SvxBrushItem*>(aTemp.release()));
998 return bRetval;
1001 void SwFEShell::SetBoxDirection( const SvxFrameDirectionItem& rNew )
1003 CurrShell aCurr( this );
1004 StartAllAction();
1005 GetDoc()->SetBoxAttr( *getShellCursor( false ), rNew );
1006 EndAllActionAndCall();
1009 bool SwFEShell::GetBoxDirection( std::unique_ptr<SvxFrameDirectionItem>& rToFill ) const
1011 std::unique_ptr<SfxPoolItem> aTemp = std::move(rToFill);
1012 bool bRetval(SwDoc::GetBoxAttr(*getShellCursor( false ), aTemp));
1013 rToFill.reset(static_cast<SvxFrameDirectionItem*>(aTemp.release()));
1014 return bRetval;
1017 void SwFEShell::SetBoxAlign( sal_uInt16 nAlign )
1019 CurrShell aCurr( this );
1020 StartAllAction();
1021 GetDoc()->SetBoxAlign( *getShellCursor( false ), nAlign );
1022 EndAllActionAndCall();
1025 sal_uInt16 SwFEShell::GetBoxAlign() const
1027 return SwDoc::GetBoxAlign( *getShellCursor( false ) );
1030 void SwFEShell::SetTabBackground( const SvxBrushItem &rNew )
1032 SwFrame *pFrame = GetCurrFrame();
1033 if( !pFrame || !pFrame->IsInTab() )
1034 return;
1036 CurrShell aCurr( this );
1037 StartAllAction();
1038 GetDoc()->SetAttr( rNew, *pFrame->ImplFindTabFrame()->GetFormat() );
1039 EndAllAction(); // no call, nothing changes!
1040 GetDoc()->getIDocumentState().SetModified();
1043 void SwFEShell::GetTabBackground( std::unique_ptr<SvxBrushItem>& rToFill ) const
1045 SwFrame *pFrame = GetCurrFrame();
1046 if( pFrame && pFrame->IsInTab() )
1047 rToFill = pFrame->ImplFindTabFrame()->GetFormat()->makeBackgroundBrushItem();
1050 bool SwFEShell::HasWholeTabSelection() const
1052 // whole table selected?
1053 if ( IsTableMode() )
1055 SwSelBoxes aBoxes;
1056 ::GetTableSelCrs( *this, aBoxes );
1057 if( !aBoxes.empty() )
1059 const SwTableNode *pTableNd = IsCursorInTable();
1060 return pTableNd &&
1061 aBoxes[0]->GetSttIdx() - 1 == pTableNd->EndOfSectionNode()->StartOfSectionIndex() &&
1062 aBoxes.back()->GetSttNd()->EndOfSectionIndex() + 1 == pTableNd->EndOfSectionIndex();
1065 return false;
1068 bool SwFEShell::HasBoxSelection() const
1070 if(!IsCursorInTable())
1071 return false;
1072 // whole table selected?
1073 if( IsTableMode() )
1074 return true;
1075 SwPaM* pPam = GetCursor();
1076 // empty boxes are also selected as the absence of selection
1077 bool bChg = false;
1078 if( pPam->GetPoint() == pPam->End())
1080 bChg = true;
1081 pPam->Exchange();
1083 SwNode* pNd;
1084 if( pPam->GetPoint()->GetNodeIndex() -1 ==
1085 ( pNd = &pPam->GetPointNode())->StartOfSectionIndex() &&
1086 !pPam->GetPoint()->GetContentIndex() &&
1087 pPam->GetMark()->GetNodeIndex() + 1 ==
1088 pNd->EndOfSectionIndex())
1090 SwNodeIndex aIdx( *pNd->EndOfSectionNode(), -1 );
1091 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
1092 if( !pCNd )
1094 pCNd = SwNodes::GoPrevious( &aIdx );
1095 assert(pCNd && "no ContentNode in box ??");
1097 if( pPam->GetMark()->GetContentIndex() == pCNd->Len() )
1099 if( bChg )
1100 pPam->Exchange();
1101 return true;
1104 if( bChg )
1105 pPam->Exchange();
1106 return false;
1109 void SwFEShell::ProtectCells()
1111 SvxProtectItem aProt( RES_PROTECT );
1112 aProt.SetContentProtect( true );
1114 CurrShell aCurr( this );
1115 StartAllAction();
1117 GetDoc()->SetBoxAttr( *getShellCursor( false ), aProt );
1119 if( !IsCursorReadonly() )
1121 if( IsTableMode() )
1122 ClearMark();
1123 ParkCursorInTab();
1125 EndAllActionAndCall();
1128 // cancel table selection
1129 void SwFEShell::UnProtectCells()
1131 CurrShell aCurr( this );
1132 StartAllAction();
1134 SwSelBoxes aBoxes;
1135 if( IsTableMode() )
1136 ::GetTableSelCrs( *this, aBoxes );
1137 else
1139 SwFrame *pFrame = GetCurrFrame();
1140 do {
1141 pFrame = pFrame->GetUpper();
1142 } while ( pFrame && !pFrame->IsCellFrame() );
1143 if( pFrame )
1145 SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1146 aBoxes.insert( pBox );
1150 if( !aBoxes.empty() )
1151 GetDoc()->UnProtectCells( aBoxes );
1153 EndAllActionAndCall();
1156 void SwFEShell::UnProtectTables()
1158 CurrShell aCurr( this );
1159 StartAllAction();
1160 GetDoc()->UnProtectTables( *GetCursor() );
1161 EndAllActionAndCall();
1164 bool SwFEShell::HasTableAnyProtection( const OUString* pTableName,
1165 bool* pFullTableProtection )
1167 return GetDoc()->HasTableAnyProtection( GetCursor()->GetPoint(), pTableName,
1168 pFullTableProtection );
1171 bool SwFEShell::CanUnProtectCells() const
1173 bool bUnProtectAvailable = false;
1174 const SwTableNode *pTableNd = IsCursorInTable();
1175 if( pTableNd && !pTableNd->IsProtect() )
1177 SwSelBoxes aBoxes;
1178 if( IsTableMode() )
1179 ::GetTableSelCrs( *this, aBoxes );
1180 else
1182 SwFrame *pFrame = GetCurrFrame();
1183 do {
1184 if ( pFrame )
1185 pFrame = pFrame->GetUpper();
1186 } while ( pFrame && !pFrame->IsCellFrame() );
1187 if( pFrame )
1189 SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1190 aBoxes.insert( pBox );
1193 if( !aBoxes.empty() )
1194 bUnProtectAvailable = ::HasProtectedCells( aBoxes );
1196 return bUnProtectAvailable;
1199 sal_uInt16 SwFEShell::GetRowsToRepeat() const
1201 const SwFrame *pFrame = GetCurrFrame();
1202 const SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr;
1203 if( pTab )
1204 return pTab->GetTable()->GetRowsToRepeat();
1205 return 0;
1208 void SwFEShell::SetRowsToRepeat( sal_uInt16 nSet )
1210 SwFrame *pFrame = GetCurrFrame();
1211 SwTabFrame *pTab = pFrame ? pFrame->FindTabFrame() : nullptr;
1212 if( pTab && pTab->GetTable()->GetRowsToRepeat() != nSet )
1214 SwWait aWait( *GetDoc()->GetDocShell(), true );
1215 CurrShell aCurr( this );
1216 StartAllAction();
1217 GetDoc()->SetRowsToRepeat( *pTab->GetTable(), nSet );
1218 EndAllActionAndCall();
1222 // returns the number of rows consecutively selected from top
1223 static sal_uInt16 lcl_GetRowNumber( const SwPosition& rPos )
1225 Point aTmpPt;
1226 const SwContentNode *pNd;
1227 const SwContentFrame *pFrame;
1229 std::pair<Point, bool> const tmp(aTmpPt, false);
1230 pNd = rPos.GetNode().GetContentNode();
1231 if( nullptr != pNd )
1232 pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), &rPos, &tmp);
1233 else
1234 pFrame = nullptr;
1236 const SwFrame* pRow = (pFrame && pFrame->IsInTab()) ? pFrame->GetUpper() : nullptr;
1238 while (pRow && (!pRow->GetUpper() || !pRow->GetUpper()->IsTabFrame()))
1239 pRow = pRow->GetUpper();
1241 if (!pRow)
1242 return USHRT_MAX;
1244 const SwTabFrame* pTabFrame = static_cast<const SwTabFrame*>(pRow->GetUpper());
1245 const SwTableLine* pTabLine = static_cast<const SwRowFrame*>(pRow)->GetTabLine();
1246 sal_uInt16 nRet = USHRT_MAX;
1247 sal_uInt16 nI = 0;
1248 while ( sal::static_int_cast<SwTableLines::size_type>(nI) < pTabFrame->GetTable()->GetTabLines().size() )
1250 if ( pTabFrame->GetTable()->GetTabLines()[ nI ] == pTabLine )
1252 nRet = nI;
1253 break;
1255 ++nI;
1258 return nRet;
1261 sal_uInt16 SwFEShell::GetRowSelectionFromTop() const
1263 sal_uInt16 nRet = 0;
1264 const SwPaM* pPaM = IsTableMode() ? GetTableCursor() : GetCursor_();
1265 const sal_uInt16 nPtLine = lcl_GetRowNumber( *pPaM->GetPoint() );
1267 if ( !IsTableMode() )
1269 nRet = 0 == nPtLine ? 1 : 0;
1271 else
1273 const sal_uInt16 nMkLine = lcl_GetRowNumber( *pPaM->GetMark() );
1275 if ( ( nPtLine == 0 && nMkLine != USHRT_MAX ) ||
1276 ( nMkLine == 0 && nPtLine != USHRT_MAX ) )
1278 nRet = std::max( nPtLine, nMkLine ) + 1;
1282 return nRet;
1286 * 1. case: bRepeat = true
1287 * returns true if the current frame is located inside a table headline in
1288 * a follow frame
1290 * 2. case: bRepeat = false
1291 * returns true if the current frame is located inside a table headline OR
1292 * inside the first line of a table!!!
1294 bool SwFEShell::CheckHeadline( bool bRepeat ) const
1296 bool bRet = false;
1297 if ( !IsTableMode() )
1299 SwFrame *pFrame = GetCurrFrame(); // DONE MULTIIHEADER
1300 SwTabFrame* pTab = (pFrame && pFrame->IsInTab()) ? pFrame->FindTabFrame() : nullptr;
1301 if (pTab)
1303 if ( bRepeat )
1305 bRet = pTab->IsFollow() && pTab->IsInHeadline( *pFrame );
1307 else
1309 bRet = static_cast<SwLayoutFrame*>(pTab->Lower())->IsAnLower( pFrame ) ||
1310 pTab->IsInHeadline( *pFrame );
1314 return bRet;
1317 void SwFEShell::AdjustCellWidth( const bool bBalance, const bool bNoShrink )
1319 CurrShell aCurr( this );
1320 StartAllAction();
1322 // switch on wait-cursor, as we do not know how
1323 // much content is affected
1324 TableWait aWait(std::numeric_limits<size_t>::max(), nullptr,
1325 *GetDoc()->GetDocShell());
1327 GetDoc()->AdjustCellWidth( *getShellCursor( false ), bBalance, bNoShrink );
1328 EndAllActionAndCall();
1331 bool SwFEShell::IsAdjustCellWidthAllowed( bool bBalance ) const
1333 // at least one row with content should be contained in the selection
1335 SwFrame *pFrame = GetCurrFrame();
1336 if( !pFrame || !pFrame->IsInTab() )
1337 return false;
1339 SwSelBoxes aBoxes;
1340 ::GetTableSelCrs( *this, aBoxes );
1342 if ( bBalance )
1343 return aBoxes.size() > 1;
1345 if ( aBoxes.empty() )
1349 pFrame = pFrame->GetUpper();
1351 while (pFrame && !pFrame->IsCellFrame());
1353 if (!pFrame)
1354 return false;
1356 SwTableBox *pBox = const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox());
1357 aBoxes.insert( pBox );
1360 for (size_t i = 0; i < aBoxes.size(); ++i)
1362 SwTableBox *pBox = aBoxes[i];
1363 if ( pBox->GetSttNd() )
1365 SwNodeIndex aIdx( *pBox->GetSttNd(), 1 );
1366 SwTextNode* pCNd = aIdx.GetNode().GetTextNode();
1367 if( !pCNd )
1368 pCNd = static_cast<SwTextNode*>(GetDoc()->GetNodes().GoNext( &aIdx ));
1370 while ( pCNd )
1372 if (!pCNd->GetText().isEmpty())
1373 return true;
1374 ++aIdx;
1375 pCNd = aIdx.GetNode().GetTextNode();
1379 return false;
1382 void SwFEShell::SetTableStyle(const OUString& rStyleName)
1384 // make sure SwDoc has the style
1385 SwTableAutoFormat *pTableFormat = GetDoc()->GetTableStyles().FindAutoFormat(rStyleName);
1386 if (!pTableFormat)
1387 return;
1389 SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
1390 if (!pTableNode)
1391 return;
1393 // set the name & update
1394 UpdateTableStyleFormatting(pTableNode, false, &rStyleName);
1397 // AutoFormat for the table/table selection
1398 bool SwFEShell::SetTableStyle(const SwTableAutoFormat& rStyle)
1400 // make sure SwDoc has the style
1401 GetDoc()->GetTableStyles().AddAutoFormat(rStyle);
1403 SwTableNode *pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
1404 if (!pTableNode)
1405 return false;
1407 // set the name & update
1408 return UpdateTableStyleFormatting(pTableNode, false, &rStyle.GetName());
1411 bool SwFEShell::UpdateTableStyleFormatting(SwTableNode *pTableNode,
1412 bool bResetDirect, OUString const*const pStyleName)
1414 if (!pTableNode)
1416 pTableNode = const_cast<SwTableNode*>(IsCursorInTable());
1417 if (!pTableNode || pTableNode->GetTable().IsTableComplex())
1418 return false;
1421 OUString const aTableStyleName(pStyleName
1422 ? *pStyleName
1423 : pTableNode->GetTable().GetTableStyleName());
1424 SwTableAutoFormat* pTableStyle = GetDoc()->GetTableStyles().FindAutoFormat(aTableStyleName);
1425 if (!pTableStyle)
1426 return false;
1428 SwSelBoxes aBoxes;
1430 // whole table or only current selection
1431 if( IsTableMode() )
1432 ::GetTableSelCrs( *this, aBoxes );
1433 else
1435 const SwTableSortBoxes& rTBoxes = pTableNode->GetTable().GetTabSortBoxes();
1436 for (size_t n = 0; n < rTBoxes.size(); ++n)
1438 SwTableBox* pBox = rTBoxes[ n ];
1439 aBoxes.insert( pBox );
1443 bool bRet;
1444 if( !aBoxes.empty() )
1446 CurrShell aCurr( this );
1447 StartAllAction();
1448 bRet = GetDoc()->SetTableAutoFormat(
1449 aBoxes, *pTableStyle, bResetDirect, pStyleName != nullptr);
1450 ClearFEShellTabCols(*GetDoc(), nullptr);
1451 EndAllActionAndCall();
1453 else
1454 bRet = false;
1455 return bRet;
1458 bool SwFEShell::GetTableAutoFormat( SwTableAutoFormat& rGet )
1460 const SwTableNode *pTableNd = IsCursorInTable();
1461 if( !pTableNd || pTableNd->GetTable().IsTableComplex() )
1462 return false;
1464 SwSelBoxes aBoxes;
1466 if ( !IsTableMode() ) // if cursor are not current
1467 GetCursor();
1469 // whole table or only current selection
1470 if( IsTableMode() )
1471 ::GetTableSelCrs( *this, aBoxes );
1472 else
1474 const SwTableSortBoxes& rTBoxes = pTableNd->GetTable().GetTabSortBoxes();
1475 for (size_t n = 0; n < rTBoxes.size(); ++n)
1477 SwTableBox* pBox = rTBoxes[ n ];
1478 aBoxes.insert( pBox );
1482 return GetDoc()->GetTableAutoFormat( aBoxes, rGet );
1485 bool SwFEShell::DeleteTableSel()
1487 // check if SPoint/Mark of current cursor are in a table
1488 SwFrame *pFrame = GetCurrFrame();
1489 if( !pFrame || !pFrame->IsInTab() )
1490 return false;
1492 if( dynamic_cast< const SwDDETable* >(pFrame->ImplFindTabFrame()->GetTable()) != nullptr )
1494 ErrorHandler::HandleError( ERR_TBLDDECHG_ERROR, GetFrameWeld(GetDoc()->GetDocShell()),
1495 DialogMask::MessageInfo | DialogMask::ButtonsOk );
1496 return false;
1499 CurrShell aCurr( this );
1500 StartAllAction();
1502 // search boxes via the layout
1503 bool bRet;
1504 SwSelBoxes aBoxes;
1505 GetTableSelCrs( *this, aBoxes );
1506 if( !aBoxes.empty() )
1508 TableWait aWait( aBoxes.size(), pFrame, *GetDoc()->GetDocShell() );
1510 // cursor should be removed from deletion area.
1511 // Put them behind/on the table; via the document
1512 // position they'll be set to the old position
1513 while( !pFrame->IsCellFrame() )
1514 pFrame = pFrame->GetUpper();
1515 ParkCursor( *static_cast<SwCellFrame*>(pFrame)->GetTabBox()->GetSttNd() );
1517 bRet = GetDoc()->DeleteRowCol( aBoxes );
1519 ClearFEShellTabCols(*GetDoc(), nullptr);
1521 else
1522 bRet = false;
1523 EndAllActionAndCall();
1524 return bRet;
1527 size_t SwFEShell::GetCurTabColNum() const
1529 //!!!GetCurMouseTabColNum() mitpflegen!!!!
1530 SwFrame *pFrame = GetCurrFrame();
1531 OSL_ENSURE( pFrame, "Cursor parked?" );
1533 // check if SPoint/Mark of current cursor are in a table
1534 if (!pFrame || !pFrame->IsInTab())
1535 return 0;
1539 // JP 26.09.95: why compare with ContentFrame
1540 // and not with CellFrame ????
1541 pFrame = pFrame->GetUpper();
1542 } while (pFrame && !pFrame->IsCellFrame());
1544 if (!pFrame)
1545 return 0;
1547 size_t nRet = 0;
1549 SwRectFnSet aRectFnSet(pFrame);
1551 const SwPageFrame* pPage = pFrame->FindPageFrame();
1553 // get TabCols, as only via these we get to the position
1554 SwTabCols aTabCols;
1555 GetTabCols( aTabCols );
1557 if( pFrame->FindTabFrame()->IsRightToLeft() )
1559 tools::Long nX = aRectFnSet.GetRight(pFrame->getFrameArea()) - aRectFnSet.GetLeft(pPage->getFrameArea());
1561 const tools::Long nRight = aTabCols.GetLeftMin() + aTabCols.GetRight();
1563 if ( !::IsSame( nX, nRight ) )
1565 nX = nRight - nX + aTabCols.GetLeft();
1566 for ( size_t i = 0; i < aTabCols.Count(); ++i )
1567 if ( ::IsSame( nX, aTabCols[i] ) )
1569 nRet = i + 1;
1570 break;
1574 else
1576 const tools::Long nX = aRectFnSet.GetLeft(pFrame->getFrameArea()) -
1577 aRectFnSet.GetLeft(pPage->getFrameArea());
1579 const tools::Long nLeft = aTabCols.GetLeftMin();
1581 if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) )
1583 for ( size_t i = 0; i < aTabCols.Count(); ++i )
1584 if ( ::IsSame( nX, nLeft + aTabCols[i] ) )
1586 nRet = i + 1;
1587 break;
1591 return nRet;
1594 static const SwFrame *lcl_FindFrameInTab( const SwLayoutFrame *pLay, const Point &rPt, SwTwips nFuzzy )
1596 const SwFrame *pFrame = pLay->Lower();
1598 while( pFrame && pLay->IsAnLower( pFrame ) )
1600 if ( pFrame->getFrameArea().IsNear( rPt, nFuzzy ) )
1602 if ( pFrame->IsLayoutFrame() )
1604 const SwFrame *pTmp = ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), rPt, nFuzzy );
1605 if ( pTmp )
1606 return pTmp;
1609 return pFrame;
1612 pFrame = pFrame->FindNext();
1615 return nullptr;
1618 static const SwCellFrame *lcl_FindFrame( const SwLayoutFrame *pLay, const Point &rPt,
1619 SwTwips nFuzzy, bool* pbRow, bool* pbCol )
1621 // bMouseMoveRowCols :
1622 // Method is called for
1623 // - Moving columns/rows with the mouse or
1624 // - Enhanced table selection
1625 const bool bMouseMoveRowCols = nullptr == pbCol;
1627 bool bCloseToRow = false;
1628 bool bCloseToCol = false;
1630 const SwFrame *pFrame = pLay->ContainsContent();
1631 const SwFrame* pRet = nullptr;
1633 if ( pFrame )
1637 if ( pFrame->IsInTab() )
1638 pFrame = const_cast<SwFrame*>(pFrame)->ImplFindTabFrame();
1640 if (!pFrame)
1641 break;
1643 if ( pFrame->IsTabFrame() )
1645 Point aPt( rPt );
1646 bool bSearchForFrameInTab = true;
1647 SwTwips nTmpFuzzy = nFuzzy;
1649 if ( !bMouseMoveRowCols )
1651 // We ignore nested tables for the enhanced table selection:
1652 while ( pFrame->GetUpper()->IsInTab() )
1653 pFrame = pFrame->GetUpper()->FindTabFrame();
1655 // We first check if the given point is 'close' to the left or top
1656 // border of the table frame:
1657 OSL_ENSURE( pFrame, "Nested table frame without outer table" );
1658 SwRectFnSet aRectFnSet(pFrame);
1659 const bool bRTL = pFrame->IsRightToLeft();
1661 SwRect aTabRect = pFrame->getFramePrintArea();
1662 aTabRect.Pos() += pFrame->getFrameArea().Pos();
1664 const SwTwips nLeft = bRTL ?
1665 aRectFnSet.GetRight(aTabRect) :
1666 aRectFnSet.GetLeft(aTabRect);
1667 const SwTwips nTop = aRectFnSet.GetTop(aTabRect);
1669 SwTwips const rPointX = aRectFnSet.IsVert() ? aPt.Y() : aPt.X();
1670 SwTwips const rPointY = aRectFnSet.IsVert() ? aPt.X() : aPt.Y();
1672 const SwTwips nXDiff = aRectFnSet.XDiff( nLeft, rPointX ) * ( bRTL ? -1 : 1 );
1673 const SwTwips nYDiff = aRectFnSet.YDiff( nTop, rPointY );
1675 bCloseToRow = nXDiff >= 0 && nXDiff < nFuzzy;
1676 bCloseToCol = nYDiff >= 0 && nYDiff < nFuzzy;
1678 if ( bCloseToCol && 2 * nYDiff > nFuzzy )
1680 const SwFrame* pPrev = pFrame->GetPrev();
1681 if ( pPrev )
1683 SwRect aPrevRect = pPrev->getFramePrintArea();
1684 aPrevRect.Pos() += pPrev->getFrameArea().Pos();
1686 if( aPrevRect.Contains( rPt ) )
1688 bCloseToCol = false;
1694 // If we found the point to be 'close' to the left or top border
1695 // of the table frame, we adjust the point to be on that border:
1696 if ( bCloseToRow && bCloseToCol )
1697 aPt = bRTL ? aTabRect.TopRight() : aRectFnSet.GetPos(aTabRect);
1698 else if ( bCloseToRow )
1699 aRectFnSet.IsVert() ? aPt.setY(nLeft) : aPt.setX(nLeft);
1700 else if ( bCloseToCol )
1701 aRectFnSet.IsVert() ? aPt.setX(nTop) : aPt.setY(nTop);
1703 if ( !bCloseToRow && !bCloseToCol )
1704 bSearchForFrameInTab = false;
1706 // Since the point has been adjusted, we call lcl_FindFrameInTab()
1707 // with a fuzzy value of 1:
1708 nTmpFuzzy = 1;
1711 const SwFrame* pTmp = bSearchForFrameInTab ?
1712 ::lcl_FindFrameInTab( static_cast<const SwLayoutFrame*>(pFrame), aPt, nTmpFuzzy ) :
1713 nullptr;
1715 if ( pTmp )
1717 pFrame = pTmp;
1718 break;
1721 pFrame = pFrame->FindNextCnt();
1723 } while ( pFrame && pLay->IsAnLower( pFrame ) );
1726 if ( pFrame && pFrame->IsInTab() && pLay->IsAnLower( pFrame ) )
1730 // We allow mouse drag of table borders within nested tables,
1731 // but disallow hotspot selection of nested tables.
1732 if ( bMouseMoveRowCols )
1734 // find the next cell frame
1735 while ( pFrame && !pFrame->IsCellFrame() )
1736 pFrame = pFrame->GetUpper();
1738 else
1740 // find the most upper cell frame:
1741 while ( pFrame &&
1742 ( !pFrame->IsCellFrame() ||
1743 !pFrame->GetUpper()->GetUpper()->IsTabFrame() ||
1744 pFrame->GetUpper()->GetUpper()->GetUpper()->IsInTab() ) )
1745 pFrame = pFrame->GetUpper();
1748 if ( pFrame ) // Note: this condition should be the same like the while condition!!!
1750 // #i32329# Enhanced table selection
1751 // used for hotspot selection of tab/cols/rows
1752 if ( !bMouseMoveRowCols )
1755 OSL_ENSURE( pbCol && pbRow, "pbCol or pbRow missing" );
1757 if ( bCloseToRow || bCloseToCol )
1759 *pbRow = bCloseToRow;
1760 *pbCol = bCloseToCol;
1761 pRet = pFrame;
1762 break;
1765 else
1767 // used for mouse move of columns/rows
1768 const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
1769 SwRect aTabRect = pTabFrame->getFramePrintArea();
1770 aTabRect.Pos() += pTabFrame->getFrameArea().Pos();
1772 SwRectFnSet aRectFnSet(pTabFrame);
1774 const SwTwips nTabTop = aRectFnSet.GetTop(aTabRect);
1775 const SwTwips nMouseTop = aRectFnSet.IsVert() ? rPt.X() : rPt.Y();
1777 // Do not allow to drag upper table border:
1778 if ( !::IsSame( nTabTop, nMouseTop ) )
1780 if ( ::IsSame( pFrame->getFrameArea().Left(), rPt.X() ) ||
1781 ::IsSame( pFrame->getFrameArea().Right(),rPt.X() ) )
1783 if ( pbRow ) *pbRow = false;
1784 pRet = pFrame;
1785 break;
1787 if ( ::IsSame( pFrame->getFrameArea().Top(), rPt.Y() ) ||
1788 ::IsSame( pFrame->getFrameArea().Bottom(),rPt.Y() ) )
1790 if ( pbRow ) *pbRow = true;
1791 pRet = pFrame;
1792 break;
1797 pFrame = pFrame->GetUpper();
1799 } while ( pFrame );
1802 // robust:
1803 OSL_ENSURE( !pRet || pRet->IsCellFrame(), "lcl_FindFrame() is supposed to find a cell frame!" );
1804 return pRet && pRet->IsCellFrame() ? static_cast<const SwCellFrame*>(pRet) : nullptr;
1807 // pbCol = 0 => Used for moving table rows/cols with mouse
1808 // pbCol != 0 => Used for selecting table/rows/cols
1810 #define ENHANCED_TABLE_SELECTION_FUZZY 10
1812 const SwFrame* SwFEShell::GetBox( const Point &rPt, bool* pbRow, bool* pbCol ) const
1814 const SwPageFrame *pPage = static_cast<SwPageFrame*>(GetLayout()->Lower());
1815 vcl::Window* pOutWin = GetWin();
1816 SwTwips nFuzzy = COLFUZZY;
1817 if( pOutWin )
1819 // #i32329# Enhanced table selection
1820 SwTwips nSize = pbCol ? ENHANCED_TABLE_SELECTION_FUZZY : RULER_MOUSE_MARGINWIDTH;
1821 Size aTmp( nSize, nSize );
1822 aTmp = pOutWin->PixelToLogic( aTmp );
1823 nFuzzy = aTmp.Width();
1826 while ( pPage && !pPage->getFrameArea().IsNear( rPt, nFuzzy ) )
1827 pPage = static_cast<const SwPageFrame*>(pPage->GetNext());
1829 const SwCellFrame *pFrame = nullptr;
1830 if ( pPage )
1832 // We cannot search the box by GetModelPositionForViewPoint or GetContentPos.
1833 // This would lead to a performance collapse for documents
1834 // with a lot of paragraphs/tables on one page
1835 //(BrowseMode!)
1837 // check flys first
1838 if ( pPage->GetSortedObjs() )
1840 for ( size_t i = 0; !pFrame && i < pPage->GetSortedObjs()->size(); ++i )
1842 SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i];
1843 if ( auto pFlyFrame = pObj->DynCastFlyFrame() )
1845 pFrame = lcl_FindFrame( pFlyFrame, rPt, nFuzzy, pbRow, pbCol );
1849 const SwLayoutFrame *pLay = static_cast<const SwLayoutFrame*>(pPage->Lower());
1850 while ( pLay && !pFrame )
1852 pFrame = lcl_FindFrame( pLay, rPt, nFuzzy, pbRow, pbCol );
1853 pLay = static_cast<const SwLayoutFrame*>(pLay->GetNext());
1856 return pFrame;
1859 /* Helper function*/
1860 /* calculated the distance between Point rC and Line Segment (rA, rB) */
1861 static double lcl_DistancePoint2Segment( const Point& rA, const Point& rB, const Point& rC )
1863 double nRet = 0;
1865 const basegfx::B2DVector aBC( rC.X() - rB.X(), rC.Y() - rB.Y() );
1866 const basegfx::B2DVector aAB( rB.X() - rA.X(), rB.Y() - rA.Y() );
1867 const double nDot1 = aBC.scalar( aAB );
1869 if ( nDot1 > 0 ) // check outside case 1
1870 nRet = aBC.getLength();
1871 else
1873 const basegfx::B2DVector aAC( rC.X() - rA.X(), rC.Y() - rA.Y() );
1874 const basegfx::B2DVector aBA( rA.X() - rB.X(), rA.Y() - rB.Y() );
1875 const double nDot2 = aAC.scalar( aBA );
1877 if ( nDot2 > 0 ) // check outside case 2
1878 nRet = aAC.getLength();
1879 else
1881 const double nDiv = aAB.getLength();
1882 nRet = nDiv ? aAB.cross( aAC ) / nDiv : 0;
1886 return std::abs(nRet);
1889 /* Helper function*/
1890 static Point lcl_ProjectOntoClosestTableFrame( const SwTabFrame& rTab, const Point& rPoint, bool bRowDrag )
1892 Point aRet( rPoint );
1893 const SwTabFrame* pCurrentTab = &rTab;
1894 const bool bVert = pCurrentTab->IsVertical();
1895 const bool bRTL = pCurrentTab->IsRightToLeft();
1897 // Western Layout:
1898 // bRowDrag = true => compare to left border of table
1899 // bRowDrag = false => compare to top border of table
1901 // Asian Layout:
1902 // bRowDrag = true => compare to right border of table
1903 // bRowDrag = false => compare to top border of table
1905 // RTL Layout:
1906 // bRowDrag = true => compare to right border of table
1907 // bRowDrag = false => compare to top border of table
1908 bool bLeft = false;
1909 bool bRight = false;
1911 if ( bRowDrag )
1913 if ( bVert || bRTL )
1914 bRight = true;
1915 else
1916 bLeft = true;
1919 // used to find the minimal distance
1920 double nMin = -1;
1921 Point aMin1;
1922 Point aMin2;
1924 Point aS1;
1925 Point aS2;
1927 while ( pCurrentTab )
1929 SwRect aTabRect( pCurrentTab->getFramePrintArea() );
1930 aTabRect += pCurrentTab->getFrameArea().Pos();
1932 if ( bLeft )
1934 // distance to left table border
1935 aS1 = aTabRect.TopLeft();
1936 aS2 = aTabRect.BottomLeft();
1938 else if ( bRight )
1940 // distance to right table border
1941 aS1 = aTabRect.TopRight();
1942 aS2 = aTabRect.BottomRight();
1944 else //if ( bTop )
1946 // distance to top table border
1947 aS1 = aTabRect.TopLeft();
1948 aS2 = aTabRect.TopRight();
1951 const double nDist = lcl_DistancePoint2Segment( aS1, aS2, rPoint );
1953 if ( nDist < nMin || -1 == nMin )
1955 aMin1 = aS1;
1956 aMin2 = aS2;
1957 nMin = nDist;
1960 pCurrentTab = pCurrentTab->GetFollow();
1963 // project onto closest line:
1964 if ( bLeft || bRight )
1966 aRet.setX(aMin1.getX());
1967 if ( aRet.getY() > aMin2.getY() )
1968 aRet.setY(aMin2.getY());
1969 else if ( aRet.getY() < aMin1.getY() )
1970 aRet.setY(aMin1.getY());
1972 else
1974 aRet.setY(aMin1.getY());
1975 if ( aRet.getX() > aMin2.getX() )
1976 aRet.setX(aMin2.getX());
1977 else if ( aRet.getX() < aMin1.getX() )
1978 aRet.setX(aMin1.getX());
1981 return aRet;
1984 // #i32329# Enhanced table selection
1985 bool SwFEShell::SelTableRowCol( const Point& rPt, const Point* pEnd, bool bRowDrag )
1987 bool bRet = false;
1988 Point aEndPt;
1989 if ( pEnd )
1990 aEndPt = *pEnd;
1992 SwPosition* ppPos[2] = { nullptr, nullptr };
1993 Point paPt [2] = { rPt, aEndPt };
1994 bool pbRow[2] = { false, false };
1995 bool pbCol[2] = { false, false };
1997 // pEnd is set during dragging.
1998 for ( sal_uInt16 i = 0; i < ( pEnd ? 2 : 1 ); ++i )
2000 const SwCellFrame* pFrame =
2001 static_cast<const SwCellFrame*>(GetBox( paPt[i], &pbRow[i], &pbCol[i] ) );
2003 if( pFrame )
2005 while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() )
2006 pFrame = static_cast<const SwCellFrame*>( static_cast<const SwLayoutFrame*>( pFrame->Lower() )->Lower() );
2007 if( pFrame && pFrame->GetTabBox()->GetSttNd() &&
2008 pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() )
2009 pFrame = nullptr;
2012 if ( pFrame )
2014 const SwContentFrame* pContent = ::GetCellContent( *pFrame );
2016 if ( pContent && pContent->IsTextFrame() )
2019 ppPos[i] = new SwPosition(static_cast<SwTextFrame const*>(pContent)->MapViewToModelPos(TextFrameIndex(0)));
2021 // paPt[i] will not be used any longer, now we use it to store
2022 // a position inside the content frame
2023 paPt[i] = pContent->getFrameArea().Center();
2027 // no calculation of end frame if start frame has not been found.
2028 if ( 1 == i || !ppPos[0] || !pEnd || !pFrame )
2029 break;
2031 // find 'closest' table frame to pEnd:
2032 const SwTabFrame* pCurrentTab = pFrame->FindTabFrame();
2033 if ( pCurrentTab->IsFollow() )
2034 pCurrentTab = pCurrentTab->FindMaster( true );
2036 const Point aProjection = lcl_ProjectOntoClosestTableFrame( *pCurrentTab, *pEnd, bRowDrag );
2037 paPt[1] = aProjection;
2040 if ( ppPos[0] )
2042 SwShellCursor* pCursor = GetCursor_();
2043 SwCursorSaveState aSaveState( *pCursor );
2044 SwPosition aOldPos( *pCursor->GetPoint() );
2046 pCursor->DeleteMark();
2047 *pCursor->GetPoint() = *ppPos[0];
2048 pCursor->GetPtPos() = paPt[0];
2050 if ( !pCursor->IsInProtectTable() )
2052 bool bNewSelection = true;
2054 if ( ppPos[1] )
2056 if ( ppPos[1]->GetNode().StartOfSectionNode() !=
2057 aOldPos.GetNode().StartOfSectionNode() )
2059 pCursor->SetMark();
2060 SwCursorSaveState aSaveState2( *pCursor );
2061 *pCursor->GetPoint() = *ppPos[1];
2062 pCursor->GetPtPos() = paPt[1];
2064 if ( pCursor->IsInProtectTable( false, false ) )
2066 pCursor->RestoreSavePos();
2067 bNewSelection = false;
2070 else
2072 pCursor->RestoreSavePos();
2073 bNewSelection = false;
2077 if ( bNewSelection )
2079 // #i35543# SelTableRowCol should remove any existing
2080 // table cursor:
2081 if ( IsTableMode() )
2082 TableCursorToCursor();
2084 if ( pbRow[0] && pbCol[0] )
2085 bRet = SwCursorShell::SelTable();
2086 else if ( pbRow[0] )
2087 bRet = SwCursorShell::SelTableRowOrCol( true, true );
2088 else if ( pbCol[0] )
2089 bRet = SwCursorShell::SelTableRowOrCol( false, true );
2091 else
2092 bRet = true;
2095 delete ppPos[0];
2096 delete ppPos[1];
2099 return bRet;
2102 SwTab SwFEShell::WhichMouseTabCol( const Point &rPt ) const
2104 SwTab nRet = SwTab::COL_NONE;
2105 bool bRow = false;
2106 bool bCol = false;
2107 bool bSelect = false;
2109 // First try: Do we get the row/col move cursor?
2110 const SwCellFrame* pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow ));
2112 if ( !pFrame )
2114 // Second try: Do we get the row/col/tab selection cursor?
2115 pFrame = static_cast<const SwCellFrame*>(GetBox( rPt, &bRow, &bCol ));
2116 bSelect = true;
2119 if( pFrame )
2121 while( pFrame && pFrame->Lower() && pFrame->Lower()->IsRowFrame() )
2122 pFrame = static_cast<const SwCellFrame*>(static_cast<const SwLayoutFrame*>(pFrame->Lower())->Lower());
2123 if( pFrame && pFrame->GetTabBox()->GetSttNd() &&
2124 pFrame->GetTabBox()->GetSttNd()->IsInProtectSect() )
2125 pFrame = nullptr;
2128 if( pFrame )
2130 if ( !bSelect )
2132 if ( pFrame->IsVertical() )
2133 nRet = bRow ? SwTab::COL_VERT : SwTab::ROW_VERT;
2134 else
2135 nRet = bRow ? SwTab::ROW_HORI : SwTab::COL_HORI;
2137 else
2139 const SwTabFrame* pTabFrame = pFrame->FindTabFrame();
2140 if ( pTabFrame->IsVertical() )
2142 if ( bRow && bCol )
2144 nRet = SwTab::SEL_VERT;
2146 else if ( bRow )
2148 nRet = SwTab::ROWSEL_VERT;
2150 else if ( bCol )
2152 nRet = SwTab::COLSEL_VERT;
2155 else
2157 if ( bRow && bCol )
2159 nRet = pTabFrame->IsRightToLeft() ?
2160 SwTab::SEL_HORI_RTL :
2161 SwTab::SEL_HORI;
2163 else if ( bRow )
2165 nRet = pTabFrame->IsRightToLeft() ?
2166 SwTab::ROWSEL_HORI_RTL :
2167 SwTab::ROWSEL_HORI;
2169 else if ( bCol )
2171 nRet = SwTab::COLSEL_HORI;
2177 return nRet;
2180 // -> #i23726#
2181 SwTextNode * SwFEShell::GetNumRuleNodeAtPos( const Point &rPt)
2183 SwTextNode * pResult = nullptr;
2185 SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel);
2187 if( GetContentAtPos(rPt, aContentAtPos) && aContentAtPos.aFnd.pNode)
2188 pResult = aContentAtPos.aFnd.pNode->GetTextNode();
2190 return pResult;
2193 bool SwFEShell::IsNumLabel( const Point &rPt, int nMaxOffset )
2195 bool bResult = false;
2197 SwContentAtPos aContentAtPos(IsAttrAtPos::NumLabel);
2199 if( GetContentAtPos(rPt, aContentAtPos))
2201 if ((nMaxOffset >= 0 && aContentAtPos.nDist <= nMaxOffset) ||
2202 (nMaxOffset < 0))
2203 bResult = true;
2206 return bResult;
2208 // <- #i23726#
2210 // #i42921#
2211 bool SwFEShell::IsVerticalModeAtNdAndPos( const SwTextNode& _rTextNode,
2212 const Point& _rDocPos )
2214 bool bRet( false );
2216 const SvxFrameDirection nTextDir =
2217 _rTextNode.GetTextDirection( SwPosition(_rTextNode), &_rDocPos );
2218 switch ( nTextDir )
2220 case SvxFrameDirection::Unknown:
2221 case SvxFrameDirection::Horizontal_RL_TB:
2222 case SvxFrameDirection::Horizontal_LR_TB:
2224 bRet = false;
2226 break;
2227 case SvxFrameDirection::Vertical_LR_TB:
2228 case SvxFrameDirection::Vertical_RL_TB:
2230 bRet = true;
2232 break;
2233 default: break;
2236 return bRet;
2239 void SwFEShell::GetMouseTabCols( SwTabCols &rToFill, const Point &rPt ) const
2241 const SwFrame *pBox = GetBox( rPt );
2242 if ( pBox )
2243 GetTabCols_( rToFill, pBox );
2246 void SwFEShell::SetMouseTabCols( const SwTabCols &rNew, bool bCurRowOnly,
2247 const Point &rPt )
2249 const SwFrame *pBox = GetBox( rPt );
2250 if( pBox )
2252 CurrShell aCurr( this );
2253 StartAllAction();
2254 GetDoc()->SetTabCols( rNew, bCurRowOnly, static_cast<const SwCellFrame*>(pBox) );
2255 EndAllActionAndCall();
2259 sal_uInt16 SwFEShell::GetCurMouseColNum( const Point &rPt ) const
2261 return GetCurColNum_( GetBox( rPt ), nullptr );
2264 size_t SwFEShell::GetCurMouseTabColNum( const Point &rPt ) const
2266 //!!!GetCurTabColNum() mitpflegen!!!!
2267 size_t nRet = 0;
2269 const SwFrame *pFrame = GetBox( rPt );
2270 OSL_ENSURE( pFrame, "Table not found" );
2271 if( pFrame )
2273 const tools::Long nX = pFrame->getFrameArea().Left();
2275 // get TabCols, only via these we get the position
2276 SwTabCols aTabCols;
2277 GetMouseTabCols( aTabCols, rPt );
2279 const tools::Long nLeft = aTabCols.GetLeftMin();
2281 if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) )
2283 for ( size_t i = 0; i < aTabCols.Count(); ++i )
2284 if ( ::IsSame( nX, nLeft + aTabCols[i] ) )
2286 nRet = i + 1;
2287 break;
2291 return nRet;
2294 void ClearFEShellTabCols(SwDoc & rDoc, SwTabFrame const*const pFrame)
2296 auto const pShell(rDoc.getIDocumentLayoutAccess().GetCurrentViewShell());
2297 if (pShell)
2299 for (SwViewShell& rCurrentShell : pShell->GetRingContainer())
2301 if (auto const pFE = dynamic_cast<SwFEShell *>(&rCurrentShell))
2303 pFE->ClearColumnRowCache(pFrame);
2309 void SwFEShell::ClearColumnRowCache(SwTabFrame const*const pFrame)
2311 if (m_pColumnCache)
2313 if (pFrame == nullptr || pFrame == m_pColumnCache->pLastTabFrame)
2315 m_pColumnCache.reset();
2318 if (m_pRowCache)
2320 if (pFrame == nullptr || pFrame == m_pRowCache->pLastTabFrame)
2322 m_pRowCache.reset();
2327 void SwFEShell::GetTableAttr( SfxItemSet &rSet ) const
2329 SwFrame *pFrame = GetCurrFrame();
2330 if( pFrame && pFrame->IsInTab() )
2331 rSet.Put( pFrame->ImplFindTabFrame()->GetFormat()->GetAttrSet() );
2334 void SwFEShell::SetTableAttr( const SfxItemSet &rNew )
2336 SwFrame *pFrame = GetCurrFrame();
2337 if( pFrame && pFrame->IsInTab() )
2339 CurrShell aCurr( this );
2340 StartAllAction();
2341 SwTabFrame *pTab = pFrame->FindTabFrame();
2342 pTab->GetTable()->SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
2343 GetDoc()->SetAttr( rNew, *pTab->GetFormat() );
2344 GetDoc()->getIDocumentState().SetModified();
2345 EndAllActionAndCall();
2349 // change a cell width/cell height/column width/row height
2350 void SwFEShell::SetColRowWidthHeight( TableChgWidthHeightType eType, sal_uInt16 nDiff )
2352 SwFrame *pFrame = GetCurrFrame();
2353 if( !pFrame || !pFrame->IsInTab() )
2354 return;
2356 CurrShell aCurr( this );
2357 StartAllAction();
2359 do {
2360 pFrame = pFrame->GetUpper();
2361 } while( !pFrame->IsCellFrame() );
2363 SwTabFrame *pTab = pFrame->ImplFindTabFrame();
2365 // if the table is in relative values (USHRT_MAX)
2366 // then it should be recalculated to absolute values now
2367 const SwFormatFrameSize& rTableFrameSz = pTab->GetFormat()->GetFrameSize();
2368 SwRectFnSet aRectFnSet(pTab);
2369 tools::Long nPrtWidth = aRectFnSet.GetWidth(pTab->getFramePrintArea());
2370 TableChgWidthHeightType eTypePos = extractPosition(eType);
2371 if( TableChgMode::VarWidthChangeAbs == pTab->GetTable()->GetTableChgMode() &&
2372 ( eTypePos == TableChgWidthHeightType::ColLeft || eTypePos == TableChgWidthHeightType::ColRight ) &&
2373 text::HoriOrientation::NONE == pTab->GetFormat()->GetHoriOrient().GetHoriOrient() &&
2374 nPrtWidth != rTableFrameSz.GetWidth() )
2376 SwFormatFrameSize aSz( rTableFrameSz );
2377 aSz.SetWidth( pTab->getFramePrintArea().Width() );
2378 pTab->GetFormat()->SetFormatAttr( aSz );
2381 SwTwips nLogDiff = nDiff;
2382 nLogDiff *= pTab->GetFormat()->GetFrameSize().GetWidth();
2383 nLogDiff /= nPrtWidth;
2385 /** The cells are destroyed in here */
2386 GetDoc()->SetColRowWidthHeight(
2387 *const_cast<SwTableBox*>(static_cast<SwCellFrame*>(pFrame)->GetTabBox()),
2388 eType, nDiff, nLogDiff );
2390 ClearFEShellTabCols(*GetDoc(), nullptr);
2391 EndAllActionAndCall();
2394 static bool lcl_IsFormulaSelBoxes( const SwTable& rTable, const SwTableBoxFormula& rFormula,
2395 SwCellFrames& rCells )
2397 SwTableBoxFormula aTmp( rFormula );
2398 SwSelBoxes aBoxes;
2399 aTmp.GetBoxesOfFormula(rTable, aBoxes);
2400 for (size_t nSelBoxes = aBoxes.size(); nSelBoxes; )
2402 SwTableBox* pBox = aBoxes[ --nSelBoxes ];
2404 if( std::none_of(rCells.begin(), rCells.end(), [&pBox](SwCellFrame* pFrame) { return pFrame->GetTabBox() == pBox; }) )
2405 return false;
2408 return true;
2411 // ask formula for auto-sum
2412 void SwFEShell::GetAutoSum( OUString& rFormula ) const
2414 SwFrame *pFrame = GetCurrFrame();
2415 SwTabFrame *pTab = pFrame ? pFrame->ImplFindTabFrame() : nullptr;
2416 if( !pTab )
2417 return;
2419 SwCellFrames aCells;
2420 OUString sFields;
2421 if( ::GetAutoSumSel( *this, aCells ))
2423 sal_uInt16 nW = 0;
2424 for( size_t n = aCells.size(); n; )
2426 SwCellFrame* pCFrame = aCells[ --n ];
2427 sal_uInt16 nBoxW = pCFrame->GetTabBox()->IsFormulaOrValueBox();
2428 if( !nBoxW )
2429 break;
2431 if( !nW )
2433 if( USHRT_MAX == nBoxW )
2434 continue; // skip space at beginning
2436 // formula only if box is contained
2437 if( RES_BOXATR_FORMULA == nBoxW &&
2438 !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
2439 GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells))
2441 nW = RES_BOXATR_VALUE;
2442 // restore previous spaces!
2443 for( size_t i = aCells.size(); n+1 < i; )
2445 sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">"
2446 + sFields;
2449 else
2450 nW = nBoxW;
2452 else if( RES_BOXATR_VALUE == nW )
2454 // search for values, Value/Formula/Text found -> include
2455 if( RES_BOXATR_FORMULA == nBoxW &&
2456 ::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
2457 GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells ))
2458 break;
2459 else if( USHRT_MAX != nBoxW )
2460 sFields = OUStringChar(cListDelim) + sFields;
2461 else
2462 break;
2464 else if( RES_BOXATR_FORMULA == nW )
2466 // only continue search when the current formula points to
2467 // all boxes contained in the selection
2468 if( RES_BOXATR_FORMULA == nBoxW )
2470 if( !::lcl_IsFormulaSelBoxes( *pTab->GetTable(), pCFrame->
2471 GetTabBox()->GetFrameFormat()->GetTableBoxFormula(), aCells ))
2473 // redo only for values!
2475 nW = RES_BOXATR_VALUE;
2476 sFields.clear();
2477 // restore previous spaces!
2478 for( size_t i = aCells.size(); n+1 < i; )
2480 sFields = "|<" + aCells[--i]->GetTabBox()->GetName() + ">"
2481 + sFields;
2484 else
2485 sFields = OUStringChar(cListDelim) + sFields;
2487 else if( USHRT_MAX == nBoxW )
2488 break;
2489 else
2490 continue; // ignore this box
2492 else
2493 // all other stuff terminates the loop
2494 // possibly allow texts??
2495 break;
2497 sFields = "<" + pCFrame->GetTabBox()->GetName() + ">" + sFields;
2501 rFormula = OUString::createFromAscii( sCalc_Sum );
2502 if (!sFields.isEmpty())
2504 rFormula += "(" + sFields + ")";
2508 bool SwFEShell::IsTableRightToLeft() const
2510 SwFrame *pFrame = GetCurrFrame();
2511 SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr;
2512 if (!pTab)
2513 return false;
2514 return pTab->IsRightToLeft();
2517 bool SwFEShell::IsMouseTableRightToLeft(const Point &rPt) const
2519 SwFrame *pFrame = const_cast<SwFrame *>(GetBox( rPt ));
2520 const SwTabFrame* pTabFrame = pFrame ? pFrame->ImplFindTabFrame() : nullptr;
2521 OSL_ENSURE( pTabFrame, "Table not found" );
2522 return pTabFrame && pTabFrame->IsRightToLeft();
2525 bool SwFEShell::IsTableVertical() const
2527 SwFrame *pFrame = GetCurrFrame();
2528 SwTabFrame *pTab = (pFrame && pFrame->IsInTab()) ? pFrame->ImplFindTabFrame() : nullptr;
2529 if (!pTab)
2530 return false;
2531 return pTab->IsVertical();
2534 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */