fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / tool / address.cxx
blob4b56e80ebbc41ce5b1023dffbb932eee25271023
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "address.hxx"
21 #include "global.hxx"
22 #include "compiler.hxx"
23 #include "document.hxx"
24 #include "externalrefmgr.hxx"
26 #include "globstr.hrc"
27 #include <sal/alloca.h>
28 #include <osl/diagnose.h>
30 #include <com/sun/star/frame/XModel.hpp>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/sheet/ExternalLinkInfo.hpp>
33 #include <com/sun/star/sheet/ExternalLinkType.hpp>
34 #include <comphelper/string.hxx>
35 #include <sfx2/objsh.hxx>
36 #include <tools/urlobj.hxx>
38 using namespace css;
40 const ScAddress::Details ScAddress::detailsOOOa1( formula::FormulaGrammar::CONV_OOO, 0, 0 );
42 ScAddress::Details::Details ( const ScDocument* pDoc,
43 const ScAddress & rAddr ) :
44 eConv( pDoc->GetAddressConvention() ),
45 nRow( rAddr.Row() ),
46 nCol( rAddr.Col() )
49 namespace {
51 const sal_Unicode* parseQuotedNameWithBuffer( const sal_Unicode* pStart, const sal_Unicode* p, OUString& rName )
53 // The current character must be on the 2nd quote.
55 // Push all the characters up to the current, but skip the very first
56 // character which is the opening quote.
57 OUStringBuffer aBuf(OUString(pStart+1, p-pStart-1));
59 ++p; // Skip the 2nd quote.
60 sal_Unicode cPrev = 0;
61 for (; *p; ++p)
63 if (*p == '\'')
65 if (cPrev == '\'')
67 // double single-quote equals one single quote.
68 aBuf.append(*p);
69 cPrev = 0;
70 continue;
73 else if (cPrev == '\'')
75 // We are past the closing quote. We're done!
76 rName = aBuf.makeStringAndClear();
77 return p;
79 else
80 aBuf.append(*p);
81 cPrev = *p;
84 return pStart;
87 /**
88 * Parse from the opening single quote to the closing single quote. Inside
89 * the quotes, a single quote character is encoded by double single-quote
90 * characters.
92 * @param p pointer to the first character to begin parsing.
93 * @param rName (reference) parsed name within the quotes. If the name is
94 * empty, either the parsing failed or it's an empty quote.
96 * @return pointer to the character immediately after the closing single
97 * quote.
99 const sal_Unicode* parseQuotedName( const sal_Unicode* p, OUString& rName )
101 if (*p != '\'')
102 return p;
104 const sal_Unicode* pStart = p;
105 sal_Unicode cPrev = 0;
106 for (++p; *p; ++p)
108 if (*p == '\'')
110 if (cPrev == '\'')
112 // double single-quote equals one single quote.
113 return parseQuotedNameWithBuffer(pStart, p, rName);
116 else if (cPrev == '\'')
118 // We are past the closing quote. We're done! Skip the opening
119 // and closing quotes.
120 rName = OUString(pStart+1, p - pStart-2);
121 return p;
124 cPrev = *p;
127 rName.clear();
128 return pStart;
133 static long int sal_Unicode_strtol ( const sal_Unicode* p, const sal_Unicode** pEnd )
135 long int accum = 0, prev = 0;
136 bool is_neg = false;
138 if( *p == '-' )
140 is_neg = true;
141 p++;
143 else if( *p == '+' )
144 p++;
146 while (rtl::isAsciiDigit( *p ))
148 accum = accum * 10 + *p - '0';
149 if( accum < prev )
151 *pEnd = NULL;
152 return 0;
154 prev = accum;
155 p++;
158 *pEnd = p;
159 return is_neg ? -accum : accum;
162 static const sal_Unicode* lcl_eatWhiteSpace( const sal_Unicode* p )
164 if ( p )
166 while( *p == ' ' )
167 ++p;
169 return p;
172 /** Determines the number of sheets an external reference spans and sets
173 rRange.aEnd.nTab accordingly. If a sheet is not found, the corresponding
174 bits in rFlags are cleared. pExtInfo is filled if it wasn't already. If in
175 cached order rStartTabName comes after rEndTabName, pExtInfo->maTabName
176 is set to rEndTabName.
177 @returns <FALSE/> if pExtInfo is already filled and rExternDocName does not
178 result in the identical file ID. Else <TRUE/>.
180 static bool lcl_ScRange_External_TabSpan(
181 ScRange & rRange,
182 sal_uInt16 & rFlags,
183 ScAddress::ExternalInfo* pExtInfo,
184 const OUString & rExternDocName,
185 const OUString & rStartTabName,
186 const OUString & rEndTabName,
187 ScDocument* pDoc )
189 if (rExternDocName.isEmpty())
190 return !pExtInfo || !pExtInfo->mbExternal;
192 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
193 if (pRefMgr->isOwnDocument( rExternDocName))
195 // This is an internal document. Get the sheet positions from the
196 // ScDocument instance.
197 if (!rStartTabName.isEmpty())
199 SCTAB nTab;
200 if (pDoc->GetTable(rStartTabName, nTab))
201 rRange.aStart.SetTab(nTab);
204 if (!rEndTabName.isEmpty())
206 SCTAB nTab;
207 if (pDoc->GetTable(rEndTabName, nTab))
208 rRange.aEnd.SetTab(nTab);
210 return !pExtInfo || !pExtInfo->mbExternal;
213 sal_uInt16 nFileId = pRefMgr->getExternalFileId( rExternDocName);
215 if (pExtInfo)
217 if (pExtInfo->mbExternal)
219 if (pExtInfo->mnFileId != nFileId)
220 return false;
222 else
224 pExtInfo->mbExternal = true;
225 pExtInfo->maTabName = rStartTabName;
226 pExtInfo->mnFileId = nFileId;
230 if (rEndTabName.isEmpty() || rStartTabName == rEndTabName)
232 rRange.aEnd.SetTab( rRange.aStart.Tab());
233 return true;
236 SCsTAB nSpan = pRefMgr->getCachedTabSpan( nFileId, rStartTabName, rEndTabName);
237 if (nSpan == -1)
238 rFlags &= ~(SCA_VALID_TAB | SCA_VALID_TAB2);
239 else if (nSpan == 0)
240 rFlags &= ~SCA_VALID_TAB2;
241 else if (nSpan >= 1)
242 rRange.aEnd.SetTab( rRange.aStart.Tab() + nSpan - 1);
243 else // (nSpan < -1)
245 rRange.aEnd.SetTab( rRange.aStart.Tab() - nSpan - 1);
246 if (pExtInfo)
247 pExtInfo->maTabName = rEndTabName;
249 return true;
252 /** Returns NULL if the string should be a sheet name, but is invalid.
253 Returns a pointer to the first character after the sheet name, if there was
254 any, else pointer to start.
255 @param pMsoxlQuoteStop
256 Starting _within_ a quoted name, but still may be 3D; quoted name stops
257 at pMsoxlQuoteStop
259 static const sal_Unicode * lcl_XL_ParseSheetRef( const sal_Unicode* start,
260 OUString& rExternTabName,
261 bool bAllow3D,
262 const sal_Unicode* pMsoxlQuoteStop )
264 OUString aTabName;
265 const sal_Unicode *p = start;
267 // XL only seems to use single quotes for sheet names.
268 if (pMsoxlQuoteStop)
270 const sal_Unicode* pCurrentStart = p;
271 while (p < pMsoxlQuoteStop)
273 if (*p == '\'')
275 // We pre-analyzed the quoting, no checks needed here.
276 if (*++p == '\'')
278 aTabName += OUString( pCurrentStart,
279 sal::static_int_cast<sal_Int32>( p - pCurrentStart));
280 pCurrentStart = ++p;
283 else if (*p == ':')
285 break; // while
287 else
288 ++p;
290 if (pCurrentStart < p)
291 aTabName += OUString( pCurrentStart, sal::static_int_cast<sal_Int32>( p - pCurrentStart));
292 if (aTabName.isEmpty())
293 return NULL;
294 if (p == pMsoxlQuoteStop)
295 ++p; // position on ! of ...'!...
296 if( *p != '!' && ( !bAllow3D || *p != ':' ) )
297 return (!bAllow3D && *p == ':') ? p : start;
299 else if( *p == '\'')
301 p = parseQuotedName(p, aTabName);
302 if (aTabName.isEmpty())
303 return NULL;
305 else
307 bool only_digits = true;
310 * Valid: Normal!a1
311 * Valid: x.y!a1
312 * Invalid: .y!a1
314 * Some names starting with digits are actually valid, but
315 * unparse quoted. Things are quite tricky: most sheet names
316 * starting with a digit are ok, but not those starting with
317 * "[0-9]*\." or "[0-9]+[eE]".
319 * Valid: 42!a1
320 * Valid: 4x!a1
321 * Invalid: 1.!a1
322 * Invalid: 1e!a1
324 while( true )
326 const sal_Unicode uc = *p;
327 if( rtl::isAsciiAlpha( uc ) || uc == '_' )
329 if( only_digits && p != start &&
330 (uc == 'e' || uc == 'E' ) )
332 p = start;
333 break;
335 only_digits = false;
336 p++;
338 else if( rtl::isAsciiDigit( uc ))
340 p++;
342 else if( uc == '.' )
344 if( only_digits ) // Valid, except after only digits.
346 p = start;
347 break;
349 p++;
351 else if (uc > 127)
353 // non ASCII character is allowed.
354 ++p;
356 else
357 break;
360 if( *p != '!' && ( !bAllow3D || *p != ':' ) )
361 return (!bAllow3D && *p == ':') ? p : start;
363 aTabName += OUString( start, sal::static_int_cast<sal_Int32>( p - start ) );
366 rExternTabName = aTabName;
367 return p;
370 /** Tries to obtain the external document index and replace by actual document
371 name.
373 @param ppErrRet
374 Contains the default pointer the caller would return if this method
375 returns FALSE, may be replaced by NULL for type or data errors.
377 @returns FALSE only if the input name is numeric and not within the index
378 sequence, or the link type cannot be determined or data mismatch. Returns
379 TRUE in all other cases, also when there is no index sequence or the input
380 name is not numeric.
382 static bool lcl_XL_getExternalDoc( const sal_Unicode** ppErrRet, OUString& rExternDocName,
383 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
385 // 1-based, sequence starts with an empty element.
386 if (pExternalLinks && pExternalLinks->hasElements())
388 // A numeric "document name" is an index into the sequence.
389 if (CharClass::isAsciiNumeric( rExternDocName))
391 sal_Int32 i = rExternDocName.toInt32();
392 if (i < 0 || i >= pExternalLinks->getLength())
393 return false; // with default *ppErrRet
394 const sheet::ExternalLinkInfo & rInfo = (*pExternalLinks)[i];
395 switch (rInfo.Type)
397 case sheet::ExternalLinkType::DOCUMENT :
399 OUString aStr;
400 if (!(rInfo.Data >>= aStr))
402 SAL_INFO(
403 "sc.core",
404 "Data type mismatch for ExternalLinkInfo "
405 << i);
406 *ppErrRet = NULL;
407 return false;
409 rExternDocName = aStr;
411 break;
412 case sheet::ExternalLinkType::SELF :
413 return false; // ???
414 case sheet::ExternalLinkType::SPECIAL :
415 // silently return nothing (do not assert), caller has to handle this
416 *ppErrRet = NULL;
417 return false;
418 default:
419 SAL_INFO(
420 "sc.core",
421 "unhandled ExternalLinkType " << rInfo.Type
422 << " for index " << i);
423 *ppErrRet = NULL;
424 return false;
428 return true;
431 const sal_Unicode* ScRange::Parse_XL_Header(
432 const sal_Unicode* p,
433 const ScDocument* pDoc,
434 OUString& rExternDocName,
435 OUString& rStartTabName,
436 OUString& rEndTabName,
437 sal_uInt16& nFlags,
438 bool bOnlyAcceptSingle,
439 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
441 const sal_Unicode* startTabs, *start = p;
442 sal_uInt16 nSaveFlags = nFlags;
444 // Is this an external reference ?
445 rStartTabName.clear();
446 rEndTabName.clear();
447 rExternDocName.clear();
448 const sal_Unicode* pMsoxlQuoteStop = NULL;
449 if (*p == '[')
451 ++p;
452 // Only single quotes are correct, and a double single quote escapes a
453 // single quote text inside the quoted text.
454 if (*p == '\'')
456 p = parseQuotedName(p, rExternDocName);
457 if (!*p || *p != ']' || rExternDocName.isEmpty())
459 rExternDocName.clear();
460 return start;
463 else
465 // non-quoted file name.
466 p = ScGlobal::UnicodeStrChr( start+1, ']' );
467 if( p == NULL )
468 return start;
469 rExternDocName += OUString( start+1, sal::static_int_cast<sal_Int32>( p-(start+1) ) );
471 ++p;
473 const sal_Unicode* pErrRet = start;
474 if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
475 return pErrRet;
477 rExternDocName = ScGlobal::GetAbsDocName(rExternDocName, pDoc->GetDocumentShell());
479 else if (*p == '\'')
481 // Sickness in Excel's ODF msoxl namespace:
482 // 'E:\[EXTDATA8.XLS]Sheet1'!$A$7 or
483 // 'E:\[EXTDATA12B.XLSB]Sheet1:Sheet3'!$A$11
484 // But, 'Sheet1'!B3 would also be a valid!
485 // Excel does not allow [ and ] characters in sheet names though.
486 // But, more sickness comes with MOOXML as there may be
487 // '[1]Sheet 4'!$A$1 where [1] is the external doc's index.
488 p = parseQuotedName(p, rExternDocName);
489 if (!*p || *p != '!')
491 rExternDocName.clear();
492 return start;
494 if (!rExternDocName.isEmpty())
496 sal_Int32 nOpen = rExternDocName.indexOf( '[');
497 if (nOpen == -1)
498 rExternDocName.clear();
499 else
501 sal_Int32 nClose = rExternDocName.indexOf( ']', nOpen+1);
502 if (nClose == -1)
503 rExternDocName.clear();
504 else
506 rExternDocName = rExternDocName.copy(0, nClose);
507 rExternDocName = rExternDocName.replaceAt( nOpen, 1, "");
508 pMsoxlQuoteStop = p - 1; // the ' quote char
509 // There may be embedded escaped quotes, just matching the
510 // doc name's length may not work.
511 for (p = start; *p != '['; ++p)
513 for ( ; *p != ']'; ++p)
515 ++p;
517 // Handle '[1]Sheet 4'!$A$1
518 if (nOpen == 0)
520 const sal_Unicode* pErrRet = start;
521 if (!lcl_XL_getExternalDoc( &pErrRet, rExternDocName, pExternalLinks))
522 return pErrRet;
527 if (rExternDocName.isEmpty())
528 p = start;
531 startTabs = p;
532 p = lcl_XL_ParseSheetRef( p, rStartTabName, !bOnlyAcceptSingle, pMsoxlQuoteStop);
533 if( NULL == p )
534 return start; // invalid tab
535 if (bOnlyAcceptSingle && *p == ':')
536 return NULL; // 3D
537 if( p != startTabs )
539 nFlags |= SCA_VALID_TAB | SCA_TAB_3D | SCA_TAB_ABSOLUTE;
540 if( *p == ':' ) // 3d ref
542 p = lcl_XL_ParseSheetRef( p+1, rEndTabName, false, pMsoxlQuoteStop);
543 if( p == NULL )
545 nFlags = nSaveFlags;
546 return start; // invalid tab
548 nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
550 else
552 // If only one sheet is given, the full reference is still valid,
553 // only the second 3D flag is not set.
554 nFlags |= SCA_VALID_TAB2 | SCA_TAB2_ABSOLUTE;
555 aEnd.SetTab( aStart.Tab() );
558 if( *p++ != '!' )
560 nFlags = nSaveFlags;
561 return start; // syntax error
563 else
564 p = lcl_eatWhiteSpace( p );
566 else
568 nFlags |= SCA_VALID_TAB | SCA_VALID_TAB2;
569 // Use the current tab, it needs to be passed in. : aEnd.SetTab( .. );
572 if (!rExternDocName.isEmpty())
574 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
575 pRefMgr->convertToAbsName(rExternDocName);
577 else
579 // Internal reference.
580 if (rStartTabName.isEmpty())
582 nFlags = nSaveFlags;
583 return start;
586 SCTAB nTab;
587 if (!pDoc->GetTable(rStartTabName, nTab))
589 // invalid table name.
590 nFlags &= ~SCA_VALID_TAB;
591 nTab = -1;
594 aStart.SetTab(nTab);
595 aEnd.SetTab(nTab);
597 if (!rEndTabName.isEmpty())
599 if (!pDoc->GetTable(rEndTabName, nTab))
601 // invalid table name.
602 nFlags &= ~SCA_VALID_TAB2;
603 nTab = -1;
606 aEnd.SetTab(nTab);
609 return p;
612 static const sal_Unicode* lcl_r1c1_get_col( const sal_Unicode* p,
613 const ScAddress::Details& rDetails,
614 ScAddress* pAddr, sal_uInt16* nFlags )
616 const sal_Unicode *pEnd;
617 long int n;
618 bool isRelative;
620 if( p[0] == '\0' )
621 return NULL;
623 p++;
624 if( ( isRelative = (*p == '[') ) )
625 p++;
626 n = sal_Unicode_strtol( p, &pEnd );
627 if( NULL == pEnd )
628 return NULL;
630 if( p == pEnd ) // C is a relative ref with offset 0
632 if( isRelative )
633 return NULL;
634 n = rDetails.nCol;
636 else if( isRelative )
638 if( *pEnd != ']' )
639 return NULL;
640 n += rDetails.nCol;
641 pEnd++;
643 else
645 *nFlags |= SCA_COL_ABSOLUTE;
646 n--;
649 if( n < 0 || n >= MAXCOLCOUNT )
650 return NULL;
651 pAddr->SetCol( static_cast<SCCOL>( n ) );
652 *nFlags |= SCA_VALID_COL;
654 return pEnd;
657 static inline const sal_Unicode* lcl_r1c1_get_row(
658 const sal_Unicode* p,
659 const ScAddress::Details& rDetails,
660 ScAddress* pAddr, sal_uInt16* nFlags )
662 const sal_Unicode *pEnd;
663 long int n;
664 bool isRelative;
666 if( p[0] == '\0' )
667 return NULL;
669 p++;
670 if( ( isRelative = (*p == '[') ) )
671 p++;
672 n = sal_Unicode_strtol( p, &pEnd );
673 if( NULL == pEnd )
674 return NULL;
676 if( p == pEnd ) // R is a relative ref with offset 0
678 if( isRelative )
679 return NULL;
680 n = rDetails.nRow;
682 else if( isRelative )
684 if( *pEnd != ']' )
685 return NULL;
686 n += rDetails.nRow;
687 pEnd++;
689 else
691 *nFlags |= SCA_ROW_ABSOLUTE;
692 n--;
695 if( n < 0 || n >= MAXROWCOUNT )
696 return NULL;
697 pAddr->SetRow( static_cast<SCROW>( n ) );
698 *nFlags |= SCA_VALID_ROW;
700 return pEnd;
703 static sal_uInt16 lcl_ScRange_Parse_XL_R1C1( ScRange& r,
704 const sal_Unicode* p,
705 ScDocument* pDoc,
706 const ScAddress::Details& rDetails,
707 bool bOnlyAcceptSingle,
708 ScAddress::ExternalInfo* pExtInfo )
710 const sal_Unicode* pTmp = NULL;
711 OUString aExternDocName, aStartTabName, aEndTabName;
712 sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB;
713 // Keep in mind that nFlags2 gets left-shifted by 4 bits before being merged.
714 sal_uInt16 nFlags2 = SCA_VALID_TAB;
716 p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
717 aEndTabName, nFlags, bOnlyAcceptSingle, NULL );
719 if (!aExternDocName.isEmpty())
720 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
721 aStartTabName, aEndTabName, pDoc);
723 if( NULL == p )
724 return 0;
726 if( *p == 'R' || *p == 'r' )
728 if( NULL == (p = lcl_r1c1_get_row( p, rDetails, &r.aStart, &nFlags )) )
729 return 0;
731 if( *p != 'C' && *p != 'c' ) // full row R#
733 if( p[0] != ':' || (p[1] != 'R' && p[1] != 'r' ) ||
734 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )))
736 // Only the initial row number is given, or the second row
737 // number is invalid. Fallback to just the initial R
738 nFlags |= (nFlags << 4);
739 r.aEnd.SetRow( r.aStart.Row() );
741 else
743 // Full row range successfully parsed.
744 nFlags |= (nFlags2 << 4);
745 p = pTmp;
748 if (p && p[0] != 0)
750 // any trailing invalid character must invalidate the whole address.
751 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
752 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
753 return nFlags;
756 nFlags |=
757 SCA_VALID_COL | SCA_VALID_COL2 |
758 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
759 r.aStart.SetCol( 0 );
760 r.aEnd.SetCol( MAXCOL );
762 return bOnlyAcceptSingle ? 0 : nFlags;
764 else if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
766 return 0;
769 if( p[0] != ':' ||
770 (p[1] != 'R' && p[1] != 'r') ||
771 NULL == (pTmp = lcl_r1c1_get_row( p+1, rDetails, &r.aEnd, &nFlags2 )) ||
772 (*pTmp != 'C' && *pTmp != 'c') ||
773 NULL == (pTmp = lcl_r1c1_get_col( pTmp, rDetails, &r.aEnd, &nFlags2 )))
775 // single cell reference
777 if (p && p[0] != 0)
779 // any trailing invalid character must invalidate the whole address.
780 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
781 return nFlags;
784 return bOnlyAcceptSingle ? nFlags : 0;
786 p = pTmp;
788 // double reference
790 if (p && p[0] != 0)
792 // any trailing invalid character must invalidate the whole range.
793 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
794 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
795 return nFlags;
798 nFlags |= (nFlags2 << 4);
799 return bOnlyAcceptSingle ? 0 : nFlags;
801 else if( *p == 'C' || *p == 'c' ) // full col C#
803 if( NULL == (p = lcl_r1c1_get_col( p, rDetails, &r.aStart, &nFlags )))
804 return 0;
806 if( p[0] != ':' || (p[1] != 'C' && p[1] != 'c') ||
807 NULL == (pTmp = lcl_r1c1_get_col( p+1, rDetails, &r.aEnd, &nFlags2 )))
808 { // Fallback to just the initial C
809 nFlags |= (nFlags << 4);
810 r.aEnd.SetCol( r.aStart.Col() );
812 else
814 nFlags |= (nFlags2 << 4);
815 p = pTmp;
818 if (p && p[0] != 0)
820 // any trailing invalid character must invalidate the whole address.
821 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
822 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
823 return nFlags;
826 nFlags |=
827 SCA_VALID_ROW | SCA_VALID_ROW2 |
828 SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
829 r.aStart.SetRow( 0 );
830 r.aEnd.SetRow( MAXROW );
832 return bOnlyAcceptSingle ? 0 : nFlags;
835 return 0;
838 static inline const sal_Unicode* lcl_a1_get_col( const sal_Unicode* p,
839 ScAddress* pAddr,
840 sal_uInt16* nFlags )
842 SCCOL nCol;
844 if( *p == '$' )
845 *nFlags |= SCA_COL_ABSOLUTE, p++;
847 if( !rtl::isAsciiAlpha( *p ) )
848 return NULL;
850 nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
851 while (nCol <= MAXCOL && rtl::isAsciiAlpha(*p))
852 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
853 if( nCol > MAXCOL || rtl::isAsciiAlpha( *p ) )
854 return NULL;
856 *nFlags |= SCA_VALID_COL;
857 pAddr->SetCol( nCol );
859 return p;
862 static inline const sal_Unicode* lcl_a1_get_row( const sal_Unicode* p,
863 ScAddress* pAddr,
864 sal_uInt16* nFlags )
866 const sal_Unicode *pEnd;
867 long int n;
869 if( *p == '$' )
870 *nFlags |= SCA_ROW_ABSOLUTE, p++;
872 n = sal_Unicode_strtol( p, &pEnd ) - 1;
873 if( NULL == pEnd || p == pEnd || n < 0 || n > MAXROW )
874 return NULL;
876 *nFlags |= SCA_VALID_ROW;
877 pAddr->SetRow( static_cast<SCROW>(n) );
879 return pEnd;
882 /// B:B or 2:2, but not B:2 or 2:B or B2:B or B:B2 or ...
883 static bool isValidSingleton( sal_uInt16 nFlags, sal_uInt16 nFlags2 )
885 bool bCols = (nFlags & SCA_VALID_COL) && ((nFlags & SCA_VALID_COL2) || (nFlags2 & SCA_VALID_COL));
886 bool bRows = (nFlags & SCA_VALID_ROW) && ((nFlags & SCA_VALID_ROW2) || (nFlags2 & SCA_VALID_ROW));
887 return (bCols && !bRows) || (!bCols && bRows);
890 static sal_uInt16 lcl_ScRange_Parse_XL_A1( ScRange& r,
891 const sal_Unicode* p,
892 ScDocument* pDoc,
893 bool bOnlyAcceptSingle,
894 ScAddress::ExternalInfo* pExtInfo,
895 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
897 const sal_Unicode* tmp1, *tmp2;
898 OUString aExternDocName, aStartTabName, aEndTabName; // for external link table
899 sal_uInt16 nFlags = SCA_VALID | SCA_VALID_TAB, nFlags2 = SCA_VALID_TAB;
901 p = r.Parse_XL_Header( p, pDoc, aExternDocName, aStartTabName,
902 aEndTabName, nFlags, bOnlyAcceptSingle, pExternalLinks );
904 if (!aExternDocName.isEmpty())
905 lcl_ScRange_External_TabSpan( r, nFlags, pExtInfo, aExternDocName,
906 aStartTabName, aEndTabName, pDoc);
908 if( NULL == p )
909 return 0;
911 tmp1 = lcl_a1_get_col( p, &r.aStart, &nFlags );
912 if( tmp1 == NULL ) // Is it a row only reference 3:5
914 if( bOnlyAcceptSingle ) // by definition full row refs are ranges
915 return 0;
917 tmp1 = lcl_a1_get_row( p, &r.aStart, &nFlags );
919 tmp1 = lcl_eatWhiteSpace( tmp1 );
920 if( !tmp1 || *tmp1++ != ':' ) // Even a singleton requires ':' (eg 2:2)
921 return 0;
923 tmp1 = lcl_eatWhiteSpace( tmp1 );
924 tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
925 if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton.
926 return 0;
928 r.aStart.SetCol( 0 ); r.aEnd.SetCol( MAXCOL );
929 nFlags |=
930 SCA_VALID_COL | SCA_VALID_COL2 |
931 SCA_COL_ABSOLUTE | SCA_COL2_ABSOLUTE;
932 nFlags |= (nFlags2 << 4);
933 return nFlags;
936 tmp2 = lcl_a1_get_row( tmp1, &r.aStart, &nFlags );
937 if( tmp2 == NULL ) // check for col only reference F:H
939 if( bOnlyAcceptSingle ) // by definition full col refs are ranges
940 return 0;
942 tmp1 = lcl_eatWhiteSpace( tmp1 );
943 if( *tmp1++ != ':' ) // Even a singleton requires ':' (eg F:F)
944 return 0;
946 tmp1 = lcl_eatWhiteSpace( tmp1 );
947 tmp2 = lcl_a1_get_col( tmp1, &r.aEnd, &nFlags2 );
948 if( !tmp2 || *tmp2 != 0 ) // Must have fully parsed a singleton.
949 return 0;
951 r.aStart.SetRow( 0 ); r.aEnd.SetRow( MAXROW );
952 nFlags |=
953 SCA_VALID_ROW | SCA_VALID_ROW2 |
954 SCA_ROW_ABSOLUTE | SCA_ROW2_ABSOLUTE;
955 nFlags |= (nFlags2 << 4);
956 return nFlags;
959 // prepare as if it's a singleton, in case we want to fall back */
960 r.aEnd.SetCol( r.aStart.Col() );
961 r.aEnd.SetRow( r.aStart.Row() ); // don't overwrite sheet number as parsed in Parse_XL_Header()
963 if ( bOnlyAcceptSingle )
965 if ( *tmp2 == 0 )
966 return nFlags;
967 else
969 // any trailing invalid character must invalidate the address.
970 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB);
971 return nFlags;
975 tmp2 = lcl_eatWhiteSpace( tmp2 );
976 if( *tmp2 != ':' )
978 // Sheet1:Sheet2!C4 is a valid range, without a second sheet it is
979 // not. Any trailing invalid character invalidates the range.
980 if (*tmp2 == 0 && (nFlags & SCA_TAB2_3D))
982 if (nFlags & SCA_COL_ABSOLUTE)
983 nFlags |= SCA_COL2_ABSOLUTE;
984 if (nFlags & SCA_ROW_ABSOLUTE)
985 nFlags |= SCA_ROW2_ABSOLUTE;
987 else
988 nFlags &= ~(SCA_VALID |
989 SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
990 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
991 return nFlags;
994 p = tmp2;
995 p = lcl_eatWhiteSpace( p+1 ); // after ':'
996 tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
997 if( !tmp1 && aEndTabName.isEmpty() ) // Probably the aEndTabName was specified after the first range
999 p = lcl_XL_ParseSheetRef( p, aEndTabName, false, NULL );
1000 if( p )
1002 SCTAB nTab = 0;
1003 if( !aEndTabName.isEmpty() && pDoc->GetTable( aEndTabName, nTab ) )
1005 r.aEnd.SetTab( nTab );
1006 nFlags |= SCA_VALID_TAB2 | SCA_TAB2_3D | SCA_TAB2_ABSOLUTE;
1008 if (*p == '!' || *p == ':')
1009 p = lcl_eatWhiteSpace( p+1 );
1010 tmp1 = lcl_a1_get_col( p, &r.aEnd, &nFlags2 );
1013 if( !tmp1 ) // strange, but maybe valid singleton
1014 return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~SCA_VALID);
1016 tmp2 = lcl_a1_get_row( tmp1, &r.aEnd, &nFlags2 );
1017 if( !tmp2 ) // strange, but maybe valid singleton
1018 return isValidSingleton( nFlags, nFlags2) ? nFlags : (nFlags & ~SCA_VALID);
1020 if ( *tmp2 != 0 )
1022 // any trailing invalid character must invalidate the range.
1023 nFlags &= ~(SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW | SCA_VALID_TAB |
1024 SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2);
1025 return nFlags;
1028 nFlags |= (nFlags2 << 4);
1029 return nFlags;
1033 @param p pointer to null-terminated sal_Unicode string
1034 @param rRawRes returns SCA_... flags without the final check for full
1035 validity that is applied to the return value, with which
1036 two addresses that form a column or row singleton range,
1037 e.g. A:A or 1:1, can be detected. Used in
1038 lcl_ScRange_Parse_OOo().
1039 @param pRange pointer to range where rAddr effectively is *pRange->aEnd,
1040 used in conjunction with pExtInfo to determine the tab span
1041 of a 3D reference.
1043 static sal_uInt16 lcl_ScAddress_Parse_OOo( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1044 sal_uInt16& rRawRes,
1045 ScAddress::ExternalInfo* pExtInfo = NULL, ScRange* pRange = NULL )
1047 sal_uInt16 nRes = 0;
1048 rRawRes = 0;
1049 OUString aDocName; // the pure Document Name
1050 OUString aTab;
1051 bool bExtDoc = false;
1052 bool bExtDocInherited = false;
1053 const ScAddress aCurPos(rAddr);
1055 // Lets see if this is a reference to something in an external file. A
1056 // document name is always quoted and has a trailing #.
1057 if (*p == '\'')
1059 const sal_Unicode* pStart = p;
1060 OUString aTmp;
1061 p = parseQuotedName(p, aTmp);
1062 aDocName = aTmp;
1063 if (*p++ == SC_COMPILER_FILE_TAB_SEP)
1064 bExtDoc = true;
1065 else
1066 // This is not a document name. Perhaps a quoted relative table
1067 // name.
1068 p = pStart;
1070 else if (pExtInfo && pExtInfo->mbExternal)
1072 // This is an external reference.
1073 bExtDoc = bExtDocInherited = true;
1076 SCCOL nCol = 0;
1077 SCROW nRow = 0;
1078 SCTAB nTab = 0;
1079 sal_uInt16 nBits = SCA_VALID_TAB;
1080 const sal_Unicode* q;
1081 if ( ScGlobal::FindUnquoted( p, '.') )
1083 nRes |= SCA_TAB_3D;
1084 if ( bExtDoc )
1085 nRes |= SCA_TAB_ABSOLUTE;
1086 if (*p == '$')
1087 nRes |= SCA_TAB_ABSOLUTE, p++;
1089 if (*p == '\'')
1091 // Tokens that start at ' can have anything in them until a final
1092 // ' but '' marks an escaped '. We've earlier guaranteed that a
1093 // string containing '' will be surrounded by '.
1094 p = parseQuotedName(p, aTab);
1096 else
1098 OUStringBuffer aTabAcc;
1099 while (*p)
1101 if( *p == '.')
1102 break;
1104 if( *p == '\'' )
1106 p++; break;
1108 aTabAcc.append(*p);
1109 p++;
1111 aTab = aTabAcc.makeStringAndClear();
1113 if( *p++ != '.' )
1114 nBits = 0;
1116 if (!bExtDoc && (!pDoc || !pDoc->GetTable( aTab, nTab )))
1118 // Specified table name is not found in this document. Assume this is an external document.
1119 aDocName = aTab;
1120 sal_Int32 n = aDocName.lastIndexOf('.');
1121 if (n > 0)
1123 // Extension found. Strip it.
1124 aTab = aTab.replaceAt(n, 1, "");
1125 bExtDoc = true;
1127 else
1128 // No extension found. This is probably not an external document.
1129 nBits = 0;
1132 else
1134 if (bExtDoc && !bExtDocInherited)
1135 return nRes; // After a document a sheet must follow.
1136 nTab = rAddr.Tab();
1138 nRes |= nBits;
1140 q = p;
1141 if (*p)
1143 nBits = SCA_VALID_COL;
1144 if (*p == '$')
1145 nBits |= SCA_COL_ABSOLUTE, p++;
1147 if (rtl::isAsciiAlpha( *p ))
1149 nCol = sal::static_int_cast<SCCOL>( toupper( char(*p++) ) - 'A' );
1150 while (nCol < MAXCOL && rtl::isAsciiAlpha(*p))
1151 nCol = sal::static_int_cast<SCCOL>( ((nCol + 1) * 26) + toupper( char(*p++) ) - 'A' );
1153 else
1154 nBits = 0;
1156 if (nCol > MAXCOL || (*p && *p != '$' && !rtl::isAsciiDigit( *p )))
1157 nBits = 0;
1158 nRes |= nBits;
1159 if( !nBits )
1160 p = q;
1163 q = p;
1164 if (*p)
1166 nBits = SCA_VALID_ROW;
1167 if (*p == '$')
1168 nBits |= SCA_ROW_ABSOLUTE, p++;
1169 if( !rtl::isAsciiDigit( *p ) )
1171 nBits = 0;
1172 nRow = SCROW(-1);
1174 else
1176 long n = rtl_ustr_toInt32( p, 10 ) - 1;
1177 while (rtl::isAsciiDigit( *p ))
1178 p++;
1179 if( n < 0 || n > MAXROW )
1180 nBits = 0;
1181 nRow = static_cast<SCROW>(n);
1183 nRes |= nBits;
1184 if( !nBits )
1185 p = q;
1188 rAddr.Set( nCol, nRow, nTab );
1190 if (!*p && bExtDoc)
1192 if (!pDoc)
1193 nRes = 0;
1194 else
1196 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
1198 // Need document name if inherited.
1199 if (bExtDocInherited)
1201 // The FileId was created using the original file name, so
1202 // obtain that. Otherwise lcl_ScRange_External_TabSpan() would
1203 // retrieve a FileId for the real name and bail out if that
1204 // differed from pExtInfo->mnFileId, as is the case when
1205 // loading documents that refer external files relative to the
1206 // current own document but were saved from a different path
1207 // than loaded.
1208 const OUString* pFileName = pRefMgr->getExternalFileName( pExtInfo->mnFileId, true);
1209 if (pFileName)
1210 aDocName = *pFileName;
1211 else
1212 nRes = 0;
1214 pRefMgr->convertToAbsName(aDocName);
1216 if ((!pExtInfo || !pExtInfo->mbExternal) && pRefMgr->isOwnDocument(aDocName))
1218 if (!pDoc->GetTable( aTab, nTab ))
1219 nRes = 0;
1220 else
1222 rAddr.SetTab( nTab);
1223 nRes |= SCA_VALID_TAB;
1226 else
1228 if (!pExtInfo)
1229 nRes = 0;
1230 else
1232 if (!pExtInfo->mbExternal)
1234 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aDocName);
1236 pExtInfo->mbExternal = true;
1237 pExtInfo->maTabName = aTab;
1238 pExtInfo->mnFileId = nFileId;
1240 if (pRefMgr->getSingleRefToken(nFileId, aTab,
1241 ScAddress(nCol, nRow, 0), NULL,
1242 &nTab).get())
1244 rAddr.SetTab( nTab);
1245 nRes |= SCA_VALID_TAB;
1247 else
1248 nRes = 0;
1250 else
1252 // This is a call for the second part of the reference,
1253 // we must have the range to adapt tab span.
1254 if (!pRange)
1255 nRes = 0;
1256 else
1258 sal_uInt16 nFlags = nRes | SCA_VALID_TAB2;
1259 if (!lcl_ScRange_External_TabSpan( *pRange, nFlags,
1260 pExtInfo, aDocName,
1261 pExtInfo->maTabName, aTab, pDoc))
1262 nRes &= ~SCA_VALID_TAB;
1263 else
1265 if (nFlags & SCA_VALID_TAB2)
1267 rAddr.SetTab( pRange->aEnd.Tab());
1268 nRes |= SCA_VALID_TAB;
1270 else
1271 nRes &= ~SCA_VALID_TAB;
1280 rRawRes |= nRes;
1282 if ( !(nRes & SCA_VALID_ROW) && (nRes & SCA_VALID_COL)
1283 && !( (nRes & SCA_TAB_3D) && (nRes & SCA_VALID_TAB)) )
1284 { // no Row, no Tab, but Col => DM (...), B (...) et al
1285 nRes = 0;
1287 if( !*p )
1289 sal_uInt16 nMask = nRes & ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1290 if( nMask == ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB ) )
1291 nRes |= SCA_VALID;
1293 else
1294 nRes = 0;
1295 return nRes;
1298 static sal_uInt16 lcl_ScAddress_Parse ( const sal_Unicode* p, ScDocument* pDoc, ScAddress& rAddr,
1299 const ScAddress::Details& rDetails,
1300 ScAddress::ExternalInfo* pExtInfo = NULL,
1301 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks = NULL )
1303 if( !*p )
1304 return 0;
1306 switch (rDetails.eConv)
1308 case formula::FormulaGrammar::CONV_XL_A1:
1309 case formula::FormulaGrammar::CONV_XL_OOX:
1311 ScRange rRange = rAddr;
1312 sal_uInt16 nFlags = lcl_ScRange_Parse_XL_A1(
1313 rRange, p, pDoc, true, pExtInfo,
1314 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1315 rAddr = rRange.aStart;
1316 return nFlags;
1318 case formula::FormulaGrammar::CONV_XL_R1C1:
1320 ScRange rRange = rAddr;
1321 sal_uInt16 nFlags = lcl_ScRange_Parse_XL_R1C1( rRange, p, pDoc, rDetails, true, pExtInfo );
1322 rAddr = rRange.aStart;
1323 return nFlags;
1325 default :
1326 case formula::FormulaGrammar::CONV_OOO:
1328 sal_uInt16 nRawRes = 0;
1329 return lcl_ScAddress_Parse_OOo( p, pDoc, rAddr, nRawRes, pExtInfo, NULL );
1334 bool ConvertSingleRef( ScDocument* pDoc, const OUString& rRefString,
1335 SCTAB nDefTab, ScRefAddress& rRefAddress,
1336 const ScAddress::Details& rDetails,
1337 ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1339 bool bRet = false;
1340 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1342 ScAddress aAddr( 0, 0, nDefTab );
1343 sal_uInt16 nRes = aAddr.Parse( rRefString, pDoc, rDetails, pExtInfo);
1344 if ( nRes & SCA_VALID )
1346 rRefAddress.Set( aAddr,
1347 ((nRes & SCA_COL_ABSOLUTE) == 0),
1348 ((nRes & SCA_ROW_ABSOLUTE) == 0),
1349 ((nRes & SCA_TAB_ABSOLUTE) == 0));
1350 bRet = true;
1353 return bRet;
1356 bool ConvertDoubleRef( ScDocument* pDoc, const OUString& rRefString, SCTAB nDefTab,
1357 ScRefAddress& rStartRefAddress, ScRefAddress& rEndRefAddress,
1358 const ScAddress::Details& rDetails,
1359 ScAddress::ExternalInfo* pExtInfo /* = NULL */ )
1361 bool bRet = false;
1362 if (pExtInfo || (ScGlobal::FindUnquoted( rRefString, SC_COMPILER_FILE_TAB_SEP) == -1))
1364 ScRange aRange( ScAddress( 0, 0, nDefTab));
1365 sal_uInt16 nRes = aRange.Parse( rRefString, pDoc, rDetails, pExtInfo);
1366 if ( nRes & SCA_VALID )
1368 rStartRefAddress.Set( aRange.aStart,
1369 ((nRes & SCA_COL_ABSOLUTE) == 0),
1370 ((nRes & SCA_ROW_ABSOLUTE) == 0),
1371 ((nRes & SCA_TAB_ABSOLUTE) == 0));
1372 rEndRefAddress.Set( aRange.aEnd,
1373 ((nRes & SCA_COL2_ABSOLUTE) == 0),
1374 ((nRes & SCA_ROW2_ABSOLUTE) == 0),
1375 ((nRes & SCA_TAB2_ABSOLUTE) == 0));
1376 bRet = true;
1379 return bRet;
1382 sal_uInt16 ScAddress::Parse( const OUString& r, ScDocument* pDoc,
1383 const Details& rDetails,
1384 ExternalInfo* pExtInfo,
1385 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1387 return lcl_ScAddress_Parse( r.getStr(), pDoc, *this, rDetails, pExtInfo, pExternalLinks );
1390 bool ScRange::Intersects( const ScRange& rRange ) const
1392 return !(
1393 std::min( aEnd.Col(), rRange.aEnd.Col() ) < std::max( aStart.Col(), rRange.aStart.Col() )
1394 || std::min( aEnd.Row(), rRange.aEnd.Row() ) < std::max( aStart.Row(), rRange.aStart.Row() )
1395 || std::min( aEnd.Tab(), rRange.aEnd.Tab() ) < std::max( aStart.Tab(), rRange.aStart.Tab() )
1399 ScRange ScRange::Intersection( const ScRange& rOther ) const
1401 SCCOL nCol1 = std::max(aStart.Col(), rOther.aStart.Col());
1402 SCCOL nCol2 = std::min(aEnd.Col(), rOther.aEnd.Col());
1403 SCROW nRow1 = std::max(aStart.Row(), rOther.aStart.Row());
1404 SCROW nRow2 = std::min(aEnd.Row(), rOther.aEnd.Row());
1405 SCTAB nTab1 = std::max(aStart.Tab(), rOther.aStart.Tab());
1406 SCTAB nTab2 = std::min(aEnd.Tab(), rOther.aEnd.Tab());
1408 if (nCol1 > nCol2 || nRow1 > nRow2 || nTab1 > nTab2)
1409 return ScRange(ScAddress::INITIALIZE_INVALID);
1411 return ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
1414 void ScRange::PutInOrder()
1416 SCCOL nCol1 = aStart.Col(), nCol2 = aEnd.Col();
1417 SCROW nRow1 = aStart.Row(), nRow2 = aEnd.Row();
1418 SCTAB nTab1 = aStart.Tab(), nTab2 = aEnd.Tab();
1420 ::PutInOrder(nCol1, nCol2);
1421 ::PutInOrder(nRow1, nRow2);
1422 ::PutInOrder(nTab1, nTab2);
1424 aStart.SetCol(nCol1);
1425 aStart.SetRow(nRow1);
1426 aStart.SetTab(nTab1);
1428 aEnd.SetCol(nCol2);
1429 aEnd.SetRow(nRow2);
1430 aEnd.SetTab(nTab2);
1433 void ScRange::Justify()
1435 SCCOL nTempCol;
1436 if ( aEnd.Col() < (nTempCol = aStart.Col()) )
1438 aStart.SetCol(aEnd.Col()); aEnd.SetCol(nTempCol);
1440 SCROW nTempRow;
1441 if ( aEnd.Row() < (nTempRow = aStart.Row()) )
1443 aStart.SetRow(aEnd.Row()); aEnd.SetRow(nTempRow);
1445 SCTAB nTempTab;
1446 if ( aEnd.Tab() < (nTempTab = aStart.Tab()) )
1448 aStart.SetTab(aEnd.Tab()); aEnd.SetTab(nTempTab);
1452 void ScRange::ExtendTo( const ScRange& rRange )
1454 OSL_ENSURE( rRange.IsValid(), "ScRange::ExtendTo - cannot extend to invalid range" );
1455 if( IsValid() )
1457 aStart.SetCol( std::min( aStart.Col(), rRange.aStart.Col() ) );
1458 aStart.SetRow( std::min( aStart.Row(), rRange.aStart.Row() ) );
1459 aStart.SetTab( std::min( aStart.Tab(), rRange.aStart.Tab() ) );
1460 aEnd.SetCol( std::max( aEnd.Col(), rRange.aEnd.Col() ) );
1461 aEnd.SetRow( std::max( aEnd.Row(), rRange.aEnd.Row() ) );
1462 aEnd.SetTab( std::max( aEnd.Tab(), rRange.aEnd.Tab() ) );
1464 else
1465 *this = rRange;
1468 static sal_uInt16 lcl_ScRange_Parse_OOo( ScRange& rRange,
1469 const OUString& r,
1470 ScDocument* pDoc,
1471 ScAddress::ExternalInfo* pExtInfo = NULL )
1473 sal_uInt16 nRes1 = 0, nRes2 = 0;
1474 sal_Int32 nPos = ScGlobal::FindUnquoted( r, ':');
1475 if (nPos != -1)
1477 OUStringBuffer aTmp(r);
1478 aTmp[nPos] = 0;
1479 const sal_Unicode* p = aTmp.getStr();
1480 sal_uInt16 nRawRes1 = 0;
1481 if (((nRes1 = lcl_ScAddress_Parse_OOo( p, pDoc, rRange.aStart, nRawRes1, pExtInfo, NULL)) != 0) ||
1482 ((nRawRes1 & (SCA_VALID_COL | SCA_VALID_ROW)) && (nRawRes1 & SCA_VALID_TAB)))
1484 rRange.aEnd = rRange.aStart; // sheet must be initialized identical to first sheet
1485 sal_uInt16 nRawRes2 = 0;
1486 nRes2 = lcl_ScAddress_Parse_OOo( p + nPos+ 1, pDoc, rRange.aEnd, nRawRes2, pExtInfo, &rRange);
1487 if (!((nRes1 & SCA_VALID) && (nRes2 & SCA_VALID)) &&
1488 // If not fully valid addresses, check if both have a valid
1489 // column or row, and both have valid (or omitted) sheet references.
1490 (nRawRes1 & (SCA_VALID_COL | SCA_VALID_ROW)) && (nRawRes1 & SCA_VALID_TAB) &&
1491 (nRawRes2 & (SCA_VALID_COL | SCA_VALID_ROW)) && (nRawRes2 & SCA_VALID_TAB) &&
1492 // Both must be column XOR row references, A:A or 1:1 but not A:1 or 1:A
1493 ((nRawRes1 & (SCA_VALID_COL | SCA_VALID_ROW)) == (nRawRes2 & (SCA_VALID_COL | SCA_VALID_ROW))))
1495 nRes1 = nRawRes1 | SCA_VALID;
1496 nRes2 = nRawRes2 | SCA_VALID;
1497 if (nRawRes1 & SCA_VALID_COL)
1499 rRange.aStart.SetRow(0);
1500 rRange.aEnd.SetRow(MAXROW);
1501 nRes1 |= SCA_VALID_ROW | SCA_ROW_ABSOLUTE;
1502 nRes2 |= SCA_VALID_ROW | SCA_ROW_ABSOLUTE;
1504 else
1506 rRange.aStart.SetCol(0);
1507 rRange.aEnd.SetCol(MAXCOL);
1508 nRes1 |= SCA_VALID_COL | SCA_COL_ABSOLUTE;
1509 nRes2 |= SCA_VALID_COL | SCA_COL_ABSOLUTE;
1512 else if ((nRes1 & SCA_VALID) && (nRes2 & SCA_VALID))
1514 // Flag entire column/row references so they can be displayed
1515 // as such. If the sticky reference parts are not both
1516 // absolute or relative, assume that the user thought about
1517 // something we should not touch.
1518 if (rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW &&
1519 ((nRes1 & SCA_ROW_ABSOLUTE) == 0) && ((nRes2 & SCA_ROW_ABSOLUTE) == 0))
1521 nRes1 |= SCA_ROW_ABSOLUTE;
1522 nRes2 |= SCA_ROW_ABSOLUTE;
1524 else if (rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL &&
1525 ((nRes1 & SCA_COL_ABSOLUTE) == 0) && ((nRes2 & SCA_COL_ABSOLUTE) == 0))
1527 nRes1 |= SCA_COL_ABSOLUTE;
1528 nRes2 |= SCA_COL_ABSOLUTE;
1531 if ((nRes1 & SCA_VALID) && (nRes2 & SCA_VALID))
1533 // PutInOrder / Justify
1534 sal_uInt16 nMask, nBits1, nBits2;
1535 SCCOL nTempCol;
1536 if ( rRange.aEnd.Col() < (nTempCol = rRange.aStart.Col()) )
1538 rRange.aStart.SetCol(rRange.aEnd.Col()); rRange.aEnd.SetCol(nTempCol);
1539 nMask = (SCA_VALID_COL | SCA_COL_ABSOLUTE);
1540 nBits1 = nRes1 & nMask;
1541 nBits2 = nRes2 & nMask;
1542 nRes1 = (nRes1 & ~nMask) | nBits2;
1543 nRes2 = (nRes2 & ~nMask) | nBits1;
1545 SCROW nTempRow;
1546 if ( rRange.aEnd.Row() < (nTempRow = rRange.aStart.Row()) )
1548 rRange.aStart.SetRow(rRange.aEnd.Row()); rRange.aEnd.SetRow(nTempRow);
1549 nMask = (SCA_VALID_ROW | SCA_ROW_ABSOLUTE);
1550 nBits1 = nRes1 & nMask;
1551 nBits2 = nRes2 & nMask;
1552 nRes1 = (nRes1 & ~nMask) | nBits2;
1553 nRes2 = (nRes2 & ~nMask) | nBits1;
1555 SCTAB nTempTab;
1556 if ( rRange.aEnd.Tab() < (nTempTab = rRange.aStart.Tab()) )
1558 rRange.aStart.SetTab(rRange.aEnd.Tab()); rRange.aEnd.SetTab(nTempTab);
1559 nMask = (SCA_VALID_TAB | SCA_TAB_ABSOLUTE | SCA_TAB_3D);
1560 nBits1 = nRes1 & nMask;
1561 nBits2 = nRes2 & nMask;
1562 nRes1 = (nRes1 & ~nMask) | nBits2;
1563 nRes2 = (nRes2 & ~nMask) | nBits1;
1565 if ( ((nRes1 & ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1566 == ( SCA_TAB_ABSOLUTE | SCA_TAB_3D ))
1567 && !(nRes2 & SCA_TAB_3D) )
1568 nRes2 |= SCA_TAB_ABSOLUTE;
1570 else
1572 // Don't leave around valid half references.
1573 nRes1 = nRes2 = 0;
1577 nRes1 = ( ( nRes1 | nRes2 ) & SCA_VALID )
1578 | nRes1
1579 | ( ( nRes2 & SCA_BITS ) << 4 );
1580 return nRes1;
1583 sal_uInt16 ScRange::Parse( const OUString& rString, ScDocument* pDoc,
1584 const ScAddress::Details& rDetails,
1585 ScAddress::ExternalInfo* pExtInfo,
1586 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1588 if (rString.isEmpty())
1589 return 0;
1591 switch (rDetails.eConv)
1593 case formula::FormulaGrammar::CONV_XL_A1:
1594 case formula::FormulaGrammar::CONV_XL_OOX:
1596 return lcl_ScRange_Parse_XL_A1( *this, rString.getStr(), pDoc, false, pExtInfo,
1597 (rDetails.eConv == formula::FormulaGrammar::CONV_XL_OOX ? pExternalLinks : NULL) );
1600 case formula::FormulaGrammar::CONV_XL_R1C1:
1602 return lcl_ScRange_Parse_XL_R1C1( *this, rString.getStr(), pDoc, rDetails, false, pExtInfo );
1605 default:
1606 case formula::FormulaGrammar::CONV_OOO:
1608 return lcl_ScRange_Parse_OOo( *this, rString, pDoc, pExtInfo );
1613 // Accept a full range, or an address
1614 sal_uInt16 ScRange::ParseAny( const OUString& rString, ScDocument* pDoc,
1615 const ScAddress::Details& rDetails )
1617 sal_uInt16 nRet = Parse( rString, pDoc, rDetails );
1618 const sal_uInt16 nValid = SCA_VALID | SCA_VALID_COL2 | SCA_VALID_ROW2 | SCA_VALID_TAB2;
1620 if ( (nRet & nValid) != nValid )
1622 ScAddress aAdr(aStart);//initialize with currentPos as fallback for table number
1623 nRet = aAdr.Parse( rString, pDoc, rDetails );
1624 if ( nRet & SCA_VALID )
1625 aStart = aEnd = aAdr;
1627 return nRet;
1630 // Parse only full row references
1631 sal_uInt16 ScRange::ParseCols( const OUString& rStr, ScDocument* pDoc,
1632 const ScAddress::Details& rDetails )
1634 if (rStr.isEmpty())
1635 return 0;
1637 const sal_Unicode* p = rStr.getStr();
1638 sal_uInt16 nRes = 0, ignored = 0;
1640 (void)pDoc; // make compiler shutup we may need this later
1642 switch (rDetails.eConv)
1644 default :
1645 case formula::FormulaGrammar::CONV_OOO: // No full col refs in OOO yet, assume XL notation
1646 case formula::FormulaGrammar::CONV_XL_A1:
1647 case formula::FormulaGrammar::CONV_XL_OOX:
1648 if (NULL != (p = lcl_a1_get_col( p, &aStart, &ignored ) ) )
1650 if( p[0] == ':')
1652 if( NULL != (p = lcl_a1_get_col( p+1, &aEnd, &ignored )))
1654 nRes = SCA_VALID_COL;
1657 else
1659 aEnd = aStart;
1660 nRes = SCA_VALID_COL;
1663 break;
1665 case formula::FormulaGrammar::CONV_XL_R1C1:
1666 if ((p[0] == 'C' || p[0] == 'c') &&
1667 NULL != (p = lcl_r1c1_get_col( p, rDetails, &aStart, &ignored )))
1669 if( p[0] == ':')
1671 if( (p[1] == 'C' || p[1] == 'c') &&
1672 NULL != (p = lcl_r1c1_get_col( p+1, rDetails, &aEnd, &ignored )))
1674 nRes = SCA_VALID_COL;
1677 else
1679 aEnd = aStart;
1680 nRes = SCA_VALID_COL;
1683 break;
1686 return (p != NULL && *p == '\0') ? nRes : 0;
1689 // Parse only full row references
1690 sal_uInt16 ScRange::ParseRows( const OUString& rStr, ScDocument* pDoc,
1691 const ScAddress::Details& rDetails )
1693 if (rStr.isEmpty())
1694 return 0;
1696 const sal_Unicode* p = rStr.getStr();
1697 sal_uInt16 nRes = 0, ignored = 0;
1699 (void)pDoc; // make compiler shutup we may need this later
1701 switch (rDetails.eConv)
1703 default :
1704 case formula::FormulaGrammar::CONV_OOO: // No full row refs in OOO yet, assume XL notation
1705 case formula::FormulaGrammar::CONV_XL_A1:
1706 case formula::FormulaGrammar::CONV_XL_OOX:
1707 if (NULL != (p = lcl_a1_get_row( p, &aStart, &ignored ) ) )
1709 if( p[0] == ':')
1711 if( NULL != (p = lcl_a1_get_row( p+1, &aEnd, &ignored )))
1713 nRes = SCA_VALID_COL;
1716 else
1718 aEnd = aStart;
1719 nRes = SCA_VALID_COL;
1722 break;
1724 case formula::FormulaGrammar::CONV_XL_R1C1:
1725 if ((p[0] == 'R' || p[0] == 'r') &&
1726 NULL != (p = lcl_r1c1_get_row( p, rDetails, &aStart, &ignored )))
1728 if( p[0] == ':')
1730 if( (p[1] == 'R' || p[1] == 'r') &&
1731 NULL != (p = lcl_r1c1_get_row( p+1, rDetails, &aEnd, &ignored )))
1733 nRes = SCA_VALID_COL;
1736 else
1738 aEnd = aStart;
1739 nRes = SCA_VALID_COL;
1742 break;
1745 return (p != NULL && *p == '\0') ? nRes : 0;
1748 template<typename T > static inline void lcl_ScColToAlpha( T& rBuf, SCCOL nCol )
1750 if (nCol < 26*26)
1752 if (nCol < 26)
1753 rBuf.append( static_cast<char>( 'A' + nCol ));
1754 else
1756 rBuf.append( static_cast<char>( 'A' + nCol / 26 - 1 ));
1757 rBuf.append( static_cast<char>( 'A' + nCol % 26 ));
1760 else
1762 sal_Int32 nInsert = rBuf.getLength();
1763 while (nCol >= 26)
1765 SCCOL nC = nCol % 26;
1766 rBuf.insert(nInsert, static_cast<char> ( 'A' + nC ));
1767 nCol = sal::static_int_cast<SCCOL>( nCol - nC );
1768 nCol = nCol / 26 - 1;
1770 rBuf.insert(nInsert, static_cast<char> ( 'A' + nCol ));
1774 void ScColToAlpha( OUStringBuffer& rBuf, SCCOL nCol)
1776 lcl_ScColToAlpha(rBuf, nCol);
1779 template <typename T > static inline void lcl_a1_append_c ( T &rString, int nCol, bool bIsAbs )
1781 if( bIsAbs )
1782 rString.append("$");
1783 lcl_ScColToAlpha( rString, sal::static_int_cast<SCCOL>(nCol) );
1786 template <typename T > static inline void lcl_a1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs )
1788 if ( bIsAbs )
1789 rString.append("$");
1790 rString.append( nRow + 1 );
1793 template <typename T > static inline void lcl_r1c1_append_c ( T &rString, sal_Int32 nCol, bool bIsAbs,
1794 const ScAddress::Details& rDetails )
1796 rString.append("C");
1797 if (bIsAbs)
1799 rString.append( nCol + 1 );
1801 else
1803 nCol -= rDetails.nCol;
1804 if (nCol != 0) {
1805 rString.append("[").append(nCol).append("]");
1810 template <typename T > static inline void lcl_r1c1_append_r ( T &rString, sal_Int32 nRow, bool bIsAbs,
1811 const ScAddress::Details& rDetails )
1813 rString.append("R");
1814 if (bIsAbs)
1816 rString.append( nRow + 1 );
1818 else
1820 nRow -= rDetails.nRow;
1821 if (nRow != 0) {
1822 rString.append("[").append(nRow).append("]");
1827 static OUString getFileNameFromDoc( const ScDocument* pDoc )
1829 // TODO : er points at ScGlobal::GetAbsDocName()
1830 // as a better template. Look into it
1831 OUString sFileName;
1832 SfxObjectShell* pShell;
1834 if( NULL != pDoc &&
1835 NULL != (pShell = pDoc->GetDocumentShell() ) )
1837 uno::Reference< frame::XModel > xModel( pShell->GetModel(), uno::UNO_QUERY );
1838 if( xModel.is() )
1840 if( !xModel->getURL().isEmpty() )
1842 INetURLObject aURL( xModel->getURL() );
1843 sFileName = aURL.GetLastName();
1845 else
1846 sFileName = pShell->GetTitle();
1849 return sFileName;
1853 static inline void lcl_string_append(OUStringBuffer &rString, const OUString &sString)
1855 rString.append(sString);
1858 static inline void lcl_string_append(OStringBuffer &rString, const OUString &sString)
1860 rString.append(OUStringToOString( sString, RTL_TEXTENCODING_UTF8 ));
1863 template<typename T > inline void lcl_Format( T& r, SCTAB nTab, SCROW nRow, SCCOL nCol, sal_uInt16 nFlags,
1864 const ScDocument* pDoc,
1865 const ScAddress::Details& rDetails)
1867 if( nFlags & SCA_VALID )
1868 nFlags |= ( SCA_VALID_ROW | SCA_VALID_COL | SCA_VALID_TAB );
1869 if( pDoc && (nFlags & SCA_VALID_TAB ) )
1871 if ( nTab >= pDoc->GetTableCount() )
1873 lcl_string_append(r, ScGlobal::GetRscString( STR_NOREF_STR ));
1874 return;
1876 if( nFlags & SCA_TAB_3D )
1878 OUString aTabName, aDocName;
1879 pDoc->GetName(nTab, aTabName);
1880 // External Reference, same as in ScCompiler::MakeTabStr()
1881 if( aTabName[0] == '\'' )
1882 { // "'Doc'#Tab"
1883 sal_Int32 nPos = ScCompiler::GetDocTabPos( aTabName);
1884 if (nPos != -1)
1886 aDocName = aTabName.copy( 0, nPos + 1 );
1887 aTabName = aTabName.copy( nPos + 1 );
1890 else if( nFlags & SCA_FORCE_DOC )
1892 // VBA has an 'external' flag that forces the addition of the
1893 // tab name _and_ the doc name. The VBA code would be
1894 // needlessly complicated if it constructed an actual external
1895 // reference so we add this somewhat cheesy kludge to force the
1896 // addition of the document name even for non-external references
1897 aDocName = getFileNameFromDoc( pDoc );
1899 ScCompiler::CheckTabQuotes( aTabName, rDetails.eConv);
1901 switch( rDetails.eConv )
1903 default :
1904 case formula::FormulaGrammar::CONV_OOO:
1905 lcl_string_append(r, aDocName);
1906 if( nFlags & SCA_TAB_ABSOLUTE )
1907 r.append("$");
1908 lcl_string_append(r, aTabName);
1909 r.append(".");
1910 break;
1912 case formula::FormulaGrammar::CONV_XL_A1:
1913 case formula::FormulaGrammar::CONV_XL_R1C1:
1914 case formula::FormulaGrammar::CONV_XL_OOX:
1915 if (!aDocName.isEmpty())
1917 lcl_string_append(r.append("["), aDocName);
1918 r.append("]");
1920 lcl_string_append(r, aTabName);
1921 r.append("!");
1922 break;
1926 switch( rDetails.eConv )
1928 default :
1929 case formula::FormulaGrammar::CONV_OOO:
1930 case formula::FormulaGrammar::CONV_XL_A1:
1931 case formula::FormulaGrammar::CONV_XL_OOX:
1932 if( nFlags & SCA_VALID_COL )
1933 lcl_a1_append_c ( r, nCol, (nFlags & SCA_COL_ABSOLUTE) != 0 );
1934 if( nFlags & SCA_VALID_ROW )
1935 lcl_a1_append_r ( r, nRow, (nFlags & SCA_ROW_ABSOLUTE) != 0 );
1936 break;
1938 case formula::FormulaGrammar::CONV_XL_R1C1:
1939 if( nFlags & SCA_VALID_ROW )
1940 lcl_r1c1_append_r ( r, nRow, (nFlags & SCA_ROW_ABSOLUTE) != 0, rDetails );
1941 if( nFlags & SCA_VALID_COL )
1942 lcl_r1c1_append_c ( r, nCol, (nFlags & SCA_COL_ABSOLUTE) != 0, rDetails );
1943 break;
1947 void ScAddress::Format( OStringBuffer& r, sal_uInt16 nFlags,
1948 const ScDocument* pDoc,
1949 const Details& rDetails) const
1951 lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
1954 OUString ScAddress::Format(sal_uInt16 nFlags, const ScDocument* pDoc,
1955 const Details& rDetails) const
1957 OUStringBuffer r;
1958 lcl_Format(r, nTab, nRow, nCol, nFlags, pDoc, rDetails);
1959 return r.makeStringAndClear();
1962 static void lcl_Split_DocTab( const ScDocument* pDoc, SCTAB nTab,
1963 const ScAddress::Details& rDetails,
1964 sal_uInt16 nFlags,
1965 OUString& rTabName, OUString& rDocName )
1967 pDoc->GetName(nTab, rTabName);
1968 rDocName.clear();
1969 // External reference, same as in ScCompiler::MakeTabStr()
1970 if ( rTabName[0] == '\'' )
1971 { // "'Doc'#Tab"
1972 sal_Int32 nPos = ScCompiler::GetDocTabPos( rTabName);
1973 if (nPos != -1)
1975 rDocName = rTabName.copy( 0, nPos + 1 );
1976 rTabName = rTabName.copy( nPos + 1 );
1979 else if( nFlags & SCA_FORCE_DOC )
1981 // VBA has an 'external' flag that forces the addition of the
1982 // tab name _and_ the doc name. The VBA code would be
1983 // needlessly complicated if it constructed an actual external
1984 // reference so we add this somewhat cheesy kludge to force the
1985 // addition of the document name even for non-external references
1986 rDocName = getFileNameFromDoc( pDoc );
1988 ScCompiler::CheckTabQuotes( rTabName, rDetails.eConv);
1991 static void lcl_ScRange_Format_XL_Header( OUStringBuffer& rString, const ScRange& rRange,
1992 sal_uInt16 nFlags, const ScDocument* pDoc,
1993 const ScAddress::Details& rDetails )
1995 if( nFlags & SCA_TAB_3D )
1997 OUString aTabName, aDocName;
1998 lcl_Split_DocTab( pDoc, rRange.aStart.Tab(), rDetails, nFlags,
1999 aTabName, aDocName );
2000 if( !aDocName.isEmpty() )
2002 rString.append("[").append(aDocName).append("]");
2004 rString.append(aTabName);
2006 if( nFlags & SCA_TAB2_3D )
2008 lcl_Split_DocTab( pDoc, rRange.aEnd.Tab(), rDetails, nFlags,
2009 aTabName, aDocName );
2010 rString.append(":");
2011 rString.append(aTabName);
2013 rString.append("!");
2017 OUString ScRange::Format( sal_uInt16 nFlags, const ScDocument* pDoc,
2018 const ScAddress::Details& rDetails ) const
2020 if( !( nFlags & SCA_VALID ) )
2022 return ScGlobal::GetRscString( STR_NOREF_STR );
2025 OUStringBuffer r;
2026 #define absrel_differ(nFlags, mask) (((nFlags) & (mask)) ^ (((nFlags) >> 4) & (mask)))
2027 switch( rDetails.eConv ) {
2028 default :
2029 case formula::FormulaGrammar::CONV_OOO: {
2030 bool bOneTab = (aStart.Tab() == aEnd.Tab());
2031 if ( !bOneTab )
2032 nFlags |= SCA_TAB_3D;
2033 r = aStart.Format(nFlags, pDoc, rDetails);
2034 if( aStart != aEnd ||
2035 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
2036 absrel_differ( nFlags, SCA_ROW_ABSOLUTE ))
2038 nFlags = ( nFlags & SCA_VALID ) | ( ( nFlags >> 4 ) & 0x070F );
2039 if ( bOneTab )
2040 pDoc = NULL;
2041 else
2042 nFlags |= SCA_TAB_3D;
2043 OUString aName(aEnd.Format(nFlags, pDoc, rDetails));
2044 r.append(":");
2045 r.append(aName);
2048 break;
2050 case formula::FormulaGrammar::CONV_XL_A1:
2051 case formula::FormulaGrammar::CONV_XL_OOX:
2052 lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
2053 if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
2055 // Full col refs always require 2 rows (2:2)
2056 lcl_a1_append_r( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0 );
2057 r.append(":");
2058 lcl_a1_append_r( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0 );
2060 else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
2062 // Full row refs always require 2 cols (A:A)
2063 lcl_a1_append_c( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0 );
2064 r.append(":");
2065 lcl_a1_append_c( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0 );
2067 else
2069 lcl_a1_append_c ( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0 );
2070 lcl_a1_append_r ( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0 );
2071 if( aStart.Col() != aEnd.Col() ||
2072 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
2073 aStart.Row() != aEnd.Row() ||
2074 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
2075 r.append(":");
2076 lcl_a1_append_c ( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0 );
2077 lcl_a1_append_r ( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0 );
2080 break;
2082 case formula::FormulaGrammar::CONV_XL_R1C1:
2083 lcl_ScRange_Format_XL_Header( r, *this, nFlags, pDoc, rDetails );
2084 if( aStart.Col() == 0 && aEnd.Col() >= MAXCOL )
2086 lcl_r1c1_append_r( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0, rDetails );
2087 if( aStart.Row() != aEnd.Row() ||
2088 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
2089 r.append(":");
2090 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0, rDetails );
2093 else if( aStart.Row() == 0 && aEnd.Row() >= MAXROW )
2095 lcl_r1c1_append_c( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0, rDetails );
2096 if( aStart.Col() != aEnd.Col() ||
2097 absrel_differ( nFlags, SCA_COL_ABSOLUTE )) {
2098 r.append(":");
2099 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0, rDetails );
2102 else
2104 lcl_r1c1_append_r( r, aStart.Row(), (nFlags & SCA_ROW_ABSOLUTE) != 0, rDetails );
2105 lcl_r1c1_append_c( r, aStart.Col(), (nFlags & SCA_COL_ABSOLUTE) != 0, rDetails );
2106 if( aStart.Col() != aEnd.Col() ||
2107 absrel_differ( nFlags, SCA_COL_ABSOLUTE ) ||
2108 aStart.Row() != aEnd.Row() ||
2109 absrel_differ( nFlags, SCA_ROW_ABSOLUTE )) {
2110 r.append(":");
2111 lcl_r1c1_append_r( r, aEnd.Row(), (nFlags & SCA_ROW2_ABSOLUTE) != 0, rDetails );
2112 lcl_r1c1_append_c( r, aEnd.Col(), (nFlags & SCA_COL2_ABSOLUTE) != 0, rDetails );
2116 #undef absrel_differ
2117 return r.makeStringAndClear();
2120 bool ScAddress::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
2122 SCsTAB nMaxTab = pDoc ? pDoc->GetTableCount() : MAXTAB;
2123 dx = Col() + dx;
2124 dy = Row() + dy;
2125 dz = Tab() + dz;
2126 bool bValid = true;
2127 if( dx < 0 )
2128 dx = 0, bValid = false;
2129 else if( dx > MAXCOL )
2130 dx = MAXCOL, bValid =false;
2131 if( dy < 0 )
2132 dy = 0, bValid = false;
2133 else if( dy > MAXROW )
2134 dy = MAXROW, bValid =false;
2135 if( dz < 0 )
2136 dz = 0, bValid = false;
2137 else if( dz > nMaxTab )
2138 dz = nMaxTab, bValid =false;
2139 Set( dx, dy, dz );
2140 return bValid;
2143 bool ScRange::Move( SCsCOL dx, SCsROW dy, SCsTAB dz, ScDocument* pDoc )
2145 if (dy && aStart.Row() == 0 && aEnd.Row() == MAXROW)
2146 dy = 0; // Entire column not to be moved.
2147 if (dx && aStart.Col() == 0 && aEnd.Col() == MAXCOL)
2148 dx = 0; // Entire row not to be moved.
2149 bool b = aStart.Move( dx, dy, dz, pDoc );
2150 b &= aEnd.Move( dx, dy, dz, pDoc );
2151 return b;
2154 OUString ScAddress::GetColRowString( bool bAbsolute,
2155 const Details& rDetails ) const
2157 OUStringBuffer aString;
2159 switch( rDetails.eConv )
2161 default :
2162 case formula::FormulaGrammar::CONV_OOO:
2163 case formula::FormulaGrammar::CONV_XL_A1:
2164 case formula::FormulaGrammar::CONV_XL_OOX:
2165 if (bAbsolute)
2166 aString.append("$");
2168 lcl_ScColToAlpha( aString, nCol);
2170 if ( bAbsolute )
2171 aString.append("$");
2173 aString.append(OUString::number(nRow+1));
2174 break;
2176 case formula::FormulaGrammar::CONV_XL_R1C1:
2177 lcl_r1c1_append_r ( aString, nRow, bAbsolute, rDetails );
2178 lcl_r1c1_append_c ( aString, nCol, bAbsolute, rDetails );
2179 break;
2182 return aString.makeStringAndClear();
2185 OUString ScRefAddress::GetRefString( ScDocument* pDoc, SCTAB nActTab,
2186 const ScAddress::Details& rDetails ) const
2188 if ( !pDoc )
2189 return EMPTY_OUSTRING;
2190 if ( Tab()+1 > pDoc->GetTableCount() )
2191 return ScGlobal::GetRscString( STR_NOREF_STR );
2193 sal_uInt16 nFlags = SCA_VALID;
2194 if ( nActTab != Tab() )
2196 nFlags |= SCA_TAB_3D;
2197 if ( !bRelTab )
2198 nFlags |= SCA_TAB_ABSOLUTE;
2200 if ( !bRelCol )
2201 nFlags |= SCA_COL_ABSOLUTE;
2202 if ( !bRelRow )
2203 nFlags |= SCA_ROW_ABSOLUTE;
2205 return aAdr.Format(nFlags, pDoc, rDetails);
2208 bool AlphaToCol( SCCOL& rCol, const OUString& rStr)
2210 SCCOL nResult = 0;
2211 sal_Int32 nStop = rStr.getLength();
2212 sal_Int32 nPos = 0;
2213 sal_Unicode c;
2214 while (nResult <= MAXCOL && nPos < nStop && (c = rStr[nPos]) != 0 &&
2215 rtl::isAsciiAlpha(c))
2217 if (nPos > 0)
2218 nResult = (nResult + 1) * 26;
2219 nResult += ScGlobal::ToUpperAlpha(c) - 'A';
2220 ++nPos;
2222 bool bOk = (ValidCol(nResult) && nPos > 0);
2223 if (bOk)
2224 rCol = nResult;
2225 return bOk;
2228 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */