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