1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <hintids.hxx>
21 #include <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
49 #include <set>
50 #include <utility>
51
52 using namespace ::com::sun::star::lang;
53 using namespace ::com::sun::star;
54
55 SwSortOptions* SwSortElement::pOptions = nullptr;
56 SwDoc* SwSortElement::pDoc = nullptr;
57 const FlatFndBox* SwSortElement::pBox = nullptr;
58 CollatorWrapper* SwSortElement::pSortCollator = nullptr;
59 lang::Locale* SwSortElement::pLocale = nullptr;
60 std::optional<OUString> SwSortElement::xLastAlgorithm;
61 LocaleDataWrapper* SwSortElement::pLclData = nullptr;
62
63 // List of all sorted elements
64
65 /// Construct a SortElement for the Sort
Init(SwDoc * pD,const SwSortOptions & rOpt,FlatFndBox const * pFltBx)66 void SwSortElement::Init( SwDoc* pD, const SwSortOptions& rOpt,
67 FlatFndBox const * pFltBx )
68 {
69 OSL_ENSURE( !pDoc && !pOptions && !pBox, "Who forgot to call Finit?" );
70 pDoc = pD;
71 pOptions = new SwSortOptions( rOpt );
72 pBox = pFltBx;
73
74 LanguageType nLang = rOpt.nLanguage;
75 if ( nLang.anyOf(
76 LANGUAGE_NONE,
77 LANGUAGE_DONTKNOW))
78 nLang = GetAppLanguage();
79 pLocale = new lang::Locale( LanguageTag::convertToLocale( nLang ) );
80
81 pSortCollator = new CollatorWrapper( ::comphelper::getProcessComponentContext() );
82 }
83
Finit()84 void SwSortElement::Finit()
85 {
86 delete pOptions;
87 pOptions = nullptr;
88 delete pLocale;
89 pLocale = nullptr;
90 xLastAlgorithm.reset();
91 delete pSortCollator;
92 pSortCollator = nullptr;
93 delete pLclData;
94 pLclData = nullptr;
95 pDoc = nullptr;
96 pBox = nullptr;
97 }
98
~SwSortElement()99 SwSortElement::~SwSortElement()
100 {
101 }
102
StrToDouble(const OUString & rStr)103 double SwSortElement::StrToDouble( const OUString& rStr )
104 {
105 if( !pLclData )
106 pLclData = new LocaleDataWrapper( LanguageTag( *pLocale ));
107
108 rtl_math_ConversionStatus eStatus;
109 sal_Int32 nEnd;
110 double nRet = pLclData->stringToDouble( rStr, true, &eStatus, &nEnd );
111
112 if( rtl_math_ConversionStatus_Ok != eStatus || nEnd == 0 )
113 nRet = 0.0;
114 return nRet;
115 }
116
keycompare(const SwSortElement & rCmp,sal_uInt16 nKey) const117 int SwSortElement::keycompare(const SwSortElement& rCmp, sal_uInt16 nKey) const
118 {
119 int nCmp = 0;
120 // The actual comparison
121 const SwSortElement *pOrig, *pCmp;
122
123 const SwSortKey& rSrtKey = pOptions->aKeys[ nKey ];
124 if( rSrtKey.eSortOrder == SwSortOrder::Ascending )
125 {
126 pOrig = this;
127 pCmp = &rCmp;
128 }
129 else
130 {
131 pOrig = &rCmp;
132 pCmp = this;
133 }
134
135 if( rSrtKey.bIsNumeric )
136 {
137 double n1 = pOrig->GetValue( nKey );
138 double n2 = pCmp->GetValue( nKey );
139
140 nCmp = n1 < n2 ? -1 : n1 == n2 ? 0 : 1;
141 }
142 else
143 {
144 if( !xLastAlgorithm || *xLastAlgorithm != rSrtKey.sSortType )
145 {
146 xLastAlgorithm = rSrtKey.sSortType;
147 pSortCollator->loadCollatorAlgorithm( *xLastAlgorithm,
148 *pLocale,
149 pOptions->bIgnoreCase ? SW_COLLATOR_IGNORES : 0 );
150 }
151
152 nCmp = pSortCollator->compareString(
153 pOrig->GetKey( nKey ), pCmp->GetKey( nKey ));
154 }
155 return nCmp;
156 }
157
operator <(const SwSortElement & rCmp) const158 bool SwSortElement::operator<(const SwSortElement& rCmp) const
159 {
160 // The actual comparison
161 for(size_t nKey = 0; nKey < pOptions->aKeys.size(); ++nKey)
162 {
163 int nCmp = keycompare(rCmp, nKey);
164
165 if (nCmp == 0)
166 continue;
167
168 return nCmp < 0;
169 }
170
171 return false;
172 }
173
GetValue(sal_uInt16 nKey) const174 double SwSortElement::GetValue( sal_uInt16 nKey ) const
175 {
176 return StrToDouble( GetKey( nKey ));
177 }
178
179 /// SortingElement for Text
SwSortTextElement(const SwNodeIndex & rPos)180 SwSortTextElement::SwSortTextElement(const SwNodeIndex& rPos)
181 : nOrg(rPos.GetIndex()), aPos(rPos)
182 {
183 }
184
GetKey(sal_uInt16 nId) const185 OUString SwSortTextElement::GetKey(sal_uInt16 nId) const
186 {
187 SwTextNode* pTextNd = aPos.GetNode().GetTextNode();
188 if( !pTextNd )
189 return OUString();
190
191 // for TextNodes
192 const OUString& rStr = pTextNd->GetText();
193
194 sal_Unicode nDeli = pOptions->cDeli;
195 sal_uInt16 nDCount = pOptions->aKeys[nId].nColumnId, i = 1;
196 sal_Int32 nStart = 0;
197
198 // Find the delimiter
199 while( nStart != -1 && i < nDCount)
200 {
201 nStart = rStr.indexOf( nDeli, nStart );
202 if( -1 != nStart )
203 {
204 nStart++;
205 i++;
206 }
207 }
208
209 // Found next delimiter or end of String
210 // and copy
211 sal_Int32 nEnd = rStr.indexOf( nDeli, nStart+1 );
212 if (nEnd == -1)
213 return rStr.copy( nStart );
214 return rStr.copy( nStart, nEnd-nStart );
215 }
216
217 /// SortingElement for Tables
SwSortBoxElement(sal_uInt16 nRC)218 SwSortBoxElement::SwSortBoxElement( sal_uInt16 nRC )
219 : nRow( nRC )
220 {
221 }
222
223 /// Get Key for a cell
GetKey(sal_uInt16 nKey) const224 OUString SwSortBoxElement::GetKey(sal_uInt16 nKey) const
225 {
226 const FndBox_* pFndBox;
227 sal_uInt16 nCol = pOptions->aKeys[nKey].nColumnId-1;
228
229 if( SwSortDirection::Rows == pOptions->eDirection )
230 pFndBox = pBox->GetBox(nCol, nRow); // Sort rows
231 else
232 pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
233
234 // Extract the Text
235 OUStringBuffer aRetStr;
236 if( pFndBox )
237 { // Get StartNode and skip it
238 const SwTableBox* pMyBox = pFndBox->GetBox();
239 OSL_ENSURE(pMyBox, "No atomic Box");
240
241 if( pMyBox->GetSttNd() )
242 {
243 // Iterate over all the Box's TextNodes
244 const SwNode *pNd = nullptr, *pEndNd = pMyBox->GetSttNd()->EndOfSectionNode();
245 for( sal_uLong nIdx = pMyBox->GetSttIdx() + 1; pNd != pEndNd; ++nIdx )
246 {
247 pNd = pDoc->GetNodes()[ nIdx ];
248 if( pNd->IsTextNode() )
249 aRetStr.append(pNd->GetTextNode()->GetText());
250 }
251 }
252 }
253 return aRetStr.makeStringAndClear();
254 }
255
GetValue(sal_uInt16 nKey) const256 double SwSortBoxElement::GetValue( sal_uInt16 nKey ) const
257 {
258 const FndBox_* pFndBox;
259 sal_uInt16 nCol = pOptions->aKeys[nKey].nColumnId-1;
260
261 if( SwSortDirection::Rows == pOptions->eDirection )
262 pFndBox = pBox->GetBox(nCol, nRow); // Sort rows
263 else
264 pFndBox = pBox->GetBox(nRow, nCol); // Sort columns
265
266 double nVal;
267 if( pFndBox )
268 {
269 const SwFormat *pFormat = pFndBox->GetBox()->GetFrameFormat();
270 if (pDoc->GetNumberFormatter()->IsTextFormat( pFormat->GetTableBoxNumFormat().GetValue()))
271 nVal = SwSortElement::GetValue( nKey );
272 else
273 nVal = pFormat->GetTableBoxValue().GetValue();
274 }
275 else
276 nVal = 0;
277
278 return nVal;
279 }
280
281 /// Sort Text in the Document
SortText(const SwPaM & rPaM,const SwSortOptions & rOpt)282 bool SwDoc::SortText(const SwPaM& rPaM, const SwSortOptions& rOpt)
283 {
284 // Check if Frame is in the Text
285 const SwPosition *pStart = rPaM.Start(), *pEnd = rPaM.End();
286
287 // Set index to the Selection's start
288 for ( const auto *pFormat : *GetSpzFrameFormats() )
289 {
290 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor();
291 SwPosition const*const pAPos = pAnchor->GetContentAnchor();
292
293 if (pAPos && (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) &&
294 pStart->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode )
295 return false;
296 }
297
298 // Check if only TextNodes are within the Selection
299 {
300 sal_uLong nStart = pStart->nNode.GetIndex(),
301 nEnd = pEnd->nNode.GetIndex();
302 while( nStart <= nEnd )
303 // Iterate over a selected range
304 if( !GetNodes()[ nStart++ ]->IsTextNode() )
305 return false;
306 }
307
308 bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
309 if( bUndo )
310 {
311 GetIDocumentUndoRedo().StartUndo( SwUndoId::START, nullptr );
312 }
313
314 SwPaM* pRedlPam = nullptr;
315 SwUndoRedlineSort* pRedlUndo = nullptr;
316 SwUndoSort* pUndoSort = nullptr;
317
318 // To-Do - add 'SwExtraRedlineTable' also ?
319 if( getIDocumentRedlineAccess().IsRedlineOn() || (!getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() ))
320 {
321 pRedlPam = new SwPaM( pStart->nNode, pEnd->nNode, -1, 1 );
322 SwContentNode* pCNd = pRedlPam->GetContentNode( false );
323 if( pCNd )
324 pRedlPam->GetMark()->nContent = pCNd->Len();
325
326 if( getIDocumentRedlineAccess().IsRedlineOn() && !IDocumentRedlineAccess::IsShowOriginal( getIDocumentRedlineAccess().GetRedlineFlags() ) )
327 {
328 if( bUndo )
329 {
330 pRedlUndo = new SwUndoRedlineSort( *pRedlPam,rOpt );
331 GetIDocumentUndoRedo().DoUndo(false);
332 }
333 // First copy the range
334 SwNodeIndex aEndIdx( pEnd->nNode, 1 );
335 SwNodeRange aRg( pStart->nNode, aEndIdx );
336 GetNodes().Copy_( aRg, aEndIdx );
337
338 // range is new from pEnd->nNode+1 to aEndIdx
339 getIDocumentRedlineAccess().DeleteRedline( *pRedlPam, true, RedlineType::Any );
340
341 pRedlPam->GetMark()->nNode.Assign( pEnd->nNode.GetNode(), 1 );
342 pCNd = pRedlPam->GetContentNode( false );
343 pRedlPam->GetMark()->nContent.Assign( pCNd, 0 );
344
345 pRedlPam->GetPoint()->nNode.Assign( aEndIdx.GetNode() );
346 pCNd = pRedlPam->GetContentNode();
347 sal_Int32 nCLen = 0;
348 if( !pCNd )
349 {
350 pCNd = GetNodes()[ aEndIdx.GetIndex()-1 ]->GetContentNode();
351 if( pCNd )
352 {
353 nCLen = pCNd->Len();
354 pRedlPam->GetPoint()->nNode.Assign( *pCNd );
355 }
356 }
357 pRedlPam->GetPoint()->nContent.Assign( pCNd, nCLen );
358
359 if( pRedlUndo )
360 pRedlUndo->SetValues( rPaM );
361 }
362 else
363 {
364 getIDocumentRedlineAccess().DeleteRedline( *pRedlPam, true, RedlineType::Any );
365 delete pRedlPam;
366 pRedlPam = nullptr;
367 }
368 }
369
370 SwNodeIndex aStart(pStart->nNode);
371 SwSortElement::Init( this, rOpt );
372 std::multiset<SwSortTextElement> aSortSet;
373 while( aStart <= pEnd->nNode )
374 {
375 // Iterate over a selected range
376 aSortSet.insert(SwSortTextElement(aStart));
377 ++aStart;
378 }
379
380 // Now comes the tricky part: Move Nodes (and always keep Undo in mind)
381 sal_uLong nBeg = pStart->nNode.GetIndex();
382 SwNodeRange aRg( aStart, aStart );
383
384 if( bUndo && !pRedlUndo )
385 {
386 pUndoSort = new SwUndoSort(rPaM, rOpt);
387 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoSort));
388 }
389
390 GetIDocumentUndoRedo().DoUndo(false);
391
392 size_t n = 0;
393 for (const auto& rElem : aSortSet)
394 {
395 aStart = nBeg + n;
396 aRg.aStart = rElem.aPos.GetIndex();
397 aRg.aEnd = aRg.aStart.GetIndex() + 1;
398
399 // Move Nodes
400 getIDocumentContentOperations().MoveNodeRange( aRg, aStart,
401 SwMoveFlags::DEFAULT );
402
403 // Insert Move in Undo
404 if(pUndoSort)
405 {
406 pUndoSort->Insert(rElem.nOrg, nBeg + n);
407 }
408 ++n;
409 }
410 // Delete all elements from the SortArray
411 aSortSet.clear();
412 SwSortElement::Finit();
413
414 if( pRedlPam )
415 {
416 if( pRedlUndo )
417 {
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);
423 }
424
425 // nBeg is start of sorted range
426 SwNodeIndex aSttIdx( GetNodes(), nBeg );
427
428 // the copied range is deleted
429 SwRangeRedline *const pDeleteRedline(
430 new SwRangeRedline( RedlineType::Delete, *pRedlPam ));
431
432 // pRedlPam points to nodes that may be deleted (hidden) by
433 // AppendRedline, so adjust it beforehand to prevent ASSERT
434 pRedlPam->GetPoint()->nNode = aSttIdx;
435 SwContentNode* pCNd = aSttIdx.GetNode().GetContentNode();
436 pRedlPam->GetPoint()->nContent.Assign( pCNd, 0 );
437
438 getIDocumentRedlineAccess().AppendRedline(pDeleteRedline, true);
439
440 // the sorted range is inserted
441 getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, *pRedlPam ), true);
442
443 if( pRedlUndo )
444 {
445 SwNodeIndex aInsEndIdx( pRedlPam->GetMark()->nNode, -1 );
446 pRedlPam->GetMark()->nNode = aInsEndIdx;
447 SwContentNode *const pPrevNode =
448 pRedlPam->GetMark()->nNode.GetNode().GetContentNode();
449 pRedlPam->GetMark()->nContent.Assign( pPrevNode, pPrevNode->Len() );
450
451 pRedlUndo->SetValues( *pRedlPam );
452 }
453
454 delete pRedlPam;
455 pRedlPam = nullptr;
456 }
457 GetIDocumentUndoRedo().DoUndo( bUndo );
458 if( bUndo )
459 {
460 GetIDocumentUndoRedo().EndUndo( SwUndoId::END, nullptr );
461 }
462
463 return true;
464 }
465
466 /// Sort Table in the Document
SortTable(const SwSelBoxes & rBoxes,const SwSortOptions & rOpt)467 bool SwDoc::SortTable(const SwSelBoxes& rBoxes, const SwSortOptions& rOpt)
468 {
469 // Via SwDoc for Undo!
470 OSL_ENSURE( !rBoxes.empty(), "no valid Box list" );
471 SwTableNode* pTableNd = const_cast<SwTableNode*>(rBoxes[0]->GetSttNd()->FindTableNode());
472 if( !pTableNd )
473 return false;
474
475 // We begin sorting
476 // Find all Boxes/Lines
477 FndBox_ aFndBox( nullptr, nullptr );
478 {
479 FndPara aPara( rBoxes, &aFndBox );
480 ForEach_FndLineCopyCol( pTableNd->GetTable().GetTabLines(), &aPara );
481 }
482
483 if(aFndBox.GetLines().empty())
484 return false;
485
486 if( !getIDocumentRedlineAccess().IsIgnoreRedline() && !getIDocumentRedlineAccess().GetRedlineTable().empty() )
487 getIDocumentRedlineAccess().DeleteRedline( *pTableNd, true, RedlineType::Any );
488
489 FndLines_t::size_type nStart = 0;
490 if( pTableNd->GetTable().GetRowsToRepeat() > 0 && rOpt.eDirection == SwSortDirection::Rows )
491 {
492 // Uppermost selected Cell
493 FndLines_t& rLines = aFndBox.GetLines();
494
495 while( nStart < rLines.size() )
496 {
497 // Respect Split Merge nesting,
498 // extract the upper most
499 SwTableLine* pLine = rLines[nStart]->GetLine();
500 while ( pLine->GetUpper() )
501 pLine = pLine->GetUpper()->GetUpper();
502
503 if( pTableNd->GetTable().IsHeadline( *pLine ) )
504 nStart++;
505 else
506 break;
507 }
508 // Are all selected in the HeaderLine? -> no Offset
509 if( nStart == rLines.size() )
510 nStart = 0;
511 }
512
513 // Switch to relative Formulas
514 SwTableFormulaUpdate aMsgHint( &pTableNd->GetTable() );
515 aMsgHint.m_eFlags = TBL_RELBOXNAME;
516 getIDocumentFieldsAccess().UpdateTableFields( &aMsgHint );
517
518 // Table as a flat array structure
519 FlatFndBox aFlatBox(this, aFndBox);
520
521 if(!aFlatBox.IsSymmetric())
522 return false;
523
524 // Delete HTML layout
525 pTableNd->GetTable().SetHTMLTableLayout(std::shared_ptr<SwHTMLTableLayout>());
526
527 // #i37739# A simple 'MakeFrames' after the node sorting
528 // does not work if the table is inside a frame and has no prev/next.
529 SwNode2LayoutSaveUpperFrames aNode2Layout(*pTableNd);
530
531 // Delete the Table's Frames
532 pTableNd->DelFrames();
533 // ? TL_CHART2: ?
534
535 SwUndoSort* pUndoSort = nullptr;
536 if (GetIDocumentUndoRedo().DoesUndo())
537 {
538 pUndoSort = new SwUndoSort( rBoxes[0]->GetSttIdx(),
539 rBoxes.back()->GetSttIdx(),
540 *pTableNd, rOpt, aFlatBox.HasItemSets() );
541 GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoSort));
542 }
543 ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
544
545 // Insert KeyElements
546 sal_uInt16 nCount = (rOpt.eDirection == SwSortDirection::Rows) ?
547 aFlatBox.GetRows() : aFlatBox.GetCols();
548
549 // Sort SortList by Key
550 SwSortElement::Init( this, rOpt, &aFlatBox );
551 std::multiset<SwSortBoxElement> aSortList;
552
553 // When sorting, do not include the first row if the HeaderLine is repeated
554 for( sal_uInt16 i = o3tl::narrowing<sal_uInt16>(nStart); i < nCount; ++i)
555 {
556 aSortList.insert(SwSortBoxElement(i));
557 }
558
559 // Move after Sorting
560 SwMovedBoxes aMovedList;
561 sal_uInt16 i = 0;
562 for (const auto& rElem : aSortList)
563 {
564 if(rOpt.eDirection == SwSortDirection::Rows)
565 {
566 MoveRow(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
567 }
568 else
569 {
570 MoveCol(this, aFlatBox, rElem.nRow, i+nStart, aMovedList, pUndoSort);
571 }
572 ++i;
573 }
574
575 // Restore table frames:
576 // #i37739# A simple 'MakeFrames' after the node sorting
577 // does not work if the table is inside a frame and has no prev/next.
578 const sal_uLong nIdx = pTableNd->GetIndex();
579 aNode2Layout.RestoreUpperFrames( GetNodes(), nIdx, nIdx + 1 );
580
581 // TL_CHART2: need to inform chart of probably changed cell names
582 UpdateCharts( pTableNd->GetTable().GetFrameFormat()->GetName() );
583
584 // Delete all Elements in the SortArray
585 aSortList.clear();
586 SwSortElement::Finit();
587
588 getIDocumentState().SetModified();
589 return true;
590 }
591
592 /// Move a row
MoveRow(SwDoc * pDoc,const FlatFndBox & rBox,sal_uInt16 nS,sal_uInt16 nT,SwMovedBoxes & rMovedList,SwUndoSort * pUD)593 void MoveRow(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
594 SwMovedBoxes& rMovedList, SwUndoSort* pUD)
595 {
596 for( sal_uInt16 i=0; i < rBox.GetCols(); ++i )
597 { // Get old cell position and remember it
598 const FndBox_* pSource = rBox.GetBox(i, nS);
599
600 // new cell position
601 const FndBox_* pTarget = rBox.GetBox(i, nT);
602
603 const SwTableBox* pT = pTarget->GetBox();
604 const SwTableBox* pS = pSource->GetBox();
605
606 bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
607
608 // and move it
609 MoveCell(pDoc, pS, pT, bMoved, pUD);
610
611 rMovedList.push_back(pS);
612
613 if( pS != pT )
614 {
615 SwFrameFormat* pTFormat = pT->GetFrameFormat();
616 const SfxItemSet* pSSet = rBox.GetItemSet( i, nS );
617
618 if( pSSet ||
619 SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMAT ) ||
620 SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMULA ) ||
621 SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_VALUE ) )
622 {
623 pTFormat = const_cast<SwTableBox*>(pT)->ClaimFrameFormat();
624 pTFormat->LockModify();
625 if( pTFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
626 pTFormat->ResetFormatAttr( RES_VERT_ORIENT );
627
628 if( pSSet )
629 pTFormat->SetFormatAttr( *pSSet );
630 pTFormat->UnlockModify();
631 }
632 }
633 }
634 }
635
636 /// Move a column
MoveCol(SwDoc * pDoc,const FlatFndBox & rBox,sal_uInt16 nS,sal_uInt16 nT,SwMovedBoxes & rMovedList,SwUndoSort * pUD)637 void MoveCol(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
638 SwMovedBoxes& rMovedList, SwUndoSort* pUD)
639 {
640 for(sal_uInt16 i=0; i < rBox.GetRows(); ++i)
641 { // Get old cell position and remember it
642 const FndBox_* pSource = rBox.GetBox(nS, i);
643
644 // new cell position
645 const FndBox_* pTarget = rBox.GetBox(nT, i);
646
647 // and move it
648 const SwTableBox* pT = pTarget->GetBox();
649 const SwTableBox* pS = pSource->GetBox();
650
651 // and move it
652 bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
653 MoveCell(pDoc, pS, pT, bMoved, pUD);
654
655 rMovedList.push_back(pS);
656
657 if( pS != pT )
658 {
659 SwFrameFormat* pTFormat = pT->GetFrameFormat();
660 const SfxItemSet* pSSet = rBox.GetItemSet( nS, i );
661
662 if( pSSet ||
663 SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMAT ) ||
664 SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_FORMULA ) ||
665 SfxItemState::SET == pTFormat->GetItemState( RES_BOXATR_VALUE ) )
666 {
667 pTFormat = const_cast<SwTableBox*>(pT)->ClaimFrameFormat();
668 pTFormat->LockModify();
669 if( pTFormat->ResetFormatAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
670 pTFormat->ResetFormatAttr( RES_VERT_ORIENT );
671
672 if( pSSet )
673 pTFormat->SetFormatAttr( *pSSet );
674 pTFormat->UnlockModify();
675 }
676 }
677 }
678 }
679
680 /// Move a single Cell
MoveCell(SwDoc * pDoc,const SwTableBox * pSource,const SwTableBox * pTar,bool bMovedBefore,SwUndoSort * pUD)681 void MoveCell(SwDoc* pDoc, const SwTableBox* pSource, const SwTableBox* pTar,
682 bool bMovedBefore, SwUndoSort* pUD)
683 {
684 OSL_ENSURE(pSource && pTar,"Source or target missing");
685
686 if(pSource == pTar)
687 return;
688
689 if(pUD)
690 pUD->Insert( pSource->GetName(), pTar->GetName() );
691
692 // Set Pam source to the first ContentNode
693 SwNodeRange aRg( *pSource->GetSttNd(), 0, *pSource->GetSttNd() );
694 SwNode* pNd = pDoc->GetNodes().GoNext( &aRg.aStart );
695
696 // If the Cell (Source) wasn't moved
697 // -> insert an empty Node and move the rest or the Mark
698 // points to the first ContentNode
699 if( pNd->StartOfSectionNode() == pSource->GetSttNd() )
700 pNd = pDoc->GetNodes().MakeTextNode( aRg.aStart,
701 pDoc->GetDfltTextFormatColl() );
702 aRg.aEnd = *pNd->EndOfSectionNode();
703
704 // If the Target is empty (there is one empty Node)
705 // -> move and delete it
706 SwNodeIndex aTar( *pTar->GetSttNd() );
707 pNd = pDoc->GetNodes().GoNext( &aTar ); // next ContentNode
708 sal_uLong nCount = pNd->EndOfSectionIndex() - pNd->StartOfSectionIndex();
709
710 bool bDelFirst = false;
711 if( nCount == 2 )
712 {
713 OSL_ENSURE( pNd->GetContentNode(), "No ContentNode");
714 bDelFirst = !pNd->GetContentNode()->Len() && bMovedBefore;
715 }
716
717 if(!bDelFirst)
718 { // We already have Content -> old Content Section Down
719 SwNodeRange aRgTar( aTar.GetNode(), 0, *pNd->EndOfSectionNode() );
720 pDoc->GetNodes().SectionDown( &aRgTar );
721 }
722
723 // Insert the Source
724 SwNodeIndex aIns( *pTar->GetSttNd()->EndOfSectionNode() );
725 pDoc->getIDocumentContentOperations().MoveNodeRange( aRg, aIns,
726 SwMoveFlags::DEFAULT );
727
728 // If first Node is empty -> delete it
729 if(bDelFirst)
730 pDoc->GetNodes().Delete( aTar );
731 }
732
733 /// Generate two-dimensional array of FndBoxes
FlatFndBox(SwDoc * pDocPtr,const FndBox_ & rBoxRef)734 FlatFndBox::FlatFndBox(SwDoc* pDocPtr, const FndBox_& rBoxRef) :
735 m_pDoc(pDocPtr),
736 m_nRow(0),
737 m_nCol(0)
738 { // If the array is symmetric
739 m_bSym = CheckLineSymmetry(rBoxRef);
740 if( !m_bSym )
741 return;
742
743 // Determine column/row count
744 m_nCols = GetColCount(rBoxRef);
745 m_nRows = GetRowCount(rBoxRef);
746
747 // Create linear array
748 size_t nCount = static_cast<size_t>(m_nRows) * m_nCols;
749 m_pArr = std::make_unique<FndBox_ const *[]>(nCount);
750 memset(m_pArr.get(), 0, sizeof(const FndBox_*) * nCount);
751
752 FillFlat( rBoxRef );
753 }
754
~FlatFndBox()755 FlatFndBox::~FlatFndBox()
756 {
757 }
758
759 /// All Lines of a Box need to have same number of Boxes
CheckLineSymmetry(const FndBox_ & rBox)760 bool FlatFndBox::CheckLineSymmetry(const FndBox_& rBox)
761 {
762 const FndLines_t &rLines = rBox.GetLines();
763 FndBoxes_t::size_type nBoxes {0};
764
765 for (FndLines_t::size_type i=0; i < rLines.size(); ++i)
766 {
767 const FndLine_* pLn = rLines[i].get();
768 const FndBoxes_t& rBoxes = pLn->GetBoxes();
769
770 // Number of Boxes of all Lines is unequal -> no symmetry
771 if( i && nBoxes != rBoxes.size())
772 return false;
773
774 nBoxes = rBoxes.size();
775 if( !CheckBoxSymmetry( *pLn ) )
776 return false;
777 }
778 return true;
779 }
780
781 /// Check Box for symmetry (All Boxes of a Line need to have same number of Lines)
CheckBoxSymmetry(const FndLine_ & rLn)782 bool FlatFndBox::CheckBoxSymmetry(const FndLine_& rLn)
783 {
784 const FndBoxes_t &rBoxes = rLn.GetBoxes();
785 FndLines_t::size_type nLines {0};
786
787 for (FndBoxes_t::size_type i = 0; i < rBoxes.size(); ++i)
788 {
789 FndBox_ const*const pBox = rBoxes[i].get();
790 const FndLines_t& rLines = pBox->GetLines();
791
792 // Number of Lines of all Boxes is unequal -> no symmetry
793 if( i && nLines != rLines.size() )
794 return false;
795
796 nLines = rLines.size();
797 if( nLines && !CheckLineSymmetry( *pBox ) )
798 return false;
799 }
800 return true;
801 }
802
803 /// Maximum count of Columns (Boxes)
GetColCount(const FndBox_ & rBox)804 sal_uInt16 FlatFndBox::GetColCount(const FndBox_& rBox)
805 {
806 const FndLines_t& rLines = rBox.GetLines();
807 // Iterate over Lines
808 if( rLines.empty() )
809 return 1;
810
811 sal_uInt16 nSum = 0;
812 for (const auto & pLine : rLines)
813 {
814 // The Boxes of a Line
815 sal_uInt16 nCount = 0;
816 const FndBoxes_t& rBoxes = pLine->GetBoxes();
817 for (const auto &rpB : rBoxes)
818 { // Iterate recursively over the Lines
819 nCount += rpB->GetLines().empty() ? 1 : GetColCount(*rpB);
820 }
821
822 if( nSum < nCount )
823 nSum = nCount;
824 }
825 return nSum;
826 }
827
828 /// Maximum count of Rows (Lines)
GetRowCount(const FndBox_ & rBox)829 sal_uInt16 FlatFndBox::GetRowCount(const FndBox_& rBox)
830 {
831 const FndLines_t& rLines = rBox.GetLines();
832 if( rLines.empty() )
833 return 1;
834
835 sal_uInt16 nLines = 0;
836 for (const auto & pLine : rLines)
837 { // The Boxes of a Line
838 const FndBoxes_t& rBoxes = pLine->GetBoxes();
839 sal_uInt16 nLn = 1;
840 for (const auto &rpB : rBoxes)
841 {
842 if (!rpB->GetLines().empty())
843 { // Iterate recursively over the Lines
844 nLn = std::max(GetRowCount(*rpB), nLn);
845 }
846 }
847
848 nLines = nLines + nLn;
849 }
850 return nLines;
851 }
852
853 /// Create a linear array of atomic FndBoxes
FillFlat(const FndBox_ & rBox,bool bLastBox)854 void FlatFndBox::FillFlat(const FndBox_& rBox, bool bLastBox)
855 {
856 bool bModRow = false;
857 const FndLines_t& rLines = rBox.GetLines();
858
859 // Iterate over Lines
860 sal_uInt16 nOldRow = m_nRow;
861 for (const auto & pLine : rLines)
862 {
863 // The Boxes of a Line
864 const FndBoxes_t& rBoxes = pLine->GetBoxes();
865 sal_uInt16 nOldCol = m_nCol;
866 for( FndBoxes_t::size_type j = 0; j < rBoxes.size(); ++j )
867 {
868 // Check the Box if it's an atomic one
869 const FndBox_ *const pBox = rBoxes[j].get();
870
871 if( pBox->GetLines().empty() )
872 {
873 // save it
874 sal_uInt16 nOff = m_nRow * m_nCols + m_nCol;
875 m_pArr[nOff] = pBox;
876
877 // Save the Formula/Format/Value values
878 const SwFrameFormat* pFormat = pBox->GetBox()->GetFrameFormat();
879 if( SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMAT ) ||
880 SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_FORMULA ) ||
881 SfxItemState::SET == pFormat->GetItemState( RES_BOXATR_VALUE ) )
882 {
883 auto pSet = std::make_unique<SfxItemSet>(
884 m_pDoc->GetAttrPool(),
885 svl::Items<
886 RES_VERT_ORIENT, RES_VERT_ORIENT,
887 RES_BOXATR_FORMAT, RES_BOXATR_VALUE>{});
888 pSet->Put( pFormat->GetAttrSet() );
889 if( m_ppItemSets.empty() )
890 {
891 size_t nCount = static_cast<size_t>(m_nRows) * m_nCols;
892 m_ppItemSets.resize(nCount);
893 }
894 m_ppItemSets[nOff] = std::move(pSet);
895 }
896
897 bModRow = true;
898 }
899 else
900 {
901 // Iterate recursively over the Lines of a Box
902 FillFlat( *pBox, ( j+1 == rBoxes.size() ) );
903 }
904 m_nCol++;
905 }
906 if(bModRow)
907 m_nRow++;
908 m_nCol = nOldCol;
909 }
910 if(!bLastBox)
911 m_nRow = nOldRow;
912 }
913
914 /// Access a specific Cell
GetBox(sal_uInt16 n_Col,sal_uInt16 n_Row) const915 const FndBox_* FlatFndBox::GetBox(sal_uInt16 n_Col, sal_uInt16 n_Row) const
916 {
917 sal_uInt16 nOff = n_Row * m_nCols + n_Col;
918 const FndBox_* pTmp = m_pArr[nOff];
919
920 OSL_ENSURE(n_Col < m_nCols && n_Row < m_nRows && pTmp, "invalid array access");
921 return pTmp;
922 }
923
GetItemSet(sal_uInt16 n_Col,sal_uInt16 n_Row) const924 const SfxItemSet* FlatFndBox::GetItemSet(sal_uInt16 n_Col, sal_uInt16 n_Row) const
925 {
926 OSL_ENSURE( m_ppItemSets.empty() || ( n_Col < m_nCols && n_Row < m_nRows), "invalid array access");
927
928 return !m_ppItemSets.empty() ? m_ppItemSets[unsigned(n_Row * m_nCols) + n_Col].get() : nullptr;
929 }
930
GetPos(const SwTableBox * pTableBox) const931 sal_uInt16 SwMovedBoxes::GetPos(const SwTableBox* pTableBox) const
932 {
933 std::vector<const SwTableBox*>::const_iterator it = std::find(mBoxes.begin(), mBoxes.end(), pTableBox);
934 return it == mBoxes.end() ? USHRT_MAX : it - mBoxes.begin();
935 }
936
937 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
938