/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include namespace { CellType adjustCellType( CellType eOrig ) { switch (eOrig) { case CELLTYPE_EDIT: return CELLTYPE_STRING; default: ; } return eOrig; } template OUString getString( const T& rVal ) { if (rVal.meType == CELLTYPE_STRING) return rVal.mpString->getString(); if (rVal.meType == CELLTYPE_EDIT) { OUStringBuffer aRet; sal_Int32 n = rVal.mpEditText->GetParagraphCount(); for (sal_Int32 i = 0; i < n; ++i) { if (i > 0) aRet.append('\n'); aRet.append(rVal.mpEditText->GetText(i)); } return aRet.makeStringAndClear(); } return EMPTY_OUSTRING; } bool equalsFormulaCells( const ScFormulaCell* p1, const ScFormulaCell* p2 ) { const ScTokenArray* pCode1 = p1->GetCode(); const ScTokenArray* pCode2 = p2->GetCode(); if (pCode1->GetLen() != pCode2->GetLen()) return false; if (pCode1->GetCodeError() != pCode2->GetCodeError()) return false; sal_uInt16 n = pCode1->GetLen(); formula::FormulaToken** ppToken1 = pCode1->GetArray(); formula::FormulaToken** ppToken2 = pCode2->GetArray(); for (sal_uInt16 i = 0; i < n; ++i) { if (!ppToken1[i]->TextEqual(*(ppToken2[i]))) return false; } return true; } template bool equalsWithoutFormatImpl( const T& left, const T& right ) { CellType eType1 = adjustCellType(left.meType); CellType eType2 = adjustCellType(right.meType); if (eType1 != eType2) return false; switch (eType1) { case CELLTYPE_NONE: return true; case CELLTYPE_VALUE: return left.mfValue == right.mfValue; case CELLTYPE_STRING: { OUString aStr1 = getString(left); OUString aStr2 = getString(right); return aStr1 == aStr2; } case CELLTYPE_FORMULA: return equalsFormulaCells(left.mpFormula, right.mpFormula); default: ; } return false; } void commitToColumn( const ScCellValue& rCell, ScColumn& rColumn, SCROW nRow ) { switch (rCell.meType) { case CELLTYPE_STRING: rColumn.SetRawString(nRow, *rCell.mpString); break; case CELLTYPE_EDIT: rColumn.SetEditText(nRow, ScEditUtil::Clone(*rCell.mpEditText, rColumn.GetDoc())); break; case CELLTYPE_VALUE: rColumn.SetValue(nRow, rCell.mfValue); break; case CELLTYPE_FORMULA: { ScAddress aDestPos(rColumn.GetCol(), nRow, rColumn.GetTab()); rColumn.SetFormulaCell(nRow, new ScFormulaCell(*rCell.mpFormula, rColumn.GetDoc(), aDestPos)); } break; default: rColumn.DeleteContent(nRow); } } bool hasStringImpl( CellType eType, ScFormulaCell* pFormula ) { switch (eType) { case CELLTYPE_STRING: case CELLTYPE_EDIT: return true; case CELLTYPE_FORMULA: return !pFormula->IsValue(); default: return false; } } bool hasNumericImpl( CellType eType, ScFormulaCell* pFormula ) { switch (eType) { case CELLTYPE_VALUE: return true; case CELLTYPE_FORMULA: return pFormula->IsValue(); default: return false; } } template OUString getStringImpl( const CellT& rCell, const ScDocument* pDoc ) { switch (rCell.meType) { case CELLTYPE_VALUE: return OUString::number(rCell.mfValue); case CELLTYPE_STRING: return rCell.mpString->getString(); case CELLTYPE_EDIT: if (rCell.mpEditText) return ScEditUtil::GetString(*rCell.mpEditText, pDoc); break; case CELLTYPE_FORMULA: return rCell.mpFormula->GetString().getString(); default: ; } return EMPTY_OUSTRING; } template OUString getRawStringImpl( const CellT& rCell, const ScDocument& rDoc ) { switch (rCell.meType) { case CELLTYPE_VALUE: return OUString::number(rCell.mfValue); case CELLTYPE_STRING: return rCell.mpString->getString(); case CELLTYPE_EDIT: if (rCell.mpEditText) return ScEditUtil::GetString(*rCell.mpEditText, &rDoc); break; case CELLTYPE_FORMULA: return rCell.mpFormula->GetRawString().getString(); default: ; } return EMPTY_OUSTRING; } } ScCellValue::ScCellValue() : meType(CELLTYPE_NONE), mfValue(0.0) {} ScCellValue::ScCellValue( const ScRefCellValue& rCell ) : meType(rCell.meType), mfValue(rCell.mfValue) { switch (rCell.meType) { case CELLTYPE_STRING: mpString = new svl::SharedString(*rCell.mpString); break; case CELLTYPE_EDIT: mpEditText = rCell.mpEditText->Clone().release(); break; case CELLTYPE_FORMULA: mpFormula = rCell.mpFormula->Clone(); break; default: ; } } ScCellValue::ScCellValue( double fValue ) : meType(CELLTYPE_VALUE), mfValue(fValue) {} ScCellValue::ScCellValue( const svl::SharedString& rString ) : meType(CELLTYPE_STRING), mpString(new svl::SharedString(rString)) {} ScCellValue::ScCellValue( const ScCellValue& r ) : meType(r.meType), mfValue(r.mfValue) { switch (r.meType) { case CELLTYPE_STRING: mpString = new svl::SharedString(*r.mpString); break; case CELLTYPE_EDIT: mpEditText = r.mpEditText->Clone().release(); break; case CELLTYPE_FORMULA: mpFormula = r.mpFormula->Clone(); break; default: ; } } ScCellValue::ScCellValue(ScCellValue&& r) noexcept : meType(r.meType) , mfValue(r.mfValue) { switch (r.meType) { case CELLTYPE_STRING: mpString = r.mpString; break; case CELLTYPE_EDIT: mpEditText = r.mpEditText; break; case CELLTYPE_FORMULA: mpFormula = r.mpFormula; break; default: ; } r.meType = CELLTYPE_NONE; } ScCellValue::~ScCellValue() { clear(); } void ScCellValue::clear() noexcept { switch (meType) { case CELLTYPE_STRING: delete mpString; break; case CELLTYPE_EDIT: delete mpEditText; break; case CELLTYPE_FORMULA: delete mpFormula; break; default: ; } // Reset to empty value. meType = CELLTYPE_NONE; mfValue = 0.0; } void ScCellValue::set( double fValue ) { clear(); meType = CELLTYPE_VALUE; mfValue = fValue; } void ScCellValue::set( const svl::SharedString& rStr ) { clear(); meType = CELLTYPE_STRING; mpString = new svl::SharedString(rStr); } void ScCellValue::set( const EditTextObject& rEditText ) { clear(); meType = CELLTYPE_EDIT; mpEditText = rEditText.Clone().release(); } void ScCellValue::set( EditTextObject* pEditText ) { clear(); meType = CELLTYPE_EDIT; mpEditText = pEditText; } void ScCellValue::set( ScFormulaCell* pFormula ) { clear(); meType = CELLTYPE_FORMULA; mpFormula = pFormula; } void ScCellValue::assign( const ScDocument& rDoc, const ScAddress& rPos ) { clear(); ScRefCellValue aRefVal(const_cast(rDoc), rPos); meType = aRefVal.meType; switch (meType) { case CELLTYPE_STRING: mpString = new svl::SharedString(*aRefVal.mpString); break; case CELLTYPE_EDIT: if (aRefVal.mpEditText) mpEditText = aRefVal.mpEditText->Clone().release(); break; case CELLTYPE_VALUE: mfValue = aRefVal.mfValue; break; case CELLTYPE_FORMULA: mpFormula = aRefVal.mpFormula->Clone(); break; default: meType = CELLTYPE_NONE; // reset to empty. } } void ScCellValue::assign(const ScCellValue& rOther, ScDocument& rDestDoc, ScCloneFlags nCloneFlags) { clear(); meType = rOther.meType; switch (meType) { case CELLTYPE_STRING: mpString = new svl::SharedString(*rOther.mpString); break; case CELLTYPE_EDIT: { // Switch to the pool of the destination document. ScFieldEditEngine& rEngine = rDestDoc.GetEditEngine(); if (rOther.mpEditText->HasOnlineSpellErrors()) { EEControlBits nControl = rEngine.GetControlWord(); const EEControlBits nSpellControl = EEControlBits::ONLINESPELLING | EEControlBits::ALLOWBIGOBJS; bool bNewControl = ((nControl & nSpellControl) != nSpellControl); if (bNewControl) rEngine.SetControlWord(nControl | nSpellControl); rEngine.SetTextCurrentDefaults(*rOther.mpEditText); mpEditText = rEngine.CreateTextObject().release(); if (bNewControl) rEngine.SetControlWord(nControl); } else { rEngine.SetTextCurrentDefaults(*rOther.mpEditText); mpEditText = rEngine.CreateTextObject().release(); } } break; case CELLTYPE_VALUE: mfValue = rOther.mfValue; break; case CELLTYPE_FORMULA: // Switch to the destination document. mpFormula = new ScFormulaCell(*rOther.mpFormula, rDestDoc, rOther.mpFormula->aPos, nCloneFlags); break; default: meType = CELLTYPE_NONE; // reset to empty. } } void ScCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const { switch (meType) { case CELLTYPE_STRING: { ScSetStringParam aParam; aParam.setTextInput(); rDoc.SetString(rPos, mpString->getString(), &aParam); } break; case CELLTYPE_EDIT: rDoc.SetEditText(rPos, mpEditText->Clone()); break; case CELLTYPE_VALUE: rDoc.SetValue(rPos, mfValue); break; case CELLTYPE_FORMULA: rDoc.SetFormulaCell(rPos, mpFormula->Clone()); break; default: rDoc.SetEmptyCell(rPos); } } void ScCellValue::commit( ScColumn& rColumn, SCROW nRow ) const { commitToColumn(*this, rColumn, nRow); } void ScCellValue::release( ScDocument& rDoc, const ScAddress& rPos ) { switch (meType) { case CELLTYPE_STRING: { // Currently, string cannot be placed without copying. ScSetStringParam aParam; aParam.setTextInput(); rDoc.SetString(rPos, mpString->getString(), &aParam); delete mpString; } break; case CELLTYPE_EDIT: // Cell takes the ownership of the text object. rDoc.SetEditText(rPos, std::unique_ptr(mpEditText)); break; case CELLTYPE_VALUE: rDoc.SetValue(rPos, mfValue); break; case CELLTYPE_FORMULA: // This formula cell instance is directly placed in the document without copying. rDoc.SetFormulaCell(rPos, mpFormula); break; default: rDoc.SetEmptyCell(rPos); } meType = CELLTYPE_NONE; mfValue = 0.0; } void ScCellValue::release( ScColumn& rColumn, SCROW nRow, sc::StartListeningType eListenType ) { switch (meType) { case CELLTYPE_STRING: { // Currently, string cannot be placed without copying. rColumn.SetRawString(nRow, *mpString); delete mpString; } break; case CELLTYPE_EDIT: // Cell takes the ownership of the text object. rColumn.SetEditText(nRow, std::unique_ptr(mpEditText)); break; case CELLTYPE_VALUE: rColumn.SetValue(nRow, mfValue); break; case CELLTYPE_FORMULA: // This formula cell instance is directly placed in the document without copying. rColumn.SetFormulaCell(nRow, mpFormula, eListenType); break; default: rColumn.DeleteContent(nRow); } meType = CELLTYPE_NONE; mfValue = 0.0; } OUString ScCellValue::getString( const ScDocument& rDoc ) const { return getStringImpl(*this, &rDoc); } bool ScCellValue::isEmpty() const { return meType == CELLTYPE_NONE; } bool ScCellValue::equalsWithoutFormat( const ScCellValue& r ) const { return equalsWithoutFormatImpl(*this, r); } ScCellValue& ScCellValue::operator= ( const ScCellValue& r ) { ScCellValue aTmp(r); swap(aTmp); return *this; } ScCellValue& ScCellValue::operator=(ScCellValue&& rCell) noexcept { clear(); meType = rCell.meType; mfValue = rCell.mfValue; switch (rCell.meType) { case CELLTYPE_STRING: mpString = rCell.mpString; break; case CELLTYPE_EDIT: mpEditText = rCell.mpEditText; break; case CELLTYPE_FORMULA: mpFormula = rCell.mpFormula; break; default: ; } //we don't need to reset mpString/mpEditText/mpFormula if we //set meType to NONE as the ScCellValue dtor keys off the meType rCell.meType = CELLTYPE_NONE; return *this; } ScCellValue& ScCellValue::operator= ( const ScRefCellValue& r ) { ScCellValue aTmp(r); swap(aTmp); return *this; } void ScCellValue::swap( ScCellValue& r ) { std::swap(meType, r.meType); // double is 8 bytes, whereas a pointer may be 4 or 8 bytes depending on // the platform. Swap by double values. std::swap(mfValue, r.mfValue); } ScRefCellValue::ScRefCellValue() : meType(CELLTYPE_NONE), mfValue(0.0) {} ScRefCellValue::ScRefCellValue( double fValue ) : meType(CELLTYPE_VALUE), mfValue(fValue) {} ScRefCellValue::ScRefCellValue( const svl::SharedString* pString ) : meType(CELLTYPE_STRING), mpString(pString) {} ScRefCellValue::ScRefCellValue( const EditTextObject* pEditText ) : meType(CELLTYPE_EDIT), mpEditText(pEditText) {} ScRefCellValue::ScRefCellValue( ScFormulaCell* pFormula ) : meType(CELLTYPE_FORMULA), mpFormula(pFormula) {} ScRefCellValue::ScRefCellValue( ScDocument& rDoc, const ScAddress& rPos ) { assign( rDoc, rPos); } ScRefCellValue::ScRefCellValue( ScDocument& rDoc, const ScAddress& rPos, sc::ColumnBlockPosition& rBlockPos ) { assign( rDoc, rPos, rBlockPos ); } void ScRefCellValue::clear() { // Reset to empty value. meType = CELLTYPE_NONE; mfValue = 0.0; } void ScRefCellValue::assign( ScDocument& rDoc, const ScAddress& rPos ) { *this = rDoc.GetRefCellValue(rPos); } void ScRefCellValue::assign( ScDocument& rDoc, const ScAddress& rPos, sc::ColumnBlockPosition& rBlockPos ) { *this = rDoc.GetRefCellValue(rPos, rBlockPos); } void ScRefCellValue::commit( ScDocument& rDoc, const ScAddress& rPos ) const { switch (meType) { case CELLTYPE_STRING: { ScSetStringParam aParam; aParam.setTextInput(); rDoc.SetString(rPos, mpString->getString(), &aParam); } break; case CELLTYPE_EDIT: rDoc.SetEditText(rPos, ScEditUtil::Clone(*mpEditText, rDoc)); break; case CELLTYPE_VALUE: rDoc.SetValue(rPos, mfValue); break; case CELLTYPE_FORMULA: rDoc.SetFormulaCell(rPos, new ScFormulaCell(*mpFormula, rDoc, rPos)); break; default: rDoc.SetEmptyCell(rPos); } } bool ScRefCellValue::hasString() const { return hasStringImpl(meType, mpFormula); } bool ScRefCellValue::hasNumeric() const { return hasNumericImpl(meType, mpFormula); } bool ScRefCellValue::hasError() const { return meType == CELLTYPE_FORMULA && mpFormula->GetErrCode() != FormulaError::NONE; } double ScRefCellValue::getValue() { switch (meType) { case CELLTYPE_VALUE: return mfValue; case CELLTYPE_FORMULA: return mpFormula->GetValue(); default: ; } return 0.0; } double ScRefCellValue::getRawValue() const { switch (meType) { case CELLTYPE_VALUE: return mfValue; case CELLTYPE_FORMULA: return mpFormula->GetRawValue(); default: ; } return 0.0; } OUString ScRefCellValue::getString( const ScDocument* pDoc ) const { return getStringImpl(*this, pDoc); } OUString ScRefCellValue::getRawString( const ScDocument& rDoc ) const { return getRawStringImpl(*this, rDoc); } bool ScRefCellValue::isEmpty() const { return meType == CELLTYPE_NONE; } bool ScRefCellValue::hasEmptyValue() { if (isEmpty()) return true; if (meType == CELLTYPE_FORMULA) return mpFormula->IsEmpty(); return false; } bool ScRefCellValue::equalsWithoutFormat( const ScRefCellValue& r ) const { return equalsWithoutFormatImpl(*this, r); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */