/************************************************************** * * 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_sc.hxx" // INCLUDE --------------------------------------------------------------- #include #include "chartpos.hxx" #include "document.hxx" #include "rechead.hxx" namespace { bool lcl_hasValueDataButNoDates( ScDocument* pDocument, SCCOL nCol, SCROW nRow, SCTAB nTab ) { bool bReturn = false; if (pDocument->HasValueData( nCol, nRow, nTab )) { //treat dates like text #i25706# sal_uInt32 nNumberFormat = pDocument->GetNumberFormat( ScAddress( nCol, nRow, nTab ) ); short nType = pDocument->GetFormatTable()->GetType(nNumberFormat); bool bIsDate = (nType & NUMBERFORMAT_DATE); bReturn = !bIsDate; } return bReturn; } } ScChartPositioner::ScChartPositioner( ScDocument* pDoc, SCTAB nTab, SCCOL nStartColP, SCROW nStartRowP, SCCOL nEndColP, SCROW nEndRowP) : pDocument( pDoc ), pPositionMap( NULL ), eGlue( SC_CHARTGLUE_NA ), nStartCol(0), nStartRow(0), bColHeaders( sal_False ), bRowHeaders( sal_False ), bDummyUpperLeft( sal_False ) { SetRangeList( ScRange( nStartColP, nStartRowP, nTab, nEndColP, nEndRowP, nTab ) ); CheckColRowHeaders(); } ScChartPositioner::ScChartPositioner( ScDocument* pDoc, const ScRangeListRef& rRangeList ) : aRangeListRef( rRangeList ), pDocument( pDoc ), pPositionMap( NULL ), eGlue( SC_CHARTGLUE_NA ), nStartCol(0), nStartRow(0), bColHeaders( sal_False ), bRowHeaders( sal_False ), bDummyUpperLeft( sal_False ) { if ( aRangeListRef.Is() ) CheckColRowHeaders(); } ScChartPositioner::ScChartPositioner( const ScChartPositioner& rPositioner ) : aRangeListRef( rPositioner.aRangeListRef ), pDocument(rPositioner.pDocument), pPositionMap( NULL ), eGlue(rPositioner.eGlue), nStartCol(rPositioner.nStartCol), nStartRow(rPositioner.nStartRow), bColHeaders(rPositioner.bColHeaders), bRowHeaders(rPositioner.bRowHeaders), bDummyUpperLeft( rPositioner.bDummyUpperLeft ) { } ScChartPositioner::~ScChartPositioner() { delete pPositionMap; } sal_Bool ScChartPositioner::operator==(const ScChartPositioner& rCmp) const { return bColHeaders == rCmp.bColHeaders && bRowHeaders == rCmp.bRowHeaders && *aRangeListRef == *rCmp.aRangeListRef; } void ScChartPositioner::SetRangeList( const ScRange& rRange ) { aRangeListRef = new ScRangeList; aRangeListRef->Append( rRange ); InvalidateGlue(); } void ScChartPositioner::GlueState() { if ( eGlue != SC_CHARTGLUE_NA ) return; bDummyUpperLeft = sal_False; ScRangePtr pR; if ( aRangeListRef->Count() <= 1 ) { if ( (pR = aRangeListRef->First())!=NULL ) { if ( pR->aStart.Tab() == pR->aEnd.Tab() ) eGlue = SC_CHARTGLUE_NONE; else eGlue = SC_CHARTGLUE_COLS; // mehrere Tabellen spaltenweise nStartCol = pR->aStart.Col(); nStartRow = pR->aStart.Row(); } else { InvalidateGlue(); nStartCol = 0; nStartRow = 0; } return; } // sal_uLong nOldPos = aRangeListRef->GetCurPos(); pR = aRangeListRef->First(); nStartCol = pR->aStart.Col(); nStartRow = pR->aStart.Row(); SCCOL nMaxCols, nEndCol; SCROW nMaxRows, nEndRow; nMaxCols = nEndCol = 0; nMaxRows = nEndRow = 0; do { // umspannenden Bereich etc. feststellen SCCOLROW nTmp, n1, n2; if ( (n1 = pR->aStart.Col()) < nStartCol ) nStartCol = static_cast(n1); if ( (n2 = pR->aEnd.Col()) > nEndCol ) nEndCol = static_cast(n2); if ( (nTmp = n2 - n1 + 1) > nMaxCols ) nMaxCols = static_cast(nTmp); if ( (n1 = pR->aStart.Row()) < nStartRow ) nStartRow = static_cast(n1); if ( (n2 = pR->aEnd.Row()) > nEndRow ) nEndRow = static_cast(n2); if ( (nTmp = n2 - n1 + 1) > nMaxRows ) nMaxRows = static_cast(nTmp); } while ( (pR = aRangeListRef->Next())!=NULL ); SCCOL nC = nEndCol - nStartCol + 1; if ( nC == 1 ) { eGlue = SC_CHARTGLUE_ROWS; return; } SCROW nR = nEndRow - nStartRow + 1; if ( nR == 1 ) { eGlue = SC_CHARTGLUE_COLS; return; } sal_uLong nCR = (sal_uLong)nC * nR; //2do: /* Erstmal simpel ohne Bitmaskiererei, maximal koennten so 8MB alloziert werden (256 Cols mal 32000 Rows), das liesse sich mit 2 Bit je Eintrag auf 2MB reduzieren, andererseits ist es so schneller. Weitere Platz-Optimierung waere, in dem Array nur die wirklich benutzten Zeilen/Spalten abzulegen, wuerde aber ein weiteres durchlaufen der RangeList und indirekten Zugriff auf das Array bedeuten. */ const sal_uInt8 nHole = 0; const sal_uInt8 nOccu = 1; const sal_uInt8 nFree = 2; const sal_uInt8 nGlue = 3; sal_uInt8* p; sal_uInt8* pA = new sal_uInt8[ nCR ]; memset( pA, 0, nCR * sizeof(sal_uInt8) ); SCCOL nCol, nCol1, nCol2; SCROW nRow, nRow1, nRow2; for ( pR = aRangeListRef->First(); pR; pR = aRangeListRef->Next() ) { // Selektionen 2D als belegt markieren nCol1 = pR->aStart.Col() - nStartCol; nCol2 = pR->aEnd.Col() - nStartCol; nRow1 = pR->aStart.Row() - nStartRow; nRow2 = pR->aEnd.Row() - nStartRow; for ( nCol = nCol1; nCol <= nCol2; nCol++ ) { p = pA + (sal_uLong)nCol * nR + nRow1; for ( nRow = nRow1; nRow <= nRow2; nRow++, p++ ) *p = nOccu; } } sal_Bool bGlue = sal_True; sal_Bool bGlueCols = sal_False; for ( nCol = 0; bGlue && nCol < nC; nCol++ ) { // Spalten probieren durchzugehen und als frei markieren p = pA + (sal_uLong)nCol * nR; for ( nRow = 0; bGlue && nRow < nR; nRow++, p++ ) { if ( *p == nOccu ) { // Wenn einer mittendrin liegt ist keine Zusammenfassung // moeglich. Am Rand koennte ok sein, wenn in dieser Spalte // in jeder belegten Zeile einer belegt ist. if ( nRow > 0 && nCol > 0 ) bGlue = sal_False; // nCol==0 kann DummyUpperLeft sein else nRow = nR; } else *p = nFree; } if ( bGlue && *(p = (pA + ((((sal_uLong)nCol+1) * nR) - 1))) == nFree ) { // Spalte als komplett frei markieren *p = nGlue; bGlueCols = sal_True; // mindestens eine freie Spalte } } sal_Bool bGlueRows = sal_False; for ( nRow = 0; bGlue && nRow < nR; nRow++ ) { // Zeilen probieren durchzugehen und als frei markieren p = pA + nRow; for ( nCol = 0; bGlue && nCol < nC; nCol++, p+=nR ) { if ( *p == nOccu ) { if ( nCol > 0 && nRow > 0 ) bGlue = sal_False; // nRow==0 kann DummyUpperLeft sein else nCol = nC; } else *p = nFree; } if ( bGlue && *(p = (pA + ((((sal_uLong)nC-1) * nR) + nRow))) == nFree ) { // Zeile als komplett frei markieren *p = nGlue; bGlueRows = sal_True; // mindestens eine freie Zeile } } // n=1: die linke obere Ecke koennte bei Beschriftung automagisch // hinzugezogen werden p = pA + 1; for ( sal_uLong n = 1; bGlue && n < nCR; n++, p++ ) { // ein unberuehrtes Feld heisst, dass es weder spaltenweise noch // zeilenweise zu erreichen war, also nichts zusamenzufassen if ( *p == nHole ) bGlue = sal_False; } if ( bGlue ) { if ( bGlueCols && bGlueRows ) eGlue = SC_CHARTGLUE_BOTH; else if ( bGlueRows ) eGlue = SC_CHARTGLUE_ROWS; else eGlue = SC_CHARTGLUE_COLS; if ( *pA != nOccu ) bDummyUpperLeft = sal_True; } else { eGlue = SC_CHARTGLUE_NONE; } delete [] pA; } void ScChartPositioner::CheckColRowHeaders() { SCCOL nCol1, nCol2, iCol; SCROW nRow1, nRow2, iRow; SCTAB nTab1, nTab2; sal_Bool bColStrings = sal_True; sal_Bool bRowStrings = sal_True; GlueState(); if ( aRangeListRef->Count() == 1 ) { aRangeListRef->First()->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); if ( nCol1 > nCol2 || nRow1 > nRow2 ) bColStrings = bRowStrings = sal_False; else { for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++) { if (lcl_hasValueDataButNoDates( pDocument, iCol, nRow1, nTab1 )) bColStrings = sal_False; } for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++) { if (lcl_hasValueDataButNoDates( pDocument, nCol1, iRow, nTab1 )) bRowStrings = sal_False; } } } else { sal_Bool bVert = (eGlue == SC_CHARTGLUE_NONE || eGlue == SC_CHARTGLUE_ROWS); for ( ScRangePtr pR = aRangeListRef->First(); pR && (bColStrings || bRowStrings); pR = aRangeListRef->Next() ) { pR->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); sal_Bool bTopRow = (nRow1 == nStartRow); if ( bRowStrings && (bVert || nCol1 == nStartCol) ) { // NONE oder ROWS: RowStrings in jeder Selektion moeglich // COLS oder BOTH: nur aus der ersten Spalte if ( nCol1 <= nCol2 ) for (iRow=nRow1; iRow<=nRow2 && bRowStrings; iRow++) { if (lcl_hasValueDataButNoDates( pDocument, nCol1, iRow, nTab1 )) bRowStrings = sal_False; } } if ( bColStrings && bTopRow ) { // ColStrings nur aus der ersten Zeile if ( nRow1 <= nRow2 ) for (iCol=nCol1; iCol<=nCol2 && bColStrings; iCol++) { if (lcl_hasValueDataButNoDates( pDocument, iCol, nRow1, nTab1 )) bColStrings = sal_False; } } } } bColHeaders = bColStrings; bRowHeaders = bRowStrings; } const ScChartPositionMap* ScChartPositioner::GetPositionMap() { CreatePositionMap(); return pPositionMap; } void ScChartPositioner::CreatePositionMap() { if ( eGlue == SC_CHARTGLUE_NA && pPositionMap ) { delete pPositionMap; pPositionMap = NULL; } if ( pPositionMap ) return ; SCSIZE nColAdd = bRowHeaders ? 1 : 0; SCSIZE nRowAdd = bColHeaders ? 1 : 0; SCCOL nCol, nCol1, nCol2; SCROW nRow, nRow1, nRow2; SCTAB nTab, nTab1, nTab2; // // wirkliche Groesse (ohne versteckte Zeilen/Spalten) // SCSIZE nColCount = 0; SCSIZE nRowCount = 0; GlueState(); sal_Bool bNoGlue = (eGlue == SC_CHARTGLUE_NONE); Table* pCols = new Table; Table* pNewRowTable = new Table; ScAddress* pNewAddress = new ScAddress; ScRangePtr pR; Table* pCol; ScAddress* pPos; SCROW nNoGlueRow = 0; for ( pR = aRangeListRef->First(); pR; pR = aRangeListRef->Next() ) { pR->GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); for ( nTab = nTab1; nTab <= nTab2; nTab++ ) { // nTab im ColKey, um gleiche Col/Row in anderer Table haben zu koennen sal_uLong nInsCol = (static_cast(nTab) << 16) | (bNoGlue ? 0 : static_cast(nCol1)); for ( nCol = nCol1; nCol <= nCol2; ++nCol, ++nInsCol ) { if ( bNoGlue || eGlue == SC_CHARTGLUE_ROWS ) { // meistens gleiche Cols if ( (pCol = (Table*) pCols->Get( nInsCol ))==NULL ) { pCols->Insert( nInsCol, pNewRowTable ); pCol = pNewRowTable; pNewRowTable = new Table; } } else { // meistens neue Cols if ( pCols->Insert( nInsCol, pNewRowTable ) ) { pCol = pNewRowTable; pNewRowTable = new Table; } else pCol = (Table*) pCols->Get( nInsCol ); } // bei anderer Tabelle wurde bereits neuer ColKey erzeugt, // die Zeilen muessen fuer's Dummy fuellen gleich sein! sal_uLong nInsRow = (bNoGlue ? nNoGlueRow : nRow1); for ( nRow = nRow1; nRow <= nRow2; nRow++, nInsRow++ ) { if ( pCol->Insert( nInsRow, pNewAddress ) ) { pNewAddress->Set( nCol, nRow, nTab ); pNewAddress = new ScAddress; } } } } // bei NoGlue werden zusammengehoerige Tabellen als ColGlue dargestellt nNoGlueRow += nRow2 - nRow1 + 1; } delete pNewAddress; delete pNewRowTable; // Anzahl der Daten nColCount = static_cast< SCSIZE >( pCols->Count()); if ( (pCol = (Table*) pCols->First())!=NULL ) { if ( bDummyUpperLeft ) pCol->Insert( 0, (void*)0 ); // Dummy fuer Beschriftung nRowCount = static_cast< SCSIZE >( pCol->Count()); } else nRowCount = 0; if ( nColCount > 0 ) nColCount -= nColAdd; if ( nRowCount > 0 ) nRowCount -= nRowAdd; if ( nColCount==0 || nRowCount==0 ) { // einen Eintrag ohne Daten erzeugen pR = aRangeListRef->First(); if ( pCols->Count() > 0 ) pCol = (Table*) pCols->First(); else { pCol = new Table; pCols->Insert( 0, pCol ); } nColCount = 1; if ( pCol->Count() > 0 ) { // kann ja eigentlich nicht sein, wenn nColCount==0 || nRowCount==0 pPos = (ScAddress*) pCol->First(); if ( pPos ) { delete pPos; pCol->Replace( pCol->GetCurKey(), (void*)0 ); } } else pCol->Insert( 0, (void*)0 ); nRowCount = 1; nColAdd = 0; nRowAdd = 0; } else { if ( bNoGlue ) { // Luecken mit Dummies fuellen, erste Spalte ist Master Table* pFirstCol = (Table*) pCols->First(); sal_uLong nCount = pFirstCol->Count(); pFirstCol->First(); for ( sal_uLong n = 0; n < nCount; n++, pFirstCol->Next() ) { sal_uLong nKey = pFirstCol->GetCurKey(); pCols->First(); while ( (pCol = (Table*) pCols->Next())!=NULL ) pCol->Insert( nKey, (void*)0 ); // keine Daten } } } pPositionMap = new ScChartPositionMap( static_cast(nColCount), static_cast(nRowCount), static_cast(nColAdd), static_cast(nRowAdd), *pCols ); // Aufraeumen for ( pCol = (Table*) pCols->First(); pCol; pCol = (Table*) pCols->Next() ) { //! nur Tables loeschen, nicht die ScAddress* delete pCol; } delete pCols; } ScChartPositionMap::ScChartPositionMap( SCCOL nChartCols, SCROW nChartRows, SCCOL nColAdd, SCROW nRowAdd, Table& rCols ) : ppData( new ScAddress* [ nChartCols * nChartRows ] ), ppColHeader( new ScAddress* [ nChartCols ] ), ppRowHeader( new ScAddress* [ nChartRows ] ), nCount( (sal_uLong) nChartCols * nChartRows ), nColCount( nChartCols ), nRowCount( nChartRows ) { DBG_ASSERT( nColCount && nRowCount, "ScChartPositionMap without dimension" ); ScAddress* pPos; SCCOL nCol; SCROW nRow; Table* pCol = (Table*) rCols.First(); // Zeilen-Header pPos = (ScAddress*) pCol->First(); if ( nRowAdd ) pPos = (ScAddress*) pCol->Next(); if ( nColAdd ) { // eigenstaendig for ( nRow = 0; nRow < nRowCount; nRow++ ) { ppRowHeader[ nRow ] = pPos; pPos = (ScAddress*) pCol->Next(); } } else { // Kopie for ( nRow = 0; nRow < nRowCount; nRow++ ) { ppRowHeader[ nRow ] = ( pPos ? new ScAddress( *pPos ) : NULL ); pPos = (ScAddress*) pCol->Next(); } } if ( nColAdd ) pCol = (Table*) rCols.Next(); // Daten spaltenweise und Spalten-Header sal_uLong nIndex = 0; for ( nCol = 0; nCol < nColCount; nCol++ ) { if ( pCol ) { pPos = (ScAddress*) pCol->First(); if ( nRowAdd ) { ppColHeader[ nCol ] = pPos; // eigenstaendig pPos = (ScAddress*) pCol->Next(); } else ppColHeader[ nCol ] = ( pPos ? new ScAddress( *pPos ) : NULL ); for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ ) { ppData[ nIndex ] = pPos; pPos = (ScAddress*) pCol->Next(); } } else { ppColHeader[ nCol ] = NULL; for ( nRow = 0; nRow < nRowCount; nRow++, nIndex++ ) { ppData[ nIndex ] = NULL; } } pCol = (Table*) rCols.Next(); } } ScChartPositionMap::~ScChartPositionMap() { for ( sal_uLong nIndex=0; nIndex < nCount; nIndex++ ) { delete ppData[nIndex]; } delete [] ppData; SCCOL j; for ( j=0; j < nColCount; j++ ) { delete ppColHeader[j]; } delete [] ppColHeader; SCROW i; for ( i=0; i < nRowCount; i++ ) { delete ppRowHeader[i]; } delete [] ppRowHeader; } //UNUSED2009-05 ScRangeListRef ScChartPositionMap::GetColRanges( SCCOL nChartCol ) const //UNUSED2009-05 { //UNUSED2009-05 ScRangeListRef xRangeList = new ScRangeList; //UNUSED2009-05 if ( nChartCol < nColCount ) //UNUSED2009-05 { //UNUSED2009-05 sal_uLong nStop = GetIndex( nChartCol, nRowCount ); //UNUSED2009-05 for ( sal_uLong nIndex = GetIndex( nChartCol, 0 ); nIndex < nStop; nIndex++ ) //UNUSED2009-05 { //UNUSED2009-05 if ( ppData[ nIndex ] ) //UNUSED2009-05 xRangeList->Join( *ppData[ nIndex ] ); //UNUSED2009-05 } //UNUSED2009-05 } //UNUSED2009-05 return xRangeList; //UNUSED2009-05 } //UNUSED2009-05 ScRangeListRef ScChartPositionMap::GetRowRanges( SCROW nChartRow ) const //UNUSED2009-05 { //UNUSED2009-05 ScRangeListRef xRangeList = new ScRangeList; //UNUSED2009-05 if ( nChartRow < nRowCount ) //UNUSED2009-05 { //UNUSED2009-05 sal_uLong nStop = GetIndex( nColCount, nChartRow ); //UNUSED2009-05 for ( sal_uLong nIndex = GetIndex( 0, nChartRow ); nIndex < nStop; //UNUSED2009-05 nIndex += nRowCount ) //UNUSED2009-05 { //UNUSED2009-05 if ( ppData[ nIndex ] ) //UNUSED2009-05 xRangeList->Join( *ppData[ nIndex ] ); //UNUSED2009-05 } //UNUSED2009-05 } //UNUSED2009-05 return xRangeList; //UNUSED2009-05 }