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