| /* -*- 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 = 500000; |
| // 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 OUStringLiteral MAXROW_STRING(u"1048575"); |
| inline constexpr OUStringLiteral MAXCOL_STRING(u"XFD"); |
| inline constexpr OUStringLiteral MAXROW_JUMBO_STRING(u"16777215"); |
| inline constexpr OUStringLiteral MAXCOL_JUMBO_STRING(u"XFD"); |
| |
| // 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, const OUString& rStr); |
| |
| /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |