xref: /trunk/main/sw/source/core/doc/docsort.cxx (revision efeef26f)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sw.hxx"
26 
27 #include <hintids.hxx>
28 #include <rtl/math.hxx>
29 #include <unotools/collatorwrapper.hxx>
30 #include <unotools/localedatawrapper.hxx>
31 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
32 #include <com/sun/star/i18n/CollatorOptions.hpp>
33 #include <comphelper/processfactory.hxx>
34 #include <editeng/unolingu.hxx>
35 #include <docary.hxx>
36 #include <fmtanchr.hxx>
37 #include <frmfmt.hxx>
38 #include <doc.hxx>
39 #include <IDocumentUndoRedo.hxx>
40 #include <node.hxx>
41 #include <pam.hxx>
42 #include <ndtxt.hxx>
43 #include <swtable.hxx>
44 #include <swundo.hxx>
45 #include <sortopt.hxx>
46 #include <docsort.hxx>
47 #include <UndoSort.hxx>
48 #include <UndoRedline.hxx>
49 #include <hints.hxx>
50 #include <tblsel.hxx>
51 #include <cellatr.hxx>
52 #include <redline.hxx>
53 #include <node2lay.hxx>
54 #include <unochart.hxx>
55 
56 #if OSL_DEBUG_LEVEL > 1
57 //nur zum debugen
58 #include <cellatr.hxx>
59 #endif
60 
61 using namespace ::com::sun::star::lang;
62 
63 SwSortOptions*		SwSortElement::pOptions = 0;
64 SwDoc* 				SwSortElement::pDoc = 0;
65 const FlatFndBox*	SwSortElement::pBox = 0;
66 CollatorWrapper* 	SwSortElement::pSortCollator = 0;
67 Locale* 			SwSortElement::pLocale = 0;
68 String* 			SwSortElement::pLastAlgorithm = 0;
69 LocaleDataWrapper* 	SwSortElement::pLclData = 0;
70 
71 SV_IMPL_OP_PTRARR_SORT( SwSortElements, SwSortElementPtr );
72 
73 
74 /*--------------------------------------------------------------------
75 	Beschreibung: Ein Sortierelement fuers Sort konstruieren
76  --------------------------------------------------------------------*/
77 
78 
Init(SwDoc * pD,const SwSortOptions & rOpt,FlatFndBox * pFltBx)79 void SwSortElement::Init( SwDoc* pD, const SwSortOptions& rOpt,
80 							FlatFndBox* pFltBx )
81 {
82 	ASSERT( !pDoc && !pOptions && !pBox, "wer hat das Finit vergessen?" );
83 	pDoc = pD;
84 	pOptions = new SwSortOptions( rOpt );
85 	pBox = pFltBx;
86 
87 	LanguageType nLang = rOpt.nLanguage;
88 	switch ( nLang )
89 	{
90 	case LANGUAGE_NONE:
91 	case LANGUAGE_DONTKNOW:
92 		nLang = (LanguageType)GetAppLanguage();
93 		break;
94 	}
95 	pLocale = new Locale( SvxCreateLocale( nLang ) );
96 
97 	pSortCollator = new CollatorWrapper(
98 								::comphelper::getProcessServiceFactory() );
99 //	pSortCollator->loadCollatorAlgorithm( sAlgorithm, aLocale,
100 //						rOpt.bIgnoreCase ? SW_COLLATOR_IGNORES : 0 );
101 }
102 
103 
Finit()104 void SwSortElement::Finit()
105 {
106 	delete pOptions, pOptions = 0;
107 	delete pLocale, pLocale = 0;
108 	delete pLastAlgorithm, pLastAlgorithm = 0;
109 	delete pSortCollator, pSortCollator = 0;
110 	delete pLclData, pLclData = 0;
111 	pDoc = 0;
112 	pBox = 0;
113 }
114 
115 
~SwSortElement()116 SwSortElement::~SwSortElement()
117 {
118 }
119 
120 
StrToDouble(const String & rStr) const121 double SwSortElement::StrToDouble( const String& rStr ) const
122 {
123 	if( !pLclData )
124 		pLclData = new LocaleDataWrapper(
125 					::comphelper::getProcessServiceFactory(), *pLocale );
126 
127     rtl_math_ConversionStatus eStatus;
128     sal_Int32 nEnd;
129 	double nRet = ::rtl::math::stringToDouble( rStr,
130 									pLclData->getNumDecimalSep().GetChar(0),
131 									pLclData->getNumThousandSep().GetChar(0),
132 									&eStatus, &nEnd );
133 
134 	if( rtl_math_ConversionStatus_Ok != eStatus || nEnd == 0 )
135 		nRet = 0.0;
136 	return nRet;
137 }
138 
139 /*--------------------------------------------------------------------
140 	Beschreibung: Operatoren zum Vergleichen
141  --------------------------------------------------------------------*/
142 
143 
operator ==(const SwSortElement &)144 sal_Bool SwSortElement::operator==(const SwSortElement& )
145 {
146 	return sal_False;
147 }
148 
149 /*--------------------------------------------------------------------
150 	Beschreibung: Kleiner-Operator fuers sortieren
151  --------------------------------------------------------------------*/
152 
operator <(const SwSortElement & rCmp)153 sal_Bool SwSortElement::operator<(const SwSortElement& rCmp)
154 {
155 
156 	// der eigentliche Vergleich
157 	//
158 	for(sal_uInt16 nKey = 0; nKey < pOptions->aKeys.Count(); ++nKey)
159 	{
160 		const SwSortElement *pOrig, *pCmp;
161 
162 		const SwSortKey* pSrtKey = pOptions->aKeys[ nKey ];
163 		if( pSrtKey->eSortOrder == SRT_ASCENDING )
164 			pOrig = this, pCmp = &rCmp;
165 		else
166 			pOrig = &rCmp, pCmp = this;
167 
168 		if( pSrtKey->bIsNumeric )
169 		{
170 			double n1 = pOrig->GetValue( nKey );
171 			double n2 = pCmp->GetValue( nKey );
172 
173 			if( n1 == n2 )
174 				continue;
175 
176 			return n1 < n2;
177 		}
178 		else
179 		{
180 			if( !pLastAlgorithm || *pLastAlgorithm != pSrtKey->sSortType )
181 			{
182 				if( pLastAlgorithm )
183 					*pLastAlgorithm = pSrtKey->sSortType;
184 				else
185 					pLastAlgorithm = new String( pSrtKey->sSortType );
186 				pSortCollator->loadCollatorAlgorithm( *pLastAlgorithm,
187 						*pLocale,
188 						pOptions->bIgnoreCase ? SW_COLLATOR_IGNORES : 0 );
189 			}
190 
191 			sal_Int32 nCmp = pSortCollator->compareString(
192 						pOrig->GetKey( nKey ), pCmp->GetKey( nKey ));
193 			if( 0 == nCmp )
194 				continue;
195 
196 			return -1 == nCmp;
197 		}
198 	}
199 	return sal_False;
200 }
201 
GetValue(sal_uInt16 nKey) const202 double SwSortElement::GetValue( sal_uInt16 nKey ) const
203 {
204 	return StrToDouble( GetKey( nKey ));
205 }
206 
207 /*--------------------------------------------------------------------
208 	Beschreibung: SortierElemente fuer Text
209  --------------------------------------------------------------------*/
210 
211 
SwSortTxtElement(const SwNodeIndex & rPos)212 SwSortTxtElement::SwSortTxtElement(const SwNodeIndex& rPos)
213 	: nOrg(rPos.GetIndex()), aPos(rPos)
214 {
215 }
216 
217 
~SwSortTxtElement()218 SwSortTxtElement::~SwSortTxtElement()
219 {
220 }
221 
222 
223 /*--------------------------------------------------------------------
224 	Beschreibung: Key ermitteln
225  --------------------------------------------------------------------*/
226 
227 
GetKey(sal_uInt16 nId) const228 String SwSortTxtElement::GetKey(sal_uInt16 nId) const
229 {
230 	SwTxtNode* pTxtNd = aPos.GetNode().GetTxtNode();
231 	if( !pTxtNd )
232 		return aEmptyStr;
233 
234 	// fuer TextNodes
235 	const String& rStr = pTxtNd->GetTxt();
236 
237 	sal_Unicode nDeli = pOptions->cDeli;
238 	sal_uInt16 nDCount = pOptions->aKeys[nId]->nColumnId, i = 1;
239 	xub_StrLen nStart = 0;
240 
241 	// Den Delimitter suchen
242 	while( nStart != STRING_NOTFOUND && i < nDCount)
243 		if( STRING_NOTFOUND != ( nStart = rStr.Search( nDeli, nStart ) ) )
244 		{
245 			nStart++;
246 			i++;
247 		}
248 
249 	// naechsten Delimitter gefunden oder Ende des Strings und Kopieren
250 	xub_StrLen nEnd = rStr.Search( nDeli, nStart+1 );
251 	return rStr.Copy( nStart, nEnd-nStart );
252 }
253 
254 
255 /*--------------------------------------------------------------------
256 	Beschreibung: Sortier-Elemente fuer Tabellen
257  --------------------------------------------------------------------*/
258 
SwSortBoxElement(sal_uInt16 nRC)259 SwSortBoxElement::SwSortBoxElement( sal_uInt16 nRC )
260 	: nRow( nRC )
261 {
262 }
263 
264 
~SwSortBoxElement()265 SwSortBoxElement::~SwSortBoxElement()
266 {
267 }
268 
269 /*--------------------------------------------------------------------
270 	Beschreibung: Schluessel zu einer Zelle ermitteln
271  --------------------------------------------------------------------*/
272 
273 
GetKey(sal_uInt16 nKey) const274 String SwSortBoxElement::GetKey(sal_uInt16 nKey) const
275 {
276 	const _FndBox* pFndBox;
277 	sal_uInt16 nCol = pOptions->aKeys[nKey]->nColumnId-1;
278 
279 	if( SRT_ROWS == pOptions->eDirection )
280 		pFndBox = pBox->GetBox(nCol, nRow);			// Zeilen sortieren
281 	else
282 		pFndBox = pBox->GetBox(nRow, nCol);			// Spalten sortieren
283 
284 	// Den Text rausfieseln
285 	String aRetStr;
286 	if( pFndBox )
287 	{	// StartNode holen und ueberlesen
288 		const SwTableBox* pMyBox = pFndBox->GetBox();
289 		ASSERT(pMyBox, "Keine atomare Box");
290 
291 		if( pMyBox->GetSttNd() )
292 		{
293 			// ueber alle TextNodes der Box
294 			const SwNode *pNd = 0, *pEndNd = pMyBox->GetSttNd()->EndOfSectionNode();
295 			for( sal_uLong nIdx = pMyBox->GetSttIdx() + 1; pNd != pEndNd; ++nIdx )
296 				if( ( pNd = pDoc->GetNodes()[ nIdx ])->IsTxtNode() )
297 					aRetStr += ((SwTxtNode*)pNd)->GetTxt();
298 		}
299 	}
300 	return aRetStr;
301 }
302 
GetValue(sal_uInt16 nKey) const303 double SwSortBoxElement::GetValue( sal_uInt16 nKey ) const
304 {
305 	const _FndBox* pFndBox;
306 	sal_uInt16 nCol = pOptions->aKeys[nKey]->nColumnId-1;
307 
308 	if( SRT_ROWS == pOptions->eDirection )
309 		pFndBox = pBox->GetBox(nCol, nRow);			// Zeilen sortieren
310 	else
311 		pFndBox = pBox->GetBox(nRow, nCol);			// Spalten sortieren
312 
313     double nVal;
314     if( pFndBox )
315     {
316         const SwFmt *pFmt = pFndBox->GetBox()->GetFrmFmt();
317         if (pFmt->GetTblBoxNumFmt().GetValue() & NUMBERFORMAT_TEXT)
318             nVal = SwSortElement::GetValue( nKey );
319         else
320             nVal = pFmt->GetTblBoxValue().GetValue();
321     }
322     else
323         nVal = 0;
324 
325     return nVal;
326 }
327 
328 /*--------------------------------------------------------------------
329 	Beschreibung: Text sortieren im Document
330  --------------------------------------------------------------------*/
331 
332 
SortText(const SwPaM & rPaM,const SwSortOptions & rOpt)333 sal_Bool SwDoc::SortText(const SwPaM& rPaM, const SwSortOptions& rOpt)
334 {
335 	// pruefen ob Rahmen im Text
336 	const SwPosition *pStart = rPaM.Start(), *pEnd = rPaM.End();
337 	// Index auf den Start der Selektion
338 
339     for ( sal_uInt16 n = 0; n < GetSpzFrmFmts()->Count(); ++n )
340     {
341         SwFrmFmt *const pFmt = static_cast<SwFrmFmt*>((*GetSpzFrmFmts())[n]);
342         SwFmtAnchor const*const pAnchor = &pFmt->GetAnchor();
343         SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
344 
345         if (pAPos && (FLY_AT_PARA == pAnchor->GetAnchorId()) &&
346 			pStart->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode )
347 			return sal_False;
348 	}
349 
350 	// pruefe ob nur TextNodes in der Selection liegen
351 	{
352 		sal_uLong nStart = pStart->nNode.GetIndex(),
353 						nEnd = pEnd->nNode.GetIndex();
354 		while( nStart <= nEnd )
355 			// Iterieren ueber einen selektierten Bereich
356 			if( !GetNodes()[ nStart++ ]->IsTxtNode() )
357 				return sal_False;
358 	}
359 
360     bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
361 	if( bUndo )
362     {
363         GetIDocumentUndoRedo().StartUndo( UNDO_START, NULL );
364     }
365 
366 	SwPaM* pRedlPam = 0;
367 	SwUndoRedlineSort* pRedlUndo = 0;
368 	SwUndoSort* pUndoSort = 0;
369 
370 	if( IsRedlineOn() || (!IsIgnoreRedline() && pRedlineTbl->Count() ))
371 	{
372 		pRedlPam = new SwPaM( pStart->nNode, pEnd->nNode, -1, 1 );
373 		SwCntntNode* pCNd = pRedlPam->GetCntntNode( sal_False );
374 		if( pCNd )
375 			pRedlPam->GetMark()->nContent = pCNd->Len();
376 
377 		if( IsRedlineOn() && !IsShowOriginal( GetRedlineMode() ) )
378 		{
379 			if( bUndo )
380 			{
381 				pRedlUndo = new SwUndoRedlineSort( *pRedlPam,rOpt );
382                 GetIDocumentUndoRedo().DoUndo(false);
383             }
384 			// erst den Bereich kopieren, dann
385 			SwNodeIndex aEndIdx( pEnd->nNode, 1 );
386 			SwNodeRange aRg( pStart->nNode, aEndIdx );
387 			GetNodes()._Copy( aRg, aEndIdx );
388 
389 			// Bereich neu ist von pEnd->nNode+1 bis aEndIdx
390 			DeleteRedline( *pRedlPam, true, USHRT_MAX );
391 
392 			pRedlPam->GetMark()->nNode.Assign( pEnd->nNode.GetNode(), 1 );
393 			pCNd = pRedlPam->GetCntntNode( sal_False );
394 			pRedlPam->GetMark()->nContent.Assign( pCNd, 0 );
395 
396 			pRedlPam->GetPoint()->nNode.Assign( aEndIdx.GetNode() );
397 			pCNd = pRedlPam->GetCntntNode( sal_True );
398 			xub_StrLen nCLen = 0;
399 			if( !pCNd &&
400 				0 != (pCNd = GetNodes()[ aEndIdx.GetIndex()-1 ]->GetCntntNode()))
401 			{
402 				nCLen = pCNd->Len();
403 				pRedlPam->GetPoint()->nNode.Assign( *pCNd );
404 			}
405 			pRedlPam->GetPoint()->nContent.Assign( pCNd, nCLen );
406 
407 			if( pRedlUndo )
408 				pRedlUndo->SetValues( rPaM );
409 		}
410 		else
411 		{
412 			DeleteRedline( *pRedlPam, true, USHRT_MAX );
413 			delete pRedlPam, pRedlPam = 0;
414 		}
415 	}
416 
417 	SwNodeIndex aStart(pStart->nNode);
418 	SwSortElement::Init( this, rOpt );
419 	SwSortElements aSortArr;
420 	while( aStart <= pEnd->nNode )
421 	{
422 		// Iterieren ueber einen selektierten Bereich
423 		SwSortTxtElement* pSE = new SwSortTxtElement( aStart );
424 		aSortArr.Insert(pSE);
425 		aStart++;
426 	}
427 
428 	// Und jetzt der Akt: Verschieben von Nodes und immer schoen auf UNDO
429 	// achten
430 	//
431 	sal_uLong nBeg = pStart->nNode.GetIndex();
432 	SwNodeRange aRg( aStart, aStart );
433 
434 	if( bUndo && !pRedlUndo )
435     {
436         pUndoSort = new SwUndoSort(rPaM, rOpt);
437         GetIDocumentUndoRedo().AppendUndo(pUndoSort);
438     }
439 
440     GetIDocumentUndoRedo().DoUndo(false);
441 
442     for ( sal_uInt16 n = 0; n < aSortArr.Count(); ++n )
443     {
444 		SwSortTxtElement* pBox = (SwSortTxtElement*)aSortArr[n];
445 		aStart 		= nBeg + n;
446 		aRg.aStart 	= pBox->aPos.GetIndex();
447 		aRg.aEnd 	= aRg.aStart.GetIndex() + 1;
448 
449 		// Nodes verschieben
450         MoveNodeRange( aRg, aStart,
451             IDocumentContentOperations::DOC_MOVEDEFAULT );
452 
453 		// Undo Verschiebungen einpflegen
454 		if(pUndoSort)
455 			pUndoSort->Insert(pBox->nOrg, nBeg + n);
456 	}
457 	// Alle Elemente aus dem SortArray loeschen
458 	aSortArr.DeleteAndDestroy(0, aSortArr.Count());
459 	SwSortElement::Finit();
460 
461 	if( pRedlPam )
462 	{
463 		if( pRedlUndo )
464 		{
465 			pRedlUndo->SetSaveRange( *pRedlPam );
466             // UGLY: temp. enable Undo
467             GetIDocumentUndoRedo().DoUndo(true);
468             GetIDocumentUndoRedo().AppendUndo( pRedlUndo );
469             GetIDocumentUndoRedo().DoUndo(false);
470         }
471 
472         // nBeg is start of sorted range
473 		SwNodeIndex aSttIdx( GetNodes(), nBeg );
474 
475         // the copied range is deleted
476         SwRedline *const pDeleteRedline(
477             new SwRedline( nsRedlineType_t::REDLINE_DELETE, *pRedlPam ));
478 
479         // pRedlPam points to nodes that may be deleted (hidden) by
480         // AppendRedline, so adjust it beforehand to prevent ASSERT
481 		pRedlPam->GetPoint()->nNode = aSttIdx;
482 		SwCntntNode* pCNd = aSttIdx.GetNode().GetCntntNode();
483 		pRedlPam->GetPoint()->nContent.Assign( pCNd, 0 );
484 
485         AppendRedline(pDeleteRedline, true);
486 
487         // the sorted range is inserted
488 		AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_INSERT, *pRedlPam ), true);
489 
490 		if( pRedlUndo )
491 		{
492 			SwNodeIndex aInsEndIdx( pRedlPam->GetMark()->nNode, -1 );
493 			pRedlPam->GetMark()->nNode = aInsEndIdx;
494             SwCntntNode *const pPrevNode =
495                 pRedlPam->GetMark()->nNode.GetNode().GetCntntNode();
496             pRedlPam->GetMark()->nContent.Assign( pPrevNode, pPrevNode->Len() );
497 
498 			pRedlUndo->SetValues( *pRedlPam );
499 		}
500 
501 		if( pRedlUndo )
502 			pRedlUndo->SetOffset( aSttIdx );
503 
504 		delete pRedlPam, pRedlPam = 0;
505     }
506     GetIDocumentUndoRedo().DoUndo( bUndo );
507 	if( bUndo )
508     {
509         GetIDocumentUndoRedo().EndUndo( UNDO_END, NULL );
510     }
511 
512 	return sal_True;
513 }
514 
515 /*--------------------------------------------------------------------
516 	Beschreibung: Tabelle sortieren im Document
517  --------------------------------------------------------------------*/
518 
SortTbl(const SwSelBoxes & rBoxes,const SwSortOptions & rOpt)519 sal_Bool SwDoc::SortTbl(const SwSelBoxes& rBoxes, const SwSortOptions& rOpt)
520 {
521 	// uebers SwDoc fuer Undo !!
522 	ASSERT( rBoxes.Count(), "keine gueltige Box-Liste" );
523 	SwTableNode* pTblNd = (SwTableNode*)rBoxes[0]->GetSttNd()->FindTableNode();
524 	if( !pTblNd )
525 		return sal_False;
526 
527 	// Auf gehts sortieren
528 	// suche alle Boxen / Lines
529 	_FndBox aFndBox( 0, 0 );
530 	{
531 		_FndPara aPara( rBoxes, &aFndBox );
532 		pTblNd->GetTable().GetTabLines().ForEach( &_FndLineCopyCol, &aPara );;
533 	}
534 
535 	if(!aFndBox.GetLines().Count())
536 		return sal_False;
537 
538 	if( !IsIgnoreRedline() && GetRedlineTbl().Count() )
539 		DeleteRedline( *pTblNd, true, USHRT_MAX );
540 
541 	sal_uInt16 nStart = 0;
542     if( pTblNd->GetTable().GetRowsToRepeat() > 0 && rOpt.eDirection == SRT_ROWS )
543 	{
544         // Oberste seleketierte Zeile
545 		_FndLines& rLines = aFndBox.GetLines();
546 
547         while( nStart < rLines.Count() )
548 		{
549 			// Verschachtelung durch Split Merge beachten,
550 			// die oberste rausholen
551 			SwTableLine* pLine = rLines[nStart]->GetLine();
552 			while ( pLine->GetUpper() )
553 				pLine = pLine->GetUpper()->GetUpper();
554 
555             if( pTblNd->GetTable().IsHeadline( *pLine ) )
556 				nStart++;
557 			else
558 				break;
559 		}
560 		// Alle selektierten in der HeaderLine ?  -> kein Offset
561         if( nStart == rLines.Count() )
562 			nStart = 0;
563 	}
564 
565 	// umschalten auf relative Formeln
566 	SwTableFmlUpdate aMsgHnt( &pTblNd->GetTable() );
567 	aMsgHnt.eFlags = TBL_RELBOXNAME;
568 	UpdateTblFlds( &aMsgHnt );
569 
570 	// Tabelle als flache Array-Struktur
571 	FlatFndBox aFlatBox(this, aFndBox);
572 
573 	if(!aFlatBox.IsSymmetric())
574 		return sal_False;
575 
576 	// MIB 9.7.97: HTML-Layout loeschen
577 	pTblNd->GetTable().SetHTMLTableLayout( 0 );
578 
579     // --> FME 2004-11-26 #i37739# A simple 'MakeFrms' after the node sorting
580     // does not work if the table is inside a frame and has no prev/next.
581     SwNode2Layout aNode2Layout( *pTblNd );
582     // <--
583 
584     // loesche die Frames der Tabelle
585 	pTblNd->DelFrms();
586     // ? TL_CHART2: ?
587 
588 	SwUndoSort* pUndoSort = 0;
589     if (GetIDocumentUndoRedo().DoesUndo())
590     {
591 		pUndoSort =	new SwUndoSort( rBoxes[0]->GetSttIdx(),
592 									rBoxes[rBoxes.Count()-1]->GetSttIdx(),
593 								   *pTblNd, rOpt, aFlatBox.HasItemSets() );
594         GetIDocumentUndoRedo().AppendUndo(pUndoSort);
595     }
596     ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
597 
598 	// SchluesselElemente einsortieren
599 	sal_uInt16 nCount = (rOpt.eDirection == SRT_ROWS) ?
600 					aFlatBox.GetRows() : aFlatBox.GetCols();
601 
602 	// SortList nach Schluessel sortieren
603 	SwSortElement::Init( this, rOpt, &aFlatBox );
604 	SwSortElements aSortList;
605 
606 	// wenn die HeaderLine wiederholt wird und die
607 	// Zeilen sortiert werden 1.Zeile nicht mitsortieren
608 	sal_uInt16 i;
609 
610 	for( i = nStart; i < nCount; ++i)
611 	{
612 		SwSortBoxElement* pEle = new SwSortBoxElement( i );
613 		aSortList.Insert(pEle);
614 	}
615 
616     // nach Sortierung verschieben
617 	SwMovedBoxes aMovedList;
618 	for(i=0; i < aSortList.Count(); ++i)
619 	{
620 		SwSortBoxElement* pBox = (SwSortBoxElement*)aSortList[i];
621 		if(rOpt.eDirection == SRT_ROWS)
622 			MoveRow(this, aFlatBox, pBox->nRow, i + nStart, aMovedList, pUndoSort);
623 		else
624 			MoveCol(this, aFlatBox, pBox->nRow, i + nStart, aMovedList, pUndoSort);
625 	}
626 
627     // Restore table frames:
628     // --> FME 2004-11-26 #i37739# A simple 'MakeFrms' after the node sorting
629     // does not work if the table is inside a frame and has no prev/next.
630     const sal_uLong nIdx = pTblNd->GetIndex();
631     aNode2Layout.RestoreUpperFrms( GetNodes(), nIdx, nIdx + 1 );
632     // <--
633 
634     // TL_CHART2: need to inform chart of probably changed cell names
635     UpdateCharts( pTblNd->GetTable().GetFrmFmt()->GetName() );
636 
637 	// Alle Elemente aus dem SortArray loeschen
638 	aSortList.DeleteAndDestroy( 0, aSortList.Count() );
639 	SwSortElement::Finit();
640 
641 	SetModified();
642 	return sal_True;
643 }
644 
645 /*--------------------------------------------------------------------
646 	Beschreibung: Zeilenweise verschieben
647  --------------------------------------------------------------------*/
648 
649 
MoveRow(SwDoc * pDoc,const FlatFndBox & rBox,sal_uInt16 nS,sal_uInt16 nT,SwMovedBoxes & rMovedList,SwUndoSort * pUD)650 void MoveRow(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
651 			 SwMovedBoxes& rMovedList, SwUndoSort* pUD)
652 {
653 	for( sal_uInt16 i=0; i < rBox.GetCols(); ++i )
654 	{	// Alte Zellen-Pos bestimmen und merken
655 		const _FndBox* pSource = rBox.GetBox(i, nS);
656 
657 		// neue Zellen-Pos
658 		const _FndBox* pTarget = rBox.GetBox(i, nT);
659 
660 		const SwTableBox* pT = pTarget->GetBox();
661 		const SwTableBox* pS = pSource->GetBox();
662 
663 		sal_Bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
664 
665 		// und verschieben
666 		MoveCell(pDoc, pS, pT, bMoved, pUD);
667 
668 		rMovedList.Insert(pS, rMovedList.Count() );
669 
670 		if( pS != pT )
671 		{
672 			SwFrmFmt* pTFmt = (SwFrmFmt*)pT->GetFrmFmt();
673 			const SfxItemSet* pSSet = rBox.GetItemSet( i, nS );
674 
675 			if( pSSet ||
676 				SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMAT ) ||
677 				SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMULA ) ||
678 				SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_VALUE ) )
679 			{
680 				pTFmt = ((SwTableBox*)pT)->ClaimFrmFmt();
681 				pTFmt->LockModify();
682                 if( pTFmt->ResetFmtAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
683                     pTFmt->ResetFmtAttr( RES_VERT_ORIENT );
684 
685 				if( pSSet )
686                     pTFmt->SetFmtAttr( *pSSet );
687 				pTFmt->UnlockModify();
688 			}
689 		}
690 	}
691 }
692 
693 /*--------------------------------------------------------------------
694 	Beschreibung: Spaltenweise verschieben
695  --------------------------------------------------------------------*/
696 
697 
MoveCol(SwDoc * pDoc,const FlatFndBox & rBox,sal_uInt16 nS,sal_uInt16 nT,SwMovedBoxes & rMovedList,SwUndoSort * pUD)698 void MoveCol(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT,
699 			 SwMovedBoxes& rMovedList, SwUndoSort* pUD)
700 {
701 	for(sal_uInt16 i=0; i < rBox.GetRows(); ++i)
702 	{	// Alte Zellen-Pos bestimmen und merken
703 		const _FndBox* pSource = rBox.GetBox(nS, i);
704 
705 		// neue Zellen-Pos
706 		const _FndBox* pTarget = rBox.GetBox(nT, i);
707 
708 		// und verschieben
709 		const SwTableBox* pT = pTarget->GetBox();
710 		const SwTableBox* pS = pSource->GetBox();
711 
712 		// und verschieben
713 		sal_Bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX;
714 		MoveCell(pDoc, pS, pT, bMoved, pUD);
715 
716 		rMovedList.Insert(pS, rMovedList.Count() );
717 
718 		if( pS != pT )
719 		{
720 			SwFrmFmt* pTFmt = (SwFrmFmt*)pT->GetFrmFmt();
721 			const SfxItemSet* pSSet = rBox.GetItemSet( nS, i );
722 
723 			if( pSSet ||
724 				SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMAT ) ||
725 				SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMULA ) ||
726 				SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_VALUE ) )
727 			{
728 				pTFmt = ((SwTableBox*)pT)->ClaimFrmFmt();
729 				pTFmt->LockModify();
730                 if( pTFmt->ResetFmtAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) )
731                     pTFmt->ResetFmtAttr( RES_VERT_ORIENT );
732 
733 				if( pSSet )
734                     pTFmt->SetFmtAttr( *pSSet );
735 				pTFmt->UnlockModify();
736 			}
737 		}
738 	}
739 }
740 
741 /*--------------------------------------------------------------------
742 	Beschreibung: Eine einzelne Zelle verschieben
743  --------------------------------------------------------------------*/
744 
745 
MoveCell(SwDoc * pDoc,const SwTableBox * pSource,const SwTableBox * pTar,sal_Bool bMovedBefore,SwUndoSort * pUD)746 void MoveCell(SwDoc* pDoc, const SwTableBox* pSource, const SwTableBox* pTar,
747 			  sal_Bool bMovedBefore, SwUndoSort* pUD)
748 {
749 	ASSERT(pSource && pTar,"Fehlende Quelle oder Ziel");
750 
751 	if(pSource == pTar)
752 		return;
753 
754 	if(pUD)
755 		pUD->Insert( pSource->GetName(), pTar->GetName() );
756 
757 	// Pam Quelle auf den ersten ContentNode setzen
758 	SwNodeRange aRg( *pSource->GetSttNd(), 0, *pSource->GetSttNd() );
759 	SwNode* pNd = pDoc->GetNodes().GoNext( &aRg.aStart );
760 
761 	// wurde die Zelle (Source) nicht verschoben
762 	// -> einen Leer-Node einfuegen und den Rest verschieben
763 	// ansonsten steht der Mark auf dem ersten Content-Node
764 	if( pNd->StartOfSectionNode() == pSource->GetSttNd() )
765 		pNd = pDoc->GetNodes().MakeTxtNode( aRg.aStart,
766 				(SwTxtFmtColl*)pDoc->GetDfltTxtFmtColl() );
767 	aRg.aEnd = *pNd->EndOfSectionNode();
768 
769 	// Ist das Ziel leer(1 leerer Node vorhanden)
770 	// -> diesen loeschen und move
771 	// Ziel
772 	SwNodeIndex	aTar( *pTar->GetSttNd() );
773 	pNd = pDoc->GetNodes().GoNext( &aTar );     // naechsten ContentNode
774 	sal_uLong nCount = pNd->EndOfSectionIndex() - pNd->StartOfSectionIndex();
775 
776 	sal_Bool bDelFirst = sal_False;
777 	if( nCount == 2 )
778 	{
779 		ASSERT( pNd->GetCntntNode(), "Kein ContentNode");
780 		bDelFirst = !pNd->GetCntntNode()->Len() && bMovedBefore;
781 	}
782 
783 	if(!bDelFirst)
784 	{	// Es besteht schon Inhalt -> alter I n h a l t  Section Down
785 		SwNodeRange aRgTar( aTar.GetNode(), 0, *pNd->EndOfSectionNode() );
786 		pDoc->GetNodes().SectionDown( &aRgTar );
787 	}
788 
789 	// Einfuegen der Source
790 	SwNodeIndex aIns( *pTar->GetSttNd()->EndOfSectionNode() );
791     pDoc->MoveNodeRange( aRg, aIns,
792         IDocumentContentOperations::DOC_MOVEDEFAULT );
793 
794 	// Falls erster Node leer -> weg damit
795 	if(bDelFirst)
796 		pDoc->GetNodes().Delete( aTar, 1 );
797 }
798 
799 /*--------------------------------------------------------------------
800 	Beschreibung: Zweidimensionales Array aus FndBoxes generieren
801  --------------------------------------------------------------------*/
802 
803 
FlatFndBox(SwDoc * pDocPtr,const _FndBox & rBox)804 FlatFndBox::FlatFndBox(SwDoc* pDocPtr, const _FndBox& rBox) :
805 	pDoc(pDocPtr),
806 	rBoxRef(rBox),
807 	pArr(0),
808 	ppItemSets(0),
809 	nRow(0),
810 	nCol(0)
811 { // Ist das Array symmetrisch
812 	if((bSym = CheckLineSymmetry(rBoxRef)) != 0)
813 	{
814 		// Spalten/Reihen-Anzahl ermitteln
815 		nCols = GetColCount(rBoxRef);
816 		nRows = GetRowCount(rBoxRef);
817 
818 		// lineares Array anlegen
819 		pArr = new _FndBoxPtr[ nRows * nCols ];
820 		_FndBox** ppTmp = (_FndBox**)pArr;
821 		memset( ppTmp, 0, sizeof(_FndBoxPtr) * nRows * nCols );
822 
823 
824 		FillFlat( rBoxRef );
825 	}
826 }
827 
828 
~FlatFndBox()829 FlatFndBox::~FlatFndBox()
830 {
831 	_FndBox** ppTmp = (_FndBox**)pArr;
832 	delete [] ppTmp;
833 
834 	if( ppItemSets )
835 		delete [] ppItemSets;
836 }
837 
838 /*--------------------------------------------------------------------
839 	Beschreibung:	Alle Lines einer Box muessen gleichviel Boxen haben
840  --------------------------------------------------------------------*/
841 
842 
CheckLineSymmetry(const _FndBox & rBox)843 sal_Bool FlatFndBox::CheckLineSymmetry(const _FndBox& rBox)
844 {
845 	const _FndLines &rLines = rBox.GetLines();
846 	sal_uInt16 nBoxes(0);
847 
848 	// UeberLines iterieren
849 	for(sal_uInt16 i=0; i < rLines.Count(); ++i)
850 	{	// Die Boxen einer Line
851 		_FndLine* pLn = rLines[i];
852 		const _FndBoxes& rBoxes = pLn->GetBoxes();
853 
854 		// Anzahl der Boxen aller Lines ungleich -> keine Symmetrie
855 		if( i  && nBoxes != rBoxes.Count())
856 			return sal_False;
857 
858 		nBoxes = rBoxes.Count();
859 		if( !CheckBoxSymmetry( *pLn ) )
860 			return sal_False;
861 	}
862 	return sal_True;
863 }
864 
865 /*--------------------------------------------------------------------
866 	Beschreibung:	Box auf Symmetrie pruefen
867 					Alle Boxen einer Line muessen gleichviele Lines haben
868  --------------------------------------------------------------------*/
869 
870 
CheckBoxSymmetry(const _FndLine & rLn)871 sal_Bool FlatFndBox::CheckBoxSymmetry(const _FndLine& rLn)
872 {
873 	const _FndBoxes &rBoxes = rLn.GetBoxes();
874 	sal_uInt16 nLines(0);
875 
876 	// Ueber Boxes iterieren
877 	for(sal_uInt16 i=0; i < rBoxes.Count(); ++i)
878 	{	// Die Boxen einer Line
879 		_FndBox* pBox = rBoxes[i];
880 		const _FndLines& rLines = pBox->GetLines();
881 
882 		// Anzahl der Boxen aller Lines ungleich -> keine Symmetrie
883 		if( i && nLines != rLines.Count() )
884 			return sal_False;
885 
886 		nLines = rLines.Count();
887 		if( nLines && !CheckLineSymmetry( *pBox ) )
888 			return sal_False;
889 	}
890 	return sal_True;
891 }
892 
893 /*--------------------------------------------------------------------
894 	Beschreibung: max Anzahl der Spalten (Boxes)
895  --------------------------------------------------------------------*/
896 
897 
GetColCount(const _FndBox & rBox)898 sal_uInt16 FlatFndBox::GetColCount(const _FndBox& rBox)
899 {
900 	const _FndLines& rLines = rBox.GetLines();
901 	// Ueber Lines iterieren
902 	if( !rLines.Count() )
903 		return 1;
904 
905 	sal_uInt16 nSum = 0;
906 	for( sal_uInt16 i=0; i < rLines.Count(); ++i )
907 	{
908 		// Die Boxen einer Line
909 		sal_uInt16 nCount = 0;
910 		const _FndBoxes& rBoxes = rLines[i]->GetBoxes();
911 		for( sal_uInt16 j=0; j < rBoxes.Count(); ++j )
912 				//	Rekursiv wirder ueber die Lines Iterieren
913 			nCount += rBoxes[j]->GetLines().Count()
914 						? GetColCount(*rBoxes[j]) : 1;
915 
916 		if( nSum < nCount )
917 			nSum = nCount;
918 	}
919 	return nSum;
920 }
921 
922 /*--------------------------------------------------------------------
923 	Beschreibung: max Anzahl der Zeilen (Lines)
924  --------------------------------------------------------------------*/
925 
926 
GetRowCount(const _FndBox & rBox)927 sal_uInt16 FlatFndBox::GetRowCount(const _FndBox& rBox)
928 {
929 	const _FndLines& rLines = rBox.GetLines();
930 	if( !rLines.Count() )
931 		return 1;
932 
933 	sal_uInt16 nLines = 0;
934 	for(sal_uInt16 i=0; i < rLines.Count(); ++i)
935 	{	// Die Boxen einer Line
936 		const _FndBoxes& rBoxes = rLines[i]->GetBoxes();
937 		sal_uInt16 nLn = 1;
938 		for(sal_uInt16 j=0; j < rBoxes.Count(); ++j)
939 			if( rBoxes[j]->GetLines().Count() )
940 				//	Rekursiv ueber die Lines Iterieren
941 				nLn = Max(GetRowCount(*rBoxes[j]), nLn);
942 
943 		nLines = nLines + nLn;
944 	}
945 	return nLines;
946 }
947 
948 /*--------------------------------------------------------------------
949 	Beschreibung: lineares Array aus atomaren FndBoxes erzeugen
950  --------------------------------------------------------------------*/
951 
952 
FillFlat(const _FndBox & rBox,sal_Bool bLastBox)953 void FlatFndBox::FillFlat(const _FndBox& rBox, sal_Bool bLastBox)
954 {
955 	sal_Bool bModRow = sal_False;
956 	const _FndLines& rLines = rBox.GetLines();
957 
958 	// Ueber Lines iterieren
959 	sal_uInt16 nOldRow = nRow;
960 	for( sal_uInt16 i=0; i < rLines.Count(); ++i )
961 	{
962 		// Die Boxen einer Line
963 		const _FndBoxes& rBoxes = rLines[i]->GetBoxes();
964 		sal_uInt16 nOldCol = nCol;
965 		for( sal_uInt16 j = 0; j < rBoxes.Count(); ++j )
966 		{
967 			// Die Box pruefen ob es eine atomare Box ist
968 			const _FndBox* 	 pBox 	= rBoxes[ j ];
969 
970 			if( !pBox->GetLines().Count() )
971 			{
972 				// peichern
973 				sal_uInt16 nOff = nRow * nCols + nCol;
974 				*(pArr + nOff) = pBox;
975 
976 				// sicher die Formel/Format/Value Werte
977 				const SwFrmFmt* pFmt = pBox->GetBox()->GetFrmFmt();
978 				if( SFX_ITEM_SET == pFmt->GetItemState( RES_BOXATR_FORMAT ) ||
979 					SFX_ITEM_SET == pFmt->GetItemState( RES_BOXATR_FORMULA ) ||
980 					SFX_ITEM_SET == pFmt->GetItemState( RES_BOXATR_VALUE ) )
981 				{
982 					SfxItemSet* pSet = new SfxItemSet( pDoc->GetAttrPool(),
983 									RES_BOXATR_FORMAT, RES_BOXATR_VALUE,
984 									RES_VERT_ORIENT, RES_VERT_ORIENT, 0 );
985 					pSet->Put( pFmt->GetAttrSet() );
986 					if( !ppItemSets )
987 					{
988 						ppItemSets = new SfxItemSet*[ nRows * nCols ];
989 						memset( ppItemSets, 0, sizeof(SfxItemSet*) * nRows * nCols );
990 					}
991 					*(ppItemSets + nOff ) = pSet;
992 				}
993 
994 				bModRow = sal_True;
995 			}
996 			else
997 			{
998 				// Rekursiv wieder ueber die Lines einer Box Iterieren
999 				FillFlat( *pBox, ( j == rBoxes.Count()-1 ) );
1000 			}
1001 			nCol++;
1002 		}
1003 		if(bModRow)
1004 			nRow++;
1005 		nCol = nOldCol;
1006 	}
1007 	if(!bLastBox)
1008 		nRow = nOldRow;
1009 }
1010 
1011 /*--------------------------------------------------------------------
1012 	Beschreibung: Zugriff auf eine bestimmte Zelle
1013  --------------------------------------------------------------------*/
1014 
1015 
GetBox(sal_uInt16 n_Col,sal_uInt16 n_Row) const1016 const _FndBox* FlatFndBox::GetBox(sal_uInt16 n_Col, sal_uInt16 n_Row) const
1017 {
1018 	sal_uInt16 nOff = n_Row * nCols + n_Col;
1019 	const _FndBox* pTmp = *(pArr + nOff);
1020 
1021 	ASSERT(n_Col < nCols && n_Row < nRows && pTmp, "unzulaessiger Array-Zugriff");
1022 	return pTmp;
1023 }
1024 
GetItemSet(sal_uInt16 n_Col,sal_uInt16 n_Row) const1025 const SfxItemSet* FlatFndBox::GetItemSet(sal_uInt16 n_Col, sal_uInt16 n_Row) const
1026 {
1027 	ASSERT( !ppItemSets || ( n_Col < nCols && n_Row < nRows), "unzulaessiger Array-Zugriff");
1028 
1029 	return ppItemSets ? *(ppItemSets + (n_Row * nCols + n_Col )) : 0;
1030 }
1031 
1032 
1033