android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / docsort.cxx
blobd22ab372e32360a7e0759f4eb0dcae0fae52c370
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 <hintids.hxx>
21 #include <osl/diagnose.h>
22 #include <unotools/collatorwrapper.hxx>
23 #include <unotools/localedatawrapper.hxx>
24 #include <comphelper/processfactory.hxx>
25 #include <docary.hxx>
26 #include <fmtanchr.hxx>
27 #include <frmfmt.hxx>
28 #include <doc.hxx>
29 #include <IDocumentUndoRedo.hxx>
30 #include <IDocumentFieldsAccess.hxx>
31 #include <IDocumentRedlineAccess.hxx>
32 #include <IDocumentState.hxx>
33 #include <node.hxx>
34 #include <pam.hxx>
35 #include <ndtxt.hxx>
36 #include <swtable.hxx>
37 #include <swundo.hxx>
38 #include <sortopt.hxx>
39 #include <docsort.hxx>
40 #include <UndoSort.hxx>
41 #include <UndoRedline.hxx>
42 #include <hints.hxx>
43 #include <tblsel.hxx>
44 #include <cellatr.hxx>
45 #include <redline.hxx>
46 #include <node2lay.hxx>
47 #include <frameformats.hxx>
48 #include <svl/numformat.hxx>
50 #include <set>
51 #include <utility>
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?" );
71 pDoc = pD;
72 pOptions = new SwSortOptions( rOpt );
73 pBox = pFltBx;
75 LanguageType nLang = rOpt.nLanguage;
76 if ( nLang.anyOf(
77 LANGUAGE_NONE,
78 LANGUAGE_DONTKNOW))
79 nLang = GetAppLanguage();
80 pLocale = new lang::Locale( LanguageTag::convertToLocale( nLang ) );
82 pSortCollator = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
85 void SwSortElement::Finit()
87 delete pOptions;
88 pOptions = nullptr;
89 delete pLocale;
90 pLocale = nullptr;
91 xLastAlgorithm.reset();
92 delete pSortCollator;
93 pSortCollator = nullptr;
94 delete pLclData;
95 pLclData = nullptr;
96 pDoc = nullptr;
97 pBox = nullptr;
100 SwSortElement::~SwSortElement()
104 double SwSortElement::StrToDouble( std::u16string_view rStr )
106 if( !pLclData )
107 pLclData = new LocaleDataWrapper( LanguageTag( *pLocale ));
109 rtl_math_ConversionStatus eStatus;
110 sal_Int32 nEnd;
111 double nRet = pLclData->stringToDouble( rStr, true, &eStatus, &nEnd );
113 if( rtl_math_ConversionStatus_Ok != eStatus || nEnd == 0 )
114 nRet = 0.0;
115 return nRet;
118 int SwSortElement::keycompare(const SwSortElement& rCmp, sal_uInt16 nKey) const
120 int nCmp = 0;
121 // The actual comparison
122 const SwSortElement *pOrig, *pCmp;
124 const SwSortKey& rSrtKey = pOptions->aKeys[ nKey ];
125 if( rSrtKey.eSortOrder == SwSortOrder::Ascending )
127 pOrig = this;
128 pCmp = &rCmp;
130 else
132 pOrig = &rCmp;
133 pCmp = this;
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;
143 else
145 if( !xLastAlgorithm || *xLastAlgorithm != rSrtKey.sSortType )
147 xLastAlgorithm = rSrtKey.sSortType;
148 pSortCollator->loadCollatorAlgorithm( *xLastAlgorithm,
149 *pLocale,
150 pOptions->bIgnoreCase ? SW_COLLATOR_IGNORES : 0 );
153 nCmp = pSortCollator->compareString(
154 pOrig->GetKey( nKey ), pCmp->GetKey( nKey ));
156 return nCmp;
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);
166 if (nCmp == 0)
167 continue;
169 return nCmp < 0;
172 return false;
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();
189 if( !pTextNd )
190 return OUString();
192 // for TextNodes
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 );
203 if( -1 != nStart )
205 nStart++;
206 i++;
210 // Found next delimiter or end of String
211 // and copy
212 sal_Int32 nEnd = rStr.indexOf( nDeli, nStart+1 );
213 if (nEnd == -1)
214 return rStr.copy( nStart );
215 return rStr.copy( nStart, nEnd-nStart );
218 /// SortingElement for Tables
219 SwSortBoxElement::SwSortBoxElement( sal_uInt16 nRC )
220 : nRow( 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
232 else
233 pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
235 // Extract the Text
236 OUStringBuffer aRetStr;
237 if( pFndBox )
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
264 else
265 pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
267 double nVal;
268 if( pFndBox )
270 const SwFormat *pFormat = pFndBox->GetBox()->GetFrameFormat();
271 if (pDoc->GetNumberFormatter()->IsTextFormat( pFormat->GetTableBoxNumFormat().GetValue()))
272 nVal = SwSortElement::GetValue( nKey );
273 else
274 nVal = pFormat->GetTableBoxValue().GetValue();
276 else
277 nVal = 0;
279 return nVal;
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() )
296 return false;
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() )
306 return false;
309 bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
310 if( bUndo )
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();
324 if( pCNd )
325 pRedlPam->GetMark()->SetContent( pCNd->Len() );
327 if( getIDocumentRedlineAccess().IsRedlineOn() && !IDocumentRedlineAccess::IsShowOriginal( getIDocumentRedlineAccess().GetRedlineFlags() ) )
329 if( bUndo )
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();
346 sal_Int32 nCLen = 0;
347 if( !pCNd )
349 pCNd = GetNodes()[ aEndIdx.GetIndex()-SwNodeOffset(1) ]->GetContentNode();
350 if( pCNd )
352 nCLen = pCNd->Len();
353 pRedlPam->GetPoint()->Assign( *pCNd );
356 if (pCNd)
357 pRedlPam->GetPoint()->SetContent( nCLen );
359 if( pRedlUndo )
360 pRedlUndo->SetValues( rPaM );
362 else
364 getIDocumentRedlineAccess().DeleteRedline( *pRedlPam, true, RedlineType::Any );
365 delete pRedlPam;
366 pRedlPam = nullptr;
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));
377 ++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);
392 SwNodeOffset n(0);
393 for (const auto& rElem : aSortSet)
395 aStart = nBeg + n;
396 aRg.aStart = rElem.aPos.GetIndex();
397 aRg.aEnd = aRg.aStart.GetIndex() + 1;
399 // Move Nodes
400 getIDocumentContentOperations().MoveNodeRange( aRg, aStart.GetNode(),
401 SwMoveFlags::DEFAULT );
403 // Insert Move in Undo
404 if(pUndoSort)
406 pUndoSort->Insert(rElem.nOrg, nBeg + n);
408 ++n;
410 // Delete all elements from the SortArray
411 aSortSet.clear();
412 SwSortElement::Finit();
414 if( pRedlPam )
416 if( pRedlUndo )
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);
441 if( pRedlUndo )
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 );
450 delete pRedlPam;
451 pRedlPam = nullptr;
453 GetIDocumentUndoRedo().DoUndo( bUndo );
454 if( bUndo )
456 GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
459 return true;
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());
468 if( !pTableNd )
469 return false;
471 // We begin sorting
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())
480 return false;
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 ) )
500 nStart++;
501 else
502 break;
504 // Are all selected in the HeaderLine? -> no Offset
505 if( nStart == rLines.size() )
506 nStart = 0;
509 pTableNd->GetTable().SwitchFormulasToRelativeRepresentation();
511 // Table as a flat array structure
512 FlatFndBox aFlatBox(this, aFndBox);
514 if(!aFlatBox.IsSymmetric())
515 return false;
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();
526 // ? TL_CHART2: ?
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;
554 sal_uInt16 i = 0;
555 for (const auto& rElem : aSortList)
557 if(rOpt.eDirection == SwSortDirection::Rows)
559 MoveRow(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
561 else
563 MoveCol(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
565 ++i;
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
578 aSortList.clear();
579 SwSortElement::Finit();
581 getIDocumentState().SetModified();
582 return true;
585 /// Move a row
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);
593 // new cell position
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;
601 // and move it
602 MoveCell(pDoc, pS, pT, bMoved, pUD);
604 rMovedList.push_back(pS);
606 if( pS != pT )
608 SwFrameFormat* pTFormat = pT->GetFrameFormat();
609 const SfxItemSet* pSSet = rBox.GetItemSet( i, nS );
611 if( pSSet ||
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 );
621 if( pSSet )
622 pTFormat->SetFormatAttr( *pSSet );
623 pTFormat->UnlockModify();
629 /// Move a column
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);
637 // new cell position
638 const FndBox_* pTarget = rBox.GetBox(nT, i);
640 // and move it
641 const SwTableBox* pT = pTarget->GetBox();
642 const SwTableBox* pS = pSource->GetBox();
644 // and move it
645 bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
646 MoveCell(pDoc, pS, pT, bMoved, pUD);
648 rMovedList.push_back(pS);
650 if( pS != pT )
652 SwFrameFormat* pTFormat = pT->GetFrameFormat();
653 const SfxItemSet* pSSet = rBox.GetItemSet( nS, i );
655 if( pSSet ||
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 );
665 if( pSSet )
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");
679 if(pSource == pTar)
680 return;
682 if(pUD)
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;
710 if(!bDelFirst)
711 { // We already have Content -> old Content Section Down
712 SwNodeRange aRgTar( aTar.GetNode(), SwNodeOffset(0), *pNd->EndOfSectionNode() );
713 pDoc->GetNodes().SectionDown( &aRgTar );
716 // Insert the Source
717 SwNodeIndex aIns( *pTar->GetSttNd()->EndOfSectionNode() );
718 pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aIns.GetNode(),
719 SwMoveFlags::DEFAULT );
721 // If first Node is empty -> delete it
722 if(bDelFirst)
723 pDoc->GetNodes().Delete( aTar );
726 /// Generate two-dimensional array of FndBoxes
727 FlatFndBox::FlatFndBox(SwDoc* pDocPtr, const FndBox_& rBoxRef) :
728 m_pDoc(pDocPtr),
729 m_nRow(0),
730 m_nCol(0)
731 { // If the array is symmetric
732 m_bSym = CheckLineSymmetry(rBoxRef);
733 if( !m_bSym )
734 return;
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);
745 FillFlat( rBoxRef );
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())
765 return false;
767 nBoxes = rBoxes.size();
768 if( !CheckBoxSymmetry( *pLn ) )
769 return false;
771 return true;
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() )
787 return false;
789 nLines = rLines.size();
790 if( nLines && !CheckLineSymmetry( *pBox ) )
791 return false;
793 return true;
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
801 if( rLines.empty() )
802 return 1;
804 sal_uInt16 nSum = 0;
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);
815 if( nSum < nCount )
816 nSum = nCount;
818 return nSum;
821 /// Maximum count of Rows (Lines)
822 sal_uInt16 FlatFndBox::GetRowCount(const FndBox_& rBox)
824 const FndLines_t& rLines = rBox.GetLines();
825 if( rLines.empty() )
826 return 1;
828 sal_uInt16 nLines = 0;
829 for (const auto & pLine : rLines)
830 { // The Boxes of a Line
831 const FndBoxes_t& rBoxes = pLine->GetBoxes();
832 sal_uInt16 nLn = 1;
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;
843 return nLines;
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() )
866 // save it
867 sal_uInt16 nOff = m_nRow * m_nCols + m_nCol;
868 m_pArr[nOff] = pBox;
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 ) )
876 SfxItemSetFixed<
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));
889 bModRow = true;
891 else
893 // Iterate recursively over the Lines of a Box
894 FillFlat( *pBox, ( j+1 == rBoxes.size() ) );
896 m_nCol++;
898 if(bModRow)
899 m_nRow++;
900 m_nCol = nOldCol;
902 if(!bLastBox)
903 m_nRow = nOldRow;
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");
913 return pTmp;
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()) {
921 return nullptr;
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: */