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