1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <hintids.hxx>
21 #include <osl/diagnose.h>
22 #include <unotools/collatorwrapper.hxx>
23 #include <unotools/localedatawrapper.hxx>
24 #include <comphelper/processfactory.hxx>
26 #include <fmtanchr.hxx>
29 #include <IDocumentUndoRedo.hxx>
30 #include <IDocumentFieldsAccess.hxx>
31 #include <IDocumentRedlineAccess.hxx>
32 #include <IDocumentState.hxx>
36 #include <swtable.hxx>
38 #include <sortopt.hxx>
39 #include <docsort.hxx>
40 #include <UndoSort.hxx>
41 #include <UndoRedline.hxx>
44 #include <cellatr.hxx>
45 #include <redline.hxx>
46 #include <node2lay.hxx>
47 #include <frameformats.hxx>
48 #include <svl/numformat.hxx>
53 using namespace ::com::sun::star::lang
;
54 using namespace ::com::sun::star
;
56 SwSortOptions
* SwSortElement::pOptions
= nullptr;
57 SwDoc
* SwSortElement::pDoc
= nullptr;
58 const FlatFndBox
* SwSortElement::pBox
= nullptr;
59 CollatorWrapper
* SwSortElement::pSortCollator
= nullptr;
60 lang::Locale
* SwSortElement::pLocale
= nullptr;
61 std::optional
<OUString
> SwSortElement::xLastAlgorithm
;
62 LocaleDataWrapper
* SwSortElement::pLclData
= nullptr;
64 // List of all sorted elements
66 /// Construct a SortElement for the Sort
67 void SwSortElement::Init( SwDoc
* pD
, const SwSortOptions
& rOpt
,
68 FlatFndBox
const * pFltBx
)
70 OSL_ENSURE( !pDoc
&& !pOptions
&& !pBox
, "Who forgot to call Finit?" );
72 pOptions
= new SwSortOptions( rOpt
);
75 LanguageType nLang
= rOpt
.nLanguage
;
79 nLang
= GetAppLanguage();
80 pLocale
= new lang::Locale( LanguageTag::convertToLocale( nLang
) );
82 pSortCollator
= new CollatorWrapper( ::comphelper::getProcessComponentContext() );
85 void SwSortElement::Finit()
91 xLastAlgorithm
.reset();
93 pSortCollator
= nullptr;
100 SwSortElement::~SwSortElement()
104 double SwSortElement::StrToDouble( std::u16string_view rStr
)
107 pLclData
= new LocaleDataWrapper( LanguageTag( *pLocale
));
109 rtl_math_ConversionStatus eStatus
;
111 double nRet
= pLclData
->stringToDouble( rStr
, true, &eStatus
, &nEnd
);
113 if( rtl_math_ConversionStatus_Ok
!= eStatus
|| nEnd
== 0 )
118 int SwSortElement::keycompare(const SwSortElement
& rCmp
, sal_uInt16 nKey
) const
121 // The actual comparison
122 const SwSortElement
*pOrig
, *pCmp
;
124 const SwSortKey
& rSrtKey
= pOptions
->aKeys
[ nKey
];
125 if( rSrtKey
.eSortOrder
== SwSortOrder::Ascending
)
136 if( rSrtKey
.bIsNumeric
)
138 double n1
= pOrig
->GetValue( nKey
);
139 double n2
= pCmp
->GetValue( nKey
);
141 nCmp
= n1
< n2
? -1 : n1
== n2
? 0 : 1;
145 if( !xLastAlgorithm
|| *xLastAlgorithm
!= rSrtKey
.sSortType
)
147 xLastAlgorithm
= rSrtKey
.sSortType
;
148 pSortCollator
->loadCollatorAlgorithm( *xLastAlgorithm
,
150 pOptions
->bIgnoreCase
? SW_COLLATOR_IGNORES
: 0 );
153 nCmp
= pSortCollator
->compareString(
154 pOrig
->GetKey( nKey
), pCmp
->GetKey( nKey
));
159 bool SwSortElement::operator<(const SwSortElement
& rCmp
) const
161 // The actual comparison
162 for(size_t nKey
= 0; nKey
< pOptions
->aKeys
.size(); ++nKey
)
164 int nCmp
= keycompare(rCmp
, nKey
);
175 double SwSortElement::GetValue( sal_uInt16 nKey
) const
177 return StrToDouble( GetKey( nKey
));
180 /// SortingElement for Text
181 SwSortTextElement::SwSortTextElement(const SwNodeIndex
& rPos
)
182 : nOrg(rPos
.GetIndex()), aPos(rPos
)
186 OUString
SwSortTextElement::GetKey(sal_uInt16 nId
) const
188 SwTextNode
* pTextNd
= aPos
.GetNode().GetTextNode();
193 const OUString
& rStr
= pTextNd
->GetText();
195 sal_Unicode nDeli
= pOptions
->cDeli
;
196 sal_uInt16 nDCount
= pOptions
->aKeys
[nId
].nColumnId
, i
= 1;
197 sal_Int32 nStart
= 0;
199 // Find the delimiter
200 while( nStart
!= -1 && i
< nDCount
)
202 nStart
= rStr
.indexOf( nDeli
, nStart
);
210 // Found next delimiter or end of String
212 sal_Int32 nEnd
= rStr
.indexOf( nDeli
, nStart
+1 );
214 return rStr
.copy( nStart
);
215 return rStr
.copy( nStart
, nEnd
-nStart
);
218 /// SortingElement for Tables
219 SwSortBoxElement::SwSortBoxElement( sal_uInt16 nRC
)
224 /// Get Key for a cell
225 OUString
SwSortBoxElement::GetKey(sal_uInt16 nKey
) const
227 const FndBox_
* pFndBox
;
228 sal_uInt16 nCol
= pOptions
->aKeys
[nKey
].nColumnId
-1;
230 if( SwSortDirection::Rows
== pOptions
->eDirection
)
231 pFndBox
= pBox
->GetBox(nCol
, nRow
); // Sort rows
233 pFndBox
= pBox
->GetBox(nRow
, nCol
); // Sort columns
236 OUStringBuffer aRetStr
;
238 { // Get StartNode and skip it
239 const SwTableBox
* pMyBox
= pFndBox
->GetBox();
240 OSL_ENSURE(pMyBox
, "No atomic Box");
242 if( pMyBox
->GetSttNd() )
244 // Iterate over all the Box's TextNodes
245 const SwNode
*pNd
= nullptr, *pEndNd
= pMyBox
->GetSttNd()->EndOfSectionNode();
246 for( SwNodeOffset nIdx
= pMyBox
->GetSttIdx() + 1; pNd
!= pEndNd
; ++nIdx
)
248 pNd
= pDoc
->GetNodes()[ nIdx
];
249 if( pNd
->IsTextNode() )
250 aRetStr
.append(pNd
->GetTextNode()->GetText());
254 return aRetStr
.makeStringAndClear();
257 double SwSortBoxElement::GetValue( sal_uInt16 nKey
) const
259 const FndBox_
* pFndBox
;
260 sal_uInt16 nCol
= pOptions
->aKeys
[nKey
].nColumnId
-1;
262 if( SwSortDirection::Rows
== pOptions
->eDirection
)
263 pFndBox
= pBox
->GetBox(nCol
, nRow
); // Sort rows
265 pFndBox
= pBox
->GetBox(nRow
, nCol
); // Sort columns
270 const SwFormat
*pFormat
= pFndBox
->GetBox()->GetFrameFormat();
271 if (pDoc
->GetNumberFormatter()->IsTextFormat( pFormat
->GetTableBoxNumFormat().GetValue()))
272 nVal
= SwSortElement::GetValue( nKey
);
274 nVal
= pFormat
->GetTableBoxValue().GetValue();
282 /// Sort Text in the Document
283 bool SwDoc::SortText(const SwPaM
& rPaM
, const SwSortOptions
& rOpt
)
285 // Check if Frame is in the Text
286 auto [pStart
, pEnd
] = rPaM
.StartEnd(); // SwPosition*
288 // Set index to the Selection's start
289 for(sw::SpzFrameFormat
* pFormat
: *GetSpzFrameFormats())
291 SwFormatAnchor
const*const pAnchor
= &pFormat
->GetAnchor();
292 SwNode
const*const pAnchorNode
= pAnchor
->GetAnchorNode();
294 if (pAnchorNode
&& (RndStdIds::FLY_AT_PARA
== pAnchor
->GetAnchorId()) &&
295 pStart
->GetNode() <= *pAnchorNode
&& *pAnchorNode
<= pEnd
->GetNode() )
299 // Check if only TextNodes are within the Selection
301 SwNodeOffset nStart
= pStart
->GetNodeIndex(),
302 nEnd
= pEnd
->GetNodeIndex();
303 while( nStart
<= nEnd
)
304 // Iterate over a selected range
305 if( !GetNodes()[ nStart
++ ]->IsTextNode() )
309 bool const bUndo
= GetIDocumentUndoRedo().DoesUndo();
312 GetIDocumentUndoRedo().StartUndo( SwUndoId::START
, nullptr );
315 SwPaM
* pRedlPam
= nullptr;
316 SwUndoRedlineSort
* pRedlUndo
= nullptr;
317 SwUndoSort
* pUndoSort
= nullptr;
319 // To-Do - add 'SwExtraRedlineTable' also ?
320 if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
322 pRedlPam
= new SwPaM( pStart
->GetNode(), pEnd
->GetNode(), SwNodeOffset(-1), SwNodeOffset(1) );
323 SwContentNode
* pCNd
= pRedlPam
->GetMarkContentNode();
325 pRedlPam
->GetMark()->SetContent( pCNd
->Len() );
327 if( getIDocumentRedlineAccess().IsRedlineOn() && !IDocumentRedlineAccess::IsShowOriginal( getIDocumentRedlineAccess().GetRedlineFlags() ) )
331 pRedlUndo
= new SwUndoRedlineSort( *pRedlPam
,rOpt
);
332 GetIDocumentUndoRedo().DoUndo(false);
334 // First copy the range
335 SwNodeIndex
aEndIdx( pEnd
->GetNode(), 1 );
336 SwNodeRange
aRg( pStart
->GetNode(), aEndIdx
.GetNode() );
337 GetNodes().Copy_( aRg
, aEndIdx
.GetNode() );
339 // range is new from pEnd->nNode+1 to aEndIdx
340 getIDocumentRedlineAccess().DeleteRedline( *pRedlPam
, true, RedlineType::Any
);
342 pRedlPam
->GetMark()->Assign( pEnd
->GetNode(), SwNodeOffset(1) );
344 pRedlPam
->GetPoint()->Assign( aEndIdx
.GetNode() );
345 pCNd
= pRedlPam
->GetPointContentNode();
349 pCNd
= GetNodes()[ aEndIdx
.GetIndex()-SwNodeOffset(1) ]->GetContentNode();
353 pRedlPam
->GetPoint()->Assign( *pCNd
);
357 pRedlPam
->GetPoint()->SetContent( nCLen
);
360 pRedlUndo
->SetValues( rPaM
);
364 getIDocumentRedlineAccess().DeleteRedline( *pRedlPam
, true, RedlineType::Any
);
370 SwNodeIndex
aStart(pStart
->GetNode());
371 SwSortElement::Init( this, rOpt
);
372 std::multiset
<SwSortTextElement
> aSortSet
;
373 while( aStart
<= pEnd
->GetNode() )
375 // Iterate over a selected range
376 aSortSet
.insert(SwSortTextElement(aStart
));
380 // Now comes the tricky part: Move Nodes (and always keep Undo in mind)
381 SwNodeOffset nBeg
= pStart
->GetNodeIndex();
382 SwNodeRange
aRg( aStart
, aStart
);
384 if( bUndo
&& !pRedlUndo
)
386 pUndoSort
= new SwUndoSort(rPaM
, rOpt
);
387 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndoSort
));
390 GetIDocumentUndoRedo().DoUndo(false);
393 for (const auto& rElem
: aSortSet
)
396 aRg
.aStart
= rElem
.aPos
.GetIndex();
397 aRg
.aEnd
= aRg
.aStart
.GetIndex() + 1;
400 getIDocumentContentOperations().MoveNodeRange( aRg
, aStart
.GetNode(),
401 SwMoveFlags::DEFAULT
);
403 // Insert Move in Undo
406 pUndoSort
->Insert(rElem
.nOrg
, nBeg
+ n
);
410 // Delete all elements from the SortArray
412 SwSortElement::Finit();
418 pRedlUndo
->SetSaveRange( *pRedlPam
);
419 // UGLY: temp. enable Undo
420 GetIDocumentUndoRedo().DoUndo(true);
421 GetIDocumentUndoRedo().AppendUndo( std::unique_ptr
<SwUndo
>(pRedlUndo
) );
422 GetIDocumentUndoRedo().DoUndo(false);
425 // nBeg is start of sorted range
426 SwNodeIndex
aSttIdx( GetNodes(), nBeg
);
428 // the copied range is deleted
429 SwRangeRedline
*const pDeleteRedline(
430 new SwRangeRedline( RedlineType::Delete
, *pRedlPam
));
432 // pRedlPam points to nodes that may be deleted (hidden) by
433 // AppendRedline, so adjust it beforehand to prevent ASSERT
434 pRedlPam
->GetPoint()->Assign(aSttIdx
);
436 getIDocumentRedlineAccess().AppendRedline(pDeleteRedline
, true);
438 // the sorted range is inserted
439 getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert
, *pRedlPam
), true);
443 SwNodeIndex
aInsEndIdx( pRedlPam
->GetMark()->GetNode(), -1 );
444 SwContentNode
*const pContentNode
= aInsEndIdx
.GetNode().GetContentNode();
445 pRedlPam
->GetMark()->Assign( *pContentNode
, pContentNode
->Len() );
447 pRedlUndo
->SetValues( *pRedlPam
);
453 GetIDocumentUndoRedo().DoUndo( bUndo
);
456 GetIDocumentUndoRedo().EndUndo( SwUndoId::END
, nullptr );
462 /// Sort Table in the Document
463 bool SwDoc::SortTable(const SwSelBoxes
& rBoxes
, const SwSortOptions
& rOpt
)
465 // Via SwDoc for Undo!
466 OSL_ENSURE( !rBoxes
.empty(), "no valid Box list" );
467 SwTableNode
* pTableNd
= const_cast<SwTableNode
*>(rBoxes
[0]->GetSttNd()->FindTableNode());
472 // Find all Boxes/Lines
473 FndBox_
aFndBox( nullptr, nullptr );
475 FndPara
aPara( rBoxes
, &aFndBox
);
476 ForEach_FndLineCopyCol( pTableNd
->GetTable().GetTabLines(), &aPara
);
479 if(aFndBox
.GetLines().empty())
482 if( !getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() )
483 getIDocumentRedlineAccess().DeleteRedline( *pTableNd
, true, RedlineType::Any
);
485 FndLines_t::size_type nStart
= 0;
486 if( pTableNd
->GetTable().GetRowsToRepeat() > 0 && rOpt
.eDirection
== SwSortDirection::Rows
)
488 // Uppermost selected Cell
489 FndLines_t
& rLines
= aFndBox
.GetLines();
491 while( nStart
< rLines
.size() )
493 // Respect Split Merge nesting,
494 // extract the upper most
495 SwTableLine
* pLine
= rLines
[nStart
]->GetLine();
496 while ( pLine
->GetUpper() )
497 pLine
= pLine
->GetUpper()->GetUpper();
499 if( pTableNd
->GetTable().IsHeadline( *pLine
) )
504 // Are all selected in the HeaderLine? -> no Offset
505 if( nStart
== rLines
.size() )
509 pTableNd
->GetTable().SwitchFormulasToRelativeRepresentation();
511 // Table as a flat array structure
512 FlatFndBox
aFlatBox(this, aFndBox
);
514 if(!aFlatBox
.IsSymmetric())
517 // Delete HTML layout
518 pTableNd
->GetTable().SetHTMLTableLayout(std::shared_ptr
<SwHTMLTableLayout
>());
520 // #i37739# A simple 'MakeFrames' after the node sorting
521 // does not work if the table is inside a frame and has no prev/next.
522 SwNode2LayoutSaveUpperFrames
aNode2Layout(*pTableNd
);
524 // Delete the Table's Frames
525 pTableNd
->DelFrames();
528 SwUndoSort
* pUndoSort
= nullptr;
529 if (GetIDocumentUndoRedo().DoesUndo())
531 pUndoSort
= new SwUndoSort( rBoxes
[0]->GetSttIdx(),
532 rBoxes
.back()->GetSttIdx(),
533 *pTableNd
, rOpt
, aFlatBox
.HasItemSets() );
534 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr
<SwUndo
>(pUndoSort
));
536 ::sw::UndoGuard
const undoGuard(GetIDocumentUndoRedo());
538 // Insert KeyElements
539 sal_uInt16 nCount
= (rOpt
.eDirection
== SwSortDirection::Rows
) ?
540 aFlatBox
.GetRows() : aFlatBox
.GetCols();
542 // Sort SortList by Key
543 SwSortElement::Init( this, rOpt
, &aFlatBox
);
544 std::multiset
<SwSortBoxElement
> aSortList
;
546 // When sorting, do not include the first row if the HeaderLine is repeated
547 for( sal_uInt16 i
= o3tl::narrowing
<sal_uInt16
>(nStart
); i
< nCount
; ++i
)
549 aSortList
.insert(SwSortBoxElement(i
));
552 // Move after Sorting
553 SwMovedBoxes aMovedList
;
555 for (const auto& rElem
: aSortList
)
557 if(rOpt
.eDirection
== SwSortDirection::Rows
)
559 MoveRow(this, aFlatBox
, rElem
.nRow
, i
+nStart
, aMovedList
, pUndoSort
);
563 MoveCol(this, aFlatBox
, rElem
.nRow
, i
+nStart
, aMovedList
, pUndoSort
);
568 // Restore table frames:
569 // #i37739# A simple 'MakeFrames' after the node sorting
570 // does not work if the table is inside a frame and has no prev/next.
571 const SwNodeOffset nIdx
= pTableNd
->GetIndex();
572 aNode2Layout
.RestoreUpperFrames( GetNodes(), nIdx
, nIdx
+ 1 );
574 // TL_CHART2: need to inform chart of probably changed cell names
575 UpdateCharts( pTableNd
->GetTable().GetFrameFormat()->GetName() );
577 // Delete all Elements in the SortArray
579 SwSortElement::Finit();
581 getIDocumentState().SetModified();
586 void MoveRow(SwDoc
* pDoc
, const FlatFndBox
& rBox
, sal_uInt16 nS
, sal_uInt16 nT
,
587 SwMovedBoxes
& rMovedList
, SwUndoSort
* pUD
)
589 for( sal_uInt16 i
=0; i
< rBox
.GetCols(); ++i
)
590 { // Get old cell position and remember it
591 const FndBox_
* pSource
= rBox
.GetBox(i
, nS
);
594 const FndBox_
* pTarget
= rBox
.GetBox(i
, nT
);
596 const SwTableBox
* pT
= pTarget
->GetBox();
597 const SwTableBox
* pS
= pSource
->GetBox();
599 bool bMoved
= rMovedList
.GetPos(pT
) != USHRT_MAX
;
602 MoveCell(pDoc
, pS
, pT
, bMoved
, pUD
);
604 rMovedList
.push_back(pS
);
608 SwFrameFormat
* pTFormat
= pT
->GetFrameFormat();
609 const SfxItemSet
* pSSet
= rBox
.GetItemSet( i
, nS
);
612 SfxItemState::SET
== pTFormat
->GetItemState( RES_BOXATR_FORMAT
) ||
613 SfxItemState::SET
== pTFormat
->GetItemState( RES_BOXATR_FORMULA
) ||
614 SfxItemState::SET
== pTFormat
->GetItemState( RES_BOXATR_VALUE
) )
616 pTFormat
= const_cast<SwTableBox
*>(pT
)->ClaimFrameFormat();
617 pTFormat
->LockModify();
618 if( pTFormat
->ResetFormatAttr( RES_BOXATR_FORMAT
, RES_BOXATR_VALUE
) )
619 pTFormat
->ResetFormatAttr( RES_VERT_ORIENT
);
622 pTFormat
->SetFormatAttr( *pSSet
);
623 pTFormat
->UnlockModify();
630 void MoveCol(SwDoc
* pDoc
, const FlatFndBox
& rBox
, sal_uInt16 nS
, sal_uInt16 nT
,
631 SwMovedBoxes
& rMovedList
, SwUndoSort
* pUD
)
633 for(sal_uInt16 i
=0; i
< rBox
.GetRows(); ++i
)
634 { // Get old cell position and remember it
635 const FndBox_
* pSource
= rBox
.GetBox(nS
, i
);
638 const FndBox_
* pTarget
= rBox
.GetBox(nT
, i
);
641 const SwTableBox
* pT
= pTarget
->GetBox();
642 const SwTableBox
* pS
= pSource
->GetBox();
645 bool bMoved
= rMovedList
.GetPos(pT
) != USHRT_MAX
;
646 MoveCell(pDoc
, pS
, pT
, bMoved
, pUD
);
648 rMovedList
.push_back(pS
);
652 SwFrameFormat
* pTFormat
= pT
->GetFrameFormat();
653 const SfxItemSet
* pSSet
= rBox
.GetItemSet( nS
, i
);
656 SfxItemState::SET
== pTFormat
->GetItemState( RES_BOXATR_FORMAT
) ||
657 SfxItemState::SET
== pTFormat
->GetItemState( RES_BOXATR_FORMULA
) ||
658 SfxItemState::SET
== pTFormat
->GetItemState( RES_BOXATR_VALUE
) )
660 pTFormat
= const_cast<SwTableBox
*>(pT
)->ClaimFrameFormat();
661 pTFormat
->LockModify();
662 if( pTFormat
->ResetFormatAttr( RES_BOXATR_FORMAT
, RES_BOXATR_VALUE
) )
663 pTFormat
->ResetFormatAttr( RES_VERT_ORIENT
);
666 pTFormat
->SetFormatAttr( *pSSet
);
667 pTFormat
->UnlockModify();
673 /// Move a single Cell
674 void MoveCell(SwDoc
* pDoc
, const SwTableBox
* pSource
, const SwTableBox
* pTar
,
675 bool bMovedBefore
, SwUndoSort
* pUD
)
677 OSL_ENSURE(pSource
&& pTar
,"Source or target missing");
683 pUD
->Insert( pSource
->GetName(), pTar
->GetName() );
685 // Set Pam source to the first ContentNode
686 SwNodeRange
aRg( *pSource
->GetSttNd(), SwNodeOffset(0), *pSource
->GetSttNd() );
687 SwNode
* pNd
= pDoc
->GetNodes().GoNext( &aRg
.aStart
);
689 // If the Cell (Source) wasn't moved
690 // -> insert an empty Node and move the rest or the Mark
691 // points to the first ContentNode
692 if( pNd
->StartOfSectionNode() == pSource
->GetSttNd() )
693 pNd
= pDoc
->GetNodes().MakeTextNode( aRg
.aStart
.GetNode(),
694 pDoc
->GetDfltTextFormatColl() );
695 aRg
.aEnd
= *pNd
->EndOfSectionNode();
697 // If the Target is empty (there is one empty Node)
698 // -> move and delete it
699 SwNodeIndex
aTar( *pTar
->GetSttNd() );
700 pNd
= pDoc
->GetNodes().GoNext( &aTar
); // next ContentNode
701 SwNodeOffset nCount
= pNd
->EndOfSectionIndex() - pNd
->StartOfSectionIndex();
703 bool bDelFirst
= false;
704 if( nCount
== SwNodeOffset(2) )
706 OSL_ENSURE( pNd
->GetContentNode(), "No ContentNode");
707 bDelFirst
= !pNd
->GetContentNode()->Len() && bMovedBefore
;
711 { // We already have Content -> old Content Section Down
712 SwNodeRange
aRgTar( aTar
.GetNode(), SwNodeOffset(0), *pNd
->EndOfSectionNode() );
713 pDoc
->GetNodes().SectionDown( &aRgTar
);
717 SwNodeIndex
aIns( *pTar
->GetSttNd()->EndOfSectionNode() );
718 pDoc
->getIDocumentContentOperations().MoveNodeRange( aRg
, aIns
.GetNode(),
719 SwMoveFlags::DEFAULT
);
721 // If first Node is empty -> delete it
723 pDoc
->GetNodes().Delete( aTar
);
726 /// Generate two-dimensional array of FndBoxes
727 FlatFndBox::FlatFndBox(SwDoc
* pDocPtr
, const FndBox_
& rBoxRef
) :
731 { // If the array is symmetric
732 m_bSym
= CheckLineSymmetry(rBoxRef
);
736 // Determine column/row count
737 m_nCols
= GetColCount(rBoxRef
);
738 m_nRows
= GetRowCount(rBoxRef
);
740 // Create linear array
741 size_t nCount
= static_cast<size_t>(m_nRows
) * m_nCols
;
742 m_pArr
= std::make_unique
<FndBox_
const *[]>(nCount
);
743 memset(m_pArr
.get(), 0, sizeof(const FndBox_
*) * nCount
);
748 FlatFndBox::~FlatFndBox()
752 /// All Lines of a Box need to have same number of Boxes
753 bool FlatFndBox::CheckLineSymmetry(const FndBox_
& rBox
)
755 const FndLines_t
&rLines
= rBox
.GetLines();
756 FndBoxes_t::size_type nBoxes
{0};
758 for (FndLines_t::size_type i
=0; i
< rLines
.size(); ++i
)
760 const FndLine_
* pLn
= rLines
[i
].get();
761 const FndBoxes_t
& rBoxes
= pLn
->GetBoxes();
763 // Number of Boxes of all Lines is unequal -> no symmetry
764 if( i
&& nBoxes
!= rBoxes
.size())
767 nBoxes
= rBoxes
.size();
768 if( !CheckBoxSymmetry( *pLn
) )
774 /// Check Box for symmetry (All Boxes of a Line need to have same number of Lines)
775 bool FlatFndBox::CheckBoxSymmetry(const FndLine_
& rLn
)
777 const FndBoxes_t
&rBoxes
= rLn
.GetBoxes();
778 FndLines_t::size_type nLines
{0};
780 for (FndBoxes_t::size_type i
= 0; i
< rBoxes
.size(); ++i
)
782 FndBox_
const*const pBox
= rBoxes
[i
].get();
783 const FndLines_t
& rLines
= pBox
->GetLines();
785 // Number of Lines of all Boxes is unequal -> no symmetry
786 if( i
&& nLines
!= rLines
.size() )
789 nLines
= rLines
.size();
790 if( nLines
&& !CheckLineSymmetry( *pBox
) )
796 /// Maximum count of Columns (Boxes)
797 sal_uInt16
FlatFndBox::GetColCount(const FndBox_
& rBox
)
799 const FndLines_t
& rLines
= rBox
.GetLines();
800 // Iterate over Lines
805 for (const auto & pLine
: rLines
)
807 // The Boxes of a Line
808 sal_uInt16 nCount
= 0;
809 const FndBoxes_t
& rBoxes
= pLine
->GetBoxes();
810 for (const auto &rpB
: rBoxes
)
811 { // Iterate recursively over the Lines
812 nCount
+= rpB
->GetLines().empty() ? 1 : GetColCount(*rpB
);
821 /// Maximum count of Rows (Lines)
822 sal_uInt16
FlatFndBox::GetRowCount(const FndBox_
& rBox
)
824 const FndLines_t
& rLines
= rBox
.GetLines();
828 sal_uInt16 nLines
= 0;
829 for (const auto & pLine
: rLines
)
830 { // The Boxes of a Line
831 const FndBoxes_t
& rBoxes
= pLine
->GetBoxes();
833 for (const auto &rpB
: rBoxes
)
835 if (!rpB
->GetLines().empty())
836 { // Iterate recursively over the Lines
837 nLn
= std::max(GetRowCount(*rpB
), nLn
);
841 nLines
= nLines
+ nLn
;
846 /// Create a linear array of atomic FndBoxes
847 void FlatFndBox::FillFlat(const FndBox_
& rBox
, bool bLastBox
)
849 bool bModRow
= false;
850 const FndLines_t
& rLines
= rBox
.GetLines();
852 // Iterate over Lines
853 sal_uInt16 nOldRow
= m_nRow
;
854 for (const auto & pLine
: rLines
)
856 // The Boxes of a Line
857 const FndBoxes_t
& rBoxes
= pLine
->GetBoxes();
858 sal_uInt16 nOldCol
= m_nCol
;
859 for( FndBoxes_t::size_type j
= 0; j
< rBoxes
.size(); ++j
)
861 // Check the Box if it's an atomic one
862 const FndBox_
*const pBox
= rBoxes
[j
].get();
864 if( pBox
->GetLines().empty() )
867 sal_uInt16 nOff
= m_nRow
* m_nCols
+ m_nCol
;
870 // Save the Formula/Format/Value values
871 const SwFrameFormat
* pFormat
= pBox
->GetBox()->GetFrameFormat();
872 if( SfxItemState::SET
== pFormat
->GetItemState( RES_BOXATR_FORMAT
) ||
873 SfxItemState::SET
== pFormat
->GetItemState( RES_BOXATR_FORMULA
) ||
874 SfxItemState::SET
== pFormat
->GetItemState( RES_BOXATR_VALUE
) )
877 RES_VERT_ORIENT
, RES_VERT_ORIENT
,
878 RES_BOXATR_FORMAT
, RES_BOXATR_VALUE
>
879 aSet(m_pDoc
->GetAttrPool());
880 aSet
.Put( pFormat
->GetAttrSet() );
881 if( m_vItemSets
.empty() )
883 size_t nCount
= static_cast<size_t>(m_nRows
) * m_nCols
;
884 m_vItemSets
.resize(nCount
);
886 m_vItemSets
[nOff
].emplace(std::move(aSet
));
893 // Iterate recursively over the Lines of a Box
894 FillFlat( *pBox
, ( j
+1 == rBoxes
.size() ) );
906 /// Access a specific Cell
907 const FndBox_
* FlatFndBox::GetBox(sal_uInt16 n_Col
, sal_uInt16 n_Row
) const
909 sal_uInt16 nOff
= n_Row
* m_nCols
+ n_Col
;
910 const FndBox_
* pTmp
= m_pArr
[nOff
];
912 OSL_ENSURE(n_Col
< m_nCols
&& n_Row
< m_nRows
&& pTmp
, "invalid array access");
916 const SfxItemSet
* FlatFndBox::GetItemSet(sal_uInt16 n_Col
, sal_uInt16 n_Row
) const
918 OSL_ENSURE( m_vItemSets
.empty() || ( n_Col
< m_nCols
&& n_Row
< m_nRows
), "invalid array access");
920 if (m_vItemSets
.empty()) {
923 auto const & el
= m_vItemSets
[unsigned(n_Row
* m_nCols
) + n_Col
];
924 return el
? &*el
: nullptr;
927 sal_uInt16
SwMovedBoxes::GetPos(const SwTableBox
* pTableBox
) const
929 std::vector
<const SwTableBox
*>::const_iterator it
= std::find(mBoxes
.begin(), mBoxes
.end(), pTableBox
);
930 return it
== mBoxes
.end() ? USHRT_MAX
: it
- mBoxes
.begin();
933 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */