| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sw.hxx" |
| |
| #include <hintids.hxx> |
| #include <rtl/math.hxx> |
| #include <unotools/collatorwrapper.hxx> |
| #include <unotools/localedatawrapper.hxx> |
| #include <com/sun/star/lang/XMultiServiceFactory.hpp> |
| #include <com/sun/star/i18n/CollatorOptions.hpp> |
| #include <comphelper/processfactory.hxx> |
| #include <editeng/unolingu.hxx> |
| #include <docary.hxx> |
| #include <fmtanchr.hxx> |
| #include <frmfmt.hxx> |
| #include <doc.hxx> |
| #include <IDocumentUndoRedo.hxx> |
| #include <node.hxx> |
| #include <pam.hxx> |
| #include <ndtxt.hxx> |
| #include <swtable.hxx> |
| #include <swundo.hxx> |
| #include <sortopt.hxx> |
| #include <docsort.hxx> |
| #include <UndoSort.hxx> |
| #include <UndoRedline.hxx> |
| #include <hints.hxx> |
| #include <tblsel.hxx> |
| #include <cellatr.hxx> |
| #include <redline.hxx> |
| #include <node2lay.hxx> |
| #include <unochart.hxx> |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| //nur zum debugen |
| #include <cellatr.hxx> |
| #endif |
| |
| using namespace ::com::sun::star::lang; |
| |
| SwSortOptions* SwSortElement::pOptions = 0; |
| SwDoc* SwSortElement::pDoc = 0; |
| const FlatFndBox* SwSortElement::pBox = 0; |
| CollatorWrapper* SwSortElement::pSortCollator = 0; |
| Locale* SwSortElement::pLocale = 0; |
| String* SwSortElement::pLastAlgorithm = 0; |
| LocaleDataWrapper* SwSortElement::pLclData = 0; |
| |
| SV_IMPL_OP_PTRARR_SORT( SwSortElements, SwSortElementPtr ); |
| |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Ein Sortierelement fuers Sort konstruieren |
| --------------------------------------------------------------------*/ |
| |
| |
| void SwSortElement::Init( SwDoc* pD, const SwSortOptions& rOpt, |
| FlatFndBox* pFltBx ) |
| { |
| ASSERT( !pDoc && !pOptions && !pBox, "wer hat das Finit vergessen?" ); |
| pDoc = pD; |
| pOptions = new SwSortOptions( rOpt ); |
| pBox = pFltBx; |
| |
| LanguageType nLang = rOpt.nLanguage; |
| switch ( nLang ) |
| { |
| case LANGUAGE_NONE: |
| case LANGUAGE_DONTKNOW: |
| nLang = (LanguageType)GetAppLanguage(); |
| break; |
| } |
| pLocale = new Locale( SvxCreateLocale( nLang ) ); |
| |
| pSortCollator = new CollatorWrapper( |
| ::comphelper::getProcessServiceFactory() ); |
| // pSortCollator->loadCollatorAlgorithm( sAlgorithm, aLocale, |
| // rOpt.bIgnoreCase ? SW_COLLATOR_IGNORES : 0 ); |
| } |
| |
| |
| void SwSortElement::Finit() |
| { |
| delete pOptions, pOptions = 0; |
| delete pLocale, pLocale = 0; |
| delete pLastAlgorithm, pLastAlgorithm = 0; |
| delete pSortCollator, pSortCollator = 0; |
| delete pLclData, pLclData = 0; |
| pDoc = 0; |
| pBox = 0; |
| } |
| |
| |
| SwSortElement::~SwSortElement() |
| { |
| } |
| |
| |
| double SwSortElement::StrToDouble( const String& rStr ) const |
| { |
| if( !pLclData ) |
| pLclData = new LocaleDataWrapper( |
| ::comphelper::getProcessServiceFactory(), *pLocale ); |
| |
| rtl_math_ConversionStatus eStatus; |
| sal_Int32 nEnd; |
| double nRet = ::rtl::math::stringToDouble( rStr, |
| pLclData->getNumDecimalSep().GetChar(0), |
| pLclData->getNumThousandSep().GetChar(0), |
| &eStatus, &nEnd ); |
| |
| if( rtl_math_ConversionStatus_Ok != eStatus || nEnd == 0 ) |
| nRet = 0.0; |
| return nRet; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Operatoren zum Vergleichen |
| --------------------------------------------------------------------*/ |
| |
| |
| sal_Bool SwSortElement::operator==(const SwSortElement& ) |
| { |
| return sal_False; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Kleiner-Operator fuers sortieren |
| --------------------------------------------------------------------*/ |
| |
| sal_Bool SwSortElement::operator<(const SwSortElement& rCmp) |
| { |
| |
| // der eigentliche Vergleich |
| // |
| for(sal_uInt16 nKey = 0; nKey < pOptions->aKeys.Count(); ++nKey) |
| { |
| const SwSortElement *pOrig, *pCmp; |
| |
| const SwSortKey* pSrtKey = pOptions->aKeys[ nKey ]; |
| if( pSrtKey->eSortOrder == SRT_ASCENDING ) |
| pOrig = this, pCmp = &rCmp; |
| else |
| pOrig = &rCmp, pCmp = this; |
| |
| if( pSrtKey->bIsNumeric ) |
| { |
| double n1 = pOrig->GetValue( nKey ); |
| double n2 = pCmp->GetValue( nKey ); |
| |
| if( n1 == n2 ) |
| continue; |
| |
| return n1 < n2; |
| } |
| else |
| { |
| if( !pLastAlgorithm || *pLastAlgorithm != pSrtKey->sSortType ) |
| { |
| if( pLastAlgorithm ) |
| *pLastAlgorithm = pSrtKey->sSortType; |
| else |
| pLastAlgorithm = new String( pSrtKey->sSortType ); |
| pSortCollator->loadCollatorAlgorithm( *pLastAlgorithm, |
| *pLocale, |
| pOptions->bIgnoreCase ? SW_COLLATOR_IGNORES : 0 ); |
| } |
| |
| sal_Int32 nCmp = pSortCollator->compareString( |
| pOrig->GetKey( nKey ), pCmp->GetKey( nKey )); |
| if( 0 == nCmp ) |
| continue; |
| |
| return -1 == nCmp; |
| } |
| } |
| return sal_False; |
| } |
| |
| double SwSortElement::GetValue( sal_uInt16 nKey ) const |
| { |
| return StrToDouble( GetKey( nKey )); |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: SortierElemente fuer Text |
| --------------------------------------------------------------------*/ |
| |
| |
| SwSortTxtElement::SwSortTxtElement(const SwNodeIndex& rPos) |
| : nOrg(rPos.GetIndex()), aPos(rPos) |
| { |
| } |
| |
| |
| SwSortTxtElement::~SwSortTxtElement() |
| { |
| } |
| |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Key ermitteln |
| --------------------------------------------------------------------*/ |
| |
| |
| String SwSortTxtElement::GetKey(sal_uInt16 nId) const |
| { |
| SwTxtNode* pTxtNd = aPos.GetNode().GetTxtNode(); |
| if( !pTxtNd ) |
| return aEmptyStr; |
| |
| // fuer TextNodes |
| const String& rStr = pTxtNd->GetTxt(); |
| |
| sal_Unicode nDeli = pOptions->cDeli; |
| sal_uInt16 nDCount = pOptions->aKeys[nId]->nColumnId, i = 1; |
| xub_StrLen nStart = 0; |
| |
| // Den Delimitter suchen |
| while( nStart != STRING_NOTFOUND && i < nDCount) |
| if( STRING_NOTFOUND != ( nStart = rStr.Search( nDeli, nStart ) ) ) |
| { |
| nStart++; |
| i++; |
| } |
| |
| // naechsten Delimitter gefunden oder Ende des Strings und Kopieren |
| xub_StrLen nEnd = rStr.Search( nDeli, nStart+1 ); |
| return rStr.Copy( nStart, nEnd-nStart ); |
| } |
| |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Sortier-Elemente fuer Tabellen |
| --------------------------------------------------------------------*/ |
| |
| SwSortBoxElement::SwSortBoxElement( sal_uInt16 nRC ) |
| : nRow( nRC ) |
| { |
| } |
| |
| |
| SwSortBoxElement::~SwSortBoxElement() |
| { |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Schluessel zu einer Zelle ermitteln |
| --------------------------------------------------------------------*/ |
| |
| |
| String SwSortBoxElement::GetKey(sal_uInt16 nKey) const |
| { |
| const _FndBox* pFndBox; |
| sal_uInt16 nCol = pOptions->aKeys[nKey]->nColumnId-1; |
| |
| if( SRT_ROWS == pOptions->eDirection ) |
| pFndBox = pBox->GetBox(nCol, nRow); // Zeilen sortieren |
| else |
| pFndBox = pBox->GetBox(nRow, nCol); // Spalten sortieren |
| |
| // Den Text rausfieseln |
| String aRetStr; |
| if( pFndBox ) |
| { // StartNode holen und ueberlesen |
| const SwTableBox* pMyBox = pFndBox->GetBox(); |
| ASSERT(pMyBox, "Keine atomare Box"); |
| |
| if( pMyBox->GetSttNd() ) |
| { |
| // ueber alle TextNodes der Box |
| const SwNode *pNd = 0, *pEndNd = pMyBox->GetSttNd()->EndOfSectionNode(); |
| for( sal_uLong nIdx = pMyBox->GetSttIdx() + 1; pNd != pEndNd; ++nIdx ) |
| if( ( pNd = pDoc->GetNodes()[ nIdx ])->IsTxtNode() ) |
| aRetStr += ((SwTxtNode*)pNd)->GetTxt(); |
| } |
| } |
| return aRetStr; |
| } |
| |
| double SwSortBoxElement::GetValue( sal_uInt16 nKey ) const |
| { |
| const _FndBox* pFndBox; |
| sal_uInt16 nCol = pOptions->aKeys[nKey]->nColumnId-1; |
| |
| if( SRT_ROWS == pOptions->eDirection ) |
| pFndBox = pBox->GetBox(nCol, nRow); // Zeilen sortieren |
| else |
| pFndBox = pBox->GetBox(nRow, nCol); // Spalten sortieren |
| |
| double nVal; |
| if( pFndBox ) |
| { |
| const SwFmt *pFmt = pFndBox->GetBox()->GetFrmFmt(); |
| if (pFmt->GetTblBoxNumFmt().GetValue() & NUMBERFORMAT_TEXT) |
| nVal = SwSortElement::GetValue( nKey ); |
| else |
| nVal = pFmt->GetTblBoxValue().GetValue(); |
| } |
| else |
| nVal = 0; |
| |
| return nVal; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Text sortieren im Document |
| --------------------------------------------------------------------*/ |
| |
| |
| sal_Bool SwDoc::SortText(const SwPaM& rPaM, const SwSortOptions& rOpt) |
| { |
| // pruefen ob Rahmen im Text |
| const SwPosition *pStart = rPaM.Start(), *pEnd = rPaM.End(); |
| // Index auf den Start der Selektion |
| |
| for ( sal_uInt16 n = 0; n < GetSpzFrmFmts()->Count(); ++n ) |
| { |
| SwFrmFmt *const pFmt = static_cast<SwFrmFmt*>((*GetSpzFrmFmts())[n]); |
| SwFmtAnchor const*const pAnchor = &pFmt->GetAnchor(); |
| SwPosition const*const pAPos = pAnchor->GetCntntAnchor(); |
| |
| if (pAPos && (FLY_AT_PARA == pAnchor->GetAnchorId()) && |
| pStart->nNode <= pAPos->nNode && pAPos->nNode <= pEnd->nNode ) |
| return sal_False; |
| } |
| |
| // pruefe ob nur TextNodes in der Selection liegen |
| { |
| sal_uLong nStart = pStart->nNode.GetIndex(), |
| nEnd = pEnd->nNode.GetIndex(); |
| while( nStart <= nEnd ) |
| // Iterieren ueber einen selektierten Bereich |
| if( !GetNodes()[ nStart++ ]->IsTxtNode() ) |
| return sal_False; |
| } |
| |
| bool const bUndo = GetIDocumentUndoRedo().DoesUndo(); |
| if( bUndo ) |
| { |
| GetIDocumentUndoRedo().StartUndo( UNDO_START, NULL ); |
| } |
| |
| SwPaM* pRedlPam = 0; |
| SwUndoRedlineSort* pRedlUndo = 0; |
| SwUndoSort* pUndoSort = 0; |
| |
| if( IsRedlineOn() || (!IsIgnoreRedline() && pRedlineTbl->Count() )) |
| { |
| pRedlPam = new SwPaM( pStart->nNode, pEnd->nNode, -1, 1 ); |
| SwCntntNode* pCNd = pRedlPam->GetCntntNode( sal_False ); |
| if( pCNd ) |
| pRedlPam->GetMark()->nContent = pCNd->Len(); |
| |
| if( IsRedlineOn() && !IsShowOriginal( GetRedlineMode() ) ) |
| { |
| if( bUndo ) |
| { |
| pRedlUndo = new SwUndoRedlineSort( *pRedlPam,rOpt ); |
| GetIDocumentUndoRedo().DoUndo(false); |
| } |
| // erst den Bereich kopieren, dann |
| SwNodeIndex aEndIdx( pEnd->nNode, 1 ); |
| SwNodeRange aRg( pStart->nNode, aEndIdx ); |
| GetNodes()._Copy( aRg, aEndIdx ); |
| |
| // Bereich neu ist von pEnd->nNode+1 bis aEndIdx |
| DeleteRedline( *pRedlPam, true, USHRT_MAX ); |
| |
| pRedlPam->GetMark()->nNode.Assign( pEnd->nNode.GetNode(), 1 ); |
| pCNd = pRedlPam->GetCntntNode( sal_False ); |
| pRedlPam->GetMark()->nContent.Assign( pCNd, 0 ); |
| |
| pRedlPam->GetPoint()->nNode.Assign( aEndIdx.GetNode() ); |
| pCNd = pRedlPam->GetCntntNode( sal_True ); |
| xub_StrLen nCLen = 0; |
| if( !pCNd && |
| 0 != (pCNd = GetNodes()[ aEndIdx.GetIndex()-1 ]->GetCntntNode())) |
| { |
| nCLen = pCNd->Len(); |
| pRedlPam->GetPoint()->nNode.Assign( *pCNd ); |
| } |
| pRedlPam->GetPoint()->nContent.Assign( pCNd, nCLen ); |
| |
| if( pRedlUndo ) |
| pRedlUndo->SetValues( rPaM ); |
| } |
| else |
| { |
| DeleteRedline( *pRedlPam, true, USHRT_MAX ); |
| delete pRedlPam, pRedlPam = 0; |
| } |
| } |
| |
| SwNodeIndex aStart(pStart->nNode); |
| SwSortElement::Init( this, rOpt ); |
| SwSortElements aSortArr; |
| while( aStart <= pEnd->nNode ) |
| { |
| // Iterieren ueber einen selektierten Bereich |
| SwSortTxtElement* pSE = new SwSortTxtElement( aStart ); |
| aSortArr.Insert(pSE); |
| aStart++; |
| } |
| |
| // Und jetzt der Akt: Verschieben von Nodes und immer schoen auf UNDO |
| // achten |
| // |
| sal_uLong nBeg = pStart->nNode.GetIndex(); |
| SwNodeRange aRg( aStart, aStart ); |
| |
| if( bUndo && !pRedlUndo ) |
| { |
| pUndoSort = new SwUndoSort(rPaM, rOpt); |
| GetIDocumentUndoRedo().AppendUndo(pUndoSort); |
| } |
| |
| GetIDocumentUndoRedo().DoUndo(false); |
| |
| for ( sal_uInt16 n = 0; n < aSortArr.Count(); ++n ) |
| { |
| SwSortTxtElement* pBox = (SwSortTxtElement*)aSortArr[n]; |
| aStart = nBeg + n; |
| aRg.aStart = pBox->aPos.GetIndex(); |
| aRg.aEnd = aRg.aStart.GetIndex() + 1; |
| |
| // Nodes verschieben |
| MoveNodeRange( aRg, aStart, |
| IDocumentContentOperations::DOC_MOVEDEFAULT ); |
| |
| // Undo Verschiebungen einpflegen |
| if(pUndoSort) |
| pUndoSort->Insert(pBox->nOrg, nBeg + n); |
| } |
| // Alle Elemente aus dem SortArray loeschen |
| aSortArr.DeleteAndDestroy(0, aSortArr.Count()); |
| SwSortElement::Finit(); |
| |
| if( pRedlPam ) |
| { |
| if( pRedlUndo ) |
| { |
| pRedlUndo->SetSaveRange( *pRedlPam ); |
| // UGLY: temp. enable Undo |
| GetIDocumentUndoRedo().DoUndo(true); |
| GetIDocumentUndoRedo().AppendUndo( pRedlUndo ); |
| GetIDocumentUndoRedo().DoUndo(false); |
| } |
| |
| // nBeg is start of sorted range |
| SwNodeIndex aSttIdx( GetNodes(), nBeg ); |
| |
| // the copied range is deleted |
| SwRedline *const pDeleteRedline( |
| new SwRedline( nsRedlineType_t::REDLINE_DELETE, *pRedlPam )); |
| |
| // pRedlPam points to nodes that may be deleted (hidden) by |
| // AppendRedline, so adjust it beforehand to prevent ASSERT |
| pRedlPam->GetPoint()->nNode = aSttIdx; |
| SwCntntNode* pCNd = aSttIdx.GetNode().GetCntntNode(); |
| pRedlPam->GetPoint()->nContent.Assign( pCNd, 0 ); |
| |
| AppendRedline(pDeleteRedline, true); |
| |
| // the sorted range is inserted |
| AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_INSERT, *pRedlPam ), true); |
| |
| if( pRedlUndo ) |
| { |
| SwNodeIndex aInsEndIdx( pRedlPam->GetMark()->nNode, -1 ); |
| pRedlPam->GetMark()->nNode = aInsEndIdx; |
| SwCntntNode *const pPrevNode = |
| pRedlPam->GetMark()->nNode.GetNode().GetCntntNode(); |
| pRedlPam->GetMark()->nContent.Assign( pPrevNode, pPrevNode->Len() ); |
| |
| pRedlUndo->SetValues( *pRedlPam ); |
| } |
| |
| if( pRedlUndo ) |
| pRedlUndo->SetOffset( aSttIdx ); |
| |
| delete pRedlPam, pRedlPam = 0; |
| } |
| GetIDocumentUndoRedo().DoUndo( bUndo ); |
| if( bUndo ) |
| { |
| GetIDocumentUndoRedo().EndUndo( UNDO_END, NULL ); |
| } |
| |
| return sal_True; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Tabelle sortieren im Document |
| --------------------------------------------------------------------*/ |
| |
| sal_Bool SwDoc::SortTbl(const SwSelBoxes& rBoxes, const SwSortOptions& rOpt) |
| { |
| // uebers SwDoc fuer Undo !! |
| ASSERT( rBoxes.Count(), "keine gueltige Box-Liste" ); |
| SwTableNode* pTblNd = (SwTableNode*)rBoxes[0]->GetSttNd()->FindTableNode(); |
| if( !pTblNd ) |
| return sal_False; |
| |
| // Auf gehts sortieren |
| // suche alle Boxen / Lines |
| _FndBox aFndBox( 0, 0 ); |
| { |
| _FndPara aPara( rBoxes, &aFndBox ); |
| pTblNd->GetTable().GetTabLines().ForEach( &_FndLineCopyCol, &aPara );; |
| } |
| |
| if(!aFndBox.GetLines().Count()) |
| return sal_False; |
| |
| if( !IsIgnoreRedline() && GetRedlineTbl().Count() ) |
| DeleteRedline( *pTblNd, true, USHRT_MAX ); |
| |
| sal_uInt16 nStart = 0; |
| if( pTblNd->GetTable().GetRowsToRepeat() > 0 && rOpt.eDirection == SRT_ROWS ) |
| { |
| // Oberste seleketierte Zeile |
| _FndLines& rLines = aFndBox.GetLines(); |
| |
| while( nStart < rLines.Count() ) |
| { |
| // Verschachtelung durch Split Merge beachten, |
| // die oberste rausholen |
| SwTableLine* pLine = rLines[nStart]->GetLine(); |
| while ( pLine->GetUpper() ) |
| pLine = pLine->GetUpper()->GetUpper(); |
| |
| if( pTblNd->GetTable().IsHeadline( *pLine ) ) |
| nStart++; |
| else |
| break; |
| } |
| // Alle selektierten in der HeaderLine ? -> kein Offset |
| if( nStart == rLines.Count() ) |
| nStart = 0; |
| } |
| |
| // umschalten auf relative Formeln |
| SwTableFmlUpdate aMsgHnt( &pTblNd->GetTable() ); |
| aMsgHnt.eFlags = TBL_RELBOXNAME; |
| UpdateTblFlds( &aMsgHnt ); |
| |
| // Tabelle als flache Array-Struktur |
| FlatFndBox aFlatBox(this, aFndBox); |
| |
| if(!aFlatBox.IsSymmetric()) |
| return sal_False; |
| |
| // MIB 9.7.97: HTML-Layout loeschen |
| pTblNd->GetTable().SetHTMLTableLayout( 0 ); |
| |
| // --> FME 2004-11-26 #i37739# A simple 'MakeFrms' after the node sorting |
| // does not work if the table is inside a frame and has no prev/next. |
| SwNode2Layout aNode2Layout( *pTblNd ); |
| // <-- |
| |
| // loesche die Frames der Tabelle |
| pTblNd->DelFrms(); |
| // ? TL_CHART2: ? |
| |
| SwUndoSort* pUndoSort = 0; |
| if (GetIDocumentUndoRedo().DoesUndo()) |
| { |
| pUndoSort = new SwUndoSort( rBoxes[0]->GetSttIdx(), |
| rBoxes[rBoxes.Count()-1]->GetSttIdx(), |
| *pTblNd, rOpt, aFlatBox.HasItemSets() ); |
| GetIDocumentUndoRedo().AppendUndo(pUndoSort); |
| } |
| ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo()); |
| |
| // SchluesselElemente einsortieren |
| sal_uInt16 nCount = (rOpt.eDirection == SRT_ROWS) ? |
| aFlatBox.GetRows() : aFlatBox.GetCols(); |
| |
| // SortList nach Schluessel sortieren |
| SwSortElement::Init( this, rOpt, &aFlatBox ); |
| SwSortElements aSortList; |
| |
| // wenn die HeaderLine wiederholt wird und die |
| // Zeilen sortiert werden 1.Zeile nicht mitsortieren |
| sal_uInt16 i; |
| |
| for( i = nStart; i < nCount; ++i) |
| { |
| SwSortBoxElement* pEle = new SwSortBoxElement( i ); |
| aSortList.Insert(pEle); |
| } |
| |
| // nach Sortierung verschieben |
| SwMovedBoxes aMovedList; |
| for(i=0; i < aSortList.Count(); ++i) |
| { |
| SwSortBoxElement* pBox = (SwSortBoxElement*)aSortList[i]; |
| if(rOpt.eDirection == SRT_ROWS) |
| MoveRow(this, aFlatBox, pBox->nRow, i + nStart, aMovedList, pUndoSort); |
| else |
| MoveCol(this, aFlatBox, pBox->nRow, i + nStart, aMovedList, pUndoSort); |
| } |
| |
| // Restore table frames: |
| // --> FME 2004-11-26 #i37739# A simple 'MakeFrms' after the node sorting |
| // does not work if the table is inside a frame and has no prev/next. |
| const sal_uLong nIdx = pTblNd->GetIndex(); |
| aNode2Layout.RestoreUpperFrms( GetNodes(), nIdx, nIdx + 1 ); |
| // <-- |
| |
| // TL_CHART2: need to inform chart of probably changed cell names |
| UpdateCharts( pTblNd->GetTable().GetFrmFmt()->GetName() ); |
| |
| // Alle Elemente aus dem SortArray loeschen |
| aSortList.DeleteAndDestroy( 0, aSortList.Count() ); |
| SwSortElement::Finit(); |
| |
| SetModified(); |
| return sal_True; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Zeilenweise verschieben |
| --------------------------------------------------------------------*/ |
| |
| |
| void MoveRow(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT, |
| SwMovedBoxes& rMovedList, SwUndoSort* pUD) |
| { |
| for( sal_uInt16 i=0; i < rBox.GetCols(); ++i ) |
| { // Alte Zellen-Pos bestimmen und merken |
| const _FndBox* pSource = rBox.GetBox(i, nS); |
| |
| // neue Zellen-Pos |
| const _FndBox* pTarget = rBox.GetBox(i, nT); |
| |
| const SwTableBox* pT = pTarget->GetBox(); |
| const SwTableBox* pS = pSource->GetBox(); |
| |
| sal_Bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX; |
| |
| // und verschieben |
| MoveCell(pDoc, pS, pT, bMoved, pUD); |
| |
| rMovedList.Insert(pS, rMovedList.Count() ); |
| |
| if( pS != pT ) |
| { |
| SwFrmFmt* pTFmt = (SwFrmFmt*)pT->GetFrmFmt(); |
| const SfxItemSet* pSSet = rBox.GetItemSet( i, nS ); |
| |
| if( pSSet || |
| SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMAT ) || |
| SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMULA ) || |
| SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_VALUE ) ) |
| { |
| pTFmt = ((SwTableBox*)pT)->ClaimFrmFmt(); |
| pTFmt->LockModify(); |
| if( pTFmt->ResetFmtAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) ) |
| pTFmt->ResetFmtAttr( RES_VERT_ORIENT ); |
| |
| if( pSSet ) |
| pTFmt->SetFmtAttr( *pSSet ); |
| pTFmt->UnlockModify(); |
| } |
| } |
| } |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Spaltenweise verschieben |
| --------------------------------------------------------------------*/ |
| |
| |
| void MoveCol(SwDoc* pDoc, const FlatFndBox& rBox, sal_uInt16 nS, sal_uInt16 nT, |
| SwMovedBoxes& rMovedList, SwUndoSort* pUD) |
| { |
| for(sal_uInt16 i=0; i < rBox.GetRows(); ++i) |
| { // Alte Zellen-Pos bestimmen und merken |
| const _FndBox* pSource = rBox.GetBox(nS, i); |
| |
| // neue Zellen-Pos |
| const _FndBox* pTarget = rBox.GetBox(nT, i); |
| |
| // und verschieben |
| const SwTableBox* pT = pTarget->GetBox(); |
| const SwTableBox* pS = pSource->GetBox(); |
| |
| // und verschieben |
| sal_Bool bMoved = rMovedList.GetPos(pT) != USHRT_MAX; |
| MoveCell(pDoc, pS, pT, bMoved, pUD); |
| |
| rMovedList.Insert(pS, rMovedList.Count() ); |
| |
| if( pS != pT ) |
| { |
| SwFrmFmt* pTFmt = (SwFrmFmt*)pT->GetFrmFmt(); |
| const SfxItemSet* pSSet = rBox.GetItemSet( nS, i ); |
| |
| if( pSSet || |
| SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMAT ) || |
| SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_FORMULA ) || |
| SFX_ITEM_SET == pTFmt->GetItemState( RES_BOXATR_VALUE ) ) |
| { |
| pTFmt = ((SwTableBox*)pT)->ClaimFrmFmt(); |
| pTFmt->LockModify(); |
| if( pTFmt->ResetFmtAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE ) ) |
| pTFmt->ResetFmtAttr( RES_VERT_ORIENT ); |
| |
| if( pSSet ) |
| pTFmt->SetFmtAttr( *pSSet ); |
| pTFmt->UnlockModify(); |
| } |
| } |
| } |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Eine einzelne Zelle verschieben |
| --------------------------------------------------------------------*/ |
| |
| |
| void MoveCell(SwDoc* pDoc, const SwTableBox* pSource, const SwTableBox* pTar, |
| sal_Bool bMovedBefore, SwUndoSort* pUD) |
| { |
| ASSERT(pSource && pTar,"Fehlende Quelle oder Ziel"); |
| |
| if(pSource == pTar) |
| return; |
| |
| if(pUD) |
| pUD->Insert( pSource->GetName(), pTar->GetName() ); |
| |
| // Pam Quelle auf den ersten ContentNode setzen |
| SwNodeRange aRg( *pSource->GetSttNd(), 0, *pSource->GetSttNd() ); |
| SwNode* pNd = pDoc->GetNodes().GoNext( &aRg.aStart ); |
| |
| // wurde die Zelle (Source) nicht verschoben |
| // -> einen Leer-Node einfuegen und den Rest verschieben |
| // ansonsten steht der Mark auf dem ersten Content-Node |
| if( pNd->StartOfSectionNode() == pSource->GetSttNd() ) |
| pNd = pDoc->GetNodes().MakeTxtNode( aRg.aStart, |
| (SwTxtFmtColl*)pDoc->GetDfltTxtFmtColl() ); |
| aRg.aEnd = *pNd->EndOfSectionNode(); |
| |
| // Ist das Ziel leer(1 leerer Node vorhanden) |
| // -> diesen loeschen und move |
| // Ziel |
| SwNodeIndex aTar( *pTar->GetSttNd() ); |
| pNd = pDoc->GetNodes().GoNext( &aTar ); // naechsten ContentNode |
| sal_uLong nCount = pNd->EndOfSectionIndex() - pNd->StartOfSectionIndex(); |
| |
| sal_Bool bDelFirst = sal_False; |
| if( nCount == 2 ) |
| { |
| ASSERT( pNd->GetCntntNode(), "Kein ContentNode"); |
| bDelFirst = !pNd->GetCntntNode()->Len() && bMovedBefore; |
| } |
| |
| if(!bDelFirst) |
| { // Es besteht schon Inhalt -> alter I n h a l t Section Down |
| SwNodeRange aRgTar( aTar.GetNode(), 0, *pNd->EndOfSectionNode() ); |
| pDoc->GetNodes().SectionDown( &aRgTar ); |
| } |
| |
| // Einfuegen der Source |
| SwNodeIndex aIns( *pTar->GetSttNd()->EndOfSectionNode() ); |
| pDoc->MoveNodeRange( aRg, aIns, |
| IDocumentContentOperations::DOC_MOVEDEFAULT ); |
| |
| // Falls erster Node leer -> weg damit |
| if(bDelFirst) |
| pDoc->GetNodes().Delete( aTar, 1 ); |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Zweidimensionales Array aus FndBoxes generieren |
| --------------------------------------------------------------------*/ |
| |
| |
| FlatFndBox::FlatFndBox(SwDoc* pDocPtr, const _FndBox& rBox) : |
| pDoc(pDocPtr), |
| rBoxRef(rBox), |
| pArr(0), |
| ppItemSets(0), |
| nRow(0), |
| nCol(0) |
| { // Ist das Array symmetrisch |
| if((bSym = CheckLineSymmetry(rBoxRef)) != 0) |
| { |
| // Spalten/Reihen-Anzahl ermitteln |
| nCols = GetColCount(rBoxRef); |
| nRows = GetRowCount(rBoxRef); |
| |
| // lineares Array anlegen |
| pArr = new _FndBoxPtr[ nRows * nCols ]; |
| _FndBox** ppTmp = (_FndBox**)pArr; |
| memset( ppTmp, 0, sizeof(_FndBoxPtr) * nRows * nCols ); |
| |
| |
| FillFlat( rBoxRef ); |
| } |
| } |
| |
| |
| FlatFndBox::~FlatFndBox() |
| { |
| _FndBox** ppTmp = (_FndBox**)pArr; |
| delete [] ppTmp; |
| |
| if( ppItemSets ) |
| delete [] ppItemSets; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Alle Lines einer Box muessen gleichviel Boxen haben |
| --------------------------------------------------------------------*/ |
| |
| |
| sal_Bool FlatFndBox::CheckLineSymmetry(const _FndBox& rBox) |
| { |
| const _FndLines &rLines = rBox.GetLines(); |
| sal_uInt16 nBoxes(0); |
| |
| // UeberLines iterieren |
| for(sal_uInt16 i=0; i < rLines.Count(); ++i) |
| { // Die Boxen einer Line |
| _FndLine* pLn = rLines[i]; |
| const _FndBoxes& rBoxes = pLn->GetBoxes(); |
| |
| // Anzahl der Boxen aller Lines ungleich -> keine Symmetrie |
| if( i && nBoxes != rBoxes.Count()) |
| return sal_False; |
| |
| nBoxes = rBoxes.Count(); |
| if( !CheckBoxSymmetry( *pLn ) ) |
| return sal_False; |
| } |
| return sal_True; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Box auf Symmetrie pruefen |
| Alle Boxen einer Line muessen gleichviele Lines haben |
| --------------------------------------------------------------------*/ |
| |
| |
| sal_Bool FlatFndBox::CheckBoxSymmetry(const _FndLine& rLn) |
| { |
| const _FndBoxes &rBoxes = rLn.GetBoxes(); |
| sal_uInt16 nLines(0); |
| |
| // Ueber Boxes iterieren |
| for(sal_uInt16 i=0; i < rBoxes.Count(); ++i) |
| { // Die Boxen einer Line |
| _FndBox* pBox = rBoxes[i]; |
| const _FndLines& rLines = pBox->GetLines(); |
| |
| // Anzahl der Boxen aller Lines ungleich -> keine Symmetrie |
| if( i && nLines != rLines.Count() ) |
| return sal_False; |
| |
| nLines = rLines.Count(); |
| if( nLines && !CheckLineSymmetry( *pBox ) ) |
| return sal_False; |
| } |
| return sal_True; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: max Anzahl der Spalten (Boxes) |
| --------------------------------------------------------------------*/ |
| |
| |
| sal_uInt16 FlatFndBox::GetColCount(const _FndBox& rBox) |
| { |
| const _FndLines& rLines = rBox.GetLines(); |
| // Ueber Lines iterieren |
| if( !rLines.Count() ) |
| return 1; |
| |
| sal_uInt16 nSum = 0; |
| for( sal_uInt16 i=0; i < rLines.Count(); ++i ) |
| { |
| // Die Boxen einer Line |
| sal_uInt16 nCount = 0; |
| const _FndBoxes& rBoxes = rLines[i]->GetBoxes(); |
| for( sal_uInt16 j=0; j < rBoxes.Count(); ++j ) |
| // Rekursiv wirder ueber die Lines Iterieren |
| nCount += rBoxes[j]->GetLines().Count() |
| ? GetColCount(*rBoxes[j]) : 1; |
| |
| if( nSum < nCount ) |
| nSum = nCount; |
| } |
| return nSum; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: max Anzahl der Zeilen (Lines) |
| --------------------------------------------------------------------*/ |
| |
| |
| sal_uInt16 FlatFndBox::GetRowCount(const _FndBox& rBox) |
| { |
| const _FndLines& rLines = rBox.GetLines(); |
| if( !rLines.Count() ) |
| return 1; |
| |
| sal_uInt16 nLines = 0; |
| for(sal_uInt16 i=0; i < rLines.Count(); ++i) |
| { // Die Boxen einer Line |
| const _FndBoxes& rBoxes = rLines[i]->GetBoxes(); |
| sal_uInt16 nLn = 1; |
| for(sal_uInt16 j=0; j < rBoxes.Count(); ++j) |
| if( rBoxes[j]->GetLines().Count() ) |
| // Rekursiv ueber die Lines Iterieren |
| nLn = Max(GetRowCount(*rBoxes[j]), nLn); |
| |
| nLines = nLines + nLn; |
| } |
| return nLines; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: lineares Array aus atomaren FndBoxes erzeugen |
| --------------------------------------------------------------------*/ |
| |
| |
| void FlatFndBox::FillFlat(const _FndBox& rBox, sal_Bool bLastBox) |
| { |
| sal_Bool bModRow = sal_False; |
| const _FndLines& rLines = rBox.GetLines(); |
| |
| // Ueber Lines iterieren |
| sal_uInt16 nOldRow = nRow; |
| for( sal_uInt16 i=0; i < rLines.Count(); ++i ) |
| { |
| // Die Boxen einer Line |
| const _FndBoxes& rBoxes = rLines[i]->GetBoxes(); |
| sal_uInt16 nOldCol = nCol; |
| for( sal_uInt16 j = 0; j < rBoxes.Count(); ++j ) |
| { |
| // Die Box pruefen ob es eine atomare Box ist |
| const _FndBox* pBox = rBoxes[ j ]; |
| |
| if( !pBox->GetLines().Count() ) |
| { |
| // peichern |
| sal_uInt16 nOff = nRow * nCols + nCol; |
| *(pArr + nOff) = pBox; |
| |
| // sicher die Formel/Format/Value Werte |
| const SwFrmFmt* pFmt = pBox->GetBox()->GetFrmFmt(); |
| if( SFX_ITEM_SET == pFmt->GetItemState( RES_BOXATR_FORMAT ) || |
| SFX_ITEM_SET == pFmt->GetItemState( RES_BOXATR_FORMULA ) || |
| SFX_ITEM_SET == pFmt->GetItemState( RES_BOXATR_VALUE ) ) |
| { |
| SfxItemSet* pSet = new SfxItemSet( pDoc->GetAttrPool(), |
| RES_BOXATR_FORMAT, RES_BOXATR_VALUE, |
| RES_VERT_ORIENT, RES_VERT_ORIENT, 0 ); |
| pSet->Put( pFmt->GetAttrSet() ); |
| if( !ppItemSets ) |
| { |
| ppItemSets = new SfxItemSet*[ nRows * nCols ]; |
| memset( ppItemSets, 0, sizeof(SfxItemSet*) * nRows * nCols ); |
| } |
| *(ppItemSets + nOff ) = pSet; |
| } |
| |
| bModRow = sal_True; |
| } |
| else |
| { |
| // Rekursiv wieder ueber die Lines einer Box Iterieren |
| FillFlat( *pBox, ( j == rBoxes.Count()-1 ) ); |
| } |
| nCol++; |
| } |
| if(bModRow) |
| nRow++; |
| nCol = nOldCol; |
| } |
| if(!bLastBox) |
| nRow = nOldRow; |
| } |
| |
| /*-------------------------------------------------------------------- |
| Beschreibung: Zugriff auf eine bestimmte Zelle |
| --------------------------------------------------------------------*/ |
| |
| |
| const _FndBox* FlatFndBox::GetBox(sal_uInt16 n_Col, sal_uInt16 n_Row) const |
| { |
| sal_uInt16 nOff = n_Row * nCols + n_Col; |
| const _FndBox* pTmp = *(pArr + nOff); |
| |
| ASSERT(n_Col < nCols && n_Row < nRows && pTmp, "unzulaessiger Array-Zugriff"); |
| return pTmp; |
| } |
| |
| const SfxItemSet* FlatFndBox::GetItemSet(sal_uInt16 n_Col, sal_uInt16 n_Row) const |
| { |
| ASSERT( !ppItemSets || ( n_Col < nCols && n_Row < nRows), "unzulaessiger Array-Zugriff"); |
| |
| return ppItemSets ? *(ppItemSets + (n_Row * nCols + n_Col )) : 0; |
| } |
| |
| |