blob: 2c07bc0d81f8ffac0accfbac0bb91d38bfd4b765 [file] [log] [blame]
/**************************************************************
*
* 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;
}