/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" // INCLUDE --------------------------------------------------------------- #include #include "scitems.hxx" #include "global.hxx" #include "dociter.hxx" #include "document.hxx" #include "table.hxx" #include "column.hxx" #include "cell.hxx" #include "attarray.hxx" #include "patattr.hxx" #include "docoptio.hxx" #include "cellform.hxx" #include using ::rtl::math::approxEqual; using ::std::vector; using ::rtl::OUString; // STATIC DATA ----------------------------------------------------------- namespace { void lcl_toUpper(OUString& rStr) { rStr = ScGlobal::pCharClass->toUpper(rStr.trim(), 0, static_cast(rStr.getLength())); } } ScDocumentIterator::ScDocumentIterator( ScDocument* pDocument, SCTAB nStartTable, SCTAB nEndTable ) : pDoc( pDocument ), nStartTab( nStartTable ), nEndTab( nEndTable ) { PutInOrder( nStartTab, nEndTab ); if (!ValidTab(nStartTab)) nStartTab = MAXTAB; if (!ValidTab(nEndTab)) nEndTab = MAXTAB; pDefPattern = pDoc->GetDefPattern(); nCol = 0; nRow = 0; nTab = nStartTab; nColPos = 0; nAttrPos = 0; } ScDocumentIterator::~ScDocumentIterator() { } BOOL ScDocumentIterator::GetThisCol() { ScTable* pTab; while ( (pTab = pDoc->pTab[nTab]) == NULL ) { if ( nTab == nEndTab ) { nCol = MAXCOL; nRow = MAXROW; return FALSE; } ++nTab; } ScColumn* pCol = &pTab->aCol[nCol]; ScAttrArray* pAtt = pCol->pAttrArray; BOOL bFound = FALSE; do { SCROW nColRow; SCROW nAttrEnd; do { nAttrEnd = pAtt->pData[nAttrPos].nRow; if (nAttrEnd < nRow) ++nAttrPos; } while (nAttrEnd < nRow); do { nColRow = (nColPos < pCol->nCount) ? pCol->pItems[nColPos].nRow : MAXROW+1; if (nColRow < nRow) ++nColPos; } while (nColRow < nRow); if (nColRow == nRow) { bFound = TRUE; pCell = pCol->pItems[nColPos].pCell; pPattern = pAtt->pData[nAttrPos].pPattern; } else if ( pAtt->pData[nAttrPos].pPattern != pDefPattern ) { bFound = TRUE; pCell = NULL; pPattern = pAtt->pData[nAttrPos].pPattern; } else { nRow = Min( (SCROW)nColRow, (SCROW)(nAttrEnd+1) ); } } while (!bFound && nRow <= MAXROW); return bFound; } BOOL ScDocumentIterator::GetThis() { BOOL bEnd = FALSE; BOOL bSuccess = FALSE; while ( !bSuccess && !bEnd ) { if ( nRow > MAXROW ) bSuccess = FALSE; else bSuccess = GetThisCol(); if ( !bSuccess ) { ++nCol; if (nCol > MAXCOL) { nCol = 0; ++nTab; if (nTab > nEndTab) bEnd = TRUE; } nRow = 0; nColPos = 0; nAttrPos = 0; } } return !bEnd; } BOOL ScDocumentIterator::GetFirst() { nCol = 0; nTab = nStartTab; nRow = 0; nColPos = 0; nAttrPos = 0; return GetThis(); } BOOL ScDocumentIterator::GetNext() { ++nRow; return GetThis(); } //------------------------------------------------------------------------ ScBaseCell* ScDocumentIterator::GetCell() { return pCell; } const ScPatternAttr* ScDocumentIterator::GetPattern() { return pPattern; } void ScDocumentIterator::GetPos( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ) { rCol = nCol; rRow = nRow; rTab = nTab; } //------------------------------------------------------------------------ //------------------------------------------------------------------------ void lcl_IterGetNumberFormat( ULONG& nFormat, const ScAttrArray*& rpArr, SCROW& nAttrEndRow, const ScAttrArray* pNewArr, SCROW nRow, ScDocument* pDoc ) { if ( rpArr != pNewArr || nAttrEndRow < nRow ) { SCSIZE nPos; pNewArr->Search( nRow, nPos ); // nPos 0 gueltig wenn nicht gefunden const ScPatternAttr* pPattern = pNewArr->pData[nPos].pPattern; nFormat = pPattern->GetNumberFormat( pDoc->GetFormatTable() ); rpArr = pNewArr; nAttrEndRow = pNewArr->pData[nPos].nRow; } } //UNUSED2008-05 ScValueIterator::ScValueIterator( ScDocument* pDocument, //UNUSED2008-05 SCCOL nSCol, SCROW nSRow, SCTAB nSTab, //UNUSED2008-05 SCCOL nECol, SCROW nERow, SCTAB nETab, //UNUSED2008-05 BOOL bSTotal, BOOL bTextZero ) : //UNUSED2008-05 pDoc( pDocument ), //UNUSED2008-05 nNumFmtIndex(0), //UNUSED2008-05 nStartCol( nSCol), //UNUSED2008-05 nStartRow( nSRow), //UNUSED2008-05 nStartTab( nSTab ), //UNUSED2008-05 nEndCol( nECol ), //UNUSED2008-05 nEndRow( nERow), //UNUSED2008-05 nEndTab( nETab ), //UNUSED2008-05 nNumFmtType( NUMBERFORMAT_UNDEFINED ), //UNUSED2008-05 bNumValid( FALSE ), //UNUSED2008-05 bSubTotal(bSTotal), //UNUSED2008-05 bNextValid( FALSE ), //UNUSED2008-05 bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ), //UNUSED2008-05 bTextAsZero( bTextZero ) //UNUSED2008-05 { //UNUSED2008-05 PutInOrder( nStartCol, nEndCol); //UNUSED2008-05 PutInOrder( nStartRow, nEndRow); //UNUSED2008-05 PutInOrder( nStartTab, nEndTab ); //UNUSED2008-05 //UNUSED2008-05 if (!ValidCol(nStartCol)) nStartCol = MAXCOL; //UNUSED2008-05 if (!ValidCol(nEndCol)) nEndCol = MAXCOL; //UNUSED2008-05 if (!ValidRow(nStartRow)) nStartRow = MAXROW; //UNUSED2008-05 if (!ValidRow(nEndRow)) nEndRow = MAXROW; //UNUSED2008-05 if (!ValidTab(nStartTab)) nStartTab = MAXTAB; //UNUSED2008-05 if (!ValidTab(nEndTab)) nEndTab = MAXTAB; //UNUSED2008-05 //UNUSED2008-05 nCol = nStartCol; //UNUSED2008-05 nRow = nStartRow; //UNUSED2008-05 nTab = nStartTab; //UNUSED2008-05 //UNUSED2008-05 nColRow = 0; // wird bei GetFirst initialisiert //UNUSED2008-05 //UNUSED2008-05 nNumFormat = 0; // werden bei GetNumberFormat initialisiert //UNUSED2008-05 pAttrArray = 0; //UNUSED2008-05 nAttrEndRow = 0; //UNUSED2008-05 } ScValueIterator::ScValueIterator( ScDocument* pDocument, const ScRange& rRange, BOOL bSTotal, BOOL bTextZero ) : pDoc( pDocument ), nNumFmtIndex(0), nStartCol( rRange.aStart.Col() ), nStartRow( rRange.aStart.Row() ), nStartTab( rRange.aStart.Tab() ), nEndCol( rRange.aEnd.Col() ), nEndRow( rRange.aEnd.Row() ), nEndTab( rRange.aEnd.Tab() ), nNumFmtType( NUMBERFORMAT_UNDEFINED ), bNumValid( FALSE ), bSubTotal(bSTotal), bNextValid( FALSE ), bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ), bTextAsZero( bTextZero ) { PutInOrder( nStartCol, nEndCol); PutInOrder( nStartRow, nEndRow); PutInOrder( nStartTab, nEndTab ); if (!ValidCol(nStartCol)) nStartCol = MAXCOL; if (!ValidCol(nEndCol)) nEndCol = MAXCOL; if (!ValidRow(nStartRow)) nStartRow = MAXROW; if (!ValidRow(nEndRow)) nEndRow = MAXROW; if (!ValidTab(nStartTab)) nStartTab = MAXTAB; if (!ValidTab(nEndTab)) nEndTab = MAXTAB; nCol = nStartCol; nRow = nStartRow; nTab = nStartTab; nColRow = 0; // wird bei GetFirst initialisiert nNumFormat = 0; // werden bei GetNumberFormat initialisiert pAttrArray = 0; nAttrEndRow = 0; } BOOL ScValueIterator::GetThis(double& rValue, USHORT& rErr) { ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; for (;;) { if ( nRow > nEndRow ) { nRow = nStartRow; do { nCol++; if ( nCol > nEndCol ) { nCol = nStartCol; nTab++; if ( nTab > nEndTab ) { // rValue = 0.0; //! do not change caller's value! rErr = 0; return FALSE; // Ende und Aus } } pCol = &(pDoc->pTab[nTab])->aCol[nCol]; } while ( pCol->nCount == 0 ); pCol->Search( nRow, nColRow ); } while (( nColRow < pCol->nCount ) && ( pCol->pItems[nColRow].nRow < nRow )) nColRow++; if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= nEndRow ) { nRow = pCol->pItems[nColRow].nRow + 1; if ( !bSubTotal || !pDoc->pTab[nTab]->IsFiltered( nRow-1 ) ) { ScBaseCell* pCell = pCol->pItems[nColRow].pCell; ++nColRow; switch (pCell->GetCellType()) { case CELLTYPE_VALUE: { bNumValid = FALSE; rValue = ((ScValueCell*)pCell)->GetValue(); rErr = 0; --nRow; if ( bCalcAsShown ) { lcl_IterGetNumberFormat( nNumFormat, pAttrArray, nAttrEndRow, pCol->pAttrArray, nRow, pDoc ); rValue = pDoc->RoundValueAsShown( rValue, nNumFormat ); } // // wenn in der selben Spalte gleich noch eine Value-Cell folgt, die // auch noch im Block liegt, den Wert jetzt schon holen // if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= nEndRow && pCol->pItems[nColRow].pCell->GetCellType() == CELLTYPE_VALUE && !bSubTotal ) { fNextValue = ((ScValueCell*)pCol->pItems[nColRow].pCell)->GetValue(); nNextRow = pCol->pItems[nColRow].nRow; bNextValid = TRUE; if ( bCalcAsShown ) { lcl_IterGetNumberFormat( nNumFormat, pAttrArray, nAttrEndRow, pCol->pAttrArray, nNextRow, pDoc ); fNextValue = pDoc->RoundValueAsShown( fNextValue, nNumFormat ); } } return TRUE; // gefunden } // break; case CELLTYPE_FORMULA: { if (!bSubTotal || !((ScFormulaCell*)pCell)->IsSubTotal()) { rErr = ((ScFormulaCell*)pCell)->GetErrCode(); if ( rErr || ((ScFormulaCell*)pCell)->IsValue() ) { rValue = ((ScFormulaCell*)pCell)->GetValue(); nRow--; bNumValid = FALSE; return TRUE; // gefunden } else if ( bTextAsZero ) { rValue = 0.0; nRow--; bNumValid = FALSE; return TRUE; } } } break; case CELLTYPE_STRING : case CELLTYPE_EDIT : { if ( bTextAsZero ) { rErr = 0; rValue = 0.0; nNumFmtType = NUMBERFORMAT_NUMBER; nNumFmtIndex = 0; bNumValid = TRUE; --nRow; return TRUE; } } break; default: { // added to avoid warnings } } } } else nRow = nEndRow + 1; // naechste Spalte } } void ScValueIterator::GetCurNumFmtInfo( short& nType, ULONG& nIndex ) { if (!bNumValid) { const ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; nNumFmtIndex = pCol->GetNumberFormat( nRow ); if ( (nNumFmtIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) { const ScBaseCell* pCell; SCSIZE nIdx = nColRow - 1; // there might be rearranged something, so be on the safe side if ( nIdx < pCol->nCount && pCol->pItems[nIdx].nRow == nRow ) pCell = pCol->pItems[nIdx].pCell; else { if ( pCol->Search( nRow, nIdx ) ) pCell = pCol->pItems[nIdx].pCell; else pCell = NULL; } if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA ) ((const ScFormulaCell*)pCell)->GetFormatInfo( nNumFmtType, nNumFmtIndex ); else nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex ); } else nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex ); bNumValid = TRUE; } nType = nNumFmtType; nIndex = nNumFmtIndex; } BOOL ScValueIterator::GetFirst(double& rValue, USHORT& rErr) { nCol = nStartCol; nRow = nStartRow; nTab = nStartTab; // nColRow = 0; ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; pCol->Search( nRow, nColRow ); nNumFormat = 0; // werden bei GetNumberFormat initialisiert pAttrArray = 0; nAttrEndRow = 0; return GetThis(rValue, rErr); } /* ist inline: BOOL ScValueIterator::GetNext(double& rValue, USHORT& rErr) { ++nRow; return GetThis(rValue, rErr); } */ // ============================================================================ ScDBQueryDataIterator::DataAccess::DataAccess(const ScDBQueryDataIterator* pParent) : mpParent(pParent) { } ScDBQueryDataIterator::DataAccess::~DataAccess() { } SCROW ScDBQueryDataIterator::GetRowByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow) { ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; return pCol->pItems[nColRow].nRow; } ScBaseCell* ScDBQueryDataIterator::GetCellByColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCSIZE nColRow) { ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; return pCol->pItems[nColRow].pCell; } ScAttrArray* ScDBQueryDataIterator::GetAttrArrayByCol(ScDocument& rDoc, SCTAB nTab, SCCOL nCol) { ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; return pCol->pAttrArray; } bool ScDBQueryDataIterator::IsQueryValid(ScDocument& rDoc, const ScQueryParam& rParam, SCTAB nTab, SCROW nRow, ScBaseCell* pCell) { return rDoc.pTab[nTab]->ValidQuery(nRow, rParam, NULL, pCell); } SCSIZE ScDBQueryDataIterator::SearchColEntryIndex(ScDocument& rDoc, SCTAB nTab, SCROW nRow, SCCOL nCol) { ScColumn* pCol = &rDoc.pTab[nTab]->aCol[nCol]; SCSIZE nColRow; pCol->Search(nRow, nColRow); return nColRow; } // ---------------------------------------------------------------------------- ScDBQueryDataIterator::DataAccessInternal::DataAccessInternal(const ScDBQueryDataIterator* pParent, ScDBQueryParamInternal* pParam, ScDocument* pDoc) : DataAccess(pParent), mpParam(pParam), mpDoc(pDoc) { nCol = mpParam->mnField; nRow = mpParam->nRow1; nTab = mpParam->nTab; nColRow = 0; // wird bei GetFirst initialisiert SCSIZE i; SCSIZE nCount = mpParam->GetEntryCount(); for (i=0; (iGetEntry(i).bDoQuery); i++) { ScQueryEntry& rEntry = mpParam->GetEntry(i); sal_uInt32 nIndex = 0; rEntry.bQueryByString = !(mpDoc->GetFormatTable()->IsNumberFormat(*rEntry.pStr, nIndex, rEntry.nVal)); } nNumFormat = 0; // werden bei GetNumberFormat initialisiert pAttrArray = 0; nAttrEndRow = 0; } ScDBQueryDataIterator::DataAccessInternal::~DataAccessInternal() { } bool ScDBQueryDataIterator::DataAccessInternal::getCurrent(Value& rValue) { SCCOLROW nFirstQueryField = mpParam->GetEntry(0).nField; for ( ;; ) { if (nRow > mpParam->nRow2) { // Bottom of the range reached. Bail out. rValue.mnError = 0; return false; } SCSIZE nCellCount = mpDoc->GetCellCount(nTab, nCol); SCROW nThisRow = ScDBQueryDataIterator::GetRowByColEntryIndex(*mpDoc, nTab, nCol, nColRow); while ( (nColRow < nCellCount) && (nThisRow < nRow) ) nThisRow = ScDBQueryDataIterator::GetRowByColEntryIndex(*mpDoc, nTab, nCol, ++nColRow); if ( nColRow < nCellCount && nThisRow <= mpParam->nRow2 ) { nRow = nThisRow; ScBaseCell* pCell = NULL; if (nCol == static_cast(nFirstQueryField)) pCell = ScDBQueryDataIterator::GetCellByColEntryIndex(*mpDoc, nTab, nCol, nColRow); if (ScDBQueryDataIterator::IsQueryValid(*mpDoc, *mpParam, nTab, nRow, pCell)) { // #i109812# get cell here if it wasn't done above if (nCol != static_cast(nFirstQueryField)) pCell = ScDBQueryDataIterator::GetCellByColEntryIndex(*mpDoc, nTab, nCol, nColRow); switch (pCell ? pCell->GetCellType() : CELLTYPE_NONE) { case CELLTYPE_VALUE: { rValue.mfValue = ((ScValueCell*)pCell)->GetValue(); rValue.mbIsNumber = true; if ( bCalcAsShown ) { const ScAttrArray* pNewAttrArray = ScDBQueryDataIterator::GetAttrArrayByCol(*mpDoc, nTab, nCol); lcl_IterGetNumberFormat( nNumFormat, pAttrArray, nAttrEndRow, pNewAttrArray, nRow, mpDoc ); rValue.mfValue = mpDoc->RoundValueAsShown( rValue.mfValue, nNumFormat ); } nNumFmtType = NUMBERFORMAT_NUMBER; nNumFmtIndex = 0; rValue.mnError = 0; return TRUE; // gefunden } // break; case CELLTYPE_FORMULA: { if (((ScFormulaCell*)pCell)->IsValue()) { rValue.mfValue = ((ScFormulaCell*)pCell)->GetValue(); rValue.mbIsNumber = true; mpDoc->GetNumberFormatInfo( nNumFmtType, nNumFmtIndex, ScAddress( nCol, nRow, nTab ), pCell ); rValue.mnError = ((ScFormulaCell*)pCell)->GetErrCode(); return TRUE; // gefunden } else nRow++; } break; case CELLTYPE_STRING: case CELLTYPE_EDIT: if (mpParam->mbSkipString) ++nRow; else { rValue.maString = pCell->GetStringData(); rValue.mfValue = 0.0; rValue.mnError = 0; rValue.mbIsNumber = false; return true; } break; default: nRow++; break; } } else nRow++; } else nRow = mpParam->nRow2 + 1; // Naechste Spalte } // statement unreachable // return false; } bool ScDBQueryDataIterator::DataAccessInternal::getFirst(Value& rValue) { if (mpParam->bHasHeader) nRow++; nColRow = ScDBQueryDataIterator::SearchColEntryIndex(*mpDoc, nTab, nRow, nCol); return getCurrent(rValue); } bool ScDBQueryDataIterator::DataAccessInternal::getNext(Value& rValue) { ++nRow; return getCurrent(rValue); } // ---------------------------------------------------------------------------- ScDBQueryDataIterator::DataAccessMatrix::DataAccessMatrix(const ScDBQueryDataIterator* pParent, ScDBQueryParamMatrix* pParam) : DataAccess(pParent), mpParam(pParam) { SCSIZE nC, nR; mpParam->mpMatrix->GetDimensions(nC, nR); mnRows = static_cast(nR); mnCols = static_cast(nC); } ScDBQueryDataIterator::DataAccessMatrix::~DataAccessMatrix() { } bool ScDBQueryDataIterator::DataAccessMatrix::getCurrent(Value& rValue) { // Starting from row == mnCurRow, get the first row that satisfies all the // query parameters. for ( ;mnCurRow < mnRows; ++mnCurRow) { const ScMatrix& rMat = *mpParam->mpMatrix; if (rMat.IsEmpty(mpParam->mnField, mnCurRow)) // Don't take empty values into account. continue; bool bIsStrVal = rMat.IsString(mpParam->mnField, mnCurRow); if (bIsStrVal && mpParam->mbSkipString) continue; if (isValidQuery(mnCurRow, rMat)) { rValue.maString = rMat.GetString(mpParam->mnField, mnCurRow); rValue.mfValue = rMat.GetDouble(mpParam->mnField, mnCurRow); rValue.mbIsNumber = !bIsStrVal; rValue.mnError = 0; return true; } } return false; } bool ScDBQueryDataIterator::DataAccessMatrix::getFirst(Value& rValue) { mnCurRow = mpParam->bHasHeader ? 1 : 0; return getCurrent(rValue); } bool ScDBQueryDataIterator::DataAccessMatrix::getNext(Value& rValue) { ++mnCurRow; return getCurrent(rValue); } namespace { bool lcl_isQueryByValue(const ScQueryEntry& rEntry, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow) { if (rEntry.bQueryByString) return false; if (!rMat.IsValueOrEmpty(nCol, nRow)) return false; return true; } bool lcl_isQueryByString(const ScQueryEntry& rEntry, const ScMatrix& rMat, SCSIZE nCol, SCSIZE nRow) { switch (rEntry.eOp) { case SC_EQUAL: case SC_NOT_EQUAL: case SC_CONTAINS: case SC_DOES_NOT_CONTAIN: case SC_BEGINS_WITH: case SC_ENDS_WITH: case SC_DOES_NOT_BEGIN_WITH: case SC_DOES_NOT_END_WITH: return true; default: ; } if (rEntry.bQueryByString && rMat.IsString(nCol, nRow)) return true; return false; } } bool ScDBQueryDataIterator::DataAccessMatrix::isValidQuery(SCROW nRow, const ScMatrix& rMat) const { SCSIZE nEntryCount = mpParam->GetEntryCount(); vector aResults; aResults.reserve(nEntryCount); const CollatorWrapper& rCollator = mpParam->bCaseSens ? *ScGlobal::GetCaseCollator() : *ScGlobal::GetCollator(); for (SCSIZE i = 0; i < nEntryCount; ++i) { const ScQueryEntry& rEntry = mpParam->GetEntry(i); if (!rEntry.bDoQuery) continue; switch (rEntry.eOp) { case SC_EQUAL: case SC_LESS: case SC_GREATER: case SC_LESS_EQUAL: case SC_GREATER_EQUAL: case SC_NOT_EQUAL: break; default: // Only the above operators are supported. continue; } bool bValid = false; SCSIZE nField = static_cast(rEntry.nField); if (lcl_isQueryByValue(rEntry, rMat, nField, nRow)) { // By value double fMatVal = rMat.GetDouble(nField, nRow); bool bEqual = approxEqual(fMatVal, rEntry.nVal); switch (rEntry.eOp) { case SC_EQUAL: bValid = bEqual; break; case SC_LESS: bValid = (fMatVal < rEntry.nVal) && !bEqual; break; case SC_GREATER: bValid = (fMatVal > rEntry.nVal) && !bEqual; break; case SC_LESS_EQUAL: bValid = (fMatVal < rEntry.nVal) || bEqual; break; case SC_GREATER_EQUAL: bValid = (fMatVal > rEntry.nVal) || bEqual; break; case SC_NOT_EQUAL: bValid = !bEqual; break; default: ; } } else if (lcl_isQueryByString(rEntry, rMat, nField, nRow)) { // By string do { if (!rEntry.pStr) break; // Equality check first. OUString aMatStr = rMat.GetString(nField, nRow); lcl_toUpper(aMatStr); OUString aQueryStr = *rEntry.pStr; lcl_toUpper(aQueryStr); bool bDone = false; switch (rEntry.eOp) { case SC_EQUAL: bValid = aMatStr.equals(aQueryStr); bDone = true; break; case SC_NOT_EQUAL: bValid = !aMatStr.equals(aQueryStr); bDone = true; break; default: ; } if (bDone) break; // Unequality check using collator. sal_Int32 nCompare = rCollator.compareString(aMatStr, aQueryStr); switch (rEntry.eOp) { case SC_LESS : bValid = (nCompare < 0); break; case SC_GREATER : bValid = (nCompare > 0); break; case SC_LESS_EQUAL : bValid = (nCompare <= 0); break; case SC_GREATER_EQUAL : bValid = (nCompare >= 0); break; default: ; } } while (false); } else if (mpParam->bMixedComparison) { // Not used at the moment. } if (aResults.empty()) // First query entry. aResults.push_back(bValid); else if (rEntry.eConnect == SC_AND) { // For AND op, tuck the result into the last result value. size_t n = aResults.size(); aResults[n-1] = aResults[n-1] && bValid; } else // For OR op, store its own result. aResults.push_back(bValid); } // Row is valid as long as there is at least one result being true. vector::const_iterator itr = aResults.begin(), itrEnd = aResults.end(); for (; itr != itrEnd; ++itr) if (*itr) return true; return false; } // ---------------------------------------------------------------------------- ScDBQueryDataIterator::Value::Value() : mnError(0), mbIsNumber(true) { ::rtl::math::setNan(&mfValue); } // ---------------------------------------------------------------------------- ScDBQueryDataIterator::ScDBQueryDataIterator(ScDocument* pDocument, ScDBQueryParamBase* pParam) : mpParam (pParam) { switch (mpParam->GetType()) { case ScDBQueryParamBase::INTERNAL: { ScDBQueryParamInternal* p = static_cast(pParam); mpData.reset(new DataAccessInternal(this, p, pDocument)); } break; case ScDBQueryParamBase::MATRIX: { ScDBQueryParamMatrix* p = static_cast(pParam); mpData.reset(new DataAccessMatrix(this, p)); } } } bool ScDBQueryDataIterator::GetThis(Value& rValue) { return mpData->getCurrent(rValue); } bool ScDBQueryDataIterator::GetFirst(Value& rValue) { return mpData->getFirst(rValue); } bool ScDBQueryDataIterator::GetNext(Value& rValue) { return mpData->getNext(rValue); } // ============================================================================ ScCellIterator::ScCellIterator( ScDocument* pDocument, SCCOL nSCol, SCROW nSRow, SCTAB nSTab, SCCOL nECol, SCROW nERow, SCTAB nETab, BOOL bSTotal ) : pDoc( pDocument ), nStartCol( nSCol), nStartRow( nSRow), nStartTab( nSTab ), nEndCol( nECol ), nEndRow( nERow), nEndTab( nETab ), bSubTotal(bSTotal) { PutInOrder( nStartCol, nEndCol); PutInOrder( nStartRow, nEndRow); PutInOrder( nStartTab, nEndTab ); if (!ValidCol(nStartCol)) nStartCol = MAXCOL; if (!ValidCol(nEndCol)) nEndCol = MAXCOL; if (!ValidRow(nStartRow)) nStartRow = MAXROW; if (!ValidRow(nEndRow)) nEndRow = MAXROW; if (!ValidTab(nStartTab)) nStartTab = MAXTAB; if (!ValidTab(nEndTab)) nEndTab = MAXTAB; while (nEndTab>0 && !pDoc->pTab[nEndTab]) --nEndTab; // nur benutzte Tabellen if (nStartTab>nEndTab) nStartTab = nEndTab; nCol = nStartCol; nRow = nStartRow; nTab = nStartTab; nColRow = 0; // wird bei GetFirst initialisiert if (!pDoc->pTab[nTab]) { DBG_ERROR("Tabelle nicht gefunden"); nStartCol = nCol = MAXCOL+1; nStartRow = nRow = MAXROW+1; nStartTab = nTab = MAXTAB+1; // -> Abbruch bei GetFirst } } ScCellIterator::ScCellIterator ( ScDocument* pDocument, const ScRange& rRange, BOOL bSTotal ) : pDoc( pDocument ), nStartCol( rRange.aStart.Col() ), nStartRow( rRange.aStart.Row() ), nStartTab( rRange.aStart.Tab() ), nEndCol( rRange.aEnd.Col() ), nEndRow( rRange.aEnd.Row() ), nEndTab( rRange.aEnd.Tab() ), bSubTotal(bSTotal) { PutInOrder( nStartCol, nEndCol); PutInOrder( nStartRow, nEndRow); PutInOrder( nStartTab, nEndTab ); if (!ValidCol(nStartCol)) nStartCol = MAXCOL; if (!ValidCol(nEndCol)) nEndCol = MAXCOL; if (!ValidRow(nStartRow)) nStartRow = MAXROW; if (!ValidRow(nEndRow)) nEndRow = MAXROW; if (!ValidTab(nStartTab)) nStartTab = MAXTAB; if (!ValidTab(nEndTab)) nEndTab = MAXTAB; while (nEndTab>0 && !pDoc->pTab[nEndTab]) --nEndTab; // nur benutzte Tabellen if (nStartTab>nEndTab) nStartTab = nEndTab; nCol = nStartCol; nRow = nStartRow; nTab = nStartTab; nColRow = 0; // wird bei GetFirst initialisiert if (!pDoc->pTab[nTab]) { DBG_ERROR("Tabelle nicht gefunden"); nStartCol = nCol = MAXCOL+1; nStartRow = nRow = MAXROW+1; nStartTab = nTab = MAXTAB+1; // -> Abbruch bei GetFirst } } ScBaseCell* ScCellIterator::GetThis() { ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; for ( ;; ) { if ( nRow > nEndRow ) { nRow = nStartRow; do { nCol++; if ( nCol > nEndCol ) { nCol = nStartCol; nTab++; if ( nTab > nEndTab ) return NULL; // Ende und Aus } pCol = &(pDoc->pTab[nTab])->aCol[nCol]; } while ( pCol->nCount == 0 ); pCol->Search( nRow, nColRow ); } while ( (nColRow < pCol->nCount) && (pCol->pItems[nColRow].nRow < nRow) ) nColRow++; if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= nEndRow ) { nRow = pCol->pItems[nColRow].nRow; if ( !bSubTotal || !pDoc->pTab[nTab]->IsFiltered( nRow ) ) { ScBaseCell* pCell = pCol->pItems[nColRow].pCell; if ( bSubTotal && pCell->GetCellType() == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell)->IsSubTotal() ) nRow++; // Sub-Total-Zeilen nicht else return pCell; // gefunden } else nRow++; } else nRow = nEndRow + 1; // Naechste Spalte } } ScBaseCell* ScCellIterator::GetFirst() { if ( !ValidTab(nTab) ) return NULL; nCol = nStartCol; nRow = nStartRow; nTab = nStartTab; // nColRow = 0; ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; pCol->Search( nRow, nColRow ); return GetThis(); } ScBaseCell* ScCellIterator::GetNext() { ++nRow; return GetThis(); } //------------------------------------------------------------------------------- ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable, const ScQueryParam& rParam, BOOL bMod ) : aParam (rParam), pDoc( pDocument ), nTab( nTable), nStopOnMismatch( nStopOnMismatchDisabled ), nTestEqualCondition( nTestEqualConditionDisabled ), bAdvanceQuery( FALSE ), bIgnoreMismatchOnLeadingStrings( FALSE ) { nCol = aParam.nCol1; nRow = aParam.nRow1; nColRow = 0; // wird bei GetFirst initialisiert SCSIZE i; if (bMod) // sonst schon eingetragen { for (i=0; (iGetFormatTable()->IsNumberFormat(*rEntry.pStr, nIndex, rEntry.nVal)); } } nNumFormat = 0; // werden bei GetNumberFormat initialisiert pAttrArray = 0; nAttrEndRow = 0; } ScBaseCell* ScQueryCellIterator::GetThis() { ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; const ScQueryEntry& rEntry = aParam.GetEntry(0); SCCOLROW nFirstQueryField = rEntry.nField; bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && !rEntry.bQueryByString; bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && !aParam.bHasHeader && rEntry.bQueryByString && ((aParam.bByRow && nRow == aParam.nRow1) || (!aParam.bByRow && nCol == aParam.nCol1)); for ( ;; ) { if ( nRow > aParam.nRow2 ) { nRow = aParam.nRow1; if (aParam.bHasHeader && aParam.bByRow) nRow++; do { if ( ++nCol > aParam.nCol2 ) return NULL; // Ende und Aus if ( bAdvanceQuery ) { AdvanceQueryParamEntryField(); nFirstQueryField = rEntry.nField; } pCol = &(pDoc->pTab[nTab])->aCol[nCol]; } while ( pCol->nCount == 0 ); pCol->Search( nRow, nColRow ); bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && !aParam.bHasHeader && rEntry.bQueryByString && aParam.bByRow; } while ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow < nRow ) nColRow++; if ( nColRow < pCol->nCount && (nRow = pCol->pItems[nColRow].nRow) <= aParam.nRow2 ) { ScBaseCell* pCell = pCol->pItems[nColRow].pCell; if ( pCell->GetCellType() == CELLTYPE_NOTE ) ++nRow; else if (bAllStringIgnore && pCell->HasStringData()) ++nRow; else { BOOL bTestEqualCondition; if ( (pDoc->pTab[nTab])->ValidQuery( nRow, aParam, NULL, (nCol == static_cast(nFirstQueryField) ? pCell : NULL), (nTestEqualCondition ? &bTestEqualCondition : NULL) ) ) { if ( nTestEqualCondition && bTestEqualCondition ) nTestEqualCondition |= nTestEqualConditionMatched; return pCell; // found } else if ( nStopOnMismatch ) { // Yes, even a mismatch may have a fulfilled equal // condition if regular expressions were involved and // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried. if ( nTestEqualCondition && bTestEqualCondition ) { nTestEqualCondition |= nTestEqualConditionMatched; nStopOnMismatch |= nStopOnMismatchOccured; return NULL; } bool bStop; if (bFirstStringIgnore) { if (pCell->HasStringData()) { ++nRow; bStop = false; } else bStop = true; } else bStop = true; if (bStop) { nStopOnMismatch |= nStopOnMismatchOccured; return NULL; } } else nRow++; } } else nRow = aParam.nRow2 + 1; // Naechste Spalte bFirstStringIgnore = false; } } ScBaseCell* ScQueryCellIterator::GetFirst() { nCol = aParam.nCol1; nRow = aParam.nRow1; if (aParam.bHasHeader) nRow++; // nColRow = 0; ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; pCol->Search( nRow, nColRow ); return GetThis(); } ScBaseCell* ScQueryCellIterator::GetNext() { ++nRow; if ( nStopOnMismatch ) nStopOnMismatch = nStopOnMismatchEnabled; if ( nTestEqualCondition ) nTestEqualCondition = nTestEqualConditionEnabled; return GetThis(); } ULONG ScQueryCellIterator::GetNumberFormat() { ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; lcl_IterGetNumberFormat( nNumFormat, pAttrArray, nAttrEndRow, pCol->pAttrArray, nRow, pDoc ); return nNumFormat; } void ScQueryCellIterator::AdvanceQueryParamEntryField() { SCSIZE nEntries = aParam.GetEntryCount(); for ( SCSIZE j = 0; j < nEntries; j++ ) { ScQueryEntry& rEntry = aParam.GetEntry( j ); if ( rEntry.bDoQuery ) { if ( rEntry.nField < MAXCOL ) rEntry.nField++; else { DBG_ERRORFILE( "AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL" ); } } else break; // for } } BOOL ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol, SCROW& nFoundRow, BOOL bSearchForEqualAfterMismatch, BOOL bIgnoreMismatchOnLeadingStringsP ) { nFoundCol = MAXCOL+1; nFoundRow = MAXROW+1; SetStopOnMismatch( TRUE ); // assume sorted keys SetTestEqualCondition( TRUE ); bIgnoreMismatchOnLeadingStrings = bIgnoreMismatchOnLeadingStringsP; bool bRegExp = aParam.bRegExp && aParam.GetEntry(0).bQueryByString; bool bBinary = !bRegExp && aParam.bByRow && (aParam.GetEntry(0).eOp == SC_LESS_EQUAL || aParam.GetEntry(0).eOp == SC_GREATER_EQUAL); if (bBinary ? (BinarySearch() ? GetThis() : 0) : GetFirst()) { // First equal entry or last smaller than (greater than) entry. SCSIZE nColRowSave; ScBaseCell* pNext = 0; do { nFoundCol = GetCol(); nFoundRow = GetRow(); nColRowSave = nColRow; } while ( !IsEqualConditionFulfilled() && (pNext = GetNext()) != NULL ); // There may be no pNext but equal condition fulfilled if regular // expressions are involved. Keep the found entry and proceed. if (!pNext && !IsEqualConditionFulfilled()) { // Step back to last in range and adjust position markers for // GetNumberFormat() or similar. nCol = nFoundCol; nRow = nFoundRow; nColRow = nColRowSave; } } if ( IsEqualConditionFulfilled() ) { // Position on last equal entry. SCSIZE nEntries = aParam.GetEntryCount(); for ( SCSIZE j = 0; j < nEntries; j++ ) { ScQueryEntry& rEntry = aParam.GetEntry( j ); if ( rEntry.bDoQuery ) { switch ( rEntry.eOp ) { case SC_LESS_EQUAL : case SC_GREATER_EQUAL : rEntry.eOp = SC_EQUAL; break; default: { // added to avoid warnings } } } else break; // for } SCSIZE nColRowSave; bIgnoreMismatchOnLeadingStrings = FALSE; SetTestEqualCondition( FALSE ); do { nFoundCol = GetCol(); nFoundRow = GetRow(); nColRowSave = nColRow; } while (GetNext()); // Step back conditions same as above nCol = nFoundCol; nRow = nFoundRow; nColRow = nColRowSave; return TRUE; } if ( (bSearchForEqualAfterMismatch || aParam.bRegExp) && StoppedOnMismatch() ) { // Assume found entry to be the last value less than respectively // greater than the query. But keep on searching for an equal match. SCSIZE nEntries = aParam.GetEntryCount(); for ( SCSIZE j = 0; j < nEntries; j++ ) { ScQueryEntry& rEntry = aParam.GetEntry( j ); if ( rEntry.bDoQuery ) { switch ( rEntry.eOp ) { case SC_LESS_EQUAL : case SC_GREATER_EQUAL : rEntry.eOp = SC_EQUAL; break; default: { // added to avoid warnings } } } else break; // for } SetStopOnMismatch( FALSE ); SetTestEqualCondition( FALSE ); if (GetNext()) { // Last of a consecutive area, avoid searching the entire parameter // range as it is a real performance bottleneck in case of regular // expressions. SCSIZE nColRowSave; do { nFoundCol = GetCol(); nFoundRow = GetRow(); nColRowSave = nColRow; SetStopOnMismatch( TRUE ); } while (GetNext()); nCol = nFoundCol; nRow = nFoundRow; nColRow = nColRowSave; } } return (nFoundCol <= MAXCOL) && (nFoundRow <= MAXROW); } ScBaseCell* ScQueryCellIterator::BinarySearch() { nCol = aParam.nCol1; ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; if (!pCol->nCount) return 0; ScBaseCell* pCell; SCSIZE nHi, nLo; CollatorWrapper* pCollator = (aParam.bCaseSens ? ScGlobal::GetCaseCollator() : ScGlobal::GetCollator()); SvNumberFormatter& rFormatter = *(pDoc->GetFormatTable()); const ScQueryEntry& rEntry = aParam.GetEntry(0); bool bLessEqual = rEntry.eOp == SC_LESS_EQUAL; bool bByString = rEntry.bQueryByString; bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && !bByString; bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && !aParam.bHasHeader && bByString; nRow = aParam.nRow1; if (aParam.bHasHeader) nRow++; const ColEntry* pItems = pCol->pItems; if (pCol->Search( nRow, nLo ) && bFirstStringIgnore && pItems[nLo].pCell->HasStringData()) { String aCellStr; ULONG nFormat = pCol->GetNumberFormat( pItems[nLo].nRow); ScCellFormat::GetInputString( pItems[nLo].pCell, nFormat, aCellStr, rFormatter); sal_Int32 nTmp = pCollator->compareString( aCellStr, *rEntry.pStr); if ((rEntry.eOp == SC_LESS_EQUAL && nTmp > 0) || (rEntry.eOp == SC_GREATER_EQUAL && nTmp < 0) || (rEntry.eOp == SC_EQUAL && nTmp != 0)) ++nLo; } if (!pCol->Search( aParam.nRow2, nHi ) && nHi>0) --nHi; while (bAllStringIgnore && nLo <= nHi && nLo < pCol->nCount && pItems[nLo].pCell->HasStringData()) ++nLo; // Bookkeeping values for breaking up the binary search in case the data // range isn't strictly sorted. SCSIZE nLastInRange = nLo; SCSIZE nFirstLastInRange = nLastInRange; double fLastInRangeValue = bLessEqual ? -(::std::numeric_limits::max()) : ::std::numeric_limits::max(); String aLastInRangeString; if (!bLessEqual) aLastInRangeString.Assign( sal_Unicode(0xFFFF)); if (nLastInRange < pCol->nCount) { pCell = pItems[nLastInRange].pCell; if (pCell->HasStringData()) { ULONG nFormat = pCol->GetNumberFormat( pItems[nLastInRange].nRow); ScCellFormat::GetInputString( pCell, nFormat, aLastInRangeString, rFormatter); } else { switch ( pCell->GetCellType() ) { case CELLTYPE_VALUE : fLastInRangeValue = static_cast(pCell)->GetValue(); break; case CELLTYPE_FORMULA : fLastInRangeValue = static_cast(pCell)->GetValue(); break; default: { // added to avoid warnings } } } } sal_Int32 nRes = 0; bool bFound = false; bool bDone = false; while (nLo <= nHi && !bDone) { SCSIZE nMid = (nLo+nHi)/2; SCSIZE i = nMid; while (i <= nHi && pItems[i].pCell->GetCellType() == CELLTYPE_NOTE) ++i; if (i > nHi) { if (nMid > 0) nHi = nMid - 1; else bDone = true; continue; // while } BOOL bStr = pItems[i].pCell->HasStringData(); nRes = 0; // compares are contentquery:1 // Cell value comparison similar to ScTable::ValidQuery() if (!bStr && !bByString) { double nCellVal; pCell = pItems[i].pCell; switch ( pCell->GetCellType() ) { case CELLTYPE_VALUE : nCellVal = static_cast(pCell)->GetValue(); break; case CELLTYPE_FORMULA : nCellVal = static_cast(pCell)->GetValue(); break; default: nCellVal = 0.0; } if ((nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal)) { nRes = -1; if (bLessEqual) { if (fLastInRangeValue < nCellVal) { fLastInRangeValue = nCellVal; nLastInRange = i; } else if (fLastInRangeValue > nCellVal) { // not strictly sorted, continue with GetThis() nLastInRange = nFirstLastInRange; bDone = true; } } } else if ((nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal)) { nRes = 1; if (!bLessEqual) { if (fLastInRangeValue > nCellVal) { fLastInRangeValue = nCellVal; nLastInRange = i; } else if (fLastInRangeValue < nCellVal) { // not strictly sorted, continue with GetThis() nLastInRange = nFirstLastInRange; bDone = true; } } } } else if (bStr && bByString) { String aCellStr; ULONG nFormat = pCol->GetNumberFormat( pItems[i].nRow); ScCellFormat::GetInputString( pItems[i].pCell, nFormat, aCellStr, rFormatter); nRes = pCollator->compareString( aCellStr, *rEntry.pStr); if (nRes < 0 && bLessEqual) { sal_Int32 nTmp = pCollator->compareString( aLastInRangeString, aCellStr); if (nTmp < 0) { aLastInRangeString = aCellStr; nLastInRange = i; } else if (nTmp > 0) { // not strictly sorted, continue with GetThis() nLastInRange = nFirstLastInRange; bDone = true; } } else if (nRes > 0 && !bLessEqual) { sal_Int32 nTmp = pCollator->compareString( aLastInRangeString, aCellStr); if (nTmp > 0) { aLastInRangeString = aCellStr; nLastInRange = i; } else if (nTmp < 0) { // not strictly sorted, continue with GetThis() nLastInRange = nFirstLastInRange; bDone = true; } } } else if (!bStr && bByString) { nRes = -1; // numeric < string if (bLessEqual) nLastInRange = i; } else // if (bStr && !bByString) { nRes = 1; // string > numeric if (!bLessEqual) nLastInRange = i; } if (nRes < 0) { if (bLessEqual) nLo = nMid + 1; else // assumed to be SC_GREATER_EQUAL { if (nMid > 0) nHi = nMid - 1; else bDone = true; } } else if (nRes > 0) { if (bLessEqual) { if (nMid > 0) nHi = nMid - 1; else bDone = true; } else // assumed to be SC_GREATER_EQUAL nLo = nMid + 1; } else { nLo = i; bDone = bFound = true; } } if (!bFound) { // If all hits didn't result in a moving limit there's something // strange, e.g. data range not properly sorted, or only identical // values encountered, which doesn't mean there aren't any others in // between.. leave it to GetThis(). The condition for this would be // if (nLastInRange == nFirstLastInRange) nLo = nFirstLastInRange; // Else, in case no exact match was found, we step back for a // subsequent GetThis() to find the last in range. Effectively this is // --nLo with nLastInRange == nLo-1. Both conditions combined yield: nLo = nLastInRange; } if (nLo < pCol->nCount && pCol->pItems[nLo].nRow <= aParam.nRow2) { nRow = pItems[nLo].nRow; pCell = pItems[nLo].pCell; nColRow = nLo; } else { nRow = aParam.nRow2 + 1; pCell = 0; nColRow = pCol->nCount - 1; } return pCell; } //------------------------------------------------------------------------------- ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : pDoc( pDocument ), nTab( nTable ), nStartCol( nCol1 ), nEndCol( nCol2 ), nEndRow( nRow2 ), nCol( nCol1 ), nRow( nRow1 ), bMore( TRUE ) { SCCOL i; SCSIZE nIndex; pNextRows = new SCROW[ nCol2-nCol1+1 ]; pNextIndices = new SCSIZE[ nCol2-nCol1+1 ]; for (i=nStartCol; i<=nEndCol; i++) { ScColumn* pCol = &pDoc->pTab[nTab]->aCol[i]; pCol->Search( nRow1, nIndex ); if ( nIndex < pCol->nCount ) { pNextRows[i-nStartCol] = pCol->pItems[nIndex].nRow; pNextIndices[i-nStartCol] = nIndex; } else { pNextRows[i-nStartCol] = MAXROWCOUNT; // nichts gefunden pNextIndices[i-nStartCol] = MAXROWCOUNT; } } if (pNextRows[0] != nRow1) Advance(); } ScHorizontalCellIterator::~ScHorizontalCellIterator() { delete [] pNextRows; delete [] pNextIndices; } ScBaseCell* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow ) { if ( bMore ) { rCol = nCol; rRow = nRow; ScColumn* pCol = &pDoc->pTab[nTab]->aCol[nCol]; SCSIZE nIndex = pNextIndices[nCol-nStartCol]; DBG_ASSERT( nIndex < pCol->nCount, "ScHorizontalCellIterator::GetNext: nIndex out of range" ); ScBaseCell* pCell = pCol->pItems[nIndex].pCell; if ( ++nIndex < pCol->nCount ) { pNextRows[nCol-nStartCol] = pCol->pItems[nIndex].nRow; pNextIndices[nCol-nStartCol] = nIndex; } else { pNextRows[nCol-nStartCol] = MAXROWCOUNT; // nichts gefunden pNextIndices[nCol-nStartCol] = MAXROWCOUNT; } Advance(); return pCell; } else return NULL; } BOOL ScHorizontalCellIterator::ReturnNext( SCCOL& rCol, SCROW& rRow ) { rCol = nCol; rRow = nRow; return bMore; } void ScHorizontalCellIterator::Advance() { BOOL bFound = FALSE; SCCOL i; for (i=nCol+1; i<=nEndCol && !bFound; i++) if (pNextRows[i-nStartCol] == nRow) { nCol = i; bFound = TRUE; } if (!bFound) { SCROW nMinRow = MAXROW+1; for (i=nStartCol; i<=nEndCol; i++) if (pNextRows[i-nStartCol] < nMinRow) { nCol = i; nMinRow = pNextRows[i-nStartCol]; } if (nMinRow <= nEndRow) { nRow = nMinRow; bFound = TRUE; } } if ( !bFound ) bMore = FALSE; } //------------------------------------------------------------------------------- ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument* pDocument, SCTAB nTable, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : pDoc( pDocument ), nTab( nTable ), nStartCol( nCol1 ), nStartRow( nRow1 ), nEndCol( nCol2 ), nEndRow( nRow2 ) { DBG_ASSERT( pDoc->pTab[nTab], "Tabelle nicht da" ); SCCOL i; nRow = nStartRow; nCol = nStartCol; bRowEmpty = FALSE; pIndices = new SCSIZE[nEndCol-nStartCol+1]; pNextEnd = new SCROW[nEndCol-nStartCol+1]; ppPatterns = new const ScPatternAttr*[nEndCol-nStartCol+1]; SCROW nSkipTo = MAXROW; BOOL bEmpty = TRUE; for (i=nStartCol; i<=nEndCol; i++) { SCCOL nPos = i - nStartCol; ScAttrArray* pArray = pDoc->pTab[nTab]->aCol[i].pAttrArray; DBG_ASSERT( pArray, "pArray == 0" ); SCSIZE nIndex; pArray->Search( nStartRow, nIndex ); const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern; SCROW nThisEnd = pArray->pData[nIndex].nRow; if ( IsDefaultItem( pPattern ) ) { pPattern = NULL; if ( nThisEnd < nSkipTo ) nSkipTo = nThisEnd; // nSkipTo kann gleich hier gesetzt werden } else bEmpty = FALSE; // Attribute gefunden pIndices[nPos] = nIndex; pNextEnd[nPos] = nThisEnd; ppPatterns[nPos] = pPattern; } if (bEmpty) nRow = nSkipTo; // bis zum naechsten Bereichsende ueberspringen bRowEmpty = bEmpty; } ScHorizontalAttrIterator::~ScHorizontalAttrIterator() { delete[] (ScPatternAttr**)ppPatterns; delete[] pNextEnd; delete[] pIndices; } const ScPatternAttr* ScHorizontalAttrIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, SCROW& rRow ) { for (;;) { if (!bRowEmpty) { // in dieser Zeile suchen while ( nCol <= nEndCol && !ppPatterns[nCol-nStartCol] ) ++nCol; if ( nCol <= nEndCol ) { const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol]; rRow = nRow; rCol1 = nCol; while ( nCol < nEndCol && ppPatterns[nCol+1-nStartCol] == pPat ) ++nCol; rCol2 = nCol; ++nCol; // hochzaehlen fuer naechsten Aufruf return pPat; // gefunden } } // naechste Zeile ++nRow; if ( nRow > nEndRow ) // schon am Ende? return NULL; // nichts gefunden BOOL bEmpty = TRUE; SCCOL i; for ( i = nStartCol; i <= nEndCol; i++) { SCCOL nPos = i-nStartCol; if ( pNextEnd[nPos] < nRow ) { ScAttrArray* pArray = pDoc->pTab[nTab]->aCol[i].pAttrArray; SCSIZE nIndex = ++pIndices[nPos]; if ( nIndex < pArray->nCount ) { const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern; SCROW nThisEnd = pArray->pData[nIndex].nRow; if ( IsDefaultItem( pPattern ) ) pPattern = NULL; else bEmpty = FALSE; // Attribute gefunden pNextEnd[nPos] = nThisEnd; ppPatterns[nPos] = pPattern; DBG_ASSERT( pNextEnd[nPos] >= nRow, "Reihenfolge durcheinander" ); } else { DBG_ERROR("AttrArray reicht nicht bis MAXROW"); pNextEnd[nPos] = MAXROW; ppPatterns[nPos] = NULL; } } else if ( ppPatterns[nPos] ) bEmpty = FALSE; // Bereich noch nicht zuende } if (bEmpty) { SCCOL nCount = nEndCol-nStartCol+1; SCROW nSkipTo = pNextEnd[0]; // naechstes Bereichsende suchen for (i=1; i nRow2 ) || ( nRow1 == nRow2 && nCol1 > nCol2 ); } ScUsedAreaIterator::ScUsedAreaIterator( ScDocument* pDocument, SCTAB nTable, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : aCellIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 ), aAttrIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 ), nNextCol( nCol1 ), nNextRow( nRow1 ) { pCell = aCellIter.GetNext( nCellCol, nCellRow ); pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow ); } ScUsedAreaIterator::~ScUsedAreaIterator() { } BOOL ScUsedAreaIterator::GetNext() { // Iteratoren weiterzaehlen if ( pCell && IsGreater( nNextCol, nNextRow, nCellCol, nCellRow ) ) pCell = aCellIter.GetNext( nCellCol, nCellRow ); while ( pCell && pCell->IsBlank() ) pCell = aCellIter.GetNext( nCellCol, nCellRow ); if ( pPattern && IsGreater( nNextCol, nNextRow, nAttrCol2, nAttrRow ) ) pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow ); if ( pPattern && nAttrRow == nNextRow && nAttrCol1 < nNextCol ) nAttrCol1 = nNextCol; // naechsten Abschnitt heraussuchen BOOL bFound = TRUE; BOOL bUseCell = FALSE; if ( pCell && pPattern ) { if ( IsGreater( nCellCol, nCellRow, nAttrCol1, nAttrRow ) ) // vorne nur Attribute ? { pFoundCell = NULL; pFoundPattern = pPattern; nFoundRow = nAttrRow; nFoundStartCol = nAttrCol1; if ( nCellRow == nAttrRow && nCellCol <= nAttrCol2 ) // auch Zelle im Bereich ? nFoundEndCol = nCellCol - 1; // nur bis vor der Zelle else nFoundEndCol = nAttrCol2; // alles } else { bUseCell = TRUE; if ( nAttrRow == nCellRow && nAttrCol1 == nCellCol ) // Attribute auf der Zelle ? pFoundPattern = pPattern; else pFoundPattern = NULL; } } else if ( pCell ) // nur Zelle -> direkt uebernehmen { pFoundPattern = NULL; bUseCell = TRUE; // Position von Zelle } else if ( pPattern ) // nur Attribute -> direkt uebernehmen { pFoundCell = NULL; pFoundPattern = pPattern; nFoundRow = nAttrRow; nFoundStartCol = nAttrCol1; nFoundEndCol = nAttrCol2; } else // gar nichts bFound = FALSE; if ( bUseCell ) // Position von Zelle { pFoundCell = pCell; nFoundRow = nCellRow; nFoundStartCol = nFoundEndCol = nCellCol; } if (bFound) { nNextRow = nFoundRow; nNextCol = nFoundEndCol + 1; } return bFound; } //------------------------------------------------------------------------------- ScDocAttrIterator::ScDocAttrIterator(ScDocument* pDocument, SCTAB nTable, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) : pDoc( pDocument ), nTab( nTable ), nEndCol( nCol2 ), nStartRow( nRow1 ), nEndRow( nRow2 ), nCol( nCol1 ) { if ( ValidTab(nTab) && pDoc->pTab[nTab] ) pColIter = pDoc->pTab[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow ); else pColIter = NULL; } ScDocAttrIterator::~ScDocAttrIterator() { delete pColIter; } const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& rCol, SCROW& rRow1, SCROW& rRow2 ) { while ( pColIter ) { const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 ); if ( pPattern ) { rCol = nCol; return pPattern; } delete pColIter; ++nCol; if ( nCol <= nEndCol ) pColIter = pDoc->pTab[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow ); else pColIter = NULL; } return NULL; // is nix mehr } //------------------------------------------------------------------------------- ScAttrRectIterator::ScAttrRectIterator(ScDocument* pDocument, SCTAB nTable, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) : pDoc( pDocument ), nTab( nTable ), nEndCol( nCol2 ), nStartRow( nRow1 ), nEndRow( nRow2 ), nIterStartCol( nCol1 ), nIterEndCol( nCol1 ) { if ( ValidTab(nTab) && pDoc->pTab[nTab] ) { pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow ); while ( nIterEndCol < nEndCol && pDoc->pTab[nTab]->aCol[nIterEndCol].IsAllAttrEqual( pDoc->pTab[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) ) ++nIterEndCol; } else pColIter = NULL; } ScAttrRectIterator::~ScAttrRectIterator() { delete pColIter; } void ScAttrRectIterator::DataChanged() { if (pColIter) { SCROW nNextRow = pColIter->GetNextRow(); delete pColIter; pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nNextRow, nEndRow ); } } const ScPatternAttr* ScAttrRectIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, SCROW& rRow1, SCROW& rRow2 ) { while ( pColIter ) { const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 ); if ( pPattern ) { rCol1 = nIterStartCol; rCol2 = nIterEndCol; return pPattern; } delete pColIter; nIterStartCol = nIterEndCol+1; if ( nIterStartCol <= nEndCol ) { nIterEndCol = nIterStartCol; pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow ); while ( nIterEndCol < nEndCol && pDoc->pTab[nTab]->aCol[nIterEndCol].IsAllAttrEqual( pDoc->pTab[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) ) ++nIterEndCol; } else pColIter = NULL; } return NULL; // is nix mehr }