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