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