xref: /core/sc/inc/address.hxx (revision 32ff26f7)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column:100 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #ifndef INCLUDED_SC_INC_ADDRESS_HXX
21 #define INCLUDED_SC_INC_ADDRESS_HXX
22 
23 #include <rtl/ustrbuf.hxx>
24 #include <rtl/strbuf.hxx>
25 
26 #include <array>
27 #include <limits>
28 #include <ostream>
29 
30 #include "scdllapi.h"
31 #include "types.hxx"
32 #include <formula/grammar.hxx>
33 
34 #include <o3tl/typed_flags_set.hxx>
35 #include <o3tl/underlyingenumvalue.hxx>
36 
37 namespace com::sun::star {
38     namespace sheet {
39         struct ExternalLinkInfo;
40     }
41 }
42 
43 namespace com::sun::star::uno { template <typename > class Sequence; }
44 
45 class ScDocument;
46 
47 /** size_t typedef to be able to find places where code was changed from USHORT
48     to size_t and is used to read/write from/to streams. */
49 typedef size_t SCSIZE;
50 
51 // Maximum possible value of data type, NOT maximum row value.
52 // MSC confuses numeric_limit max() with macro max() if vcl/wintypes.hxx is
53 // included, we should not be using those stupid macros anyway.
54 #undef min
55 #undef max
56 const SCROW    SCROW_MAX    = ::std::numeric_limits<SCROW>::max();
57 const SCCOL    SCCOL_MAX    = ::std::numeric_limits<SCCOL>::max();
58 const SCTAB    SCTAB_MAX    = ::std::numeric_limits<SCTAB>::max();
59 const SCCOLROW SCCOLROW_MAX = ::std::numeric_limits<SCCOLROW>::max();
60 const SCSIZE   SCSIZE_MAX   = ::std::numeric_limits<SCSIZE>::max();
61 
62 // Count values
63 const SCROW       MAXROWCOUNT    = 1048576;
64 const SCCOL       MAXCOLCOUNT    = 1024;
65 const SCCOL       INITIALCOLCOUNT = 64; // initial number of columns we allocate memory for
66 /// limiting to 10000 for now, problem with 32 bit builds for now
67 const SCTAB       MAXTABCOUNT    = 10000;
68 // Maximum values
69 const SCROW       MAXROW         = MAXROWCOUNT - 1;
70 const SCCOL       MAXCOL         = MAXCOLCOUNT - 1;
71 const SCTAB       MAXTAB         = MAXTABCOUNT - 1;
72 const SCCOLROW    MAXCOLROW      = MAXROW;
73 const SCROW       MAXROW_JUMBO   = 16 * 1000 * 1000;
74 const SCCOL       MAXCOL_JUMBO   = 16384;
75 // Maximum tiled rendering values
76 const SCROW       MAXTILEDROW    = 500000;
77 // Limit the initial tab count to prevent users to set the count too high,
78 // which could cause the memory usage of blank documents to exceed the
79 // available system memory.
80 const SCTAB       MAXINITTAB = 1024;
81 const SCTAB       MININITTAB = 1;
82 
83 // Special values
84 const SCTAB SC_TAB_APPEND     = SCTAB_MAX;
85 const SCTAB TABLEID_DOC       = SCTAB_MAX;  // entire document, e.g. protect
86 const SCROW SCROWS32K         = 32000; // for fuzzing
87 const SCCOL SCCOL_REPEAT_NONE = SCCOL_MAX;
88 const SCROW SCROW_REPEAT_NONE = SCROW_MAX;
89 const SCCOL SC_TABSTART_NONE  = SCCOL_MAX;
90 
91 const SCROW MAXROW_30         = 8191;
92 
93 [[nodiscard]] inline bool ValidCol( SCCOL nCol, SCCOL nMaxCol )
94 {
95     assert(nMaxCol == MAXCOL || nMaxCol == MAXCOL_JUMBO); // temporary to debug jumbo sheets work
96     return nCol >= 0 && nCol <= nMaxCol;
97 }
98 
99 [[nodiscard]] inline bool ValidRow( SCROW nRow, SCROW nMaxRow)
100 {
101     assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work
102     return nRow >= 0 && nRow <= nMaxRow;
103 }
104 
105 [[nodiscard]] inline bool ValidTab( SCTAB nTab )
106 {
107     return nTab >= 0 && nTab <= MAXTAB;
108 }
109 
110 [[nodiscard]] inline bool ValidTab( SCTAB nTab, SCTAB nMaxTab )
111 {
112     return nTab >= 0 && nTab <= nMaxTab;
113 }
114 
115 [[nodiscard]] inline bool ValidColRow( SCCOL nCol, SCROW nRow, SCCOL nMaxCol, SCROW nMaxRow )
116 {
117     assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work
118     return ValidCol(nCol,nMaxCol) && ValidRow(nRow,nMaxRow);
119 }
120 
121 [[nodiscard]] inline bool ValidColRowTab( SCCOL nCol, SCROW nRow, SCTAB nTab, SCCOL nMaxCol, SCROW nMaxRow )
122 {
123     assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work
124     return ValidCol(nCol,nMaxCol) && ValidRow(nRow,nMaxRow) && ValidTab( nTab);
125 }
126 
127 [[nodiscard]] inline SCCOL SanitizeCol( SCCOL nCol, SCCOL nMaxCol )
128 {
129     assert(nMaxCol == MAXCOL || nMaxCol == MAXCOL_JUMBO); // temporary to debug jumbo sheets work
130     return nCol < 0 ? 0 : (nCol > nMaxCol ? nMaxCol : nCol);
131 }
132 
133 [[nodiscard]] inline SCROW SanitizeRow( SCROW nRow, SCROW nMaxRow )
134 {
135     assert(nMaxRow == MAXROW || nMaxRow == MAXROW_JUMBO); // temporary to debug jumbo sheets work
136     return nRow < 0 ? 0 : (nRow > nMaxRow ? nMaxRow : nRow);
137 }
138 
139 [[nodiscard]] inline SCTAB SanitizeTab( SCTAB nTab )
140 {
141     return nTab < 0 ? 0 : (nTab > MAXTAB ? MAXTAB : nTab);
142 }
143 
144 // The result of ConvertRef() is a bit group of the following:
145 enum class ScRefFlags : sal_uInt16
146 {
147     ZERO          = 0x0000,
148     COL_ABS       = 0x0001,
149     ROW_ABS       = 0x0002,
150     TAB_ABS       = 0x0004,
151     TAB_3D        = 0x0008,
152     COL2_ABS      = 0x0010,
153     ROW2_ABS      = 0x0020,
154     TAB2_ABS      = 0x0040,
155     TAB2_3D       = 0x0080,
156     ROW_VALID     = 0x0100,
157     COL_VALID     = 0x0200,
158     TAB_VALID     = 0x0400,
159     // BITS for convenience
160     BITS          = COL_ABS | ROW_ABS | TAB_ABS | TAB_3D
161                     | ROW_VALID | COL_VALID | TAB_VALID,
162     // somewhat cheesy kludge to force the display of the document name even for
163     // local references.  Requires TAB_3D to be valid
164     FORCE_DOC     = 0x0800,
165     ROW2_VALID    = 0x1000,
166     COL2_VALID    = 0x2000,
167     TAB2_VALID    = 0x4000,
168     VALID         = 0x8000,
169 
170     TAB_ABS_3D    = TAB_ABS | TAB_3D,
171 
172     ADDR_ABS      = VALID | COL_ABS | ROW_ABS | TAB_ABS,
173 
174     RANGE_ABS     = ADDR_ABS | COL2_ABS | ROW2_ABS | TAB2_ABS,
175 
176     ADDR_ABS_3D   = ADDR_ABS | TAB_3D,
177     RANGE_ABS_3D  = RANGE_ABS | TAB_3D
178 };
179 
180 namespace o3tl
181 {
182     template<> struct typed_flags<ScRefFlags> : is_typed_flags<ScRefFlags, 0xffff> {};
183 }
184 inline void applyStartToEndFlags(ScRefFlags &target,const ScRefFlags source)
185 {
186     target |= ScRefFlags(o3tl::underlyingEnumValue(source) << 4);
187 }
188 inline void applyStartToEndFlags(ScRefFlags &target)
189 {
190     target |= ScRefFlags(o3tl::underlyingEnumValue(target) << 4);
191 }
192 
193 //  ScAddress
194 class SAL_WARN_UNUSED ScAddress
195 {
196 private:
197     // Even if the fields are in the order "row, column, tab", in all (?) the ScAddress and
198     // ScDocument APIs that take separate row, column, and tab parameters, the parameters are in the
199     // order "column, row, tab", which matches the most common (A1) address syntax, if you ignore
200     // the sheet (tab). Don't let this confuse you, like it confused me for a while.
201 
202     SCROW   nRow;
203     SCCOL   nCol;
204     SCTAB   nTab;
205 
206 public:
207 
208     enum Uninitialized      { UNINITIALIZED };
209     enum InitializeInvalid  { INITIALIZE_INVALID };
210 
211     struct Details
212     {
213         formula::FormulaGrammar::AddressConvention  eConv;
214         SCROW       nRow;
215         SCCOL       nCol;
216 
217         Details( formula::FormulaGrammar::AddressConvention eConvP, SCROW nRowP, SCCOL nColP ) :
218             eConv(eConvP), nRow(nRowP), nCol(nColP)
219         {}
220         Details( formula::FormulaGrammar::AddressConvention eConvP, ScAddress const & rAddr ) :
221             eConv(eConvP), nRow(rAddr.Row()),  nCol(rAddr.Col())
222         {}
223         Details( formula::FormulaGrammar::AddressConvention eConvP) :
224             eConv(eConvP), nRow(0), nCol(0)
225         {}
226         /* Use the formula::FormulaGrammar::AddressConvention associated with rAddr::Tab() */
227         Details( const ScDocument& rDoc, const ScAddress& rAddr );
228     };
229     SC_DLLPUBLIC static const Details detailsOOOa1;
230 
231     struct ExternalInfo
232     {
233         OUString    maTabName;
234         sal_uInt16  mnFileId;
235         bool        mbExternal;
236 
237         ExternalInfo() :
238             mnFileId(0), mbExternal(false)
239         {}
240     };
241 
242     ScAddress() :
243         nRow(0), nCol(0), nTab(0)
244     {}
245     ScAddress( SCCOL nColP, SCROW nRowP, SCTAB nTabP ) :
246         nRow(nRowP), nCol(nColP), nTab(nTabP)
247     {}
248     /** Yes, it is what it seems to be: Uninitialized. May be used for
249         performance reasons if it is initialized by other means. */
250     ScAddress( Uninitialized )
251     {}
252     ScAddress( InitializeInvalid ) :
253         nRow(-1), nCol(-1), nTab(-1)
254     {}
255     ScAddress( const ScAddress& rAddress ) :
256         nRow(rAddress.nRow), nCol(rAddress.nCol), nTab(rAddress.nTab)
257     {}
258     inline ScAddress& operator=( const ScAddress& rAddress );
259 
260     inline void Set( SCCOL nCol, SCROW nRow, SCTAB nTab );
261 
262     SCROW Row() const
263     {
264         return nRow;
265     }
266 
267     SCCOL Col() const
268     {
269         return nCol;
270     }
271     SCTAB Tab() const
272     {
273         return nTab;
274     }
275     void SetRow( SCROW nRowP )
276     {
277         nRow = nRowP;
278     }
279     void SetCol( SCCOL nColP )
280     {
281         nCol = nColP;
282     }
283     void SetTab( SCTAB nTabP )
284     {
285         nTab = nTabP;
286     }
287     void SetInvalid()
288     {
289         nRow = -1;
290         nCol = -1;
291         nTab = -1;
292     }
293     bool IsValid() const
294     {
295         return (nRow >= 0) && (nCol >= 0) && (nTab >= 0);
296     }
297 
298     inline void PutInOrder( ScAddress& rAddress );
299 
300     void IncRow( SCROW nDelta = 1 )
301     {
302         nRow = sal::static_int_cast<SCROW>(nRow + nDelta);
303     }
304     void IncCol( SCCOL nDelta = 1 )
305     {
306         nCol = sal::static_int_cast<SCCOL>(nCol + nDelta);
307     }
308     void IncTab( SCTAB nDelta = 1 )
309     {
310         nTab = sal::static_int_cast<SCTAB>(nTab + nDelta);
311     }
312     void GetVars( SCCOL& nColP, SCROW& nRowP, SCTAB& nTabP ) const
313     {
314         nColP = nCol;
315         nRowP = nRow;
316         nTabP = nTab;
317     }
318 
319     /**
320         @param  pSheetEndPos
321                 If given and Parse() successfully parsed a sheet name it returns
322                 the end position (exclusive) behind the sheet name AND a
323                 following sheet name separator. This independent of whether the
324                 resulting reference is fully valid or not.
325      */
326     SC_DLLPUBLIC ScRefFlags Parse(
327                     const OUString&, const ScDocument* = nullptr,
328                     const Details& rDetails = detailsOOOa1,
329                     ExternalInfo* pExtInfo = nullptr,
330                     const css::uno::Sequence<css::sheet::ExternalLinkInfo>* pExternalLinks = nullptr,
331                     sal_Int32* pSheetEndPos = nullptr,
332                     const OUString* pErrRef = nullptr );
333 
334     SC_DLLPUBLIC void Format( OStringBuffer& r, ScRefFlags nFlags,
335                                   const ScDocument* pDocument = nullptr,
336                                   const Details& rDetails = detailsOOOa1) const;
337 
338     SC_DLLPUBLIC OUString Format( ScRefFlags nFlags,
339                                   const ScDocument* pDocument = nullptr,
340                                   const Details& rDetails = detailsOOOa1) const;
341 
342     /**
343         @param  rErrorPos
344                 If FALSE is returned, the positions contain <0 or >MAX...
345                 values if shifted out of bounds.
346         @param  pDocument
347                 The document for the maximum defined sheet number.
348      */
349     [[nodiscard]] SC_DLLPUBLIC bool Move( SCCOL nDeltaX, SCROW nDeltaY, SCTAB nDeltaZ,
350             ScAddress& rErrorPos, const ScDocument* pDocument = nullptr );
351 
352     inline bool operator==( const ScAddress& rAddress ) const;
353     inline bool operator!=( const ScAddress& rAddress ) const;
354     inline bool operator<( const ScAddress& rAddress ) const;
355     inline bool operator<=( const ScAddress& rAddress ) const;
356     inline bool lessThanByRow( const ScAddress& rAddress ) const;
357 
358     inline size_t hash() const;
359 
360     /**
361      * Create a human-readable string representation of the cell address.  You
362      * cannot specify precise formatting with this method; use Format() if you
363      * need to specify how the address needs to be formatted.
364      *
365      * The address string does not display sheet name.
366      *
367      * @return human-readable string representation of the cell address.
368      */
369     OUString GetColRowString() const;
370 };
371 
372 // For use in SAL_DEBUG etc. Output format not guaranteed to be stable.
373 template<typename charT, typename traits>
374 inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT, traits> & stream, const ScAddress& rAddress)
375 {
376     stream <<
377         rAddress.Tab()+1 << "!"
378         "R" << rAddress.Row()+1 <<
379         "C" << rAddress.Col()+1;
380 
381     return stream;
382 }
383 
384 inline void ScAddress::PutInOrder( ScAddress& rAddress )
385 {
386     if ( rAddress.Col() < Col() )
387     {
388         SCCOL nTmp = rAddress.Col();
389         rAddress.SetCol( Col() );
390         SetCol( nTmp );
391     }
392     if ( rAddress.Row() < Row() )
393     {
394         SCROW nTmp = rAddress.Row();
395         rAddress.SetRow( Row() );
396         SetRow( nTmp );
397     }
398     if ( rAddress.Tab() < Tab() )
399     {
400         SCTAB nTmp = rAddress.Tab();
401         rAddress.SetTab( Tab() );
402         SetTab( nTmp );
403     }
404 }
405 
406 inline void ScAddress::Set( SCCOL nColP, SCROW nRowP, SCTAB nTabP )
407 {
408     nCol = nColP;
409     nRow = nRowP;
410     nTab = nTabP;
411 }
412 
413 inline ScAddress& ScAddress::operator=( const ScAddress& rAddress )
414 {
415     nCol = rAddress.nCol;
416     nRow = rAddress.nRow;
417     nTab = rAddress.nTab;
418     return *this;
419 }
420 
421 inline bool ScAddress::operator==( const ScAddress& rAddress ) const
422 {
423     return nRow == rAddress.nRow && nCol == rAddress.nCol && nTab == rAddress.nTab;
424 }
425 
426 inline bool ScAddress::operator!=( const ScAddress& rAddress ) const
427 {
428     return !operator==( rAddress );
429 }
430 
431 /** Less than ordered by tab,col,row. */
432 inline bool ScAddress::operator<( const ScAddress& rAddress ) const
433 {
434     if (nTab == rAddress.nTab)
435     {
436         if (nCol == rAddress.nCol)
437             return nRow < rAddress.nRow;
438         else
439             return nCol < rAddress.nCol;
440     }
441     else
442         return nTab < rAddress.nTab;
443 }
444 
445 inline bool ScAddress::operator<=( const ScAddress& rAddress ) const
446 {
447     return operator<( rAddress ) || operator==( rAddress );
448 }
449 
450 /** Less than ordered by tab,row,col as needed by row-wise import/export */
451 inline bool ScAddress::lessThanByRow( const ScAddress& rAddress ) const
452 {
453     if (nTab == rAddress.nTab)
454     {
455         if (nRow == rAddress.nRow)
456             return nCol < rAddress.nCol;
457         else
458             return nRow < rAddress.nRow;
459     }
460     else
461         return nTab < rAddress.nTab;
462 }
463 
464 inline size_t ScAddress::hash() const
465 {
466 #if SAL_TYPES_SIZEOFPOINTER == 8
467     // 16 bits for the columns, and 20 bits for the rows
468     return (static_cast<size_t>(nTab) << 36) ^
469            (static_cast<size_t>(nCol) << 20) ^
470             static_cast<size_t>(nRow);
471 #else
472     // Assume that there are not that many addresses with row > 2^16 AND column
473     // > 2^8 AND sheet > 2^8 so we won't have too many collisions.
474     if (nRow <= 0xffff)
475         return (static_cast<size_t>(nTab) << 24) ^
476             (static_cast<size_t>(nCol) << 16) ^ static_cast<size_t>(nRow);
477     else
478         return (static_cast<size_t>(nTab) << 28) ^
479             (static_cast<size_t>(nCol) << 24) ^ static_cast<size_t>(nRow);
480 #endif
481 }
482 
483 struct ScAddressHashFunctor
484 {
485     size_t operator()( const ScAddress & rAddress ) const
486     {
487         return rAddress.hash();
488     }
489 };
490 
491 inline bool ValidAddress( const ScAddress& rAddress, SCCOL nMaxCol = MAXCOL, SCROW nMaxRow = MAXROW )
492 {
493     return ValidCol(rAddress.Col(), nMaxCol) && ValidRow(rAddress.Row(), nMaxRow) && ValidTab(rAddress.Tab());
494 }
495 
496 //  ScRange
497 class SAL_WARN_UNUSED ScRange final
498 {
499 public:
500     ScAddress aStart;
501     ScAddress aEnd;
502 
503     ScRange() :
504         aStart(), aEnd()
505     {}
506 
507     ScRange( ScAddress::Uninitialized eUninitialized ) :
508         aStart( eUninitialized ), aEnd( eUninitialized )
509     {}
510     ScRange( ScAddress::InitializeInvalid eInvalid ) :
511         aStart( eInvalid ), aEnd( eInvalid )
512     {}
513     ScRange( const ScAddress& aInputStart, const ScAddress& aInputEnd ) :
514         aStart( aInputStart ), aEnd( aInputEnd )
515     {
516         aStart.PutInOrder( aEnd );
517     }
518     ScRange( const ScRange& rRange ) :
519         aStart( rRange.aStart ), aEnd( rRange.aEnd )
520     {}
521     ScRange( const ScAddress& rRange ) :
522         aStart( rRange ), aEnd( rRange )
523     {}
524     ScRange( SCCOL nCol, SCROW nRow, SCTAB nTab ) :
525         aStart( nCol, nRow, nTab ), aEnd( aStart )
526     {}
527     ScRange( SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2 ) :
528         aStart( nCol1, nRow1, nTab1 ), aEnd( nCol2, nRow2, nTab2 )
529     {}
530 
531     ScRange& operator=( const ScRange& rRange )
532     {
533         aStart = rRange.aStart;
534         aEnd = rRange.aEnd;
535         return *this;
536     }
537     ScRange& operator=( const ScAddress& rPos )
538     {
539         aStart = aEnd = rPos;
540         return *this;
541     }
542     void SetInvalid()
543     {
544         aStart.SetInvalid();
545         aEnd.SetInvalid();
546     }
547     bool IsValid() const
548     {
549         return aStart.IsValid() && aEnd.IsValid();
550     }
551     inline bool In( const ScAddress& ) const;   ///< is Address& in Range?
552     inline bool In( const ScRange& ) const;     ///< is Range& in Range?
553 
554     SC_DLLPUBLIC ScRefFlags Parse( const OUString&, const ScDocument* = nullptr,
555                                    const ScAddress::Details& rDetails = ScAddress::detailsOOOa1,
556                                    ScAddress::ExternalInfo* pExtInfo = nullptr,
557                                    const css::uno::Sequence<css::sheet::ExternalLinkInfo>* pExternalLinks = nullptr,
558                                    const OUString* pErrRef = nullptr );
559 
560     SC_DLLPUBLIC ScRefFlags ParseAny( const OUString&, const ScDocument*,
561                                       const ScAddress::Details& rDetails = ScAddress::detailsOOOa1 );
562     SC_DLLPUBLIC ScRefFlags ParseCols( const ScDocument* pDoc,
563                                        const OUString&,
564                                        const ScAddress::Details& rDetails = ScAddress::detailsOOOa1 );
565     SC_DLLPUBLIC void ParseRows( const ScDocument* pDoc,
566                                        const OUString&,
567                                        const ScAddress::Details& rDetails = ScAddress::detailsOOOa1 );
568 
569     /** Parse an Excel style reference up to and including the sheet name
570         separator '!', including detection of external documents and sheet
571         names, and in case of MOOXML import the bracketed index is used to
572         determine the actual document name passed in pExternalLinks. For
573         internal references (resulting rExternDocName empty), aStart.nTab and
574         aEnd.nTab are set, or -1 if sheet name not found.
575         @param bOnlyAcceptSingle  If <TRUE/>, a 3D reference (Sheet1:Sheet2)
576             encountered results in an error (NULL returned).
577         @param pExternalLinks  pointer to ExternalLinkInfo sequence, may be
578             NULL for non-filter usage, in which case indices such as [1] are
579             not resolved.
580         @param pErrRef  pointer to "#REF!" string if to be accepted.
581         @returns
582             Pointer to the position after '!' if successfully parsed, and
583             rExternDocName, rStartTabName and/or rEndTabName filled if
584             applicable. ScRefFlags::... flags set in nFlags.
585             Or if no valid document and/or sheet header could be parsed the start
586             position passed with pString.
587             Or NULL if a 3D sheet header could be parsed but
588             bOnlyAcceptSingle==true was given.
589      */
590     const sal_Unicode* Parse_XL_Header( const sal_Unicode* pString, const ScDocument* pDocument,
591                                         OUString& rExternDocName, OUString& rStartTabName,
592                                         OUString& rEndTabName, ScRefFlags& nFlags,
593                                         bool bOnlyAcceptSingle,
594                                         const css::uno::Sequence<css::sheet::ExternalLinkInfo>* pExternalLinks = nullptr,
595                                         const OUString* pErrRef = nullptr );
596 
597     /** Returns string with formatted cell range from aStart to aEnd,
598         according to provided address convention.
599         @param nFlags
600             Cell reference flags
601         @param pDocument
602             Pointer to document which is used for example to get tab names.
603         @param rDetails
604             Provide information about required address convention.
605             Supported address conventions are:
606                 CONV_OOO      'doc'#sheet.A1:sheet2.B2
607                 CONV_XL_A1,   [doc]sheet:sheet2!A1:B2
608                 CONV_XL_OOX,  [#]sheet:sheet2!A1:B2
609                 CONV_XL_R1C1, [doc]sheet:sheet2!R1C1:R2C2
610         @param bFullAddressNotation
611             If TRUE, the full address notation will be used.
612             For example in case all columns are used, "A1:AMJ177" is full address notation
613             and "1:177" is shortened address notation.
614         @returns
615             String contains formatted cell range in address convention
616      */
617     SC_DLLPUBLIC OUString Format( const ScDocument& rDocument,
618                                   ScRefFlags nFlags = ScRefFlags::ZERO,
619                                   const ScAddress::Details& rDetails = ScAddress::detailsOOOa1,
620                                   bool bFullAddressNotation = false ) const;
621 
622     inline void GetVars( SCCOL& nCol1, SCROW& nRow1, SCTAB& nTab1,
623                          SCCOL& nCol2, SCROW& nRow2, SCTAB& nTab2 ) const;
624     SC_DLLPUBLIC void PutInOrder();
625 
626     /**
627         @param  rErrorRange
628                 If FALSE is returned, the positions contain <0 or >MAX...
629                 values if shifted out of bounds.
630         @param  pDocument
631                 The document for the maximum defined sheet number.
632      */
633     [[nodiscard]] SC_DLLPUBLIC bool Move( SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ,
634             ScRange& rErrorRange, const ScDocument* pDocument = nullptr );
635 
636     /** Same as Move() but with sticky end col/row anchors. */
637     [[nodiscard]] SC_DLLPUBLIC bool MoveSticky( const ScDocument& rDoc, SCCOL aDeltaX, SCROW aDeltaY, SCTAB aDeltaZ,
638             ScRange& rErrorRange );
639 
640     SC_DLLPUBLIC void IncColIfNotLessThan(const ScDocument* pDoc, SCCOL nStartCol, SCCOL nOffset);
641     SC_DLLPUBLIC void IncRowIfNotLessThan(const ScDocument* pDoc, SCROW nStartRow, SCROW nOffset);
642 
643     SC_DLLPUBLIC void ExtendTo( const ScRange& rRange );
644     SC_DLLPUBLIC bool Intersects( const ScRange& rRange ) const;    // do two ranges intersect?
645 
646     ScRange Intersection( const ScRange& rOther ) const;
647 
648     /// If maximum end column should not be adapted during reference update.
649     inline bool IsEndColSticky() const;
650     /// If maximum end row should not be adapted during reference update.
651     inline bool IsEndRowSticky() const;
652 
653     /** Increment or decrement end column unless sticky or until it becomes
654         sticky. Checks if the range encompasses at least two columns so should
655         be called before adjusting the start column. */
656     void IncEndColSticky( const ScDocument* pDoc, SCCOL nDelta );
657 
658     /** Increment or decrement end row unless sticky or until it becomes
659         sticky. Checks if the range encompasses at least two rows so should
660         be called before adjusting the start row. */
661     void IncEndRowSticky( const ScDocument* pDoc, SCROW nDelta );
662 
663     inline bool operator==( const ScRange& rRange ) const;
664     inline bool operator!=( const ScRange& rRange ) const;
665     inline bool operator<( const ScRange& rRange ) const;
666     inline bool operator<=( const ScRange& rRange ) const;
667 
668     /// Hash 2D area ignoring table number.
669     inline size_t hashArea() const;
670     /// Hash start column and start and end rows.
671     inline size_t hashStartColumn() const;
672 };
673 
674 // For use in SAL_DEBUG etc. Output format not guaranteed to be stable.
675 template<typename charT, typename traits>
676 inline std::basic_ostream<charT, traits> & operator <<(std::basic_ostream<charT, traits> & stream, const ScRange& rRange)
677 {
678     stream << rRange.aStart;
679     if (rRange.aEnd != rRange.aStart)
680     {
681         stream << ":";
682         if (rRange.aEnd.Tab() != rRange.aStart.Tab())
683             stream << rRange.aEnd;
684         else
685             stream <<
686                 "R" << rRange.aEnd.Row()+1 <<
687                 "C" << rRange.aEnd.Col()+1;
688     }
689 
690     return stream;
691 }
692 
693 inline void ScRange::GetVars( SCCOL& nCol1, SCROW& nRow1, SCTAB& nTab1,
694                               SCCOL& nCol2, SCROW& nRow2, SCTAB& nTab2 ) const
695 {
696     aStart.GetVars( nCol1, nRow1, nTab1 );
697     aEnd.GetVars( nCol2, nRow2, nTab2 );
698 }
699 
700 inline bool ScRange::IsEndColSticky() const
701 {
702     // Only in an actual column range, i.e. not if both columns are MAXCOL.
703     return aEnd.Col() == MAXCOL && aStart.Col() < aEnd.Col();
704 }
705 
706 inline bool ScRange::IsEndRowSticky() const
707 {
708     // Only in an actual row range, i.e. not if both rows are MAXROW.
709     return aEnd.Row() == MAXROW && aStart.Row() < aEnd.Row();
710 }
711 
712 inline bool ScRange::operator==( const ScRange& rRange ) const
713 {
714     return ( (aStart == rRange.aStart) && (aEnd == rRange.aEnd) );
715 }
716 
717 inline bool ScRange::operator!=( const ScRange& rRange ) const
718 {
719     return !operator==( rRange );
720 }
721 
722 /// Sort on upper left corner tab,col,row, if equal then use lower right too.
723 inline bool ScRange::operator<( const ScRange& r ) const
724 {
725     return aStart < r.aStart || (aStart == r.aStart && aEnd < r.aEnd) ;
726 }
727 
728 inline bool ScRange::operator<=( const ScRange& rRange ) const
729 {
730     return operator<( rRange ) || operator==( rRange );
731 }
732 
733 inline bool ScRange::In( const ScAddress& rAddress ) const
734 {
735     return
736         aStart.Col() <= rAddress.Col() && rAddress.Col() <= aEnd.Col() &&
737         aStart.Row() <= rAddress.Row() && rAddress.Row() <= aEnd.Row() &&
738         aStart.Tab() <= rAddress.Tab() && rAddress.Tab() <= aEnd.Tab();
739 }
740 
741 inline bool ScRange::In( const ScRange& rRange ) const
742 {
743     return
744         aStart.Col() <= rRange.aStart.Col() && rRange.aEnd.Col() <= aEnd.Col() &&
745         aStart.Row() <= rRange.aStart.Row() && rRange.aEnd.Row() <= aEnd.Row() &&
746         aStart.Tab() <= rRange.aStart.Tab() && rRange.aEnd.Tab() <= aEnd.Tab();
747 }
748 
749 inline size_t ScRange::hashArea() const
750 {
751 #if SAL_TYPES_SIZEOFPOINTER == 8
752     // 12 bits for the columns and 20 bits for the rows
753     return
754         (static_cast<size_t>(aStart.Row()) << 44) ^
755         (static_cast<size_t>(aStart.Col()) << 32) ^
756         (static_cast<size_t>(aEnd.Col())   << 20) ^
757          static_cast<size_t>(aEnd.Row());
758 #else
759     // Assume that there are not that many ranges with identical corners so we
760     // won't have too many collisions. Also assume that more lower row and
761     // column numbers are used so that there are not too many conflicts with
762     // the columns hashed into the values, and that start row and column
763     // usually don't exceed certain values. High bits are not masked off and
764     // may overlap with lower bits of other values, e.g. if start column is
765     // greater than assumed.
766     return
767         (static_cast<size_t>(aStart.Row()) << 26) ^ // start row <= 2^6
768         (static_cast<size_t>(aStart.Col()) << 21) ^ // start column <= 2^5
769         (static_cast<size_t>(aEnd.Col())   << 15) ^ // end column <= 2^6
770          static_cast<size_t>(aEnd.Row());           // end row <= 2^15
771 #endif
772 }
773 
774 inline size_t ScRange::hashStartColumn() const
775 {
776 #if SAL_TYPES_SIZEOFPOINTER == 8
777     // 20 bits for the rows
778     return
779         (static_cast<size_t>(aStart.Col()) << 40) ^
780         (static_cast<size_t>(aStart.Row()) << 20) ^
781          static_cast<size_t>(aEnd.Row());
782 #else
783     // Assume that for the start row more lower row numbers are used so that
784     // there are not too many conflicts with the column hashed into the higher
785     // values.
786     return
787         (static_cast<size_t>(aStart.Col()) << 24) ^ // start column <= 2^8
788         (static_cast<size_t>(aStart.Row()) << 16) ^ // start row <= 2^8
789          static_cast<size_t>(aEnd.Row());
790 #endif
791 }
792 
793 inline bool ValidRange( const ScRange& rRange, SCCOL nMaxCol = MAXCOL, SCROW nMaxRow = MAXROW )
794 {
795     return ValidAddress(rRange.aStart, nMaxCol, nMaxRow) && ValidAddress(rRange.aEnd, nMaxCol, nMaxRow);
796 }
797 
798 //  ScRangePair
799 class SAL_WARN_UNUSED ScRangePair final
800 {
801 private:
802     std::array<ScRange,2> aRange;
803 
804 public:
805     ScRangePair( const ScRangePair& r )
806     {
807         aRange[0] = r.aRange[0];
808         aRange[1] = r.aRange[1];
809     }
810     ScRangePair( const ScRange& rRange1, const ScRange& rRange2 )
811     {
812         aRange[0] = rRange1;
813         aRange[1] = rRange2;
814     }
815 
816     inline ScRangePair& operator= ( const ScRangePair& rRange );
817     const ScRange& GetRange( sal_uInt16 n ) const
818     {
819         return aRange[n];
820     }
821     ScRange& GetRange( sal_uInt16 n )
822     {
823         return aRange[n];
824     }
825 };
826 
827 inline ScRangePair& ScRangePair::operator= ( const ScRangePair& rRange )
828 {
829     aRange[0] = rRange.aRange[0];
830     aRange[1] = rRange.aRange[1];
831     return *this;
832 }
833 
834 //  ScRefAddress
835 class SAL_WARN_UNUSED ScRefAddress
836 {
837 private:
838     ScAddress           aAdr;
839     bool                bRelCol;
840     bool                bRelRow;
841     bool                bRelTab;
842 public:
843     ScRefAddress() :
844         bRelCol(false), bRelRow(false), bRelTab(false)
845     {}
846     ScRefAddress( SCCOL nCol, SCROW nRow, SCTAB nTab ) :
847         aAdr(nCol, nRow, nTab),
848         bRelCol(false), bRelRow(false), bRelTab(false)
849     {}
850     ScRefAddress( const ScRefAddress& rRef ) :
851         aAdr(rRef.aAdr), bRelCol(rRef.bRelCol), bRelRow(rRef.bRelRow),
852         bRelTab(rRef.bRelTab)
853     {}
854 
855     inline ScRefAddress& operator=( const ScRefAddress& );
856 
857     bool IsRelCol() const
858     {
859         return bRelCol;
860     }
861     bool IsRelRow() const
862     {
863         return bRelRow;
864     }
865     bool IsRelTab() const
866     {
867         return bRelTab;
868     }
869 
870     void SetRelCol(bool bNewRelCol)
871     {
872         bRelCol = bNewRelCol;
873     }
874     void SetRelRow(bool bNewRelRow)
875     {
876         bRelRow = bNewRelRow;
877     }
878     void SetRelTab(bool bNewRelTab)
879     {
880         bRelTab = bNewRelTab;
881     }
882 
883     inline void Set( const ScAddress& rAdr,
884                      bool bNewRelCol, bool bNewRelRow, bool bNewRelTab );
885     inline void Set( SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
886                      bool bNewRelCol, bool bNewRelRow, bool bNewRelTab );
887 
888     const ScAddress& GetAddress() const
889     {
890         return aAdr;
891     }
892 
893     SCCOL Col() const
894     {
895         return aAdr.Col();
896     }
897     SCROW Row() const
898     {
899         return aAdr.Row();
900     }
901     SCTAB Tab() const
902     {
903         return aAdr.Tab();
904     }
905 
906     inline bool operator == ( const ScRefAddress& r ) const;
907 
908     OUString  GetRefString( const ScDocument* pDocument, SCTAB nActTab,
909                             const ScAddress::Details& rDetails = ScAddress::detailsOOOa1) const;
910 };
911 
912 inline ScRefAddress& ScRefAddress::operator=( const ScRefAddress& rRef )
913 {
914     aAdr = rRef.aAdr;
915     bRelCol = rRef.bRelCol;
916     bRelRow = rRef.bRelRow;
917     bRelTab = rRef.bRelTab;
918     return *this;
919 }
920 
921 inline void ScRefAddress::Set( const ScAddress& rAdr,
922                                bool bNewRelCol, bool bNewRelRow, bool bNewRelTab )
923 {
924     aAdr = rAdr;
925     bRelCol = bNewRelCol;
926     bRelRow = bNewRelRow;
927     bRelTab = bNewRelTab;
928 }
929 
930 inline void ScRefAddress::Set( SCCOL nNewCol, SCROW nNewRow, SCTAB nNewTab,
931                                bool bNewRelCol, bool bNewRelRow, bool bNewRelTab )
932 {
933     aAdr.Set( nNewCol, nNewRow, nNewTab);
934     bRelCol = bNewRelCol;
935     bRelRow = bNewRelRow;
936     bRelTab = bNewRelTab;
937 }
938 
939 inline bool ScRefAddress::operator==( const ScRefAddress& rRefAddress ) const
940 {
941     return aAdr == rRefAddress.aAdr &&
942            bRelCol == rRefAddress.bRelCol &&
943            bRelRow == rRefAddress.bRelRow &&
944            bRelTab == rRefAddress.bRelTab;
945 }
946 
947 // Global functions
948 
949 // Special values for cells always broadcasting or listening (ScRecalcMode::ALWAYS
950 // and the like).
951 #define BCA_BRDCST_ALWAYS ScAddress( 0, SCROW_MAX, 0 )
952 #define BCA_LISTEN_ALWAYS ScRange( BCA_BRDCST_ALWAYS, BCA_BRDCST_ALWAYS )
953 
954 template< typename T > inline void PutInOrder( T& nStart, T& nEnd )
955 {
956     if (nEnd < nStart)
957     {
958         std::swap(nStart, nEnd);
959     }
960 }
961 
962 bool ConvertSingleRef( const ScDocument* pDocument, const OUString& rRefString,
963                        SCTAB nDefTab, ScRefAddress& rRefAddress,
964                        const ScAddress::Details& rDetails,
965                        ScAddress::ExternalInfo* pExtInfo = nullptr );
966 
967 bool ConvertDoubleRef( const ScDocument* pDocument, const OUString& rRefString,
968                        SCTAB nDefTab, ScRefAddress& rStartRefAddress,
969                        ScRefAddress& rEndRefAddress,
970                        const ScAddress::Details& rDetails,
971                        ScAddress::ExternalInfo* pExtInfo = nullptr );
972 
973 /// append alpha representation of column to buffer
974 SC_DLLPUBLIC void ScColToAlpha( OUStringBuffer& rBuffer, SCCOL nCol);
975 
976 inline void ScColToAlpha( OUString& rStr, SCCOL nCol)
977 {
978     OUStringBuffer aBuf(4);
979     ScColToAlpha( aBuf, nCol);
980     rStr += aBuf;
981 }
982 
983 inline OUString ScColToAlpha( SCCOL nCol )
984 {
985     OUStringBuffer aBuf(4);
986     ScColToAlpha( aBuf, nCol);
987     return aBuf.makeStringAndClear();
988 }
989 
990 /// get column number of A..IV... string
991 bool AlphaToCol(const ScDocument& rDoc, SCCOL& rCol, const OUString& rStr);
992 
993 #endif // INCLUDED_SC_INC_ADDRESS_HXX
994 
995 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
996