diff options
Diffstat (limited to 'sc/inc/address.hxx')
-rw-r--r-- | sc/inc/address.hxx | 978 |
1 files changed, 978 insertions, 0 deletions
diff --git a/sc/inc/address.hxx b/sc/inc/address.hxx new file mode 100644 index 0000000000..03cfb1db79 --- /dev/null +++ b/sc/inc/address.hxx @@ -0,0 +1,978 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column:100 -*- */ +/* + * 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/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#pragma once + +#include <rtl/ustrbuf.hxx> +#include <rtl/strbuf.hxx> + +#include <array> +#include <limits> +#include <ostream> + +#include "scdllapi.h" +#include "types.hxx" +#include <formula/grammar.hxx> + +#include <o3tl/typed_flags_set.hxx> +#include <o3tl/underlyingenumvalue.hxx> + +namespace com::sun::star { + namespace sheet { + struct ExternalLinkInfo; + } +} + +namespace com::sun::star::uno { template <typename > class Sequence; } + +class ScDocument; + +/** size_t typedef to be able to find places where code was changed from USHORT + to size_t and is used to read/write from/to streams. */ +typedef size_t SCSIZE; + +// Maximum possible value of data type, NOT maximum row value. +// MSC confuses numeric_limit max() with macro max() if vcl/wintypes.hxx is +// included, we should not be using those stupid macros anyway. +#undef min +#undef max +const SCROW SCROW_MAX = ::std::numeric_limits<SCROW>::max(); +const SCCOL SCCOL_MAX = ::std::numeric_limits<SCCOL>::max(); +const SCTAB SCTAB_MAX = ::std::numeric_limits<SCTAB>::max(); +const SCCOLROW SCCOLROW_MAX = ::std::numeric_limits<SCCOLROW>::max(); +const SCSIZE SCSIZE_MAX = ::std::numeric_limits<SCSIZE>::max(); + +// Count values +const SCROW MAXROWCOUNT = 1048576; +const SCCOL MAXCOLCOUNT = 16384; +const SCCOL INITIALCOLCOUNT = 1; // initial number of columns we allocate memory for +/// limiting to 10000 for now, problem with 32 bit builds for now +const SCTAB MAXTABCOUNT = 10000; +// Maximum values +const SCROW MAXROW = MAXROWCOUNT - 1; +const SCCOL MAXCOL = MAXCOLCOUNT - 1; +const SCTAB MAXTAB = MAXTABCOUNT - 1; +const SCCOLROW MAXCOLROW = MAXROW; +const SCROW MAXROWCOUNT_JUMBO = 16 * 1024 * 1024; +const SCCOL MAXCOLCOUNT_JUMBO = 16384; +const SCROW MAXROW_JUMBO = MAXROWCOUNT_JUMBO - 1; +const SCCOL MAXCOL_JUMBO = MAXCOLCOUNT_JUMBO - 1; +// Maximum tiled rendering values +const SCROW MAXTILEDROW = MAXROW; +// Limit the initial tab count to prevent users to set the count too high, +// which could cause the memory usage of blank documents to exceed the +// available system memory. +const SCTAB MAXINITTAB = 1024; +const SCTAB MININITTAB = 1; + +inline constexpr OUString MAXROW_STRING(u"1048575"_ustr); +inline constexpr OUString MAXCOL_STRING(u"XFD"_ustr); +inline constexpr OUString MAXROW_JUMBO_STRING(u"16777215"_ustr); +inline constexpr OUString MAXCOL_JUMBO_STRING(u"XFD"_ustr); + +// Special values +const SCTAB SC_TAB_APPEND = SCTAB_MAX; +const SCTAB TABLEID_DOC = SCTAB_MAX; // entire document, e.g. protect +const SCROW SCROWS32K = 32000; // for fuzzing +const SCCOL SCCOL_REPEAT_NONE = SCCOL_MAX; +const SCROW SCROW_REPEAT_NONE = SCROW_MAX; +const SCCOL SC_TABSTART_NONE = SCCOL_MAX; + +const SCROW MAXROW_30 = 8191; + +[[nodiscard]] inline bool ValidCol( SCCOL nCol, SCCOL nMaxCol ) +{ + assert(nMaxCol == MAXCOL || nMaxCol == MAXCOL_JUMBO); // temporary to debug jumbo sheets work + return nCol >= 0 && nCol <= nMaxCol; +} + +[[nodiscard]] inline bool ValidRow( SCROW nRow, SCROW nMaxRow) +{ + assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work + return nRow >= 0 && nRow <= nMaxRow; +} + +[[nodiscard]] inline bool ValidTab( SCTAB nTab ) +{ + return nTab >= 0 && nTab <= MAXTAB; +} + +[[nodiscard]] inline bool ValidTab( SCTAB nTab, SCTAB nMaxTab ) +{ + return nTab >= 0 && nTab <= nMaxTab; +} + +[[nodiscard]] inline bool ValidColRow( SCCOL nCol, SCROW nRow, SCCOL nMaxCol, SCROW nMaxRow ) +{ + assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work + return ValidCol(nCol,nMaxCol) && ValidRow(nRow,nMaxRow); +} + +[[nodiscard]] inline bool ValidColRowTab( SCCOL nCol, SCROW nRow, SCTAB nTab, SCCOL nMaxCol, SCROW nMaxRow ) +{ + assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work + return ValidCol(nCol,nMaxCol) && ValidRow(nRow,nMaxRow) && ValidTab( nTab); +} + +[[nodiscard]] inline SCCOL SanitizeCol( SCCOL nCol, SCCOL nMaxCol ) +{ + assert(nMaxCol == MAXCOL || nMaxCol == MAXCOL_JUMBO); // temporary to debug jumbo sheets work + return nCol < 0 ? 0 : (nCol > nMaxCol ? nMaxCol : nCol); +} + +[[nodiscard]] inline SCROW SanitizeRow( SCROW nRow, SCROW nMaxRow ) +{ + assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work + return nRow < 0 ? 0 : (nRow > nMaxRow ? nMaxRow : nRow); +} + +[[nodiscard]] inline SCTAB SanitizeTab( SCTAB nTab ) +{ + return nTab < 0 ? 0 : (nTab > MAXTAB ? MAXTAB : nTab); +} + +template <typename T> inline void PutInOrder(T& nStart, T& nEnd) +{ + if (nEnd < nStart) + std::swap(nStart, nEnd); +} + +// The result of ConvertRef() is a bit group of the following: +enum class ScRefFlags : sal_uInt16 +{ + ZERO = 0x0000, + COL_ABS = 0x0001, + ROW_ABS = 0x0002, + TAB_ABS = 0x0004, + TAB_3D = 0x0008, + COL2_ABS = 0x0010, + ROW2_ABS = 0x0020, + TAB2_ABS = 0x0040, + TAB2_3D = 0x0080, + ROW_VALID = 0x0100, + COL_VALID = 0x0200, + TAB_VALID = 0x0400, + // BITS for convenience + BITS = COL_ABS | ROW_ABS | TAB_ABS | TAB_3D + | ROW_VALID | COL_VALID | TAB_VALID, + // somewhat cheesy kludge to force the display of the document name even for + // local references. Requires TAB_3D to be valid + FORCE_DOC = 0x0800, + ROW2_VALID = 0x1000, + COL2_VALID = 0x2000, + TAB2_VALID = 0x4000, + VALID = 0x8000, + + TAB_ABS_3D = TAB_ABS | TAB_3D, + + ADDR_ABS = VALID | COL_ABS | ROW_ABS | TAB_ABS, + + RANGE_ABS = ADDR_ABS | COL2_ABS | ROW2_ABS | TAB2_ABS, + + ADDR_ABS_3D = ADDR_ABS | TAB_3D, + RANGE_ABS_3D = RANGE_ABS | TAB_3D +}; + +namespace o3tl +{ + template<> struct typed_flags<ScRefFlags> : is_typed_flags<ScRefFlags, 0xffff> {}; +} +inline void applyStartToEndFlags(ScRefFlags &target,const ScRefFlags source) +{ + target |= ScRefFlags(o3tl::to_underlying(source) << 4); +} +inline void applyStartToEndFlags(ScRefFlags &target) +{ + target |= ScRefFlags(o3tl::to_underlying(target) << 4); +} + +// ScAddress +class SAL_WARN_UNUSED ScAddress +{ +private: + // Even if the fields are in the order "row, column, tab", in all (?) the ScAddress and + // ScDocument APIs that take separate row, column, and tab parameters, the parameters are in the + // order "column, row, tab", which matches the most common (A1) address syntax, if you ignore + // the sheet (tab). Don't let this confuse you, like it confused me for a while. + + SCROW nRow; + SCCOL nCol; + SCTAB nTab; + +public: + + enum Uninitialized { UNINITIALIZED }; + enum InitializeInvalid { INITIALIZE_INVALID }; + + struct Details + { + formula::FormulaGrammar::AddressConvention eConv; + SCROW nRow; + SCCOL nCol; + + Details( formula::FormulaGrammar::AddressConvention eConvP, SCROW nRowP, SCCOL nColP ) : + eConv(eConvP), nRow(nRowP), nCol(nColP) + {} + Details( formula::FormulaGrammar::AddressConvention eConvP, ScAddress const & rAddr ) : + eConv(eConvP), nRow(rAddr.Row()), nCol(rAddr.Col()) + {} + Details( formula::FormulaGrammar::AddressConvention eConvP) : + eConv(eConvP), nRow(0), nCol(0) + {} + /* Use the formula::FormulaGrammar::AddressConvention associated with rAddr::Tab() */ + Details( const ScDocument& rDoc, const ScAddress& rAddr ); + }; + SC_DLLPUBLIC static const Details detailsOOOa1; + + struct ExternalInfo + { + OUString maTabName; + sal_uInt16 mnFileId; + bool mbExternal; + + ExternalInfo() : + mnFileId(0), mbExternal(false) + {} + }; + + ScAddress() : + nRow(0), nCol(0), nTab(0) + {} + ScAddress( SCCOL nColP, SCROW nRowP, SCTAB nTabP ) : + nRow(nRowP), nCol(nColP), nTab(nTabP) + {} + /** coverity[uninit_member] - Yes, it is what it seems to be: Uninitialized. + May be used for performance reasons if it is initialized by other means. */ + ScAddress( Uninitialized ) + {} + ScAddress( InitializeInvalid ) : + nRow(-1), nCol(-1), nTab(-1) + {} + ScAddress( const ScAddress& rAddress ) : + nRow(rAddress.nRow), nCol(rAddress.nCol), nTab(rAddress.nTab) + {} + inline ScAddress& operator=( const ScAddress& rAddress ); + + inline void Set( SCCOL nCol, SCROW nRow, SCTAB nTab ); + + SCROW Row() const + { + return nRow; + } + + SCCOL Col() const + { + return nCol; + } + SCTAB Tab() const + { + return nTab; + } + void SetRow( SCROW nRowP ) + { + nRow = nRowP; + } + void SetCol( SCCOL nColP ) + { + nCol = nColP; + } + void SetTab( SCTAB nTabP ) + { + nTab = nTabP; + } + void SetInvalid() + { + nRow = -1; + nCol = -1; + nTab = -1; + } + bool IsValid() const + { + return (nRow >= 0) && (nCol >= 0) && (nTab >= 0); + } + + inline void PutInOrder( ScAddress& rAddress ); + + void IncRow( SCROW nDelta = 1 ) + { + nRow = sal::static_int_cast<SCROW>(nRow + nDelta); + } + void IncCol( SCCOL nDelta = 1 ) + { + nCol = sal::static_int_cast<SCCOL>(nCol + nDelta); + } + void IncTab( SCTAB nDelta = 1 ) + { + nTab = sal::static_int_cast<SCTAB>(nTab + nDelta); + } + void GetVars( SCCOL& nColP, SCROW& nRowP, SCTAB& nTabP ) const + { + nColP = nCol; + nRowP = nRow; + nTabP = nTab; + } + + /** + @param pSheetEndPos + If given and Parse() successfully parsed a sheet name it returns + the end position (exclusive) behind the sheet name AND a + following sheet name separator. This independent of whether the + resulting reference is fully valid or not. + */ + SC_DLLPUBLIC ScRefFlags Parse( + const OUString&, const ScDocument&, + const Details& rDetails = detailsOOOa1, + ExternalInfo* pExtInfo = nullptr, + const css::uno::Sequence<css::sheet::ExternalLinkInfo>* pExternalLinks = nullptr, + sal_Int32* pSheetEndPos = nullptr, + const OUString* pErrRef = nullptr ); + + SC_DLLPUBLIC void Format( OStringBuffer& r, ScRefFlags nFlags, + const ScDocument* pDocument = nullptr, + const Details& rDetails = detailsOOOa1) const; + + SC_DLLPUBLIC OUString Format( ScRefFlags nFlags, + const ScDocument* pDocument = nullptr, + const Details& rDetails = detailsOOOa1) const; + + /** + @param rErrorPos + If FALSE is returned, the positions contain <0 or >MAX... + values if shifted out of bounds. + @param pDocument + The document for the maximum defined sheet number. + */ + [[nodiscard]] SC_DLLPUBLIC bool Move( SCCOL nDeltaX, SCROW nDeltaY, SCTAB nDeltaZ, + ScAddress& rErrorPos, const ScDocument& rDoc ); + + inline bool operator==( const ScAddress& rAddress ) const; + inline bool operator!=( const ScAddress& rAddress ) const; + inline bool operator<( const ScAddress& rAddress ) const; + inline bool operator<=( const ScAddress& rAddress ) const; + inline bool lessThanByRow( const ScAddress& rAddress ) const; + + inline size_t hash() const; + + /** + * Create a human-readable string representation of the cell address. You + * cannot specify precise formatting with this method; use Format() if you + * need to specify how the address needs to be formatted. + * + * The address string does not display sheet name. + * + * @return human-readable string representation of the cell address. + */ + OUString GetColRowString() const; +}; + +// For use in SAL_DEBUG etc. Output format not guaranteed to be stable. +template<typename charT, typename traits> +inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT, traits> & stream, const ScAddress& rAddress) +{ + stream << + rAddress.Tab()+1 << "!" + "R" << rAddress.Row()+1 << + "C" << rAddress.Col()+1; + + return stream; +} + +inline void ScAddress::PutInOrder( ScAddress& rAddress ) +{ + ::PutInOrder(nCol, rAddress.nCol); + ::PutInOrder(nRow, rAddress.nRow); + ::PutInOrder(nTab, rAddress.nTab); +} + +inline void ScAddress::Set( SCCOL nColP, SCROW nRowP, SCTAB nTabP ) +{ + nCol = nColP; + nRow = nRowP; + nTab = nTabP; +} + +inline ScAddress& ScAddress::operator=( const ScAddress& rAddress ) +{ + nCol = rAddress.nCol; + nRow = rAddress.nRow; + nTab = rAddress.nTab; + return *this; +} + +inline bool ScAddress::operator==( const ScAddress& rAddress ) const +{ + return nRow == rAddress.nRow && nCol == rAddress.nCol && nTab == rAddress.nTab; +} + +inline bool ScAddress::operator!=( const ScAddress& rAddress ) const +{ + return !operator==( rAddress ); +} + +/** Less than ordered by tab,col,row. */ +inline bool ScAddress::operator<( const ScAddress& rAddress ) const +{ + if (nTab == rAddress.nTab) + { + if (nCol == rAddress.nCol) + return nRow < rAddress.nRow; + else + return nCol < rAddress.nCol; + } + else + return nTab < rAddress.nTab; +} + +inline bool ScAddress::operator<=( const ScAddress& rAddress ) const +{ + return operator<( rAddress ) || operator==( rAddress ); +} + +/** Less than ordered by tab,row,col as needed by row-wise import/export */ +inline bool ScAddress::lessThanByRow( const ScAddress& rAddress ) const +{ + if (nTab == rAddress.nTab) + { + if (nRow == rAddress.nRow) + return nCol < rAddress.nCol; + else + return nRow < rAddress.nRow; + } + else + return nTab < rAddress.nTab; +} + +inline size_t ScAddress::hash() const +{ +#if SAL_TYPES_SIZEOFPOINTER == 8 + // 16 bits for the columns, and 20 bits for the rows + return (static_cast<size_t>(nTab) << 36) ^ + (static_cast<size_t>(nCol) << 20) ^ + static_cast<size_t>(nRow); +#else + // Assume that there are not that many addresses with row > 2^16 AND column + // > 2^8 AND sheet > 2^8 so we won't have too many collisions. + if (nRow <= 0xffff) + return (static_cast<size_t>(nTab) << 24) ^ + (static_cast<size_t>(nCol) << 16) ^ static_cast<size_t>(nRow); + else + return (static_cast<size_t>(nTab) << 28) ^ + (static_cast<size_t>(nCol) << 24) ^ static_cast<size_t>(nRow); +#endif +} + +struct ScAddressHashFunctor +{ + size_t operator()( const ScAddress & rAddress ) const + { + return rAddress.hash(); + } +}; + +[[nodiscard]] inline bool ValidAddress( const ScAddress& rAddress, SCCOL nMaxCol, SCROW nMaxRow ) +{ + return ValidCol(rAddress.Col(), nMaxCol) && ValidRow(rAddress.Row(), nMaxRow) && ValidTab(rAddress.Tab()); +} + +// ScRange +class SAL_WARN_UNUSED SC_DLLPUBLIC ScRange final +{ +public: + ScAddress aStart; + ScAddress aEnd; + + ScRange() : + aStart(), aEnd() + {} + + ScRange( ScAddress::Uninitialized eUninitialized ) : + aStart( eUninitialized ), aEnd( eUninitialized ) + {} + ScRange( ScAddress::InitializeInvalid eInvalid ) : + aStart( eInvalid ), aEnd( eInvalid ) + {} + ScRange( const ScAddress& aInputStart, const ScAddress& aInputEnd ) : + aStart( aInputStart ), aEnd( aInputEnd ) + { + PutInOrder(); + } + ScRange( const ScRange& rRange ) : + aStart( rRange.aStart ), aEnd( rRange.aEnd ) + {} + ScRange( const ScAddress& rRange ) : + aStart( rRange ), aEnd( rRange ) + {} + ScRange( SCCOL nCol, SCROW nRow, SCTAB nTab ) : + aStart( nCol, nRow, nTab ), aEnd( aStart ) + {} + ScRange( SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2 ) : + aStart( nCol1, nRow1, nTab1 ), aEnd( nCol2, nRow2, nTab2 ) + {} + + ScRange& operator=( const ScRange& rRange ) + { + aStart = rRange.aStart; + aEnd = rRange.aEnd; + return *this; + } + ScRange& operator=( const ScAddress& rPos ) + { + aStart = aEnd = rPos; + return *this; + } + void SetInvalid() + { + aStart.SetInvalid(); + aEnd.SetInvalid(); + } + bool IsValid() const + { + return aStart.IsValid() && aEnd.IsValid(); + } + inline bool Contains( const ScAddress& ) const; ///< is Address& fully in Range? + inline bool Contains( const ScRange& ) const; ///< is Range& fully in Range? + inline bool Intersects( const ScRange& rRange ) const; // do two ranges intersect? + + ScRefFlags Parse( const OUString&, const ScDocument&, + const ScAddress::Details& rDetails = ScAddress::detailsOOOa1, + ScAddress::ExternalInfo* pExtInfo = nullptr, + const css::uno::Sequence<css::sheet::ExternalLinkInfo>* pExternalLinks = nullptr, + const OUString* pErrRef = nullptr ); + + ScRefFlags ParseAny( const OUString&, const ScDocument&, + const ScAddress::Details& rDetails = ScAddress::detailsOOOa1 ); + ScRefFlags ParseCols( const ScDocument& rDoc, + const OUString&, + const ScAddress::Details& rDetails = ScAddress::detailsOOOa1 ); + void ParseRows( const ScDocument& rDoc, + const OUString&, + const ScAddress::Details& rDetails = ScAddress::detailsOOOa1 ); + + /** Parse an Excel style reference up to and including the sheet name + separator '!', including detection of external documents and sheet + names, and in case of MOOXML import the bracketed index is used to + determine the actual document name passed in pExternalLinks. For + internal references (resulting rExternDocName empty), aStart.nTab and + aEnd.nTab are set, or -1 if sheet name not found. + @param bOnlyAcceptSingle If <TRUE/>, a 3D reference (Sheet1:Sheet2) + encountered results in an error (NULL returned). + @param pExternalLinks pointer to ExternalLinkInfo sequence, may be + NULL for non-filter usage, in which case indices such as [1] are + not resolved. + @param pErrRef pointer to "#REF!" string if to be accepted. + @returns + Pointer to the position after '!' if successfully parsed, and + rExternDocName, rStartTabName and/or rEndTabName filled if + applicable. ScRefFlags::... flags set in nFlags. + Or if no valid document and/or sheet header could be parsed the start + position passed with pString. + Or NULL if a 3D sheet header could be parsed but + bOnlyAcceptSingle==true was given. + */ + const sal_Unicode* Parse_XL_Header( const sal_Unicode* pString, const ScDocument& rDocument, + OUString& rExternDocName, OUString& rStartTabName, + OUString& rEndTabName, ScRefFlags& nFlags, + bool bOnlyAcceptSingle, + const css::uno::Sequence<css::sheet::ExternalLinkInfo>* pExternalLinks = nullptr, + const OUString* pErrRef = nullptr ); + + /** Returns string with formatted cell range from aStart to aEnd, + according to provided address convention. + @param nFlags + Cell reference flags + @param rDocument + Reference to document which is used for example to get tab names. + @param rDetails + Provide information about required address convention. + Supported address conventions are: + CONV_OOO 'doc'#sheet.A1:sheet2.B2 + CONV_XL_A1, [doc]sheet:sheet2!A1:B2 + CONV_XL_OOX, [#]sheet:sheet2!A1:B2 + CONV_XL_R1C1, [doc]sheet:sheet2!R1C1:R2C2 + @param bFullAddressNotation + If TRUE, the full address notation will be used. + For example in case all columns are used, "A1:AMJ177" is full address notation + and "1:177" is shortened address notation. + @returns + String contains formatted cell range in address convention + */ + OUString Format( const ScDocument& rDocument, + ScRefFlags nFlags = ScRefFlags::ZERO, + const ScAddress::Details& rDetails = ScAddress::detailsOOOa1, + bool bFullAddressNotation = false ) const; + + inline void GetVars( SCCOL& nCol1, SCROW& nRow1, SCTAB& nTab1, + SCCOL& nCol2, SCROW& nRow2, SCTAB& nTab2 ) const; + void PutInOrder() { aStart.PutInOrder(aEnd); } + + /** + @param rErrorRange + If FALSE is returned, the positions contain <0 or >MAX... + values if shifted out of bounds. + @param pDocument + The document for the maximum defined sheet number. + */ + [[nodiscard]] bool Move( SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ, + ScRange& rErrorRange, const ScDocument& rDoc ); + + /** Same as Move() but with sticky end col/row anchors. */ + [[nodiscard]] bool MoveSticky( const ScDocument& rDoc, SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ, + ScRange& rErrorRange ); + + void IncColIfNotLessThan(const ScDocument& rDoc, SCCOL nStartCol, SCCOL nOffset); + void IncRowIfNotLessThan(const ScDocument& rDoc, SCROW nStartRow, SCROW nOffset); + + void ExtendTo( const ScRange& rRange ); + + ScRange Intersection( const ScRange& rOther ) const; + + /// If maximum end column should not be adapted during reference update. + bool IsEndColSticky( const ScDocument& rDoc ) const; + /// If maximum end row should not be adapted during reference update. + bool IsEndRowSticky( const ScDocument& rDoc ) const; + + /** Increment or decrement end column unless sticky or until it becomes + sticky. Checks if the range encompasses at least two columns so should + be called before adjusting the start column. */ + void IncEndColSticky( const ScDocument& rDoc, SCCOL nDelta ); + + /** Increment or decrement end row unless sticky or until it becomes + sticky. Checks if the range encompasses at least two rows so should + be called before adjusting the start row. */ + void IncEndRowSticky( const ScDocument& rDoc, SCROW nDelta ); + + inline bool operator==( const ScRange& rRange ) const; + inline bool operator!=( const ScRange& rRange ) const; + inline bool operator<( const ScRange& rRange ) const; + inline bool operator<=( const ScRange& rRange ) const; + + /// Hash 2D area ignoring table number. + inline size_t hashArea() const; + /// Hash start column and start and end rows. + inline size_t hashStartColumn() const; +}; + +// For use in SAL_DEBUG etc. Output format not guaranteed to be stable. +template<typename charT, typename traits> +inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT, traits> & stream, const ScRange& rRange) +{ + stream << rRange.aStart; + if (rRange.aEnd != rRange.aStart) + { + stream << ":"; + if (rRange.aEnd.Tab() != rRange.aStart.Tab()) + stream << rRange.aEnd; + else + stream << + "R" << rRange.aEnd.Row()+1 << + "C" << rRange.aEnd.Col()+1; + } + + return stream; +} + +inline void ScRange::GetVars( SCCOL& nCol1, SCROW& nRow1, SCTAB& nTab1, + SCCOL& nCol2, SCROW& nRow2, SCTAB& nTab2 ) const +{ + aStart.GetVars( nCol1, nRow1, nTab1 ); + aEnd.GetVars( nCol2, nRow2, nTab2 ); +} + +inline bool ScRange::operator==( const ScRange& rRange ) const +{ + return ( (aStart == rRange.aStart) && (aEnd == rRange.aEnd) ); +} + +inline bool ScRange::operator!=( const ScRange& rRange ) const +{ + return !operator==( rRange ); +} + +/// Sort on upper left corner tab,col,row, if equal then use lower right too. +inline bool ScRange::operator<( const ScRange& r ) const +{ + return aStart < r.aStart || (aStart == r.aStart && aEnd < r.aEnd) ; +} + +inline bool ScRange::operator<=( const ScRange& rRange ) const +{ + return operator<( rRange ) || operator==( rRange ); +} + +inline bool ScRange::Contains( const ScAddress& rAddress ) const +{ + return + aStart.Col() <= rAddress.Col() && rAddress.Col() <= aEnd.Col() && + aStart.Row() <= rAddress.Row() && rAddress.Row() <= aEnd.Row() && + aStart.Tab() <= rAddress.Tab() && rAddress.Tab() <= aEnd.Tab(); +} + +inline bool ScRange::Contains( const ScRange& rRange ) const +{ + return + aStart.Col() <= rRange.aStart.Col() && rRange.aEnd.Col() <= aEnd.Col() && + aStart.Row() <= rRange.aStart.Row() && rRange.aEnd.Row() <= aEnd.Row() && + aStart.Tab() <= rRange.aStart.Tab() && rRange.aEnd.Tab() <= aEnd.Tab(); +} + +inline bool ScRange::Intersects( const ScRange& rRange ) const +{ + return + aStart.Col() <= rRange.aEnd.Col() && rRange.aStart.Col() <= aEnd.Col() && + aStart.Row() <= rRange.aEnd.Row() && rRange.aStart.Row() <= aEnd.Row() && + aStart.Tab() <= rRange.aEnd.Tab() && rRange.aStart.Tab() <= aEnd.Tab(); +} + +inline size_t ScRange::hashArea() const +{ +#if SAL_TYPES_SIZEOFPOINTER == 8 + // 12 bits for the columns and 20 bits for the rows + return + (static_cast<size_t>(aStart.Row()) << 44) ^ + (static_cast<size_t>(aStart.Col()) << 32) ^ + (static_cast<size_t>(aEnd.Col()) << 20) ^ + static_cast<size_t>(aEnd.Row()); +#else + // Assume that there are not that many ranges with identical corners so we + // won't have too many collisions. Also assume that more lower row and + // column numbers are used so that there are not too many conflicts with + // the columns hashed into the values, and that start row and column + // usually don't exceed certain values. High bits are not masked off and + // may overlap with lower bits of other values, e.g. if start column is + // greater than assumed. + return + (static_cast<size_t>(aStart.Row()) << 26) ^ // start row <= 2^6 + (static_cast<size_t>(aStart.Col()) << 21) ^ // start column <= 2^5 + (static_cast<size_t>(aEnd.Col()) << 15) ^ // end column <= 2^6 + static_cast<size_t>(aEnd.Row()); // end row <= 2^15 +#endif +} + +inline size_t ScRange::hashStartColumn() const +{ +#if SAL_TYPES_SIZEOFPOINTER == 8 + // 20 bits for the rows + return + (static_cast<size_t>(aStart.Col()) << 40) ^ + (static_cast<size_t>(aStart.Row()) << 20) ^ + static_cast<size_t>(aEnd.Row()); +#else + // Assume that for the start row more lower row numbers are used so that + // there are not too many conflicts with the column hashed into the higher + // values. + return + (static_cast<size_t>(aStart.Col()) << 24) ^ // start column <= 2^8 + (static_cast<size_t>(aStart.Row()) << 16) ^ // start row <= 2^8 + static_cast<size_t>(aEnd.Row()); +#endif +} + +[[nodiscard]] inline bool ValidRange( const ScRange& rRange, SCCOL nMaxCol, SCROW nMaxRow ) +{ + return ValidAddress(rRange.aStart, nMaxCol, nMaxRow) && ValidAddress(rRange.aEnd, nMaxCol, nMaxRow); +} + +// ScRangePair +class SAL_WARN_UNUSED ScRangePair final +{ +private: + std::array<ScRange,2> aRange; + +public: + ScRangePair( const ScRangePair& r ) + { + aRange[0] = r.aRange[0]; + aRange[1] = r.aRange[1]; + } + ScRangePair( const ScRange& rRange1, const ScRange& rRange2 ) + { + aRange[0] = rRange1; + aRange[1] = rRange2; + } + + inline ScRangePair& operator= ( const ScRangePair& rRange ); + const ScRange& GetRange( sal_uInt16 n ) const + { + return aRange[n]; + } + ScRange& GetRange( sal_uInt16 n ) + { + return aRange[n]; + } +}; + +inline ScRangePair& ScRangePair::operator= ( const ScRangePair& rRange ) +{ + aRange[0] = rRange.aRange[0]; + aRange[1] = rRange.aRange[1]; + return *this; +} + +// ScRefAddress +class SAL_WARN_UNUSED ScRefAddress +{ +private: + ScAddress aAdr; + bool bRelCol; + bool bRelRow; + bool bRelTab; +public: + ScRefAddress() : + bRelCol(false), bRelRow(false), bRelTab(false) + {} + ScRefAddress( SCCOL nCol, SCROW nRow, SCTAB nTab ) : + aAdr(nCol, nRow, nTab), + bRelCol(false), bRelRow(false), bRelTab(false) + {} + ScRefAddress( const ScRefAddress& rRef ) : + aAdr(rRef.aAdr), bRelCol(rRef.bRelCol), bRelRow(rRef.bRelRow), + bRelTab(rRef.bRelTab) + {} + + inline ScRefAddress& operator=( const ScRefAddress& ); + + bool IsRelCol() const + { + return bRelCol; + } + bool IsRelRow() const + { + return bRelRow; + } + bool IsRelTab() const + { + return bRelTab; + } + + void SetRelCol(bool bNewRelCol) + { + bRelCol = bNewRelCol; + } + void SetRelRow(bool bNewRelRow) + { + bRelRow = bNewRelRow; + } + void SetRelTab(bool bNewRelTab) + { + bRelTab = bNewRelTab; + } + + inline void Set( const ScAddress& rAdr, + bool bNewRelCol, bool bNewRelRow, bool bNewRelTab ); + inline void Set( SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab, + bool bNewRelCol, bool bNewRelRow, bool bNewRelTab ); + + const ScAddress& GetAddress() const + { + return aAdr; + } + + SCCOL Col() const + { + return aAdr.Col(); + } + SCROW Row() const + { + return aAdr.Row(); + } + SCTAB Tab() const + { + return aAdr.Tab(); + } + + inline bool operator == ( const ScRefAddress& r ) const; + + OUString GetRefString( const ScDocument& rDocument, SCTAB nActTab, + const ScAddress::Details& rDetails = ScAddress::detailsOOOa1) const; +}; + +inline ScRefAddress& ScRefAddress::operator=( const ScRefAddress& rRef ) +{ + aAdr = rRef.aAdr; + bRelCol = rRef.bRelCol; + bRelRow = rRef.bRelRow; + bRelTab = rRef.bRelTab; + return *this; +} + +inline void ScRefAddress::Set( const ScAddress& rAdr, + bool bNewRelCol, bool bNewRelRow, bool bNewRelTab ) +{ + aAdr = rAdr; + bRelCol = bNewRelCol; + bRelRow = bNewRelRow; + bRelTab = bNewRelTab; +} + +inline void ScRefAddress::Set( SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab, + bool bNewRelCol, bool bNewRelRow, bool bNewRelTab ) +{ + aAdr.Set( nNewCol, nNewRow, nNewTab); + bRelCol = bNewRelCol; + bRelRow = bNewRelRow; + bRelTab = bNewRelTab; +} + +inline bool ScRefAddress::operator==( const ScRefAddress& rRefAddress ) const +{ + return aAdr == rRefAddress.aAdr && + bRelCol == rRefAddress.bRelCol && + bRelRow == rRefAddress.bRelRow && + bRelTab == rRefAddress.bRelTab; +} + +// Global functions + +// Special values for cells always broadcasting or listening (ScRecalcMode::ALWAYS +// and the like). +#define BCA_BRDCST_ALWAYS ScAddress( 0, SCROW_MAX, 0 ) +#define BCA_LISTEN_ALWAYS ScRange( BCA_BRDCST_ALWAYS, BCA_BRDCST_ALWAYS ) + +bool ConvertSingleRef( const ScDocument& pDocument, const OUString& rRefString, + SCTAB nDefTab, ScRefAddress& rRefAddress, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo = nullptr ); + +bool ConvertDoubleRef( const ScDocument& rDocument, const OUString& rRefString, + SCTAB nDefTab, ScRefAddress& rStartRefAddress, + ScRefAddress& rEndRefAddress, + const ScAddress::Details& rDetails, + ScAddress::ExternalInfo* pExtInfo = nullptr ); + +/// append alpha representation of column to buffer +SC_DLLPUBLIC void ScColToAlpha( OUStringBuffer& rBuffer, SCCOL nCol); + +inline void ScColToAlpha( OUString& rStr, SCCOL nCol) +{ + OUStringBuffer aBuf(4); + ScColToAlpha( aBuf, nCol); + rStr += aBuf; +} + +inline OUString ScColToAlpha( SCCOL nCol ) +{ + OUStringBuffer aBuf(4); + ScColToAlpha( aBuf, nCol); + return aBuf.makeStringAndClear(); +} + +/// get column number of A..IV... string +bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, std::u16string_view rStr); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |